mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-18 08:13:23 +00:00
Compare commits
255 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
409c93bfb2 | ||
|
|
543a79bb66 | ||
|
|
16d1054b96 | ||
|
|
165b6ef9bb | ||
|
|
fb94b858f6 | ||
|
|
ea80925533 | ||
|
|
62b896ee07 | ||
|
|
8657ea6704 | ||
|
|
05bd6b6d77 | ||
|
|
19256e4eaa | ||
|
|
905f6aa9ec | ||
|
|
daa0dac5b9 | ||
|
|
0478fc69f4 | ||
|
|
b99e74823c | ||
|
|
ebeb6be7a7 | ||
|
|
d3979da2b4 | ||
|
|
f276f66991 | ||
|
|
3ac5c10f08 | ||
|
|
13c84ba174 | ||
|
|
b19fe5c86a | ||
|
|
ff9d0a5a77 | ||
|
|
50af884563 | ||
|
|
0737c54a0e | ||
|
|
a7e516925c | ||
|
|
2f1adf9d4b | ||
|
|
cd5417c2e0 | ||
|
|
ba621fcb85 | ||
|
|
7442b0d0ca | ||
|
|
c275926117 | ||
|
|
895a9ec99b | ||
|
|
6dfd70dfee | ||
|
|
0bdb321fd7 | ||
|
|
b508a8a16a | ||
|
|
ddee3a42a9 | ||
|
|
b000215b26 | ||
|
|
a2b652907f | ||
|
|
6bf1bbc8a8 | ||
|
|
1a402e425e | ||
|
|
59ef6e88de | ||
|
|
7d4f953c89 | ||
|
|
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 | ||
|
|
5a59747bc9 | ||
|
|
53ec096c95 | ||
|
|
8ccb768391 | ||
|
|
9d84e2a180 | ||
|
|
6a5fae583f | ||
|
|
8d15d0a0fb | ||
|
|
d4b8b70a83 | ||
|
|
a276f97776 | ||
|
|
9438b25538 | ||
|
|
6c15ca2c3b | ||
|
|
39782fa339 | ||
|
|
d7e29e2ee5 | ||
|
|
6b4693eaaf | ||
|
|
7f816b03d5 | ||
|
|
ed3db4427b | ||
|
|
80ca8c3ca3 | ||
|
|
e43473734e | ||
|
|
8a468b8352 | ||
|
|
51ce10cfc7 | ||
|
|
b5f7144c9c | ||
|
|
86a931d383 |
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
|
||||
|
||||
558
README.md
558
README.md
@@ -1,7 +1,45 @@
|
||||
# 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 other systems that support Philips Hue. 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.**
|
||||
|
||||
**ISSUE: Google Home now seems to not support local connection to Philips Hue Hubs and requires that it connect to meethue.com. Since the ha-bridge only emulates the local API, and is not associated with Philips, this method will not work. If you have an older Google Home application, this may still work. YMMV.**
|
||||
|
||||
**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 APIs 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 +58,31 @@ 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.0.5.jar
|
||||
java -jar ha-bridge-4.5.1.jar
|
||||
```
|
||||
### Automation on Linux systems
|
||||
To have this conigured and running automatically ther eare 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
|
||||
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.5.1.jar is in your /home/pi/habridge directory.
|
||||
```
|
||||
pi@raspberrypi:~ $ mkdir habridge
|
||||
pi@raspberrypi:~ $ cd habridge
|
||||
|
||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.5.1/ha-bridge-4.5.1.jar
|
||||
```
|
||||
|
||||
#### System Control Setup on a pi (preferred)
|
||||
For next gen Linux systems (this includes the Raspberry Pi), here is a systemctl unit file that you can install. Here is a link on how to do this: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
|
||||
|
||||
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,67 +91,197 @@ 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.0.5.jar
|
||||
WorkingDirectory=/home/pi/habridge
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.5.1.jar
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
Save the file in the editor by hitting CTL-X and then saying Y to update and save.
|
||||
|
||||
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.
|
||||
|
||||
Edit the shell script for starting:
|
||||
```
|
||||
pi@raspberrypi:~/habridge $ nano starthabridge.sh
|
||||
```
|
||||
Then cut and past this, modify any locations that are not correct
|
||||
```
|
||||
cd /home/pi/habridge
|
||||
rm /home/pi/habridge/habridge-log.txt
|
||||
nohup java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.5.1.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
|
||||
|
||||
chmod 777 /home/pi/habridge/habridge-log.txt
|
||||
```
|
||||
Exit and save the file with ctrl-X and follow the prompts and then execute on the command line:
|
||||
```
|
||||
pi@raspberrypi:~/habridge $ chmod u+x starthabridge.sh
|
||||
```
|
||||
Then execute the script:
|
||||
```
|
||||
pi@raspberrypi:~/habridge $ ./starthabridge.sh
|
||||
```
|
||||
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:
|
||||
|
||||
Enable the required Apache modules:
|
||||
|
||||
`a2enmod proxy proxy_http 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>
|
||||
```
|
||||
|
||||
Restart apache for the changes to take effect.
|
||||
|
||||
`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
|
||||
```
|
||||
### -Dsecurity.key=`<Your Key To Encrypt Security Data>`
|
||||
This option is very important to set if you will be using username/passwords to secure the ha-bridge. The ha-bridge needs to encrypt the settings in the config file and to make sure they are secured specifically to you is to provide this key. Otherwise a default key is used and it is available in the code on github for the ha-bridge here, so not very secure in that sense. **It is very important provide this if you are using username/password.** To override the default, specify -Dsecurity.key=`<Your Key To Encrypt Security Data>` explicitly on the command line. This is will prevent any issues if your config file gets hacked. The command line example:
|
||||
```
|
||||
java -jar -Dsecurity.key=Xfawer354WertSdf321234asd ha-bridge-W.X.Y.jar
|
||||
```
|
||||
### -Dexec.garden=`<The path to your scripts and program directory>`
|
||||
This sets a directory of your choosing to have a walled area for what can be executed by the Exec Command type. This is a good feature to use if you use the capabilities of executing a script or program from the ha-bridge. The default is not set which allows any program or script to be called and anyone with access to the your system could create an exec command call and execute it from the API. This is will prevent any issues if your system gets hacked. To override the default, specify -Dexec.garden=`<The path to your scripts and program directory>` explicitly on the command line. The command line example:
|
||||
```
|
||||
java -jar -Dexec.garden=C:\Users\John\bin
|
||||
```
|
||||
## 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.
|
||||
|
||||
At the bottom of the screen is the "Bridge Device DB Backup" which can be accessed with clicking on the `+` to expand this frame. Here you can backup and restore configurations that you have saved. These configs can be named or by clicking the `Backup Device DB' button will create a backup and name it for you. You can manage these backups by restoring them or deleting them.
|
||||
#### Renumber Devices
|
||||
This changes the numbering of the added devices to start at 1 and goes up from there. It was originally intended for a conversion from the previous system version that used large numbers and was not necessary. This also allows the system to try and number sequentially. If you use this button, you will need to re-discover your devices as their ID's will have changed.
|
||||
#### Link
|
||||
If this is present, you have enabled the ue link button feature for the ha-bridge. If you want a new system to recognize the ha-bridge, you will need to press this button when you are doign a discovery.
|
||||
### The Bridge Control Tab
|
||||
This is where all of the configuration occurs for what ports and IP's the bridge runs on. It also contains the configurations for target devices so that Helper Tabs for configuration can be added as well as the connection information to control those devices.
|
||||
#### Bridge server
|
||||
This field is used to test the bridge server with the UPNP IP Address and to make sure that the bridge is responding.
|
||||
#### Bridge Control Buttons
|
||||
These buttons are for managing the bridge. The Save button is enabled when there is a change to the configuration. The Bridge Reinitialize button will recycle the internal running of the bridge in the java process. The Stop button will stop the java process. The Refresh button will refresh the page and settings.
|
||||
#### The Security Dialog
|
||||
This is where you can set the different security settings for the ha-bridge. There are two settings, one for enabling Hue like operation to secure the Hue API with the internally generated user for the calls that are done after the link button. The other is to secure the hue API with a username/password that is created as well. The other fields are to add and delete users and to set and change passwords for those users. If there are no users in the system, the system will not require a username/password to operate.
|
||||
#### Configuration Path and File
|
||||
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 `<directory>/<filename>` explicitly.
|
||||
#### Device DB Path and File
|
||||
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 devce/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
|
||||
@@ -109,17 +295,54 @@ 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 format of the example is in JSON where the JSON tags equate to the UI labels of the On Items/Dim Items/Off Items. i.e.: JSON item = UI Target Item, JSON type = UI Type, etc...
|
||||
|
||||
The Add/Edit tab will show you the fields to fill in for the above in a form, when you have 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:
|
||||
```
|
||||
@@ -129,43 +352,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.
|
||||
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":"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.
|
||||
@@ -174,62 +397,88 @@ 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"}]
|
||||
|
||||
[{"item":"udp://192.168.1.1:5000/0x45${intensity.percent}55","type":"udpDevice"}]
|
||||
|
||||
[{"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"}]
|
||||
|
||||
```
|
||||
|
||||
Also, you may want to use the REST API's listed below to configure your devices.
|
||||
Also, you may want to use the REST APIs listed below to configure your devices.
|
||||
## Ask Alexa
|
||||
After this Tell Alexa: "Alexa, discover my devices". If there is an issue you can go to the `Menu / Settings / Connected Home` for the echo on the mobile app or your browser and have Alexa forget all devices and then do the discovery again.
|
||||
|
||||
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, 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>`
|
||||
ON Commands | Alexa, turn on `<Device Name>`
|
||||
OFF Commands | Alexa, turn off `<Device Name>`
|
||||
DIM Commands | Alexa, brighten `<Device Name>` to `<Position>`
|
||||
DIM Commands| Alexa, dim `<Device Name>` to `<Position>`
|
||||
DIM Commands| Alexa, brighten `<Device Name>`
|
||||
DIM Commands| Alexa, dim `<Device Name>`
|
||||
DIM Commands| Alexa, set `<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.
|
||||
|
||||
**ISSUE: Google Home now seems to not support local connection to Philips Hue Hubs and requires that it connect to meethue.com. Since the ha-bridge only emulates the local API, and is not associated with Philips, this method will not work. If you have an older Google Home application, this may still work. YMMV.**
|
||||
|
||||
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:
|
||||
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:
|
||||
```
|
||||
{"var1":"value1","var2":"value2","var3:"value3"}
|
||||
```
|
||||
@@ -238,7 +487,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
|
||||
@@ -261,19 +510,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.
|
||||
@@ -285,8 +534,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.
|
||||
@@ -298,12 +547,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
|
||||
@@ -312,8 +557,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.
|
||||
@@ -321,8 +566,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
|
||||
@@ -347,14 +592,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
|
||||
@@ -366,7 +613,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
|
||||
@@ -379,8 +626,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
|
||||
@@ -389,8 +636,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
|
||||
@@ -405,15 +652,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
|
||||
@@ -428,8 +675,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
|
||||
@@ -442,7 +689,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
|
||||
@@ -592,7 +839,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
|
||||
-----|-------|-------------
|
||||
@@ -633,7 +880,7 @@ The example below is representative of some HUE device responses.
|
||||
|
||||
Name | Type | Description
|
||||
-----|-------|-------------
|
||||
device | HUE lights object | The HUE light detail descriptor, see api for lights response below.
|
||||
device | HUE lights object | The HUE light detail descriptor, see API for lights response below.
|
||||
huedeviceid | string | The id of the actual passthru HUE light id.
|
||||
hueaddress | string | The address of the target HUE bridge.
|
||||
huename | string | A name given to the target HUE bridge.
|
||||
@@ -642,7 +889,7 @@ huename | string | A name given to the target HUE bridge.
|
||||
[{"device":{"state":{"on":true,"bri":254,"hue":4444,"sat":254,"effect":"none","ct":0,"alert":"none","colormode":"hs","reachable":true,"xy":[0.0,0.0]},"type":"Extended color light","name":"Hue Lamp 1","modelid":"LCT001","uniqueid":"00:17:88:01:00:d4:12:08-0a","swversion":"65003148"},"huedeviceid":"1","hueaddress":"192.168.0.118:8000","huename":"HueEmul"},{"device":{"state":{"on":true,"bri":254,"hue":23536,"sat":144,"effect":"none","ct":201,"alert":"none","colormode":"hs","reachable":true,"xy":[0.346,0.3568]},"type":"Extended color light","name":"Hue Lamp 2","modelid":"LCT001","uniqueid":"00:17:88:01:00:d4:12:08-0b","swversion":"65003148"},"huedeviceid":"2","hueaddress":"192.168.0.118:8000","huename":"HueEmul"},{"device":{"state":{"on":true,"bri":254,"hue":65136,"sat":254,"effect":"none","ct":201,"alert":"none","colormode":"hs","reachable":true,"xy":[0.346,0.3568]},"type":"Extended color light","name":"Hue Lamp 3","modelid":"LCT001","uniqueid":"00:17:88:01:00:d4:12:08-0c","swversion":"65003148"},"huedeviceid":"3","hueaddress":"192.168.0.118:8000","huename":"HueEmul"}]
|
||||
```
|
||||
## HUE REST API usage
|
||||
This section will describe the REST api available for controlling the bridge based off of the HUE API. This Bridge does not support the full HUE API, only the calls that are supported with the HA Bridge are shown. The REST body examples are all formatted for easy reading, the actual body usage should be like this:
|
||||
This section will describe the REST API available for controlling the bridge based off of the HUE API. This Bridge does not support the full HUE API, only the calls that are supported with the HA Bridge are shown. The REST body examples are all formatted for easy reading, the actual body usage should be like this:
|
||||
```
|
||||
{"var1":"value1","var2":"value2","var3:"value3"}
|
||||
```
|
||||
@@ -751,12 +998,12 @@ A response to a successful PUT request contains confirmation of the arguments pa
|
||||
]
|
||||
```
|
||||
### Update bridge internal light state
|
||||
Allows the user to set the internal state of the light on and off, modify the brightness. This is not a HUE API call and is special to the bridge as it keeps track of the state changes to the light from the api. It is intended to allow you to sync the bridge state with your HA system state.
|
||||
Allows the user to set the internal state of the light on and off, modify the brightness. This is not a HUE API call and is special to the bridge as it keeps track of the state changes to the light from the API. It is intended to allow you to sync the bridge state with your HA system state.
|
||||
```
|
||||
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
|
||||
@@ -888,22 +1135,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
|
||||
```
|
||||
@@ -913,18 +1188,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
|
||||
@@ -954,13 +1229,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
|
||||
```
|
||||
|
||||
53
pom.xml
53
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.bwssystems.HABridge</groupId>
|
||||
<artifactId>ha-bridge</artifactId>
|
||||
<version>2.0.5</version>
|
||||
<version>4.5.1</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>
|
||||
@@ -89,18 +94,8 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.2.4</version>
|
||||
<version>2.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.cedarsoftware</groupId>
|
||||
<artifactId>json-io</artifactId>
|
||||
<version>4.1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.eval</groupId>
|
||||
<artifactId>eval</artifactId>
|
||||
@@ -116,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>
|
||||
@@ -139,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>
|
||||
@@ -193,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
|
||||
@@ -206,4 +227,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
@@ -3,6 +3,7 @@ package com.bwssystems.HABridge;
|
||||
public class BridgeControlDescriptor {
|
||||
private boolean reinit;
|
||||
private boolean stop;
|
||||
private boolean linkButton;
|
||||
|
||||
public BridgeControlDescriptor() {
|
||||
super();
|
||||
@@ -22,4 +23,12 @@ public class BridgeControlDescriptor {
|
||||
public void setStop(boolean stop) {
|
||||
this.stop = stop;
|
||||
}
|
||||
|
||||
public boolean isLinkButton() {
|
||||
return linkButton;
|
||||
}
|
||||
|
||||
public void setLinkButton(boolean linkButton) {
|
||||
this.linkButton = linkButton;
|
||||
}
|
||||
}
|
||||
|
||||
356
src/main/java/com/bwssystems/HABridge/BridgeSecurity.java
Normal file
356
src/main/java/com/bwssystems/HABridge/BridgeSecurity.java
Normal file
@@ -0,0 +1,356 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.PBEParameterSpec;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.api.hue.HueError;
|
||||
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
|
||||
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import spark.Request;
|
||||
|
||||
public class BridgeSecurity {
|
||||
private static final Logger log = LoggerFactory.getLogger(BridgeSecurity.class);
|
||||
private static final String USER_SESSION_ID = "user";
|
||||
private static final String DEPRACATED_INTERNAL_USER = "thehabridgeuser";
|
||||
private static final String TEST_USER_TYPE = "test_ha_bridge";
|
||||
private static final byte[] SALT = {
|
||||
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
|
||||
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
|
||||
};
|
||||
private char[] habridgeKey;
|
||||
private String execGarden;
|
||||
private BridgeSecurityDescriptor securityDescriptor;
|
||||
private boolean settingsChanged;
|
||||
|
||||
public BridgeSecurity(char[] theKey, String theExecGarden) {
|
||||
habridgeKey = theKey;
|
||||
execGarden = theExecGarden;
|
||||
securityDescriptor = null;
|
||||
settingsChanged = false;
|
||||
}
|
||||
|
||||
public void setSecurityData(String theData) {
|
||||
String anError = null;
|
||||
if(theData != null && !theData.isEmpty()) {
|
||||
try {
|
||||
securityDescriptor = new Gson().fromJson(decrypt(theData), BridgeSecurityDescriptor.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
anError = e.getMessage();
|
||||
} catch (GeneralSecurityException e) {
|
||||
anError = e.getMessage();
|
||||
} catch (IOException e) {
|
||||
anError = e.getMessage();
|
||||
}
|
||||
if(anError != null)
|
||||
log.warn("Cound not get security data, using default security (none): " + anError);
|
||||
}
|
||||
|
||||
if(theData == null || anError != null) {
|
||||
securityDescriptor = new BridgeSecurityDescriptor();
|
||||
}
|
||||
}
|
||||
|
||||
public String getSecurityDescriptorData() throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
return encrypt(new Gson().toJson(securityDescriptor));
|
||||
}
|
||||
|
||||
public boolean isUseLinkButton() {
|
||||
return securityDescriptor.isUseLinkButton();
|
||||
}
|
||||
|
||||
public String setPassword(User aUser) throws IOException {
|
||||
String error = null;
|
||||
if(aUser != null) {
|
||||
error = aUser.validate();
|
||||
if(error == null) {
|
||||
if(securityDescriptor.getUsers() != null) {
|
||||
User theUser = securityDescriptor.getUsers().get(aUser.getUsername());
|
||||
if(theUser != null) {
|
||||
theUser.setPassword(aUser.getPassword());
|
||||
theUser.setPassword2(null);
|
||||
settingsChanged = true;
|
||||
}
|
||||
else
|
||||
error = "User not found";
|
||||
}
|
||||
else
|
||||
error = "User not found";
|
||||
}
|
||||
}
|
||||
else
|
||||
error = "invalid user object given";
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
public String addUser(User aUser) throws IOException {
|
||||
String error = null;
|
||||
if(aUser != null) {
|
||||
error = aUser.validate();
|
||||
if(error == null) {
|
||||
if(securityDescriptor.getUsers() == null)
|
||||
securityDescriptor.setUsers(new HashMap<String, User>());
|
||||
if(securityDescriptor.getUsers().get(aUser.getUsername()) == null) {
|
||||
securityDescriptor.getUsers().put(aUser.getUsername(), aUser);
|
||||
settingsChanged = true;
|
||||
}
|
||||
else
|
||||
error = "Invalid request";
|
||||
}
|
||||
}
|
||||
else
|
||||
error = "invalid user object given";
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
public String delUser(User aUser) throws IOException {
|
||||
String error = null;
|
||||
if(aUser != null) {
|
||||
if(securityDescriptor.getUsers() != null) {
|
||||
if(securityDescriptor.getUsers().get(aUser.getUsername()) != null) {
|
||||
securityDescriptor.getUsers().remove(aUser.getUsername());
|
||||
settingsChanged = true;
|
||||
}
|
||||
else
|
||||
error = "User not found";
|
||||
}
|
||||
}
|
||||
else
|
||||
error = "invalid user object given";
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
public String getExecGarden() {
|
||||
return execGarden;
|
||||
}
|
||||
public void setUseLinkButton(boolean useThis) {
|
||||
securityDescriptor.setUseLinkButton(useThis);
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
public boolean isSecureHueApi() {
|
||||
return securityDescriptor.isSecureHueApi();
|
||||
}
|
||||
|
||||
public void setSecureHueApi(boolean theState) {
|
||||
securityDescriptor.setSecureHueApi(theState);
|
||||
}
|
||||
public SecurityInfo getSecurityInfo() {
|
||||
SecurityInfo theInfo = new SecurityInfo();
|
||||
theInfo.setUseLinkButton(isUseLinkButton());
|
||||
theInfo.setSecureHueApi(isSecureHueApi());
|
||||
theInfo.setSecure(isSecure());
|
||||
return theInfo;
|
||||
}
|
||||
public LoginResult validatePassword(User targetUser) throws IOException {
|
||||
LoginResult result = new LoginResult();
|
||||
if(targetUser != null && targetUser.getUsername() != null) {
|
||||
if(securityDescriptor.getUsers() != null && securityDescriptor.getUsers().get(targetUser.getUsername()) != null) {
|
||||
User theUser = securityDescriptor.getUsers().get(targetUser.getUsername());
|
||||
if(theUser.getPassword() != null) {
|
||||
theUser.setPassword2(targetUser.getPassword());
|
||||
if(theUser.validatePassword()) {
|
||||
theUser.setPassword2(null);
|
||||
result.setUser(targetUser);
|
||||
}
|
||||
else
|
||||
result.setError("user or password not correct");
|
||||
} else {
|
||||
result.setError("input password is not set....");
|
||||
}
|
||||
}
|
||||
else
|
||||
result.setError("user or password not correct");
|
||||
}
|
||||
else
|
||||
result.setError("input user not given");
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
return securityDescriptor.isSecure();
|
||||
}
|
||||
|
||||
public boolean isSettingsChanged() {
|
||||
return settingsChanged;
|
||||
}
|
||||
|
||||
public void setSettingsChanged(boolean settingsChanged) {
|
||||
this.settingsChanged = settingsChanged;
|
||||
}
|
||||
|
||||
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 (securityDescriptor.getWhitelist() != null) {
|
||||
Set<String> theUserIds = securityDescriptor.getWhitelist().keySet();
|
||||
Iterator<String> userIterator = theUserIds.iterator();
|
||||
while (userIterator.hasNext()) {
|
||||
validUser = userIterator.next();
|
||||
if (validUser.equals(aUser)) {
|
||||
found = true;
|
||||
log.debug("validateWhitelistUser: found a user <" + aUser + ">");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found && !strict) {
|
||||
log.debug("validateWhitelistUser: a user was not found and it is not strict rules <" + aUser + "> being created");
|
||||
newWhitelistUser(aUser, userDescription);
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
log.debug("validateWhitelistUser: a user was not found and it is strict rules <" + aUser + ">");
|
||||
return HueErrorResponse.createResponse("1", "/api/" + aUser == null ? "" : aUser, "unauthorized user", null, null, null).getTheErrors();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void newWhitelistUser(String aUser, String userDescription) {
|
||||
if(aUser.equals(DEPRACATED_INTERNAL_USER))
|
||||
return;
|
||||
if (securityDescriptor.getWhitelist() == null) {
|
||||
securityDescriptor.setWhitelist(new HashMap<>());
|
||||
}
|
||||
if(userDescription == null)
|
||||
userDescription = "auto insert user";
|
||||
|
||||
securityDescriptor.getWhitelist().put(aUser, WhitelistEntry.createEntry(userDescription));
|
||||
setSettingsChanged(true);
|
||||
}
|
||||
|
||||
public String createWhitelistUser(String userDescription) {
|
||||
String aUser = getNewUserID();
|
||||
newWhitelistUser(aUser, userDescription);
|
||||
return aUser;
|
||||
}
|
||||
|
||||
public void convertWhitelist(Map<String, WhitelistEntry> whitelist) {
|
||||
securityDescriptor.setWhitelist(whitelist);
|
||||
}
|
||||
|
||||
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 void removeTestUsers() {
|
||||
if (securityDescriptor.getWhitelist() != null) {
|
||||
Object anUser = securityDescriptor.getWhitelist().remove(DEPRACATED_INTERNAL_USER);
|
||||
if(anUser != null)
|
||||
setSettingsChanged(true);
|
||||
|
||||
Iterator<Entry<String, WhitelistEntry>> it = securityDescriptor.getWhitelist().entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<String, WhitelistEntry> pair = it.next();
|
||||
it.remove(); // avoids a ConcurrentModificationException
|
||||
if(pair.getValue().getName().equals(TEST_USER_TYPE)) {
|
||||
securityDescriptor.getWhitelist().remove(pair.getKey());
|
||||
setSettingsChanged(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
|
||||
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(habridgeKey));
|
||||
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
|
||||
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
|
||||
return base64Encode(pbeCipher.doFinal(property.getBytes("UTF-8")));
|
||||
}
|
||||
|
||||
private static String base64Encode(byte[] bytes) {
|
||||
return Base64.getEncoder().encodeToString(bytes);
|
||||
}
|
||||
|
||||
private String decrypt(String property) throws GeneralSecurityException, IOException {
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
|
||||
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(habridgeKey));
|
||||
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
|
||||
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
|
||||
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
|
||||
}
|
||||
|
||||
private static byte[] base64Decode(String property) throws IOException {
|
||||
return Base64.getDecoder().decode(property);
|
||||
}
|
||||
|
||||
public void addAuthenticatedUser(Request request, User u) {
|
||||
request.session().attribute(USER_SESSION_ID, u);
|
||||
|
||||
}
|
||||
|
||||
public void removeAuthenticatedUser(Request request) {
|
||||
request.session().removeAttribute(USER_SESSION_ID);
|
||||
|
||||
}
|
||||
|
||||
public User getAuthenticatedUser(Request request) {
|
||||
User theUser = request.session().attribute(USER_SESSION_ID);
|
||||
if(theUser == null) {
|
||||
String authHeader = request.headers("Authorization");
|
||||
if(authHeader != null) {
|
||||
byte[] authData;
|
||||
try {
|
||||
authData = base64Decode(authHeader.substring(6));
|
||||
} catch (IOException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
return theUser;
|
||||
}
|
||||
String[] credentials = new String(authData).split(":");
|
||||
String username = credentials[0];
|
||||
String password = credentials[1];
|
||||
theUser = new User();
|
||||
theUser.setUsername(username);
|
||||
theUser.setPassword(password);
|
||||
LoginResult theResult = null;
|
||||
try {
|
||||
theResult = validatePassword(theUser);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
return null;
|
||||
}
|
||||
if(theResult != null && theResult.getError() == null) {
|
||||
addAuthenticatedUser(request, theUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
return theUser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.Map;
|
||||
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
|
||||
|
||||
public class BridgeSecurityDescriptor {
|
||||
private Map<String, User> users;
|
||||
private boolean useLinkButton;
|
||||
private String execGarden;
|
||||
private boolean secureHueApi;
|
||||
private Map<String, WhitelistEntry> whitelist;
|
||||
|
||||
public BridgeSecurityDescriptor() {
|
||||
super();
|
||||
this.setUseLinkButton(false);
|
||||
}
|
||||
|
||||
public Map<String, User> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public void setUsers(Map<String, User> users) {
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
public boolean isUseLinkButton() {
|
||||
return useLinkButton;
|
||||
}
|
||||
|
||||
public void setUseLinkButton(boolean useLinkButton) {
|
||||
this.useLinkButton = useLinkButton;
|
||||
}
|
||||
|
||||
public String getExecGarden() {
|
||||
return execGarden;
|
||||
}
|
||||
|
||||
public void setExecGarden(String execGarden) {
|
||||
this.execGarden = execGarden;
|
||||
}
|
||||
|
||||
public boolean isSecureHueApi() {
|
||||
return secureHueApi;
|
||||
}
|
||||
|
||||
public void setSecureHueApi(boolean secureHueApi) {
|
||||
this.secureHueApi = secureHueApi;
|
||||
}
|
||||
public Map<String, WhitelistEntry> getWhitelist() {
|
||||
return whitelist;
|
||||
}
|
||||
public void setWhitelist(Map<String, WhitelistEntry> whitelist) {
|
||||
this.whitelist = whitelist;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
boolean secureFlag = false;
|
||||
if(users != null && !users.isEmpty()) {
|
||||
for (Map.Entry<String, User> entry : users.entrySet())
|
||||
{
|
||||
if(entry.getValue().getPassword() != null && !entry.getValue().getPassword().isEmpty()) {
|
||||
secureFlag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return secureFlag;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
@@ -9,24 +10,44 @@ 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.security.GeneralSecurityException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
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 {
|
||||
private static final Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
private BridgeSettingsDescriptor theBridgeSettings;
|
||||
private BridgeControlDescriptor bridgeControl;
|
||||
private BridgeSecurity bridgeSecurity;
|
||||
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
|
||||
|
||||
public BridgeSettings() {
|
||||
super();
|
||||
bridgeControl = new BridgeControlDescriptor();
|
||||
theBridgeSettings = new BridgeSettingsDescriptor();
|
||||
bridgeSecurity = null;
|
||||
String theKey = System.getProperty("security.key");
|
||||
if(theKey == null)
|
||||
theKey = "IWantMyPasswordsToBeAbleToBeDecodedPleaseSeeTheReadme";
|
||||
String execGarden = System.getProperty("exec.garden");
|
||||
bridgeSecurity = new BridgeSecurity(theKey.toCharArray(), execGarden);
|
||||
String ipV6Stack = System.getProperty("ipV6Stack");
|
||||
if(ipV6Stack == null || !ipV6Stack.equalsIgnoreCase("true")) {
|
||||
System.setProperty("java.net.preferIPv4Stack" , "true");
|
||||
}
|
||||
|
||||
}
|
||||
public BridgeControlDescriptor getBridgeControl() {
|
||||
return bridgeControl;
|
||||
@@ -34,13 +55,18 @@ public class BridgeSettings extends BackupHandler {
|
||||
public BridgeSettingsDescriptor getBridgeSettingsDescriptor() {
|
||||
return theBridgeSettings;
|
||||
}
|
||||
public BridgeSecurity getBridgeSecurity() {
|
||||
return bridgeSecurity;
|
||||
}
|
||||
public static String getCurrentDate() {
|
||||
return dateFormat.format(new Date());
|
||||
}
|
||||
|
||||
public void buildSettings() {
|
||||
Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
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 +74,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 +123,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)));
|
||||
@@ -105,35 +147,19 @@ public class BridgeSettings extends BackupHandler {
|
||||
theBridgeSettings.setNestpwd(System.getProperty("nest.pwd"));
|
||||
}
|
||||
|
||||
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);
|
||||
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().trim().equals("") || theBridgeSettings.getUpnpConfigAddress().trim().equals("0.0.0.0")) {
|
||||
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,19 +171,34 @@ public class BridgeSettings extends BackupHandler {
|
||||
if(theBridgeSettings.getUpnpDeviceDb() == null)
|
||||
theBridgeSettings.setUpnpDeviceDb(Configuration.DEVICE_DB_DIRECTORY);
|
||||
|
||||
if(theBridgeSettings.getNumberoflogmessages() == null)
|
||||
theBridgeSettings.setNumberoflogmessages(Configuration.NUMBER_OF_LOG_MESSAGES);
|
||||
|
||||
if(theBridgeSettings.getButtonsleep() <= 0)
|
||||
if(theBridgeSettings.getNumberoflogmessages() == null || theBridgeSettings.getNumberoflogmessages() <= 0)
|
||||
theBridgeSettings.setNumberoflogmessages(new Integer(Configuration.NUMBER_OF_LOG_MESSAGES));
|
||||
|
||||
if(theBridgeSettings.getButtonsleep() == null || theBridgeSettings.getButtonsleep() < 0)
|
||||
theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP));
|
||||
|
||||
theBridgeSettings.setVeraconfigured(theBridgeSettings.isValidVera());
|
||||
theBridgeSettings.setHarmonyconfigured(theBridgeSettings.isValidHarmony());
|
||||
theBridgeSettings.setNestConfigured(theBridgeSettings.isValidNest());
|
||||
theBridgeSettings.setHueconfigured(theBridgeSettings.isValidHue());
|
||||
if(serverPortOverride != null)
|
||||
theBridgeSettings.setHalconfigured(theBridgeSettings.isValidHal());
|
||||
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-");
|
||||
|
||||
bridgeSecurity.setSecurityData(theBridgeSettings.getSecurityData());
|
||||
if(theBridgeSettings.getWhitelist() != null) {
|
||||
bridgeSecurity.convertWhitelist(theBridgeSettings.getWhitelist());
|
||||
theBridgeSettings.removeWhitelist();
|
||||
updateConfigFile();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadConfig() {
|
||||
@@ -171,42 +212,62 @@ public class BridgeSettings extends BackupHandler {
|
||||
|
||||
private void _loadConfig(Path aPath) {
|
||||
String jsonContent = configReader(aPath);
|
||||
BridgeSettingsDescriptor aBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettingsDescriptor.class);
|
||||
theBridgeSettings.setButtonsleep(aBridgeSettings.getButtonsleep());
|
||||
theBridgeSettings.setUpnpConfigAddress(aBridgeSettings.getUpnpConfigAddress());
|
||||
theBridgeSettings.setServerPort(aBridgeSettings.getServerPort());
|
||||
theBridgeSettings.setUpnpResponsePort(aBridgeSettings.getUpnpResponsePort());
|
||||
theBridgeSettings.setUpnpDeviceDb(aBridgeSettings.getUpnpDeviceDb());
|
||||
theBridgeSettings.setVeraAddress(aBridgeSettings.getVeraAddress());
|
||||
theBridgeSettings.setHarmonyAddress(aBridgeSettings.getHarmonyAddress());
|
||||
theBridgeSettings.setHarmonyUser(aBridgeSettings.getHarmonyUser());
|
||||
theBridgeSettings.setHarmonyPwd(aBridgeSettings.getHarmonyPwd());
|
||||
theBridgeSettings.setUpnpStrict(aBridgeSettings.isUpnpStrict());
|
||||
theBridgeSettings.setTraceupnp(aBridgeSettings.isTraceupnp());
|
||||
theBridgeSettings.setNestuser(aBridgeSettings.getNestuser());
|
||||
theBridgeSettings.setNestpwd(aBridgeSettings.getNestpwd());
|
||||
theBridgeSettings.setVeraconfigured(aBridgeSettings.isValidVera());
|
||||
theBridgeSettings.setHarmonyconfigured(aBridgeSettings.isValidHarmony());
|
||||
theBridgeSettings.setNestConfigured(aBridgeSettings.isValidNest());
|
||||
theBridgeSettings.setNumberoflogmessages(aBridgeSettings.getNumberoflogmessages());
|
||||
theBridgeSettings.setFarenheit(aBridgeSettings.isFarenheit());
|
||||
theBridgeSettings.setHueaddress(aBridgeSettings.getHueaddress());
|
||||
theBridgeSettings.setHueconfigured(aBridgeSettings.isValidHue());
|
||||
if(jsonContent == null)
|
||||
return;
|
||||
try {
|
||||
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) {
|
||||
Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
log.debug("Save HA Bridge settings.");
|
||||
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
if(bridgeSecurity.isSettingsChanged()) {
|
||||
try {
|
||||
newBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.warn("could not get encoded security data: " + e.getMessage());
|
||||
return;
|
||||
} catch (GeneralSecurityException e) {
|
||||
log.warn("could not get encoded security data: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
bridgeSecurity.setSettingsChanged(false);
|
||||
}
|
||||
String jsonValue = aRenderer.render(newBridgeSettings);
|
||||
configWriter(jsonValue, configPath);
|
||||
_loadConfig(configPath);
|
||||
}
|
||||
|
||||
|
||||
private void configWriter(String content, Path filePath) {
|
||||
Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
public void updateConfigFile() {
|
||||
log.debug("Save HA Bridge settings.");
|
||||
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
if(bridgeSecurity.isSettingsChanged()) {
|
||||
try {
|
||||
theBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.warn("could not get encoded security data: " + e.getMessage());
|
||||
return;
|
||||
} catch (GeneralSecurityException e) {
|
||||
log.warn("could not get encoded security data: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
bridgeSecurity.setSettingsChanged(false);
|
||||
}
|
||||
String jsonValue = aRenderer.render(theBridgeSettings);
|
||||
configWriter(jsonValue, configPath);
|
||||
_loadConfig(configPath);
|
||||
}
|
||||
|
||||
|
||||
private synchronized void configWriter(String content, Path filePath) {
|
||||
if(Files.exists(filePath) && !Files.isWritable(filePath)){
|
||||
log.error("Error file is not writable: " + filePath);
|
||||
return;
|
||||
@@ -223,10 +284,25 @@ public class BridgeSettings extends BackupHandler {
|
||||
try {
|
||||
Path target = null;
|
||||
if(Files.exists(filePath)) {
|
||||
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "habridge.config.old");
|
||||
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "habridge.config.old." + getCurrentDate());
|
||||
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 {
|
||||
String osName = System.getProperty("os.name");
|
||||
if(osName.toLowerCase().indexOf("win") < 0)
|
||||
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) {
|
||||
@@ -235,15 +311,12 @@ public class BridgeSettings extends BackupHandler {
|
||||
}
|
||||
|
||||
private String configReader(Path filePath) {
|
||||
Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
|
||||
String content = null;
|
||||
if(Files.notExists(filePath) || !Files.isReadable(filePath)){
|
||||
log.warn("Error reading the file: " + filePath + " - Does not exist or is not readable. continuing...");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
content = new String(Files.readAllBytes(filePath));
|
||||
} catch (IOException e) {
|
||||
@@ -252,4 +325,40 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,105 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import com.bwssystems.HABridge.api.hue.HueConstants;
|
||||
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
|
||||
|
||||
public class BridgeSettingsDescriptor {
|
||||
@SerializedName("upnpconfigaddress")
|
||||
@Expose
|
||||
private String upnpconfigaddress;
|
||||
@SerializedName("serverport")
|
||||
@Expose
|
||||
private Integer serverport;
|
||||
@SerializedName("upnpresponseport")
|
||||
@Expose
|
||||
private Integer upnpresponseport;
|
||||
@SerializedName("upnpdevicedb")
|
||||
@Expose
|
||||
private String upnpdevicedb;
|
||||
@SerializedName("veraaddress")
|
||||
@Expose
|
||||
private IpList veraaddress;
|
||||
@SerializedName("harmonyaddress")
|
||||
@Expose
|
||||
private IpList harmonyaddress;
|
||||
private String harmonyuser;
|
||||
private String harmonypwd;
|
||||
@SerializedName("buttonsleep")
|
||||
@Expose
|
||||
private Integer buttonsleep;
|
||||
@SerializedName("upnpstrict")
|
||||
@Expose
|
||||
private boolean upnpstrict;
|
||||
@SerializedName("traceupnp")
|
||||
@Expose
|
||||
private boolean traceupnp;
|
||||
@SerializedName("nestuser")
|
||||
@Expose
|
||||
private String nestuser;
|
||||
@SerializedName("nestpwd")
|
||||
@Expose
|
||||
private String nestpwd;
|
||||
@SerializedName("farenheit")
|
||||
@Expose
|
||||
private boolean farenheit;
|
||||
@SerializedName("configfile")
|
||||
@Expose
|
||||
private String configfile;
|
||||
@SerializedName("numberoflogmessages")
|
||||
@Expose
|
||||
private Integer numberoflogmessages;
|
||||
@SerializedName("hueaddress")
|
||||
@Expose
|
||||
private IpList hueaddress;
|
||||
@SerializedName("haladdress")
|
||||
@Expose
|
||||
private IpList haladdress;
|
||||
@SerializedName("haltoken")
|
||||
@Expose
|
||||
private String haltoken;
|
||||
@SerializedName("whitelist")
|
||||
@Expose
|
||||
private Map<String, WhitelistEntry> whitelist;
|
||||
@SerializedName("myechourl")
|
||||
@Expose
|
||||
private String myechourl;
|
||||
@SerializedName("webaddress")
|
||||
@Expose
|
||||
private String webaddress;
|
||||
@SerializedName("mqttaddress")
|
||||
@Expose
|
||||
private IpList mqttaddress;
|
||||
@SerializedName("hassaddress")
|
||||
@Expose
|
||||
private IpList hassaddress;
|
||||
@SerializedName("domoticzaddress")
|
||||
@Expose
|
||||
private IpList domoticzaddress;
|
||||
@SerializedName("somfyaddress")
|
||||
@Expose
|
||||
private IpList somfyaddress;
|
||||
@SerializedName("hubversion")
|
||||
@Expose
|
||||
private String hubversion;
|
||||
@SerializedName("securityData")
|
||||
@Expose
|
||||
private String securityData;
|
||||
|
||||
|
||||
private boolean settingsChanged;
|
||||
private boolean veraconfigured;
|
||||
private boolean harmonyconfigured;
|
||||
private boolean nestconfigured;
|
||||
private boolean farenheit;
|
||||
private String configfile;
|
||||
private Integer numberoflogmessages;
|
||||
private IpList hueaddress;
|
||||
private boolean hueconfigured;
|
||||
private boolean nestconfigured;
|
||||
private boolean halconfigured;
|
||||
private boolean mqttconfigured;
|
||||
private boolean hassconfigured;
|
||||
private boolean domoticzconfigured;
|
||||
private boolean somfyconfigured;
|
||||
private boolean lifxconfigured;
|
||||
|
||||
public BridgeSettingsDescriptor() {
|
||||
super();
|
||||
@@ -31,9 +107,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;
|
||||
@@ -68,27 +153,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;
|
||||
}
|
||||
@@ -116,9 +195,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;
|
||||
}
|
||||
@@ -167,6 +252,102 @@ public class BridgeSettingsDescriptor {
|
||||
public void setHueconfigured(boolean hueconfigured) {
|
||||
this.hueconfigured = hueconfigured;
|
||||
}
|
||||
public IpList getHaladdress() {
|
||||
return haladdress;
|
||||
}
|
||||
public void setHaladdress(IpList haladdress) {
|
||||
this.haladdress = haladdress;
|
||||
}
|
||||
public String getHaltoken() {
|
||||
return haltoken;
|
||||
}
|
||||
public void setHaltoken(String haltoken) {
|
||||
this.haltoken = haltoken;
|
||||
}
|
||||
public boolean isHalconfigured() {
|
||||
return halconfigured;
|
||||
}
|
||||
public void setHalconfigured(boolean halconfigured) {
|
||||
this.halconfigured = halconfigured;
|
||||
}
|
||||
public Map<String, WhitelistEntry> getWhitelist() {
|
||||
return whitelist;
|
||||
}
|
||||
protected void removeWhitelist() {
|
||||
whitelist = null;
|
||||
}
|
||||
public boolean isSettingsChanged() {
|
||||
return settingsChanged;
|
||||
}
|
||||
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 String getSecurityData() {
|
||||
return securityData;
|
||||
}
|
||||
public void setSecurityData(String securityData) {
|
||||
this.securityData = securityData;
|
||||
}
|
||||
public Boolean isValidVera() {
|
||||
if(this.getVeraAddress() == null || this.getVeraAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
@@ -181,10 +362,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() {
|
||||
@@ -202,4 +379,49 @@ public class BridgeSettingsDescriptor {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidHal() {
|
||||
if(this.getHaladdress() == null || this.getHaladdress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getHaladdress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
if(this.getHaltoken() == null || this.getHaltoken().equals(""))
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,9 +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.harmony.HarmonyHome;
|
||||
import com.bwssystems.hue.HueHome;
|
||||
import com.bwssystems.HABridge.util.UDPDatagramSender;
|
||||
|
||||
public class HABridge {
|
||||
|
||||
@@ -33,10 +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;
|
||||
HomeManager homeManager;
|
||||
HueMulator theHueMulator;
|
||||
UDPDatagramSender udpSender;
|
||||
UpnpSettingsResource theSettingResponder;
|
||||
UpnpListener theUpnpListener;
|
||||
SystemControl theSystem;
|
||||
@@ -48,49 +45,67 @@ public class HABridge {
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") starting....");
|
||||
|
||||
bridgeSettings = new BridgeSettings();
|
||||
// sparkjava config directive to set html static file location for Jetty
|
||||
staticFileLocation("/public");
|
||||
while(!bridgeSettings.getBridgeControl().isStop()) {
|
||||
bridgeSettings.buildSettings();
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") initializing....");
|
||||
bridgeSettings.getBridgeSecurity().removeTestUsers();
|
||||
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
|
||||
staticFileLocation("/public");
|
||||
if(!bridgeSettings.getBridgeControl().isReinit())
|
||||
init();
|
||||
bridgeSettings.getBridgeControl().setReinit(false);
|
||||
// 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 class to handle the resource setup rest api
|
||||
theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), harmonyHome, nestHome, hueHome);
|
||||
// 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);
|
||||
|
||||
bridgeSettings.getBridgeControl().setReinit(false);
|
||||
// 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, udpSender);
|
||||
// setup the class to handle the resource setup rest api
|
||||
theResources = new DeviceResource(bridgeSettings, 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;
|
||||
}
|
||||
stop();
|
||||
nestHome.closeTheNest();
|
||||
nestHome = null;
|
||||
harmonyHome.shutdownHarmonyHubs();
|
||||
harmonyHome = null;
|
||||
if(!bridgeSettings.getBridgeControl().isStop()) {
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
bridgeSettings.getBridgeSecurity().removeTestUsers();
|
||||
if(bridgeSettings.getBridgeSecurity().isSettingsChanged())
|
||||
bridgeSettings.updateConfigFile();
|
||||
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(BridgeSettings 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(BridgeSettings 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;
|
||||
}
|
||||
}
|
||||
26
src/main/java/com/bwssystems/HABridge/LinkButtonPressed.java
Normal file
26
src/main/java/com/bwssystems/HABridge/LinkButtonPressed.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LinkButtonPressed extends TimerTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(LinkButtonPressed.class);
|
||||
private BridgeControlDescriptor linkDescriptor;
|
||||
private Timer myTimer;
|
||||
|
||||
public LinkButtonPressed(BridgeControlDescriptor theDescriptor, Timer aTimer) {
|
||||
linkDescriptor = theDescriptor;
|
||||
myTimer = aTimer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
log.info("Link button time ended....");
|
||||
linkDescriptor.setLinkButton(false);
|
||||
myTimer.cancel();
|
||||
}
|
||||
|
||||
}
|
||||
22
src/main/java/com/bwssystems/HABridge/LoginResult.java
Normal file
22
src/main/java/com/bwssystems/HABridge/LoginResult.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class LoginResult {
|
||||
|
||||
private String error;
|
||||
|
||||
private User user;
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
26
src/main/java/com/bwssystems/HABridge/SecurityInfo.java
Normal file
26
src/main/java/com/bwssystems/HABridge/SecurityInfo.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class SecurityInfo {
|
||||
private boolean useLinkButton;
|
||||
private boolean secureHueApi;
|
||||
private boolean isSecure;
|
||||
|
||||
public boolean isUseLinkButton() {
|
||||
return useLinkButton;
|
||||
}
|
||||
public void setUseLinkButton(boolean useLinkButton) {
|
||||
this.useLinkButton = useLinkButton;
|
||||
}
|
||||
public boolean isSecureHueApi() {
|
||||
return secureHueApi;
|
||||
}
|
||||
public void setSecureHueApi(boolean secureHueApi) {
|
||||
this.secureHueApi = secureHueApi;
|
||||
}
|
||||
public boolean isSecure() {
|
||||
return isSecure;
|
||||
}
|
||||
public void setSecure(boolean isSecure) {
|
||||
this.isSecure = isSecure;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import static spark.Spark.get;
|
||||
import static spark.Spark.options;
|
||||
import static spark.Spark.post;
|
||||
import static spark.Spark.put;
|
||||
import static spark.Spark.before;
|
||||
import static spark.Spark.halt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
@@ -12,17 +14,19 @@ import java.net.MulticastSocket;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Timer;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
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;
|
||||
@@ -55,17 +59,28 @@ public class SystemControl {
|
||||
// This function sets up the sparkjava rest calls for the hue api
|
||||
public void setupServer() {
|
||||
log.info("System control service started....");
|
||||
before(SYSTEM_CONTEXT + "/*", (request, response) -> {
|
||||
if(bridgeSettings.getBridgeSecurity().isSecure()) {
|
||||
String pathInfo = request.pathInfo();
|
||||
if(pathInfo == null || (!pathInfo.equals(SYSTEM_CONTEXT + "/login") && !pathInfo.equals(SYSTEM_CONTEXT + "/habridge/version"))) {
|
||||
User authUser = bridgeSettings.getBridgeSecurity().getAuthenticatedUser(request);
|
||||
if(authUser == null) {
|
||||
halt(401, "{\"message\":\"User not authenticated\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// http://ip_address:port/system/habridge/version gets the version of this bridge instance
|
||||
get (SYSTEM_CONTEXT + "/habridge/version", "application/json", (request, response) -> {
|
||||
get (SYSTEM_CONTEXT + "/habridge/version", (request, response) -> {
|
||||
log.debug("Get HA Bridge version: v" + version.getVersion());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return "{\"version\":\"" + version.getVersion() + "\"}";
|
||||
response.type("application/json");
|
||||
return "{\"version\":\"" + version.getVersion() + "\",\"isSecure\":" + bridgeSettings.getBridgeSecurity().isSecure() + "}";
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/logmsgs gets the log messages for the bridge
|
||||
get (SYSTEM_CONTEXT + "/logmsgs", "application/json", (request, response) -> {
|
||||
get (SYSTEM_CONTEXT + "/logmsgs", (request, response) -> {
|
||||
log.debug("Get logmsgs.");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
String logMsgs;
|
||||
int count = -1;
|
||||
if(cyclicBufferAppender == null)
|
||||
@@ -86,24 +101,190 @@ public class SystemControl {
|
||||
}
|
||||
}
|
||||
logMsgs = logMsgs + "]";
|
||||
response.status(200);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return logMsgs;
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/logmgmt/loggers gets the logger info for the bridge
|
||||
get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", "application/json", (request, response) -> {
|
||||
get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", (request, response) -> {
|
||||
log.debug("Get loggers info with showAll argument: " + request.params(":all"));
|
||||
Boolean showAll = false;
|
||||
if(request.params(":all").equals("true"))
|
||||
showAll = true;
|
||||
theLogServiceMgr.setShowAll(showAll);
|
||||
theLogServiceMgr.init();
|
||||
response.status(200);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return theLogServiceMgr.getConfiguredLoggers();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/setpassword CORS request
|
||||
options(SYSTEM_CONTEXT + "/setpassword", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/setpassword which sets a password for a given user
|
||||
post(SYSTEM_CONTEXT + "/setpassword", (request, response) -> {
|
||||
log.debug("setpassword....");
|
||||
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
|
||||
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
|
||||
String errorMessage = bridgeSettings.getBridgeSecurity().setPassword(theUser);
|
||||
if(errorMessage != null) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
|
||||
} else {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
}
|
||||
|
||||
if(errorMessage == null)
|
||||
errorMessage = "{}";
|
||||
response.type("application/json");
|
||||
return errorMessage;
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/adduser CORS request
|
||||
options(SYSTEM_CONTEXT + "/adduser", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/adduser which adds a new user
|
||||
put(SYSTEM_CONTEXT + "/adduser", (request, response) -> {
|
||||
log.debug("adduser....");
|
||||
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
|
||||
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
|
||||
String errorMessage = theUser.validate();
|
||||
if(errorMessage != null) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
|
||||
} else {
|
||||
errorMessage = bridgeSettings.getBridgeSecurity().addUser(theUser);
|
||||
if(errorMessage == null) {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
} else {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
|
||||
}
|
||||
}
|
||||
|
||||
if(errorMessage == null)
|
||||
errorMessage = "{}";
|
||||
response.type("application/json");
|
||||
return errorMessage;
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/deluser CORS request
|
||||
options(SYSTEM_CONTEXT + "/deluser", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/deluser which dels a user
|
||||
put(SYSTEM_CONTEXT + "/deluser", (request, response) -> {
|
||||
log.debug("deluser....");
|
||||
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
|
||||
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
|
||||
String errorMessage = bridgeSettings.getBridgeSecurity().delUser(theUser);
|
||||
if(errorMessage != null) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
|
||||
} else {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
}
|
||||
|
||||
if(errorMessage == null)
|
||||
errorMessage = "{}";
|
||||
response.type("application/json");
|
||||
return errorMessage;
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/login CORS request
|
||||
options(SYSTEM_CONTEXT + "/login", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/login validates the login
|
||||
post(SYSTEM_CONTEXT + "/login", (request, response) -> {
|
||||
log.debug("login....");
|
||||
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
|
||||
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
|
||||
LoginResult result = bridgeSettings.getBridgeSecurity().validatePassword(theUser);
|
||||
if(result.getUser() != null)
|
||||
bridgeSettings.getBridgeSecurity().addAuthenticatedUser(request, theUser);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return result;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/presslinkbutton CORS request
|
||||
options(SYSTEM_CONTEXT + "/presslinkbutton", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/presslinkbutton which sets the link button for device registration
|
||||
put(SYSTEM_CONTEXT + "/presslinkbutton", (request, response) -> {
|
||||
log.info("Link button pressed....");
|
||||
bridgeSettings.getBridgeControl().setLinkButton(true);
|
||||
Timer theTimer = new Timer();
|
||||
theTimer.schedule(new LinkButtonPressed(bridgeSettings.getBridgeControl(), theTimer), 30000);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return "";
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/securityinfo gets the security info for the bridge
|
||||
get (SYSTEM_CONTEXT + "/securityinfo", (request, response) -> {
|
||||
log.debug("Get security info");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSecurity().getSecurityInfo();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/changesecurityinfo CORS request
|
||||
options(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/changesecurityinfo which sets the security settings other than passwords and users
|
||||
post(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
|
||||
log.debug("changesecurityinfo....");
|
||||
SecurityInfo theInfo = new Gson().fromJson(request.body(), SecurityInfo.class);
|
||||
bridgeSettings.getBridgeSecurity().setUseLinkButton(theInfo.isUseLinkButton());
|
||||
bridgeSettings.getBridgeSecurity().setSecureHueApi(theInfo.isSecureHueApi());
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSecurity().getSecurityInfo();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/logmgmt/update CORS request
|
||||
options(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/logmgmt/update", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
@@ -112,28 +293,28 @@ public class SystemControl {
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/logmgmt/update which changes logging parameters for the process
|
||||
put(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
|
||||
put(SYSTEM_CONTEXT + "/logmgmt/update", (request, response) -> {
|
||||
log.debug("update loggers: " + request.body());
|
||||
response.status(200);
|
||||
LoggerInfo updateLoggers[];
|
||||
updateLoggers = new Gson().fromJson(request.body(), LoggerInfo[].class);
|
||||
LoggingForm theModel = theLogServiceMgr.getModel();
|
||||
theModel.setUpdatedLoggers(Arrays.asList(updateLoggers));
|
||||
theLogServiceMgr.updateLogLevels();
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return theLogServiceMgr.getConfiguredLoggers();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/settings which returns the bridge configuration settings
|
||||
get(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
get(SYSTEM_CONTEXT + "/settings", (request, response) -> {
|
||||
log.debug("bridge settings requested from " + request.ip());
|
||||
|
||||
response.status(200);
|
||||
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/settings CORS request
|
||||
options(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/settings", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
@@ -142,17 +323,17 @@ public class SystemControl {
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/settings which returns the bridge configuration settings
|
||||
put(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
put(SYSTEM_CONTEXT + "/settings", (request, response) -> {
|
||||
log.debug("save bridge settings requested from " + request.ip() + " with body: " + request.body());
|
||||
BridgeSettingsDescriptor newBridgeSettings = new Gson().fromJson(request.body(), BridgeSettingsDescriptor.class);
|
||||
bridgeSettings.save(newBridgeSettings);
|
||||
response.status(200);
|
||||
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/control/reinit CORS request
|
||||
options(SYSTEM_CONTEXT + "/control/reinit", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/control/reinit", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
@@ -161,12 +342,14 @@ public class SystemControl {
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/control/reinit sets the parameter reinit the server
|
||||
put(SYSTEM_CONTEXT + "/control/reinit", "application/json", (request, response) -> {
|
||||
put(SYSTEM_CONTEXT + "/control/reinit", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return reinit();
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/control/stop CORS request
|
||||
options(SYSTEM_CONTEXT + "/control/stop", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/control/stop", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
@@ -175,19 +358,22 @@ public class SystemControl {
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/control/stop sets the parameter stop the server
|
||||
put(SYSTEM_CONTEXT + "/control/stop", "application/json", (request, response) -> {
|
||||
put(SYSTEM_CONTEXT + "/control/stop", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return stop();
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/backup/available returns a list of config backup filenames
|
||||
get (SYSTEM_CONTEXT + "/backup/available", "application/json", (request, response) -> {
|
||||
get (SYSTEM_CONTEXT + "/backup/available", (request, response) -> {
|
||||
log.debug("Get backup filenames");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBackups();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/create CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/backup/create", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "PUT");
|
||||
@@ -195,16 +381,18 @@ public class SystemControl {
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
put (SYSTEM_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
put (SYSTEM_CONTEXT + "/backup/create", (request, response) -> {
|
||||
log.debug("Create backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
BackupFilename returnFilename = new BackupFilename();
|
||||
returnFilename.setFilename(bridgeSettings.backup(aFilename.getFilename()));
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return returnFilename;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/delete CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/backup/delete", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
@@ -212,18 +400,20 @@ public class SystemControl {
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (SYSTEM_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
post (SYSTEM_CONTEXT + "/backup/delete", (request, response) -> {
|
||||
log.debug("Delete backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null)
|
||||
bridgeSettings.deleteBackup(aFilename.getFilename());
|
||||
else
|
||||
log.warn("No filename given for delete backup.");
|
||||
return null;
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return "";
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/restore CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/backup/restore", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
@@ -231,7 +421,7 @@ public class SystemControl {
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (SYSTEM_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
post (SYSTEM_CONTEXT + "/backup/restore", (request, response) -> {
|
||||
log.debug("Restore backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null) {
|
||||
@@ -240,6 +430,8 @@ public class SystemControl {
|
||||
}
|
||||
else
|
||||
log.warn("No filename given for restore backup.");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
}
|
||||
|
||||
65
src/main/java/com/bwssystems/HABridge/User.java
Normal file
65
src/main/java/com/bwssystems/HABridge/User.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import spark.utils.StringUtils;
|
||||
|
||||
public class User {
|
||||
private int id;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String password2;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
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 String getPassword2() {
|
||||
return password2;
|
||||
}
|
||||
|
||||
public void setPassword2(String password2) {
|
||||
this.password2 = password2;
|
||||
}
|
||||
|
||||
public String validate() {
|
||||
String error = null;
|
||||
|
||||
if(StringUtils.isEmpty(username)) {
|
||||
error = "You have to enter a username";
|
||||
} else if(StringUtils.isEmpty(password)) {
|
||||
error = "You have to enter a password";
|
||||
} else if(!password.equals(password2)) {
|
||||
error = "The two passwords do not match";
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
public boolean validatePassword() {
|
||||
if(password != null && password2 != null)
|
||||
return password.equals(password2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@ public class DeviceResponse {
|
||||
private String name;
|
||||
private String modelid;
|
||||
private String manufacturername;
|
||||
private String luminaireuniqueid;
|
||||
private String uniqueid;
|
||||
private String swversion;
|
||||
|
||||
@@ -70,16 +71,25 @@ public class DeviceResponse {
|
||||
this.swversion = swversion;
|
||||
}
|
||||
|
||||
public static DeviceResponse createResponse(DeviceDescriptor device){
|
||||
public String getLuminaireuniqueid() {
|
||||
return luminaireuniqueid;
|
||||
}
|
||||
|
||||
public void setLuminaireuniqueid(String luminaireuniqueid) {
|
||||
this.luminaireuniqueid = luminaireuniqueid;
|
||||
}
|
||||
|
||||
public static DeviceResponse createResponse(DeviceDescriptor device){
|
||||
DeviceResponse response = new 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");
|
||||
response.setSwversion("66012040");
|
||||
response.setLuminaireuniqueid(null);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
public class DeviceTypes {
|
||||
private Boolean bridge;
|
||||
private String[] lights;
|
||||
public Boolean getBridge() {
|
||||
return bridge;
|
||||
}
|
||||
public void setBridge(Boolean bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
public String[] getLights() {
|
||||
return lights;
|
||||
}
|
||||
public void setLights(String[] lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
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;
|
||||
}
|
||||
public void setAction(DeviceState action) {
|
||||
this.action = action;
|
||||
}
|
||||
public String[] getLights() {
|
||||
return lights;
|
||||
}
|
||||
public void setLights(String[] lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
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 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(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, String devicetype, String userid) {
|
||||
public HueApiResponse(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist, String emulateHubVersion) {
|
||||
super();
|
||||
this.setConfig(HueConfig.createConfig(name, ipaddress, devicetype, userid));
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@@ -29,19 +28,23 @@ public class HueConfig
|
||||
private String localtime;
|
||||
private String timezone;
|
||||
private String zigbeechannel;
|
||||
private String modelid;
|
||||
private String bridgeid;
|
||||
private Boolean factorynew;
|
||||
private String replacesbridgeid;
|
||||
private Map<String, WhitelistEntry> whitelist;
|
||||
|
||||
public static HueConfig createConfig(String name, String ipaddress, String devicetype, String userid) {
|
||||
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.4.0");
|
||||
aConfig.setApiversion(HueConstants.API_VERSION);
|
||||
aConfig.setPortalservices(false);
|
||||
aConfig.setGateway(ipaddress);
|
||||
aConfig.setSwversion("01005215");
|
||||
aConfig.setLinkbutton(false);
|
||||
aConfig.setSwversion(emulateHubVersion);
|
||||
aConfig.setLinkbutton(true);
|
||||
aConfig.setIpaddress(ipaddress);
|
||||
aConfig.setProxyport(0);
|
||||
aConfig.setSwupdate(Swupdate.createSwupdate());
|
||||
@@ -53,8 +56,10 @@ public class HueConfig
|
||||
aConfig.setLocaltime(dateFormat.format(new Date()));
|
||||
aConfig.setTimezone(TimeZone.getDefault().getID());
|
||||
aConfig.setZigbeechannel("6");
|
||||
Map<String, WhitelistEntry> awhitelist = new HashMap<>();
|
||||
awhitelist.put(userid, WhitelistEntry.createEntry(devicetype));
|
||||
aConfig.setBridgeid(HuePublicConfig.createConfig(name, ipaddress, emulateHubVersion).getHueBridgeIdFromMac());
|
||||
aConfig.setModelid(HueConstants.MODEL_ID);
|
||||
aConfig.setFactorynew(false);
|
||||
aConfig.setReplacesbridgeid(null);
|
||||
aConfig.setWhitelist(awhitelist);
|
||||
|
||||
return aConfig;
|
||||
@@ -235,4 +240,36 @@ public class HueConfig
|
||||
public void setZigbeechannel(String zigbeechannel) {
|
||||
this.zigbeechannel = zigbeechannel;
|
||||
}
|
||||
|
||||
public String getModelid() {
|
||||
return modelid;
|
||||
}
|
||||
|
||||
public void setModelid(String modelid) {
|
||||
this.modelid = modelid;
|
||||
}
|
||||
|
||||
public String getBridgeid() {
|
||||
return bridgeid;
|
||||
}
|
||||
|
||||
public void setBridgeid(String bridgeid) {
|
||||
this.bridgeid = bridgeid;
|
||||
}
|
||||
|
||||
public Boolean getFactorynew() {
|
||||
return factorynew;
|
||||
}
|
||||
|
||||
public void setFactorynew(Boolean factorynew) {
|
||||
this.factorynew = factorynew;
|
||||
}
|
||||
|
||||
public String getReplacesbridgeid() {
|
||||
return replacesbridgeid;
|
||||
}
|
||||
|
||||
public void setReplacesbridgeid(String replacesbridgeid) {
|
||||
this.replacesbridgeid = replacesbridgeid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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-";
|
||||
}
|
||||
19
src/main/java/com/bwssystems/HABridge/api/hue/HueError.java
Normal file
19
src/main/java/com/bwssystems/HABridge/api/hue/HueError.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
public class HueError {
|
||||
|
||||
private HueErrorDetails error;
|
||||
|
||||
public HueError(HueErrorDetails error) {
|
||||
super();
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public HueErrorDetails getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(HueErrorDetails error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
public class HueErrorDetails {
|
||||
private String type;
|
||||
private String address;
|
||||
private String description;
|
||||
private String method_name;
|
||||
private String resource_name;
|
||||
private String value;
|
||||
public HueErrorDetails(String type, String address, String description, String method_name, String resource_name,
|
||||
String value) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.address = address;
|
||||
this.description = description;
|
||||
this.method_name = method_name;
|
||||
this.resource_name = resource_name;
|
||||
this.value = value;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
public String getMethod_name() {
|
||||
return method_name;
|
||||
}
|
||||
public void setMethod_name(String method_name) {
|
||||
this.method_name = method_name;
|
||||
}
|
||||
public String getResource_name() {
|
||||
return resource_name;
|
||||
}
|
||||
public void setResource_name(String resource_name) {
|
||||
this.resource_name = resource_name;
|
||||
}
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
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>();
|
||||
}
|
||||
|
||||
public void addError(HueError anError) {
|
||||
theErrors.add(anError);
|
||||
}
|
||||
|
||||
public HueError[] getTheErrors() {
|
||||
HueError theList[] = new HueError[theErrors.size()];
|
||||
theList = theErrors.toArray(theList);
|
||||
return theList;
|
||||
}
|
||||
|
||||
public void setTheErrors(ArrayList<HueError> theErrors) {
|
||||
this.theErrors = theErrors;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
|
||||
public class HuePublicConfig
|
||||
{
|
||||
private String name;
|
||||
private String apiversion;
|
||||
private String swversion;
|
||||
private String mac;
|
||||
private String bridgeid;
|
||||
private String replacesbridgeid;
|
||||
private Boolean factorynew;
|
||||
private String modelid;
|
||||
|
||||
public static HuePublicConfig createConfig(String name, String ipaddress, String emulateHubVersion) {
|
||||
HuePublicConfig aConfig = new HuePublicConfig();
|
||||
aConfig.setMac(HuePublicConfig.getMacAddress(ipaddress));
|
||||
aConfig.setApiversion(HueConstants.API_VERSION);
|
||||
aConfig.setSwversion(emulateHubVersion);
|
||||
aConfig.setName(name);
|
||||
aConfig.setBridgeid(aConfig.getHueBridgeIdFromMac());
|
||||
aConfig.setModelid(HueConstants.MODEL_ID);
|
||||
aConfig.setFactorynew(false);
|
||||
aConfig.setReplacesbridgeid(null);
|
||||
|
||||
return aConfig;
|
||||
}
|
||||
|
||||
private static String getMacAddress(String addr)
|
||||
{
|
||||
InetAddress ip;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
|
||||
ip = InetAddress.getByName(addr);
|
||||
|
||||
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
|
||||
|
||||
byte[] mac = network.getHardwareAddress();
|
||||
|
||||
for (int i = 0; i < mac.length; i++) {
|
||||
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? ":" : ""));
|
||||
}
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
} catch (SocketException e){
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
} catch (Exception e){
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getSNUUIDFromMac()
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(this.getMac(), ":");
|
||||
String bridgeUUID = "";
|
||||
while(st.hasMoreTokens()) {
|
||||
bridgeUUID = bridgeUUID + st.nextToken();
|
||||
}
|
||||
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() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
public void setMac(String mac) {
|
||||
this.mac = mac;
|
||||
}
|
||||
|
||||
public String getSwversion() {
|
||||
return swversion;
|
||||
}
|
||||
|
||||
public void setSwversion(String swversion) {
|
||||
this.swversion = swversion;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public String getApiversion() {
|
||||
return apiversion;
|
||||
}
|
||||
|
||||
public void setApiversion(String apiversion) {
|
||||
this.apiversion = apiversion;
|
||||
}
|
||||
|
||||
|
||||
public String getModelid() {
|
||||
return modelid;
|
||||
}
|
||||
|
||||
public void setModelid(String modelid) {
|
||||
this.modelid = modelid;
|
||||
}
|
||||
|
||||
public String getBridgeid() {
|
||||
return bridgeid;
|
||||
}
|
||||
|
||||
public void setBridgeid(String bridgeid) {
|
||||
this.bridgeid = bridgeid;
|
||||
}
|
||||
|
||||
public Boolean getFactorynew() {
|
||||
return factorynew;
|
||||
}
|
||||
|
||||
public void setFactorynew(Boolean factorynew) {
|
||||
this.factorynew = factorynew;
|
||||
}
|
||||
|
||||
public String getReplacesbridgeid() {
|
||||
return replacesbridgeid;
|
||||
}
|
||||
|
||||
public void setReplacesbridgeid(String replacesbridgeid) {
|
||||
this.replacesbridgeid = replacesbridgeid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
// import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by arm on 4/14/15.
|
||||
*/
|
||||
public class StateChangeBody {
|
||||
private boolean on;
|
||||
private int bri;
|
||||
private int hue;
|
||||
private int sat;
|
||||
private String effect;
|
||||
private int ct;
|
||||
private String alert;
|
||||
private List<Double> xy;
|
||||
private int transitiontime;
|
||||
private int bri_inc;
|
||||
private int hue_inc;
|
||||
private int sat_inc;
|
||||
private List<Double> xy_inc;
|
||||
private int ct_inc;
|
||||
|
||||
public boolean isOn() {
|
||||
return on;
|
||||
}
|
||||
|
||||
public void setOn(boolean on) {
|
||||
this.on = on;
|
||||
}
|
||||
|
||||
public int getBri() {
|
||||
return bri;
|
||||
}
|
||||
|
||||
public void setBri(int bri) {
|
||||
this.bri = bri;
|
||||
}
|
||||
|
||||
public int getHue() {
|
||||
return hue;
|
||||
}
|
||||
|
||||
public void setHue(int hue) {
|
||||
this.hue = hue;
|
||||
}
|
||||
|
||||
public int getSat() {
|
||||
return sat;
|
||||
}
|
||||
|
||||
public void setSat(int sat) {
|
||||
this.sat = sat;
|
||||
}
|
||||
|
||||
public String getEffect() {
|
||||
return effect;
|
||||
}
|
||||
|
||||
public void setEffect(String effect) {
|
||||
this.effect = effect;
|
||||
}
|
||||
|
||||
public int getCt() {
|
||||
return ct;
|
||||
}
|
||||
|
||||
public void setCt(int ct) {
|
||||
this.ct = ct;
|
||||
}
|
||||
|
||||
public String getAlert() {
|
||||
return alert;
|
||||
}
|
||||
|
||||
public void setAlert(String alert) {
|
||||
this.alert = alert;
|
||||
}
|
||||
|
||||
public List<Double> getXy() {
|
||||
return xy;
|
||||
}
|
||||
|
||||
public void setXy(List<Double> xy) {
|
||||
this.xy = xy;
|
||||
}
|
||||
|
||||
public int getTransitiontime() {
|
||||
return transitiontime;
|
||||
}
|
||||
|
||||
public void setTransitiontime(int transitiontime) {
|
||||
this.transitiontime = transitiontime;
|
||||
}
|
||||
|
||||
public int getBri_inc() {
|
||||
return bri_inc;
|
||||
}
|
||||
|
||||
public void setBri_inc(int bri_inc) {
|
||||
this.bri_inc = bri_inc;
|
||||
}
|
||||
|
||||
public int getHue_inc() {
|
||||
return hue_inc;
|
||||
}
|
||||
|
||||
public void setHue_inc(int hue_inc) {
|
||||
this.hue_inc = hue_inc;
|
||||
}
|
||||
|
||||
public int getSat_inc() {
|
||||
return sat_inc;
|
||||
}
|
||||
|
||||
public void setSat_inc(int sat_inc) {
|
||||
this.sat_inc = sat_inc;
|
||||
}
|
||||
|
||||
public List<Double> getXy_inc() {
|
||||
return xy_inc;
|
||||
}
|
||||
|
||||
public void setXy_inc(List<Double> xy_inc) {
|
||||
this.xy_inc = xy_inc;
|
||||
}
|
||||
|
||||
public int getCt_inc() {
|
||||
return ct_inc;
|
||||
}
|
||||
|
||||
public void setCt_inc(int ct_inc) {
|
||||
this.ct_inc = ct_inc;
|
||||
}
|
||||
}
|
||||
@@ -3,20 +3,36 @@ package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
public class Swupdate
|
||||
{
|
||||
private Integer updatestate;
|
||||
private Boolean checkforupdate;
|
||||
private DeviceTypes devicetypes;
|
||||
private String text;
|
||||
private Boolean notify;
|
||||
private Integer updatestate;
|
||||
private String url;
|
||||
|
||||
public static Swupdate createSwupdate() {
|
||||
Swupdate aSwupdate = new Swupdate();
|
||||
aSwupdate.setUpdatestate(0);
|
||||
aSwupdate.setCheckforupdate(false);
|
||||
aSwupdate.setDevicetypes(new DeviceTypes());
|
||||
aSwupdate.setNotify(false);
|
||||
aSwupdate.setText("");
|
||||
aSwupdate.setUpdatestate(0);
|
||||
aSwupdate.setUrl("");
|
||||
return aSwupdate;
|
||||
}
|
||||
|
||||
public Boolean getCheckforupdate() {
|
||||
return checkforupdate;
|
||||
}
|
||||
public void setCheckforupdate(Boolean checkforupdate) {
|
||||
this.checkforupdate = checkforupdate;
|
||||
}
|
||||
public DeviceTypes getDevicetypes() {
|
||||
return devicetypes;
|
||||
}
|
||||
public void setDevicetypes(DeviceTypes devicetypes) {
|
||||
this.devicetypes = devicetypes;
|
||||
}
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.bwssystems.HABridge.devicemanagmeent;
|
||||
|
||||
import static spark.Spark.get;
|
||||
import static spark.Spark.halt;
|
||||
import static spark.Spark.options;
|
||||
import static spark.Spark.post;
|
||||
import static spark.Spark.put;
|
||||
import static spark.Spark.before;
|
||||
import static spark.Spark.delete;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -15,19 +17,19 @@ import org.apache.http.HttpStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.HomeManager;
|
||||
import com.bwssystems.HABridge.User;
|
||||
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.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
|
||||
@@ -36,35 +38,17 @@ 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 HomeManager homeManager;
|
||||
private BridgeSettings bridgeSettings;
|
||||
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) {
|
||||
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;
|
||||
setupEndpoints();
|
||||
public DeviceResource(BridgeSettings theSettings, HomeManager aHomeManager) {
|
||||
bridgeSettings = theSettings;
|
||||
this.deviceRepository = new DeviceRepository(bridgeSettings.getBridgeSettingsDescriptor().getUpnpDeviceDb());
|
||||
homeManager = aHomeManager;
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
setupEndpoints();
|
||||
}
|
||||
|
||||
public DeviceRepository getDeviceRepository() {
|
||||
@@ -73,6 +57,15 @@ public class DeviceResource {
|
||||
|
||||
private void setupEndpoints() {
|
||||
log.info("HABridge device management service started.... ");
|
||||
before(API_CONTEXT + "/*", (request, response) -> {
|
||||
// This never gets called as the HueMulator class covers this path. This is here for backup
|
||||
if(bridgeSettings.getBridgeSecurity().isSecure()) {
|
||||
User authUser = bridgeSettings.getBridgeSecurity().getAuthenticatedUser(request);
|
||||
if(authUser == null) {
|
||||
halt(401, "{\"message\":\"User not authenticated\"}");
|
||||
}
|
||||
}
|
||||
});
|
||||
// http://ip_address:port/api/devices CORS request
|
||||
options(API_CONTEXT, "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
@@ -91,14 +84,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);
|
||||
@@ -182,84 +205,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");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
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);
|
||||
}
|
||||
157
src/main/java/com/bwssystems/HABridge/hue/BrightnessDecode.java
Normal file
157
src/main/java/com/bwssystems/HABridge/hue/BrightnessDecode.java
Normal file
@@ -0,0 +1,157 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
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(Locale.ROOT, "%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.BridgeSettings;
|
||||
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(BridgeSettings 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(BridgeSettings bridgeSettings) {
|
||||
theSession = null;
|
||||
theNest = null;
|
||||
nestItems = null;
|
||||
validNest = bridgeSettings.getBridgeSettingsDescriptor().isValidNest();
|
||||
aGsonHandler = null;
|
||||
log.info("Nest Home created." + (validNest ? "" : " No Nest configured."));
|
||||
|
||||
if(validNest) {
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
|
||||
isFarenheit = bridgeSettings.getBridgeSettingsDescriptor().isFarenheit();
|
||||
try {
|
||||
theSession = new NestSession(bridgeSettings.getBridgeSettingsDescriptor().getNestuser(), bridgeSettings.getBridgeSettingsDescriptor().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.BridgeSettings;
|
||||
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(BridgeSettings 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(BridgeSettings bridgeSettings) {
|
||||
validDomoticz = bridgeSettings.getBridgeSettingsDescriptor().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.getBridgeSettingsDescriptor().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,95 @@
|
||||
package com.bwssystems.HABridge.plugins.exec;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
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);
|
||||
private BridgeSettings theSettings;
|
||||
|
||||
public CommandHome(BridgeSettings 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() + " and exec Garden: " + (theSettings.getBridgeSecurity().getExecGarden() == null ? "not given" : theSettings.getBridgeSecurity().getExecGarden()));
|
||||
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 execGarden = theSettings.getBridgeSecurity().getExecGarden();
|
||||
if(execGarden != null && !execGarden.trim().isEmpty()) {
|
||||
if(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
|
||||
intermediate = execGarden + "\\" + intermediate;
|
||||
else
|
||||
intermediate = execGarden + "/" + 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 + " with message: " + e.getMessage());
|
||||
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(BridgeSettings bridgeSettings) {
|
||||
log.info("Command Home for system program execution created.");
|
||||
this.theSettings = bridgeSettings;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
// noop
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// noop
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class DeviceElements {
|
||||
@SerializedName(value="DeviceElements", alternate={"SceneElements", "GroupElements", "HVACElements", "MacroElements", "IrElements", "IrButtons"})
|
||||
private List<DeviceName> DeviceElements;
|
||||
|
||||
public List<DeviceName> getDeviceElements() {
|
||||
return DeviceElements;
|
||||
}
|
||||
|
||||
public void setDeviceElements(List<DeviceName> deviceElements) {
|
||||
DeviceElements = deviceElements;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class DeviceName {
|
||||
@SerializedName(value="DeviceName", alternate={"SceneName", "GroupName", "HVACName", "MacroName", "IrName", "IrButton"})
|
||||
private String DeviceName;
|
||||
|
||||
public String getDeviceName() {
|
||||
return DeviceName;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
DeviceName = deviceName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
public class HalDevice {
|
||||
private String haldevicetype;
|
||||
private String haldevicename;
|
||||
private String haladdress;
|
||||
private String halname;
|
||||
private Boolean secure;
|
||||
private DeviceElements buttons;
|
||||
public String getHaldevicetype() {
|
||||
return haldevicetype;
|
||||
}
|
||||
public void setHaldevicetype(String haldevicetype) {
|
||||
this.haldevicetype = haldevicetype;
|
||||
}
|
||||
public String getHaldevicename() {
|
||||
return haldevicename;
|
||||
}
|
||||
public void setHaldevicename(String haldevicename) {
|
||||
this.haldevicename = haldevicename;
|
||||
}
|
||||
public String getHaladdress() {
|
||||
return haladdress;
|
||||
}
|
||||
public void setHaladdress(String haladdress) {
|
||||
this.haladdress = haladdress;
|
||||
}
|
||||
public String getHalname() {
|
||||
return halname;
|
||||
}
|
||||
public void setHalname(String halname) {
|
||||
this.halname = halname;
|
||||
}
|
||||
public Boolean getSecure() {
|
||||
return secure;
|
||||
}
|
||||
public void setSecure(Boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
public DeviceElements getButtons() {
|
||||
return buttons;
|
||||
}
|
||||
public void setButtons(DeviceElements buttons) {
|
||||
this.buttons = buttons;
|
||||
}
|
||||
}
|
||||
177
src/main/java/com/bwssystems/HABridge/plugins/hal/HalHome.java
Normal file
177
src/main/java/com/bwssystems/HABridge/plugins/hal/HalHome.java
Normal file
@@ -0,0 +1,177 @@
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
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.BridgeSettings;
|
||||
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.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class HalHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(HalHome.class);
|
||||
private Map<String, HalInfo> hals;
|
||||
private Boolean validHal;
|
||||
|
||||
public HalHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
if(!validHal)
|
||||
return null;
|
||||
log.debug("consolidating devices for HALs");
|
||||
List<HalDevice> theResponse = null;
|
||||
Iterator<String> keys = hals.keySet().iterator();
|
||||
List<HalDevice> deviceList = new ArrayList<HalDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = hals.get(key).getLights();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else {
|
||||
log.warn("Cannot get lights for Hal with name: " + key + ", skipping this HAL.");
|
||||
continue;
|
||||
}
|
||||
theResponse = hals.get(key).getAppliances();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get appliances for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getTheatre();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get theatre for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getCustom();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get custom for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getHVAC();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get HVAC for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getHome(key);
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get Homes for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getGroups();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get Groups for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getMacros();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get Macros for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getScenes();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get Scenes for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getButtons();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get Buttons for Hal with name: " + key);
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
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();
|
||||
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) {
|
||||
String responseString = null;
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && theUrl.contains("http://")) {
|
||||
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;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
// port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
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);
|
||||
|
||||
for (Map.Entry<String, HalInfo> entry : hals.entrySet())
|
||||
{
|
||||
if(entry.getValue().getHalAddress().getIp().equals(hostAddr)) {
|
||||
if (entry.getValue().deviceCommand(anUrl) == null) {
|
||||
log.warn("Error on calling hal to change device state: " + anUrl);
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
validHal = bridgeSettings.getBridgeSettingsDescriptor().isValidHal();
|
||||
log.info("HAL Home created." + (validHal ? "" : " No HAL devices configured."));
|
||||
if(!validHal)
|
||||
return null;
|
||||
hals = new HashMap<String, HalInfo>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHaladdress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHal = theList.next();
|
||||
try {
|
||||
hals.put(aHal.getName(), new HalInfo(aHal, bridgeSettings.getBridgeSettingsDescriptor().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
|
||||
|
||||
}
|
||||
}
|
||||
210
src/main/java/com/bwssystems/HABridge/plugins/hal/HalInfo.java
Normal file
210
src/main/java/com/bwssystems/HABridge/plugins/hal/HalInfo.java
Normal file
@@ -0,0 +1,210 @@
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.util.TextStringFormatter;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class HalInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(HalInfo.class);
|
||||
private static final String DEVICE_REQUEST = "/DeviceData!DeviceCmd=GetNames!DeviceType=";
|
||||
private static final String HVAC_REQUEST = "/HVACData!HVACCmd=GetNames";
|
||||
private static final String GROUP_REQUEST = "/GroupData!GroupCmd=GetNames";
|
||||
private static final String MACRO_REQUEST = "/MacroData!MacroCmd=GetNames";
|
||||
private static final String SCENE_REQUEST = "/SceneData!SceneCmd=GetNames";
|
||||
private static final String IRDATA_REQUEST = "/IrData!IRCmd=GetNames";
|
||||
private static final String IRBUTTON_REQUEST = "/IrData!IRCmd=GetButtons!IrDevice=";
|
||||
private static final String TOKEN_REQUEST = "?Token=";
|
||||
private static final String LIGHT_REQUEST = "Light";
|
||||
private static final String APPL_REQUEST = "Appl";
|
||||
// private static final String VIDEO_REQUEST = "Video";
|
||||
private static final String THEATRE_REQUEST = "Theatre";
|
||||
private static final String CUSTOM_REQUEST = "Custom";
|
||||
private static final String HVAC_TYPE = "HVAC";
|
||||
private static final String HOME_TYPE = "Home";
|
||||
private static final String GROUP_TYPE = "Group";
|
||||
private static final String MACRO_TYPE = "Macro";
|
||||
private static final String SCENE_TYPE = "Scene";
|
||||
private static final String IRDATA_TYPE = "IrData";
|
||||
private HTTPHandler httpClient;
|
||||
private NamedIP halAddress;
|
||||
private String theToken;
|
||||
|
||||
public HalInfo(NamedIP addressName, String aGivenToken) {
|
||||
super();
|
||||
httpClient = new HTTPHandler();
|
||||
halAddress = addressName;
|
||||
theToken = aGivenToken;
|
||||
}
|
||||
|
||||
public List<HalDevice> getLights() {
|
||||
return getHalDevices(DEVICE_REQUEST + LIGHT_REQUEST + TOKEN_REQUEST, LIGHT_REQUEST);
|
||||
}
|
||||
|
||||
public List<HalDevice> getAppliances() {
|
||||
return getHalDevices(DEVICE_REQUEST + APPL_REQUEST + TOKEN_REQUEST, APPL_REQUEST);
|
||||
}
|
||||
|
||||
public List<HalDevice> getTheatre() {
|
||||
return getHalDevices(DEVICE_REQUEST + THEATRE_REQUEST + TOKEN_REQUEST, THEATRE_REQUEST);
|
||||
}
|
||||
|
||||
public List<HalDevice> getCustom() {
|
||||
return getHalDevices(DEVICE_REQUEST + CUSTOM_REQUEST + TOKEN_REQUEST, CUSTOM_REQUEST);
|
||||
}
|
||||
|
||||
public List<HalDevice> getHVAC() {
|
||||
return getHalDevices(HVAC_REQUEST + TOKEN_REQUEST, HVAC_TYPE);
|
||||
}
|
||||
|
||||
public List<HalDevice> getGroups() {
|
||||
return getHalDevices(GROUP_REQUEST + TOKEN_REQUEST, GROUP_TYPE);
|
||||
}
|
||||
|
||||
public List<HalDevice> getMacros() {
|
||||
return getHalDevices(MACRO_REQUEST + TOKEN_REQUEST, MACRO_TYPE);
|
||||
}
|
||||
|
||||
public List<HalDevice> getScenes() {
|
||||
return getHalDevices(SCENE_REQUEST + TOKEN_REQUEST, SCENE_TYPE);
|
||||
}
|
||||
|
||||
public List<HalDevice> getButtons() {
|
||||
List<HalDevice> irDataDevices = getHalDevices(IRDATA_REQUEST + TOKEN_REQUEST, IRDATA_TYPE);
|
||||
|
||||
return getDeviceButtons(irDataDevices);
|
||||
}
|
||||
|
||||
public List<HalDevice> getHome(String theDeviceName) {
|
||||
List<HalDevice> deviceList = null;
|
||||
deviceList = new ArrayList<HalDevice>();
|
||||
HalDevice aNewHalDevice = new HalDevice();
|
||||
aNewHalDevice.setHaldevicetype(HOME_TYPE);
|
||||
aNewHalDevice.setHaldevicename(theDeviceName);
|
||||
deviceList.add(aNewHalDevice);
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
private List<HalDevice> getHalDevices(String apiType, String deviceType) {
|
||||
DeviceElements theHalApiResponse = null;
|
||||
List<HalDevice> deviceList = null;
|
||||
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
if(halAddress.getSecure())
|
||||
theUrl = "https://";
|
||||
else
|
||||
theUrl = "http://";
|
||||
theUrl = theUrl + halAddress.getIp() + apiType + theToken;
|
||||
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);
|
||||
if(theHalApiResponse.getDeviceElements() == null) {
|
||||
StatusDescription theStatus = new Gson().fromJson(theData, StatusDescription.class);
|
||||
if(theStatus.getStatus() == null) {
|
||||
log.warn("Cannot get an devices for type " + deviceType + " for hal " + halAddress.getName() + " as response is not parsable.");
|
||||
}
|
||||
else {
|
||||
log.warn("Cannot get an devices for type " + deviceType + " for hal " + halAddress.getName() + ". Status: " + theStatus.getStatus() + ", with description: " + theStatus.getDescription());
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
deviceList = new ArrayList<HalDevice>();
|
||||
|
||||
Iterator<DeviceName> theDeviceNames = theHalApiResponse.getDeviceElements().iterator();
|
||||
while(theDeviceNames.hasNext()) {
|
||||
DeviceName theDevice = theDeviceNames.next();
|
||||
HalDevice aNewHalDevice = new HalDevice();
|
||||
aNewHalDevice.setHaldevicetype(deviceType);
|
||||
aNewHalDevice.setHaldevicename(theDevice.getDeviceName());
|
||||
aNewHalDevice.setHaladdress(halAddress.getIp());
|
||||
aNewHalDevice.setHalname(halAddress.getName());
|
||||
aNewHalDevice.setSecure(halAddress.getSecure());
|
||||
deviceList.add(aNewHalDevice);
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn("Get Hal device types " + deviceType + " for " + halAddress.getName() + " - returned null, no data.");
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
private List<HalDevice> getDeviceButtons(List<HalDevice> theIrDevices) {
|
||||
DeviceElements theHalApiResponse = null;
|
||||
List<HalDevice> deviceList = null;
|
||||
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
if(theIrDevices == null)
|
||||
return null;
|
||||
Iterator<HalDevice> theHalDevices = theIrDevices.iterator();
|
||||
deviceList = new ArrayList<HalDevice>();
|
||||
while (theHalDevices.hasNext()) {
|
||||
HalDevice theHalDevice = theHalDevices.next();
|
||||
if(halAddress.getSecure())
|
||||
theUrl = "https://";
|
||||
else
|
||||
theUrl = "http://";
|
||||
theUrl = theUrl + halAddress.getIp() + IRBUTTON_REQUEST + TextStringFormatter.forQuerySpaceUrl(theHalDevice.getHaldevicename()) + TOKEN_REQUEST + theToken;
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
|
||||
if (theData != null) {
|
||||
log.debug("GET IrData for IR Device " + theHalDevice.getHaldevicename() + " HalApiResponse - data: " + theData);
|
||||
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 "
|
||||
+ halAddress.getName() + " as response is not parsable.");
|
||||
} else {
|
||||
log.warn("Cannot get buttons for IR Device " + theHalDevice.getHaldevicename() + " for hal "
|
||||
+ halAddress.getName() + ". Status: " + theStatus.getStatus() + ", with description: "
|
||||
+ theStatus.getDescription());
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
theHalDevice.setButtons(theHalApiResponse);
|
||||
deviceList.add(theHalDevice);
|
||||
|
||||
} else {
|
||||
log.warn("Get Hal buttons for IR Device " + theHalDevice.getHaldevicename() + " for "
|
||||
+ halAddress.getName() + " - returned null, no data.");
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
public String deviceCommand(String theUrl) {
|
||||
String theData = null;
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
|
||||
return theData;
|
||||
}
|
||||
|
||||
public NamedIP getHalAddress() {
|
||||
return halAddress;
|
||||
}
|
||||
|
||||
public void setHalAddress(NamedIP halAddress) {
|
||||
this.halAddress = halAddress;
|
||||
}
|
||||
|
||||
public void closeInfo() {
|
||||
if(httpClient != null)
|
||||
httpClient.closeHandler();
|
||||
httpClient = null;
|
||||
halAddress = null;
|
||||
theToken = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
public class StatusDescription {
|
||||
private String Status;
|
||||
private String Description;
|
||||
public String getStatus() {
|
||||
return Status;
|
||||
}
|
||||
public void setStatus(String status) {
|
||||
Status = status;
|
||||
}
|
||||
public String getDescription() {
|
||||
return Description;
|
||||
}
|
||||
public void setDescription(String description) {
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
@@ -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.BridgeSettings;
|
||||
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(BridgeSettings 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(BridgeSettings bridgeSettings) {
|
||||
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
|
||||
validHarmony = bridgeSettings.getBridgeSettingsDescriptor().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.getBridgeSettingsDescriptor().setHarmonyAddress(thedevList);
|
||||
}
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().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.getBridgeSettingsDescriptor(), 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.BridgeSettings;
|
||||
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(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
hassMap = null;
|
||||
aGsonHandler = null;
|
||||
validHass = bridgeSettings.getBridgeSettingsDescriptor().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.getBridgeSettingsDescriptor().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.BridgeSettings;
|
||||
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(BridgeSettings 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(BridgeSettings 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.BridgeSettings;
|
||||
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(BridgeSettings 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(BridgeSettings bridgeSettings) {
|
||||
validHue = bridgeSettings.getBridgeSettingsDescriptor().isValidHue();
|
||||
log.info("Hue passthru Home created." + (validHue ? "" : " No Hue passtrhu systems configured."));
|
||||
if(validHue) {
|
||||
hues = new HashMap<String, HueInfo>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().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.BridgeSettings;
|
||||
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(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
lifxMap = null;
|
||||
aGsonHandler = null;
|
||||
validLifx = bridgeSettings.getBridgeSettingsDescriptor().isValidLifx();
|
||||
log.info("LifxDevice Home created." + (validLifx ? "" : " No LifxDevices configured."));
|
||||
if(validLifx) {
|
||||
try {
|
||||
log.info("Open Lifx client....");
|
||||
InetAddress configuredAddress = InetAddress.getByName(bridgeSettings.getBridgeSettingsDescriptor().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.BridgeSettings;
|
||||
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(BridgeSettings 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(BridgeSettings bridgeSettings) {
|
||||
validMqtt = bridgeSettings.getBridgeSettingsDescriptor().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.getBridgeSettingsDescriptor().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.BridgeSettings;
|
||||
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(BridgeSettings 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(BridgeSettings bridgeSettings) {
|
||||
validSomfy = bridgeSettings.getBridgeSettingsDescriptor().isValidSomfy();
|
||||
log.info("Somfy Home created." + (validSomfy ? "" : " No Somfys configured."));
|
||||
if(validSomfy) {
|
||||
somfys = new HashMap<>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().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
154
src/main/java/com/bwssystems/HABridge/plugins/tcp/TCPHome.java
Normal file
154
src/main/java/com/bwssystems/HABridge/plugins/tcp/TCPHome.java
Normal file
@@ -0,0 +1,154 @@
|
||||
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.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
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;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
public class TCPHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(TCPHome.class);
|
||||
private byte[] sendData;
|
||||
private Map<String, Socket> theSockets;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
|
||||
public TCPHome(BridgeSettings 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.contains("tcp://")) {
|
||||
if(!theUrl.startsWith("{\"tcpDevice\""))
|
||||
theUrl = "{\"tcpDevice\":\"" + theUrl + "\"}";
|
||||
TcpDevice theDevice = aGsonHandler.fromJson(theUrl, TcpDevice.class);
|
||||
String intermediate = theDevice.getTcpDevice().substring(theDevice.getTcpDevice().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) {
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("901", null, "Cannot connect, Unknown Host", null, "/lights/" + device.getId(), null).getTheErrors());
|
||||
}
|
||||
|
||||
try {
|
||||
dataSendSocket = new Socket(IPAddress, Integer.parseInt(port));
|
||||
if(theDevice.isPersistent())
|
||||
theSockets.put(hostPortion, dataSendSocket);
|
||||
} catch (Exception e) {
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("901", null, "Cannot connect, Socket Creation issue", null, "/lights/" + device.getId(), null).getTheErrors());
|
||||
}
|
||||
}
|
||||
|
||||
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 (IOException e) {
|
||||
log.warn("Could not send data to TCP socket <<<" + e.getMessage() + ">>>, closing socket: " + theUrl);
|
||||
try {
|
||||
dataSendSocket.close();
|
||||
} catch (IOException e1) {
|
||||
// noop
|
||||
}
|
||||
dataSendSocket = null;
|
||||
if(theDevice.isPersistent())
|
||||
theSockets.remove(hostPortion);
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("901", null, "Cannot send data", null, "/lights/" + device.getId(), null).getTheErrors());
|
||||
}
|
||||
|
||||
if(!theDevice.isPersistent()) {
|
||||
try {
|
||||
if(dataSendSocket != null)
|
||||
dataSendSocket.close();
|
||||
} catch (IOException e1) {
|
||||
// noop
|
||||
}
|
||||
dataSendSocket = null;
|
||||
}
|
||||
} 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(BridgeSettings bridgeSettings) {
|
||||
log.info("TCP Home created.");
|
||||
theSockets = new HashMap<String, Socket>();
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user