mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-18 16:17:30 +00:00
Compare commits
194 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bcec27861 | ||
|
|
bb0ffeb570 | ||
|
|
0c4292bfd7 | ||
|
|
0083c5854f | ||
|
|
c13b9bd8f4 | ||
|
|
ca5a6c6667 | ||
|
|
c9c6d6e66d | ||
|
|
c8b1827150 | ||
|
|
d7d83e866e | ||
|
|
fb24e9d1a3 | ||
|
|
d15a1c58d0 | ||
|
|
08c87eb3aa | ||
|
|
7da4bf13e0 | ||
|
|
cd701ca02e | ||
|
|
1b676632fe | ||
|
|
62e366c028 | ||
|
|
838b86a266 | ||
|
|
ed8fc95782 | ||
|
|
f6cb41b880 | ||
|
|
a578aa9fd8 | ||
|
|
7b97bd75ae | ||
|
|
4de14217b4 | ||
|
|
02918dc49d | ||
|
|
3a8c64aac6 | ||
|
|
10df2f1c3e | ||
|
|
c7cf48bb6b | ||
|
|
54e9303708 | ||
|
|
6b4344bbe8 | ||
|
|
745986f08f | ||
|
|
5e59b33ed7 | ||
|
|
c45cb24b20 | ||
|
|
adc34ddaa6 | ||
|
|
88f34f3221 | ||
|
|
15223630eb | ||
|
|
2f456aa0d0 | ||
|
|
201aaa8bca | ||
|
|
6e7b48aa5b | ||
|
|
afd1af4094 | ||
|
|
61156e9820 | ||
|
|
6116d37675 | ||
|
|
f8474f5f41 | ||
|
|
0305646b4f | ||
|
|
3fea7f4f1a | ||
|
|
44fbaa68f8 | ||
|
|
dd0032a567 | ||
|
|
470f6b3c15 | ||
|
|
3016712ad8 | ||
|
|
2fbf26a5fa | ||
|
|
6b3ae1b971 | ||
|
|
71258c7e52 | ||
|
|
ee066f1449 | ||
|
|
e5871e61b5 | ||
|
|
6b8a714959 | ||
|
|
cf3ec7cfe4 | ||
|
|
805aac9fde | ||
|
|
1221df4c96 | ||
|
|
1c897e3b36 | ||
|
|
0ac8061118 | ||
|
|
65b0d6e470 | ||
|
|
99e2243e2d | ||
|
|
7a354619d0 | ||
|
|
2dc245bb96 | ||
|
|
c679548bbd | ||
|
|
16a248ba8e | ||
|
|
0ce23c0f00 | ||
|
|
babf81ea31 | ||
|
|
1c7260600a | ||
|
|
f1592a1998 | ||
|
|
24dd427fb4 | ||
|
|
f8de640f5d | ||
|
|
a091262b80 | ||
|
|
0bcc1628c8 | ||
|
|
ffd95aad87 | ||
|
|
b5e6e3ad60 | ||
|
|
611cc7be4a | ||
|
|
4b7ba0fabe | ||
|
|
6abe1b082c | ||
|
|
b08a285bd0 | ||
|
|
e68282a230 | ||
|
|
c9d55e26ac | ||
|
|
5a3a02cb34 | ||
|
|
a9f48e1f9c | ||
|
|
8b9fd355b4 | ||
|
|
b4da321368 | ||
|
|
881f739c0b | ||
|
|
f148c2b9fc | ||
|
|
fd486588f5 | ||
|
|
d118dd8523 | ||
|
|
58e1679529 | ||
|
|
104d341864 | ||
|
|
7772a3de0f | ||
|
|
a348086910 | ||
|
|
31cfcb650b | ||
|
|
90d56ab686 | ||
|
|
8dad922eec | ||
|
|
e5f088cfb9 | ||
|
|
a550288847 | ||
|
|
e0be1507f6 | ||
|
|
e9dc023971 | ||
|
|
56ebe2d929 | ||
|
|
aecadc529b | ||
|
|
7a55f6875f | ||
|
|
382828f615 | ||
|
|
cd62538785 | ||
|
|
b644b5e3cc | ||
|
|
6f544e1a7d | ||
|
|
4744a68906 | ||
|
|
010260429e | ||
|
|
e694660af3 | ||
|
|
a0a2d90617 | ||
|
|
5a292c98a2 | ||
|
|
9155989791 | ||
|
|
bf9a332ecf | ||
|
|
123ac65723 | ||
|
|
9c4eb58739 | ||
|
|
363fb605a6 | ||
|
|
ea6c31494b | ||
|
|
a3325aa048 | ||
|
|
66d7306cda | ||
|
|
b7d6d099a6 | ||
|
|
e36145f216 | ||
|
|
2e177f3d4a | ||
|
|
d827605fa5 | ||
|
|
729d548d0e | ||
|
|
ca6b1ae32d | ||
|
|
8cd571c183 | ||
|
|
40a9eb95ac | ||
|
|
3a3e5180b1 | ||
|
|
4225f4554e | ||
|
|
b5a3bea803 | ||
|
|
2579949a23 | ||
|
|
c9d76bed86 | ||
|
|
1500f7fb56 | ||
|
|
2f10135b1d | ||
|
|
8b0aa6fe98 | ||
|
|
11eefa9efd | ||
|
|
74c15db202 | ||
|
|
d0a63747c7 | ||
|
|
74cccd6de3 | ||
|
|
321b63f0fb | ||
|
|
4a105b94fd | ||
|
|
7677ed08d8 | ||
|
|
2b1b70cf4f | ||
|
|
9c252a61ba | ||
|
|
460931f6e0 | ||
|
|
5746d271b1 | ||
|
|
6f58d38224 | ||
|
|
e6cde7a3bd | ||
|
|
a325a28731 | ||
|
|
bf43d5c33e | ||
|
|
dcc4041d7d | ||
|
|
a3f5b47260 | ||
|
|
e7acff9f54 | ||
|
|
9c4b428c86 | ||
|
|
80b3e87e44 | ||
|
|
e14482e232 | ||
|
|
08166c0ebf | ||
|
|
373515e1ed | ||
|
|
68f38e1d95 | ||
|
|
90f2bce282 | ||
|
|
3eba9b9245 | ||
|
|
7d39b79e05 | ||
|
|
407b0e0bd5 | ||
|
|
9baff8d403 | ||
|
|
23a6c7059c | ||
|
|
84fb79f9d9 | ||
|
|
3702de8efd | ||
|
|
f0ab9afd66 | ||
|
|
e5c4e09543 | ||
|
|
425ac9fb91 | ||
|
|
32203b65be | ||
|
|
7e9600d2a7 | ||
|
|
dcc470486f | ||
|
|
330adbdd4c | ||
|
|
23d43b0136 | ||
|
|
004276d3ea | ||
|
|
e90e0f69ef | ||
|
|
7163a12384 | ||
|
|
bb65650e53 | ||
|
|
7f7e96465b | ||
|
|
8ff7bc0120 | ||
|
|
4da5f65d89 | ||
|
|
faa67827c6 | ||
|
|
bbce1f4235 | ||
|
|
8575deadf1 | ||
|
|
ad4015927c | ||
|
|
f7df6951b0 | ||
|
|
c20d046b30 | ||
|
|
20dedec8ab | ||
|
|
580c037b1e | ||
|
|
77cb064d60 | ||
|
|
0e4319ea1d | ||
|
|
c61e623e23 | ||
|
|
669483f686 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,5 +1,5 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
text=auto
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
|
||||
490
README.md
490
README.md
@@ -1,7 +1,43 @@
|
||||
# ha-bridge
|
||||
Emulates Philips Hue api to other home automation gateways such as an Amazon Echo. 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
Here are some diagrams to put this software in perspective.
|
||||
|
||||
The Echo Path looks like this:
|
||||
```
|
||||
+------------------------+ +------------------------+
|
||||
+-------------+ | H A +------------------| | A +------------------+ |
|
||||
| Amazon Echo |----->| U P | ha-bridge core |--->| P | Device to control| |
|
||||
+-------------+ | E I +------------------| | I +------------------+ |
|
||||
+------------------------+ +------------------------+
|
||||
```
|
||||
The Google Home Path looks like this:
|
||||
```
|
||||
+------------------------+ +------------------------+
|
||||
+-------------+ | H A +------------------| | A +------------------+ |
|
||||
| Google Home |----->| U P | ha-bridge core |--->| P | Device to control| |
|
||||
+-------------+ | E I +------------------| | I +------------------+ |
|
||||
+------------------------+ +------------------------+
|
||||
```
|
||||
THe Harmony Hub Path looks like this:
|
||||
```
|
||||
+------------------------+ +------------------------+
|
||||
+-------------+ | H A +------------------| | A +------------------+ |
|
||||
| Harmony Hub |----->| U P | ha-bridge core |--->| P | Device to control| |
|
||||
+-------------+ | E I +------------------| | I +------------------+ |
|
||||
+------------------------+ +------------------------+
|
||||
```
|
||||
**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 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, Somfy Tahoma 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.
|
||||
|
||||
@@ -20,13 +56,36 @@ Then locate the jar and start the server with:
|
||||
|
||||
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-2.5.0.jar
|
||||
java -jar ha-bridge-4.3.0.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
|
||||
|
||||
For next gen Linux systems, 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
|
||||
Create the directory and make sure that ha-bridge-4.3.0.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.3.0/ha-bridge-4.3.0.jar
|
||||
```
|
||||
Create the directory and make sure that ha-bridge-4.3.0.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.3.0/ha-bridge-4.3.0.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
|
||||
|
||||
Start here to create the habridge.service unit file:
|
||||
```
|
||||
pi@raspberrypi:~ $ cd /etc/systemd/system
|
||||
pi@raspberrypi:~ $ sudo nano habridge.service
|
||||
```
|
||||
Copy the text below into the editor nano.
|
||||
```
|
||||
[Unit]
|
||||
Description=HA Bridge
|
||||
@@ -35,20 +94,34 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/amazon-echo/data/habridge.config /home/pi/amazon-echo/ha-bridge-2.5.0.jar
|
||||
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.3.0.jar
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
Save the file in the editor by hitting CTL-X and then saying Y to update and save.
|
||||
|
||||
Basic script setup to run the bridge on a pi.
|
||||
Reload the system control config:
|
||||
```
|
||||
pi@raspberrypi:~ $ sudo systemctl daemon-reload
|
||||
```
|
||||
To start the bridge:
|
||||
```
|
||||
pi@raspberrypi:~ $ sudo systemctl start habridge.service
|
||||
```
|
||||
To start the service at boot, use the `enable` command:
|
||||
```
|
||||
pi@raspberrypi:~ $ sudo systemctl enable habridge.service
|
||||
```
|
||||
To look at the log, the output goes into the system log at `/var/log/syslog':
|
||||
```
|
||||
pi@raspberrypi:~ $ tail -f /var/log/syslog
|
||||
```
|
||||
#### Basic script setup to run the bridge on a pi.
|
||||
|
||||
*NOTE ON RC.LOCAL*: Due to the way network subsystem is brought up on the pi, it uses the new systemctl to start services. The old style runlevel setup, which rc.local is part of does not get the benefit of knowing if the network has been fully realized. Starting ha-bridge from rc.local on next gen systems will cause unexpected results and issues with discovering registered devices.
|
||||
|
||||
Create the directory and make sure that ha-bridge-2.5.0.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/v2.0.6/ha-bridge-2.5.0.jar
|
||||
```
|
||||
Edit the shell script for starting:
|
||||
```
|
||||
pi@raspberrypi:~/habridge $ nano starthabridge.sh
|
||||
@@ -57,7 +130,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 /home/pi/habridge/ha-bridge-2.5.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.3.0.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:
|
||||
@@ -72,22 +146,76 @@ 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;
|
||||
}
|
||||
```
|
||||
## 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:
|
||||
```
|
||||
java -jar -Dconfig.file=/home/me/data/myhabridge.config ha-bridge-W.X.Y.jar
|
||||
```
|
||||
### -Dserver.port=`<port number>`
|
||||
The default port number for the bridge is 8080. To override what the default or what is in the configuration file for this parameter, specify -Dserver.port=`<port number>` explicitly. This is especially helpful if you are running the ha-bridge for the first time and have another application on port 8080. The command line example:
|
||||
The default port number for the bridge is 80. To override what the default or what is in the configuration file for this parameter, specify -Dserver.port=`<port number>` explicitly. This is especially helpful if you are running the ha-bridge for the first time and have another application on port 80. The command line example:
|
||||
```
|
||||
java -jar -Dserver.port=80 ha-bridge-W.X.Y.jar
|
||||
```
|
||||
Note: if using with a Google Home device, port 80 *must* be used.
|
||||
|
||||
### -Dserver.ip=`<ip address>`
|
||||
The default ip address for the bridge to listen on is all interfaces (0.0.0.0). To override what the default or what is in the configuration file for this parameter, specify -Dserver.ip=`<ip address>` explicitly. This is especially helpful if you are running the ha-bridge for the first time and have another application on that utilizes the default interface. The command line example:
|
||||
```
|
||||
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:8080 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.
|
||||
|
||||
@@ -104,34 +232,38 @@ The default location for the configuration file to contain the settings for the
|
||||
The default location for the db to contain the devices as they are added is "data/devices.db". If you would like a different filename or directory, specify `<directory>/<filename> explicitly.
|
||||
#### UPNP IP Address
|
||||
The server defaults to the first available address on the host if this is not given. This default may NOT be the correct IP that is your public IP for your host on the network. It is best to set this parameter to not have discovery issues. Replace this value with the server ipv4 address you would like to use as the address that any upnp device will call after discovery.
|
||||
#### Web Server IP Address
|
||||
The server defaults to all interfaces on the machine (0.0.0.0). Replace this value with the server ipv4 address you would like to use as the address that will bind to a specific ip address on an interface if you would like. This is only necessary if you want to isolate how access is handled to the web UI.
|
||||
#### Web Server Port
|
||||
The server defaults to running on port 8080. To override what the default is, specify a different number. ATTENTION: If you want to use any of the apps made for the Hue to control this bridge, you should set this port to 80.
|
||||
The server defaults to running on port 80. To override what the default is, specify a different number. ATTENTION: If you want to use any of the apps made for the Hue to control this bridge, you should keep this port set to 80.
|
||||
#### UPNP Response Port
|
||||
The upnp response port that will be used. The default is 50000.
|
||||
#### Vera Names and IP Addresses
|
||||
Provide IP Addresses of your Veras 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 Vera and device/scene you configure.
|
||||
Provide IP Addresses of your Veras 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 Vera and device/scene you configure.
|
||||
#### 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
|
||||
The user name of the MyHarmony.com account for the Harmony Hub. This needs to be given if you are using the Harmony Hub features.
|
||||
#### Harmony Password
|
||||
The password for the user name of the MyHarmony.com account for the Harmony Hub. This needs to be given if you are using the Harmony Hub Features.
|
||||
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. Also, an option of webhook can be called when the activity changes on the harmony hub that will send an HTTP GET call to the the address of your choosing. This can contain the replacement variables of ${activity.id} and/or ${activity.label}. Example : http://192.168.0.1/activity/${activity.id}/${activity.label} OR http://hook?a=${activity.label}
|
||||
#### 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 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.
|
||||
#### Somfy Tahoma Username
|
||||
The user name used to login to www.tahomalink.com. This needs to be provided if you're using the Somfy Tahoma features (for connecting to IO Homecontrol used by Velux among others). There is no need to give any IP address or host information as this contacts your cloud account. *Note:* you have to 'turn on' a window to open it, and 'turn off' to close.
|
||||
#### Somfy Tahoma Password
|
||||
The password associated with the Somfy Tahoma username above
|
||||
#### 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
|
||||
@@ -145,17 +277,52 @@ 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.
|
||||
|
||||
For the Helpers, each item being presented from the target system has a button such as `Generate Bridge Device`, `Build A Button` or specific tasks such as `Temp` for thermostats that is used to create the specific device parameters and give it a name in the "Add a Bridge Device..." area below. The next thing to check is the name for the bridge device that it is something that makes sense especially if you usign the ha-bridge with an Echo as this is what the Echo will interpret as the device you want. Then select the `Add Bridge Device` button to finalize it.
|
||||
For the Helpers, each item being presented from the target system has a button such as `Build Item`, `Build A Button` or specific tasks such as `Temp` for thermostats that is used to create the specific device parameters. The build action buttons will put you into the edit screen. The next thing to check is the name for the bridge device that it is something that makes sense especially if you using the ha-bridge with an Echo or Google Home as this is what the Echo or Google Home will interpret as the device you want. 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 helper tabs will also show you what you have already configured for that target type. Click on the `+` and you will see them and be able to delete them.
|
||||
#### The Manual Add Tab
|
||||
#### 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", "somfyDevice"
|
||||
|
||||
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:
|
||||
```
|
||||
@@ -165,44 +332,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} or ${intensity.percent.hex} for 0-100 or ${intensity.decimal_percent} for 0.00-1.00 or ${intensity.byte} or ${intensity.byte.hex} 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)}" or "${intensity.math(X/4).hex}". 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 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"},
|
||||
"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.
|
||||
@@ -211,20 +377,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":"exec://C:\\Users\\John\\Documents\\Applications\\putty.exe 192.168.1.1","type":"cmdDevice"},{"item":"exec://notepad.exe","type":"cmdDevice"}]
|
||||
|
||||
OR
|
||||
[{"item":"/home/pi/scripts/dothisscript.sh","type":"cmdDevice"}]
|
||||
```
|
||||
#### 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.percent.hex}", "${intensity.decimal_percent}", "${intensity.byte}", "${intensity.byte.hex}", "${intensity.math(using X in your calc)}" and "${intensity.math(using X in your calc).hex}".
|
||||
|
||||
[{"item":"C:\\Users\\John\\Documents\\Applications\\putty.exe 192.168.1.1"},
|
||||
{"item":"notepad.exe"}]
|
||||
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)}".
|
||||
|
||||
OR
|
||||
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
|
||||
|
||||
/home/me/startsomething.sh
|
||||
Also, device data can be inserted into your payloads by the use of "${device.name}", "${device.id}", "${device.uniqueid}", "${device.targetDevice}", "${device.mapId}", "${device.mapType}" and "${device.deviceType}". These work just like the dimming value replacements.
|
||||
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"}]
|
||||
|
||||
OR
|
||||
[{"item":"udp://192.168.1.1:5000/0x45${intensity.percent}55","type":"udpDevice"}]
|
||||
|
||||
[{"item":"exec://notepad.exe"}]
|
||||
[{"item":"tcp://192.168.1.1:5000/This is the intensity real value ${intensity.byte}","type":"tcpDevice"}]
|
||||
|
||||
[{"item":{"clientId":"TestClient","topic":"Yep","message":"This is the time ${time.format(yyyy-MM-ddTHH:mm:ssXXX)}"},"type":"mqttDevice"}]
|
||||
|
||||
```
|
||||
|
||||
@@ -234,41 +407,57 @@ After this Tell Alexa: "Alexa, discover my devices". If there is an issue you ca
|
||||
|
||||
Then you can say "Alexa, Turn on the office light" or whatever name you have given your configured devices.
|
||||
|
||||
Here is the table of items to use to tell Alexa what you want to do:
|
||||
Here is the table of items to use to tell Alexa what you want to do, this has changed over time due to Amazon reserving words for skills:
|
||||
|
||||
To do this... | Say this...
|
||||
--------------|------------
|
||||
ON Commands |
|
||||
| Alexa, turn on `<Device Name>`
|
||||
| Alexa, start `<Device Name>`
|
||||
| Alexa, unlock `<Device Name>`
|
||||
| Alexa, open `<Device Name>`
|
||||
| Alexa, boot up `<Device Name>`
|
||||
| Alexa, run `<Device Name>`
|
||||
| Alexa, arm `<Device Name>`
|
||||
OFF Commands |
|
||||
| Alexa, turn off `<Device Name>`
|
||||
| Alexa, stop `<Device Name>` (this one is tricky to get right)
|
||||
| Alexa, stop running `<Device Name>` (also very tricky)
|
||||
| Alexa, lock `<Device Name>`
|
||||
| Alexa, close `<Device Name>`
|
||||
| Alexa, shutdown `<Device Name>`
|
||||
| Alexa, shut `<Device Name>`
|
||||
| Alexa, disarm `<Device Name>`
|
||||
DIM Commands |
|
||||
| Alexa, brighten `<Device Name>` to `<Position>`
|
||||
| Alexa, dim `<Device Name> to <Position>`
|
||||
| Alexa, dim `<Device Name>` to `<Position>`
|
||||
| Alexa, brighten `<Device Name>`
|
||||
| Alexa, dim `<Device Name>`
|
||||
| Alexa, raise `<Device Name>` to `<Position>`
|
||||
| Alexa, lower `<Device Name>` to `<Position>`
|
||||
| Alexa, set `<Device Name>` to `<Position>`
|
||||
| Alexa, turn up `<Device Name>` to `<Position>`
|
||||
| Alexa, turn down `<Device Name>` to `<Position>`
|
||||
|
||||
To see what Alexa thinks you said, you can check in the home page for your Alexa.
|
||||
|
||||
To view or remove devices that Alexa knows about, you can use the mobile app `Menu / Settings / Connected Home` or go to http://echo.amazon.com/#cards.
|
||||
|
||||
## Google Assistant
|
||||
Google Home is supported as of v3.2.0 and forward, but only if the bridge is running on port 80.
|
||||
|
||||
Use the Google Home app on a phone to add new "home control" devices by going into `Settings / Home Control / +`
|
||||
as described [here](https://support.google.com/googlehome/answer/7124115?hl=en&ref_topic=7125624#homecontrol).
|
||||
Click on `Philips Hue` under the `Add new` section. If ha-bridge is on the same network as the
|
||||
phone as well as the Home device, then the app should quickly pass through the pairing step and
|
||||
populate with all of the devices. If instead it takes you to a Philips Hue login page, this means
|
||||
that the bridge was not properly discovered.
|
||||
|
||||
Then you can say "OK Google, Turn on the office light" or whatever name you have given your configured devices.
|
||||
|
||||
The Google Assistant can also group lights into rooms as described in the main [help article](https://support.google.com/googlehome/answer/7072090?hl=en&ref_topic=7029100).
|
||||
|
||||
Here is the table of items to use to tell Google what you want to do. Note that either "OK Google"
|
||||
or "Hey Google" can be used as a trigger.
|
||||
|
||||
To do this: | Say "Hey Google", then...
|
||||
------------|--------------------------
|
||||
To turn on/off a light | "Turn on <light name>"
|
||||
Dim a light | "Dim the <light name>"
|
||||
Brighten a light | "Brighten the <light name>"
|
||||
Set a light brightness to a certain percentage | "Set <light name> to 50%"
|
||||
Dim/Brighten lights by a certain percentage | "Dim/Brighten <light name> by 50%"
|
||||
Turn on/off all lights in room | "Turn on/off lights in <room name>"
|
||||
Turn on/off all lights | "Turn on/off all of the lights"
|
||||
|
||||
To see what Home thinks you said, you can ask "Hey Google, What did I say?" or check the history in the app.
|
||||
|
||||
New or removed devices are picked up automatically as soon as they are added/removed from ha-bridge.
|
||||
No re-discovery step is necessary.
|
||||
|
||||
## Configuration REST API Usage
|
||||
This section will describe the REST api available for configuration. The REST body examples are all formatted for easy reading, the actual body usage should be like this:
|
||||
```
|
||||
@@ -279,7 +468,7 @@ These calls can be accomplished with a REST tool using the following URLs and HT
|
||||
### Add a device
|
||||
Add a new device to the HA Bridge configuration. There is a basic examples and then three alternate examples for the add. Please note that dimming is supported as well as custom value based on the dimming number given from the echo. This is under the Dimming and Value example.
|
||||
```
|
||||
POST http://host:8080/api/devices
|
||||
POST http://host/api/devices
|
||||
```
|
||||
#### Body Arguments
|
||||
Name | Type | Description | Required
|
||||
@@ -302,19 +491,19 @@ 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
|
||||
Dimming is also supported by using the expressions ${intensity.percent} for 0-100 or ${intensity.byte} for 0-255 for straight pass through of the value.
|
||||
Dimming is also supported by using the expressions ${intensity.percent} for 0-100 or ${intensity.decimal_percent} for 0.00-1.00 or ${intensity.byte} for 0-255 for straight pass through of the value.
|
||||
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.
|
||||
@@ -326,8 +515,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.
|
||||
@@ -339,12 +528,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
|
||||
@@ -353,8 +538,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.
|
||||
@@ -362,8 +547,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
|
||||
@@ -388,14 +573,16 @@ 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.**
|
||||
```
|
||||
POST http://host:port/api/devices/<id>
|
||||
PUT http://host:port/api/devices/<id>
|
||||
```
|
||||
#### Body Arguments
|
||||
Name | Type | Description | Required
|
||||
@@ -407,7 +594,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
|
||||
@@ -420,8 +607,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
|
||||
@@ -430,8 +617,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
|
||||
@@ -446,15 +633,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
|
||||
@@ -469,8 +656,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
|
||||
@@ -483,7 +670,7 @@ This call returns a null json "{}".
|
||||
### Get HA Bridge Version
|
||||
Get current version of the HA bridge software.
|
||||
```
|
||||
GET http://host:port/api/devices/habridge/version
|
||||
GET http://host:port/system/habridge/version
|
||||
```
|
||||
#### Response
|
||||
Name | Type | Description
|
||||
@@ -633,7 +820,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
|
||||
-----|-------|-------------
|
||||
@@ -797,7 +984,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
|
||||
@@ -929,22 +1116,50 @@ ST: upnp:rootdevice\r\n
|
||||
ST: ssdp:all\r\n
|
||||
```
|
||||
|
||||
If this criteria is met, the following response is provided to the calling application:
|
||||
If this criteria is met, the following three responses are provided to the calling application:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK\r\n
|
||||
CACHE-CONTROL: max-age=86400\r\n
|
||||
EXT:\r\n
|
||||
LOCATION: http://192.168.1.1:8080/description.xml\r\n
|
||||
SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1\r\n
|
||||
ST: urn:schemas-upnp-org:device:basic:1\r\n
|
||||
"USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n
|
||||
HTTP/1.1 200 OK
|
||||
HOST: 239.255.255.250:1900
|
||||
CACHE-CONTROL: max-age=100
|
||||
EXT:
|
||||
LOCATION: http://192.168.1.1:80/description.xml
|
||||
SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0
|
||||
hue-bridgeid: 001E06FFFE123456
|
||||
ST: upnp:rootdevice
|
||||
USN: uuid:2f402f80-da50-11e1-9b23-001e06123456::upnp:rootdevice
|
||||
```
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
HOST: 239.255.255.250:1900
|
||||
CACHE-CONTROL: max-age=100
|
||||
EXT:
|
||||
LOCATION: http://192.168.1.1:80/description.xml
|
||||
SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0
|
||||
hue-bridgeid: 001E06FFFE123456
|
||||
ST: uuid:2f402f80-da50-11e1-9b23-001e06123456
|
||||
USN: uuid:2f402f80-da50-11e1-9b23-001e06123456
|
||||
```
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
HOST: 239.255.255.250:1900
|
||||
CACHE-CONTROL: max-age=100
|
||||
EXT:
|
||||
LOCATION: http://192.168.1.1:80/description.xml
|
||||
SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0
|
||||
hue-bridgeid: 001E06FFFE123456
|
||||
ST: urn:schemas-upnp-org:device:basic:1
|
||||
USN: uuid:2f402f80-da50-11e1-9b23-001e06123456
|
||||
```
|
||||
|
||||
Note that `192.168.1.1` and `12345` are replaced with the actual IP address and last 6 digits of the MAC address, respectively.
|
||||
|
||||
|
||||
### 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:8080/description.xml
|
||||
GET http://host:80/description.xml
|
||||
```
|
||||
#### Response
|
||||
```
|
||||
@@ -954,18 +1169,18 @@ GET http://host:8080/description.xml
|
||||
<major>1</major>\n
|
||||
<minor>0</minor>\n
|
||||
</specVersion>\n
|
||||
<URLBase>http://192.168.1.1:8080/</URLBase>\n
|
||||
<URLBase>http://192.168.1.1:80/</URLBase>\n
|
||||
<device>\n
|
||||
<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\n
|
||||
<friendlyName>HA-Bridge (192.168.1.1)</friendlyName>\n
|
||||
<friendlyName>Philips hue (192.168.1.1)</friendlyName>\n
|
||||
<manufacturer>Royal Philips Electronics</manufacturer>\n
|
||||
<manufacturerURL>http://www.bwssystems.com</manufacturerURL>\n
|
||||
<modelDescription>Hue Emulator for HA bridge</modelDescription>\n
|
||||
<modelName>Philips hue bridge 2012</modelName>\n
|
||||
<modelNumber>929000226503</modelNumber>\n
|
||||
<modelURL>http://www.bwssystems.com/apps.html</modelURL>\n
|
||||
<manufacturerURL>http://www.philips.com</manufacturerURL>\n
|
||||
<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>\n"
|
||||
<modelName>Philips hue bridge 2015</modelName>\n
|
||||
<modelNumber>BSB002</modelNumber>\n
|
||||
<modelURL>http://www.meethue.com</modelURL>\n
|
||||
<serialNumber>0017880ae670</serialNumber>\n
|
||||
<UDN>uuid:88f6698f-2c83-4393-bd03-cd54a9f8595</UDN>\n
|
||||
<UDN>uuid:2f402f80-da50-11e1-9b23-001788102201</UDN>\n
|
||||
<serviceList>\n
|
||||
<service>\n
|
||||
<serviceType>(null)</serviceType>\n
|
||||
@@ -995,13 +1210,8 @@ GET http://host:8080/description.xml
|
||||
</device>\n
|
||||
</root>\n
|
||||
```
|
||||
## Debugging
|
||||
To turn on debugging for the bridge, use the following extra parm in the command line:
|
||||
```
|
||||
-Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG
|
||||
```
|
||||
## 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
|
||||
```
|
||||
|
||||
41
pom.xml
41
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.bwssystems.HABridge</groupId>
|
||||
<artifactId>ha-bridge</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>4.3.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HA Bridge</name>
|
||||
@@ -15,6 +15,7 @@
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
@@ -22,13 +23,17 @@
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>Eclipse Paho Repo</id>
|
||||
<url>https://repo.eclipse.org/content/repositories/paho-releases/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>harmony-java-client</artifactId>
|
||||
<version>1.0.8</version>
|
||||
<version>1.1.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@@ -43,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>nest-controller</artifactId>
|
||||
<version>1.0.8</version>
|
||||
<version>1.0.14</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@@ -106,6 +111,26 @@
|
||||
<artifactId>smack-core</artifactId>
|
||||
<version>4.0.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>lifx-sdk-java</artifactId>
|
||||
<version>2.1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -129,7 +154,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<version>2.4.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@@ -183,6 +208,12 @@
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>org.eclipse.paho:org.eclipse.paho.client.mqttv3</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
<transformers>
|
||||
<transformer
|
||||
@@ -196,4 +227,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
@@ -9,14 +9,17 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.util.BackupHandler;
|
||||
import com.bwssystems.util.JsonTransformer;
|
||||
import com.bwssystems.HABridge.util.BackupHandler;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class BridgeSettings extends BackupHandler {
|
||||
@@ -28,6 +31,11 @@ public class BridgeSettings extends BackupHandler {
|
||||
super();
|
||||
bridgeControl = new BridgeControlDescriptor();
|
||||
theBridgeSettings = new BridgeSettingsDescriptor();
|
||||
String ipV6Stack = System.getProperty("ipV6Stack");
|
||||
if(ipV6Stack == null || !ipV6Stack.equalsIgnoreCase("true")) {
|
||||
System.setProperty("java.net.preferIPv4Stack" , "true");
|
||||
}
|
||||
|
||||
}
|
||||
public BridgeControlDescriptor getBridgeControl() {
|
||||
return bridgeControl;
|
||||
@@ -36,11 +44,10 @@ public class BridgeSettings extends BackupHandler {
|
||||
return theBridgeSettings;
|
||||
}
|
||||
public void buildSettings() {
|
||||
InetAddress address = null;
|
||||
String addressString = null;
|
||||
String theVeraAddress = null;
|
||||
String theSomfyAddress = null;
|
||||
String theHarmonyAddress = null;
|
||||
|
||||
String configFileProperty = System.getProperty("config.file");
|
||||
if(configFileProperty == null) {
|
||||
Path filePath = Paths.get(Configuration.CONFIG_FILE);
|
||||
@@ -48,6 +55,7 @@ public class BridgeSettings extends BackupHandler {
|
||||
configFileProperty = Configuration.CONFIG_FILE;
|
||||
}
|
||||
String serverPortOverride = System.getProperty("server.port");
|
||||
String serverIpOverride = System.getProperty("server.ip");
|
||||
if(configFileProperty != null)
|
||||
{
|
||||
log.info("reading from config file: " + configFileProperty);
|
||||
@@ -96,8 +104,23 @@ public class BridgeSettings extends BackupHandler {
|
||||
}
|
||||
}
|
||||
theBridgeSettings.setHarmonyAddress(theHarmonyList);
|
||||
theBridgeSettings.setHarmonyUser(System.getProperty("harmony.user"));
|
||||
theBridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd"));
|
||||
|
||||
theSomfyAddress = System.getProperty("somfy.address");
|
||||
IpList theSomfyList = null;
|
||||
if(theSomfyAddress != null) {
|
||||
try {
|
||||
theSomfyList = new Gson().fromJson(theSomfyAddress, IpList.class);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
theSomfyList = new Gson().fromJson("{devices:[{name:default,ip:" + theSomfyAddress + "}]}", IpList.class);
|
||||
} catch (Exception et) {
|
||||
log.error("Cannot parse somfy.address, not set with message: " + e.getMessage(), e);
|
||||
theSomfyList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
theBridgeSettings.setSomfyAddress(theSomfyList);
|
||||
|
||||
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
|
||||
theBridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
|
||||
theBridgeSettings.setButtonsleep(Integer.parseInt(System.getProperty("button.sleep", Configuration.DEFAULT_BUTTON_SLEEP)));
|
||||
@@ -106,34 +129,18 @@ public class BridgeSettings extends BackupHandler {
|
||||
}
|
||||
|
||||
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().equals("")) {
|
||||
try {
|
||||
log.info("Getting an IP address for this host....");
|
||||
Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();
|
||||
|
||||
while (ifs.hasMoreElements() && addressString == null) {
|
||||
NetworkInterface xface = ifs.nextElement();
|
||||
Enumeration<InetAddress> addrs = xface.getInetAddresses();
|
||||
String name = xface.getName();
|
||||
int IPsPerNic = 0;
|
||||
|
||||
while (addrs.hasMoreElements() && IPsPerNic == 0) {
|
||||
address = addrs.nextElement();
|
||||
if (InetAddressUtils.isIPv4Address(address.getHostAddress())) {
|
||||
log.debug(name + " ... has IPV4 addr " + address);
|
||||
if(!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE)|| !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS)) {
|
||||
IPsPerNic++;
|
||||
addressString = address.getHostAddress();
|
||||
log.info("Adding " + addressString + " from interface " + name + " as our default upnp config address.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
log.error("Cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
theBridgeSettings.setUpnpConfigAddress(addressString);
|
||||
addressString = checkIpAddress(null, true);
|
||||
if(addressString != null) {
|
||||
theBridgeSettings.setUpnpConfigAddress(addressString);
|
||||
log.info("Adding " + addressString + " as our default upnp config address.");
|
||||
}
|
||||
else
|
||||
log.error("Cannot get ip address of this host.");
|
||||
}
|
||||
else {
|
||||
addressString = checkIpAddress(theBridgeSettings.getUpnpConfigAddress(), false);
|
||||
if(addressString == null)
|
||||
log.warn("The upnp config address, " + theBridgeSettings.getUpnpConfigAddress() + ", does not match any known IP's on this host.");
|
||||
}
|
||||
|
||||
if(theBridgeSettings.getUpnpResponsePort() == null)
|
||||
@@ -145,16 +152,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());
|
||||
@@ -162,9 +163,18 @@ public class BridgeSettings extends BackupHandler {
|
||||
theBridgeSettings.setNestConfigured(theBridgeSettings.isValidNest());
|
||||
theBridgeSettings.setHueconfigured(theBridgeSettings.isValidHue());
|
||||
theBridgeSettings.setHalconfigured(theBridgeSettings.isValidHal());
|
||||
if(serverPortOverride != null)
|
||||
theBridgeSettings.setMqttconfigured(theBridgeSettings.isValidMQTT());
|
||||
theBridgeSettings.setHassconfigured(theBridgeSettings.isValidHass());
|
||||
theBridgeSettings.setDomoticzconfigured(theBridgeSettings.isValidDomoticz());
|
||||
theBridgeSettings.setSomfyconfigured(theBridgeSettings.isValidSomfy());
|
||||
// Lifx is either configured or not, so it does not need an update.
|
||||
if(serverPortOverride != null)
|
||||
theBridgeSettings.setServerPort(serverPortOverride);
|
||||
if(serverIpOverride != null)
|
||||
theBridgeSettings.setWebaddress(serverIpOverride);
|
||||
setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-");
|
||||
|
||||
setupInternalTestUser();
|
||||
}
|
||||
|
||||
public void loadConfig() {
|
||||
@@ -178,14 +188,15 @@ public class BridgeSettings extends BackupHandler {
|
||||
|
||||
private void _loadConfig(Path aPath) {
|
||||
String jsonContent = configReader(aPath);
|
||||
if(jsonContent == null)
|
||||
return;
|
||||
try {
|
||||
theBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettingsDescriptor.class);
|
||||
theBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettingsDescriptor.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("Issue loading values from file: " + aPath.toUri().toString() + ", Gson convert failed.");
|
||||
theBridgeSettings = new BridgeSettingsDescriptor();
|
||||
theBridgeSettings.setConfigfile(aPath.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void save(BridgeSettingsDescriptor newBridgeSettings) {
|
||||
@@ -198,6 +209,16 @@ public class BridgeSettings extends BackupHandler {
|
||||
}
|
||||
|
||||
|
||||
public void updateConfigFile() {
|
||||
log.debug("Save HA Bridge settings.");
|
||||
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
String jsonValue = aRenderer.render(theBridgeSettings);
|
||||
configWriter(jsonValue, configPath);
|
||||
_loadConfig(configPath);
|
||||
}
|
||||
|
||||
|
||||
private void configWriter(String content, Path filePath) {
|
||||
if(Files.exists(filePath) && !Files.isWritable(filePath)){
|
||||
log.error("Error file is not writable: " + filePath);
|
||||
@@ -219,6 +240,19 @@ public class BridgeSettings extends BackupHandler {
|
||||
Files.move(filePath, target);
|
||||
}
|
||||
Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE);
|
||||
|
||||
// set attributes to be for user only
|
||||
// using PosixFilePermission to set file permissions
|
||||
Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
|
||||
// add owners permission
|
||||
perms.add(PosixFilePermission.OWNER_READ);
|
||||
perms.add(PosixFilePermission.OWNER_WRITE);
|
||||
|
||||
try {
|
||||
Files.setPosixFilePermissions(filePath, perms);
|
||||
} catch(UnsupportedOperationException e) {
|
||||
log.info("Cannot set permissions for config file on this system as it is not supported. Continuing");
|
||||
}
|
||||
if(target != null)
|
||||
Files.delete(target);
|
||||
} catch (IOException e) {
|
||||
@@ -233,7 +267,6 @@ public class BridgeSettings extends BackupHandler {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
content = new String(Files.readAllBytes(filePath));
|
||||
} catch (IOException e) {
|
||||
@@ -242,4 +275,44 @@ public class BridgeSettings extends BackupHandler {
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private String checkIpAddress(String ipAddress, boolean checkForLocalhost) {
|
||||
Enumeration<NetworkInterface> ifs = null;
|
||||
try {
|
||||
ifs = NetworkInterface.getNetworkInterfaces();
|
||||
} catch(SocketException e) {
|
||||
log.error("checkIpAddress cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
String addressString = null;
|
||||
InetAddress address = null;
|
||||
while (ifs.hasMoreElements() && addressString == null) {
|
||||
NetworkInterface xface = ifs.nextElement();
|
||||
Enumeration<InetAddress> addrs = xface.getInetAddresses();
|
||||
String name = xface.getName();
|
||||
int IPsPerNic = 0;
|
||||
|
||||
while (addrs.hasMoreElements() && IPsPerNic == 0) {
|
||||
address = addrs.nextElement();
|
||||
if (InetAddressUtils.isIPv4Address(address.getHostAddress())) {
|
||||
log.debug(name + " ... has IPV4 addr " + address);
|
||||
if(checkForLocalhost && (!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE) || !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS))) {
|
||||
IPsPerNic++;
|
||||
addressString = address.getHostAddress();
|
||||
log.debug("checkIpAddress found " + addressString + " from interface " + name);
|
||||
}
|
||||
else if(ipAddress != null && ipAddress.equalsIgnoreCase(address.getHostAddress())){
|
||||
addressString = ipAddress;
|
||||
IPsPerNic++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return addressString;
|
||||
}
|
||||
private void setupInternalTestUser() {
|
||||
theBridgeSettings.setupInternalTestUser();
|
||||
if(theBridgeSettings.isSettingsChanged())
|
||||
this.updateConfigFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.bwssystems.HABridge.api.hue.HueConstants;
|
||||
import com.bwssystems.HABridge.api.hue.HueError;
|
||||
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
|
||||
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
|
||||
|
||||
public class BridgeSettingsDescriptor {
|
||||
private static final String DEFAULT_INTERNAL_USER = "thehabridgeuser";
|
||||
private static final String DEFAULT_USER_DESCRIPTION = "default_test_user";
|
||||
private String upnpconfigaddress;
|
||||
private Integer serverport;
|
||||
private Integer upnpresponseport;
|
||||
private String upnpdevicedb;
|
||||
private IpList veraaddress;
|
||||
private IpList harmonyaddress;
|
||||
private String harmonyuser;
|
||||
private String harmonypwd;
|
||||
private Integer buttonsleep;
|
||||
private boolean upnpstrict;
|
||||
private boolean traceupnp;
|
||||
@@ -32,6 +40,19 @@ public class BridgeSettingsDescriptor {
|
||||
private boolean halconfigured;
|
||||
private Map<String, WhitelistEntry> whitelist;
|
||||
private boolean settingsChanged;
|
||||
private String myechourl;
|
||||
private String webaddress;
|
||||
private IpList mqttaddress;
|
||||
private boolean mqttconfigured;
|
||||
private IpList hassaddress;
|
||||
private boolean hassconfigured;
|
||||
private String hubversion;
|
||||
private IpList domoticzaddress;
|
||||
private boolean domoticzconfigured;
|
||||
private IpList somfyaddress;
|
||||
private boolean somfyconfigured;
|
||||
|
||||
private boolean lifxconfigured;
|
||||
|
||||
public BridgeSettingsDescriptor() {
|
||||
super();
|
||||
@@ -39,12 +60,18 @@ public class BridgeSettingsDescriptor {
|
||||
this.traceupnp = false;
|
||||
this.nestconfigured = false;
|
||||
this.veraconfigured = false;
|
||||
this.somfyconfigured = false;
|
||||
this.harmonyconfigured = false;
|
||||
this.hueconfigured = false;
|
||||
this.halconfigured = false;
|
||||
this.mqttconfigured = false;
|
||||
this.hassconfigured = false;
|
||||
this.farenheit = true;
|
||||
this.whitelist = null;
|
||||
this.settingsChanged = false;
|
||||
this.myechourl = "echo.amazon.com/#cards";
|
||||
this.webaddress = "0.0.0.0";
|
||||
this.hubversion = HueConstants.HUB_VERSION;
|
||||
}
|
||||
public String getUpnpConfigAddress() {
|
||||
return upnpconfigaddress;
|
||||
@@ -79,27 +106,21 @@ public class BridgeSettingsDescriptor {
|
||||
public IpList getVeraAddress() {
|
||||
return veraaddress;
|
||||
}
|
||||
public IpList getSomfyAddress() {
|
||||
return somfyaddress;
|
||||
}
|
||||
public void setVeraAddress(IpList veraAddress) {
|
||||
this.veraaddress = veraAddress;
|
||||
}
|
||||
public void setSomfyAddress(IpList somfyAddress) {
|
||||
this.somfyaddress = somfyAddress;
|
||||
}
|
||||
public IpList getHarmonyAddress() {
|
||||
return harmonyaddress;
|
||||
}
|
||||
public void setHarmonyAddress(IpList harmonyaddress) {
|
||||
this.harmonyaddress = harmonyaddress;
|
||||
}
|
||||
public String getHarmonyUser() {
|
||||
return harmonyuser;
|
||||
}
|
||||
public void setHarmonyUser(String harmonyuser) {
|
||||
this.harmonyuser = harmonyuser;
|
||||
}
|
||||
public String getHarmonyPwd() {
|
||||
return harmonypwd;
|
||||
}
|
||||
public void setHarmonyPwd(String harmonypwd) {
|
||||
this.harmonypwd = harmonypwd;
|
||||
}
|
||||
public boolean isUpnpStrict() {
|
||||
return upnpstrict;
|
||||
}
|
||||
@@ -127,9 +148,15 @@ public class BridgeSettingsDescriptor {
|
||||
public boolean isVeraconfigured() {
|
||||
return veraconfigured;
|
||||
}
|
||||
public boolean isSomfyconfigured() {
|
||||
return somfyconfigured;
|
||||
}
|
||||
public void setVeraconfigured(boolean veraconfigured) {
|
||||
this.veraconfigured = veraconfigured;
|
||||
}
|
||||
public void setSomfyconfigured(boolean somfyconfigured) {
|
||||
this.somfyconfigured = somfyconfigured;
|
||||
}
|
||||
public boolean isHarmonyconfigured() {
|
||||
return harmonyconfigured;
|
||||
}
|
||||
@@ -208,6 +235,66 @@ public class BridgeSettingsDescriptor {
|
||||
public void setSettingsChanged(boolean settingsChanged) {
|
||||
this.settingsChanged = settingsChanged;
|
||||
}
|
||||
public String getMyechourl() {
|
||||
return myechourl;
|
||||
}
|
||||
public void setMyechourl(String myechourl) {
|
||||
this.myechourl = myechourl;
|
||||
}
|
||||
public String getWebaddress() {
|
||||
return webaddress;
|
||||
}
|
||||
public void setWebaddress(String webaddress) {
|
||||
this.webaddress = webaddress;
|
||||
}
|
||||
public IpList getMqttaddress() {
|
||||
return mqttaddress;
|
||||
}
|
||||
public void setMqttaddress(IpList mqttaddress) {
|
||||
this.mqttaddress = mqttaddress;
|
||||
}
|
||||
public boolean isMqttconfigured() {
|
||||
return mqttconfigured;
|
||||
}
|
||||
public void setMqttconfigured(boolean mqttconfigured) {
|
||||
this.mqttconfigured = mqttconfigured;
|
||||
}
|
||||
public IpList getHassaddress() {
|
||||
return hassaddress;
|
||||
}
|
||||
public void setHassaddress(IpList hassaddress) {
|
||||
this.hassaddress = hassaddress;
|
||||
}
|
||||
public boolean isHassconfigured() {
|
||||
return hassconfigured;
|
||||
}
|
||||
public void setHassconfigured(boolean hassconfigured) {
|
||||
this.hassconfigured = hassconfigured;
|
||||
}
|
||||
public String getHubversion() {
|
||||
return hubversion;
|
||||
}
|
||||
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 isLifxconfigured() {
|
||||
return lifxconfigured;
|
||||
}
|
||||
public void setLifxconfigured(boolean lifxconfigured) {
|
||||
this.lifxconfigured = lifxconfigured;
|
||||
}
|
||||
public Boolean isValidVera() {
|
||||
if(this.getVeraAddress() == null || this.getVeraAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
@@ -222,10 +309,6 @@ public class BridgeSettingsDescriptor {
|
||||
List<NamedIP> devicesList = this.getHarmonyAddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
if(this.getHarmonyPwd() == null || this.getHarmonyPwd().equals(""))
|
||||
return false;
|
||||
if(this.getHarmonyUser() == null || this.getHarmonyUser().equals(""))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidNest() {
|
||||
@@ -253,4 +336,112 @@ public class BridgeSettingsDescriptor {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidMQTT() {
|
||||
if(this.getMqttaddress() == null || this.getMqttaddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getMqttaddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidHass() {
|
||||
if(this.getHassaddress() == null || this.getHassaddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getHassaddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
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;
|
||||
}
|
||||
public Boolean isValidSomfy() {
|
||||
if(this.getSomfyAddress() == null || this.getSomfyAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getSomfyAddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidLifx() {
|
||||
return this.isLifxconfigured();
|
||||
}
|
||||
|
||||
public HueError[] validateWhitelistUser(String aUser, String userDescription, boolean strict) {
|
||||
String validUser = null;
|
||||
boolean found = false;
|
||||
if (aUser != null && !aUser.equalsIgnoreCase("undefined") && !aUser.equalsIgnoreCase("null")
|
||||
&& !aUser.equalsIgnoreCase("")) {
|
||||
if (whitelist != null) {
|
||||
Set<String> theUserIds = whitelist.keySet();
|
||||
Iterator<String> userIterator = theUserIds.iterator();
|
||||
while (userIterator.hasNext()) {
|
||||
validUser = userIterator.next();
|
||||
if (validUser.equals(aUser))
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found && !strict) {
|
||||
newWhitelistUser(aUser, userDescription);
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return HueErrorResponse.createResponse("1", "/api/" + aUser, "unauthorized user", null, null, null).getTheErrors();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void newWhitelistUser(String aUser, String userDescription) {
|
||||
if (whitelist == null) {
|
||||
whitelist = new HashMap<>();
|
||||
}
|
||||
if(userDescription == null)
|
||||
userDescription = "auto insert user";
|
||||
|
||||
whitelist.put(aUser, WhitelistEntry.createEntry(userDescription));
|
||||
setSettingsChanged(true);
|
||||
}
|
||||
|
||||
public String createWhitelistUser(String userDescription) {
|
||||
String aUser = getNewUserID();
|
||||
newWhitelistUser(aUser, userDescription);
|
||||
return aUser;
|
||||
}
|
||||
|
||||
private String getNewUserID() {
|
||||
UUID uid = UUID.randomUUID();
|
||||
StringTokenizer st = new StringTokenizer(uid.toString(), "-");
|
||||
String newUser = "";
|
||||
while (st.hasMoreTokens()) {
|
||||
newUser = newUser + st.nextToken();
|
||||
}
|
||||
|
||||
return newUser;
|
||||
}
|
||||
|
||||
public String getInternalTestUser() {
|
||||
return DEFAULT_INTERNAL_USER;
|
||||
}
|
||||
|
||||
public void setupInternalTestUser() {
|
||||
boolean found = false;
|
||||
for (String key : whitelist.keySet()) {
|
||||
if(key.equals(DEFAULT_INTERNAL_USER))
|
||||
found = true;
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
newWhitelistUser(DEFAULT_INTERNAL_USER, DEFAULT_USER_DESCRIPTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@ public class Configuration {
|
||||
public final static String DEFAULT_ADDRESS = "1.1.1.1";
|
||||
public final static String LOOP_BACK_ADDRESS = "127.0.0.1";
|
||||
public final static String LOOP_BACK_INTERFACE = "lo";
|
||||
public final static String DEFAULT_WEB_PORT = "8080";
|
||||
public final static String DEFAULT_WEB_PORT = "80";
|
||||
public final static String DEFAULT_BUTTON_SLEEP = "100";
|
||||
public static final int UPNP_DISCOVERY_PORT = 1900;
|
||||
public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250";
|
||||
public static final String CONFIG_FILE = "data/habridge.config";
|
||||
public static final int NUMBER_OF_LOG_MESSAGES = 512;
|
||||
public static final long UPNP_NOTIFY_TIMEOUT = 20000;
|
||||
}
|
||||
|
||||
87
src/main/java/com/bwssystems/HABridge/DeviceMapTypes.java
Normal file
87
src/main/java/com/bwssystems/HABridge/DeviceMapTypes.java
Normal file
@@ -0,0 +1,87 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DeviceMapTypes {
|
||||
|
||||
public final static String[] CUSTOM_DEVICE = { "custom", "Custom"};
|
||||
public final static String[] VERA_DEVICE = { "veraDevice", "Vera Device"};
|
||||
public final static String[] VERA_SCENE = { "veraScene", "Vera Scene"};
|
||||
public final static String[] HARMONY_ACTIVITY = { "harmonyActivity", "Harmony Activity"};
|
||||
public final static String[] HARMONY_BUTTON = { "harmonyButton", "Harmony Button"};
|
||||
public final static String[] NEST_HOMEAWAY = { "nestHomeAway", "Nest Home Status"};
|
||||
public final static String[] NEST_THERMO_SET = { "nestThermoSet", "Nest Thermostat"};
|
||||
public final static String[] HUE_DEVICE = { "hueDevice", "Hue Device"};
|
||||
public final static String[] HAL_DEVICE = { "halDevice", "HAL Device"};
|
||||
public final static String[] HAL_BUTTON = { "halButton", "HAL Button"};
|
||||
public final static String[] HAL_HOME = { "halHome", "HAL Home Status"};
|
||||
public final static String[] HAL_THERMO_SET = { "halThermoSet", "HAL Thermostat"};
|
||||
public final static String[] MQTT_MESSAGE = { "mqttMessage", "MQTT Message"};
|
||||
public final static String[] EXEC_DEVICE_COMPAT = { "exec", "Execute Script/Program"};
|
||||
public final static String[] CMD_DEVICE = { "cmdDevice", "Execute Command/Script/Program"};
|
||||
public final static String[] HASS_DEVICE = { "hassDevice", "HomeAssistant Device"};
|
||||
public final static String[] TCP_DEVICE = { "tcpDevice", "TCP Device"};
|
||||
public final static String[] TCP_DEVICE_COMPAT = { "TCP", "TCP Device"};
|
||||
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 String[] SOMFY_DEVICE = { "somfyDevice", "Somfy Device"};
|
||||
public final static String[] LIFX_DEVICE = { "lifxDevice", "LIFX Device"};
|
||||
|
||||
public final static int typeIndex = 0;
|
||||
public final static int displayIndex = 1;
|
||||
|
||||
ArrayList<String[]> deviceMapTypes;
|
||||
|
||||
public 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);
|
||||
deviceMapTypes.add(LIFX_DEVICE);
|
||||
deviceMapTypes.add(MQTT_MESSAGE);
|
||||
deviceMapTypes.add(NEST_HOMEAWAY);
|
||||
deviceMapTypes.add(NEST_THERMO_SET);
|
||||
deviceMapTypes.add(SOMFY_DEVICE);
|
||||
deviceMapTypes.add(TCP_DEVICE);
|
||||
deviceMapTypes.add(UDP_DEVICE);
|
||||
deviceMapTypes.add(VERA_DEVICE);
|
||||
deviceMapTypes.add(VERA_SCENE);
|
||||
deviceMapTypes.add(SOMFY_DEVICE);
|
||||
}
|
||||
public static int getTypeIndex() {
|
||||
return typeIndex;
|
||||
}
|
||||
public static int getDisplayIndex() {
|
||||
return displayIndex;
|
||||
}
|
||||
public ArrayList<String[]> getDeviceMapTypes() {
|
||||
return deviceMapTypes;
|
||||
}
|
||||
|
||||
public Boolean validateType(String type) {
|
||||
if(type == null || type.trim().isEmpty())
|
||||
return false;
|
||||
for(String[] mapType : deviceMapTypes) {
|
||||
if(type.trim().contentEquals(mapType[typeIndex]))
|
||||
return true;
|
||||
}
|
||||
if(type.trim().contentEquals(EXEC_DEVICE_COMPAT[typeIndex]))
|
||||
return true;
|
||||
if(type.trim().contentEquals(TCP_DEVICE_COMPAT[typeIndex]))
|
||||
return true;
|
||||
if(type.trim().contentEquals(UDP_DEVICE_COMPAT[typeIndex]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,7 @@ import com.bwssystems.HABridge.devicemanagmeent.*;
|
||||
import com.bwssystems.HABridge.hue.HueMulator;
|
||||
import com.bwssystems.HABridge.upnp.UpnpListener;
|
||||
import com.bwssystems.HABridge.upnp.UpnpSettingsResource;
|
||||
import com.bwssystems.NestBridge.NestHome;
|
||||
import com.bwssystems.hal.HalHome;
|
||||
import com.bwssystems.harmony.HarmonyHome;
|
||||
import com.bwssystems.hue.HueHome;
|
||||
import com.bwssystems.HABridge.util.UDPDatagramSender;
|
||||
|
||||
public class HABridge {
|
||||
|
||||
@@ -34,11 +31,9 @@ public class HABridge {
|
||||
public static void main(String[] args) {
|
||||
Logger log = LoggerFactory.getLogger(HABridge.class);
|
||||
DeviceResource theResources;
|
||||
HarmonyHome harmonyHome;
|
||||
NestHome nestHome;
|
||||
HueHome hueHome;
|
||||
HalHome halHome;
|
||||
HomeManager homeManager;
|
||||
HueMulator theHueMulator;
|
||||
UDPDatagramSender udpSender;
|
||||
UpnpSettingsResource theSettingResponder;
|
||||
UpnpListener theUpnpListener;
|
||||
SystemControl theSystem;
|
||||
@@ -52,9 +47,9 @@ public class HABridge {
|
||||
bridgeSettings = new BridgeSettings();
|
||||
while(!bridgeSettings.getBridgeControl().isStop()) {
|
||||
bridgeSettings.buildSettings();
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") initializing....");
|
||||
log.info("HA Bridge initializing....");
|
||||
// sparkjava config directive to set ip address for the web server to listen on
|
||||
// ipAddress("0.0.0.0"); // not used
|
||||
ipAddress(bridgeSettings.getBridgeSettingsDescriptor().getWebaddress());
|
||||
// sparkjava config directive to set port for the web server to listen on
|
||||
port(bridgeSettings.getBridgeSettingsDescriptor().getServerPort());
|
||||
// sparkjava config directive to set html static file location for Jetty
|
||||
@@ -62,39 +57,40 @@ public class HABridge {
|
||||
// setup system control api first
|
||||
theSystem = new SystemControl(bridgeSettings, theVersion);
|
||||
theSystem.setupServer();
|
||||
//setup the harmony connection if available
|
||||
harmonyHome = new HarmonyHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
//setup the nest connection if available
|
||||
nestHome = new NestHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
//setup the hue passtrhu configuration if available
|
||||
hueHome = new HueHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
//setup the hal configuration if available
|
||||
halHome = new HalHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
// setup the class to handle the resource setup rest api
|
||||
theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), harmonyHome, nestHome, hueHome, halHome);
|
||||
// setup the class to handle the hue emulator rest api
|
||||
theHueMulator = new HueMulator(bridgeSettings.getBridgeSettingsDescriptor(), theResources.getDeviceRepository(), harmonyHome, nestHome, hueHome);
|
||||
theHueMulator.setupServer();
|
||||
// setup the class to handle the upnp response rest api
|
||||
theSettingResponder = new UpnpSettingsResource(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
theSettingResponder.setupServer();
|
||||
// wait for the sparkjava initialization of the rest api classes to be complete
|
||||
awaitInitialization();
|
||||
|
||||
// start the upnp ssdp discovery listener
|
||||
theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl());
|
||||
if(theUpnpListener.startListening())
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") reinitialization requessted....");
|
||||
else
|
||||
bridgeSettings.getBridgeControl().setStop(true);
|
||||
if(bridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged())
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
// setup the UDP Datagram socket to be used by the HueMulator and the upnpListener
|
||||
udpSender = UDPDatagramSender.createUDPDatagramSender(bridgeSettings.getBridgeSettingsDescriptor().getUpnpResponsePort());
|
||||
if(udpSender == null) {
|
||||
bridgeSettings.getBridgeControl().setStop(true);
|
||||
}
|
||||
else {
|
||||
//Setup the device connection homes through the manager
|
||||
homeManager = new HomeManager();
|
||||
homeManager.buildHomes(bridgeSettings.getBridgeSettingsDescriptor(), udpSender);
|
||||
// setup the class to handle the resource setup rest api
|
||||
theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), homeManager);
|
||||
// setup the class to handle the upnp response rest api
|
||||
theSettingResponder = new UpnpSettingsResource(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
theSettingResponder.setupServer();
|
||||
// setup the class to handle the hue emulator rest api
|
||||
theHueMulator = new HueMulator(bridgeSettings, theResources.getDeviceRepository(), homeManager);
|
||||
theHueMulator.setupServer();
|
||||
// wait for the sparkjava initialization of the rest api classes to be complete
|
||||
awaitInitialization();
|
||||
|
||||
// start the upnp ssdp discovery listener
|
||||
theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl(), udpSender);
|
||||
if(theUpnpListener.startListening())
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") reinitialization requessted....");
|
||||
else
|
||||
bridgeSettings.getBridgeControl().setStop(true);
|
||||
if(bridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged())
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
homeManager.closeHomes();
|
||||
udpSender.closeResponseSocket();
|
||||
udpSender = null;
|
||||
}
|
||||
bridgeSettings.getBridgeControl().setReinit(false);
|
||||
stop();
|
||||
nestHome.closeTheNest();
|
||||
nestHome = null;
|
||||
harmonyHome.shutdownHarmonyHubs();
|
||||
harmonyHome = null;
|
||||
}
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting....");
|
||||
System.exit(0);
|
||||
|
||||
9
src/main/java/com/bwssystems/HABridge/Home.java
Normal file
9
src/main/java/com/bwssystems/HABridge/Home.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import com.bwssystems.HABridge.devicemanagmeent.ResourceHandler;
|
||||
import com.bwssystems.HABridge.hue.HueMulatorHandler;
|
||||
|
||||
public interface Home extends HueMulatorHandler, ResourceHandler {
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings);
|
||||
public void closeHome();
|
||||
}
|
||||
117
src/main/java/com/bwssystems/HABridge/HomeManager.java
Normal file
117
src/main/java/com/bwssystems/HABridge/HomeManager.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
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;
|
||||
import com.bwssystems.HABridge.plugins.hass.HassHome;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHome;
|
||||
import com.bwssystems.HABridge.plugins.hue.HueHome;
|
||||
import com.bwssystems.HABridge.plugins.lifx.LifxHome;
|
||||
import com.bwssystems.HABridge.plugins.mqtt.MQTTHome;
|
||||
import com.bwssystems.HABridge.plugins.somfy.SomfyHome;
|
||||
import com.bwssystems.HABridge.plugins.tcp.TCPHome;
|
||||
import com.bwssystems.HABridge.plugins.udp.UDPHome;
|
||||
import com.bwssystems.HABridge.plugins.vera.VeraHome;
|
||||
import com.bwssystems.HABridge.util.UDPDatagramSender;
|
||||
|
||||
public class HomeManager {
|
||||
Map<String, Home> homeList;
|
||||
Map<String, Home> resourceList;
|
||||
|
||||
public HomeManager() {
|
||||
homeList = new HashMap<String, Home>();
|
||||
resourceList = new HashMap<String, Home>();
|
||||
}
|
||||
|
||||
// factory method
|
||||
public void buildHomes(BridgeSettingsDescriptor bridgeSettings, UDPDatagramSender aUdpDatagramSender) {
|
||||
Home aHome = null;
|
||||
//setup the harmony connection if available
|
||||
aHome = new HarmonyHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the nest connection if available
|
||||
aHome = new NestHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.NEST_HOMEAWAY[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.NEST_HOMEAWAY[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.NEST_THERMO_SET[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the hue passtrhu configuration if available
|
||||
aHome = new HueHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the hal configuration if available
|
||||
aHome = new HalHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.HAL_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.HAL_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.HAL_BUTTON[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.HAL_HOME[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.HAL_THERMO_SET[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the mqtt handlers if available
|
||||
aHome = new MQTTHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.MQTT_MESSAGE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.MQTT_MESSAGE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the HomeAssistant configuration if available
|
||||
aHome = new HassHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the command execution Home
|
||||
aHome = new CommandHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.EXEC_DEVICE_COMPAT[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.CMD_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the http handler Home
|
||||
aHome = new HTTPHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.HTTP_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.CUSTOM_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the tcp handler Home
|
||||
aHome = new TCPHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.TCP_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.TCP_DEVICE_COMPAT[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the udp handler Home
|
||||
aHome = new UDPHome(bridgeSettings, aUdpDatagramSender);
|
||||
homeList.put(DeviceMapTypes.UDP_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.UDP_DEVICE_COMPAT[DeviceMapTypes.typeIndex], aHome);
|
||||
// Setup Vera Home if available
|
||||
aHome = new VeraHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the Domoticz configuration if available
|
||||
aHome = new DomoticzHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the Somfy configuration if available
|
||||
aHome = new SomfyHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the Lifx configuration if available
|
||||
aHome = new LifxHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
}
|
||||
|
||||
public Home findHome(String type) {
|
||||
return homeList.get(type);
|
||||
}
|
||||
public ResourceHandler findResource(String type) {
|
||||
return resourceList.get(type);
|
||||
}
|
||||
|
||||
public void closeHomes() {
|
||||
Collection<Home> theHomes = homeList.values();
|
||||
for(Home aHome : theHomes) {
|
||||
aHome.closeHome();
|
||||
}
|
||||
homeList.clear();
|
||||
homeList = null;
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,13 @@ package com.bwssystems.HABridge;
|
||||
|
||||
public class NamedIP {
|
||||
private String name;
|
||||
private String ip;
|
||||
private String ip;
|
||||
private String webhook;
|
||||
private String port;
|
||||
private String username;
|
||||
private String password;
|
||||
private Boolean secure;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -16,10 +21,34 @@ public class NamedIP {
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
public String getPort() {
|
||||
public String getWebhook() {
|
||||
return webhook;
|
||||
}
|
||||
public void setWebhook(final String webhook) {
|
||||
this.webhook = webhook;
|
||||
}
|
||||
public String getPort() {
|
||||
return port;
|
||||
}
|
||||
public void setPort(String port) {
|
||||
this.port = port;
|
||||
}
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
public Boolean getSecure() {
|
||||
return secure;
|
||||
}
|
||||
public void setSecure(Boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.dao.BackupFilename;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.bwssystems.HABridge.util.TextStringFormatter;
|
||||
import com.bwssystems.logservices.LoggerInfo;
|
||||
import com.bwssystems.logservices.LoggingForm;
|
||||
import com.bwssystems.logservices.LoggingManager;
|
||||
import com.bwssystems.util.TextStringFormatter;
|
||||
import com.bwssystems.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
@@ -62,6 +62,13 @@ public class SystemControl {
|
||||
return "{\"version\":\"" + version.getVersion() + "\"}";
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/habridge/testuser gets the valid test user for calling the api
|
||||
get (SYSTEM_CONTEXT + "/habridge/testuser", "application/json", (request, response) -> {
|
||||
log.debug("Get HA Bridge testuser: " + bridgeSettings.getBridgeSettingsDescriptor().getInternalTestUser());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return "{\"user\":\"" + bridgeSettings.getBridgeSettingsDescriptor().getInternalTestUser() + "\"}";
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/logmsgs gets the log messages for the bridge
|
||||
get (SYSTEM_CONTEXT + "/logmsgs", "application/json", (request, response) -> {
|
||||
log.debug("Get logmsgs.");
|
||||
|
||||
@@ -1,13 +1,87 @@
|
||||
package com.bwssystems.HABridge.api;
|
||||
|
||||
public class CallItem {
|
||||
private String item;
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
public String getItem() {
|
||||
public class CallItem {
|
||||
private JsonElement item;
|
||||
private Integer count;
|
||||
private Integer delay;
|
||||
private String type;
|
||||
private String filterIPs;
|
||||
private String httpVerb;
|
||||
private String httpBody;
|
||||
private String httpHeaders;
|
||||
private String contentType;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getFilterIPs() {
|
||||
return filterIPs;
|
||||
}
|
||||
|
||||
public void setFilterIPs(String filterIPs) {
|
||||
this.filterIPs = filterIPs;
|
||||
}
|
||||
|
||||
public JsonElement getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public void setItem(String anitem) {
|
||||
item = anitem;
|
||||
public void setItem(JsonElement item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public Integer getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(Integer count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public Integer getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
public void setDelay(Integer delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public String getHttpVerb() {
|
||||
return httpVerb;
|
||||
}
|
||||
|
||||
public void setHttpVerb(String httpVerb) {
|
||||
this.httpVerb = httpVerb;
|
||||
}
|
||||
|
||||
public String getHttpBody() {
|
||||
return httpBody;
|
||||
}
|
||||
|
||||
public void setHttpBody(String httpBody) {
|
||||
this.httpBody = httpBody;
|
||||
}
|
||||
|
||||
public String getHttpHeaders() {
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
public void setHttpHeaders(String httpHeaders) {
|
||||
this.httpHeaders = httpHeaders;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.bwssystems.HABridge.api;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class CallItemDeserializer implements JsonDeserializer<CallItem> {
|
||||
@Override
|
||||
public CallItem deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx)
|
||||
{
|
||||
CallItem aCallItem = new CallItem();
|
||||
JsonObject jsonObj = json.getAsJsonObject();
|
||||
JsonElement jsonElem;
|
||||
jsonElem = jsonObj.get("item");
|
||||
aCallItem.setItem(jsonElem);
|
||||
jsonElem = jsonObj.get("delay");
|
||||
aCallItem.setDelay(jsonElem.getAsInt());
|
||||
jsonElem = jsonObj.get("count");
|
||||
aCallItem.setCount(jsonElem.getAsInt());
|
||||
jsonElem = jsonObj.get("type");
|
||||
aCallItem.setType(jsonElem.getAsString());
|
||||
jsonElem = jsonObj.get("filterIPs");
|
||||
aCallItem.setFilterIPs(jsonElem.getAsString());
|
||||
|
||||
return aCallItem;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -84,7 +84,7 @@ public class DeviceResponse {
|
||||
response.setState(device.getDeviceState());
|
||||
|
||||
response.setName(device.getName());
|
||||
response.setUniqueid(device.getId());
|
||||
response.setUniqueid(device.getUniqueid());
|
||||
response.setManufacturername("Philips");
|
||||
response.setType("Dimmable light");
|
||||
response.setModelid("LWB004");
|
||||
|
||||
@@ -17,6 +17,7 @@ public class DeviceState {
|
||||
private String colormode;
|
||||
private boolean reachable;
|
||||
private List<Double> xy;
|
||||
// private int transitiontime;
|
||||
|
||||
public boolean isOn() {
|
||||
return on;
|
||||
@@ -97,7 +98,15 @@ public class DeviceState {
|
||||
public void setXy(List<Double> xy) {
|
||||
this.xy = xy;
|
||||
}
|
||||
public static DeviceState createDeviceState() {
|
||||
// public int getTransitiontime() {
|
||||
// return transitiontime;
|
||||
// }
|
||||
|
||||
// public void setTransitiontime(int transitiontime) {
|
||||
// this.transitiontime = transitiontime;
|
||||
// }
|
||||
|
||||
public static DeviceState createDeviceState() {
|
||||
DeviceState newDeviceState = new DeviceState();
|
||||
newDeviceState.fillIn();
|
||||
// newDeviceState.setColormode("none");
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
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;
|
||||
}
|
||||
@@ -23,11 +36,46 @@ public class GroupResponse {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static GroupResponse createGroupResponse(String[] theLights) {
|
||||
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) {
|
||||
theList[i] = device.getId();
|
||||
i++;
|
||||
}
|
||||
GroupResponse theResponse = new GroupResponse();
|
||||
theResponse.setAction(DeviceState.createDeviceState());
|
||||
theResponse.setName("Lightset 0");
|
||||
theResponse.setLights(theLights);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,15 @@ 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;
|
||||
private HueConfig config;
|
||||
|
||||
public HueApiResponse(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist) {
|
||||
public HueApiResponse(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist, String emulateHubVersion) {
|
||||
super();
|
||||
this.setConfig(HueConfig.createConfig(name, ipaddress, awhitelist));
|
||||
this.setConfig(HueConfig.createConfig(name, ipaddress, awhitelist, emulateHubVersion));
|
||||
this.setRules(new HashMap<>());
|
||||
this.setSensors(new HashMap<>());
|
||||
this.setSchedules(new HashMap<>());
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,17 +34,17 @@ public class HueConfig
|
||||
private String replacesbridgeid;
|
||||
private Map<String, WhitelistEntry> whitelist;
|
||||
|
||||
public static HueConfig createConfig(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist) {
|
||||
public static HueConfig createConfig(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist, String emulateHubVersion) {
|
||||
HueConfig aConfig = new HueConfig();
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
aConfig.setMac(HueConfig.getMacAddress(ipaddress));
|
||||
aConfig.setApiversion("1.10.0");
|
||||
aConfig.setApiversion(HueConstants.API_VERSION);
|
||||
aConfig.setPortalservices(false);
|
||||
aConfig.setGateway(ipaddress);
|
||||
aConfig.setSwversion("01028090");
|
||||
aConfig.setLinkbutton(false);
|
||||
aConfig.setSwversion(emulateHubVersion);
|
||||
aConfig.setLinkbutton(true);
|
||||
aConfig.setIpaddress(ipaddress);
|
||||
aConfig.setProxyport(0);
|
||||
aConfig.setSwupdate(Swupdate.createSwupdate());
|
||||
@@ -56,8 +56,8 @@ public class HueConfig
|
||||
aConfig.setLocaltime(dateFormat.format(new Date()));
|
||||
aConfig.setTimezone(TimeZone.getDefault().getID());
|
||||
aConfig.setZigbeechannel("6");
|
||||
aConfig.setBridgeid(HuePublicConfig.getBridgeIdFromMac(aConfig.getMac(), ipaddress));
|
||||
aConfig.setModelid("BSB002");
|
||||
aConfig.setBridgeid(HuePublicConfig.createConfig(name, ipaddress, emulateHubVersion).getHueBridgeIdFromMac());
|
||||
aConfig.setModelid(HueConstants.MODEL_ID);
|
||||
aConfig.setFactorynew(false);
|
||||
aConfig.setReplacesbridgeid(null);
|
||||
aConfig.setWhitelist(awhitelist);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
public class HueConstants {
|
||||
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-";
|
||||
}
|
||||
@@ -5,6 +5,11 @@ import java.util.ArrayList;
|
||||
public class HueErrorResponse {
|
||||
private ArrayList<HueError> theErrors;
|
||||
|
||||
public static HueErrorResponse createResponse(String type, String address, String description, String method_name, String resource_name, String value) {
|
||||
HueErrorResponse theErrorResp = new HueErrorResponse();
|
||||
theErrorResp.addError(new HueError(new HueErrorDetails(type, address, description, method_name, resource_name, value)));
|
||||
return theErrorResp;
|
||||
}
|
||||
public HueErrorResponse() {
|
||||
super();
|
||||
theErrors = new ArrayList<HueError>();
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
public class HuePublicConfig
|
||||
{
|
||||
@@ -20,14 +18,14 @@ public class HuePublicConfig
|
||||
private Boolean factorynew;
|
||||
private String modelid;
|
||||
|
||||
public static HuePublicConfig createConfig(String name, String ipaddress) {
|
||||
public static HuePublicConfig createConfig(String name, String ipaddress, String emulateHubVersion) {
|
||||
HuePublicConfig aConfig = new HuePublicConfig();
|
||||
aConfig.setMac(HuePublicConfig.getMacAddress(ipaddress));
|
||||
aConfig.setApiversion("1.10.0");
|
||||
aConfig.setSwversion("01028090");
|
||||
aConfig.setApiversion(HueConstants.API_VERSION);
|
||||
aConfig.setSwversion(emulateHubVersion);
|
||||
aConfig.setName(name);
|
||||
aConfig.setBridgeid(HuePublicConfig.getBridgeIdFromMac(aConfig.getMac(), ipaddress));
|
||||
aConfig.setModelid("BSB002");
|
||||
aConfig.setBridgeid(aConfig.getHueBridgeIdFromMac());
|
||||
aConfig.setModelid(HueConstants.MODEL_ID);
|
||||
aConfig.setFactorynew(false);
|
||||
aConfig.setReplacesbridgeid(null);
|
||||
|
||||
@@ -67,23 +65,22 @@ public class HuePublicConfig
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected static String getBridgeIdFromMac(String macAddr, String ipAddr)
|
||||
public String getSNUUIDFromMac()
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(macAddr, ":");
|
||||
String bridgeId = "";
|
||||
String port = null;
|
||||
StringTokenizer st = new StringTokenizer(this.getMac(), ":");
|
||||
String bridgeUUID = "";
|
||||
while(st.hasMoreTokens()) {
|
||||
bridgeId = bridgeId + st.nextToken();
|
||||
bridgeUUID = bridgeUUID + st.nextToken();
|
||||
}
|
||||
if(ipAddr.contains(":")) {
|
||||
port = ipAddr.substring(ipAddr.indexOf(":"));
|
||||
BigInteger bigInt = BigInteger.valueOf(Integer.getInteger(port).intValue());
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
bridgeId = bridgeId + DatatypeConverter.printHexBinary(theBytes);
|
||||
}
|
||||
else
|
||||
bridgeId = bridgeId + "0800";
|
||||
return bridgeId;
|
||||
bridgeUUID = bridgeUUID.toLowerCase();
|
||||
return bridgeUUID.toLowerCase();
|
||||
}
|
||||
|
||||
protected String getHueBridgeIdFromMac()
|
||||
{
|
||||
String cleanMac = this.getSNUUIDFromMac();
|
||||
String bridgeId = cleanMac.substring(0, 6) + "FFFE" + cleanMac.substring(6);
|
||||
return bridgeId.toUpperCase();
|
||||
}
|
||||
|
||||
public String getMac() {
|
||||
|
||||
@@ -11,6 +11,9 @@ public class DeviceDescriptor{
|
||||
@SerializedName("id")
|
||||
@Expose
|
||||
private String id;
|
||||
@SerializedName("uniqueid")
|
||||
@Expose
|
||||
private String uniqueid;
|
||||
@SerializedName("name")
|
||||
@Expose
|
||||
private String name;
|
||||
@@ -50,6 +53,27 @@ public class DeviceDescriptor{
|
||||
@SerializedName("contentBodyOff")
|
||||
@Expose
|
||||
private String contentBodyOff;
|
||||
@SerializedName("contentBodyDim")
|
||||
@Expose
|
||||
private String contentBodyDim;
|
||||
@SerializedName("inactive")
|
||||
@Expose
|
||||
private boolean inactive;
|
||||
@SerializedName("noState")
|
||||
@Expose
|
||||
private boolean noState;
|
||||
@SerializedName("offState")
|
||||
@Expose
|
||||
private boolean offState;
|
||||
@SerializedName("requesterAddress")
|
||||
@Expose
|
||||
private String requesterAddress;
|
||||
@SerializedName("description")
|
||||
@Expose
|
||||
private String description;
|
||||
@SerializedName("comments")
|
||||
@Expose
|
||||
private String comments;
|
||||
|
||||
private DeviceState deviceState;
|
||||
|
||||
@@ -125,6 +149,14 @@ public class DeviceDescriptor{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUniqueid() {
|
||||
return uniqueid;
|
||||
}
|
||||
|
||||
public void setUniqueid(String uniqueid) {
|
||||
this.uniqueid = uniqueid;
|
||||
}
|
||||
|
||||
public String getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
@@ -165,6 +197,14 @@ public class DeviceDescriptor{
|
||||
this.contentBodyOff = contentBodyOff;
|
||||
}
|
||||
|
||||
public String getContentBodyDim() {
|
||||
return contentBodyDim;
|
||||
}
|
||||
|
||||
public void setContentBodyDim(String contentBodyDim) {
|
||||
this.contentBodyDim = contentBodyDim;
|
||||
}
|
||||
|
||||
public DeviceState getDeviceState() {
|
||||
if(deviceState == null)
|
||||
deviceState = DeviceState.createDeviceState();
|
||||
@@ -175,4 +215,73 @@ 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 isOffState() {
|
||||
return offState;
|
||||
}
|
||||
|
||||
public void setOffState(boolean offState) {
|
||||
this.offState = offState;
|
||||
}
|
||||
|
||||
public String getRequesterAddress() {
|
||||
return requesterAddress;
|
||||
}
|
||||
|
||||
public void setRequesterAddress(String requesterAddress) {
|
||||
this.requesterAddress = requesterAddress;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public void setComments(String comments) {
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.bwssystems.HABridge.dao;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -9,14 +10,17 @@ import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.util.BackupHandler;
|
||||
import com.bwssystems.util.JsonTransformer;
|
||||
import com.bwssystems.HABridge.util.BackupHandler;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
@@ -29,7 +33,7 @@ public class DeviceRepository extends BackupHandler {
|
||||
private Map<String, DeviceDescriptor> devices;
|
||||
private Path repositoryPath;
|
||||
private Gson gson;
|
||||
final private Random random = new Random();
|
||||
private Integer nextId;
|
||||
private Logger log = LoggerFactory.getLogger(DeviceRepository.class);
|
||||
|
||||
public DeviceRepository(String deviceDb) {
|
||||
@@ -41,6 +45,7 @@ public class DeviceRepository extends BackupHandler {
|
||||
repositoryPath = null;
|
||||
repositoryPath = Paths.get(deviceDb);
|
||||
setupParams(repositoryPath, ".bk", "device.db-");
|
||||
nextId = 0;
|
||||
_loadRepository(repositoryPath);
|
||||
}
|
||||
|
||||
@@ -57,6 +62,9 @@ public class DeviceRepository extends BackupHandler {
|
||||
DeviceDescriptor list[] = gson.fromJson(jsonContent, DeviceDescriptor[].class);
|
||||
for(int i = 0; i < list.length; i++) {
|
||||
put(list[i].getId(), list[i]);
|
||||
if(Integer.decode(list[i].getId()) > nextId) {
|
||||
nextId = Integer.decode(list[i].getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,11 +74,42 @@ public class DeviceRepository extends BackupHandler {
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<DeviceDescriptor> findByDeviceType(String aType) {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
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 List<DeviceDescriptor> findAllByRequester(String anAddress) {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
List<DeviceDescriptor> theReturnList = new ArrayList<DeviceDescriptor>();
|
||||
Iterator<DeviceDescriptor> anIterator = list.iterator();
|
||||
DeviceDescriptor theDevice;
|
||||
String theRequesterAddress;
|
||||
|
||||
HashMap<String,String > addressMap;
|
||||
while (anIterator.hasNext()) {
|
||||
theDevice = anIterator.next();
|
||||
theRequesterAddress = theDevice.getRequesterAddress();
|
||||
addressMap = new HashMap<String, String>();
|
||||
if(theRequesterAddress != null) {
|
||||
if (theRequesterAddress.contains(",")) {
|
||||
String[] theArray = theRequesterAddress.split(",");
|
||||
for (String v : theArray) {
|
||||
addressMap.put(v.trim(), v.trim());
|
||||
}
|
||||
} else
|
||||
addressMap.put(theRequesterAddress, theRequesterAddress);
|
||||
}
|
||||
if (theRequesterAddress == null || theRequesterAddress.length() == 0 || addressMap.containsKey(anAddress))
|
||||
theReturnList.add(theDevice);
|
||||
}
|
||||
return theReturnList;
|
||||
}
|
||||
|
||||
public DeviceDescriptor findOne(String id) {
|
||||
return devices.get(id);
|
||||
}
|
||||
@@ -84,8 +123,17 @@ public class DeviceRepository extends BackupHandler {
|
||||
for(int i = 0; i < descriptors.length; i++) {
|
||||
if(descriptors[i].getId() != null && descriptors[i].getId().length() > 0)
|
||||
devices.remove(descriptors[i].getId());
|
||||
else
|
||||
descriptors[i].setId(String.valueOf(random.nextInt(Integer.MAX_VALUE)));
|
||||
else {
|
||||
nextId++;
|
||||
descriptors[i].setId(String.valueOf(nextId));
|
||||
}
|
||||
if(descriptors[i].getUniqueid() == null || descriptors[i].getUniqueid().length() == 0) {
|
||||
BigInteger bigInt = BigInteger.valueOf(Integer.decode(descriptors[i].getId()));
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||
|
||||
descriptors[i].setUniqueid("00:17:88:5E:D3:" + hexValue + "-" + hexValue);
|
||||
}
|
||||
put(descriptors[i].getId(), descriptors[i]);
|
||||
theNames = theNames + " " + descriptors[i].getName() + ", ";
|
||||
}
|
||||
@@ -94,6 +142,28 @@ public class DeviceRepository extends BackupHandler {
|
||||
log.debug("Save device(s): " + theNames);
|
||||
}
|
||||
|
||||
public void renumber() {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
Iterator<DeviceDescriptor> deviceIterator = list.iterator();
|
||||
Map<String, DeviceDescriptor> newdevices = new HashMap<String, DeviceDescriptor>();;
|
||||
nextId = 0;
|
||||
log.debug("Renumber devices.");
|
||||
while(deviceIterator.hasNext()) {
|
||||
nextId++;
|
||||
DeviceDescriptor theDevice = deviceIterator.next();
|
||||
theDevice.setId(String.valueOf(nextId));
|
||||
BigInteger bigInt = BigInteger.valueOf(nextId);
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||
|
||||
theDevice.setUniqueid("00:17:88:5E:D3:" + hexValue + "-" + hexValue);
|
||||
newdevices.put(theDevice.getId(), theDevice);
|
||||
}
|
||||
devices = newdevices;
|
||||
String jsonValue = gson.toJson(findAll());
|
||||
repositoryWriter(jsonValue, repositoryPath);
|
||||
}
|
||||
|
||||
public String delete(DeviceDescriptor aDescriptor) {
|
||||
if (aDescriptor != null) {
|
||||
devices.remove(aDescriptor.getId());
|
||||
|
||||
@@ -16,19 +16,17 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.HomeManager;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.BackupFilename;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.dao.DeviceRepository;
|
||||
import com.bwssystems.HABridge.dao.ErrorMessage;
|
||||
import com.bwssystems.NestBridge.NestHome;
|
||||
import com.bwssystems.hal.HalHome;
|
||||
import com.bwssystems.harmony.HarmonyHome;
|
||||
import com.bwssystems.hue.HueHome;
|
||||
import com.bwssystems.luupRequests.Device;
|
||||
import com.bwssystems.luupRequests.Scene;
|
||||
import com.bwssystems.util.JsonTransformer;
|
||||
import com.bwssystems.vera.VeraHome;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
spark core server for bridge configuration
|
||||
@@ -37,41 +35,14 @@ public class DeviceResource {
|
||||
private static final String API_CONTEXT = "/api/devices";
|
||||
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
|
||||
private DeviceRepository deviceRepository;
|
||||
private VeraHome veraHome;
|
||||
private HarmonyHome myHarmonyHome;
|
||||
private NestHome nestHome;
|
||||
private HueHome hueHome;
|
||||
private HalHome halHome;
|
||||
private HomeManager homeManager;
|
||||
private Gson aGsonHandler;
|
||||
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
|
||||
|
||||
public DeviceResource(BridgeSettingsDescriptor theSettings, HarmonyHome theHarmonyHome, NestHome aNestHome, HueHome aHueHome, HalHome aHalHome) {
|
||||
public DeviceResource(BridgeSettingsDescriptor theSettings, HomeManager aHomeManager) {
|
||||
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
|
||||
|
||||
if(theSettings.isValidVera())
|
||||
this.veraHome = new VeraHome(theSettings);
|
||||
else
|
||||
this.veraHome = null;
|
||||
|
||||
if(theSettings.isValidHarmony())
|
||||
this.myHarmonyHome = theHarmonyHome;
|
||||
else
|
||||
this.myHarmonyHome = null;
|
||||
|
||||
if(theSettings.isValidNest())
|
||||
this.nestHome = aNestHome;
|
||||
else
|
||||
this.nestHome = null;
|
||||
|
||||
if(theSettings.isValidHue())
|
||||
this.hueHome = aHueHome;
|
||||
else
|
||||
this.hueHome = null;
|
||||
|
||||
if(theSettings.isValidHal())
|
||||
this.halHome = aHalHome;
|
||||
else
|
||||
this.halHome = null;
|
||||
|
||||
homeManager = aHomeManager;
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
setupEndpoints();
|
||||
}
|
||||
|
||||
@@ -99,14 +70,44 @@ public class DeviceResource {
|
||||
else {
|
||||
devices = new Gson().fromJson("[" + request.body() + "]", DeviceDescriptor[].class);
|
||||
}
|
||||
CallItem[] callItems = null;
|
||||
String errorMessage = null;
|
||||
for(int i = 0; i < devices.length; i++) {
|
||||
if(devices[i].getContentBody() != null ) {
|
||||
if (devices[i].getContentType() == null || devices[i].getHttpVerb() == null || !supportedVerbs.contains(devices[i].getHttpVerb().toLowerCase())) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
log.debug("Bad http verb in create a Device(s): " + request.body());
|
||||
return new ErrorMessage("Bad http verb in create a Device(s): " + request.body() + " ");
|
||||
errorMessage = "Bad http verb in create device(s) for name: " + devices[i].getName() + " with verb: " + devices[i].getHttpVerb();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
try {
|
||||
if(devices[i].getOnUrl() != null && !devices[i].getOnUrl().isEmpty())
|
||||
callItems = aGsonHandler.fromJson(devices[i].getOnUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "Bad on URL JSON in create device(s) for name: " + devices[i].getName() + " with on URL: " + devices[i].getOnUrl();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
try {
|
||||
if(devices[i].getDimUrl() != null && !devices[i].getDimUrl().isEmpty())
|
||||
callItems = aGsonHandler.fromJson(devices[i].getDimUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "Bad dim URL JSON in create device(s) for name: " + devices[i].getName() + " with dim URL: " + devices[i].getDimUrl();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
try {
|
||||
if(devices[i].getOffUrl() != null && !devices[i].getOffUrl().isEmpty())
|
||||
callItems = aGsonHandler.fromJson(devices[i].getOffUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "Bad off URL JSON in create device(s) for name: " + devices[i].getName() + " with off URL: " + devices[i].getOffUrl();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
deviceRepository.save(devices);
|
||||
@@ -190,94 +191,99 @@ public class DeviceResource {
|
||||
|
||||
get (API_CONTEXT + "/vera/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get vera devices");
|
||||
if(veraHome == null){
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Vera is not available.");
|
||||
}
|
||||
List<Device> theDevices = veraHome.getDevices();
|
||||
if(theDevices == null) {
|
||||
response.status(HttpStatus.SC_SERVICE_UNAVAILABLE);
|
||||
return new ErrorMessage("A Vera request failed to get devices. Check your Vera IP addresses.");
|
||||
}
|
||||
else
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return theDevices;
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/vera/scenes", "application/json", (request, response) -> {
|
||||
log.debug("Get vera scenes");
|
||||
if(veraHome == null){
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Vera is not available.");
|
||||
}
|
||||
List<Scene> theScenes = veraHome.getScenes();
|
||||
if(theScenes == null) {
|
||||
response.status(HttpStatus.SC_SERVICE_UNAVAILABLE);
|
||||
return new ErrorMessage("A Vera is not available and failed to get scenes. Check your Vera IP addresses.");
|
||||
}
|
||||
else
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return theScenes;
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/harmony/activities", "application/json", (request, response) -> {
|
||||
log.debug("Get harmony activities");
|
||||
if(myHarmonyHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Harmony is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return myHarmonyHome.getActivities();
|
||||
return homeManager.findResource(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/harmony/show", "application/json", (request, response) -> {
|
||||
log.debug("Get harmony current activity");
|
||||
if(myHarmonyHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Harmony is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return myHarmonyHome.getCurrentActivities();
|
||||
return homeManager.findResource(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex]).getItems("current_activity");
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/harmony/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get harmony devices");
|
||||
if(myHarmonyHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Harmony is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return myHarmonyHome.getDevices();
|
||||
return homeManager.findResource(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/nest/items", "application/json", (request, response) -> {
|
||||
log.debug("Get nest items");
|
||||
if(nestHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Nest is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return nestHome.getItems();
|
||||
return homeManager.findResource(DeviceMapTypes.NEST_HOMEAWAY[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.NEST_HOMEAWAY[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/hue/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get hue items");
|
||||
if(hueHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Hue is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return hueHome.getDevices();
|
||||
return homeManager.findResource(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/hal/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get hal items");
|
||||
if(halHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Hal is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return halHome.getDevices();
|
||||
return homeManager.findResource(DeviceMapTypes.HAL_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HAL_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/mqtt/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get MQTT brokers");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.MQTT_MESSAGE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.MQTT_MESSAGE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/hass/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get HomeAssistant Clients");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
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 + "/lifx/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get LIFX devices");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/somfy/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get somfy devices");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/map/types", "application/json", (request, response) -> {
|
||||
log.debug("Get map types");
|
||||
return new DeviceMapTypes().getDeviceMapTypes();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/devices/exec/renumber CORS request
|
||||
options(API_CONTEXT + "/exec/renumber", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (API_CONTEXT + "/exec/renumber", "application/json", (request, response) -> {
|
||||
log.debug("Renumber devices.");
|
||||
deviceRepository.renumber();
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/backup/available", "application/json", (request, response) -> {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.bwssystems.HABridge.devicemanagmeent;
|
||||
|
||||
public interface ResourceHandler {
|
||||
public Object getItems(String type);
|
||||
}
|
||||
156
src/main/java/com/bwssystems/HABridge/hue/BrightnessDecode.java
Normal file
156
src/main/java/com/bwssystems/HABridge/hue/BrightnessDecode.java
Normal file
@@ -0,0 +1,156 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.Conversion;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import net.java.dev.eval.Expression;
|
||||
|
||||
public class BrightnessDecode {
|
||||
private static final Logger log = LoggerFactory.getLogger(BrightnessDecode.class);
|
||||
private static final String INTENSITY_PERCENT = "${intensity.percent}";
|
||||
private static final String INTENSITY_DECIMAL_PERCENT = "${intensity.decimal_percent}";
|
||||
private static final String INTENSITY_BYTE = "${intensity.byte}";
|
||||
private static final String INTENSITY_MATH = "${intensity.math(";
|
||||
private static final String INTENSITY_MATH_VALUE = "X";
|
||||
private static final String INTENSITY_MATH_CLOSE = ")}";
|
||||
private static final String INTENSITY_MATH_CLOSE_HEX = ").hex}";
|
||||
private static final String INTENSITY_PERCENT_HEX = "${intensity.percent.hex}";
|
||||
private static final String INTENSITY_BYTE_HEX = "${intensity.byte.hex}";
|
||||
|
||||
public static int calculateIntensity(int setIntensity, Integer targetBri, Integer targetBriInc) {
|
||||
if (targetBri != null) {
|
||||
setIntensity = targetBri;
|
||||
} else if (targetBriInc != null) {
|
||||
if ((setIntensity + targetBriInc) <= 0)
|
||||
setIntensity = targetBriInc;
|
||||
else if ((setIntensity + targetBriInc) > 254)
|
||||
setIntensity = targetBriInc;
|
||||
else
|
||||
setIntensity = setIntensity + targetBriInc;
|
||||
}
|
||||
return setIntensity;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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-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
|
||||
*/
|
||||
public static String replaceIntensityValue(String request, int intensity, boolean isHex) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
boolean notDone = true;
|
||||
String replaceValue = null;
|
||||
String replaceTarget = null;
|
||||
int percentBrightness = (int) Math.round(intensity / 255.0 * 100);
|
||||
float decimalBrightness = (float) (intensity / 255.0);
|
||||
Map<String, BigDecimal> variables = new HashMap<String, BigDecimal>();
|
||||
String mathDescriptor = null;
|
||||
|
||||
while(notDone) {
|
||||
notDone = false;
|
||||
if (request.contains(INTENSITY_BYTE)) {
|
||||
if (isHex) {
|
||||
replaceValue = convertToHex(intensity);
|
||||
} else {
|
||||
replaceValue = String.valueOf(intensity);
|
||||
}
|
||||
replaceTarget = INTENSITY_BYTE;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_BYTE_HEX)) {
|
||||
replaceValue = convertToHex(intensity);
|
||||
replaceTarget = INTENSITY_BYTE_HEX;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_PERCENT)) {
|
||||
if (isHex) {
|
||||
replaceValue = convertToHex(percentBrightness);
|
||||
} else {
|
||||
replaceValue = String.valueOf(percentBrightness);
|
||||
}
|
||||
replaceTarget = INTENSITY_PERCENT;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_PERCENT_HEX)) {
|
||||
replaceValue = convertToHex(percentBrightness);
|
||||
replaceTarget = INTENSITY_PERCENT_HEX;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_DECIMAL_PERCENT)) {
|
||||
replaceValue = String.format("%1.2f", decimalBrightness);
|
||||
replaceTarget = INTENSITY_DECIMAL_PERCENT;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_MATH_CLOSE)) {
|
||||
mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(),
|
||||
request.indexOf(INTENSITY_MATH_CLOSE));
|
||||
variables.put(INTENSITY_MATH_VALUE, new BigDecimal(intensity));
|
||||
|
||||
log.debug("Math eval is: " + mathDescriptor + ", Where " + INTENSITY_MATH_VALUE + " is: "
|
||||
+ String.valueOf(intensity));
|
||||
Integer endResult = calculateMath(variables, mathDescriptor);
|
||||
if(endResult != null) {
|
||||
if (isHex) {
|
||||
replaceValue = convertToHex(endResult);
|
||||
} else {
|
||||
replaceValue = endResult.toString();
|
||||
}
|
||||
replaceTarget = INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE;
|
||||
notDone = true;
|
||||
}
|
||||
} else if (request.contains(INTENSITY_MATH_CLOSE_HEX)) {
|
||||
mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(),
|
||||
request.indexOf(INTENSITY_MATH_CLOSE_HEX));
|
||||
variables.put(INTENSITY_MATH_VALUE, new BigDecimal(intensity));
|
||||
|
||||
Integer endResult = calculateMath(variables, mathDescriptor);
|
||||
if(endResult != null) {
|
||||
if (isHex) {
|
||||
replaceValue = convertToHex(endResult);
|
||||
} else {
|
||||
replaceValue = endResult.toString();
|
||||
}
|
||||
replaceTarget = INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE_HEX;
|
||||
notDone = true;
|
||||
}
|
||||
}
|
||||
if(notDone)
|
||||
request = request.replace(replaceTarget, replaceValue);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
// Helper Method
|
||||
public static String calculateReplaceIntensityValue(String request, int theIntensity, Integer targetBri, Integer targetBriInc, boolean isHex) {
|
||||
return replaceIntensityValue(request, calculateIntensity(theIntensity, targetBri, targetBriInc), isHex);
|
||||
}
|
||||
|
||||
// Apache Commons Conversion utils likes little endian too much
|
||||
private static String convertToHex(int theValue) {
|
||||
String destHex = "00";
|
||||
String hexValue = Conversion.intToHex(theValue, 0, destHex, 0, 2);
|
||||
byte[] theBytes = hexValue.getBytes();
|
||||
byte[] newBytes = new byte[2];
|
||||
newBytes[0] = theBytes[1];
|
||||
newBytes[1] = theBytes[0];
|
||||
return new String(newBytes);
|
||||
}
|
||||
|
||||
private static Integer calculateMath(Map<String, BigDecimal> variables, String mathDescriptor) {
|
||||
Integer endResult = null;
|
||||
try {
|
||||
Expression exp = new Expression(mathDescriptor);
|
||||
BigDecimal result = exp.eval(variables);
|
||||
endResult = Math.round(result.floatValue());
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not execute Math: " + mathDescriptor, e);
|
||||
endResult = null;
|
||||
}
|
||||
return endResult;
|
||||
}
|
||||
}
|
||||
21
src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java
Normal file
21
src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ColorDecode {
|
||||
|
||||
public static String convertCIEtoRGB(List<Double> xy) {
|
||||
double x;
|
||||
double y;
|
||||
double Y;
|
||||
|
||||
x = xy.get(0) * 100;
|
||||
y = xy.get(1) * 100;
|
||||
Y= y;
|
||||
double R = 3.240479*((x*Y)/y) + -1.537150*Y + -0.498535*(((1-x-y)*Y)/y);
|
||||
double G = -0.969256*((x*Y)/y) + 1.875992*Y + 0.041556*(((1-x-y)*Y)/y);
|
||||
double B = 0.055648*((x*Y)/y) + -0.204043*Y + 1.057311*(((1-x-y)*Y)/y);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
|
||||
public class DeviceDataDecode {
|
||||
private static final Logger log = LoggerFactory.getLogger(DeviceDataDecode.class);
|
||||
private static final String DEVICE_ID = "${device.id}";
|
||||
private static final String DEVICE_UNIQUEID = "${device.uniqueid}";
|
||||
private static final String DEVICE_NAME = "${device.name}";
|
||||
private static final String DEVICE_MAPID = "${device.mapId}";
|
||||
private static final String DEVICE_MAPTYPE = "${device.mapType}";
|
||||
private static final String DEVICE_DEVICETYPE = "${device.deviceType}";
|
||||
private static final String DEVICE_TARGETDEVICE = "${device.targetDevice}";
|
||||
|
||||
public static String replaceDeviceData(String request, DeviceDescriptor device) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
boolean notDone = true;
|
||||
|
||||
while(notDone) {
|
||||
notDone = false;
|
||||
if (request.contains(DEVICE_ID)) {
|
||||
request = request.replace(DEVICE_ID, device.getId());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_UNIQUEID)) {
|
||||
request = request.replace(DEVICE_UNIQUEID, device.getUniqueid());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_NAME)) {
|
||||
request = request.replace(DEVICE_NAME, device.getName());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_MAPID)) {
|
||||
request = request.replace(DEVICE_MAPID, device.getMapId());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_MAPTYPE)) {
|
||||
request = request.replace(DEVICE_MAPTYPE, device.getMapType());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_DEVICETYPE)) {
|
||||
request = request.replace(DEVICE_DEVICETYPE, device.getDeviceType());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_TARGETDEVICE)) {
|
||||
request = request.replace(DEVICE_TARGETDEVICE, device.getTargetDevice());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
log.debug("Request <<" + request + ">>, not done: " + notDone);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
|
||||
public interface HueMulatorHandler {
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
public class MultiCommandUtil {
|
||||
private Integer setCount;
|
||||
private Integer theDelay;
|
||||
private Integer delayDefault;
|
||||
|
||||
public Integer getSetCount() {
|
||||
return setCount;
|
||||
}
|
||||
|
||||
public void setSetCount(Integer setCount) {
|
||||
this.setCount = setCount;
|
||||
}
|
||||
|
||||
public Integer getTheDelay() {
|
||||
return theDelay;
|
||||
}
|
||||
|
||||
public void setTheDelay(Integer theDelay) {
|
||||
this.theDelay = theDelay;
|
||||
}
|
||||
|
||||
public Integer getDelayDefault() {
|
||||
return delayDefault;
|
||||
}
|
||||
|
||||
public void setDelayDefault(Integer delayDefault) {
|
||||
this.delayDefault = delayDefault;
|
||||
}
|
||||
}
|
||||
44
src/main/java/com/bwssystems/HABridge/hue/TimeDecode.java
Normal file
44
src/main/java/com/bwssystems/HABridge/hue/TimeDecode.java
Normal file
@@ -0,0 +1,44 @@
|
||||
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;
|
||||
}
|
||||
boolean notDone = true;
|
||||
|
||||
while(notDone) {
|
||||
notDone = false;
|
||||
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()));
|
||||
notDone = true;
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not format current time: " + timeFormatDescriptor, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.bwssystems.HABridge.plugins.NestBridge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
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.nest.controller.Home;
|
||||
import com.bwssystems.nest.controller.Nest;
|
||||
import com.bwssystems.nest.controller.NestSession;
|
||||
import com.bwssystems.nest.controller.Thermostat;
|
||||
import com.bwssystems.nest.protocol.error.LoginException;
|
||||
import com.bwssystems.nest.protocol.status.WhereDetail;
|
||||
import com.bwssystems.nest.protocol.status.WhereItem;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
public class NestHome implements com.bwssystems.HABridge.Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(NestHome.class);
|
||||
private NestSession theSession;
|
||||
private Nest theNest;
|
||||
private ArrayList<NestItem> nestItems;
|
||||
private Gson aGsonHandler;
|
||||
private Boolean isFarenheit;
|
||||
private Boolean validNest;
|
||||
|
||||
public NestHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
if(!validNest)
|
||||
return null;
|
||||
|
||||
if(nestItems == null) {
|
||||
nestItems = new ArrayList<NestItem>();
|
||||
Set<String> homeNames = theNest.getHomeNames();
|
||||
Home aHome = null;
|
||||
NestItem anItem = null;
|
||||
for(String name : homeNames) {
|
||||
aHome = theNest.getHome(name);
|
||||
anItem = new NestItem();
|
||||
anItem.setId(name);
|
||||
anItem.setName(aHome.getDetail().getName());
|
||||
anItem.setType("Home");
|
||||
anItem.setLocation(aHome.getDetail().getLocation());
|
||||
nestItems.add(anItem);
|
||||
}
|
||||
Thermostat thermo = null;
|
||||
Set<String> thermoNames = theNest.getThermostatNames();
|
||||
for(String name : thermoNames) {
|
||||
thermo = theNest.getThermostat(name);
|
||||
anItem = new NestItem();
|
||||
anItem.setId(name);
|
||||
anItem.setType("Thermostat");
|
||||
String where = null;
|
||||
String homeName= null;
|
||||
Boolean found = false;
|
||||
for(String aHomeName : homeNames) {
|
||||
WhereDetail aDetail = theNest.getWhere(aHomeName);
|
||||
ListIterator<WhereItem> anIterator = aDetail.getWheres().listIterator();
|
||||
while(anIterator.hasNext()) {
|
||||
WhereItem aWhereItem = (WhereItem) anIterator.next();
|
||||
if(aWhereItem.getWhereId().equals(thermo.getDeviceDetail().getWhereId())) {
|
||||
where = aWhereItem.getName();
|
||||
homeName = theNest.getHome(aHomeName).getDetail().getName();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found)
|
||||
break;
|
||||
}
|
||||
anItem.setName(where + "(" + name.substring(name.length() - 4) + ")");
|
||||
anItem.setLocation(where + " - " + homeName);
|
||||
nestItems.add(anItem);
|
||||
}
|
||||
}
|
||||
|
||||
return nestItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(theSession != null) {
|
||||
theNest.endNestSession();
|
||||
}
|
||||
theNest = null;
|
||||
theSession = null;
|
||||
nestItems = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
log.debug("executing HUE api request to set away for nest " + anItem.getType() + ": " + anItem.getItem().toString());
|
||||
if(!validNest) {
|
||||
log.warn("Should not get here, no Nest available");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"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 = 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 = 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())
|
||||
.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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.bwssystems.HABridge.Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
theSession = null;
|
||||
theNest = null;
|
||||
nestItems = null;
|
||||
validNest = bridgeSettings.isValidNest();
|
||||
aGsonHandler = null;
|
||||
log.info("Nest Home created." + (validNest ? "" : " No Nest configured."));
|
||||
|
||||
if(validNest) {
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
|
||||
isFarenheit = bridgeSettings.isFarenheit();
|
||||
try {
|
||||
theSession = new NestSession(bridgeSettings.getNestuser(), bridgeSettings.getNestpwd());
|
||||
theNest = new Nest(theSession);
|
||||
} catch (LoginException e) {
|
||||
log.error("Caught Login Exception, setting Nest to invalid....");
|
||||
validNest = false;
|
||||
theSession = null;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.NestBridge;
|
||||
package com.bwssystems.HABridge.plugins.NestBridge;
|
||||
|
||||
public class NestInstruction {
|
||||
private String name;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.NestBridge;
|
||||
package com.bwssystems.HABridge.plugins.NestBridge;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.bwssystems.HABridge.plugins.domoticz;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Base64;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
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=true";
|
||||
private NamedIP domoticzAddress;
|
||||
|
||||
public DomoticzHandler(NamedIP addressName) {
|
||||
super();
|
||||
domoticzAddress = addressName;
|
||||
}
|
||||
|
||||
public List<DomoticzDevice> getDevices(HTTPHandler httpClient) {
|
||||
return getDomoticzDevices(GET_REQUEST, DEVICES_TYPE, FILTER_USED, httpClient);
|
||||
}
|
||||
|
||||
public List<DomoticzDevice> getScenes(HTTPHandler httpClient) {
|
||||
return getDomoticzDevices(GET_REQUEST, SCENES_TYPE, null, httpClient);
|
||||
}
|
||||
|
||||
private List<DomoticzDevice> getDomoticzDevices(String rootRequest, String type, String postpend, HTTPHandler httpClient) {
|
||||
Devices theDomoticzApiResponse = null;
|
||||
List<DomoticzDevice> deviceList = null;
|
||||
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
if(postpend != null && !postpend.isEmpty())
|
||||
theUrl = buildUrl(rootRequest + type + postpend);
|
||||
else
|
||||
theUrl = buildUrl(rootRequest + type);
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, buildHeaders());
|
||||
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 String buildUrl(String thePayload) {
|
||||
String newUrl = null;
|
||||
|
||||
if(thePayload != null && !thePayload.isEmpty()) {
|
||||
if(domoticzAddress.getSecure() != null && domoticzAddress.getSecure())
|
||||
newUrl = "https://";
|
||||
else
|
||||
newUrl = "http://";
|
||||
|
||||
newUrl = newUrl + domoticzAddress.getIp();
|
||||
|
||||
if(domoticzAddress.getPort() != null && !domoticzAddress.getPort().isEmpty())
|
||||
newUrl = newUrl + ":" + domoticzAddress.getPort();
|
||||
|
||||
if(thePayload.startsWith("/"))
|
||||
newUrl = newUrl + thePayload;
|
||||
else
|
||||
newUrl = newUrl + "/" + thePayload;
|
||||
}
|
||||
|
||||
return newUrl;
|
||||
}
|
||||
|
||||
public NameValue[] buildHeaders() {
|
||||
NameValue[] headers = null;
|
||||
|
||||
if(domoticzAddress.getUsername() != null && !domoticzAddress.getUsername().isEmpty()
|
||||
&& domoticzAddress.getPassword() != null && !domoticzAddress.getPassword().isEmpty()) {
|
||||
NameValue theAuth = new NameValue();
|
||||
theAuth.setName("Authorization");
|
||||
String encoding = Base64.getEncoder().encodeToString((domoticzAddress.getUsername() + ":" + domoticzAddress.getPassword()).getBytes());
|
||||
theAuth.setValue("Basic " + encoding);
|
||||
headers = new NameValue[1];
|
||||
headers[0] = theAuth;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
public NamedIP getDomoticzAddress() {
|
||||
return domoticzAddress;
|
||||
}
|
||||
|
||||
public void setDomoticzAddress(NamedIP DomoticzAddress) {
|
||||
this.domoticzAddress = DomoticzAddress;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
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.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 DomoticzHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(DomoticzHome.class);
|
||||
private Map<String, DomoticzHandler> domoticzs;
|
||||
private Boolean validDomoticz;
|
||||
private HTTPHandler httpClient;
|
||||
|
||||
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(httpClient);
|
||||
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(httpClient);
|
||||
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) {
|
||||
Devices theDomoticzApiResponse = null;
|
||||
String responseString = null;
|
||||
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && (theUrl.startsWith("http://") || theUrl.startsWith("https://"))) {
|
||||
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
DomoticzHandler theHandler = findHandlerByAddress(hostAddr);
|
||||
if(theHandler != null){
|
||||
String theData;
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
theData = httpClient.doHttpRequest(theHandler.buildUrl(anUrl), null, null, null, theHandler.buildHeaders());
|
||||
try {
|
||||
theDomoticzApiResponse = new Gson().fromJson(theData, Devices.class);
|
||||
if(theDomoticzApiResponse.getStatus().equals("OK"))
|
||||
responseString = null;
|
||||
else {
|
||||
log.warn("Call failed for Domoticz " + theHandler.getDomoticzAddress().getName() + " with status " + theDomoticzApiResponse.getStatus() + " for item " + theDomoticzApiResponse.getTitle());
|
||||
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);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Cannot interrpret result from call for Domoticz " + theHandler.getDomoticzAddress().getName() + " as response is not parsable.");
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
log.warn("Domoticz Call could not complete, no address found: " + theUrl);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
log.warn("Domoticz Call to be presented as http(s)://<ip_address>(:<port>)/payload, format of request unknown: " + theUrl);
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
validDomoticz = bridgeSettings.isValidDomoticz();
|
||||
log.info("Domoticz Home created." + (validDomoticz ? "" : " No Domoticz devices configured."));
|
||||
if(!validDomoticz)
|
||||
return null;
|
||||
httpClient = new HTTPHandler();
|
||||
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;
|
||||
}
|
||||
|
||||
private DomoticzHandler findHandlerByAddress(String hostAddress) {
|
||||
DomoticzHandler aHandler = null;
|
||||
boolean found = false;
|
||||
Iterator<String> keys = domoticzs.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
aHandler = domoticzs.get(key);
|
||||
if(aHandler != null && aHandler.getDomoticzAddress().getIp().equals(hostAddress)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
aHandler = null;
|
||||
return aHandler;
|
||||
}
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(httpClient != null)
|
||||
httpClient.closeHandler();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.bwssystems.HABridge.plugins.exec;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
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);
|
||||
|
||||
public CommandHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int itensity, Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
log.debug("Exec Request called with url: " + anItem.getItem().getAsString());
|
||||
String responseString = null;
|
||||
String intermediate;
|
||||
if (anItem.getItem().getAsString().contains("exec://"))
|
||||
intermediate = anItem.getItem().getAsString().substring(anItem.getItem().getAsString().indexOf("://") + 3);
|
||||
else
|
||||
intermediate = anItem.getItem().getAsString();
|
||||
intermediate = BrightnessDecode.calculateReplaceIntensityValue(intermediate, itensity, targetBri, targetBriInc, false);
|
||||
intermediate = DeviceDataDecode.replaceDeviceData(intermediate, device);
|
||||
intermediate = TimeDecode.replaceTimeValue(intermediate);
|
||||
String anError = doExecRequest(intermediate, lightId);
|
||||
if (anError != null) {
|
||||
responseString = anError;
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
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(anItem);
|
||||
log.debug("Process running: " + p.isAlive());
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not execute request: " + anItem, e);
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId
|
||||
+ "state\"}}]";
|
||||
}
|
||||
} else {
|
||||
log.warn("Could not execute request. Request is empty.");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId
|
||||
+ "state\"}}]";
|
||||
}
|
||||
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
log.info("Command Home for system program execution created.");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
// noop
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// noop
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.hal;
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.hal;
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.hal;
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
public class HalDevice {
|
||||
private String haldevicetype;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.hal;
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -10,30 +10,26 @@ 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 HalHome {
|
||||
public class HalHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(HalHome.class);
|
||||
private Map<String, HalInfo> hals;
|
||||
private Boolean validHal;
|
||||
|
||||
public HalHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
hals = new HashMap<String, HalInfo>();
|
||||
if(!bridgeSettings.isValidHal())
|
||||
return;
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHaladdress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHal = theList.next();
|
||||
try {
|
||||
hals.put(aHal.getName(), new HalInfo(aHal, bridgeSettings.getHaltoken()));
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot get harmony client (" + aHal.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
public List<HalDevice> getDevices() {
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
if(!validHal)
|
||||
return null;
|
||||
log.debug("consolidating devices for hues");
|
||||
List<HalDevice> theResponse = null;
|
||||
Iterator<String> keys = hals.keySet().iterator();
|
||||
@@ -97,17 +93,46 @@ public class HalHome {
|
||||
}
|
||||
|
||||
private Boolean addHalDevices(List<HalDevice> theDeviceList, List<HalDevice> theSourceList, String theKey) {
|
||||
if(!validHal)
|
||||
return null;
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
validHal = bridgeSettings.isValidHal();
|
||||
log.info("HAL Home created." + (validHal ? "" : " No HAL devices configured."));
|
||||
if(!validHal)
|
||||
return null;
|
||||
hals = new HashMap<String, HalInfo>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHaladdress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHal = theList.next();
|
||||
try {
|
||||
hals.put(aHal.getName(), new HalInfo(aHal, bridgeSettings.getHaltoken()));
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot get hal client (" + aHal.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// noop
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,15 @@
|
||||
package com.bwssystems.hal;
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.util.TextStringFormatter;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.util.TextStringFormatter;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class HalInfo {
|
||||
@@ -39,13 +33,13 @@ public class HalInfo {
|
||||
private static final String MACRO_TYPE = "Macro";
|
||||
private static final String SCENE_TYPE = "Scene";
|
||||
private static final String IRDATA_TYPE = "IrData";
|
||||
private HttpClient httpClient;
|
||||
private HTTPHandler httpClient;
|
||||
private NamedIP halAddress;
|
||||
private String theToken;
|
||||
|
||||
public HalInfo(NamedIP addressName, String aGivenToken) {
|
||||
super();
|
||||
httpClient = HttpClients.createDefault();
|
||||
httpClient = new HTTPHandler();
|
||||
halAddress = addressName;
|
||||
theToken = aGivenToken;
|
||||
}
|
||||
@@ -105,7 +99,7 @@ public class HalInfo {
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
theUrl = "http://" + halAddress.getIp() + apiType + theToken;
|
||||
theData = doHttpGETRequest(theUrl);
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
|
||||
if(theData != null) {
|
||||
log.debug("GET " + deviceType + " HalApiResponse - data: " + theData);
|
||||
theHalApiResponse = new Gson().fromJson(theData, DeviceElements.class);
|
||||
@@ -127,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);
|
||||
|
||||
}
|
||||
@@ -150,11 +146,15 @@ public class HalInfo {
|
||||
while (theHalDevices.hasNext()) {
|
||||
HalDevice theHalDevice = theHalDevices.next();
|
||||
theUrl = "http://" + halAddress.getIp() + IRBUTTON_REQUEST + TextStringFormatter.forQuerySpaceUrl(theHalDevice.getHaldevicename()) + TOKEN_REQUEST + theToken;
|
||||
theData = doHttpGETRequest(theUrl);
|
||||
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 "
|
||||
@@ -177,24 +177,6 @@ public class HalInfo {
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
// This function executes the url against the hal
|
||||
protected String doHttpGETRequest(String url) {
|
||||
String theContent = null;
|
||||
log.debug("calling GET on URL: " + url);
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
try {
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
log.debug("GET on URL responded: " + response.getStatusLine().getStatusCode());
|
||||
if(response.getStatusLine().getStatusCode() == 200){
|
||||
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); //read content for data
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("doHttpGETRequest: Error calling out to HA gateway: " + e.getMessage());
|
||||
}
|
||||
return theContent;
|
||||
}
|
||||
|
||||
public NamedIP getHalAddress() {
|
||||
return halAddress;
|
||||
}
|
||||
@@ -203,4 +185,11 @@ public class HalInfo {
|
||||
this.halAddress = halAddress;
|
||||
}
|
||||
|
||||
public void closeInfo() {
|
||||
if(httpClient != null)
|
||||
httpClient.closeHandler();
|
||||
httpClient = null;
|
||||
halAddress = null;
|
||||
theToken = null;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.hal;
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
public class StatusDescription {
|
||||
private String Status;
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.bwssystems.HABridge.plugins.harmony;
|
||||
|
||||
public class ButtonPress {
|
||||
private String device;
|
||||
private String button;
|
||||
private Integer delay;
|
||||
private Integer count;
|
||||
private String hub;
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
public void setDevice(String device) {
|
||||
this.device = device;
|
||||
}
|
||||
public String getButton() {
|
||||
return button;
|
||||
}
|
||||
public void setButton(String button) {
|
||||
this.button = button;
|
||||
}
|
||||
public Integer getDelay() {
|
||||
return delay;
|
||||
}
|
||||
public void setDelay(Integer delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
public Integer getCount() {
|
||||
return count;
|
||||
}
|
||||
public void setCount(Integer count) {
|
||||
this.count = count;
|
||||
}
|
||||
public Boolean isValid() {
|
||||
if (device != null && !device.isEmpty()){
|
||||
if (button != null && !button.isEmpty())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public String getHub() {
|
||||
return hub;
|
||||
}
|
||||
public void setHub(String hub) {
|
||||
this.hub = hub;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.harmony;
|
||||
package com.bwssystems.HABridge.plugins.harmony;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.harmony;
|
||||
package com.bwssystems.HABridge.plugins.harmony;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.harmony;
|
||||
package com.bwssystems.HABridge.plugins.harmony;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.harmony;
|
||||
package com.bwssystems.HABridge.plugins.harmony;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
package com.bwssystems.HABridge.plugins.harmony;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.IpList;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
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.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import net.whistlingfish.harmony.config.Activity;
|
||||
import net.whistlingfish.harmony.config.Device;
|
||||
|
||||
public class HarmonyHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(HarmonyHome.class);
|
||||
private Map<String, HarmonyServer> hubs;
|
||||
private Boolean isDevMode;
|
||||
private Boolean validHarmony;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public HarmonyHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validHarmony)
|
||||
return;
|
||||
if(isDevMode || hubs == null)
|
||||
return;
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
hubs.get(key).getMyHarmony().shutdown();
|
||||
}
|
||||
|
||||
hubs = null;
|
||||
}
|
||||
|
||||
public HarmonyHandler getHarmonyHandler(String aName) {
|
||||
if(!validHarmony)
|
||||
return null;
|
||||
HarmonyHandler aHandler = null;
|
||||
if(aName == null || aName.equals("")) {
|
||||
aName = "default";
|
||||
}
|
||||
|
||||
if(hubs.get(aName) == null) {
|
||||
Set<String> keys = hubs.keySet();
|
||||
if(!keys.isEmpty()) {
|
||||
aHandler = hubs.get(keys.toArray()[0]).getMyHarmony();
|
||||
}
|
||||
else
|
||||
aHandler = null;
|
||||
}
|
||||
else
|
||||
aHandler = hubs.get(aName).getMyHarmony();
|
||||
return aHandler;
|
||||
}
|
||||
|
||||
public List<HarmonyActivity> getActivities() {
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
ArrayList<HarmonyActivity> activityList = new ArrayList<HarmonyActivity>();
|
||||
if(!validHarmony)
|
||||
return null;
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Iterator<Activity> activities = hubs.get(key).getMyHarmony().getActivities().iterator();
|
||||
while(activities.hasNext()) {
|
||||
HarmonyActivity anActivity = new HarmonyActivity();
|
||||
anActivity.setActivity(activities.next());
|
||||
anActivity.setHub(key);
|
||||
activityList.add(anActivity);
|
||||
}
|
||||
}
|
||||
return activityList;
|
||||
}
|
||||
public List<HarmonyActivity> getCurrentActivities() {
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
ArrayList<HarmonyActivity> activityList = new ArrayList<HarmonyActivity>();
|
||||
if(!validHarmony)
|
||||
return null;
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Activity theActivity = hubs.get(key).getMyHarmony().getCurrentActivity();
|
||||
HarmonyActivity anActivity = new HarmonyActivity();
|
||||
anActivity.setActivity(theActivity);
|
||||
anActivity.setHub(key);
|
||||
activityList.add(anActivity);
|
||||
}
|
||||
return activityList;
|
||||
}
|
||||
public List<HarmonyDevice> getDevices() {
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
ArrayList<HarmonyDevice> deviceList = new ArrayList<HarmonyDevice>();
|
||||
if(!validHarmony)
|
||||
return null;
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Iterator<Device> devices = hubs.get(key).getMyHarmony().getDevices().iterator();
|
||||
while(devices.hasNext()) {
|
||||
HarmonyDevice aDevice = new HarmonyDevice();
|
||||
aDevice.setDevice(devices.next());
|
||||
aDevice.setHub(key);
|
||||
deviceList.add(aDevice);
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
log.debug("executing HUE api request to change " + anItem.getType() + " to Harmony: " + device.getName());
|
||||
if(!validHarmony) {
|
||||
log.warn("Should not get here, no harmony configured");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no harmony configured\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
if(anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex]))
|
||||
{
|
||||
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());
|
||||
if (myHarmony == null) {
|
||||
log.warn("Should not get here, no harmony hub available");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no harmony hub available\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
myHarmony.startActivity(anActivity);
|
||||
}
|
||||
} else if(anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex])) {
|
||||
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 + "]";
|
||||
}
|
||||
|
||||
url = BrightnessDecode.calculateReplaceIntensityValue(url, intensity, targetBri, targetBriInc, false);
|
||||
ButtonPress[] deviceButtons = aGsonHandler.fromJson(url, ButtonPress[].class);
|
||||
Integer theCount = 1;
|
||||
for(int z = 0; z < deviceButtons.length; z++) {
|
||||
if(deviceButtons[z].getCount() != null && deviceButtons[z].getCount() > 0)
|
||||
theCount = deviceButtons[z].getCount();
|
||||
for(int y = 0; y < theCount; y++) {
|
||||
if( y > 0 || z > 0) {
|
||||
try {
|
||||
Thread.sleep(aMultiUtil.getTheDelay());
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (anItem.getDelay() != null && anItem.getDelay() > 0)
|
||||
aMultiUtil.setTheDelay(anItem.getDelay());
|
||||
else
|
||||
aMultiUtil.setTheDelay(aMultiUtil.getDelayDefault());
|
||||
log.debug("pressing button: " + deviceButtons[z].getDevice() + " - " + deviceButtons[z].getButton() + " - iteration: " + String.valueOf(z) + " - count: " + String.valueOf(y));
|
||||
if(deviceButtons[z].getHub() == null || deviceButtons[z].getHub().isEmpty())
|
||||
deviceButtons[z].setHub(device.getTargetDevice());
|
||||
HarmonyHandler myHarmony = getHarmonyHandler(deviceButtons[z].getHub());
|
||||
if (myHarmony == null)
|
||||
log.warn("Button Press - Should not get here, no harmony hub available");
|
||||
else
|
||||
myHarmony.pressButton(deviceButtons[z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
|
||||
validHarmony = bridgeSettings.isValidHarmony();
|
||||
log.info("Harmony Home created." + (validHarmony ? "" : " No Harmony devices configured.") + (isDevMode ? " DevMode is set." : ""));
|
||||
if(validHarmony || isDevMode) {
|
||||
hubs = new HashMap<String, HarmonyServer>();
|
||||
aGsonHandler =
|
||||
new GsonBuilder()
|
||||
.create();
|
||||
if(isDevMode) {
|
||||
NamedIP devModeIp = new NamedIP();
|
||||
devModeIp.setIp("10.10.10.10");
|
||||
devModeIp.setName("devMode");
|
||||
List<NamedIP> theList = new ArrayList<NamedIP>();
|
||||
theList.add(devModeIp);
|
||||
IpList thedevList = new IpList();
|
||||
thedevList.setDevices(theList);
|
||||
bridgeSettings.setHarmonyAddress(thedevList);
|
||||
}
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHarmonyAddress().getDevices().iterator();
|
||||
while(theList.hasNext() && validHarmony) {
|
||||
NamedIP aHub = theList.next();
|
||||
boolean loopControl = true;
|
||||
int retryCount = 0;
|
||||
while(loopControl) {
|
||||
try {
|
||||
hubs.put(aHub.getName(), HarmonyServer.setup(bridgeSettings, isDevMode, aHub));
|
||||
loopControl = false;
|
||||
} catch (Exception e) {
|
||||
if(retryCount > 3) {
|
||||
log.error("Cannot get harmony client (" + aHub.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
|
||||
loopControl = false;
|
||||
} else {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e1) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
retryCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(hubs.isEmpty())
|
||||
validHarmony = false;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
if(validHarmony) {
|
||||
if(type.equalsIgnoreCase(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex]))
|
||||
return getActivities();
|
||||
if(type.equalsIgnoreCase(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex]))
|
||||
return getDevices();
|
||||
if(type.equalsIgnoreCase("current_activity"))
|
||||
return getCurrentActivities();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.bwssystems.HABridge.plugins.harmony;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import net.whistlingfish.harmony.ActivityChangeListener;
|
||||
import net.whistlingfish.harmony.HarmonyClient;
|
||||
import net.whistlingfish.harmony.HarmonyClientModule;
|
||||
import net.whistlingfish.harmony.config.Activity;
|
||||
import net.whistlingfish.harmony.protocol.OAReplyProvider;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
public class HarmonyServer {
|
||||
|
||||
private static final String ACTIVIY_ID = "${activity.id}";
|
||||
private static final String ACTIVIY_LABEL = "${activity.label}";
|
||||
|
||||
@Inject
|
||||
private HarmonyClient harmonyClient;
|
||||
|
||||
private HarmonyHandler myHarmony;
|
||||
private DevModeResponse devResponse;
|
||||
private OAReplyProvider dummyProvider;
|
||||
private NamedIP myNameAndIP;
|
||||
private Boolean isDevMode;
|
||||
private HTTPHandler httpClient;
|
||||
private Logger log = LoggerFactory.getLogger(HarmonyServer.class);
|
||||
|
||||
public HarmonyServer(NamedIP theHarmonyAddress) {
|
||||
super();
|
||||
myHarmony = null;
|
||||
dummyProvider = null;
|
||||
myNameAndIP = theHarmonyAddress;
|
||||
isDevMode = false;
|
||||
httpClient = new HTTPHandler();
|
||||
}
|
||||
|
||||
public static HarmonyServer setup(
|
||||
BridgeSettingsDescriptor bridgeSettings,
|
||||
Boolean harmonyDevMode,
|
||||
NamedIP theHarmonyAddress
|
||||
) throws Exception {
|
||||
if (!bridgeSettings.isValidHarmony() && harmonyDevMode) {
|
||||
return new HarmonyServer(theHarmonyAddress);
|
||||
}
|
||||
Injector injector = null;
|
||||
if (!harmonyDevMode) {
|
||||
injector = Guice.createInjector(new HarmonyClientModule());
|
||||
}
|
||||
HarmonyServer mainObject = new HarmonyServer(theHarmonyAddress);
|
||||
if (!harmonyDevMode) {
|
||||
injector.injectMembers(mainObject);
|
||||
}
|
||||
mainObject.execute(bridgeSettings, harmonyDevMode);
|
||||
return mainObject;
|
||||
}
|
||||
|
||||
private void execute(BridgeSettingsDescriptor mySettings, Boolean harmonyDevMode) throws Exception {
|
||||
Boolean noopCalls = Boolean.parseBoolean(System.getProperty("noop.calls", "false"));
|
||||
isDevMode = harmonyDevMode;
|
||||
String modeString = "";
|
||||
if (dummyProvider != null) {
|
||||
log.debug("something is very wrong as dummyProvider is not null...");
|
||||
}
|
||||
if (isDevMode) {
|
||||
modeString = " (development mode)";
|
||||
} else if (noopCalls) {
|
||||
modeString = " (no op calls to harmony)";
|
||||
}
|
||||
log.info("setup initiated " + modeString + "....");
|
||||
if (isDevMode) {
|
||||
harmonyClient = null;
|
||||
devResponse = new DevModeResponse();
|
||||
} else {
|
||||
devResponse = null;
|
||||
harmonyClient.addListener(new ActivityChangeListener() {
|
||||
@Override
|
||||
public void activityStarted(Activity activity) {
|
||||
String webhook = myNameAndIP.getWebhook();
|
||||
if(webhook != null) {
|
||||
try {
|
||||
// Replacing variables
|
||||
webhook = webhook.replace(ACTIVIY_ID, activity.getId().toString());
|
||||
webhook = webhook.replace(ACTIVIY_LABEL, URLEncoder.encode(activity.getLabel(), "UTF-8"));
|
||||
|
||||
log.info(format("calling webhook: %s", webhook));
|
||||
|
||||
// Calling webhook
|
||||
httpClient.doHttpRequest(webhook, HttpGet.METHOD_NAME, null, null, null);
|
||||
} catch (Exception e) {
|
||||
log.warn("could not call webhook: " + webhook, e);
|
||||
}
|
||||
}
|
||||
log.info(format("activity changed: [%d] %s", activity.getId(), activity.getLabel()));
|
||||
}
|
||||
});
|
||||
harmonyClient.connect(myNameAndIP.getIp());
|
||||
}
|
||||
myHarmony = new HarmonyHandler(harmonyClient, noopCalls, devResponse);
|
||||
}
|
||||
|
||||
public HarmonyHandler getMyHarmony() {
|
||||
return myHarmony;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.bwssystems.harmony;
|
||||
package com.bwssystems.HABridge.plugins.harmony;
|
||||
|
||||
public class RunActivity {
|
||||
private String name;
|
||||
private String hub;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -15,4 +16,12 @@ public class RunActivity {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getHub() {
|
||||
return hub;
|
||||
}
|
||||
|
||||
public void setHub(String hub) {
|
||||
this.hub = hub;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.hass;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Field {
|
||||
|
||||
@SerializedName("fields")
|
||||
@Expose
|
||||
private Map<String, JsonElement> fields;
|
||||
|
||||
public Field(Map<String, JsonElement> fields) {
|
||||
super();
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
public Map<String, JsonElement> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
public void setFields(Map<String, JsonElement> fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.bwssystems.HABridge.plugins.hass;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class FieldDeserializer implements JsonDeserializer<Field> {
|
||||
private Map<String, JsonElement> fields;
|
||||
@Override
|
||||
public Field deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx)
|
||||
{
|
||||
JsonObject obj = json.getAsJsonObject();
|
||||
String theKey;
|
||||
|
||||
fields = new HashMap<String, JsonElement>();
|
||||
for(Entry<String, JsonElement> entry:obj.entrySet()){
|
||||
theKey = entry.getKey();
|
||||
JsonElement theRawDetail = obj.get(theKey);
|
||||
fields.put(theKey, theRawDetail);
|
||||
}
|
||||
|
||||
return new Field(fields);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.bwssystems.HABridge.plugins.hass;
|
||||
|
||||
public class HassCommand {
|
||||
private String entityId;
|
||||
private String hassName;
|
||||
private String state;
|
||||
private String bri;
|
||||
public String getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
public void setEntityId(String entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
public String getHassName() {
|
||||
return hassName;
|
||||
}
|
||||
public void setHassName(String hassName) {
|
||||
this.hassName = hassName;
|
||||
}
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
public String getBri() {
|
||||
return bri;
|
||||
}
|
||||
public void setBri(String bri) {
|
||||
this.bri = bri;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.bwssystems.HABridge.plugins.hass;
|
||||
|
||||
public class HassDevice {
|
||||
private State deviceState;
|
||||
private String deviceName;
|
||||
private String domain;
|
||||
private Boolean secure;
|
||||
private String hassaddress;
|
||||
private String hassname;
|
||||
public State getDeviceState() {
|
||||
return deviceState;
|
||||
}
|
||||
public void setDeviceState(State deviceState) {
|
||||
this.deviceState = deviceState;
|
||||
}
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
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;
|
||||
}
|
||||
public void setHassaddress(String hassaddress) {
|
||||
this.hassaddress = hassaddress;
|
||||
}
|
||||
public String getHassname() {
|
||||
return hassname;
|
||||
}
|
||||
public void setHassname(String hassname) {
|
||||
this.hassname = hassname;
|
||||
}
|
||||
}
|
||||
164
src/main/java/com/bwssystems/HABridge/plugins/hass/HassHome.java
Normal file
164
src/main/java/com/bwssystems/HABridge/plugins/hass/HassHome.java
Normal file
@@ -0,0 +1,164 @@
|
||||
package com.bwssystems.HABridge.plugins.hass;
|
||||
|
||||
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.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
public class HassHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(HassHome.class);
|
||||
private Map<String, HomeAssistant> hassMap;
|
||||
private Boolean validHass;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public HassHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
hassMap = null;
|
||||
aGsonHandler = null;
|
||||
validHass = bridgeSettings.isValidHass();
|
||||
log.info("HomeAssistant Home created." + (validHass ? "" : " No HomeAssistants configured."));
|
||||
if(validHass) {
|
||||
hassMap = new HashMap<String,HomeAssistant>();
|
||||
aGsonHandler =
|
||||
new GsonBuilder()
|
||||
.create();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHassaddress().getDevices().iterator();
|
||||
while(theList.hasNext() && validHass) {
|
||||
NamedIP aHass = theList.next();
|
||||
try {
|
||||
hassMap.put(aHass.getName(), new HomeAssistant(aHass));
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot get hass (" + aHass.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
|
||||
validHass = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public HomeAssistant getHomeAssistant(String aName) {
|
||||
if(!validHass)
|
||||
return null;
|
||||
HomeAssistant aHomeAssistant;
|
||||
if(aName == null || aName.equals("")) {
|
||||
aHomeAssistant = null;
|
||||
log.debug("Cannot get HomeAssistant for name as it is empty.");
|
||||
}
|
||||
else {
|
||||
aHomeAssistant = hassMap.get(aName);
|
||||
log.debug("Retrieved a HomeAssistant for name: " + aName);
|
||||
}
|
||||
return aHomeAssistant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
log.debug("consolidating devices for hass");
|
||||
if(!validHass)
|
||||
return null;
|
||||
List<State> theResponse = null;
|
||||
Iterator<String> keys = hassMap.keySet().iterator();
|
||||
List<HassDevice> deviceList = new ArrayList<HassDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = hassMap.get(key).getDevices();
|
||||
if(theResponse != null)
|
||||
addHassDevices(deviceList, theResponse, key);
|
||||
else {
|
||||
log.warn("Cannot get devices for HomeAssistant with name: " + key + ", skipping this Hass.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
private Boolean addHassDevices(List<HassDevice> theDeviceList, List<State> theSourceList, String theKey) {
|
||||
if(!validHass)
|
||||
return null;
|
||||
Iterator<State> devices = theSourceList.iterator();
|
||||
while(devices.hasNext()) {
|
||||
State theDevice = devices.next();
|
||||
HassDevice aNewHassDevice = new HassDevice();
|
||||
aNewHassDevice.setDeviceState(theDevice);
|
||||
aNewHassDevice.setHassaddress(hassMap.get(theKey).getHassAddress().getIp());
|
||||
aNewHassDevice.setHassname(theKey);
|
||||
JsonElement friendlyName = theDevice.getAttributes().get("friendly_name");
|
||||
if(friendlyName == null)
|
||||
aNewHassDevice.setDeviceName(theDevice.getEntityId());
|
||||
else
|
||||
aNewHassDevice.setDeviceName(friendlyName.getAsString());
|
||||
aNewHassDevice.setDomain(theDevice.getEntityId().substring(0, theDevice.getEntityId().indexOf(".")));
|
||||
theDeviceList.add(aNewHassDevice);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String theReturn = null;
|
||||
log.debug("executing HUE api request to send message to HomeAssistant: " + anItem.getItem().toString());
|
||||
if(!validHass) {
|
||||
log.warn("Should not get here, no HomeAssistant clients configured");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no HomeAssistants configured\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
|
||||
} else {
|
||||
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));
|
||||
HomeAssistant homeAssistant = getHomeAssistant(hassCommand.getHassName());
|
||||
if (homeAssistant == null) {
|
||||
log.warn("Should not get here, no HomeAssistants available");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no HiomeAssistant clients available\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
log.debug("calling HomeAssistant: " + hassCommand.getHassName() + " - "
|
||||
+ hassCommand.getEntityId() + " - " + hassCommand.getState() + " - " + hassCommand.getBri());
|
||||
homeAssistant.callCommand(hassCommand);
|
||||
}
|
||||
}
|
||||
return theReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validHass)
|
||||
return;
|
||||
if(hassMap == null)
|
||||
return;
|
||||
Iterator<String> keys = hassMap.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
hassMap.get(key).closeClient();
|
||||
}
|
||||
|
||||
hassMap = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.bwssystems.HABridge.plugins.hass;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class HomeAssistant {
|
||||
private static final Logger log = LoggerFactory.getLogger(HomeAssistant.class);
|
||||
private NamedIP hassAddress;
|
||||
private HTTPHandler anHttpHandler;
|
||||
|
||||
public HomeAssistant(NamedIP addressName) {
|
||||
super();
|
||||
anHttpHandler = new HTTPHandler();
|
||||
hassAddress = addressName;
|
||||
}
|
||||
|
||||
public NamedIP getHassAddress() {
|
||||
return hassAddress;
|
||||
}
|
||||
|
||||
public void setHassAddress(NamedIP hassAddress) {
|
||||
this.hassAddress = hassAddress;
|
||||
}
|
||||
|
||||
public Boolean callCommand(HassCommand aCommand) {
|
||||
log.debug("calling HomeAssistant: " + aCommand.getHassName() + " - "
|
||||
+ aCommand.getEntityId() + " - " + aCommand.getState() + " - " + aCommand.getBri());
|
||||
String aUrl = null;
|
||||
if(hassAddress.getSecure() != null && hassAddress.getSecure())
|
||||
aUrl = "https";
|
||||
else
|
||||
aUrl = "http";
|
||||
String domain = aCommand.getEntityId().substring(0, aCommand.getEntityId().indexOf("."));
|
||||
aUrl = aUrl + "://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/services/";
|
||||
if(domain.equals("group"))
|
||||
aUrl = aUrl + "homeassistant";
|
||||
else
|
||||
aUrl = aUrl + domain;
|
||||
String aBody = "{\"entity_id\":\"" + aCommand.getEntityId() + "\"";
|
||||
NameValue[] headers = null;
|
||||
if(hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
|
||||
NameValue password = new NameValue();
|
||||
password.setName("x-ha-access");
|
||||
password.setValue(hassAddress.getPassword());
|
||||
headers = new NameValue[1];
|
||||
headers[0] = password;
|
||||
}
|
||||
if(aCommand.getState().equalsIgnoreCase("on")) {
|
||||
aUrl = aUrl + "/turn_on";
|
||||
if(aCommand.getBri() != null)
|
||||
aBody = aBody + ",\"brightness\":" + aCommand.getBri() + "}";
|
||||
else
|
||||
aBody = aBody + "}";
|
||||
}
|
||||
else {
|
||||
aUrl = aUrl + "/turn_off";
|
||||
aBody = aBody + "}";
|
||||
}
|
||||
log.debug("Calling HomeAssistant with url: " + aUrl);
|
||||
String theData = anHttpHandler.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "application/json", aBody, headers);
|
||||
log.debug("call Command return is: <" + theData + ">");
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<State> getDevices() {
|
||||
List<State> theDeviceStates = null;
|
||||
State[] theHassStates;
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
NameValue[] headers = null;
|
||||
if(hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
|
||||
NameValue password = new NameValue();
|
||||
password.setName("x-ha-access");
|
||||
password.setValue(hassAddress.getPassword());
|
||||
headers = new NameValue[1];
|
||||
headers[0] = password;
|
||||
}
|
||||
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);
|
||||
theHassStates = new Gson().fromJson(theData, State[].class);
|
||||
if(theHassStates == null) {
|
||||
log.warn("Cannot get an devices for HomeAssistant " + hassAddress.getName() + " as response is not parsable.");
|
||||
}
|
||||
else {
|
||||
theDeviceStates = new ArrayList<State>(Arrays.asList(theHassStates));
|
||||
}
|
||||
}
|
||||
else
|
||||
log.warn("Cannot get an devices for HomeAssistant " + hassAddress.getName() + " http call failed.");
|
||||
return theDeviceStates;
|
||||
}
|
||||
|
||||
|
||||
protected void closeClient() {
|
||||
anHttpHandler.closeHandler();
|
||||
anHttpHandler = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.hass;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Service {
|
||||
|
||||
@SerializedName("domain")
|
||||
@Expose
|
||||
private String domain;
|
||||
@SerializedName("services")
|
||||
@Expose
|
||||
private Map<String, ServiceElement> services;
|
||||
|
||||
public Service(String domain, Map<String, ServiceElement> services) {
|
||||
super();
|
||||
this.domain = domain;
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* The domain
|
||||
*/
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param domain
|
||||
* The domain
|
||||
*/
|
||||
public void setDomain(String domain) {
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* The services
|
||||
*/
|
||||
public Map<String, ServiceElement> getServices() {
|
||||
return services;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param domain
|
||||
* The services
|
||||
*/
|
||||
public void setServices(Map<String, ServiceElement> services) {
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.bwssystems.HABridge.plugins.hass;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class ServiceDeserializer implements JsonDeserializer<Service> {
|
||||
private Map<String, ServiceElement> services;
|
||||
private String domain;
|
||||
@Override
|
||||
public Service deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx)
|
||||
{
|
||||
JsonObject objServices = json.getAsJsonObject();
|
||||
String theKey;
|
||||
domain = objServices.get("domain").getAsString();
|
||||
JsonObject obj = objServices.get("services").getAsJsonObject();
|
||||
services = new HashMap<String, ServiceElement>();
|
||||
for(Entry<String, JsonElement> entry:obj.entrySet()){
|
||||
ServiceElement theServiceElement = new ServiceElement();
|
||||
theKey = entry.getKey();
|
||||
JsonObject theRawDetail = obj.getAsJsonObject(theKey);
|
||||
|
||||
theServiceElement.setDescription(theRawDetail.get("description").getAsString());
|
||||
theServiceElement.setField(ctx.deserialize(theRawDetail.get("fields"), Field.class));
|
||||
services.put(theKey, theServiceElement);
|
||||
}
|
||||
return new Service(domain, services);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.hass;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class ServiceElement {
|
||||
|
||||
@SerializedName("description")
|
||||
@Expose
|
||||
private String description;
|
||||
@SerializedName("fields")
|
||||
@Expose
|
||||
private Field fields;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* The description
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param description
|
||||
* The description
|
||||
*/
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* The fields
|
||||
*/
|
||||
public Field getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fields
|
||||
* The fields
|
||||
*/
|
||||
public void setField(Field fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
}
|
||||
117
src/main/java/com/bwssystems/HABridge/plugins/hass/State.java
Normal file
117
src/main/java/com/bwssystems/HABridge/plugins/hass/State.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package com.bwssystems.HABridge.plugins.hass;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class State {
|
||||
|
||||
@SerializedName("attributes")
|
||||
@Expose
|
||||
private Map<String, JsonElement> attributes;
|
||||
@SerializedName("entity_id")
|
||||
@Expose
|
||||
private String entityId;
|
||||
@SerializedName("last_changed")
|
||||
@Expose
|
||||
private String lastChanged;
|
||||
@SerializedName("last_updated")
|
||||
@Expose
|
||||
private String lastUpdated;
|
||||
@SerializedName("state")
|
||||
@Expose
|
||||
private String state;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* The attributes
|
||||
*/
|
||||
public Map<String, JsonElement> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param attributes
|
||||
* The attributes
|
||||
*/
|
||||
public void setAttributes(Map<String, JsonElement> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* The entityId
|
||||
*/
|
||||
public String getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param entityId
|
||||
* The entity_id
|
||||
*/
|
||||
public void setEntityId(String entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* The lastChanged
|
||||
*/
|
||||
public String getLastChanged() {
|
||||
return lastChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lastChanged
|
||||
* The last_changed
|
||||
*/
|
||||
public void setLastChanged(String lastChanged) {
|
||||
this.lastChanged = lastChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* The lastUpdated
|
||||
*/
|
||||
public String getLastUpdated() {
|
||||
return lastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lastUpdated
|
||||
* The last_updated
|
||||
*/
|
||||
public void setLastUpdated(String lastUpdated) {
|
||||
this.lastUpdated = lastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* The state
|
||||
*/
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param state
|
||||
* The state
|
||||
*/
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.bwssystems.HABridge.plugins.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.config.CookieSpecs;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
|
||||
public class HTTPHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(HTTPHandler.class);
|
||||
private CloseableHttpClient httpClient;
|
||||
private RequestConfig globalConfig;
|
||||
|
||||
|
||||
public HTTPHandler() {
|
||||
globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
|
||||
httpClient = HttpClients.custom().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;
|
||||
ContentType parsedContentType = null;
|
||||
StringEntity requestBody = null;
|
||||
if (contentType != null && !contentType.trim().isEmpty()) {
|
||||
parsedContentType = ContentType.parse(contentType);
|
||||
if (body != null && body.length() > 0)
|
||||
requestBody = new StringEntity(body, parsedContentType);
|
||||
}
|
||||
try {
|
||||
theURI = new URI(url);
|
||||
} catch (URISyntaxException e1) {
|
||||
log.warn("Error creating URI http request: " + url + " with message: " + e1.getMessage());
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (httpVerb == null || httpVerb.trim().isEmpty() || HttpGet.METHOD_NAME.equalsIgnoreCase(httpVerb)) {
|
||||
request = new HttpGet(theURI);
|
||||
} else if (HttpPost.METHOD_NAME.equalsIgnoreCase(httpVerb)) {
|
||||
HttpPost postRequest = new HttpPost(theURI);
|
||||
if (requestBody != null)
|
||||
postRequest.setEntity(requestBody);
|
||||
request = postRequest;
|
||||
} else if (HttpPut.METHOD_NAME.equalsIgnoreCase(httpVerb)) {
|
||||
HttpPut putRequest = new HttpPut(theURI);
|
||||
if (requestBody != null)
|
||||
putRequest.setEntity(requestBody);
|
||||
request = putRequest;
|
||||
}
|
||||
else
|
||||
request = new HttpGet(theURI);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Error creating outbound http request: IllegalArgumentException in log", e);
|
||||
return null;
|
||||
}
|
||||
log.debug("Making outbound call in doHttpRequest: " + request);
|
||||
if (headers != null && headers.length > 0) {
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
request.setHeader(headers[i].getName(), headers[i].getValue());
|
||||
}
|
||||
}
|
||||
HttpResponse response;
|
||||
try {
|
||||
for(int retryCount = 0; retryCount < 2; retryCount++) {
|
||||
response = httpClient.execute(request);
|
||||
log.debug((httpVerb == null ? "GET" : httpVerb) + " execute (" + retryCount + ") on URL responded: "
|
||||
+ response.getStatusLine().getStatusCode());
|
||||
if (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300) {
|
||||
if (response.getEntity() != null) {
|
||||
try {
|
||||
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); // read
|
||||
// content
|
||||
// for
|
||||
// data
|
||||
EntityUtils.consume(response.getEntity()); // close out
|
||||
// inputstream
|
||||
// ignore
|
||||
// content
|
||||
} catch (Exception e) {
|
||||
log.debug("Error ocurred in handling response entity after successful call, still responding success. "
|
||||
+ e.getMessage(), e);
|
||||
}
|
||||
log.debug("Successfull response - The http response is <<<" + theContent + ">>>");
|
||||
}
|
||||
retryCount = 2;
|
||||
} else {
|
||||
log.warn("HTTP response code was not an expected successful response of between 200 - 299, the code was: " + response.getStatusLine());
|
||||
try {
|
||||
String someContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); // read
|
||||
// content
|
||||
// for
|
||||
// data
|
||||
EntityUtils.consume(response.getEntity()); // close out
|
||||
// inputstream
|
||||
// ignore
|
||||
// content
|
||||
log.debug("Unsuccessfull response - The http response is <<<" + someContent + ">>>");
|
||||
} catch (Exception e) {
|
||||
//noop
|
||||
}
|
||||
if (response.getStatusLine().getStatusCode() == 504) {
|
||||
log.warn("HTTP response code was 504, retrying...");
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e1) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
else
|
||||
retryCount = 2;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Error calling out to HA gateway: IOException in log", e);
|
||||
}
|
||||
return theContent;
|
||||
}
|
||||
|
||||
// public HttpClient getHttpClient() {
|
||||
// return httpClient;
|
||||
// }
|
||||
|
||||
|
||||
public CloseableHttpClient getHttpClient() {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
|
||||
public void closeHandler() {
|
||||
try {
|
||||
httpClient.close();
|
||||
} catch (IOException e) {
|
||||
// noop
|
||||
}
|
||||
httpClient = null;
|
||||
}
|
||||
}
|
||||
101
src/main/java/com/bwssystems/HABridge/plugins/http/HTTPHome.java
Normal file
101
src/main/java/com/bwssystems/HABridge/plugins/http/HTTPHome.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package com.bwssystems.HABridge.plugins.http;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
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.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class HTTPHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(HTTPHome.class);
|
||||
private HTTPHandler anHttpHandler;
|
||||
|
||||
public HTTPHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && (theUrl.startsWith("http://") || theUrl.startsWith("https://"))) {
|
||||
//Backwards Compatibility Items
|
||||
if(anItem.getHttpVerb() == null || anItem.getHttpVerb().isEmpty())
|
||||
{
|
||||
if(device.getHttpVerb() != null && !device.getHttpVerb().isEmpty())
|
||||
anItem.setHttpVerb(device.getHttpVerb());
|
||||
}
|
||||
|
||||
if(anItem.getHttpHeaders() == null || anItem.getHttpHeaders().isEmpty()) {
|
||||
if(device.getHeaders() != null && !device.getHeaders().isEmpty() )
|
||||
anItem.setHttpHeaders(device.getHeaders());
|
||||
}
|
||||
|
||||
log.debug("executing HUE api request to Http "
|
||||
+ (anItem.getHttpVerb() == null ? "GET" : anItem.getHttpVerb()) + ": "
|
||||
+ anItem.getItem().getAsString());
|
||||
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrl,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device);
|
||||
anUrl = TimeDecode.replaceTimeValue(anUrl);
|
||||
|
||||
String aBody = null;
|
||||
if(anItem.getHttpBody()!= null && !anItem.getHttpBody().isEmpty()) {
|
||||
aBody = BrightnessDecode.calculateReplaceIntensityValue(anItem.getHttpBody(),
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
aBody = DeviceDataDecode.replaceDeviceData(aBody, device);
|
||||
aBody = TimeDecode.replaceTimeValue(aBody);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
} else {
|
||||
log.warn("HTTP Call to be presented as http(s)://<ip_address>(:<port>)/payload, format of request unknown: " + theUrl);
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
anHttpHandler = new HTTPHandler();
|
||||
log.info("Http Home created.");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
// Not a resource
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(anHttpHandler != null)
|
||||
anHttpHandler.closeHandler();
|
||||
anHttpHandler = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.hue;
|
||||
package com.bwssystems.HABridge.plugins.hue;
|
||||
|
||||
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
package com.bwssystems.hue;
|
||||
package com.bwssystems.HABridge.plugins.hue;
|
||||
|
||||
public class HueDeviceIdentifier {
|
||||
private String hueName;
|
||||
private String ipAddress;
|
||||
private String deviceId;
|
||||
public String getHueName() {
|
||||
return hueName;
|
||||
}
|
||||
public void setHueName(String hueName) {
|
||||
this.hueName = hueName;
|
||||
}
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
136
src/main/java/com/bwssystems/HABridge/plugins/hue/HueHome.java
Normal file
136
src/main/java/com/bwssystems/HABridge/plugins/hue/HueHome.java
Normal file
@@ -0,0 +1,136 @@
|
||||
package com.bwssystems.HABridge.plugins.hue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
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.api.hue.DeviceResponse;
|
||||
import com.bwssystems.HABridge.api.hue.HueApiResponse;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
public class HueHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(HueHome.class);
|
||||
private Map<String, HueInfo> hues;
|
||||
private Boolean validHue;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public HueHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
log.debug("consolidating devices for hues");
|
||||
if(!validHue)
|
||||
return null;
|
||||
Iterator<String> keys = hues.keySet().iterator();
|
||||
ArrayList<HueDevice> deviceList = new ArrayList<HueDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
HueApiResponse theResponse = hues.get(key).getHueApiResponse();
|
||||
if(theResponse != null) {
|
||||
Map<String, DeviceResponse> theDevices = theResponse.getLights();
|
||||
if(theDevices != null) {
|
||||
Iterator<String> deviceKeys = theDevices.keySet().iterator();
|
||||
while(deviceKeys.hasNext()) {
|
||||
String theDeviceKey = deviceKeys.next();
|
||||
HueDevice aNewHueDevice = new HueDevice();
|
||||
aNewHueDevice.setDevice(theDevices.get(theDeviceKey));
|
||||
aNewHueDevice.setHuedeviceid(theDeviceKey);
|
||||
aNewHueDevice.setHueaddress(hues.get(key).getHueAddress().getIp());
|
||||
aNewHueDevice.setHuename(key);
|
||||
deviceList.add(aNewHueDevice);
|
||||
}
|
||||
}
|
||||
else {
|
||||
deviceList = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
log.warn("Cannot get lights for Hue with name: " + key);
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
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);
|
||||
return deviceResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
if(!validHue)
|
||||
return null;
|
||||
String responseString = 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());
|
||||
|
||||
HueInfo theHue = hues.get(deviceId.getHueName());
|
||||
|
||||
// make call
|
||||
responseString = theHue.changeState(deviceId, lightId, body);
|
||||
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
validHue = bridgeSettings.isValidHue();
|
||||
log.info("Hue passthru Home created." + (validHue ? "" : " No Hue passtrhu systems configured."));
|
||||
if(validHue) {
|
||||
hues = new HashMap<String, HueInfo>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHueaddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHue = theList.next();
|
||||
hues.put(aHue.getName(), new HueInfo(aHue));
|
||||
}
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validHue)
|
||||
return;
|
||||
if(hues == null)
|
||||
return;
|
||||
Iterator<String> keys = hues.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
hues.get(key).closeHue();;
|
||||
}
|
||||
hues = null;
|
||||
}
|
||||
}
|
||||
199
src/main/java/com/bwssystems/HABridge/plugins/hue/HueInfo.java
Normal file
199
src/main/java/com/bwssystems/HABridge/plugins/hue/HueInfo.java
Normal file
@@ -0,0 +1,199 @@
|
||||
package com.bwssystems.HABridge.plugins.hue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.SuccessUserResponse;
|
||||
import com.bwssystems.HABridge.api.UserCreateRequest;
|
||||
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
||||
import com.bwssystems.HABridge.api.hue.HueApiResponse;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
|
||||
public class HueInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(HueInfo.class);
|
||||
private HTTPHandler httpClient;
|
||||
private NamedIP hueAddress;
|
||||
public static final String HUE_REQUEST = "/api";
|
||||
|
||||
public HueInfo(NamedIP addressName) {
|
||||
super();
|
||||
httpClient = new HTTPHandler();
|
||||
hueAddress = addressName;
|
||||
}
|
||||
|
||||
public HueApiResponse getHueApiResponse() {
|
||||
log.debug("Get hue info to hue " + hueAddress.getName());
|
||||
HueApiResponse theHueApiResponse = null;
|
||||
|
||||
if(hueAddress.getUsername() == null) {
|
||||
registerWithHue();
|
||||
if(hueAddress.getUsername() == null) {
|
||||
log.warn("Could not register with hue: " + hueAddress.getName());
|
||||
}
|
||||
}
|
||||
|
||||
String theUrl = "http://" + hueAddress.getIp() + HUE_REQUEST + "/" + hueAddress.getUsername();
|
||||
String theData;
|
||||
boolean loopControl = true;
|
||||
int retryCount = 0;
|
||||
while(loopControl) {
|
||||
if(retryCount > 3) {
|
||||
log.warn("Max Retry reached to get Hue data from " + hueAddress.getName());
|
||||
loopControl = false;
|
||||
break;
|
||||
} else if (retryCount > 0) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
theUrl = "http://" + hueAddress.getIp() + HueUtil.HUE_REQUEST + "/" + hueAddress.getUsername();
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
|
||||
if(theData != null) {
|
||||
log.debug("GET HueApiResponse - data: " + theData);
|
||||
if(theData.contains("[{\"error\":")) {
|
||||
if(theData.contains("unauthorized user")) {
|
||||
registerWithHue();
|
||||
if(hueAddress.getUsername() == null) {
|
||||
log.warn("Retry Register to Hue for " + hueAddress.getName() + " returned error: " + theData);
|
||||
}
|
||||
retryCount++;
|
||||
}
|
||||
else {
|
||||
log.warn("GET HueApiResponse for " + hueAddress.getName() + " - returned error: " + theData);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
theHueApiResponse = new Gson().fromJson(theData, HueApiResponse.class);
|
||||
log.debug("GET HueApiResponse for " + hueAddress.getName() + " - Gson parse - name: " + theHueApiResponse.getConfig().getName() + ", mac addr: " + theHueApiResponse.getConfig().getMac());
|
||||
loopControl = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn("GET HueApiResponse for " + hueAddress.getName() + " - returned null, no data.");
|
||||
loopControl = false;
|
||||
}
|
||||
}
|
||||
return theHueApiResponse;
|
||||
}
|
||||
|
||||
public String registerWithHue() {
|
||||
UserCreateRequest theLogin = new UserCreateRequest();
|
||||
theLogin.setDevicetype("HABridge#MyMachine");
|
||||
HttpPost postRequest = new HttpPost("http://" + hueAddress.getIp() + HUE_REQUEST);
|
||||
ContentType parsedContentType = ContentType.parse("application/json");
|
||||
StringEntity requestBody = new StringEntity(new Gson().toJson(theLogin), parsedContentType);
|
||||
HttpResponse response = null;
|
||||
postRequest.setEntity(requestBody);
|
||||
HttpClient anHttpClient = httpClient.getHttpClient();
|
||||
try {
|
||||
response = anHttpClient.execute(postRequest);
|
||||
log.debug("registerWithHue - POST execute on " + hueAddress.getName() + "URL responded: " + response.getStatusLine().getStatusCode());
|
||||
if(response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300){
|
||||
String theBody = EntityUtils.toString(response.getEntity());
|
||||
log.debug("registerWithHue response data: " + theBody);
|
||||
if(theBody.contains("[{\"error\":")) {
|
||||
if(theBody.contains("link button not")) {
|
||||
log.warn("registerWithHue needs link button pressed on HUE bridge: " + hueAddress.getName());
|
||||
}
|
||||
else
|
||||
log.warn("registerWithHue returned an unexpected error: " + theBody);
|
||||
}
|
||||
else {
|
||||
SuccessUserResponse[] theResponses = new Gson().fromJson(theBody, SuccessUserResponse[].class); //read content for data, SuccessUserResponse[].class);
|
||||
hueAddress.setUsername(theResponses[0].getSuccess().getUsername());
|
||||
}
|
||||
}
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
} catch (IOException e) {
|
||||
log.warn("Error logging into HUE: IOException in log", e);
|
||||
}
|
||||
return hueAddress.getUsername();
|
||||
}
|
||||
|
||||
public DeviceResponse getHueDeviceInfo(String hueDeviceId, DeviceDescriptor device) {
|
||||
log.debug("Get hue device info to "+ hueAddress.getName() + " passthru id " + hueDeviceId);
|
||||
String responseString = null;
|
||||
DeviceResponse deviceResponse = null;
|
||||
if(hueAddress.getUsername() == null)
|
||||
registerWithHue();
|
||||
if (hueAddress.getUsername() != null) {
|
||||
// make call
|
||||
responseString = httpClient.doHttpRequest(
|
||||
"http://" + hueAddress.getIp() + "/api/" + hueAddress.getUsername()
|
||||
+ "/lights/" + hueDeviceId,
|
||||
HttpGet.METHOD_NAME, "application/json", null, null);
|
||||
if (responseString == null) {
|
||||
log.warn("Error on calling hue device to get state: " + device.getName());
|
||||
deviceResponse = DeviceResponse.createResponse(device);
|
||||
} else if (responseString.contains("[{\"error\":") && responseString.contains("unauthorized user")) {
|
||||
log.warn("Error on calling hue device to get state: " + device.getName() + " with errors: " + responseString);
|
||||
deviceResponse = DeviceResponse.createResponse(device);
|
||||
} else {
|
||||
deviceResponse = new Gson().fromJson(responseString, DeviceResponse.class);
|
||||
if (deviceResponse != null)
|
||||
deviceResponse.setName(device.getName());
|
||||
}
|
||||
} else {
|
||||
log.warn("Error on calling hue device to get state: " + device.getName() + " Could not register with hue: " + hueAddress.getName());
|
||||
deviceResponse = DeviceResponse.createResponse(device);
|
||||
}
|
||||
|
||||
return deviceResponse;
|
||||
}
|
||||
|
||||
public String changeState(HueDeviceIdentifier deviceId, String lightId, String body) {
|
||||
log.debug("Changing state for ha-bridge id " + lightId + " to " + deviceId.getHueName() + " passthru id " + deviceId.getDeviceId() + " with state " + body );
|
||||
String responseString = null;
|
||||
if(hueAddress.getUsername() == null)
|
||||
registerWithHue();
|
||||
if (hueAddress.getUsername() != null) {
|
||||
responseString = httpClient.doHttpRequest(
|
||||
"http://" + deviceId.getIpAddress() + "/api/" + hueAddress.getUsername()
|
||||
+ "/lights/" + deviceId.getDeviceId() + "/state",
|
||||
HttpPut.METHOD_NAME, "application/json", body, null);
|
||||
if (responseString == null) {
|
||||
log.warn("Error on calling Hue passthru to change device state: " + deviceId.getHueName());
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Error on calling HUE to change device state\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else if (responseString.contains("[{\"error\":")) {
|
||||
if(responseString.contains("unauthorized user")) {
|
||||
}
|
||||
log.warn("Error occurred when calling Hue Passthru: " + responseString);
|
||||
}
|
||||
} else {
|
||||
log.warn("Error on calling hue device to change state: " + deviceId.getHueName() + " Could not register with hue: " + hueAddress.getName());
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
public void closeHue() {
|
||||
httpClient.closeHandler();
|
||||
httpClient = null;
|
||||
}
|
||||
|
||||
public NamedIP getHueAddress() {
|
||||
return hueAddress;
|
||||
}
|
||||
|
||||
public void setHueAddress(NamedIP hueAddress) {
|
||||
this.hueAddress = hueAddress;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.hue;
|
||||
package com.bwssystems.HABridge.plugins.hue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -13,13 +13,14 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.api.SuccessUserResponse;
|
||||
import com.bwssystems.HABridge.api.UserCreateRequest;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class HueUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(HueUtil.class);
|
||||
public static final String HUE_REQUEST = "/api";
|
||||
|
||||
public static final String registerWithHue(HttpClient anHttpClient, String ipAddress, String aName, String theUser, HueErrorStringSet errorStringSet) {
|
||||
public static final String registerWithHue(HTTPHandler anHttpHandler, String ipAddress, String aName, String theUser) {
|
||||
UserCreateRequest theLogin = new UserCreateRequest();
|
||||
theLogin.setDevicetype("HABridge#MyMachine");
|
||||
HttpPost postRequest = new HttpPost("http://" + ipAddress + HUE_REQUEST);
|
||||
@@ -27,6 +28,7 @@ public class HueUtil {
|
||||
StringEntity requestBody = new StringEntity(new Gson().toJson(theLogin), parsedContentType);
|
||||
HttpResponse response = null;
|
||||
postRequest.setEntity(requestBody);
|
||||
HttpClient anHttpClient = anHttpHandler.getHttpClient();
|
||||
try {
|
||||
response = anHttpClient.execute(postRequest);
|
||||
log.debug("POST execute on URL responded: " + response.getStatusLine().getStatusCode());
|
||||
@@ -39,7 +41,6 @@ public class HueUtil {
|
||||
}
|
||||
else
|
||||
log.warn("registerWithHue returned an unexpected error: " + theBody);
|
||||
errorStringSet.setErrorString(theBody);
|
||||
}
|
||||
else {
|
||||
SuccessUserResponse[] theResponses = new Gson().fromJson(theBody, SuccessUserResponse[].class); //read content for data, SuccessUserResponse[].class);
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.bwssystems.HABridge.plugins.lifx;
|
||||
|
||||
import com.github.besherman.lifx.LFXGroup;
|
||||
import com.github.besherman.lifx.LFXLight;
|
||||
|
||||
public class LifxDevice {
|
||||
private Object lifxObject;
|
||||
private String type;
|
||||
public final static String LIGHT_TYPE = "Light";
|
||||
public final static String GROUP_TYPE = "Group";
|
||||
|
||||
public LifxDevice(Object lifxObject, String type) {
|
||||
super();
|
||||
this.lifxObject = lifxObject;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public LifxEntry toEntry() {
|
||||
LifxEntry anEntry = null;
|
||||
if(type.equals(LIGHT_TYPE)) {
|
||||
anEntry = new LifxEntry();
|
||||
anEntry.setId(((LFXLight)lifxObject).getID());
|
||||
anEntry.setName(((LFXLight)lifxObject).getLabel());
|
||||
anEntry.setType(LIGHT_TYPE);
|
||||
}
|
||||
if(type.equals(GROUP_TYPE)) {
|
||||
anEntry = new LifxEntry();
|
||||
anEntry.setId("na");
|
||||
anEntry.setName(((LFXGroup)lifxObject).getLabel());
|
||||
anEntry.setType(GROUP_TYPE);
|
||||
}
|
||||
return anEntry;
|
||||
}
|
||||
public Object getLifxObject() {
|
||||
return lifxObject;
|
||||
}
|
||||
|
||||
public void setLifxObject(Object lifxObject) {
|
||||
this.lifxObject = lifxObject;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.bwssystems.HABridge.plugins.lifx;
|
||||
|
||||
public class LifxEntry {
|
||||
private String name;
|
||||
private String id;
|
||||
private String type;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
257
src/main/java/com/bwssystems/HABridge/plugins/lifx/LifxHome.java
Normal file
257
src/main/java/com/bwssystems/HABridge/plugins/lifx/LifxHome.java
Normal file
@@ -0,0 +1,257 @@
|
||||
package com.bwssystems.HABridge.plugins.lifx;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
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.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.github.besherman.lifx.LFXClient;
|
||||
import com.github.besherman.lifx.LFXGroup;
|
||||
import com.github.besherman.lifx.LFXGroupCollection;
|
||||
import com.github.besherman.lifx.LFXGroupCollectionListener;
|
||||
import com.github.besherman.lifx.LFXLight;
|
||||
import com.github.besherman.lifx.LFXLightCollection;
|
||||
import com.github.besherman.lifx.LFXLightCollectionListener;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
public class LifxHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(LifxHome.class);
|
||||
private static final float DIM_DIVISOR = (float)254.00;
|
||||
private Map<String, LifxDevice> lifxMap;
|
||||
private LFXClient client;
|
||||
private Boolean validLifx;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public LifxHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
lifxMap = null;
|
||||
aGsonHandler = null;
|
||||
validLifx = bridgeSettings.isValidLifx();
|
||||
log.info("LifxDevice Home created." + (validLifx ? "" : " No LifxDevices configured."));
|
||||
if(validLifx) {
|
||||
try {
|
||||
log.info("Open Lifx client....");
|
||||
InetAddress configuredAddress = InetAddress.getByName(bridgeSettings.getUpnpConfigAddress());
|
||||
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(configuredAddress);
|
||||
InetAddress bcastInetAddr = null;
|
||||
if (networkInterface != null) {
|
||||
for (InterfaceAddress ifaceAddr : networkInterface.getInterfaceAddresses()) {
|
||||
InetAddress addr = ifaceAddr.getAddress();
|
||||
if (addr instanceof Inet4Address) {
|
||||
bcastInetAddr = ifaceAddr.getBroadcast();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bcastInetAddr != null) {
|
||||
lifxMap = new HashMap<String, LifxDevice>();
|
||||
log.info("Opening LFX Client with broadcast address: " + bcastInetAddr.getHostAddress());
|
||||
client = new LFXClient(bcastInetAddr.getHostAddress());
|
||||
client.getLights().addLightCollectionListener(new MyLightListener(lifxMap));
|
||||
client.getGroups().addGroupCollectionListener(new MyGroupListener(lifxMap));
|
||||
client.open(false);
|
||||
aGsonHandler =
|
||||
new GsonBuilder()
|
||||
.create();
|
||||
} else {
|
||||
log.warn("Could not open LIFX, no bcast addr available, check your upnp config address.");
|
||||
client = null;
|
||||
validLifx = false;
|
||||
return this;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not open LIFX, with IO Exception", e);
|
||||
client = null;
|
||||
validLifx = false;
|
||||
return this;
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Could not open LIFX, with Interruprted Exception", e);
|
||||
client = null;
|
||||
validLifx = false;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LifxDevice getLifxDevice(String aName) {
|
||||
if(!validLifx)
|
||||
return null;
|
||||
LifxDevice aLifxDevice = null;
|
||||
if(aName == null || aName.equals("")) {
|
||||
log.debug("Cannot get LifxDevice for name as it is empty.");
|
||||
}
|
||||
else {
|
||||
aLifxDevice = lifxMap.get(aName);
|
||||
log.debug("Retrieved a LifxDevice for name: " + aName);
|
||||
}
|
||||
return aLifxDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
log.debug("consolidating devices for lifx");
|
||||
if(!validLifx)
|
||||
return null;
|
||||
LifxEntry theResponse = null;
|
||||
Iterator<String> keys = lifxMap.keySet().iterator();
|
||||
List<LifxEntry> deviceList = new ArrayList<LifxEntry>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = lifxMap.get(key).toEntry();
|
||||
if(theResponse != null)
|
||||
deviceList.add(theResponse);
|
||||
else {
|
||||
log.warn("Cannot get LifxDevice with name: " + key + ", skipping this Lifx.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
private Boolean addLifxLights(LFXLightCollection theDeviceList) {
|
||||
if(!validLifx)
|
||||
return false;
|
||||
Iterator<LFXLight> devices = theDeviceList.iterator();;
|
||||
while(devices.hasNext()) {
|
||||
LFXLight theDevice = devices.next();
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(theDevice, LifxDevice.LIGHT_TYPE);
|
||||
lifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Boolean addLifxGroups(LFXGroupCollection theDeviceList) {
|
||||
if(!validLifx)
|
||||
return false;
|
||||
Iterator<LFXGroup> devices = theDeviceList.iterator();;
|
||||
while(devices.hasNext()) {
|
||||
LFXGroup theDevice = devices.next();
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(theDevice, LifxDevice.GROUP_TYPE);
|
||||
lifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String theReturn = null;
|
||||
float aBriValue;
|
||||
float theValue;
|
||||
log.debug("executing HUE api request to send message to LifxDevice: " + anItem.getItem().toString());
|
||||
if(!validLifx) {
|
||||
log.warn("Should not get here, no LifxDevice clients configured");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no LifxDevices configured\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
|
||||
} else {
|
||||
LifxEntry lifxCommand = null;
|
||||
if(anItem.getItem().isJsonObject())
|
||||
lifxCommand = aGsonHandler.fromJson(anItem.getItem(), LifxEntry.class);
|
||||
else
|
||||
lifxCommand = aGsonHandler.fromJson(anItem.getItem().getAsString(), LifxEntry.class);
|
||||
LifxDevice theDevice = getLifxDevice(lifxCommand.getName());
|
||||
if (theDevice == null) {
|
||||
log.warn("Should not get here, no LifxDevices available");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no Lifx clients available\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
log.debug("calling LifxDevice: " + lifxCommand.getName());
|
||||
if(theDevice.getType().equals(LifxDevice.LIGHT_TYPE)) {
|
||||
LFXLight theLight = (LFXLight)theDevice.getLifxObject();
|
||||
if(body.contains("true"))
|
||||
theLight.setPower(true);
|
||||
if(body.contains("false"))
|
||||
theLight.setPower(false);
|
||||
if(targetBri != null || targetBriInc != null) {
|
||||
aBriValue = (float)BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc);
|
||||
theValue = aBriValue/DIM_DIVISOR;
|
||||
if(theValue > (float)1.0)
|
||||
theValue = (float)0.99;
|
||||
theLight.setBrightness(theValue);
|
||||
}
|
||||
} else if (theDevice.getType().equals(LifxDevice.GROUP_TYPE)) {
|
||||
LFXGroup theGroup = (LFXGroup)theDevice.getLifxObject();
|
||||
if(body.contains("true"))
|
||||
theGroup.setPower(true);
|
||||
if(body.contains("false"))
|
||||
theGroup.setPower(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return theReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validLifx)
|
||||
return;
|
||||
client.close();
|
||||
}
|
||||
private static class MyLightListener implements LFXLightCollectionListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(MyLightListener.class);
|
||||
private Map<String, LifxDevice> aLifxMap;
|
||||
public MyLightListener(Map<String, LifxDevice> theMap) {
|
||||
aLifxMap = theMap;
|
||||
}
|
||||
@Override
|
||||
public void lightAdded(LFXLight light) {
|
||||
log.debug("Light added, label: " + light.getLabel() + " and id: " + light.getID());
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(light, LifxDevice.LIGHT_TYPE);
|
||||
aLifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lightRemoved(LFXLight light) {
|
||||
log.debug("Light removed, label: " + light.getLabel() + " and id: " + light.getID());
|
||||
aLifxMap.remove(light.getLabel());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
private static class MyGroupListener implements LFXGroupCollectionListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(MyLightListener.class);
|
||||
private Map<String, LifxDevice> aLifxMap;
|
||||
public MyGroupListener(Map<String, LifxDevice> theMap) {
|
||||
aLifxMap = theMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void groupAdded(LFXGroup group) {
|
||||
log.debug("Group: " + group.getLabel() + " added: " + group.size());
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(group, LifxDevice.GROUP_TYPE);
|
||||
aLifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void groupRemoved(LFXGroup group) {
|
||||
log.debug("Group: " + group.getLabel() + " removed");
|
||||
aLifxMap.remove(group.getLabel());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.bwssystems.HABridge.plugins.mqtt;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
|
||||
public class MQTTBroker {
|
||||
private String clientId;
|
||||
private String ip;
|
||||
|
||||
public MQTTBroker(NamedIP brokerConfig) {
|
||||
super();
|
||||
this.setIp(brokerConfig.getIp());
|
||||
this.setClientId(brokerConfig.getName());
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.bwssystems.HABridge.plugins.mqtt;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.eclipse.paho.client.mqttv3.MqttClient;
|
||||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
|
||||
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
|
||||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
|
||||
public class MQTTHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(MQTTHandler.class);
|
||||
private NamedIP myConfig;
|
||||
private MqttClient myClient;
|
||||
private int qos = 1;
|
||||
|
||||
public MQTTHandler(NamedIP aConfig) {
|
||||
super();
|
||||
log.info("Setting up handler for name: " + aConfig.getName());
|
||||
MemoryPersistence persistence = new MemoryPersistence();
|
||||
myConfig = aConfig;
|
||||
try {
|
||||
myClient = new MqttClient("tcp://" + myConfig.getIp(), myConfig.getName(), persistence);
|
||||
} catch (MqttException e) {
|
||||
log.error("Could not create MQTT client for name: " + myConfig.getName() + " and ip: " + myConfig.getIp() + " with message: " + e.getMessage());
|
||||
}
|
||||
MqttConnectOptions connOpts = new MqttConnectOptions();
|
||||
connOpts.setCleanSession(true);
|
||||
connOpts.setAutomaticReconnect(true);
|
||||
if(aConfig.getUsername() != null && aConfig.getUsername().trim().length() > 0) {
|
||||
if(aConfig.getPassword() != null && aConfig.getPassword().trim().length() > 0) {
|
||||
connOpts.setUserName(aConfig.getUsername().trim());
|
||||
connOpts.setPassword(aConfig.getPassword().trim().toCharArray());
|
||||
}
|
||||
}
|
||||
try {
|
||||
myClient.connect(connOpts);
|
||||
} catch (MqttSecurityException e) {
|
||||
log.error("Could not connect MQTT client for name: " + myConfig.getName() + " and ip: " + myConfig.getIp() + " with message: " + e.getMessage());
|
||||
} catch (MqttException e) {
|
||||
log.error("Could not connect MQTT client for name: " + myConfig.getName() + " and ip: " + myConfig.getIp() + " with message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void publishMessage(String topic, String content) {
|
||||
MqttMessage message = new MqttMessage(StringEscapeUtils.unescapeJava(content).getBytes());
|
||||
message.setQos(qos);
|
||||
try {
|
||||
myClient.publish(topic, message);
|
||||
} catch (MqttPersistenceException e) {
|
||||
log.error("Could not publish to MQTT client for name: " + myConfig.getName() + " and ip: " + myConfig.getIp() + " with message: " + e.getMessage());
|
||||
} catch (MqttException e) {
|
||||
log.error("Could not publish to MQTT client for name: " + myConfig.getName() + " and ip: " + myConfig.getIp() + " with message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public NamedIP getMyConfig() {
|
||||
return myConfig;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
try {
|
||||
myClient.disconnect();
|
||||
} catch (MqttException e) {
|
||||
log.warn("Could not disconnect MQTT client for name: " + myConfig.getName() + " and ip: " + myConfig.getIp());
|
||||
}
|
||||
myClient = null;
|
||||
}
|
||||
}
|
||||
144
src/main/java/com/bwssystems/HABridge/plugins/mqtt/MQTTHome.java
Normal file
144
src/main/java/com/bwssystems/HABridge/plugins/mqtt/MQTTHome.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package com.bwssystems.HABridge.plugins.mqtt;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
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.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
public class MQTTHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(MQTTHome.class);
|
||||
private Map<String, MQTTHandler> handlers;
|
||||
private Boolean validMqtt;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public MQTTHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validMqtt)
|
||||
return;
|
||||
log.debug("Shutting down MQTT handlers.");
|
||||
if(handlers != null && !handlers.isEmpty()) {
|
||||
Iterator<String> keys = handlers.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
handlers.get(key).shutdown();
|
||||
}
|
||||
}
|
||||
handlers = null;
|
||||
}
|
||||
|
||||
public MQTTHandler getMQTTHandler(String aName) {
|
||||
if(!validMqtt)
|
||||
return null;
|
||||
MQTTHandler aHandler;
|
||||
if(aName == null || aName.equals("")) {
|
||||
aHandler = null;
|
||||
log.debug("Cannot get MQTT handler for name as it is empty.");
|
||||
}
|
||||
else {
|
||||
aHandler = handlers.get(aName);
|
||||
log.debug("Retrieved a MQTT hanlder for name: " + aName);
|
||||
}
|
||||
return aHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
if(!validMqtt)
|
||||
return null;
|
||||
Iterator<String> keys = handlers.keySet().iterator();
|
||||
ArrayList<MQTTBroker> deviceList = new ArrayList<MQTTBroker>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
MQTTHandler aHandler = handlers.get(key);
|
||||
MQTTBroker aDevice = new MQTTBroker(aHandler.getMyConfig());
|
||||
deviceList.add(aDevice);
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
log.debug("executing HUE api request to send message to MQTT broker: " + anItem.getItem().toString());
|
||||
if (validMqtt) {
|
||||
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 = DeviceDataDecode.replaceDeviceData(mqttObject, device);
|
||||
mqttObject = TimeDecode.replaceTimeValue(mqttObject);
|
||||
if (mqttObject.substring(0, 1).equalsIgnoreCase("{"))
|
||||
mqttObject = "[" + mqttObject + "]";
|
||||
MQTTMessage[] mqttMessages = aGsonHandler.fromJson(mqttObject, MQTTMessage[].class);
|
||||
Integer theCount = 1;
|
||||
for(int z = 0; z < mqttMessages.length; z++) {
|
||||
if(mqttMessages[z].getCount() != null && mqttMessages[z].getCount() > 0)
|
||||
theCount = mqttMessages[z].getCount();
|
||||
for(int y = 0; y < theCount; y++) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("Should not get here, no mqtt brokers configured");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no mqtt brokers configured\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
validMqtt = bridgeSettings.isValidMQTT();
|
||||
log.info("MQTT Home created." + (validMqtt ? "" : " No MQTT Clients configured."));
|
||||
if(validMqtt) {
|
||||
aGsonHandler =
|
||||
new GsonBuilder()
|
||||
.create();
|
||||
handlers = new HashMap<String, MQTTHandler>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getMqttaddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aClientConfig = theList.next();
|
||||
MQTTHandler aHandler = new MQTTHandler(aClientConfig);
|
||||
if(aHandler != null)
|
||||
handlers.put(aClientConfig.getName(), aHandler);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.bwssystems.HABridge.plugins.mqtt;
|
||||
|
||||
public class MQTTMessage {
|
||||
private String clientId;
|
||||
private String topic;
|
||||
private String message;
|
||||
private Integer delay;
|
||||
private Integer count;
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
public Integer getDelay() {
|
||||
return delay;
|
||||
}
|
||||
public void setDelay(Integer delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
public Integer getCount() {
|
||||
return count;
|
||||
}
|
||||
public void setCount(Integer count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.bwssystems.HABridge.plugins.somfy;
|
||||
|
||||
public class SomfyDevice {
|
||||
private String id;
|
||||
private String room;
|
||||
private String category;
|
||||
private String somfyname;
|
||||
private String name;
|
||||
private String deviceUrl;
|
||||
private String deviceType;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setRoom(String room) {
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
public String getRoom() {
|
||||
return room;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setSomfyname(String somfyname) {
|
||||
this.somfyname = somfyname;
|
||||
}
|
||||
|
||||
public String getSomfyname() {
|
||||
return somfyname;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setDeviceUrl(String deviceUrl) {
|
||||
this.deviceUrl = deviceUrl;
|
||||
}
|
||||
|
||||
public String getDeviceUrl() {
|
||||
return deviceUrl;
|
||||
}
|
||||
|
||||
public void setDeviceType(String deviceType) {
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public String getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.bwssystems.HABridge.plugins.somfy;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
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;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Support for Somfy Tahoma hub which allows control of IO Homecontrol devices such as Velux windows.
|
||||
* Currently supports 'turn on' for open window, and 'turn off' for close.
|
||||
*
|
||||
* Known issues:
|
||||
* //TODO - Fix bug on UI where bulk update seems to add the single device twice if 'update' is clicked (this is a general bug with Vera too I think)
|
||||
* Enhancements:
|
||||
* //TODO - support 'dimming' for partial window opening.
|
||||
*
|
||||
*/
|
||||
public class SomfyHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(SomfyHome.class);
|
||||
private Map<String, SomfyInfo> somfys;
|
||||
private Boolean validSomfy;
|
||||
|
||||
public SomfyHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
createHome(bridgeSettings);
|
||||
|
||||
}
|
||||
|
||||
public SomfyInfo getSomfyHandler(String somfyName) {
|
||||
return somfys.get(somfyName);
|
||||
}
|
||||
|
||||
public List<SomfyDevice> getDevices() {
|
||||
log.debug("consolidating devices for somfy");
|
||||
Iterator<String> keys = somfys.keySet().iterator();
|
||||
ArrayList<SomfyDevice> deviceList = new ArrayList<>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
List<SomfyDevice> devices = somfys.get(key).getSomfyDevices();
|
||||
deviceList.addAll(devices);
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
if(validSomfy) {
|
||||
if(type.equalsIgnoreCase(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex]))
|
||||
return getDevices();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
if (!validSomfy) {
|
||||
log.warn("Should not get here, no somfy hub available");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no somfy hub available\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
if (anItem.getType() != null && anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex])) {
|
||||
|
||||
log.debug("executing HUE api request to change activity to Somfy: " + anItem.getItem().toString());
|
||||
String jsonToPost = anItem.getItem().toString();
|
||||
|
||||
SomfyInfo somfyHandler = getSomfyHandler(device.getTargetDevice());
|
||||
if(somfyHandler == null) {
|
||||
log.warn("Should not get here, no Somfy configured");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no somfy configured\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
try {
|
||||
somfyHandler.execApply(jsonToPost);
|
||||
} catch (Exception e) {
|
||||
log.warn("Error posting request to Somfy");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Error posting request to SomfyTahoma\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
validSomfy = bridgeSettings.isValidSomfy();
|
||||
log.info("Somfy Home created." + (validSomfy ? "" : " No Somfys configured."));
|
||||
if(validSomfy) {
|
||||
somfys = new HashMap<>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getSomfyAddress().getDevices().iterator();
|
||||
while (theList.hasNext()) {
|
||||
NamedIP aSomfy = theList.next();
|
||||
somfys.put(aSomfy.getName(), new SomfyInfo(aSomfy, aSomfy.getName()));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
somfys = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.bwssystems.HABridge.plugins.somfy;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup.Device;
|
||||
import com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup.GetSetup;
|
||||
import com.google.gson.Gson;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class SomfyInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(SomfyInfo.class);
|
||||
private final String somfyName;
|
||||
private final NamedIP namedIP;
|
||||
private HTTPHandler httpClient;
|
||||
private static final String CONNECT_HOST = "https://www.tahomalink.com/";
|
||||
private static final String BASE_URL = CONNECT_HOST + "enduser-mobile-web/externalAPI/";
|
||||
private static final String BASE_URL_ENDUSER = CONNECT_HOST + "enduser-mobile-web/enduserAPI/";
|
||||
|
||||
public SomfyInfo(NamedIP namedIP, String somfyName) {
|
||||
super();
|
||||
this.somfyName = somfyName;
|
||||
this.namedIP = namedIP;
|
||||
}
|
||||
|
||||
private void initHttpClient() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
|
||||
if(httpClient==null) {
|
||||
httpClient = new HTTPHandler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<SomfyDevice> getSomfyDevices() {
|
||||
|
||||
List<SomfyDevice> somfyDevices = new ArrayList<>();
|
||||
try {
|
||||
login(namedIP.getUsername(), namedIP.getPassword());
|
||||
GetSetup setupData = getSetup();
|
||||
for(Device device : setupData.getSetup().getDevices()) {
|
||||
somfyDevices.add(mapDeviceToSomfyDevice(device));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Could not get Somfy devices", e);
|
||||
}
|
||||
return somfyDevices;
|
||||
}
|
||||
|
||||
|
||||
public void login(String username, String password) throws Exception {
|
||||
|
||||
initHttpClient();
|
||||
NameValue[] httpHeader = getHttpHeaders();
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("userId", username));
|
||||
nvps.add(new BasicNameValuePair("userPassword", password));
|
||||
log.debug("Making SOMFY http login call");
|
||||
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(nvps);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
urlEncodedFormEntity.writeTo(bos);
|
||||
String body = bos.toString();
|
||||
String response = httpClient.doHttpRequest(BASE_URL + "json/login",HttpPost.METHOD_NAME, "application/x-www-form-urlencoded", body,httpHeader);
|
||||
log.debug(response);
|
||||
}
|
||||
|
||||
private NameValue[] getHttpHeaders() {
|
||||
NameValue userAgentHeader = new NameValue();
|
||||
userAgentHeader.setName("User-Agent");
|
||||
userAgentHeader.setValue("mine");
|
||||
return new NameValue[]{userAgentHeader};
|
||||
}
|
||||
|
||||
public GetSetup getSetup() throws IOException {
|
||||
NameValue[] httpHeader = getHttpHeaders();
|
||||
log.info("Making SOMFY http setup call");
|
||||
String response = httpClient.doHttpRequest(BASE_URL + "json/getSetup", HttpGet.METHOD_NAME, "", "", httpHeader );
|
||||
log.debug(response);
|
||||
GetSetup setupData = new Gson().fromJson(response, GetSetup.class);
|
||||
return setupData;
|
||||
}
|
||||
|
||||
public void execApply(String jsonToPost) throws Exception {
|
||||
login(namedIP.getUsername(), namedIP.getPassword());
|
||||
log.info("Making SOMFY http exec call");
|
||||
String response = httpClient.doHttpRequest(BASE_URL_ENDUSER + "exec/apply", HttpPost.METHOD_NAME, "application/json;charset=UTF-8", jsonToPost, getHttpHeaders());
|
||||
log.info(response);
|
||||
}
|
||||
|
||||
|
||||
protected SomfyDevice mapDeviceToSomfyDevice(Device device) {
|
||||
SomfyDevice somfyDevice = new SomfyDevice();
|
||||
somfyDevice.setId(device.getOid());
|
||||
somfyDevice.setCategory(device.getUiClass());
|
||||
somfyDevice.setRoom("");
|
||||
somfyDevice.setSomfyname(somfyName);
|
||||
somfyDevice.setName(device.getLabel());
|
||||
somfyDevice.setDeviceUrl(device.getDeviceURL());
|
||||
somfyDevice.setDeviceType(device.getWidget());
|
||||
return somfyDevice;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Device {
|
||||
|
||||
@SerializedName("label")
|
||||
@Expose
|
||||
private String label;
|
||||
@SerializedName("deviceURL")
|
||||
@Expose
|
||||
private String deviceURL;
|
||||
@SerializedName("widget")
|
||||
@Expose
|
||||
private String widget;
|
||||
@SerializedName("oid")
|
||||
@Expose
|
||||
private String oid;
|
||||
@SerializedName("uiClass")
|
||||
@Expose
|
||||
private String uiClass;
|
||||
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getDeviceURL() {
|
||||
return deviceURL;
|
||||
}
|
||||
|
||||
public void setDeviceURL(String deviceURL) {
|
||||
this.deviceURL = deviceURL;
|
||||
}
|
||||
|
||||
|
||||
public String getWidget() {
|
||||
return widget;
|
||||
}
|
||||
|
||||
public void setWidget(String widget) {
|
||||
this.widget = widget;
|
||||
}
|
||||
|
||||
|
||||
public String getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(String oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public String getUiClass() {
|
||||
return uiClass;
|
||||
}
|
||||
|
||||
public void setUiClass(String uiClass) {
|
||||
this.uiClass = uiClass;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class GetSetup {
|
||||
|
||||
@SerializedName("setup")
|
||||
@Expose
|
||||
private Setup setup;
|
||||
|
||||
public Setup getSetup() {
|
||||
return setup;
|
||||
}
|
||||
|
||||
public void setSetup(Setup setup) {
|
||||
this.setup = setup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Setup {
|
||||
|
||||
@SerializedName("id")
|
||||
@Expose
|
||||
private String id;
|
||||
@SerializedName("devices")
|
||||
@Expose
|
||||
private List<Device> devices = null;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<Device> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
public void setDevices(List<Device> devices) {
|
||||
this.devices = devices;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
130
src/main/java/com/bwssystems/HABridge/plugins/tcp/TCPHome.java
Normal file
130
src/main/java/com/bwssystems/HABridge/plugins/tcp/TCPHome.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package com.bwssystems.HABridge.plugins.tcp;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
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);
|
||||
private byte[] sendData;
|
||||
private Map<String, Socket> theSockets;
|
||||
|
||||
|
||||
public TCPHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Socket dataSendSocket = null;
|
||||
log.debug("executing HUE api request to TCP: " + anItem.getItem().getAsString());
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && theUrl.startsWith("tcp://")) {
|
||||
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
InetAddress IPAddress = null;
|
||||
dataSendSocket = theSockets.get(hostPortion);
|
||||
if(dataSendSocket == null) {
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
try {
|
||||
IPAddress = InetAddress.getByName(hostAddr);
|
||||
} catch (UnknownHostException e) {
|
||||
// noop
|
||||
}
|
||||
|
||||
try {
|
||||
dataSendSocket = new Socket(IPAddress, Integer.parseInt(port));
|
||||
theSockets.put(hostPortion, dataSendSocket);
|
||||
} catch (Exception e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
|
||||
if (theUrlBody.startsWith("0x")) {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
|
||||
theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device);
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
} else {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, false);
|
||||
theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device);
|
||||
theUrlBody = StringEscapeUtils.unescapeJava(theUrlBody);
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
try {
|
||||
DataOutputStream outToClient = new DataOutputStream(dataSendSocket.getOutputStream());
|
||||
outToClient.write(sendData);
|
||||
outToClient.flush();
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not send data to TCP socket <<<" + e.getMessage() + ">>>, closing socket: " + theUrl);
|
||||
try {
|
||||
dataSendSocket.close();
|
||||
} catch (IOException e1) {
|
||||
// noop
|
||||
}
|
||||
theSockets.remove(hostPortion);
|
||||
}
|
||||
} else
|
||||
log.warn("Tcp Call to be presented as tcp://<ip_address>:<port>/payload, format of request unknown: " + theUrl);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
log.info("TCP Home created.");
|
||||
theSockets = new HashMap<String, Socket>();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
// Not a resource
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
log.debug("Shutting down TCP sockets.");
|
||||
if(theSockets != null && !theSockets.isEmpty()) {
|
||||
Iterator<String> keys = theSockets.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
try {
|
||||
theSockets.get(key).close();
|
||||
} catch (IOException e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
100
src/main/java/com/bwssystems/HABridge/plugins/udp/UDPHome.java
Normal file
100
src/main/java/com/bwssystems/HABridge/plugins/udp/UDPHome.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package com.bwssystems.HABridge.plugins.udp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.bwssystems.HABridge.util.UDPDatagramSender;
|
||||
|
||||
public class UDPHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(UDPHome.class);
|
||||
private UDPDatagramSender theUDPDatagramSender;
|
||||
private byte[] sendData;
|
||||
|
||||
public UDPHome(BridgeSettingsDescriptor bridgeSettings, UDPDatagramSender aUDPDatagramSender) {
|
||||
super();
|
||||
theUDPDatagramSender = aUDPDatagramSender;
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@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 UDP: " + anItem.getItem().getAsString());
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && theUrl.startsWith("udp://")) {
|
||||
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
InetAddress IPAddress = null;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
try {
|
||||
IPAddress = InetAddress.getByName(hostAddr);
|
||||
} catch (UnknownHostException e) {
|
||||
log.warn("Udp Call, unknown host, continuing...");
|
||||
return null;
|
||||
}
|
||||
|
||||
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
|
||||
if (theUrlBody.startsWith("0x")) {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
|
||||
theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device);
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
} else {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, false);
|
||||
theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device);
|
||||
theUrlBody = StringEscapeUtils.unescapeJava(theUrlBody);
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
try {
|
||||
theUDPDatagramSender.sendUDPResponse(sendData, IPAddress, Integer.parseInt(port));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("Udp Call, Number format exception on port, continuing...");
|
||||
} catch (IOException e) {
|
||||
log.warn("IO exception on udp call, continuing...");
|
||||
}
|
||||
} else
|
||||
log.warn("Udp Call to be presented as udp://<ip_address>:<port>/payload, format of request unknown: " + theUrl);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
log.info("UDP Home created.");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
// Not a resource
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
111
src/main/java/com/bwssystems/HABridge/plugins/vera/VeraHome.java
Normal file
111
src/main/java/com/bwssystems/HABridge/plugins/vera/VeraHome.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package com.bwssystems.HABridge.plugins.vera;
|
||||
|
||||
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.DeviceMapTypes;
|
||||
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;
|
||||
import com.bwssystems.HABridge.plugins.vera.luupRequests.Device;
|
||||
import com.bwssystems.HABridge.plugins.vera.luupRequests.Scene;
|
||||
import com.bwssystems.HABridge.plugins.vera.luupRequests.Sdata;
|
||||
|
||||
public class VeraHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(VeraHome.class);
|
||||
private Map<String, VeraInfo> veras;
|
||||
private Boolean validVera;
|
||||
|
||||
public VeraHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
public List<Device> getDevices() {
|
||||
log.debug("consolidating devices for veras");
|
||||
Iterator<String> keys = veras.keySet().iterator();
|
||||
ArrayList<Device> deviceList = new ArrayList<Device>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Sdata theSdata = veras.get(key).getSdata();
|
||||
if(theSdata != null) {
|
||||
Iterator<Device> devices = theSdata.getDevices().iterator();
|
||||
while(devices.hasNext()) {
|
||||
deviceList.add(devices.next());
|
||||
}
|
||||
}
|
||||
else {
|
||||
deviceList = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
public List<Scene> getScenes() {
|
||||
log.debug("consolidating scenes for veras");
|
||||
Iterator<String> keys = veras.keySet().iterator();
|
||||
ArrayList<Scene> sceneList = new ArrayList<Scene>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Sdata theSdata = veras.get(key).getSdata();
|
||||
if(theSdata != null) {
|
||||
Iterator<Scene> scenes = theSdata.getScenes().iterator();
|
||||
while(scenes.hasNext()) {
|
||||
sceneList.add(scenes.next());
|
||||
}
|
||||
}
|
||||
else {
|
||||
sceneList = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sceneList;
|
||||
}
|
||||
|
||||
@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 Object getItems(String type) {
|
||||
if(validVera) {
|
||||
if(type.equalsIgnoreCase(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex]))
|
||||
return getDevices();
|
||||
if(type.equalsIgnoreCase(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex]))
|
||||
return getScenes();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
validVera = bridgeSettings.isValidVera();
|
||||
log.info("Vera Home created." + (validVera ? "" : " No Veras configured."));
|
||||
if(validVera) {
|
||||
veras = new HashMap<String, VeraInfo>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getVeraAddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aVera = theList.next();
|
||||
veras.put(aVera.getName(), new VeraInfo(aVera));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
veras = null;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,31 @@
|
||||
package com.bwssystems.vera;
|
||||
package com.bwssystems.HABridge.plugins.vera;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.luupRequests.Categorie;
|
||||
import com.bwssystems.luupRequests.Device;
|
||||
import com.bwssystems.luupRequests.Room;
|
||||
import com.bwssystems.luupRequests.Scene;
|
||||
import com.bwssystems.luupRequests.Sdata;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.plugins.vera.luupRequests.Categorie;
|
||||
import com.bwssystems.HABridge.plugins.vera.luupRequests.Device;
|
||||
import com.bwssystems.HABridge.plugins.vera.luupRequests.Room;
|
||||
import com.bwssystems.HABridge.plugins.vera.luupRequests.Scene;
|
||||
import com.bwssystems.HABridge.plugins.vera.luupRequests.Sdata;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
|
||||
public class VeraInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(VeraInfo.class);
|
||||
private HttpClient httpClient;
|
||||
private HTTPHandler httpClient;
|
||||
private static final String SDATA_REQUEST = ":3480/data_request?id=sdata&output_format=json";
|
||||
private NamedIP veraAddress;
|
||||
|
||||
public VeraInfo(NamedIP addressName) {
|
||||
super();
|
||||
httpClient = HttpClients.createDefault();
|
||||
httpClient = new HTTPHandler();
|
||||
veraAddress = addressName;
|
||||
}
|
||||
|
||||
@@ -41,7 +35,7 @@ public class VeraInfo {
|
||||
String theUrl = "http://" + veraAddress.getIp() + SDATA_REQUEST;
|
||||
String theData;
|
||||
|
||||
theData = doHttpGETRequest(theUrl);
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
|
||||
if(theData != null) {
|
||||
theSdata = new Gson().fromJson(theData, Sdata.class);
|
||||
log.debug("GET sdata - full: " + theSdata.getFull() + ", version: " + theSdata.getVersion());
|
||||
@@ -88,22 +82,4 @@ public class VeraInfo {
|
||||
theScene.setVeraname(veraAddress.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// This function executes the url against the vera
|
||||
protected String doHttpGETRequest(String url) {
|
||||
String theContent = null;
|
||||
log.debug("calling GET on URL: " + url);
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
try {
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
log.debug("GET on URL responded: " + response.getStatusLine().getStatusCode());
|
||||
if(response.getStatusLine().getStatusCode() == 200){
|
||||
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); //read content for data
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("doHttpGETRequest: Error calling out to HA gateway: " + e.getMessage());
|
||||
}
|
||||
return theContent;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
package com.bwssystems.HABridge.plugins.vera.luupRequests;
|
||||
|
||||
public class Categorie {
|
||||
private String name;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
package com.bwssystems.HABridge.plugins.vera.luupRequests;
|
||||
|
||||
public class Device {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
package com.bwssystems.HABridge.plugins.vera.luupRequests;
|
||||
|
||||
public class Room {
|
||||
private String name;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
package com.bwssystems.HABridge.plugins.vera.luupRequests;
|
||||
|
||||
public class Scene {
|
||||
private String active;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
package com.bwssystems.HABridge.plugins.vera.luupRequests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
package com.bwssystems.HABridge.plugins.vera.luupRequests;
|
||||
|
||||
public class Section {
|
||||
private String name;
|
||||
@@ -6,79 +6,86 @@ import org.slf4j.LoggerFactory;
|
||||
import com.bwssystems.HABridge.BridgeControlDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.Configuration;
|
||||
import com.bwssystems.HABridge.api.hue.HueConstants;
|
||||
import com.bwssystems.HABridge.api.hue.HuePublicConfig;
|
||||
import com.bwssystems.HABridge.util.UDPDatagramSender;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Enumeration;
|
||||
import org.apache.http.conn.util.*;
|
||||
|
||||
|
||||
public class UpnpListener {
|
||||
private Logger log = LoggerFactory.getLogger(UpnpListener.class);
|
||||
private int upnpResponsePort;
|
||||
private UDPDatagramSender theUDPDatagramSender;
|
||||
private int httpServerPort;
|
||||
private String responseAddress;
|
||||
private boolean strict;
|
||||
private boolean traceupnp;
|
||||
private BridgeControlDescriptor bridgeControl;
|
||||
private boolean discoveryTemplateLatest;
|
||||
private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" +
|
||||
"CACHE-CONTROL: max-age=86400\r\n" +
|
||||
private String bridgeId;
|
||||
private String bridgeSNUUID;
|
||||
private HuePublicConfig aHueConfig;
|
||||
private String responseTemplate1 = "HTTP/1.1 200 OK\r\n" +
|
||||
"HOST: %s:%s\r\n" +
|
||||
"CACHE-CONTROL: max-age=100\r\n" +
|
||||
"EXT:\r\n" +
|
||||
"LOCATION: http://%s:%s/description.xml\r\n" +
|
||||
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.10.0\r\n" +
|
||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
|
||||
"USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n";
|
||||
private String discoveryTemplateOld = "HTTP/1.1 200 OK\r\n" +
|
||||
"CACHE-CONTROL: max-age=86400\r\n" +
|
||||
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
|
||||
"hue-bridgeid: %s\r\n" +
|
||||
"ST: upnp:rootdevice\r\n" +
|
||||
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
|
||||
private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" +
|
||||
"HOST: %s:%s\r\n" +
|
||||
"CACHE-CONTROL: max-age=100\r\n" +
|
||||
"EXT:\r\n" +
|
||||
"LOCATION: http://%s:%s/description.xml\r\n" +
|
||||
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1\r\n" +
|
||||
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
|
||||
"hue-bridgeid: %s\r\n" +
|
||||
"ST: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
|
||||
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
|
||||
private String responseTemplate3 = "HTTP/1.1 200 OK\r\n" +
|
||||
"HOST: %s:%s\r\n" +
|
||||
"CACHE-CONTROL: max-age=100\r\n" +
|
||||
"EXT:\r\n" +
|
||||
"LOCATION: http://%s:%s/description.xml\r\n" +
|
||||
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
|
||||
"hue-bridgeid: %s\r\n" +
|
||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
|
||||
"USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n";
|
||||
|
||||
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl) {
|
||||
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
|
||||
|
||||
private String notifyTemplate = "NOTIFY * HTTP/1.1\r\n" +
|
||||
"HOST: %s:%s\r\n" +
|
||||
"CACHE-CONTROL: max-age=100\r\n" +
|
||||
"LOCATION: http://%s:%s/description.xml\r\n" +
|
||||
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
|
||||
"NTS: ssdp:alive\r\n" +
|
||||
"hue-bridgeid: %s\r\n" +
|
||||
"NT: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
|
||||
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
|
||||
|
||||
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl, UDPDatagramSender aUdpDatagramSender) {
|
||||
super();
|
||||
upnpResponsePort = theSettings.getUpnpResponsePort();
|
||||
theUDPDatagramSender = aUdpDatagramSender;
|
||||
httpServerPort = Integer.valueOf(theSettings.getServerPort());
|
||||
responseAddress = theSettings.getUpnpConfigAddress();
|
||||
strict = theSettings.isUpnpStrict();
|
||||
traceupnp = theSettings.isTraceupnp();
|
||||
bridgeControl = theControl;
|
||||
discoveryTemplateLatest = true;
|
||||
aHueConfig = HuePublicConfig.createConfig("temp", responseAddress, HueConstants.HUB_VERSION);
|
||||
bridgeId = aHueConfig.getBridgeid();
|
||||
bridgeSNUUID = aHueConfig.getSNUUIDFromMac();
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public boolean startListening(){
|
||||
log.info("UPNP Discovery Listener starting....");
|
||||
DatagramSocket responseSocket = null;
|
||||
MulticastSocket upnpMulticastSocket = null;
|
||||
Enumeration<NetworkInterface> ifs = null;
|
||||
|
||||
boolean portLoopControl = true;
|
||||
int retryCount = 0;
|
||||
while(portLoopControl) {
|
||||
try {
|
||||
responseSocket = new DatagramSocket(upnpResponsePort);
|
||||
if(retryCount > 0)
|
||||
log.info("Upnp Response Port issue, found open port: " + upnpResponsePort);
|
||||
portLoopControl = false;
|
||||
} catch(SocketException e) {
|
||||
if(retryCount == 0)
|
||||
log.warn("Upnp Response Port is in use, starting loop to find open port for 20 tries - configured port is: " + upnpResponsePort);
|
||||
if(retryCount >= 20) {
|
||||
portLoopControl = false;
|
||||
log.error("Upnp Response Port issue, could not find open port - last port tried: " + upnpResponsePort + " with message: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(portLoopControl) {
|
||||
retryCount++;
|
||||
upnpResponsePort++;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
|
||||
} catch(IOException e){
|
||||
@@ -128,6 +135,13 @@ public class UpnpListener {
|
||||
log.info("UPNP Discovery Listener running and ready....");
|
||||
boolean loopControl = true;
|
||||
boolean error = false;
|
||||
try {
|
||||
upnpMulticastSocket.setSoTimeout((int) Configuration.UPNP_NOTIFY_TIMEOUT);
|
||||
} catch (SocketException e1) {
|
||||
log.warn("Could not sent soTimeout on multi-cast socket");
|
||||
}
|
||||
Instant current, previous;
|
||||
previous = Instant.now();
|
||||
while (loopControl) { // trigger shutdown here
|
||||
byte[] buf = new byte[1024];
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||
@@ -135,12 +149,21 @@ public class UpnpListener {
|
||||
upnpMulticastSocket.receive(packet);
|
||||
if (isSSDPDiscovery(packet)) {
|
||||
try {
|
||||
sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort());
|
||||
sendUpnpResponse(packet.getAddress(), packet.getPort());
|
||||
} catch (IOException e) {
|
||||
log.error("UpnpListener encountered an error sending upnp response packet. Shutting down", e);
|
||||
error = true;
|
||||
log.warn("UpnpListener encountered an error sending upnp response packet. IP: " + packet.getAddress().getHostAddress() + " with message: " + e.getMessage());
|
||||
log.debug("UpnpListener send upnp exception: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
current = Instant.now();
|
||||
if(ChronoUnit.MILLIS.between(previous, current) > Configuration.UPNP_NOTIFY_TIMEOUT) {
|
||||
sendUpnpNotify(socketAddress.getAddress(), upnpMulticastSocket);
|
||||
previous = Instant.now();
|
||||
}
|
||||
|
||||
} catch (SocketTimeoutException e) {
|
||||
sendUpnpNotify(socketAddress.getAddress(), upnpMulticastSocket);
|
||||
} catch (IOException e) {
|
||||
log.error("UpnpListener encountered an error reading socket. Shutting down", e);
|
||||
error = true;
|
||||
@@ -155,7 +178,6 @@ public class UpnpListener {
|
||||
}
|
||||
}
|
||||
upnpMulticastSocket.close();
|
||||
responseSocket.close();
|
||||
if (bridgeControl.isReinit())
|
||||
log.info("UPNP Discovery Listener - ended, restart found");
|
||||
if (bridgeControl.isStop())
|
||||
@@ -184,7 +206,8 @@ public class UpnpListener {
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
||||
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
}
|
||||
log.debug("isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
||||
else
|
||||
log.debug("isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
||||
return true;
|
||||
}
|
||||
else if (!strict)
|
||||
@@ -194,7 +217,8 @@ public class UpnpListener {
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
||||
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
}
|
||||
log.debug("isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
||||
else
|
||||
log.debug("isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -205,17 +229,47 @@ public class UpnpListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void sendUpnpResponse(DatagramSocket socket, InetAddress requester, int sourcePort) throws IOException {
|
||||
protected void sendUpnpResponse(InetAddress requester, int sourcePort) throws IOException {
|
||||
String discoveryResponse = null;
|
||||
if(discoveryTemplateLatest)
|
||||
discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort);
|
||||
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID);
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: sendUpnpResponse discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
|
||||
}
|
||||
else
|
||||
discoveryResponse = String.format(discoveryTemplateOld, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort);
|
||||
if(traceupnp)
|
||||
log.info("Traceupnp: sendUpnpResponse discovery template with address: " + responseAddress + " and port: " + httpServerPort);
|
||||
log.debug("sendUpnpResponse discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
|
||||
theUDPDatagramSender.sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
|
||||
|
||||
discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: sendUpnpResponse discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
|
||||
}
|
||||
else
|
||||
log.debug("sendUpnpResponse discovery template with address: " + responseAddress + " and port: " + httpServerPort);
|
||||
DatagramPacket response = new DatagramPacket(discoveryResponse.getBytes(), discoveryResponse.length(), requester, sourcePort);
|
||||
socket.send(response);
|
||||
log.debug("sendUpnpResponse discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
|
||||
theUDPDatagramSender.sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
|
||||
|
||||
discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID);
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: sendUpnpResponse discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
|
||||
}
|
||||
else
|
||||
log.debug("sendUpnpResponse discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
|
||||
theUDPDatagramSender.sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
|
||||
}
|
||||
|
||||
protected void sendUpnpNotify(InetAddress aSocketAddress, MulticastSocket theUpnpMulticastSocket) {
|
||||
String notifyData = null;
|
||||
log.debug("Sending notify packet for upnp.");
|
||||
notifyData = String.format(notifyTemplate, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: sendUpnpNotify notifyTemplate is <<<" + notifyData + ">>>");
|
||||
}
|
||||
DatagramPacket notifyPacket = new DatagramPacket(notifyData.getBytes(), notifyData.length(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
|
||||
try {
|
||||
theUpnpMulticastSocket.send(notifyPacket);
|
||||
} catch (IOException e1) {
|
||||
log.warn("UpnpListener encountered an error sending upnp notify packet. IP: " + notifyPacket.getAddress().getHostAddress() + " with message: " + e1.getMessage());
|
||||
log.debug("UpnpListener send upnp notify exception: ", e1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.api.hue.HueConstants;
|
||||
import com.bwssystems.HABridge.api.hue.HuePublicConfig;
|
||||
|
||||
import static spark.Spark.get;
|
||||
|
||||
@@ -29,10 +31,10 @@ public class UpnpSettingsResource {
|
||||
+ "<manufacturerURL>http://www.philips.com</manufacturerURL>\n"
|
||||
+ "<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>\n"
|
||||
+ "<modelName>Philips hue bridge 2015</modelName>\n"
|
||||
+ "<modelNumber>BSB002</modelNumber>\n"
|
||||
+ "<modelNumber>" + HueConstants.MODEL_ID + "</modelNumber>\n"
|
||||
+ "<modelURL>http://www.meethue.com</modelURL>\n"
|
||||
+ "<serialNumber>0017880ae670</serialNumber>\n"
|
||||
+ "<UDN>uuid:2f402f80-da50-11e1-9b23-001788102201</UDN>\n"
|
||||
+ "<serialNumber>%s</serialNumber>\n"
|
||||
+ "<UDN>uuid:" + HueConstants.UUID_PREFIX + "%s</UDN>\n"
|
||||
+ "<serviceList>\n"
|
||||
+ "<service>\n"
|
||||
+ "<serviceType>(null)</serviceType>\n"
|
||||
@@ -77,7 +79,8 @@ public class UpnpSettingsResource {
|
||||
log.debug("upnp device settings requested: " + " from " + request.ip() + ":" + request.port());
|
||||
String portNumber = Integer.toString(request.port());
|
||||
String filledTemplate = null;
|
||||
filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress());
|
||||
String bridgeIdMac = HuePublicConfig.createConfig("temp", theSettings.getUpnpConfigAddress(), HueConstants.HUB_VERSION).getSNUUIDFromMac();
|
||||
filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress(), bridgeIdMac, bridgeIdMac);
|
||||
if(theSettings.isTraceupnp())
|
||||
log.info("Traceupnp: upnp device settings template filled with address: " + theSettings.getUpnpConfigAddress() + " and port: " + portNumber);
|
||||
else
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.util;
|
||||
package com.bwssystems.HABridge.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user