Compare commits

...

26 Commits

Author SHA1 Message Date
Admin
1c7260600a Minor update for HAL decode issue
Fixes #428
2017-02-01 11:31:04 -06:00
Admin
f1592a1998 Issue with not checking for null 2017-01-30 15:57:35 -06:00
Admin
24dd427fb4 Fixed immediate bugs for
Fixes #129 Hue pass-thru always set to purple
Fixes #406 Dimming with Home Assistant
Fixes #414 Domoticz error retrieving devices
2017-01-30 15:40:58 -06:00
BWS Systems
f8de640f5d added missed quotes 2017-01-27 15:50:00 -06:00
BWS Systems
a091262b80 Merge pull request #413 from bwssytems/targetV4.1.0
Target v4.1.0
2017-01-27 15:30:44 -06:00
BWS Systems
0bcc1628c8 Merge branch 'master' into targetV4.1.0 2017-01-27 15:30:05 -06:00
Admin
ffd95aad87 Remerge 2017-01-27 15:17:37 -06:00
Admin
b5e6e3ad60 v4.1.0 Added feature for Domoticz and fixed some major bugs, ie: mqtt,
and some enhancements.
2017-01-27 15:11:30 -06:00
Admin
611cc7be4a Updated REAMDE to new constructs. Added Time repalcement Added ability
inactivate a device.
2017-01-26 16:46:17 -06:00
Admin
4b7ba0fabe Finished Domoticz impl and secure calls for Home Assistant 2017-01-25 16:24:44 -06:00
Admin
6abe1b082c Rebased to master 2017-01-25 15:39:02 -06:00
Admin
b08a285bd0 One more update for JsonArray detection. 2017-01-25 15:23:48 -06:00
Admin
e68282a230 Merge remote-tracking branch 'origin/master' 2017-01-25 15:21:37 -06:00
Admin
c9d55e26ac Fix immediate issues for JSON decoding
Fixes #391
Fixes #392
Fixes #398
2017-01-25 15:19:46 -06:00
BWS Systems
5a3a02cb34 Added security note. 2017-01-25 12:14:59 -06:00
Admin
a9f48e1f9c Add domoticz web page 2017-01-25 09:16:33 -06:00
bwssystems
8b9fd355b4 More updates 2017-01-25 06:58:51 -06:00
Admin
b4da321368 Continue Domoitcz impl 2017-01-24 16:49:56 -06:00
BWS Systems
881f739c0b Added proxy instructions 2017-01-24 12:47:36 -06:00
BWS Systems
f148c2b9fc updated jar version 2017-01-24 10:28:14 -06:00
Admin
fd486588f5 Fixed more immediate issues
Fixes #272
Fixes #384
Fixes #387
Fixes #389
2017-01-24 09:31:48 -06:00
Admin
d118dd8523 Continue on domoticz impl 2017-01-24 08:29:59 -06:00
BWS Systems
58e1679529 updated jar version 2017-01-23 19:05:29 -06:00
Admin
104d341864 Starting to build Domoticz handler 2017-01-23 16:35:34 -06:00
Admin
7772a3de0f Immediate bug fixes
Fixes #378
Fixes #380

Added FAQ link in Help menu
2017-01-23 15:44:53 -06:00
BWS Systems
a348086910 Add FAQ 2017-01-22 18:56:10 -06:00
53 changed files with 1757 additions and 318 deletions

306
README.md
View File

@@ -1,12 +1,16 @@
# ha-bridge
Emulates Philips Hue api to other home automation gateways such as an Amazon Echo or Google Home. The Bridge handles basic commands such as "On", "Off" and "brightness" commands of the hue protocol. This bridge can control most devices that have a distinct API.
**SECURITY RISK: If you are unsure on how this software operates and what it exposes to your network, please make sure you understand that it can allow root access to your system. It is best practice to not open this to the Internet through your router as there are no security protocols in place to protect the system. The License agreement states specifically that you use this at your own risk.**
**ATTENTION: This requires a physical Amazon Echo, Dot or Tap and does not work with prototype devices built using the Alexa Voice Service e.g. Amazon's Alexa AVS Sample App and Sam Machin's AlexaPi. The AVS version does not have any capability for Hue Bridge discovery!**
**NOTE: This software does require the user to have knwoledge on how processes run on Linux or Windows with java. Also, an understanding of networking basics will help as well. This system reveives upnp udp multicast packets from devices to be found, so that is some thing to understand. Please make sure you have all your devices use static IP addresses from your router. Most all questions have been answered already. PLEASE USE GOOGLE TO FIND YOUR ANSWERS!**
**NOTE: This software does require the user to have knowledge on how processes run on Linux or Windows with java. Also, an understanding of networking basics will help as well. This system receives upnp udp multicast packets from devices to be found, so that is something to understand. Please make sure you have all your devices use static IP addresses from your router. Most all questions have been answered already. PLEASE USE GOOGLE TO FIND YOUR ANSWERS!**
**NOTE: This software does not control Philips Hue devices directly. A physical Philips Hue Hub is required for that, by which the ha-bridge can then proxy all of your real Hue bridges behind this bridge.**
**FAQ: Please look here for the current FAQs! https://github.com/bwssytems/ha-bridge/wiki/HA-Bridge-FAQs**
In the cases of systems that require authorization and/or have API's that cannot be handled in the current method, a module may need to be built. The Harmony Hub is such a module and so is the Nest module. The Bridge has helpers to build devices for the gateway for the Logitech Harmony Hub, Vera, Vera Lite or Vera Edge, Nest and the ability to proxy all of your real Hue bridges behind this bridge.
Alternatively the Bridge supports custom calls as well using http/https/udp and tcp such as the LimitlessLED/MiLight bulbs using the UDP protocol. Binary data is supported with UDP/TCP.
@@ -29,16 +33,23 @@ ATTENTION: This requires JDK 1.8 to run
ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below.
```
java -jar ha-bridge-4.0.0.jar
java -jar ha-bridge-4.1.1.jar
```
### Automation on Linux systems
To have this configured and running automatically there are a few resources to use. One is using Docker and a docker container has been built for this and can be gotten here: https://github.com/aptalca/docker-ha-bridge
Create the directory and make sure that ha-bridge-4.0.0.jar is in your /home/pi/habridge directory.
Create the directory and make sure that ha-bridge-4.1.1.jar is in your /home/pi/habridge directory.
```
pi@raspberrypi:~ $ mkdir habridge
pi@raspberrypi:~ $ cd habridge
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.0.0/ha-bridge-4.0.0.jar
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.1.1/ha-bridge-4.1.1.jar
```
Create the directory and make sure that ha-bridge-4.1.1.jar is in your /home/pi/habridge directory.
```
pi@raspberrypi:~ $ mkdir habridge
pi@raspberrypi:~ $ cd habridge
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.1.1/ha-bridge-4.1.1.jar
```
#### System Control Setup on a pi (preferred)
For next gen Linux systems (this includes the Raspberry Pi), here is a systemctl unit file that you can install. Here is a link on how to do this: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
@@ -57,7 +68,8 @@ After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.0.0.jar
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.1.1.jar
[Install]
WantedBy=multi-user.target
@@ -92,7 +104,8 @@ Then cut and past this, modify any locations that are not correct
```
cd /home/pi/habridge
rm /home/pi/habridge/habridge-log.txt
nohup java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.0.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
nohup java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.1.1.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
chmod 777 /home/pi/habridge/habridge-log.txt
```
Exit and save the file with ctrl-X and follow the prompts and then execute on the command line:
@@ -107,8 +120,104 @@ You should now be running the bridge. Check for errors:
```
pi@raspberrypi:~/habridge $ tail -f habridge-log.txt
```
## Run ha-bridge alongside web server already on port 80
These examples will help you proxy your current webserver requests to the ha-bridge running on a different port, such as 8080.
### Apache Example
Reverse proxy with Apache on Ubuntu linux:
a2enmod proxy
a2enmod proxy_http
a2enmod headers
Added the following lines to my Apache config file “000-default”
```
<VirtualHost *:80>
ProxyPass /api http://localhost:8080/api nocanon
ProxyPassReverse /api http://localhost:8080/api
ProxyRequests Off
AllowEncodedSlashes NoDecode
# Local reverse proxy authorization override
# Most unix distribution deny proxy by default (ie /etc/apache2/mods-enabled/proxy.conf in Ubuntu)
<Proxy http://localhost:8080/api*>
Order deny,allow
Allow from all
</Proxy>
….. (the rest of the VirtualHost config section) …..
</VirtualHost>
```
service apache2 restart
### lighthttpd Example
```
server.modules += ( "mod_proxy" )
proxy.server = (
"/api" =>
(
( "host" => "127.0.0.1",
"port" => "8080"
)
)
)
```
### nginx Example
```
location /api/ {
proxy_pass http://127.0.0.1:8080/api;
}
```
## Run ha-bridge alongside web server already on port 80
These examples will help you proxy your current webserver requests to the ha-bridge running on a different port, such as 8080.
### Apache Example
Reverse proxy with Apache on Ubuntu linux:
a2enmod proxy
a2enmod proxy_http
a2enmod headers
Added the following lines to my Apache config file “000-default”
```
<VirtualHost *:80>
ProxyPass /api http://localhost:8080/api nocanon
ProxyPassReverse /api http://localhost:8080/api
ProxyRequests Off
AllowEncodedSlashes NoDecode
# Local reverse proxy authorization override
# Most unix distribution deny proxy by default (ie /etc/apache2/mods-enabled/proxy.conf in Ubuntu)
<Proxy http://localhost:8080/api*>
Order deny,allow
Allow from all
</Proxy>
….. (the rest of the VirtualHost config section) …..
</VirtualHost>
```
service apache2 restart
### lighthttpd Example
```
server.modules += ( "mod_proxy" )
proxy.server = (
"/api" =>
(
( "host" => "127.0.0.1",
"port" => "8080"
)
)
)
```
### nginx Example
```
location /api/ {
proxy_pass http://127.0.0.1:8080/api;
}
```
## Available Arguments
Arguments are now deprecated. The ha-bridge will use the old -D arguments and populate the configuration screen, Brisge Control Tab, which can now be saved to a file and will not be needed. There is only one optional argument that overrides and that is the location of the configuration file. The default is the relative path "data/habridge.config".
Arguments are now deprecated. The ha-bridge will use the old -D arguments and populate the configuration screen, Bridge Control Tab, which can now be saved to a file and will not be needed. There is only one optional argument that overrides and that is the location of the configuration file. The default is the relative path "data/habridge.config".
### -Dconfig.file=`<filepath>`
The default location for the configuration file to contain the settings for the bridge is the relative path from where the bridge is started in "data/habridge.config". If you would like a different filename or directory, specify -Dconfig.file=`<directory>/<filename>` explicitly. The command line example:
```
@@ -128,7 +237,7 @@ java -jar -Dserver.ip=192.168.1.1 ha-bridge-W.X.Y.jar
```
## HA Bridge Usage and Configuration
This section will cover the basics of configuration and where this configuration can be done. This requires that you have started your bridge process and then have pointed your
favorite web interface by going to the http://<my ip address>:<port> or http://localhost:<port> with port you have assigned. The default quick link is http://localhost for yoru reference.
favorite web interface by going to the http://<my ip address>:<port> or http://localhost:<port> with port you have assigned. The default quick link is http://localhost for your reference.
### The Bridge Devices Tab
This screen allows you to see your devices you have configured for the ha-bridge to present to a controller, such as an Amazon Echo/Dot. It gives you a count of devices as there have been reports that the Echo only supports a limited number, but has been growing as of late, YMMV. You can test each device from this page as this calls the ha-bridge just as a controller would, i.e. the Echo. This is useful to make sure your configuration for each device is correct and for trouble shooting. You can also manages your devices as well by editing and making a new device copy as well as deleting it.
@@ -156,27 +265,27 @@ Provide IP Addresses of your Veras that you want to utilize with the bridge. Als
#### Harmony Names and IP Addresses
Provide IP Addresses of your Harmony Hubs that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the activity or buttons by the call it receives and send it to the target Harmony Hub and activity/button you configure.
#### Harmony Username
depracated
deprecated
#### Harmony Password
depracated
deprecated
#### Hue Names and IP Addresses
Provide IP Addresses of your Hue Bridges that you want to proxy through the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will passthru the call it receives to the target Hue and device you configure.
Don't forget - You will need to push the link button when you got to the Hue Tab the first time ater the process comes up. (The user name is not persistent when the process comes up.)
Don't forget - You will need to push the link button when you got to the Hue Tab the first time after the process comes up. (The user name is not persistent when the process comes up.)
#### HAL Names and IP Addresses
Provide IP Addresses of your HAL Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target HAL and device/scene you configure.
#### HAL Token
The token you generate or give to a HAL and must be the same for all HAL's you have identified. This needs to be given if you are using the HAL features.
#### MQTT Client IDs and IP Addresses
Provide Client ID and IP Addresses and ports of your MQTT Brokers that you want to utilize with the bridge. Also, you can provide the username and password if you have secured yourMQTT broker which is optional. When these Client ID and IP's are given, the bridge will be able to publish mqtt messages by the call it receives and send it to the target MQTT Broker you configure. The MQTT Messages Tab will become available to help you build messages.
Provide Client ID and IP Addresses and ports of your MQTT Brokers that you want to utilize with the bridge. Also, you can provide the username and password if you have secured your MQTT broker which is optional. When these Client ID and IP's are given, the bridge will be able to publish MQTT messages by the call it receives and send it to the target MQTT Broker you configure. The MQTT Messages Tab will become available to help you build messages.
#### Nest Username
The user name of the home.nest.com account for the Nest user. This needs to be given if you are using the Nest features. There is no need to give any ip address or host information as this contacts your cloud account.
#### Nest Password
The password for the user name of the home.nest.com account for the Nest user. This needs to be given if you are using the Nest features.
#### Nest Temp Farenheit
This setting allows the value being sent into the bridge to be interpreted as Farenheit or Celsius. The default is to have Farenheit.
#### Nest Temp Fahrenheit
This setting allows the value being sent into the bridge to be interpreted as Fahrenheit or Celsius. The default is to have Fahrenheit.
#### Button Press/Call Item Loop Sleep Interval (ms)
This setting is the time used in between button presses when there is multple buttons in a button device. It also controls the time between multiple items in a custom device call. This is defaulted to 100ms and the number represnts milliseonds (1000 milliseconds = 1 second).
This setting is the time used in between button presses when there is multiple buttons in a button device. It also controls the time between multiple items in a custom device call. This is defaulted to 100ms and the number represents milliseconds (1000 milliseconds = 1 second).
#### Log Messages to Buffer
This controls how many log messages will be kept and displayed on the log tab. This does not affect what is written to the standard output for logging. The default is 512. Changing this will incur more memory usage of the process.
#### UPNP Strict Handling
@@ -190,7 +299,7 @@ This screen displays the last 512 or number of rows defined in the config screen
The bottom part of the Logs Screen has configuration to change the logging levels as it is running. The ROOT is the basic setting and will turn on only top level logging. To set logging at a lower level, select the `Show All Loggers` checkbox and then you can set the explicit level on each of the processes components. The most helpful logger would be setting DEBUG for com.bwssystems.HABridge.hue.HueMulator component. Changing this and then selecting the `Update Log Levels` button applies the new log settings.
### Bridge Device Additions
You must configure devices before you will have any thing for the Echo or other controller that is connected to the ha-bridge to receive.
You must configure devices before you will have anything for the Echo or other controller that is connected to the ha-bridge to receive.
#### Helpers
The easy way to get devices configured is with the use of the helpers for the Vera or Harmony, Nest and Hue to create devices that the bridge will present.
@@ -200,7 +309,42 @@ The helper tabs will also show you what you have already configured for that tar
#### The Add/Edit Tab
Another way to add a device is through the Manual Add Tab. This allows you to manually enter the name, the on and off URLs and select if there are custom handling with the type of call that can be made. This allows for control of anything that has a distinct request that can be executed so you are not limited to the Vera, Harmony, Nest or other Hue.
The format of these can be the default HTTP request which executes the URLs formatted as `http://<your stuff here>` as a GET. Other options to this are to select the HTTP Verb and add the data type and add a body that is passed with the request. Secure https is supported as well, just use `https://<your secure call here>`. When using POST and PUT, you have the ability to specify the body that will be sent with the request as well as the application type for the http call.
There is a new format for the on/dim/off URL areas. The new editor handles the intricacies of the components, but is broken down here for explanation.
Here are the fields that can be put into the call item:
Json Type | field name | What | Use
----------|------------|------|-----
String or JsonElement | item| This is the payload that will be called for devices | Required
Integer | count | This is how many times this items will be executed | Optional
Integer | delay | This is how long we will wait until the next call after | Optional
String | type | This is the type of device we are executing | Required
String | filterIPs | This is used filter on the IPs given in the list | Optional
String | httpVerb | This is the http command if given, default is GET | Optional
String | httpBody | Send this Body with a PUT or POST | Optional
String | httpHeaders | Send these headers with the http call | Optional
String | contentType | Define the type of content in the body | Optional
Example from device.db:
```
[{"item":<a String that is quoted or another JSON object>,"type":"<atype>"."count":X."delay":X."filterIPs":"<comma separated list of IP addresses that are valid>"."httpVerb":"<GET,PUT,POST>","httpBody":"<body info>","httpHeaders":[{"name":"header name","value":"header value"},{"name":"another header","value":"another value"}],"contentType":"<http content type i.e application/json>"},{"item":<another item>,"type":"<aType>"}]
```
The Add/Edit tab will show you the fields to fill in for the above in a form, when you hcae completed putting in the things you want, make sure to hit the `Add` button at the right.
The format of the item can be the default HTTP request which executes the URLs formatted as `http://<your stuff here>` as a GET. Other options to this are to select the HTTP Verb and add the data type and add a body that is passed with the request. Secure https is supported as well, just use `https://<your secure call here>`. When using POST and PUT, you have the ability to specify the body that will be sent with the request as well as the application type for the http call.
The valid device types are: "custom", "veraDevice", "veraScene", "harmonyActivity", "harmonyButton", "nestHomeAway", "nestThermoSet", "hueDevice", "halDevice",
"halButton", "halHome", "halThermoSet", "mqttMessage", "cmdDevice", "hassDevice", "tcpDevice", "udpDevice", "httpDevice", "domoticzDevice"
Filter Ip example:
```
Turn on Lights in Bedroom 1 (http://api.call.here/1) - Restricted to Echo 1 (10.1.1.1)
Turn on Lights in Bedroom 2 (http://api.call.here/2) - Restricted to Echo 2 (10.2.2.2)
Turn on Lights in Bedroom 3 (http://api.call.here/3) - Restricted to Echo 3 (10.3.3.3)
Device: "Lights"
On URL: [{"item":"http://api.call.here/1", "httpVerb":"POST", "httpBody":"value1=1&value2=2","type":"httpDevice","filterIPs":"10.1.1.1"},{"item":"http://api.call.here/2", "httpVerb":"POST", "httpBody":"value1=1&value2=2","type":"httpDevice","filterIPs":"10.2.2.2"},{"item":"http://api.call.here/3", "httpVerb":"POST", "httpBody":"value1=1&value2=2","type":"httpDevice","filterIPs":"10.3.3.3"}]
```
Headers can be added as well using a Json construct [{"name":"header type name","value":"the header value"}] with the format example:
```
@@ -210,44 +354,43 @@ Headers can be added as well using a Json construct [{"name":"header type name",
Another option that is detected by the bridge is to use UDP or TCP direct calls such as `udp://<ip_address>:<port>/<your stuff here>` to send a UDP request. TCP calls are handled the same way as `tcp://<ip_address>:<port>/<your stuff here>`. If your data for the UDP or TCP request is formatted as "0x00F009B9" lexical hex format, the bridge will convert the data into a binary stream to send.
You can also use the value replacement constructs within these statements. Such as using the expressions ${intensity.percent} for 0-100 or ${intensity.byte} for 0-255 for straight pass through of the value or items that require special calculated values using ${intensity.math()} i.e. "${intensity.math(X/4)}".
You can also use the value replacement constructs within these statements. Such as using the expressions "${time.format(Java time format string)}" for inserting a date/time stamp, ${intensity.percent} for 0-100 or ${intensity.byte} for 0-255 for straight pass through of the value or items that require special calculated values using ${intensity.math()} i.e. "${intensity.math(X/4)}". See Value Passing Controls Below.
Examples:
```
GET
http://192.168.1.1:8180/set/this/value/${intensity.percent}
PUT
http://192.168.1.1:8280/set/this
ContentBody: {"someValue":"${intensity.byte}"}
[{"item":"http://192.168.1.1:8180/set/this/value/${intensity.percent}","type":"httpDevice","httpVerb":"GET"}]
udp://192.168.1.1:5000/0x45${intensity.percent}55
udp://192.168.2.2:6000/fireoffthismessage\n
[{"item":"http://192.168.1.1:8280/set/this","type":"httpDevice","httpVerb":"PUT","httpBody":{"someValue":"${intensity.byte}"}}]
tcp://192.168.3.3:9000/sendthismessage
[{"item":"udp://192.168.1.1:5000/0x45${intensity.percent}55","type":"udpDevice"}]
tcp://192.168.4.4:10000/0x435f12dd${intensity.math((X -4)*50)}438c
[{"item":"udp://192.168.2.2:6000/fireoffthismessage\n","type":"udpDevice"}]
tcp://192.168.5.5:110000/0x
[{"item":"tcp://192.168.3.3:9000/sendthismessage","type":"tcpDevice"}]
[{"item":"tcp://192.168.4.4:10000/0x435f12dd${intensity.math((X -4)*50)}438c","type":"tcpDevice"}]
[{"item":"tcp://192.168.5.5:110000/0x","type":"tcpDevice"}]
```
#### Multiple Call Construct
Also available is the ability to specify multiple commands in the On URL, Dim URL and Off URL areas by adding Json constructs listed here. This is only for the types of tcp, udp, http, https or a new exec type. Also within the item format you can specify delay in milliseconds and count per item. These new paramters work on device buttons for the Harmony as well.
Also available is the ability to specify multiple commands in the On URL, Dim URL and Off URL areas by adding Json constructs listed here. This is only for the types of tcp, udp, http, https or a new exec type. Also within the item format you can specify delay in milliseconds and count per item. These new parameters work on device buttons for the Harmony as well.
Format Example in the URL areas:
```
[{"item":"http://192.168.1.1:8180/do/this/thing"},
{"item":"http://192.168.1.1:8180/do/the/next/thing","delay":1000,"count":2},
{"item":"http://192.168.1.1:8180/do/another/thing"}]
[{"item":"http://192.168.1.1:8180/do/this/thing","type":"httpDevice"},
{"item":"http://192.168.1.1:8180/do/the/next/thing","delay":1000,"count":2,"type":"httpDevice"},
{"item":"http://192.168.1.1:8180/do/another/thing","type":"httpDevice"}]
[{"item":"udp://192.168.1.1:5000/0x450555"},
{"item":"udp://192.168.1.1:5000/0x45${intensity.percent}55"}]
[{"item":"udp://192.168.1.1:5000/0x450555","type":"udpDevice"},
{"item":"udp://192.168.1.1:5000/0x45${intensity.percent}55","type":"udpDevice"}]
[{"item":"udp://192.168.1.1:5000/0x450555"},
{"item":"http://192.168.1.1:8180/do/this/thing"},
{"item":"tcp://192.168.2.1/sendthisdata"},
{"item":"https://192.168.12.1/do/this/secure/thing"},
{"item":"exec://notepad.exe"}]
[{"item":"udp://192.168.1.1:5000/0x450555","type":"udpDevice"},
{"item":"http://192.168.1.1:8180/do/this/thing","type":"httpDevice"},
{"item":"tcp://192.168.2.1/sendthisdata","type":"tcpDevice"},
{"item":"https://192.168.12.1/do/this/secure/thing","type":"httpDevice"},
{"item":"exec://notepad.exe","type":"cmdDevice"}]
```
#### Script or Command Execution
The release as of v2.0.0 will now support the execution of a local script or program. This will blindly fire off a process to run and is bound by the privileges of the java process.
@@ -256,20 +399,27 @@ To configure this type of manual add, you will need to select the Device type of
In the URL areas, the format of the execution is just providing what command line you would like to run, or using the multiple call item construct described above.
```
notepad.exe
[{"item":"C:\\Users\\John\\Documents\\Applications\\putty.exe 192.168.1.1","type":"cmdDevice"},
{"item":"notepad.exe","type":"cmdDevice"}]
OR
[{"item":"C:\\Users\\John\\Documents\\Applications\\putty.exe 192.168.1.1"},
{"item":"notepad.exe"}]
[{"item":"exec://notepad.exe","type":"cmdDevice"}]
OR
```
#### Value Passing Controls
There are multiple replacement constructs available to be put into any of the calls except Harmony items, Net Items and HAL items. These constructs are: "${time.format(Java time format string)}", "${intensity.percent}", "${intensity.byte}" and "${intensity.math(using X in your calc)}".
You can control items that require special calculated values using ${intensity.math(<your expression using "X" as the value to operate on>)} i.e. "${intensity.math(X/4)}".
For the items that want to have a date time put into the message, utilize ${time.format(yyyy-MM-ddTHH:mm:ssXXX)} where "yyyy-MM-ddTHH:mm:ssXXX" can be any format from the Java SimpleDateFormat documented here: https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html
e.g.
```
[{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=10&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.math(X/4)}","type":"httpDevice"}]
/home/me/startsomething.sh
[{"item":"udp://192.168.1.1:5000/0x45${intensity.percent}55","type":"udpDevice"}]
OR
[{"item":"tcp://192.168.1.1:5000/This is the intensity real value ${intensity.byte}","type":"tcpDevice"}]
[{"item":"exec://notepad.exe"}]
[{"item":{"clientId":"TestClient","topic":"Yep","message":"This is the time ${time.format(yyyy-MM-ddTHH:mm:ssXXX)}"},"type":"mqttDevice"}]
```
@@ -363,8 +513,8 @@ contentBodyOff | string | This is the content body that you would like to send w
{
"name" : "bedroom light",
"deviceType" : "switch",
"onUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41",
"offUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41"
"onUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41","type":"veraDevice"}],
"offUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41","type":"veraDevice"}]
}
```
#### Dimming Control Example
@@ -374,8 +524,8 @@ e.g.
{
"name": "entry light",
"deviceType": "switch",
"offUrl": "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=31",
"onUrl": "http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=31&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.percent}"
"offUrl": [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=31","type":"veraDevice"}],
"onUrl": [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=31&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.percent}","type":"veraDevice"}]
}
```
See the echo's documentation for the dimming phrase.
@@ -387,8 +537,8 @@ e.g.
{
"name": "Thermostat,
"deviceType": "custom",
"offUrl": "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=10",
"onUrl": "http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=10&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.math(X/4)}"
"offUrl": [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=10","type":"veraDevice"}],
"onUrl": [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=10&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.math(X/4)}","type":"veraDevice"}]
}
```
See the echo's documentation for the dimming phrase.
@@ -400,12 +550,8 @@ e.g:
{
"name": "test device",
"deviceType": "custom",
"offUrl": "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=31",
"onUrl": "http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=31&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.percent}",
"httpVerb":"POST",
"contentType" : "application/json",
"contentBody" : "{\"fooBar\":\"baz_on\"}"
"contentBodyOff" : "{\"fooBar\":\"baz_off\"}"
"offUrl": [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=31","httpVerb":"POST","contentType" : "application/json","httpBody" : "{\"fooBar\":\"baz_off\"}],
"onUrl": [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=31&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.percent}","type":"httpDevice","httpVerb":"POST","contentType" : "application/json","httpBody" : "{\"fooBar\":\"baz_on\"}]
}
```
#### Custom Usage URLs Example
@@ -414,8 +560,8 @@ Anything that takes an action as a result of an HTTP request will probably work
{
"name": "night mode",
"deviceType": ""custom",
"offUrl": "http://192.168.1.201:3480/data_request?id=lu_action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=SetHouseMode&Mode=1",
"onUrl": "http://192.168.1.201:3480/data_request?id=lu_action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=SetHouseMode&Mode=3"
"offUrl": [{"item":"http://192.168.1.201:3480/data_request?id=lu_action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=SetHouseMode&Mode=1","type":"httpDevice"}],
"onUrl": [{"item":"http://192.168.1.201:3480/data_request?id=lu_action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=SetHouseMode&Mode=3","type":"httpDevice"}]
}
```
Here is a UDP example that can send binary data.
@@ -423,8 +569,8 @@ Here is a UDP example that can send binary data.
{
"name": "UDPPacket",
"deviceType": "custom",
"offUrl": "udp://192.168.1.1:8899/0x460055",
"onUrl": "udp://192.168.1.1:8899/0x450055"
"offUrl": [{"item":"udp://192.168.1.1:8899/0x460055","type":"udpDevice"}],
"onUrl": [{"item":"udp://192.168.1.1:8899/0x450055","type":"udpDevice"}]
}
```
#### Response
@@ -449,12 +595,12 @@ contentBodyOff | string | This is the content body that you would like to send w
"id" : "12345",
"name" : "bedroom light",
"deviceType" : "switch",
"onUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41",
"offUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41"
"onUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41","type":"veraDevice"}],
"offUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41","type":"veraDevice"}]
}
```
### Update a Device
Update an existing device using it's ID that was given when the device was created and the update could contain any of the fields that are used and shown in the previous examples when adding a device.
Update an existing device using its ID that was given when the device was created and the update could contain any of the fields that are used and shown in the previous examples when adding a device.
**Note: You must supply all fields of the device in return as this is a replacement update for the given id.**
```
@@ -470,7 +616,7 @@ mapType | string | This identifies what type of source item was used from the he
deviceType | string | This identifies what type of device entry this is. It is used by the system and should be the values of "switch", "scene", "custom", "activity", "button", "thermo", "passthru", "exec", "UDP", "TCP" or "custom". | Required
targetDevice | string | A name given to the target when there are multiples of a given type in the configuration | Optional
onUrl | string | This is the URL or Data Description that is executed for an "on" request. | Required
dimUrl | string | This is the URL or Data Description that is executed for an "on" request when a intensity value is sent. | Optional
dimUrl | string | This is the URL or Data Description that is executed for an "on" request when an intensity value is sent. | Optional
offUrl | string | This is the URL or Data Description that is executed for an "off" request. | Optional
headers | string | This is a header or list of headers that is used for http/https calls when given. | Optional
httpVerb | string | This is used for "custom" calls that the user would like to execute. The values can only be "GET, "PUT", "POST". | Optional
@@ -483,8 +629,8 @@ contentBodyOff | string | This is the content body that you would like to send w
"id" : "6789",
"name" : "table light",
"deviceType" : "switch",
"onUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41",
"offUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41"
"onUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41","type":"veraDevice"}],
"offUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41","type":"veraDevice"}]
}
```
#### Response
@@ -493,8 +639,8 @@ contentBodyOff | string | This is the content body that you would like to send w
"id" : "6789",
"name" : "table light",
"deviceType" : "switch",
"onUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41",
"offUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41"
"onUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41","type":"veraDevice"}],
"offUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41","type":"veraDevice"}]
}
```
### Get All Devices
@@ -509,15 +655,15 @@ Individual entries are the same as a single device but in json list format.
"id" : "12345",
"name" : "bedroom light",
"deviceType" : "switch",
"onUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41",
"offUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41"
"onUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41","type":"veraDevice"}],
"offUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41","type":"veraDevice"}]
}
{
"id" : "6789",
"name" : "table light",
"deviceType" : "switch",
"onUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41",
"offUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41"
"onUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41","type":"veraDevice"}],
"offUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41","type":"veraDevice"}]
}]
```
### Get a Specific Device
@@ -532,8 +678,8 @@ The response is the same layout as defined in the add device response.
"id" : "6789",
"name" : "table light",
"deviceType" : "switch",
"onUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41",
"offUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41"
"onUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=41","type":"veraDevice"}],
"offUrl" : [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41","type":"veraDevice"}]
}
```
### Delete a Specific Device
@@ -696,7 +842,7 @@ Show the Harmony Hub's current activity.
GET http://host:port/api/devices/harmony/show
```
#### Response
Only listing the relevant fields that are needed for identity of an activity. TThe example below is representative of an activity.
Only listing the relevant fields that are needed for identity of an activity. The example below is representative of an activity.
Name | Type | Description
-----|-------|-------------
@@ -860,7 +1006,7 @@ Allows the user to set the internal state of the light on and off, modify the br
PUT http://host:port/api/<username>/lights/<id>/bridgeupdatestate
```
#### Body arguments
These are examples that can be used in the control of items iwthin the bridge, but for HUE passthru devices, the complete state object is sent.
These are examples that can be used in the control of items within the bridge, but for HUE passthru devices, the complete state object is sent.
Name | Type | Description
-----|-------|-------------
on | bool | On/Off state of the light. On=true, Off=false. Optional
@@ -1032,7 +1178,7 @@ Note that `192.168.1.1` and `12345` are replaced with the actual IP address and
### UPNP description service
The bridge provides the description service which is used by the calling app to interogate access details after it has decided the upnp multicast response is the correct device.
The bridge provides the description service which is used by the calling app to interrogate access details after it has decided the upnp multicast response is the correct device.
#### Get Description
```
GET http://host:80/description.xml
@@ -1087,7 +1233,7 @@ GET http://host:80/description.xml
</root>\n
```
## Development Mode
To turn on development mode so that it will not need an Harmony Hub for testing, use the following extra parm in the command line and the harmony ip and login info will not be needed:
To turn on development mode so that it will not need a Harmony Hub for testing, use the following extra parameter in the command line and the harmony ip and login info will not be needed:
```
java -jar -Ddev.mode=true ha-bridge-0.X.Y.jar
```

View File

@@ -5,7 +5,7 @@
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>4.0.0</version>
<version>4.1.2</version>
<packaging>jar</packaging>
<name>HA Bridge</name>
@@ -217,4 +217,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View File

@@ -134,16 +134,10 @@ public class BridgeSettings extends BackupHandler {
if(theBridgeSettings.getUpnpDeviceDb() == null)
theBridgeSettings.setUpnpDeviceDb(Configuration.DEVICE_DB_DIRECTORY);
if(theBridgeSettings.getNumberoflogmessages() == null)
if(theBridgeSettings.getNumberoflogmessages() == null || theBridgeSettings.getNumberoflogmessages() <= 0)
theBridgeSettings.setNumberoflogmessages(new Integer(Configuration.NUMBER_OF_LOG_MESSAGES));
if(theBridgeSettings.getNumberoflogmessages() <= 0)
theBridgeSettings.setNumberoflogmessages(new Integer(Configuration.NUMBER_OF_LOG_MESSAGES));
if(theBridgeSettings.getButtonsleep() == null)
theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP));
if(theBridgeSettings.getButtonsleep() < 0)
if(theBridgeSettings.getButtonsleep() == null || theBridgeSettings.getButtonsleep() < 0)
theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP));
theBridgeSettings.setVeraconfigured(theBridgeSettings.isValidVera());
@@ -153,7 +147,8 @@ public class BridgeSettings extends BackupHandler {
theBridgeSettings.setHalconfigured(theBridgeSettings.isValidHal());
theBridgeSettings.setMqttconfigured(theBridgeSettings.isValidMQTT());
theBridgeSettings.setHassconfigured(theBridgeSettings.isValidHass());
if(serverPortOverride != null)
theBridgeSettings.setDomoticzconfigured(theBridgeSettings.isValidDomoticz());
if(serverPortOverride != null)
theBridgeSettings.setServerPort(serverPortOverride);
if(serverIpOverride != null)
theBridgeSettings.setWebaddress(serverIpOverride);

View File

@@ -38,6 +38,8 @@ public class BridgeSettingsDescriptor {
private IpList hassaddress;
private boolean hassconfigured;
private String hubversion;
private IpList domoticzaddress;
private boolean domoticzconfigured;
public BridgeSettingsDescriptor() {
super();
@@ -249,6 +251,18 @@ public class BridgeSettingsDescriptor {
public void setHubversion(String hubversion) {
this.hubversion = hubversion;
}
public IpList getDomoticzaddress() {
return domoticzaddress;
}
public void setDomoticzaddress(IpList domoticzaddress) {
this.domoticzaddress = domoticzaddress;
}
public boolean isDomoticzconfigured() {
return domoticzconfigured;
}
public void setDomoticzconfigured(boolean domoticzconfigured) {
this.domoticzconfigured = domoticzconfigured;
}
public Boolean isValidVera() {
if(this.getVeraAddress() == null || this.getVeraAddress().getDevices().size() <= 0)
return false;
@@ -306,4 +320,12 @@ public class BridgeSettingsDescriptor {
return false;
return true;
}
public Boolean isValidDomoticz() {
if(this.getDomoticzaddress() == null || this.getDomoticzaddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getDomoticzaddress().getDevices();
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
}

View File

@@ -25,6 +25,7 @@ public class DeviceMapTypes {
public final static String[] UDP_DEVICE = { "udpDevice", "UDP Device"};
public final static String[] UDP_DEVICE_COMPAT = { "UDP", "UDP Device"};
public final static String[] HTTP_DEVICE = { "httpDevice", "HTTP Device"};
public final static String[] DOMOTICZ_DEVICE = { "domoticzDevice", "Domoticz Device"};
public final static int typeIndex = 0;
public final static int displayIndex = 1;
@@ -35,10 +36,13 @@ public class DeviceMapTypes {
super();
deviceMapTypes = new ArrayList<String[]>();
deviceMapTypes.add(CMD_DEVICE);
deviceMapTypes.add(DOMOTICZ_DEVICE);
deviceMapTypes.add(HAL_DEVICE);
deviceMapTypes.add(HAL_HOME);
deviceMapTypes.add(HAL_THERMO_SET);
deviceMapTypes.add(HAL_BUTTON);
deviceMapTypes.add(HARMONY_ACTIVITY);
deviceMapTypes.add(HARMONY_BUTTON);
deviceMapTypes.add(HASS_DEVICE);
deviceMapTypes.add(HTTP_DEVICE);
deviceMapTypes.add(HUE_DEVICE);
@@ -49,8 +53,6 @@ public class DeviceMapTypes {
deviceMapTypes.add(UDP_DEVICE);
deviceMapTypes.add(VERA_DEVICE);
deviceMapTypes.add(VERA_SCENE);
deviceMapTypes.add(HARMONY_ACTIVITY);
deviceMapTypes.add(HARMONY_BUTTON);
}
public static int getTypeIndex() {
return typeIndex;

View File

@@ -5,6 +5,7 @@ import java.util.Map;
import com.bwssystems.HABridge.devicemanagmeent.ResourceHandler;
import com.bwssystems.HABridge.plugins.NestBridge.NestHome;
import com.bwssystems.HABridge.plugins.domoticz.DomoticzHome;
import com.bwssystems.HABridge.plugins.exec.CommandHome;
import com.bwssystems.HABridge.plugins.hal.HalHome;
import com.bwssystems.HABridge.plugins.harmony.HarmonyHome;
@@ -69,6 +70,7 @@ public class HomeManager {
homeList.put(DeviceMapTypes.CUSTOM_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the tcp handler Home
aHome = new TCPHome(bridgeSettings);
homeList.put(DeviceMapTypes.TCP_DEVICE[DeviceMapTypes.typeIndex], aHome);
@@ -81,6 +83,9 @@ public class HomeManager {
aHome = new VeraHome(bridgeSettings);
resourceList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
resourceList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
//setup the HomeAssistant configuration if available
aHome = new DomoticzHome(bridgeSettings);
resourceList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
}
public Home findHome(String type) {

View File

@@ -6,6 +6,7 @@ public class NamedIP {
private String port;
private String username;
private String password;
private Boolean secure;
public String getName() {
return name;
@@ -37,4 +38,10 @@ public class NamedIP {
public void setPassword(String password) {
this.password = password;
}
public Boolean getSecure() {
return secure;
}
public void setSecure(Boolean secure) {
this.secure = secure;
}
}

View File

@@ -3,11 +3,20 @@ package com.bwssystems.HABridge.api.hue;
import java.util.List;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.google.gson.annotations.SerializedName;
public class GroupResponse {
@SerializedName("action")
private DeviceState action;
@SerializedName("lights")
private String[] lights;
@SerializedName("name")
private String name;
@SerializedName("type")
private String type;
@SerializedName("class")
String class_name;
public DeviceState getAction() {
return action;
}
@@ -27,7 +36,19 @@ public class GroupResponse {
this.name = name;
}
public static GroupResponse createGroupResponse(List<DeviceDescriptor> deviceList) {
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getClass_name() {
return class_name;
}
public void setClass_name(String class_name) {
this.class_name = class_name;
}
public static GroupResponse createDefaultGroupResponse(List<DeviceDescriptor> deviceList) {
String[] theList = new String[deviceList.size()];
int i = 0;
for (DeviceDescriptor device : deviceList) {
@@ -38,6 +59,23 @@ public class GroupResponse {
theResponse.setAction(DeviceState.createDeviceState());
theResponse.setName("Lightset 0");
theResponse.setLights(theList);
theResponse.setType("LightGroup");
return theResponse;
}
public static GroupResponse createOtherGroupResponse(List<DeviceDescriptor> deviceList) {
String[] theList = new String[deviceList.size()];
int i = 0;
for (DeviceDescriptor device : deviceList) {
theList[i] = device.getId();
i++;
}
GroupResponse theResponse = new GroupResponse();
theResponse.setAction(DeviceState.createDeviceState());
theResponse.setName("AGroup");
theResponse.setLights(theList);
theResponse.setType("Room");
theResponse.setClass_name("Other");
return theResponse;
}
}

View File

@@ -12,7 +12,7 @@ import com.google.gson.JsonObject;
public class HueApiResponse {
private Map<String, DeviceResponse> lights;
private Map<String, JsonObject> scenes;
private Map<String, JsonObject> groups;
private Map<String, GroupResponse> groups;
private Map<String, JsonObject> schedules;
private Map<String, JsonObject> sensors;
private Map<String, JsonObject> rules;
@@ -44,11 +44,11 @@ public class HueApiResponse {
this.scenes = scenes;
}
public Map<String, JsonObject> getGroups() {
public Map<String, GroupResponse> getGroups() {
return groups;
}
public void setGroups(Map<String, JsonObject> groups) {
public void setGroups(Map<String, GroupResponse> groups) {
this.groups = groups;
}

View File

@@ -1,7 +1,7 @@
package com.bwssystems.HABridge.api.hue;
public class HueConstants {
public final static String HUB_VERSION = "01036562";
public final static String HUB_VERSION = "01036659";
public final static String API_VERSION = "1.15.0";
public final static String MODEL_ID = "BSB002";
public final static String UUID_PREFIX = "2f402f80-da50-11e1-9b23-";

View File

@@ -56,6 +56,12 @@ public class DeviceDescriptor{
@SerializedName("contentBodyDim")
@Expose
private String contentBodyDim;
@SerializedName("inactive")
@Expose
private boolean inactive;
@SerializedName("noState")
@Expose
private boolean noState;
private DeviceState deviceState;
@@ -197,4 +203,41 @@ public class DeviceDescriptor{
this.deviceState = deviceState;
}
}
public boolean isInactive() {
return inactive;
}
public void setInactive(boolean inactive) {
this.inactive = inactive;
}
public boolean isNoState() {
return noState;
}
public void setNoState(boolean noState) {
this.noState = noState;
}
public boolean containsType(String aType) {
if(aType == null)
return false;
if(this.mapType != null && this.mapType.contains(aType))
return true;
if(this.deviceType != null && this.deviceType.contains(aType))
return true;
if(this.onUrl != null && this.onUrl.contains(aType))
return true;
if(this.dimUrl != null && this.dimUrl.contains(aType))
return true;
if(this.offUrl != null && this.offUrl.contains(aType))
return true;
return false;
}
}

View File

@@ -74,6 +74,15 @@ public class DeviceRepository extends BackupHandler {
return list;
}
public List<DeviceDescriptor> findActive() {
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>();
for(DeviceDescriptor aDevice : new ArrayList<DeviceDescriptor>(devices.values())) {
if(!aDevice.isInactive())
list.add(aDevice);
}
return list;
}
public DeviceDescriptor findOne(String id) {
return devices.get(id);
}

View File

@@ -213,6 +213,12 @@ public class DeviceResource {
return homeManager.findResource(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/domoticz/devices", "application/json", (request, response) -> {
log.debug("Get Domoticz Clients");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/map/types", "application/json", (request, response) -> {
log.debug("Get map types");
return new DeviceMapTypes().getDeviceMapTypes();

View File

@@ -26,7 +26,7 @@ public class BrightnessDecode {
} else if (targetBriInc != null) {
if ((setIntensity + targetBriInc) <= 0)
setIntensity = targetBriInc;
else if ((setIntensity + targetBriInc) > 255)
else if ((setIntensity + targetBriInc) > 254)
setIntensity = targetBriInc;
else
setIntensity = setIntensity + targetBriInc;
@@ -38,7 +38,7 @@ public class BrightnessDecode {
* light weight templating here, was going to use free marker but it was a
* bit too heavy for what we were trying to do.
*
* currently provides: intensity.byte : 0-255 brightness. this is raw from
* currently provides: intensity.byte : 0-254 brightness. this is raw from
* the echo intensity.percent : 0-100, adjusted for the vera
* intensity.math(X*1) : where X is the value from the interface call and
* can use net.java.dev.eval math

View File

@@ -9,6 +9,7 @@ import com.bwssystems.HABridge.api.hue.DeviceResponse;
import com.bwssystems.HABridge.api.hue.DeviceState;
import com.bwssystems.HABridge.api.hue.GroupResponse;
import com.bwssystems.HABridge.api.hue.HueApiResponse;
import com.bwssystems.HABridge.api.hue.HueConfig;
import com.bwssystems.HABridge.api.hue.HueError;
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
import com.bwssystems.HABridge.api.hue.HuePublicConfig;
@@ -73,16 +74,34 @@ public class HueMulator {
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json");
response.status(HttpStatus.SC_OK);
return basicListHandler("groups", request.params(":userid"), request.ip());
});
return groupsListHandler(request.params(":userid"), request.ip());
} , new JsonTransformer());
// http://ip_address:port/api/{userId}/groups/{groupId} returns json
// object for specified group. Only 0 is supported
get(HUE_CONTEXT + "/:userid/groups/:groupid", "application/json", (request, response) -> {
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json");
response.status(HttpStatus.SC_OK);
return groupsListHandler(request.params(":groupid"), request.params(":userid"), request.ip());
return groupsIdHandler(request.params(":groupid"), request.params(":userid"), request.ip());
} , new JsonTransformer());
// http://ip_address:port/:userid/groups CORS request
options(HUE_CONTEXT + "/:userid/groups", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html");
return "";
});
// http://ip_address:port/:userid/groups
// dummy handler
post(HUE_CONTEXT + "/:userid/groups", "application/json", (request, response) -> {
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json");
response.status(HttpStatus.SC_OK);
log.debug("group add requested from " + request.ip() + " user " + request.params(":userid") + " with body " + request.body());
return "[{\"success\":{\"id\":\"1\"}}]";
});
// http://ip_address:port/api/{userId}/scenes returns json objects of
// all scenes configured
get(HUE_CONTEXT + "/:userid/scenes", "application/json", (request, response) -> {
@@ -91,6 +110,24 @@ public class HueMulator {
response.status(HttpStatus.SC_OK);
return basicListHandler("scenes", request.params(":userid"), request.ip());
});
// http://ip_address:port/:userid/scenes CORS request
options(HUE_CONTEXT + "/:userid/scenes", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html");
return "";
});
// http://ip_address:port/:userid/scenes
// dummy handler
post(HUE_CONTEXT + "/:userid/scenes", "application/json", (request, response) -> {
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json");
response.status(HttpStatus.SC_OK);
log.debug("scene add requested from " + request.ip() + " user " + request.params(":userid") + " with body " + request.body());
return "[{\"success\":{\"id\":\"1\"}}]";
});
// http://ip_address:port/api/{userId}/schedules returns json objects of
// all schedules configured
get(HUE_CONTEXT + "/:userid/schedules", "application/json", (request, response) -> {
@@ -99,6 +136,24 @@ public class HueMulator {
response.status(HttpStatus.SC_OK);
return basicListHandler("schedules", request.params(":userid"), request.ip());
});
// http://ip_address:port/:userid/schedules CORS request
options(HUE_CONTEXT + "/:userid/schedules", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html");
return "";
});
// http://ip_address:port/:userid/schedules
// dummy handler
post(HUE_CONTEXT + "/:userid/schedules", "application/json", (request, response) -> {
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json");
response.status(HttpStatus.SC_OK);
log.debug("schedules add requested from " + request.ip() + " user " + request.params(":userid") + " with body " + request.body());
return "[{\"success\":{\"id\":\"1\"}}]";
});
// http://ip_address:port/api/{userId}/sensors returns json objects of
// all sensors configured
get(HUE_CONTEXT + "/:userid/sensors", "application/json", (request, response) -> {
@@ -107,6 +162,24 @@ public class HueMulator {
response.status(HttpStatus.SC_OK);
return basicListHandler("sensors", request.params(":userid"), request.ip());
});
// http://ip_address:port/:userid/sensors CORS request
options(HUE_CONTEXT + "/:userid/sensors", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html");
return "";
});
// http://ip_address:port/:userid/sensors
// dummy handler
post(HUE_CONTEXT + "/:userid/sensors", "application/json", (request, response) -> {
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json");
response.status(HttpStatus.SC_OK);
log.debug("sensors add requested from " + request.ip() + " user " + request.params(":userid") + " with body " + request.body());
return "[{\"success\":{\"id\":\"1\"}}]";
});
// http://ip_address:port/api/{userId}/rules returns json objects of all
// rules configured
get(HUE_CONTEXT + "/:userid/rules", "application/json", (request, response) -> {
@@ -115,6 +188,24 @@ public class HueMulator {
response.status(HttpStatus.SC_OK);
return basicListHandler("rules", request.params(":userid"), request.ip());
});
// http://ip_address:port/:userid/rules CORS request
options(HUE_CONTEXT + "/:userid/rules", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html");
return "";
});
// http://ip_address:port/:userid/rules
// dummy handler
post(HUE_CONTEXT + "/:userid/rules", "application/json", (request, response) -> {
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json");
response.status(HttpStatus.SC_OK);
log.debug("rules add requested from " + request.ip() + " user " + request.params(":userid") + " with body " + request.body());
return "[{\"success\":{\"id\":\"1\"}}]";
});
// http://ip_address:port/api/{userId}/resourcelinks returns json
// objects of all resourcelinks configured
get(HUE_CONTEXT + "/:userid/resourcelinks", "application/json", (request, response) -> {
@@ -123,6 +214,24 @@ public class HueMulator {
response.status(HttpStatus.SC_OK);
return basicListHandler("resourcelinks", request.params(":userid"), request.ip());
});
// http://ip_address:port/:userid/resourcelinks CORS request
options(HUE_CONTEXT + "/:userid/resourcelinks", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html");
return "";
});
// http://ip_address:port/:userid/resourcelinks
// dummy handler
post(HUE_CONTEXT + "/:userid/resourcelinks", "application/json", (request, response) -> {
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json");
response.status(HttpStatus.SC_OK);
log.debug("resourcelinks add requested from " + request.ip() + " user " + request.params(":userid") + " with body " + request.body());
return "[{\"success\":{\"id\":\"1\"}}]";
});
// http://ip_address:port/api/{userId}/lights returns json objects of
// all lights configured
get(HUE_CONTEXT + "/:userid/lights", "application/json", (request, response) -> {
@@ -194,6 +303,28 @@ public class HueMulator {
response.status(HttpStatus.SC_OK);
return getConfig(request.params(":userid"), request.ip());
} , new JsonTransformer());
// http://ip_address:port/:userid/config CORS request
options(HUE_CONTEXT + "/:userid/config", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html");
return "";
});
// http://ip_address:port/:userid/config uses json
// object to set the config. this is to handle swupdates
put(HUE_CONTEXT + "/:userid/config", "application/json", (request, response) -> {
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json");
response.status(HttpStatus.SC_OK);
log.debug("Config change requested from " + request.ip() + " user " + request.params(":userid") + " with body " + request.body());
HueConfig aConfig = aGsonHandler.fromJson(request.body(), HueConfig.class);
if(aConfig.getPortalservices() != null) {
return "[{\"success\":{\"/config/portalservices\":true}}]";
}
return "[{\"success\":{\"/config/name\":\"My bridge\"}}]";
});
// http://ip_address:port/api/{userId} returns json objects for the full
// state
@@ -276,7 +407,7 @@ public class HueMulator {
}
if (deviceState != null) {
deviceState.setOn(stateChanges.isOn());
if(!deviceState.isOn() && deviceState.getBri() == 255)
if(!deviceState.isOn() && deviceState.getBri() == 254)
deviceState.setBri(0);
}
notFirstChange = true;
@@ -416,7 +547,7 @@ public class HueMulator {
}
if(deviceState.isOn() && deviceState.getBri() <= 0)
deviceState.setBri(255);
deviceState.setBri(254);
if(!deviceState.isOn() && (targetBri != null || targetBriInc != null))
deviceState.setOn(true);
@@ -463,7 +594,7 @@ public class HueMulator {
}
if (!found) {
log.debug("Valudate user, No User supplied");
log.debug("Validate user, No User supplied");
return HueErrorResponse.createResponse("1", "/api/" + aUser, "unauthorized user", null, null, null).getTheErrors();
}
@@ -496,14 +627,32 @@ public class HueMulator {
return "{}";
}
private Object groupsListHandler(String userId, String requestIp) {
log.debug("hue group list requested: " + userId + " from " + requestIp);
HueError[] theErrors = null;
Map<String, GroupResponse> groupResponseMap = null;
theErrors = validateWhitelistUser(userId, false);
if (theErrors == null) {
groupResponseMap = new HashMap<String, GroupResponse>();
groupResponseMap.put("1", (GroupResponse) this.groupsIdHandler("1", userId, requestIp));
return groupResponseMap;
}
private Object groupsListHandler(String groupId, String userId, String requestIp) {
log.debug("hue group 0 list requested: " + userId + " from " + requestIp);
return theErrors;
}
private Object groupsIdHandler(String groupId, String userId, String requestIp) {
log.debug("hue group id: <" + groupId + "> requested: " + userId + " from " + requestIp);
HueError[] theErrors = null;
theErrors = validateWhitelistUser(userId, false);
if (theErrors == null) {
if (groupId.equalsIgnoreCase("0")) {
GroupResponse theResponse = GroupResponse.createGroupResponse(repository.findAll());
GroupResponse theResponse = GroupResponse.createDefaultGroupResponse(repository.findActive());
return theResponse;
}
if (!groupId.equalsIgnoreCase("0")) {
GroupResponse theResponse = GroupResponse.createOtherGroupResponse(repository.findActive());
return theResponse;
}
theErrors = HueErrorResponse.createResponse("3", userId + "/groups/" + groupId, "Object not found", null, null, null).getTheErrors();
@@ -520,13 +669,27 @@ public class HueMulator {
log.debug("hue lights list requested: " + userId + " from " + requestIp);
theErrors = validateWhitelistUser(userId, false);
if (theErrors == null) {
List<DeviceDescriptor> deviceList = repository.findAll();
List<DeviceDescriptor> deviceList = repository.findActive();
deviceResponseMap = new HashMap<String, DeviceResponse>();
for (DeviceDescriptor device : deviceList) {
DeviceResponse deviceResponse = null;
if ((device.getMapType() != null && device.getMapType().equalsIgnoreCase(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex]))) {
HueDeviceIdentifier deviceId = aGsonHandler.fromJson(device.getOnUrl(), HueDeviceIdentifier.class);
deviceResponse = myHueHome.getHueDeviceInfo(deviceId, device);
if (device.containsType(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
CallItem[] callItems = null;
try {
if(device.getOnUrl() != null)
callItems = aGsonHandler.fromJson(device.getOnUrl(), CallItem[].class);
} catch(JsonSyntaxException e) {
log.warn("Could not decode Json for url items to get Hue state for device: " + device.getName());
callItems = null;
}
for (int i = 0; callItems != null && i < callItems.length; i++) {
if((callItems[i].getType() != null && callItems[i].getType().equals(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) ||
(callItems[i].getItem().getAsString().contains("hueName"))) {
deviceResponse = myHueHome.getHueDeviceInfo(callItems[i], device);
i = callItems.length;
}
}
}
if (deviceResponse == null)
@@ -551,7 +714,13 @@ public class HueMulator {
log.debug("hue api user create requested: " + body + " from " + ipAddress);
if (body != null && !body.isEmpty()) {
aNewUser = aGsonHandler.fromJson(body, UserCreateRequest.class);
try {
aNewUser = aGsonHandler.fromJson(body, UserCreateRequest.class);
} catch (Exception e) {
log.warn("Could not add user. Request garbled: " + body);
return aGsonHandler.toJson(HueErrorResponse.createResponse("2", "/",
"Could not add user.", null, null, null).getTheErrors(), HueError[].class);
}
newUser = aNewUser.getUsername();
aDeviceType = aNewUser.getDevicetype();
}
@@ -575,7 +744,7 @@ public class HueMulator {
log.info("Traceupnp: hue api/:userid/config config requested: " + userId + " from " + ipAddress);
log.debug("hue api config requested: " + userId + " from " + ipAddress);
if (validateWhitelistUser(userId, true) != null) {
log.debug("Valudate user, No User supplied, returning public config");
log.debug("hue api config requested, No User supplied, returning public config");
HuePublicConfig apiResponse = HuePublicConfig.createConfig("Philips hue",
bridgeSettings.getUpnpConfigAddress(), bridgeSettings.getHubversion());
return apiResponse;
@@ -596,15 +765,8 @@ public class HueMulator {
HueApiResponse apiResponse = new HueApiResponse("Philips hue", bridgeSettings.getUpnpConfigAddress(),
bridgeSettings.getWhitelist(), bridgeSettings.getHubversion());
Object aReturn = this.lightsListHandler(userId, ipAddress);
Map<String, DeviceResponse> deviceList = new HashMap<String, DeviceResponse>();
if(aReturn.getClass() == deviceList.getClass()) {
deviceList = (Map<String, DeviceResponse>) aReturn;
apiResponse.setLights(deviceList);
}
else {
return aReturn;
}
apiResponse.setLights((Map<String, DeviceResponse>) this.lightsListHandler(userId, ipAddress));
apiResponse.setGroups((Map<String, GroupResponse>) this.groupsListHandler(userId, ipAddress));
return apiResponse;
}
@@ -623,10 +785,25 @@ public class HueMulator {
log.debug("found device named: " + device.getName());
}
DeviceResponse lightResponse = null;
if ((device.getMapType() != null && device.getMapType().equalsIgnoreCase(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex]))) {
HueDeviceIdentifier deviceId = aGsonHandler.fromJson(device.getOnUrl(), HueDeviceIdentifier.class);
lightResponse = myHueHome.getHueDeviceInfo(deviceId, device);
} else
if (device.containsType(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
CallItem[] callItems = null;
try {
if(device.getOnUrl() != null)
callItems = aGsonHandler.fromJson(device.getOnUrl(), CallItem[].class);
} catch(JsonSyntaxException e) {
log.warn("Could not decode Json for url items to get Hue state for device: " + device.getName());
callItems = null;
}
for (int i = 0; callItems != null && i < callItems.length; i++) {
if((callItems[i].getType() != null && callItems[i].getType().equals(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) || callItems[i].getItem().getAsString().startsWith("{\"ipAddress\":\"")) {
lightResponse = myHueHome.getHueDeviceInfo(callItems[i], device);
i = callItems.length;
}
}
}
if (lightResponse == null)
lightResponse = DeviceResponse.createResponse(device);
return lightResponse;
@@ -643,7 +820,11 @@ public class HueMulator {
HueError[] theErrors = validateWhitelistUser(userId, false);
if (theErrors != null)
return aGsonHandler.toJson(theErrors);
theStateChanges = aGsonHandler.fromJson(body, StateChangeBody.class);
try {
theStateChanges = aGsonHandler.fromJson(body, StateChangeBody.class);
} catch (Exception e) {
theStateChanges = null;
}
if (theStateChanges == null) {
log.warn("Could not parse state change body. Light state not changed.");
return aGsonHandler.toJson(HueErrorResponse.createResponse("2", "/lights/" + lightId,
@@ -689,8 +870,11 @@ public class HueMulator {
HueError[] theErrors = validateWhitelistUser(userId, false);
if (theErrors != null)
return aGsonHandler.toJson(theErrors);
theStateChanges = aGsonHandler.fromJson(body, StateChangeBody.class);
try {
theStateChanges = aGsonHandler.fromJson(body, StateChangeBody.class);
} catch (Exception e) {
theStateChanges = null;
}
if (theStateChanges == null) {
log.warn("Could not parse state change body. Light state not changed.");
return aGsonHandler.toJson(HueErrorResponse.createResponse("2", "/lights/" + lightId,
@@ -713,8 +897,10 @@ public class HueMulator {
}
state = device.getDeviceState();
if (state == null)
if (state == null) {
state = DeviceState.createDeviceState();
device.setDeviceState(state);
}
if (targetBri != null || targetBriInc != null) {
url = device.getDimUrl();
@@ -730,11 +916,11 @@ public class HueMulator {
}
// code for backwards compatibility
if(!(device.getMapType() != null && device.getMapType().equalsIgnoreCase(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex]))) {
if(device.getMapType() != null && device.getMapType().equalsIgnoreCase(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
if(url == null)
url = device.getOnUrl();
}
if (url != null) {
if (url != null && !url.equals("")) {
if (!url.startsWith("[")) {
if (url.startsWith("{\"item"))
url = "[" + url + "]";
@@ -802,8 +988,13 @@ public class HueMulator {
}
if (responseString == null || !responseString.contains("[{\"error\":")) {
responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, state, targetBri, targetBriInc);
device.setDeviceState(state);
if(!device.isNoState()) {
responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, state, targetBri, targetBriInc);
device.setDeviceState(state);
} else {
DeviceState dummyState = DeviceState.createDeviceState();
responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, dummyState, targetBri, targetBriInc);
}
}
return responseString;

View File

@@ -0,0 +1,38 @@
package com.bwssystems.HABridge.hue;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TimeDecode {
private static final Logger log = LoggerFactory.getLogger(TimeDecode.class);
private static final String TIME_FORMAT = "${time.format(";
private static final String TIME_FORMAT_CLOSE = ")}";
/*
* light weight templating here, was going to use free marker but it was a
* bit too heavy for what we were trying to do.
*
* currently provides: time format using Java DateTimeFormatter options
*/
public static String replaceTimeValue(String request) {
if (request == null) {
return null;
}
if (request.contains(TIME_FORMAT)) {
String timeFormatDescriptor = request.substring(request.indexOf(TIME_FORMAT) + TIME_FORMAT.length(),
request.indexOf(TIME_FORMAT_CLOSE));
try {
log.debug("Time eval is: " + timeFormatDescriptor);
SimpleDateFormat dateFormat = new SimpleDateFormat(timeFormatDescriptor);
request = request.replace(TIME_FORMAT + timeFormatDescriptor + TIME_FORMAT_CLOSE, dateFormat.format(new Date()));
} catch (Exception e) {
log.warn("Could not format current time: " + timeFormatDescriptor, e);
}
}
return request;
}
}

View File

@@ -111,46 +111,54 @@ public class NestHome implements com.bwssystems.HABridge.Home {
+ "\",\"description\": \"Should not get here, no Nest available\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
} else if (anItem.getType() != null && anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.NEST_HOMEAWAY[DeviceMapTypes.typeIndex])) {
NestInstruction homeAway = aGsonHandler.fromJson(anItem.getItem().toString(), NestInstruction.class);
NestInstruction homeAway = null;
if(anItem.getItem().isJsonObject())
homeAway = aGsonHandler.fromJson(anItem.getItem(), NestInstruction.class);
else
homeAway = aGsonHandler.fromJson(anItem.getItem().getAsString(), NestInstruction.class);
theNest.getHome(homeAway.getName()).setAway(homeAway.getAway());
} else if (anItem.getType() != null && anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.NEST_THERMO_SET[DeviceMapTypes.typeIndex])) {
NestInstruction thermoSetting = aGsonHandler.fromJson(anItem.getItem().toString(), NestInstruction.class);
if (thermoSetting.getControl().equalsIgnoreCase("temp")) {
if (targetBri != null) {
if (isFarenheit)
thermoSetting
.setTemp(
String.valueOf((Double
.parseDouble(BrightnessDecode.calculateReplaceIntensityValue(thermoSetting.getTemp(),
intensity, targetBri, targetBriInc, false)) - 32.0) / 1.8));
else
thermoSetting
.setTemp(
String.valueOf(Double.parseDouble(BrightnessDecode.calculateReplaceIntensityValue(thermoSetting.getTemp(),
intensity, targetBri, targetBriInc, false))));
log.debug("Setting thermostat: " + thermoSetting.getName() + " to "
+ thermoSetting.getTemp() + "C");
theNest.getThermostat(thermoSetting.getName())
.setTargetTemperature(Float.parseFloat(thermoSetting.getTemp()));
}
} else if (thermoSetting.getControl().contains("range")
|| thermoSetting.getControl().contains("heat")
|| thermoSetting.getControl().contains("cool")
|| thermoSetting.getControl().contains("off")) {
log.debug("Setting thermostat target type: " + thermoSetting.getName() + " to "
+ thermoSetting.getControl());
theNest.getThermostat(thermoSetting.getName()).setTargetType(thermoSetting.getControl());
} else if (thermoSetting.getControl().contains("fan")) {
log.debug("Setting thermostat fan mode: " + thermoSetting.getName() + " to "
+ thermoSetting.getControl().substring(4));
NestInstruction thermoSetting = null;
if(anItem.getItem().isJsonObject())
thermoSetting = aGsonHandler.fromJson(anItem.getItem(), NestInstruction.class);
else
thermoSetting = aGsonHandler.fromJson(anItem.getItem().getAsString(), NestInstruction.class);
if (thermoSetting.getControl().equalsIgnoreCase("temp")) {
if (targetBri != null) {
if (isFarenheit)
thermoSetting
.setTemp(
String.valueOf((Double
.parseDouble(BrightnessDecode.calculateReplaceIntensityValue(thermoSetting.getTemp(),
intensity, targetBri, targetBriInc, false)) - 32.0) / 1.8));
else
thermoSetting
.setTemp(
String.valueOf(Double.parseDouble(BrightnessDecode.calculateReplaceIntensityValue(thermoSetting.getTemp(),
intensity, targetBri, targetBriInc, false))));
log.debug("Setting thermostat: " + thermoSetting.getName() + " to "
+ thermoSetting.getTemp() + "C");
theNest.getThermostat(thermoSetting.getName())
.setFanMode(thermoSetting.getControl().substring(4));
} else {
log.warn("no valid Nest control info: " + thermoSetting.getControl());
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"no valid Nest control info\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
.setTargetTemperature(Float.parseFloat(thermoSetting.getTemp()));
}
} else if (thermoSetting.getControl().contains("range")
|| thermoSetting.getControl().contains("heat")
|| thermoSetting.getControl().contains("cool")
|| thermoSetting.getControl().contains("off")) {
log.debug("Setting thermostat target type: " + thermoSetting.getName() + " to "
+ thermoSetting.getControl());
theNest.getThermostat(thermoSetting.getName()).setTargetType(thermoSetting.getControl());
} else if (thermoSetting.getControl().contains("fan")) {
log.debug("Setting thermostat fan mode: " + thermoSetting.getName() + " to "
+ thermoSetting.getControl().substring(4));
theNest.getThermostat(thermoSetting.getName())
.setFanMode(thermoSetting.getControl().substring(4));
} else {
log.warn("no valid Nest control info: " + thermoSetting.getControl());
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"no valid Nest control info\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
}
}
return responseString;
}

View File

@@ -0,0 +1,55 @@
package com.bwssystems.HABridge.plugins.domoticz;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class DeviceResult {
@SerializedName("Description")
@Expose
private String description;
@SerializedName("Name")
@Expose
private String name;
@SerializedName("Type")
@Expose
private String type;
@SerializedName("idx")
@Expose
private String idx;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getIdx() {
return idx;
}
public void setIdx(String idx) {
this.idx = idx;
}
}

View File

@@ -0,0 +1,99 @@
package com.bwssystems.HABridge.plugins.domoticz;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Devices {
@SerializedName("ActTime")
@Expose
private Integer actTime;
@SerializedName("AllowWidgetOrdering")
@Expose
private Boolean allowWidgetOrdering;
@SerializedName("ServerTime")
@Expose
private String serverTime;
@SerializedName("Sunrise")
@Expose
private String sunrise;
@SerializedName("Sunset")
@Expose
private String sunset;
@SerializedName("result")
@Expose
private List<DeviceResult> result = null;
@SerializedName("status")
@Expose
private String status;
@SerializedName("title")
@Expose
private String title;
public Integer getActTime() {
return actTime;
}
public void setActTime(Integer actTime) {
this.actTime = actTime;
}
public Boolean getAllowWidgetOrdering() {
return allowWidgetOrdering;
}
public void setAllowWidgetOrdering(Boolean allowWidgetOrdering) {
this.allowWidgetOrdering = allowWidgetOrdering;
}
public String getServerTime() {
return serverTime;
}
public void setServerTime(String serverTime) {
this.serverTime = serverTime;
}
public String getSunrise() {
return sunrise;
}
public void setSunrise(String sunrise) {
this.sunrise = sunrise;
}
public String getSunset() {
return sunset;
}
public void setSunset(String sunset) {
this.sunset = sunset;
}
public List<DeviceResult> getResult() {
return result;
}
public void setResult(List<DeviceResult> result) {
this.result = result;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

View File

@@ -0,0 +1,39 @@
package com.bwssystems.HABridge.plugins.domoticz;
public class DomoticzDevice {
private String devicetype;
private String devicename;
private String idx;
private String domoticzaddress;
private String domoticzname;
public String getDevicetype() {
return devicetype;
}
public void setDevicetype(String devicetype) {
this.devicetype = devicetype;
}
public String getDevicename() {
return devicename;
}
public void setDevicename(String devicename) {
this.devicename = devicename;
}
public String getIdx() {
return idx;
}
public void setIdx(String idx) {
this.idx = idx;
}
public String getDomoticzaddress() {
return domoticzaddress;
}
public void setDomoticzaddress(String domoticzaddress) {
this.domoticzaddress = domoticzaddress;
}
public String getDomoticzname() {
return domoticzname;
}
public void setDomoticzname(String domoticzname) {
this.domoticzname = domoticzname;
}
}

View File

@@ -0,0 +1,81 @@
package com.bwssystems.HABridge.plugins.domoticz;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
import com.google.gson.Gson;
public class DomoticzHandler {
private static final Logger log = LoggerFactory.getLogger(DomoticzHandler.class);
private static final String GET_REQUEST = "/json.htm?type=";
private static final String DEVICES_TYPE = "devices";
private static final String SCENES_TYPE = "scenes";
private static final String FILTER_USED = "&used=";
private HTTPHandler httpClient;
private NamedIP domoticzAddress;
public DomoticzHandler(NamedIP addressName) {
super();
httpClient = new HTTPHandler();
domoticzAddress = addressName;
}
public List<DomoticzDevice> getDevices() {
return getDomoticzDevices(GET_REQUEST, DEVICES_TYPE, FILTER_USED);
}
public List<DomoticzDevice> getScenes() {
return getDomoticzDevices(GET_REQUEST, SCENES_TYPE, null);
}
private List<DomoticzDevice> getDomoticzDevices(String rootRequest, String type, String postpend) {
Devices theDomoticzApiResponse = null;
List<DomoticzDevice> deviceList = null;
String theUrl = null;
String theData;
theUrl = "http://" + domoticzAddress.getIp() + ":" + domoticzAddress.getPort() + rootRequest + type;
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
if(theData != null) {
log.debug("GET " + type + " DomoticzApiResponse - data: " + theData);
theDomoticzApiResponse = new Gson().fromJson(theData, Devices.class);
if(theDomoticzApiResponse.getResult() == null) {
log.warn("Cannot get any devices for type " + type + " for Domoticz " + domoticzAddress.getName() + " as response is not parsable.");
return deviceList;
}
deviceList = new ArrayList<DomoticzDevice>();
Iterator<DeviceResult> theDeviceNames = theDomoticzApiResponse.getResult().iterator();
while(theDeviceNames.hasNext()) {
DeviceResult theDevice = theDeviceNames.next();
DomoticzDevice aNewDomoticzDevice = new DomoticzDevice();
aNewDomoticzDevice.setDevicetype(theDevice.getType());
aNewDomoticzDevice.setDevicename(theDevice.getName());
aNewDomoticzDevice.setIdx(theDevice.getIdx());
aNewDomoticzDevice.setDomoticzaddress(domoticzAddress.getIp() + ":" + domoticzAddress.getPort());
aNewDomoticzDevice.setDomoticzname(domoticzAddress.getName());
deviceList.add(aNewDomoticzDevice);
}
}
else {
log.warn("Get Domoticz device types " + type + " for " + domoticzAddress.getName() + " - returned null, no data.");
}
return deviceList;
}
public NamedIP getDomoticzAddress() {
return domoticzAddress;
}
public void setDomoticzAddress(NamedIP DomoticzAddress) {
this.domoticzAddress = DomoticzAddress;
}
}

View File

@@ -0,0 +1,98 @@
package com.bwssystems.HABridge.plugins.domoticz;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
public class DomoticzHome implements Home {
private static final Logger log = LoggerFactory.getLogger(DomoticzHome.class);
private Map<String, DomoticzHandler> domoticzs;
private Boolean validDomoticz;
public DomoticzHome(BridgeSettingsDescriptor bridgeSettings) {
super();
createHome(bridgeSettings);
}
@Override
public Object getItems(String type) {
if(!validDomoticz)
return null;
log.debug("consolidating devices for hues");
List<DomoticzDevice> theResponse = null;
Iterator<String> keys = domoticzs.keySet().iterator();
List<DomoticzDevice> deviceList = new ArrayList<DomoticzDevice>();
while(keys.hasNext()) {
String key = keys.next();
theResponse = domoticzs.get(key).getDevices();
if(theResponse != null)
addDomoticzDevices(deviceList, theResponse, key);
else {
log.warn("Cannot get lights for Domoticz with name: " + key + ", skipping this Domoticz.");
continue;
}
theResponse = domoticzs.get(key).getScenes();
if(theResponse != null)
addDomoticzDevices(deviceList, theResponse, key);
else
log.warn("Cannot get Scenes for Domoticz with name: " + key);
}
return deviceList;
}
private Boolean addDomoticzDevices(List<DomoticzDevice> theDeviceList, List<DomoticzDevice> theSourceList, String theKey) {
if(!validDomoticz)
return null;
Iterator<DomoticzDevice> devices = theSourceList.iterator();
while(devices.hasNext()) {
DomoticzDevice theDevice = devices.next();
theDeviceList.add(theDevice);
}
return true;
}
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
// Not a device handler
return null;
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
validDomoticz = bridgeSettings.isValidDomoticz();
log.info("Domoticz Home created." + (validDomoticz ? "" : " No Domoticz devices configured."));
if(!validDomoticz)
return null;
domoticzs = new HashMap<String, DomoticzHandler>();
Iterator<NamedIP> theList = bridgeSettings.getDomoticzaddress().getDevices().iterator();
while(theList.hasNext()) {
NamedIP aDomoticz = theList.next();
try {
domoticzs.put(aDomoticz.getName(), new DomoticzHandler(aDomoticz));
} catch (Exception e) {
log.error("Cannot get Domoticz client (" + aDomoticz.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
return null;
}
}
return this;
}
@Override
public void closeHome() {
// noop
}
}

View File

@@ -11,6 +11,7 @@ import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
public class CommandHome implements Home {
private static final Logger log = LoggerFactory.getLogger(CommandHome.class);
@@ -25,24 +26,25 @@ public class CommandHome implements Home {
log.debug("Exec Request called with url: " + anItem.getItem().getAsString());
String responseString = null;
String intermediate;
if (anItem.getItem().toString().contains("exec://"))
intermediate = anItem.getItem().getAsString().substring(anItem.getItem().toString().indexOf("://") + 3);
if (anItem.getItem().getAsString().contains("exec://"))
intermediate = anItem.getItem().getAsString().substring(anItem.getItem().getAsString().indexOf("://") + 3);
else
intermediate = anItem.getItem().getAsString();
String anError = doExecRequest(intermediate,
BrightnessDecode.calculateIntensity(itensity, targetBri, targetBriInc), lightId);
intermediate = BrightnessDecode.calculateReplaceIntensityValue(intermediate, itensity, targetBri, targetBriInc, false);
intermediate = TimeDecode.replaceTimeValue(intermediate);
String anError = doExecRequest(intermediate, lightId);
if (anError != null) {
responseString = anError;
}
return responseString;
}
private String doExecRequest(String anItem, int intensity, String lightId) {
private String doExecRequest(String anItem, String lightId) {
log.debug("Executing request: " + anItem);
String responseString = null;
if (anItem != null && !anItem.equalsIgnoreCase("")) {
try {
Process p = Runtime.getRuntime().exec(BrightnessDecode.replaceIntensityValue(anItem, intensity, false));
Process p = Runtime.getRuntime().exec(anItem);
log.debug("Process running: " + p.isAlive());
} catch (IOException e) {
log.warn("Could not execute request: " + anItem, e);

View File

@@ -13,20 +13,13 @@ import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.api.NameValue;
import com.bwssystems.HABridge.api.hue.HueError;
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
import com.google.gson.Gson;
public class HalHome implements Home {
private static final Logger log = LoggerFactory.getLogger(HalHome.class);
private Map<String, HalInfo> hals;
private Boolean validHal;
private HTTPHandler anHttpHandler;
public HalHome(BridgeSettingsDescriptor bridgeSettings) {
super();
@@ -105,38 +98,16 @@ public class HalHome implements Home {
Iterator<HalDevice> devices = theSourceList.iterator();
while(devices.hasNext()) {
HalDevice theDevice = devices.next();
HalDevice aNewHalDevice = new HalDevice();
aNewHalDevice.setHaldevicetype(theDevice.getHaldevicetype());
aNewHalDevice.setHaldevicename(theDevice.getHaldevicename());
aNewHalDevice.setButtons(theDevice.getButtons());
aNewHalDevice.setHaladdress(hals.get(theKey).getHalAddress().getIp());
aNewHalDevice.setHalname(theKey);
theDeviceList.add(aNewHalDevice);
theDeviceList.add(theDevice);
}
anHttpHandler = new HTTPHandler();
return true;
}
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
log.debug("executing HUE api request to HAL Http " + anItem.getItem().getAsString());
String responseString = null;
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(anItem.getItem().getAsString(),
intensity, targetBri, targetBriInc, false);
String aBody;
aBody = BrightnessDecode.calculateReplaceIntensityValue(anItem.getHttpBody(),
intensity, targetBri, targetBriInc, false);
// make call
if (anHttpHandler.doHttpRequest(anUrl, anItem.getHttpVerb(), anItem.getContentType(), aBody,
new Gson().fromJson(anItem.getHttpHeaders(), NameValue[].class)) == null) {
log.warn("Error on calling url to change device state: " + anUrl);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
}
return responseString;
// Not a device handler
return null;
}
@Override

View File

@@ -121,6 +121,8 @@ public class HalInfo {
HalDevice aNewHalDevice = new HalDevice();
aNewHalDevice.setHaldevicetype(deviceType);
aNewHalDevice.setHaldevicename(theDevice.getDeviceName());
aNewHalDevice.setHaladdress(halAddress.getIp());
aNewHalDevice.setHalname(halAddress.getName());
deviceList.add(aNewHalDevice);
}
@@ -147,8 +149,12 @@ public class HalInfo {
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
if (theData != null) {
log.debug("GET IrData for IR Device " + theHalDevice.getHaldevicename() + " HalApiResponse - data: " + theData);
theHalApiResponse = new Gson().fromJson(theData, DeviceElements.class);
if (theHalApiResponse.getDeviceElements() == null) {
try {
theHalApiResponse = new Gson().fromJson(theData, DeviceElements.class);
} catch (Exception e) {
theHalApiResponse = null;
}
if (theHalApiResponse == null || theHalApiResponse.getDeviceElements() == null) {
StatusDescription theStatus = new Gson().fromJson(theData, StatusDescription.class);
if (theStatus.getStatus() == null) {
log.warn("Cannot get buttons for IR Device " + theHalDevice.getHaldevicename() + " for hal "

View File

@@ -135,7 +135,11 @@ public class HarmonyHome implements Home {
} else {
if(anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex]))
{
RunActivity anActivity = aGsonHandler.fromJson(anItem.getItem(), RunActivity.class);
RunActivity anActivity = null;
if(anItem.getItem().isJsonObject())
anActivity = aGsonHandler.fromJson(anItem.getItem(), RunActivity.class);
else
anActivity = aGsonHandler.fromJson(anItem.getItem().getAsString(), RunActivity.class);
if(anActivity.getHub() == null || anActivity.getHub().isEmpty())
anActivity.setHub(device.getTargetDevice());
HarmonyHandler myHarmony = getHarmonyHandler(anActivity.getHub());
@@ -148,7 +152,12 @@ public class HarmonyHome implements Home {
myHarmony.startActivity(anActivity);
}
} else if(anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex])) {
String url = anItem.getItem().toString();
String url = null;
if(anItem.getItem().isJsonObject() || anItem.getItem().isJsonArray()) {
url = aGsonHandler.toJson(anItem.getItem());
} else
url = anItem.getItem().getAsString();
if (url.substring(0, 1).equalsIgnoreCase("{")) {
url = "[" + url + "]";
}

View File

@@ -4,6 +4,7 @@ public class HassDevice {
private State deviceState;
private String deviceName;
private String domain;
private Boolean secure;
private String hassaddress;
private String hassname;
public State getDeviceState() {
@@ -24,6 +25,12 @@ public class HassDevice {
public void setDomain(String domain) {
this.domain = domain;
}
public Boolean getSecure() {
return secure;
}
public void setSecure(Boolean secure) {
this.secure = secure;
}
public String getHassaddress() {
return hassaddress;
}

View File

@@ -125,9 +125,13 @@ public class HassHome implements Home {
+ lightId + "state\"}}]";
} else {
HassCommand hassCommand = aGsonHandler.fromJson(anItem.getItem(), HassCommand.class);
HassCommand hassCommand = null;
if(anItem.getItem().isJsonObject())
hassCommand = aGsonHandler.fromJson(anItem.getItem(), HassCommand.class);
else
hassCommand = aGsonHandler.fromJson(anItem.getItem().getAsString(), HassCommand.class);
hassCommand.setBri(BrightnessDecode.replaceIntensityValue(hassCommand.getBri(),
BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false));
BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false));
HomeAssistant homeAssistant = getHomeAssistant(hassCommand.getHassName());
if (homeAssistant == null) {
log.warn("Should not get here, no HomeAssistants available");

View File

@@ -36,7 +36,12 @@ public class HomeAssistant {
public Boolean callCommand(HassCommand aCommand) {
log.debug("calling HomeAssistant: " + aCommand.getHassName() + " - "
+ aCommand.getEntityId() + " - " + aCommand.getState() + " - " + aCommand.getBri());
String aUrl = "http://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/services/" + aCommand.getEntityId().substring(0, aCommand.getEntityId().indexOf("."));
String aUrl = null;
if(hassAddress.getSecure() != null && hassAddress.getSecure())
aUrl = "https";
else
aUrl = "http";
aUrl = aUrl + "://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/services/" + aCommand.getEntityId().substring(0, aCommand.getEntityId().indexOf("."));
String aBody = "{\"entity_id\":\"" + aCommand.getEntityId() + "\"";
NameValue[] headers = null;
if(hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
@@ -49,7 +54,7 @@ public class HomeAssistant {
if(aCommand.getState().equalsIgnoreCase("on")) {
aUrl = aUrl + "/turn_on";
if(aCommand.getBri() != null)
aBody = aBody + ",\"state\":\"on\",\"attributes\":{\"brightness\":" + aCommand.getBri() + "}}";
aBody = aBody + ",\"brightness\":" + aCommand.getBri() + "}";
else
aBody = aBody + "}";
}
@@ -75,7 +80,11 @@ public class HomeAssistant {
headers = new NameValue[1];
headers[0] = password;
}
theUrl = "http://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/states";
if(hassAddress.getSecure() != null && hassAddress.getSecure())
theUrl = "https";
else
theUrl = "http";
theUrl = theUrl + "://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/states";
theData = anHttpHandler.doHttpRequest(theUrl, HttpGet.METHOD_NAME, "application/json", null, headers);
if(theData != null) {
log.debug("GET Hass States - data: " + theData);

View File

@@ -4,8 +4,12 @@ import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
@@ -30,27 +34,29 @@ import com.bwssystems.HABridge.api.NameValue;
public class HTTPHandler {
private static final Logger log = LoggerFactory.getLogger(HTTPHandler.class);
private HttpClient httpClient;
private CloseableHttpClient httpclientSSL;
private SSLContext sslcontext;
private SSLConnectionSocketFactory sslsf;
private RequestConfig globalConfig;
// private CloseableHttpClient httpclientSSL;
// private SSLContext sslcontext;
// private SSLConnectionSocketFactory sslsf;
// private RequestConfig globalConfig;
public HTTPHandler() {
httpClient = HttpClients.createDefault();
// Removed Specific SSL as Apache HttpClient automatically uses SSL if the URI starts with https://
// Trust own CA and all self-signed certs
sslcontext = SSLContexts.createDefault();
// sslcontext = SSLContexts.createDefault();
// Allow TLSv1 protocol only
sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1,TLSv1.1,TLSv1.2" }, null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
httpclientSSL = HttpClients.custom().setSSLSocketFactory(sslsf).setDefaultRequestConfig(globalConfig).build();
// sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLS" }, null,
// SSLConnectionSocketFactory.getDefaultHostnameVerifier());
// globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
// httpclientSSL = HttpClients.custom().setSSLSocketFactory(sslsf).setDefaultRequestConfig(globalConfig).build();
}
// This function executes the url from the device repository against the
// target as http or https as defined
public String doHttpRequest(String url, String httpVerb, String contentType, String body, NameValue[] headers) {
log.debug("doHttpRequest with url: " + url + " with http command: " + httpVerb + " with body: " + body);
HttpUriRequest request = null;
String theContent = null;
URI theURI = null;
@@ -96,9 +102,10 @@ public class HTTPHandler {
}
try {
HttpResponse response;
if (url.startsWith("https"))
response = httpclientSSL.execute(request);
else
// Removed Specific SSL as Apache HttpClient automatically uses SSL if the URI starts with https://
// if (url.startsWith("xyzhttps"))
// response = httpclientSSL.execute(request);
// else
response = httpClient.execute(request);
log.debug((httpVerb == null ? "GET" : httpVerb) + " execute on URL responded: "
+ response.getStatusLine().getStatusCode());
@@ -132,18 +139,18 @@ public class HTTPHandler {
}
public CloseableHttpClient getHttpclientSSL() {
return httpclientSSL;
}
// public CloseableHttpClient getHttpclientSSL() {
// return httpclientSSL;
// }
public void closeHandler() {
httpClient = null;
try {
httpclientSSL.close();
} catch (IOException e) {
// noop
}
httpclientSSL = null;
// try {
// httpclientSSL.close();
// } catch (IOException e) {
// // noop
// }
// httpclientSSL = null;
}
}

View File

@@ -12,6 +12,7 @@ import com.bwssystems.HABridge.api.hue.HueErrorResponse;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
import com.google.gson.Gson;
public class HTTPHome implements Home {
@@ -46,9 +47,14 @@ public class HTTPHome implements Home {
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(anItem.getItem().getAsString(),
intensity, targetBri, targetBriInc, false);
String aBody;
aBody = BrightnessDecode.calculateReplaceIntensityValue(anItem.getHttpBody(),
intensity, targetBri, targetBriInc, false);
anUrl = TimeDecode.replaceTimeValue(anUrl);
String aBody = null;
if(anItem.getHttpBody()!= null && !anItem.getHttpBody().isEmpty()) {
aBody = BrightnessDecode.calculateReplaceIntensityValue(anItem.getHttpBody(),
intensity, targetBri, targetBriInc, false);
aBody = TimeDecode.replaceTimeValue(aBody);
}
// make call
if (anHttpHandler.doHttpRequest(anUrl, anItem.getHttpVerb(), anItem.getContentType(), aBody,
new Gson().fromJson(anItem.getHttpHeaders(), NameValue[].class)) == null) {

View File

@@ -65,9 +65,17 @@ public class HueHome implements Home {
return deviceList;
}
public DeviceResponse getHueDeviceInfo(HueDeviceIdentifier deviceId, DeviceDescriptor device) {
public DeviceResponse getHueDeviceInfo(CallItem anItem, DeviceDescriptor device) {
if(!validHue)
return null;
HueDeviceIdentifier deviceId = null;
if(anItem.getItem().isJsonObject())
deviceId = aGsonHandler.fromJson(anItem.getItem(), HueDeviceIdentifier.class);
else
deviceId = aGsonHandler.fromJson(anItem.getItem().getAsString(), HueDeviceIdentifier.class);
if(deviceId.getHueName() == null || deviceId.getHueName().isEmpty())
deviceId.setHueName(device.getTargetDevice());
DeviceResponse deviceResponse = null;
HueInfo aHueInfo = hues.get(device.getTargetDevice());
deviceResponse = aHueInfo.getHueDeviceInfo(deviceId.getDeviceId(), device);
@@ -80,7 +88,11 @@ public class HueHome implements Home {
if(!validHue)
return null;
String responseString = null;
HueDeviceIdentifier deviceId = aGsonHandler.fromJson(anItem.getItem(), HueDeviceIdentifier.class);
HueDeviceIdentifier deviceId = null;
if(anItem.getItem().isJsonObject())
deviceId = aGsonHandler.fromJson(anItem.getItem(), HueDeviceIdentifier.class);
else
deviceId = aGsonHandler.fromJson(anItem.getItem().getAsString(), HueDeviceIdentifier.class);
if(deviceId.getHueName() == null || deviceId.getHueName().isEmpty())
deviceId.setHueName(device.getTargetDevice());

View File

@@ -15,6 +15,7 @@ import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -79,8 +80,15 @@ public class MQTTHome implements Home {
String responseString = null;
log.debug("executing HUE api request to send message to MQTT broker: " + anItem.getItem().toString());
if (validMqtt) {
String mqttObject = BrightnessDecode.calculateReplaceIntensityValue(anItem.getItem().toString(),
String mqttObject = null;
if(anItem.getItem().isJsonObject() || anItem.getItem().isJsonArray()) {
mqttObject = aGsonHandler.toJson(anItem.getItem());
}
else
mqttObject =anItem.getItem().getAsString();
mqttObject = BrightnessDecode.calculateReplaceIntensityValue(mqttObject,
intensity, targetBri, targetBriInc, false);
mqttObject = TimeDecode.replaceTimeValue(mqttObject);
if (mqttObject.substring(0, 1).equalsIgnoreCase("{"))
mqttObject = "[" + mqttObject + "]";
MQTTMessage[] mqttMessages = aGsonHandler.fromJson(mqttObject, MQTTMessage[].class);
@@ -89,18 +97,16 @@ public class MQTTHome implements Home {
if(mqttMessages[z].getCount() != null && mqttMessages[z].getCount() > 0)
theCount = mqttMessages[z].getCount();
for(int y = 0; y < theCount; y++) {
if( y > 0 || z > 0) {
log.debug("publishing message: " + mqttMessages[y].getClientId() + " - "
+ mqttMessages[y].getTopic() + " - " + mqttMessages[y].getMessage()
+ " - count: " + String.valueOf(z));
log.debug("publishing message: " + mqttMessages[y].getClientId() + " - "
+ mqttMessages[y].getTopic() + " - " + mqttMessages[y].getMessage()
+ " - count: " + String.valueOf(z));
MQTTHandler mqttHandler = getMQTTHandler(mqttMessages[y].getClientId());
if (mqttHandler == null) {
log.warn("Should not get here, no mqtt hanlder available");
} else {
mqttHandler.publishMessage(mqttMessages[y].getTopic(), mqttMessages[y].getMessage());
}
}
MQTTHandler mqttHandler = getMQTTHandler(mqttMessages[y].getClientId());
if (mqttHandler == null) {
log.warn("Should not get here, no mqtt hanlder available");
} else {
mqttHandler.publishMessage(mqttMessages[y].getTopic(), mqttMessages[y].getMessage());
}
}
}
} else {

View File

@@ -16,6 +16,7 @@ import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
public class TCPHome implements Home {
private static final Logger log = LoggerFactory.getLogger(TCPHome.class);
@@ -48,11 +49,11 @@ public class TCPHome implements Home {
// noop
}
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
if (theUrlBody.startsWith("0x")) {
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
} else {
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, false);
sendData = theUrlBody.getBytes();
}

View File

@@ -15,6 +15,7 @@ import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
import com.bwssystems.HABridge.util.UDPDatagramSender;
public class UDPHome implements Home {
@@ -46,22 +47,23 @@ public class UDPHome implements Home {
try {
IPAddress = InetAddress.getByName(hostAddr);
} catch (UnknownHostException e) {
// noop
log.warn("Udp Call, unknown host, continuing...");
return null;
}
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
if (theUrlBody.startsWith("0x")) {
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
} else {
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, false);
sendData = theUrlBody.getBytes();
}
try {
theUDPDatagramSender.sendUDPResponse(sendData, IPAddress, Integer.parseInt(port));
} catch (NumberFormatException e) {
// noop
log.warn("Udp Call, Number format exception on port, continuing...");
} catch (IOException e) {
// noop
log.warn("IO exception on udp call, continuing...");
}
return null;
}

View File

@@ -37,9 +37,15 @@
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="http://{{bridge.settings.myechourl}}" target="_blank">My Echo</a></li>
<li><a href="https://github.com/bwssytems/ha-bridge/blob/master/README.md" target="_blank">Help</a></li>
<li class="dropdown">
<a id="dLabel" href="" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">About <span class="caret"></span></a>
<a id="dLabel1" href="" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Help <span class="caret"></span></a>
<ul class="dropdown-menu" aria-labelledby="dLabel">
<li><a href="https://github.com/bwssytems/ha-bridge/blob/master/README.md" target="_blank">Readme</a></li>
<li><a href="https://github.com/bwssytems/ha-bridge/wiki/HA-Bridge-FAQs" target="_blank">FAQ</a></li>
</ul>
</li>
<li class="dropdown">
<a id="dLabel2" href="" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">About <span class="caret"></span></a>
<ul class="dropdown-menu" aria-labelledby="dLabel">
<li><a href="http://www.bwssystems.com" target="_blank">Developed by BWS Systems</a></li>
<li><a href="http://www.amazon.com/echo" target="_blank">Amazon Echo</a></li>

View File

@@ -42,6 +42,9 @@ app.config (function ($locationProvider, $routeProvider) {
}).when ('/hassdevices', {
templateUrl: 'views/hassdevice.html',
controller: 'HassController'
}).when ('/domoticzdevices', {
templateUrl: 'views/domoticzdevice.html',
controller: 'DomoticzController'
}).otherwise ({
templateUrl: 'views/configuration.html',
controller: 'ViewingController'
@@ -68,7 +71,7 @@ String.prototype.replaceAll = function (search, replace)
app.service ('bridgeService', function ($http, $window, ngToast) {
var self = this;
this.state = {base: window.location.origin + "/api/devices", bridgelocation: window.location.origin, systemsbase: window.location.origin + "/system", huebase: window.location.origin + "/api", configs: [], backups: [], devices: [], device: {}, mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], mapTypes: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, showHal: false, showMqtt: false, showHass: false, habridgeversion: ""};
this.state = {base: window.location.origin + "/api/devices", bridgelocation: window.location.origin, systemsbase: window.location.origin + "/system", huebase: window.location.origin + "/api", configs: [], backups: [], devices: [], device: {}, mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], mapTypes: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, showHal: false, showMqtt: false, showHass: false, showDomoticz: false, habridgeversion: ""};
this.displayWarn = function(errorTitle, error) {
var toastContent = errorTitle;
@@ -158,6 +161,25 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
return a.indexOf(b) >= 0;
}
this.deviceContainsType = function (device, aType) {
if(device.mapType !== undefined && device.mapType !== null && device.mapType.indexOf(aType) >= 0)
return true;
if(device.deviceType !== undefined && device.deviceType !== null && device.deviceType.indexOf(aType) >= 0)
return true;
if(device.onUrl !== undefined && device.onUrl !== null && device.onUrl.indexOf(aType) >= 0)
return true;
if(device.dimUrl !== undefined && device.dimUrl !== null && device.dimUrl.indexOf(aType) >= 0)
return true;
if(device.offUrl !== undefined && device.offUrl !== null && device.offUrl.indexOf(aType) >= 0)
return true;
return false;
}
this.compareHarmonyNumber = function(r1, r2) {
if (r1.device !== undefined) {
if (r1.device.id === r2.device.id)
@@ -230,6 +252,11 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
return;
}
this.updateShowDomoticz = function () {
this.state.showDomoticz = self.state.settings.domoticzconfigured;
return;
}
this.loadBridgeSettings = function () {
return $http.get(this.state.systemsbase + "/settings").then(
function (response) {
@@ -241,6 +268,7 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
self.updateShowHal();
self.updateShowMqtt();
self.updateShowHass();
self.updateShowDomoticz();
},
function (error) {
self.displayWarn("Load Bridge Settings Error: ", error);
@@ -409,6 +437,19 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
);
};
this.viewDomoticzDevices = function () {
if (!this.state.showDomoticz)
return;
return $http.get(this.state.base + "/domoticz/devices").then(
function (response) {
self.state.domoticzdevices = response.data;
},
function (error) {
self.displayWarn("Get Domoticz Devices Error: ", error);
}
);
};
this.formatCallItem = function (currentItem) {
if(!currentItem.startsWith("{\"item") && !currentItem.startsWith("[{\"item")) {
if (currentItem.startsWith("[") || currentItem.startsWith("{"))
@@ -705,6 +746,10 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
self.state.olddevicename = device.name;
};
this.editNewDevice = function (device) {
self.state.device = device;
};
this.testUrl = function (device, type, value) {
var msgDescription = "unknown";
var testUrl = this.state.huebase + "/test/lights/" + device.id + "/state";
@@ -721,7 +766,7 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
$http.put(testUrl, testBody).then(
function (response) {
if (typeof(response.data[0].success) !== 'undefined') {
msgDescription = "success " + angular.toJson(response.data[0].success);
msgDescription = "success " + angular.toJson(response.data);
}
if (typeof(response.data[0].error) !== 'undefined') {
msgDescription = "error " + angular.toJson(response.data[0].error);
@@ -921,11 +966,11 @@ app.controller ('SystemController', function ($scope, $location, $http, $window,
}
}
};
$scope.addHasstoSettings = function (newhassname, newhassip, newhassport, newhasspassword) {
$scope.addHasstoSettings = function (newhassname, newhassip, newhassport, newhasspassword, newhasssecure) {
if($scope.bridge.settings.hassaddress === undefined || $scope.bridge.settings.hassaddress === null) {
$scope.bridge.settings.hassaddress = { devices: [] };
}
var newhass = {name: newhassname, ip: newhassip, port: newhassport, password: newhasspassword }
var newhass = {name: newhassname, ip: newhassip, port: newhassport, password: newhasspassword, secure: newhasssecure }
$scope.bridge.settings.hassaddress.devices.push(newhass);
$scope.newhassname = null;
$scope.newhassip = null;
@@ -939,6 +984,24 @@ app.controller ('SystemController', function ($scope, $location, $http, $window,
}
}
};
$scope.addDomoticztoSettings = function (newdomoticzname, newdomoticzip, newdomoticzport, newdomoticzusername, newdomoticzpassword) {
if($scope.bridge.settings.domoticzaddress === undefined || $scope.bridge.settings.domoticzaddress === null) {
$scope.bridge.settings.domoticzaddress = { devices: [] };
}
var newdomoticz = {name: newdomoticzname, ip: newdomoticzip, port: newdomoticzport, username: newdomoticzusername, password: newdomoticzpassword }
$scope.bridge.settings.domoticzaddress.devices.push(newdomoticz);
$scope.newdomoticzname = null;
$scope.newdomoticzip = null;
$scope.newdomoticzport = null;
$scope.newdomoticzpassword = null;
};
$scope.removeDomoticztoSettings = function (domoticzname, domoticzip) {
for(var i = $scope.bridge.settings.domoticzaddress.devices.length - 1; i >= 0; i--) {
if($scope.bridge.settings.domoticzaddress.devices[i].name === domoticzname && $scope.bridge.settings.domoticzaddress.devices[i].ip === domoticzip) {
$scope.bridge.settings.domoticzaddress.devices.splice(i, 1);
}
}
};
$scope.bridgeReinit = function () {
bridgeService.reinit();
};
@@ -1163,7 +1226,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
bridgeService.buildUrls(onpayload, dimpayload, offpayload, false, veradevice.id, veradevice.name, veradevice.veraname, "switch", "veraDevice", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1177,7 +1240,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
bridgeService.buildUrls(onpayload, null, offpayload, false, verascene.id, verascene.name, verascene.veraname, "scene", "veraScene", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1297,7 +1360,7 @@ app.controller('HarmonyController', function ($scope, $location, $http, bridgeSe
bridgeService.buildUrls(onpayload, null, offpayload, true, harmonyactivity.activity.id, harmonyactivity.activity.label, harmonyactivity.hub, "activity", "harmonyActivity", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1309,7 +1372,7 @@ app.controller('HarmonyController', function ($scope, $location, $http, bridgeSe
bridgeService.buildUrls(onpayload, null, offpayload, true, actionOn.command, harmonydevice.device.label, harmonydevice.hub, "button", "harmonyButton", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1353,7 +1416,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
offpayload = "{\"name\":\"" + nestitem.id + "\",\"away\":true,\"control\":\"status\"}";
bridgeService.buildUrls(onpayload, null, offpayload, true, nestitem.id, nestitem.name, nestitem.name, "home", "nestHomeAway", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1363,7 +1426,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
offpayload = "{\"name\":\"" + nestitem.id + "\",\"control\":\"off\"}";
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, nestitem.id + "-SetTemp", nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Temperature", nestitem.location, "thermo", "nestThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1373,7 +1436,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
offpayload = "{\"name\":\"" + nestitem.id + "\",\"control\":\"off\"}";
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, nestitem.id + "-SetHeat", nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Heat", nestitem.location, "thermo", "nestThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1383,7 +1446,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
offpayload = "{\"name\":\"" + nestitem.id + "\",\"control\":\"off\"}";
bridgeService.buildUrls(onpayload,dimpayload, offpayload, true, nestitem.id + "-SetCool", nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Cool", nestitem.location, "thermo", "nestThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1392,7 +1455,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
offpayload = "{\"name\":\"" + nestitem.id + "\",\"control\":\"off\"}";
bridgeService.buildUrls(onpayload, null, offpayload, true, nestitem.id + "-SetRange", nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Range", nestitem.location, "thermo", "nestThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1401,7 +1464,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
offpayload = "{\"name\":\"" + nestitem.id + "\",\"control\":\"off\"}";
bridgeService.buildUrls(onpayload, null, offpayload, true, nestitem.id + "-TurnOff", nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Thermostat", nestitem.location, "thermo", "nestThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1410,7 +1473,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
offpayload = "{\"name\":\"" + nestitem.id + "\",\"control\":\"fan-auto\"}";
bridgeService.buildUrls(onpayload, null, offpayload, true, nestitem.id + "-SetFan", nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Fan", nestitem.location, "thermo", "nestThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1456,7 +1519,7 @@ app.controller('HueController', function ($scope, $location, $http, bridgeServic
offpayload = "{\"ipAddress\":\"" + huedevice.hueaddress + "\",\"deviceId\":\"" + huedevice.huedeviceid +"\",\"hueName\":\"" + huedevice.huename + "\"}";
bridgeService.buildUrls(onpayload, null, offpayload, true, huedevice.device.uniqueid, huedevice.device.name, huedevice.huename, "passthru", "hueDevice", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1624,7 +1687,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
+ postCmd;
bridgeService.buildUrls(onpayload, dimpayload, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname, haldevice.haldevicename, haldevice.halname, aDeviceType, "halDevice", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1636,7 +1699,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
bridgeService.buildUrls(onpayload, null, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-" + actionOn.DeviceName, haldevice.haldevicename, haldevice.halname, "button", "halButton", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1645,7 +1708,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
offpayload = "http://" + haldevice.haladdress + "/ModeService!ModeCmd=Set!ModeName=Away?Token=" + $scope.bridge.settings.haltoken;
bridgeService.buildUrls(onpayload, null, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-HomeAway", haldevice.haldevicename, haldevice.halname, "home", "halHome", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1667,7 +1730,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
+ $scope.bridge.settings.haltoken;
bridgeService.buildUrls(onpayload, dimpayload, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-SetHeat", haldevice.haldevicename + " Heat", haldevice.halname, "thermo", "halThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1689,7 +1752,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
+ $scope.bridge.settings.haltoken;
bridgeService.buildUrls(onpayload, dimpayload, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-SetCool", haldevice.haldevicename + " Cool", haldevice.halname, "thermo", "halThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1705,7 +1768,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
+ "!HVACMode=Off?Token="
bridgeService.buildUrls(onpayload, null, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-SetAuto", haldevice.haldevicename + " Auto", haldevice.halname, "thermo", "halThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1722,7 +1785,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
$scope.device.offUrl = "http://" + haldevice.haladdress
bridgeService.buildUrls(onpayload, null, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-TurnOff", haldevice.haldevicename + " Thermostat", haldevice.halname, "thermo", "halThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1739,7 +1802,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
+ $scope.bridge.settings.haltoken;
bridgeService.buildUrls(onpayload, null, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-SetFan", haldevice.haldevicename + " Fan", haldevice.halname, "thermo", "halThermoSet", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1859,7 +1922,7 @@ app.controller('MQTTController', function ($scope, $location, $http, bridgeServi
bridgeService.buildUrls(onpayload, null, offpayload, true, mqttbroker.clientId + "-" + mqtttopic, mqttbroker.clientId + mqtttopic, mqttbroker.clientId, "mqtt", "mqttMessage", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1911,7 +1974,7 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1925,7 +1988,7 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1939,7 +2002,7 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1953,7 +2016,7 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1967,7 +2030,7 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -1981,7 +2044,7 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editDevice($scope.device);
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
@@ -2081,6 +2144,157 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
};
});
app.controller('DomoticzController', function ($scope, $location, $http, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
$scope.device_dim_control = "";
$scope.bulk = { devices: [] };
$scope.selectAll = false;
bridgeService.viewDomoticzDevices();
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
$scope.buttonsVisible = false;
$scope.clearDevice = function () {
bridgeService.clearDevice();
$scope.device = bridgeService.state.device;
};
$scope.buildDeviceUrls = function (domoticzdevice, dim_control) {
var preCmd = "";
var postOnCmd = "";
var postDimCmd = "";
var postOffCmd = "";
var nameCmd = "";
var aDeviceType;
var postCmd = "";
if(domoticzdevice.devicetype === "Scene") {
aDeviceType = "scene";
preCmd = "/json.htm?type=command&param=switchscene&idx="
postOnCmd = "&switchcmd=On";
postOffCmd = "&switchcmd=Off";
}
else {
aDeviceType = "switch";
preCmd = "/json.htm?type=command&param=switchlight&idx="
postOnCmd = "&switchcmd=On";
postDimCmd = "&switchcmd=Set%20Level&level=";
postOffCmd = "&switchcmd=Off";
}
if((dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0) && aDeviceType === "switch")
dimpayload = "http://" + domoticzdevice.domoticzaddress
+ preCmd
+ domoticzdevice.idx
+ postDimCmd
+ dim_control;
else
dimpayload = null;
onpayload = "http://" + domoticzdevice.domoticzaddress
+ preCmd
+ domoticzdevice.idx
+ postOnCmd;
offpayload = "http://" + domoticzdevice.domoticzaddress
+ preCmd
+ domoticzdevice.idx
+ postOffCmd;
bridgeService.buildUrls(onpayload, dimpayload, offpayload, false, domoticzdevice.devicename + "-" + domoticzdevice.domoticzname, domoticzdevice.devicename, domoticzdevice.domoticzname, aDeviceType, "domoticzDevice", null, null);
$scope.device = bridgeService.state.device;
bridgeService.editNewDevice($scope.device);
$location.path('/editdevice');
};
$scope.bulkAddDevices = function(dim_control) {
var devicesList = [];
for(var i = 0; i < $scope.bulk.devices.length; i++) {
for(var x = 0; x < bridgeService.state.domoticzdevices.length; x++) {
if(bridgeService.state.domoticzdevices[x].devicename === $scope.bulk.devices[i]) {
$scope.buildDeviceUrls(bridgeService.state.domoticzdevices[x],dim_control);
devicesList[i] = {
name: $scope.device.name,
mapId: $scope.device.mapId,
mapType: $scope.device.mapType,
deviceType: $scope.device.deviceType,
targetDevice: $scope.device.targetDevice,
onUrl: $scope.device.onUrl,
dimUrl: $scope.device.dimUrl,
offUrl: $scope.device.offUrl,
headers: $scope.device.headers,
httpVerb: $scope.device.httpVerb,
contentType: $scope.device.contentType,
contentBody: $scope.device.contentBody,
contentBodyDim: $scope.device.contentBodyDim,
contentBodyOff: $scope.device.contentBodyOff
};
}
}
}
bridgeService.bulkAddDevice(devicesList).then(
function () {
$scope.clearDevice();
bridgeService.viewDevices();
bridgeService.viewHalDevices();
},
function (error) {
bridgeService.displayWarn("Error adding HAL devices in bulk.", error)
}
);
$scope.bulk = { devices: [] };
$scope.selectAll = false;
};
$scope.toggleSelection = function toggleSelection(deviceId) {
var idx = $scope.bulk.devices.indexOf(deviceId);
// is currently selected
if (idx > -1) {
$scope.bulk.devices.splice(idx, 1);
if($scope.bulk.devices.length === 0 && $scope.selectAll)
$scope.selectAll = false;
}
// is newly selected
else {
$scope.bulk.devices.push(deviceId);
$scope.selectAll = true;
}
};
$scope.toggleSelectAll = function toggleSelectAll() {
if($scope.selectAll) {
$scope.selectAll = false;
$scope.bulk = { devices: [] };
}
else {
$scope.selectAll = true;
for(var x = 0; x < bridgeService.state.haldevices.length; x++) {
if($scope.bulk.devices.indexOf(bridgeService.state.haldevices[x]) < 0 && !bridgeService.findDeviceByMapId(bridgeService.state.haldevices[x].haldevicename + "-" + bridgeService.state.haldevices[x].halname, bridgeService.state.haldevices[x].halname, "halDevice"))
$scope.bulk.devices.push(bridgeService.state.haldevices[x].haldevicename);
}
}
};
$scope.toggleButtons = function () {
$scope.buttonsVisible = !$scope.buttonsVisible;
if($scope.buttonsVisible)
$scope.imgButtonsUrl = "glyphicon glyphicon-minus";
else
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
};
$scope.deleteDevice = function (device) {
$scope.bridge.device = device;
ngDialog.open({
template: 'deleteDialog',
controller: 'DeleteDialogCtrl',
className: 'ngdialog-theme-default'
});
};
$scope.editDevice = function (device) {
bridgeService.editDevice(device);
$location.path('/editdevice');
};
});
app.controller('EditController', function ($scope, $location, $http, bridgeService) {
bridgeService.viewMapTypes();
$scope.bridge = bridgeService.state;
@@ -2120,10 +2334,12 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi
$scope.editDevice = function (copy) {
if($scope.device.name === "" && $scope.device.onUrl === "") {
$scope.clearDevice();
bridgeService.displayWarn("Error adding/editing device. Name has not been given.", null);
return;
}
if(($scope.device.name === $scope.bridge.olddevicename) && copy) {
$scope.clearDevice();
bridgeService.displayWarn("Error adding device. Name has not been changed from original.", null);
return;
}
if (copy)
@@ -2141,8 +2357,10 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi
bridgeService.addDevice($scope.device).then(
function () {
$scope.clearDevice();
$location.path('/');
},
function (error) {
bridgeService.displayWarn("Error adding/updating device....", error);
}
);
@@ -2208,13 +2426,13 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi
});
app.filter('configuredVeraDevices', function () {
app.filter('configuredVeraDevices', function (bridgeService) {
return function(input) {
var out = [];
if(input === undefined || input === null || input.length === undefined)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType !== undefined && input[i].mapType !== null && input[i].mapType === "veraDevice"){
if(bridgeService.deviceContainsType(input[i], "veraDevice")){
out.push(input[i]);
}
}
@@ -2222,13 +2440,13 @@ app.filter('configuredVeraDevices', function () {
}
});
app.filter('configuredVeraScenes', function () {
app.filter('configuredVeraScenes', function (bridgeService) {
return function(input) {
var out = [];
if(input === undefined || input === null || input.length === undefined)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType !== undefined && input[i].mapType !== null && input[i].mapType === "veraScene"){
if(bridgeService.deviceContainsType(input[i], "veraScene")){
out.push(input[i]);
}
}
@@ -2242,7 +2460,7 @@ app.filter('configuredNestItems', function (bridgeService) {
if(input === undefined || input === null || input.length === undefined)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType !== undefined && input[i].mapType !== null && bridgeService.aContainsB(input[i].mapType, "nest")){
if(bridgeService.deviceContainsType(input[i], "nest")){
out.push(input[i]);
}
}
@@ -2256,7 +2474,7 @@ app.filter('configuredHueItems', function (bridgeService) {
if(input === undefined || input === null || input.length === undefined)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType !== undefined && input[i].mapType !== null && bridgeService.aContainsB(input[i].mapType, "hue")){
if(bridgeService.deviceContainsType(input[i], "hue")){
out.push(input[i]);
}
}
@@ -2270,7 +2488,7 @@ app.filter('configuredHalItems', function (bridgeService) {
if(input === undefined || input === null || input.length === undefined)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType !== undefined && input[i].mapType !== null && bridgeService.aContainsB(input[i].mapType, "hal")){
if(bridgeService.deviceContainsType(input[i], "hal")){
out.push(input[i]);
}
}
@@ -2278,13 +2496,13 @@ app.filter('configuredHalItems', function (bridgeService) {
}
});
app.filter('configuredHarmonyActivities', function () {
app.filter('configuredHarmonyActivities', function (bridgeService) {
return function(input) {
var out = [];
if(input === undefined || input === null || input.length === undefined)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType !== undefined && input[i].mapType !== null && input[i].mapType === "harmonyActivity"){
if(bridgeService.deviceContainsType(input[i], "harmonyActivity")){
out.push(input[i]);
}
}
@@ -2292,13 +2510,13 @@ app.filter('configuredHarmonyActivities', function () {
}
});
app.filter('configuredHarmonyButtons', function () {
return function(input) {
app.filter('configuredHarmonyButtons', function (bridgeService) {
return function (input) {
var out = [];
if(input === undefined || input === null || input.length === undefined)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType !== undefined && input[i].mapType !== null && input[i].mapType === "harmonyButtons"){
if (bridgeService.deviceContainsType(input[i], "harmonyButton")) {
out.push(input[i]);
}
}
@@ -2306,13 +2524,13 @@ app.filter('configuredHarmonyButtons', function () {
}
});
app.filter('configuredMqttMsgs', function () {
app.filter('configuredMqttMsgs', function (bridgeService) {
return function(input) {
var out = [];
if(input === undefined || input === null || input.length === undefined)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType !== undefined && input[i].mapType !== null && input[i].mapType === "mqttMessage"){
if (bridgeService.deviceContainsType(input[i], "mqtt")) {
out.push(input[i]);
}
}
@@ -2326,7 +2544,21 @@ app.filter('configuredHassItems', function (bridgeService) {
if(input === undefined || input === null || input.length === undefined)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType !== undefined && input[i].mapType !== null && bridgeService.aContainsB(input[i].mapType, "hass")){
if (bridgeService.deviceContainsType(input[i], "hass")) {
out.push(input[i]);
}
}
return out;
}
});
app.filter('configuredDomoticzItems', function (bridgeService) {
return function(input) {
var out = [];
if(input === undefined || input === null || input.length === undefined)
return out;
for (var i = 0; i < input.length; i++) {
if (bridgeService.deviceContainsType(input[i], "domoticz")) {
out.push(input[i]);
}
}

View File

@@ -19,6 +19,7 @@
href="#!/haldevices">HAL Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>
@@ -41,6 +42,8 @@
<th sortable-header col="name">Name</th>
<th sortable-header col="deviceType">Type</th>
<th sortable-header col="targetDevice">Target</th>
<th sortable-header col="inactive">Inactive</th>
<th sortable-header col="noState">No State</th>
<th>Actions</th>
</tr>
</thead>
@@ -50,6 +53,8 @@
<td>{{device.name}}</td>
<td>{{device.deviceType}}</td>
<td>{{device.targetDevice}}</td>
<td>{{device.inactive}}</td>
<td>{{device.noState}}</td>
<td>
<p>
<button class="btn btn-info" type="submit"

View File

@@ -0,0 +1,140 @@
<ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
<li role="presentation"><a href="#!/logs">Logs</a></li>
<li ng-if="bridge.showVera" role="presentation"><a
href="#!/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a
href="#!/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a
href="#!/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a
href="#!/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
<li ng-if="bridge.showHue" role="presentation"><a
href="#!/huedevices">Hue Devices</a></li>
<li ng-if="bridge.showHal" role="presentation"><a href="#!/domoticzdevices">HAL
Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li role="presentation" class="active"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">Domoticz Device List
({{bridge.domoticzdevices.length}})</h2>
</div>
<div class="panel-body">
<p class="text-muted">For any Domoticz Device, use the build action buttons
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
you can modify the name to anything you want that will be the keyword
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
action button to add another item for a multi-command. After you are
done in the edit tab, click the 'Add Bridge Device' to finish that selection
setup. The 'Already Configured Domoticz Devices' list below will show what
is already setup for your Domoticz.</p>
<p>
Also, use this select menu for which type of dim control you would
like to be generated: <select name="device-dim-control"
id="device-dim-control" ng-model="device_dim_control">
<option value="">none</option>
<option value="${intensity.byte}">Pass-thru Value</option>
<option value="${intensity.percent}">Percentage</option>
<option value="${intensity.math(X*1)}">Custom Math</option>
</select>
</p>
<p>Use the check boxes by the names to use the bulk addition
feature. Select your items and dim control type if wanted, then click
bulk add below. Your items will be added with on and off or dim and
off if selected with the name of the device from the Domoticz.</p>
<scrollable-table watch="bridge.domoticzdevices">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th sortable-header col="name">
<span><input type="checkbox" name="selectAll"
value="{{selectAll}}"
ng-checked="selectAll"
ng-click="toggleSelectAll()"> Name</span></th>
<th sortable-header col="type">Type</th>
<th sortable-header col="domoticzname">Domoticz</th>
<th>Build Actions</th>
</tr>
</thead>
<tr ng-repeat="domoticzdevice in bridge.domoticzdevices">
<td>{{$index+1}}</td>
<td><input type="checkbox" name="bulk.devices[]"
value="{{domoticzdevice.devicename}}"
ng-checked="bulk.devices.indexOf(domoticzdevice.devicename) > -1"
ng-click="toggleSelection(domoticzdevice.devicename)">
{{domoticzdevice.devicename}}</td>
<td>{{domoticzdevice.devicetype}}</td>
<td>{{domoticzdevice.domoticzname}}</td>
<td>
<button class="btn btn-success" type="submit"
ng-click="buildDeviceUrls(domoticzdevice, device_dim_control)">Build Item</button>
</td>
</tr>
</table>
</scrollable-table>
<div class="panel-footer">
<button class="btn btn-success" type="submit"
ng-click="bulkAddDevices(device_dim_control)">Bulk Add
({{bulk.devices.length}})</button>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
Already Configured Domoticz Devices <a ng-click="toggleButtons()"><span
class={{imgButtonsUrl}} aria-hidden="true"></span></a></a>
</h2>
</div>
<div ng-if="buttonsVisible" class="panel-body">
<scrollable-table watch="bridge.domoticzdevices">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th sortable-header col="name">Name</th>
<th sortable-header col="category">Category</th>
<th sortable-header col="domoticzname">Domoticz</th>
<th>Map Id</th>
<th>Actions</th>
</tr>
</thead>
<tr
ng-repeat="device in bridge.devices | configuredDomoticzItems">
<td>{{$index+1}}</td>
<td>{{device.name}}</td>
<td>{{device.deviceType}}</td>
<td>{{device.targetDevice}}</td>
<td>{{device.mapId}}</td>
<td>
<p>
<button class="btn btn-warning" type="submit"
ng-click="editDevice(device)">Edit</button>
<button class="btn btn-danger" type="submit"
ng-click="deleteDevice(device)">Delete</button>
</p>
</td>
</tr>
</table>
</scrollable-table>
</div>
</div>
<script type="text/ng-template" id="deleteMapandIdDialog">
<div class="ngdialog-message">
<h2>Device Map and Id?</h2>
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
<p>Are you Sure?</p>
</div>
<div class="ngdialog-buttons mt">
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
</div>
</script>

View File

@@ -19,6 +19,7 @@
href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a
href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation" class="active"><a href="#!/editdevice">Add/Edit</a></li>
</ul>
@@ -62,6 +63,18 @@
<td><input type="text" class="form-control" id="device-name"
ng-model="device.name" placeholder="Device Name"></td>
</tr>
<tr>
<td>Inactive</td>
<td><input type="checkbox"
ng-model="device.inactive" ng-true-value=true
ng-false-value=false> {{device.inactive}}</td>
</tr>
<tr>
<td>No State (Do not update state for device)</td>
<td><input type="checkbox"
ng-model="device.noState" ng-true-value=true
ng-false-value=false> {{device.noState}}</td>
</tr>
<tr>
<td><label>Target</label></td>

View File

@@ -16,6 +16,7 @@
Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -17,6 +17,7 @@
href="#!/haldevices">HAL Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -17,6 +17,7 @@
href="#!/haldevices">HAL Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -16,8 +16,8 @@
<li ng-if="bridge.showHal" role="presentation" class="active"><a href="#!/haldevices">HAL
Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li role="presentation" class="active"><a href="#!/hassdevices">HomeAssistant
Devices</a></li>
<li role="presentation" class="active"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>
@@ -37,7 +37,7 @@
is already setup for your HomeAssitant.</p>
<p>
Also, use this select menu for which type of dim control you would
like to be generated: <select name="device-dim-control"
like to be generated, BUT for Home Assistant, the selection should be Pass-thru: <select name="device-dim-control"
id="device-dim-control" ng-model="device_dim_control">
<option value="">none</option>
<option value="${intensity.byte}">Pass-thru Value</option>

View File

@@ -17,6 +17,7 @@
href="#!/haldevices">HAL Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -17,6 +17,7 @@
href="#!/haldevices">HAL Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -11,6 +11,7 @@
<li ng-if="bridge.showHal" role="presentation"><a href="#!/haldevices">HAL Devices</a></li>
<li role="presentation" class="active"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -17,6 +17,7 @@
href="#!/haldevices">HAL Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -18,6 +18,7 @@
href="#!/haldevices">HAL Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>
@@ -276,6 +277,7 @@
<th>IP</th>
<th>Port</th>
<th>Password (opt)</th>
<th>Use SSL</th>
<th>Manage</th>
</tr>
</thead>
@@ -285,6 +287,7 @@
<td>{{hass.port}}</td>
<td ng-if="hass.password">*******</td>
<td ng-if="!hass.password"> </td>
<td>{{hass.secure}}</td>
<td><button class="btn btn-danger" type="submit"
ng-click="removeHasstoSettings(hass.name, hass.ip)">Del</button></td>
</tr>
@@ -301,8 +304,56 @@
<td><input id="bridge-settings-next-hass-password"
class="form-control" type="password" ng-model="newhasspassword"
placeholder="Home Assistant password (opt)"></td>
<td><input type="checkbox"
ng-model="newhasssecure" ng-true-value=true
ng-false-value=false></td>
<td><button class="btn btn-success" type="submit"
ng-click="addHasstoSettings(newhassname, newhassip, newhassport, newhasspassword)">Add</button></td>
ng-click="addHasstoSettings(newhassname, newhassip, newhassport, newhasspassword, newhasssecure)">Add</button></td>
</tr>
</table></td>
</tr>
<tr>
<td>Domoticz Names and IP Addresses</td>
<td><table
class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Name</th>
<th>IP</th>
<th>Port</th>
<th>Username (opt)</th>
<th>Password (opt)</th>
<th>Manage</th>
</tr>
</thead>
<tr ng-repeat="domoticz in bridge.settings.domoticzaddress.devices">
<td>{{domoticz.name}}</td>
<td>{{domoticz.ip}}</td>
<td>{{domoticz.port}}</td>
<td>{{domoticz.username}}</td>
<td ng-if="domoticz.password">*******</td>
<td ng-if="!domoticz.password"> </td>
<td><button class="btn btn-danger" type="submit"
ng-click="removeDomoticztoSettings(domoticz.name, domoticz.ip)">Del</button></td>
</tr>
<tr>
<td><input id="bridge-settings-next-domoticz-name"
class="form-control" type="text" ng-model="newdomoticzname"
placeholder="A Domoticz"></td>
<td><input id="bridge-settings-next-domoticz-ip"
class="form-control" type="text" ng-model="newdomoticzip"
placeholder="192.168.1.3"></td>
<td><input id="bridge-settings-next-domoticz-port"
class="form-control" type="text" ng-model="newdomoticzport"
placeholder="8080"></td>
<td><input id="bridge-settings-next-domoticz-username"
class="form-control" type="text" ng-model="newdomoticzusername"
placeholder="Domoticz username"></td>
<td><input id="bridge-settings-next-domoticz-password"
class="form-control" type="password" ng-model="newdomoticzpassword"
placeholder="Domoticz password (opt)"></td>
<td><button class="btn btn-success" type="submit"
ng-click="addDomoticztoSettings(newdomoticzname, newdomoticzip, newdomoticzport, newdomoticzusername, newdomoticzpassword)">Add</button></td>
</tr>
</table></td>
</tr>
@@ -369,7 +420,7 @@
<div class="panel-heading">
<h1 class="panel-title">
Bridge Settings Backup <a ng-click="toggle()"><span
class={{imgUrl}} aria-hidden="true"></a>
class={{imgUrl}} aria-hidden="true"></span></a>
</h1>
</div>
<div ng-if="visible" class="panel-body">

View File

@@ -16,6 +16,7 @@
href="#!/haldevices">HAL Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -16,6 +16,7 @@
href="#!/haldevices">HAL Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -0,0 +1,45 @@
package com.bwssystems.hass.test;
import java.util.Iterator;
import com.bwssystems.HABridge.plugins.domoticz.DeviceResult;
import com.bwssystems.HABridge.plugins.domoticz.Devices;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class DomoticzDeviceConstructor {
public final static String DevicesTestData = "{ \"ActTime\" : 1485295582, \"ServerTime\" : \"2017-01-24 16:06:22\", \"Sunrise\" : \"07:11\", \"Sunset\" : \"16:53\", \"result\" : [ { \"AddjMulti\" : 1.0, \"AddjMulti2\" : 1.0, \"AddjValue\" : 0.0, \"AddjValue2\" : 0.0, \"BatteryLevel\" : 255, \"CustomImage\" : 2, \"Data\" : \"On\", \"Description\" : \"\", \"Favorite\" : 1, \"HardwareID\" : 3, \"HardwareName\" : \"MyHue\", \"HardwareType\" : \"Philips Hue Bridge\", \"HardwareTypeVal\" : 38, \"HaveDimmer\" : true, \"HaveGroupCmd\" : false, \"HaveTimeout\" : false, \"ID\" : \"1\", \"Image\" : \"TV\", \"IsSubDevice\" : false, \"LastUpdate\" : \"2017-01-23 17:15:22\", \"Level\" : 0, \"LevelInt\" : 0, \"MaxDimLevel\" : 100, \"Name\" : \"TV\", \"Notifications\" : \"false\", \"PlanID\" : \"0\", \"PlanIDs\" : [ 0 ], \"Protected\" : false, \"ShowNotifications\" : true, \"SignalLevel\" : \"-\", \"Status\" : \"On\", \"StrParam1\" : \"\", \"StrParam2\" : \"\", \"SubType\" : \"RGBW\", \"SwitchType\" : \"On/Off\", \"SwitchTypeVal\" : 0, \"Timers\" : \"false\", \"Type\" : \"Lighting Limitless/Applamp\", \"TypeImg\" : \"lightbulb\", \"Unit\" : 1, \"Used\" : 1, \"UsedByCamera\" : false, \"XOffset\" : \"0\", \"YOffset\" : \"0\", \"idx\" : \"23\" }, { \"AddjMulti\" : 1.0, \"AddjMulti2\" : 1.0, \"AddjValue\" : 0.0, \"AddjValue2\" : 0.0, \"BatteryLevel\" : 255, \"CustomImage\" : 0, \"Data\" : \"On\", \"Description\" : \"\", \"Favorite\" : 1, \"HardwareID\" : 3, \"HardwareName\" : \"MyHue\", \"HardwareType\" : \"Philips Hue Bridge\", \"HardwareTypeVal\" : 38, \"HaveDimmer\" : true, \"HaveGroupCmd\" : false, \"HaveTimeout\" : false, \"ID\" : \"0000000B\", \"Image\" : \"Light\", \"IsSubDevice\" : false, \"LastUpdate\" : \"2017-01-23 16:15:31\", \"Level\" : 0, \"LevelInt\" : 0, \"MaxDimLevel\" : 100, \"Name\" : \"lights\", \"Notifications\" : \"false\", \"PlanID\" : \"0\", \"PlanIDs\" : [ 0 ], \"Protected\" : false, \"ShowNotifications\" : true, \"SignalLevel\" : \"-\", \"Status\" : \"On\", \"StrParam1\" : \"\", \"StrParam2\" : \"\", \"SubType\" : \"RGBW\", \"SwitchType\" : \"On/Off\", \"SwitchTypeVal\" : 0, \"Timers\" : \"false\", \"Type\" : \"Lighting Limitless/Applamp\", \"TypeImg\" : \"lightbulb\", \"Unit\" : 1, \"Used\" : 1, \"UsedByCamera\" : false, \"XOffset\" : \"0\", \"YOffset\" : \"0\", \"idx\" : \"25\" }, { \"AddjMulti\" : 1.0, \"AddjMulti2\" : 1.0, \"AddjValue\" : 0.0, \"AddjValue2\" : 0.0, \"BatteryLevel\" : 255, \"CustomImage\" : 0, \"Data\" : \"Off\", \"Description\" : \"\", \"Favorite\" : 1, \"HardwareID\" : 3, \"HardwareName\" : \"MyHue\", \"HardwareType\" : \"Philips Hue Bridge\", \"HardwareTypeVal\" : 38, \"HaveDimmer\" : true, \"HaveGroupCmd\" : false, \"HaveTimeout\" : false, \"ID\" : \"00000014\", \"Image\" : \"Light\", \"IsSubDevice\" : false, \"LastUpdate\" : \"2017-01-23 11:25:59\", \"Level\" : 0, \"LevelInt\" : 0, \"MaxDimLevel\" : 100, \"Name\" : \"testUDP\", \"Notifications\" : \"false\", \"PlanID\" : \"0\", \"PlanIDs\" : [ 0 ], \"Protected\" : false, \"ShowNotifications\" : true, \"SignalLevel\" : \"-\", \"Status\" : \"Off\", \"StrParam1\" : \"\", \"StrParam2\" : \"\", \"SubType\" : \"RGBW\", \"SwitchType\" : \"Dimmer\", \"SwitchTypeVal\" : 7, \"Timers\" : \"false\", \"Type\" : \"Lighting Limitless/Applamp\", \"TypeImg\" : \"dimmer\", \"Unit\" : 1, \"Used\" : 1, \"UsedByCamera\" : false, \"XOffset\" : \"0\", \"YOffset\" : \"0\", \"idx\" : \"35\" }, { \"AddjMulti\" : 1.0, \"AddjMulti2\" : 1.0, \"AddjValue\" : 0.0, \"AddjValue2\" : 0.0, \"BatteryLevel\" : 255, \"CustomImage\" : 0, \"Data\" : \"Off\", \"Description\" : \"\", \"Favorite\" : 1, \"HardwareID\" : 3, \"HardwareName\" : \"MyHue\", \"HardwareType\" : \"Philips Hue Bridge\", \"HardwareTypeVal\" : 38, \"HaveDimmer\" : true, \"HaveGroupCmd\" : false, \"HaveTimeout\" : false, \"ID\" : \"00000009\", \"Image\" : \"Light\", \"IsSubDevice\" : false, \"LastUpdate\" : \"2017-01-24 09:18:22\", \"Level\" : 94, \"LevelInt\" : 94, \"MaxDimLevel\" : 100, \"Name\" : \"Test Light on CM15 (PL) N1\", \"Notifications\" : \"false\", \"PlanID\" : \"0\", \"PlanIDs\" : [ 0 ], \"Protected\" : false, \"ShowNotifications\" : true, \"SignalLevel\" : \"-\", \"Status\" : \"Off\", \"StrParam1\" : \"\", \"StrParam2\" : \"\", \"SubType\" : \"RGBW\", \"SwitchType\" : \"Dimmer\", \"SwitchTypeVal\" : 7, \"Timers\" : \"false\", \"Type\" : \"Lighting Limitless/Applamp\", \"TypeImg\" : \"dimmer\", \"Unit\" : 1, \"Used\" : 1, \"UsedByCamera\" : false, \"XOffset\" : \"0\", \"YOffset\" : \"0\", \"idx\" : \"44\" } ], \"status\" : \"OK\", \"title\" : \"Devices\" }";
public final static String ScenesTestData = "{ \"ActTime\" : 1485295431, \"AllowWidgetOrdering\" : true, \"ServerTime\" : \"2017-01-24 16:03:51\", \"Sunrise\" : \"07:11\", \"Sunset\" : \"16:53\", \"result\" : [ { \"Description\" : \"\", \"Favorite\" : 0, \"LastUpdate\" : \"2017-01-23 11:06:31\", \"Name\" : \"Watch TV\", \"OffAction\" : \"\", \"OnAction\" : \"\", \"Protected\" : false, \"Status\" : \"On\", \"Timers\" : \"false\", \"Type\" : \"Scene\", \"UsedByCamera\" : false, \"idx\" : \"1\" }, { \"Description\" : \"\", \"Favorite\" : 0, \"LastUpdate\" : \"2017-01-23 11:25:58\", \"Name\" : \"TestScene\", \"OffAction\" : \"\", \"OnAction\" : \"\", \"Protected\" : false, \"Status\" : \"Off\", \"Timers\" : \"false\", \"Type\" : \"Scene\", \"UsedByCamera\" : false, \"idx\" : \"2\" } ], \"status\" : \"OK\", \"title\" : \"Scenes\" }";
public static void main(String[] args){
DomoticzDeviceConstructor aTestService = new DomoticzDeviceConstructor();
if(aTestService.validateStructure())
System.out.println("Test Successful");
}
public Boolean validateStructure() {
Gson aGson;
try {
aGson = new GsonBuilder()
// .registerTypeAdapter(Service.class, new ServiceDeserializer())
// .registerTypeHierarchyAdapter(Field.class, new FieldDeserializer())
.create();
System.out.println("Decode Domoticz Devices Data");
Devices aDeviceContainer = aGson.fromJson(DevicesTestData, Devices.class);
Iterator<DeviceResult> aList = aDeviceContainer.getResult().iterator();
while(aList.hasNext()) {
DeviceResult theResult = aList.next();
System.out.println(" " + theResult.getName() + " - " + theResult.getDescription() + " - " + theResult.getType());
}
System.out.println("Decode Domoticz Sceness Data");
aDeviceContainer = aGson.fromJson(ScenesTestData, Devices.class);
aList = aDeviceContainer.getResult().iterator();
while(aList.hasNext()) {
DeviceResult theResult = aList.next();
System.out.println(" " + theResult.getName() + " - " + theResult.getDescription() + " - " + theResult.getType());
}
} catch (Exception e) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,14 @@
package com.bwssystems.hass.test;
import org.junit.Assert;
import org.junit.Test;
public class DomoticzStructureTestCase {
@Test
public void testValidateStructure() {
DomoticzDeviceConstructor aTestService = new DomoticzDeviceConstructor();
Assert.assertEquals(aTestService.validateStructure(), true);
}
}