mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-18 08:13:23 +00:00
Compare commits
128 Commits
v4.5.0alph
...
v5.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c1f1f5b96 | ||
|
|
82788f1ecd | ||
|
|
d5920e1454 | ||
|
|
b26a63bb7a | ||
|
|
f17cea3c9d | ||
|
|
7f68285a43 | ||
|
|
cb46a13802 | ||
|
|
de07393e6e | ||
|
|
4b40b03da4 | ||
|
|
4f5d4acf56 | ||
|
|
271bd3913c | ||
|
|
869ffaaf36 | ||
|
|
5a73cc20a9 | ||
|
|
1a936c631c | ||
|
|
1a8a6f7a6f | ||
|
|
879d3b5326 | ||
|
|
3313548ec2 | ||
|
|
e6db6e11e5 | ||
|
|
c843e8d1ac | ||
|
|
47074ff60f | ||
|
|
047a7de612 | ||
|
|
7fc7f00308 | ||
|
|
14e940134c | ||
|
|
1897633e75 | ||
|
|
5a052d9374 | ||
|
|
4b048c2a7f | ||
|
|
27f77b9caa | ||
|
|
18c47ee5e4 | ||
|
|
27dd8475e9 | ||
|
|
37b381085c | ||
|
|
b2a30f5771 | ||
|
|
b88b3fa245 | ||
|
|
52aac32474 | ||
|
|
037b151729 | ||
|
|
3f3d643053 | ||
|
|
f27d905869 | ||
|
|
4c694cb285 | ||
|
|
ca2c5f7b04 | ||
|
|
0c49df1473 | ||
|
|
fe613f7688 | ||
|
|
4b247557d4 | ||
|
|
dc49de41ce | ||
|
|
9d07fac929 | ||
|
|
4ecbad6558 | ||
|
|
4c86e42776 | ||
|
|
00dbea6dac | ||
|
|
ed2bf3bd83 | ||
|
|
58fb085180 | ||
|
|
800f5ec2aa | ||
|
|
bdf5770ba0 | ||
|
|
a213672341 | ||
|
|
d337546da7 | ||
|
|
b9437d42e8 | ||
|
|
71c4447d25 | ||
|
|
a489061bac | ||
|
|
75b925992b | ||
|
|
05b9f195d7 | ||
|
|
5eca809c4a | ||
|
|
fa00b7106a | ||
|
|
df85c8a349 | ||
|
|
d8d5e8f39a | ||
|
|
690bdaa748 | ||
|
|
feee22dbac | ||
|
|
548da932ad | ||
|
|
b147f6606e | ||
|
|
31fe05fe2b | ||
|
|
924f3059fb | ||
|
|
5231eac4b0 | ||
|
|
28144312ff | ||
|
|
60e8855aa7 | ||
|
|
5ea14f9069 | ||
|
|
ce79fb4b82 | ||
|
|
98ce4e2a3a | ||
|
|
026626b5ab | ||
|
|
95c342b548 | ||
|
|
cb9312f6c3 | ||
|
|
8831fec6be | ||
|
|
0227a05970 | ||
|
|
087d66694e | ||
|
|
20ad6891e8 | ||
|
|
cd2bd072ed | ||
|
|
3a5262ff33 | ||
|
|
430eff958c | ||
|
|
86371c03b2 | ||
|
|
3207b6b76e | ||
|
|
3d4724ac57 | ||
|
|
63ac729967 | ||
|
|
0900fec60f | ||
|
|
5ccb793ddb | ||
|
|
3ebab82977 | ||
|
|
e877f410f3 | ||
|
|
220e337d08 | ||
|
|
92b7f4e260 | ||
|
|
78d39325b0 | ||
|
|
208b1cad2a | ||
|
|
ff760a488c | ||
|
|
9200c0fc5d | ||
|
|
998450af4e | ||
|
|
d78c7c536d | ||
|
|
a55c53299a | ||
|
|
fb9cc64839 | ||
|
|
409c93bfb2 | ||
|
|
543a79bb66 | ||
|
|
16d1054b96 | ||
|
|
165b6ef9bb | ||
|
|
fb94b858f6 | ||
|
|
ea80925533 | ||
|
|
62b896ee07 | ||
|
|
8657ea6704 | ||
|
|
05bd6b6d77 | ||
|
|
19256e4eaa | ||
|
|
905f6aa9ec | ||
|
|
daa0dac5b9 | ||
|
|
0478fc69f4 | ||
|
|
b99e74823c | ||
|
|
9aaab37a17 | ||
|
|
df618afeda | ||
|
|
d36769e2a0 | ||
|
|
ebeb6be7a7 | ||
|
|
d3979da2b4 | ||
|
|
f276f66991 | ||
|
|
3ac5c10f08 | ||
|
|
13c84ba174 | ||
|
|
b19fe5c86a | ||
|
|
ff9d0a5a77 | ||
|
|
50af884563 | ||
|
|
0737c54a0e | ||
|
|
a7e516925c |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,3 +12,6 @@ data
|
||||
/.settings/
|
||||
/start.bat
|
||||
/.classpath
|
||||
|
||||
sftp-config\.json
|
||||
/bin/
|
||||
|
||||
202
README.md
202
README.md
@@ -1,9 +1,9 @@
|
||||
# ha-bridge
|
||||
Emulates Philips Hue api to other home automation gateways such as an Amazon Echo or Google Home. The Bridge handles basic commands such as "On", "Off" and "brightness" commands of the hue protocol. This bridge can control most devices that have a distinct API.
|
||||
Emulates Philips Hue API to other home automation gateways such as an Amazon Echo/Dot Gen 1 (gen 2 has issues discovering ha-bridge) 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.
|
||||
|
||||
Here are some diagrams to put this software in perspective.
|
||||
|
||||
The Echo Path looks like this:
|
||||
The Echo path looks like this:
|
||||
```
|
||||
+------------------------+ +------------------------+
|
||||
+-------------+ | H A +------------------| | A +------------------+ |
|
||||
@@ -11,15 +11,7 @@ The Echo Path looks like this:
|
||||
+-------------+ | 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:
|
||||
THe Harmony Hub path looks like this:
|
||||
```
|
||||
+------------------------+ +------------------------+
|
||||
+-------------+ | H A +------------------| | A +------------------+ |
|
||||
@@ -27,6 +19,14 @@ THe Harmony Hub Path looks like this:
|
||||
+-------------+ | E I +------------------| | I +------------------+ |
|
||||
+------------------------+ +------------------------+
|
||||
```
|
||||
A Custom implementation path looks like this:
|
||||
```
|
||||
+------------------------+ +------------------------+
|
||||
+-------------+ | H A +------------------| | A +------------------+ |
|
||||
| HA System |----->| 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!**
|
||||
@@ -35,9 +35,13 @@ THe Harmony Hub Path looks like this:
|
||||
|
||||
**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: Amazon Echo (2nd Generation) has issues finding the ha-bridge. The only workaround is to have a first generation Echo or Dot on your network that finds the ha-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 API's that cannot be handled in the current method, a module may need to be built. The Harmony Hub is such a module and so is the Nest module. The Bridge has helpers to build devices for the gateway for the Logitech Harmony Hub, Vera, Vera Lite or Vera Edge, Nest, Somfy Tahoma and the ability to proxy all of your real Hue bridges behind this bridge.
|
||||
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, Home Assistant, Domoticz, MQTT, HAL, Fibaro, HomeWizard, LIFX, OpenHAB, FHEM, Broadlink 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.
|
||||
|
||||
@@ -59,24 +63,24 @@ ATTENTION: This requires JDK 1.8 to run
|
||||
ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below.
|
||||
|
||||
```
|
||||
java -jar ha-bridge-4.5.0.jar
|
||||
java -jar ha-bridge-5.2.0.jar
|
||||
```
|
||||
|
||||
ATTENTION: If running Java9, you will need to add the xml bind module
|
||||
```
|
||||
java -jar --add-modules java.xml.bind ha-bridge-5.2.0.jar
|
||||
```
|
||||
### Automation on Linux systems
|
||||
To have this configured and running automatically there are a few resources to use. One is using Docker and a docker container has been built for this and can be gotten here: https://github.com/aptalca/docker-ha-bridge
|
||||
|
||||
Create the directory and make sure that ha-bridge-4.5.0.jar is in your /home/pi/habridge directory.
|
||||
Create the directory and make sure that ha-bridge-5.2.0.jar is in your /home/pi/habridge directory.
|
||||
```
|
||||
pi@raspberrypi:~ $ mkdir habridge
|
||||
pi@raspberrypi:~ $ cd habridge
|
||||
|
||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.5.0/ha-bridge-4.5.0.jar
|
||||
```
|
||||
Create the directory and make sure that ha-bridge-4.5.0.jar is in your /home/pi/habridge directory.
|
||||
```
|
||||
pi@raspberrypi:~ $ mkdir habridge
|
||||
pi@raspberrypi:~ $ cd habridge
|
||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.5.0/ha-bridge-4.5.0.jar
|
||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v5.2.0/ha-bridge-5.2.0.jar
|
||||
```
|
||||
|
||||
#### System Control Setup on a pi (preferred)
|
||||
For next gen Linux systems (this includes the Raspberry Pi), here is a systemctl unit file that you can install. Here is a link on how to do this: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
|
||||
|
||||
@@ -95,7 +99,7 @@ After=network.target
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/home/pi/habridge
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.5.0.jar
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-5.2.0.jar
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -130,7 +134,7 @@ 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.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
|
||||
nohup java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-5.2.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
|
||||
|
||||
chmod 777 /home/pi/habridge/habridge-log.txt
|
||||
```
|
||||
@@ -148,12 +152,14 @@ pi@raspberrypi:~/habridge $ tail -f habridge-log.txt
|
||||
```
|
||||
## Run ha-bridge alongside web server already on port 80
|
||||
These examples will help you proxy your current webserver requests to the ha-bridge running on a different port, such as 8080.
|
||||
|
||||
### Apache Example
|
||||
|
||||
Reverse proxy with Apache on Ubuntu linux:
|
||||
|
||||
a2enmod proxy
|
||||
a2enmod proxy_http
|
||||
a2enmod headers
|
||||
Enable the required Apache modules:
|
||||
|
||||
`a2enmod proxy proxy_http headers`
|
||||
|
||||
Added the following lines to my Apache config file “000-default”
|
||||
|
||||
@@ -175,7 +181,10 @@ Added the following lines to my Apache config file “000-default”
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
service apache2 restart
|
||||
Restart apache for the changes to take effect.
|
||||
|
||||
`service apache2 restart`
|
||||
|
||||
### lighthttpd Example
|
||||
```
|
||||
server.modules += ( "mod_proxy" )
|
||||
@@ -196,6 +205,11 @@ location /api/ {
|
||||
```
|
||||
## Available Arguments
|
||||
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".
|
||||
### -Djava.net.preferIPv4Stack=true
|
||||
This will guarantee that the ha-bridge will not use an IPV6 address. This cannot be automatically set inside the code.
|
||||
```
|
||||
java -jar -Djava.net.preferIPv4Stack=true ha-bridge-W.X.Y.jar
|
||||
```
|
||||
### -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:
|
||||
```
|
||||
@@ -214,10 +228,15 @@ The default ip address for the bridge to listen on is all interfaces (0.0.0.0).
|
||||
java -jar -Dserver.ip=192.168.1.1 ha-bridge-W.X.Y.jar
|
||||
```
|
||||
### -Dsecurity.key=`<Your Key To Encrypt Security Data>`
|
||||
The default security key is encoded into the Java code. This should not be used as anyone with access to the code can decode your passworsd. To override what 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 haced. The command line example:
|
||||
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 for your reference.
|
||||
@@ -225,6 +244,10 @@ favorite web interface by going to the http://<my ip address>:<port> or http://l
|
||||
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 Hue 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 doing 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
|
||||
@@ -232,13 +255,17 @@ This field is used to test the bridge server with the UPNP IP Address and to mak
|
||||
#### 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.
|
||||
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.
|
||||
#### Use UPNP Address Interface
|
||||
The server tries to bind to all interfaces to respond to UPNP request. Setting this to `true` will make the binding to the interface that has the `UPNP IP Address`. The default is to be all interfaces which is set as false.
|
||||
#### Use Rooms for Alexa
|
||||
This setting controls rooms for Alexa. If it is set to true, any device ID abaove 10000 is treated as a special group. The default is set as false.
|
||||
#### 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
|
||||
@@ -247,6 +274,8 @@ The server defaults to running on port 80. To override what the default is, spec
|
||||
The upnp response port that will be used. The default is 50000.
|
||||
#### Vera Names and IP Addresses
|
||||
Provide IP Addresses of your Veras that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Vera and device/scene you configure.
|
||||
#### Fibaro Names and IP Addresses
|
||||
Provide IP Addresses of your Fibaros 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 Fibaro and device/scene you configure. There are filter switches available to limit some of the returns for devices and scenes such as use save logs, use user description, use only Lili command and a switch that cleans up format Trash Chars. The default filters are false for everything but Trash Chars.
|
||||
#### 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. 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
|
||||
@@ -255,28 +284,44 @@ Provide IP Addresses of your Hue Bridges that you want to proxy through the brid
|
||||
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.
|
||||
#### Home Assistant Names and IP Addresses
|
||||
Provide IP Addresses and ports of your Home Assistant 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 Home Assistant and device/scene you configure.
|
||||
#### HomeWizard Gateways Names and IP Addresses
|
||||
Provide IP Addresses of your HomeWizard 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 HomeWizard and device/scene you configure.
|
||||
#### Domoticz Names and IP Addresses
|
||||
Provide IP Addresses of your Domoticz 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 Domoticz and device/scene you configure.
|
||||
#### Somfy Tahoma Names and IP Addresses
|
||||
Provide user name and password 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.
|
||||
#### OpenHAB Names and IP Addresses
|
||||
Provide IP Addresses of your OpenHAB 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 OpenHAB and device/scene you configure.
|
||||
#### FHEM Names and IP Addresses
|
||||
Provide IP Addresses of your FHEM 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 FHEM and device/scene you configure.
|
||||
#### 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 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
|
||||
#### LIFX Support
|
||||
This setting will have the ha-bridge look for LIFX devices on your network. Since this is broadcast based, there is no other info needed.
|
||||
#### Broadlink Support
|
||||
This setting will have the ha-bridge look for Broadlink devices on your network (i.e. RM2 IRcontrollers, Smart Power Strips and Smart Plugs). Since this is broadcast based, there is no other info needed.
|
||||
#### Emulate Hue Hub Version
|
||||
This setting is used to set the version that the ha-bridge will return in the hub version field. The default is 9999999999 which should work to be higher than the versions that are being used.
|
||||
#### Emulate MAC
|
||||
This setting is in bridge-id, uuid, etc. in ha-bridge hue config replies. Leave blank unless needed as it is mainly a tool to keep a config to a specific set of devices whtn the ha-bridge is moved to another machine
|
||||
#### Button Press/Call Item Loop Sleep Interval (ms)
|
||||
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
|
||||
Upnp has been set to be very specific as to respond as a Hue. There may be a need to make this response a little more open for other devices that may want to find the ha-bridge. The default is to be strict which is set as true.
|
||||
#### Trace UPNP Calls
|
||||
Turn on tracing for upnp discovery messages to the log. The default is false.
|
||||
#### UPNP Send Delay
|
||||
This setting is for the upnp spec to delay a certain amount between upnp response messages sent back to a client. This is defaulted to 650ms and can be tuned to any value up to 1500ms.
|
||||
#### My Echo URL
|
||||
This sets the URL that is used in the menu bar to ge to your echo. For certain countries, this needs to be set to a different URL.
|
||||
|
||||
At the bottom of the screen is the "Bridge Settings 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 Settings' button will create a backup and name it for you. You can manage these backups by restoring them or deleting them.
|
||||
### The Logs Tab
|
||||
@@ -286,7 +331,7 @@ The bottom part of the Logs Screen has configuration to change the logging level
|
||||
### Bridge Device Additions
|
||||
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.
|
||||
The easy way to get devices configured is with the use of the helpers for the Vera or Harmony, Nest, Hue and others to create devices that the bridge will present.
|
||||
|
||||
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.
|
||||
|
||||
@@ -296,7 +341,18 @@ Another way to add a device is through the Manual Add Tab. This allows you to ma
|
||||
|
||||
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.
|
||||
|
||||
It is imperative when adding a line by hand that you hit the ```Add``` button at the end of the line before adding or updating the whole entry.
|
||||
|
||||
```Update Bridge Device``` Button is for editing a current bridge device entry.
|
||||
|
||||
```Add Bridge Device``` Button is to ccreate a new bridge device entry.
|
||||
|
||||
```Clear``` Button is used to clear the contents of the entry on the screen.
|
||||
|
||||
```Change Edit Mode``` Button is used to swithc back to a pure editor for the on/off/dim/color lines within the device entry.
|
||||
|
||||
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
|
||||
@@ -321,7 +377,7 @@ The Add/Edit tab will show you the fields to fill in for the above in a form, wh
|
||||
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"
|
||||
"halButton", "halHome", "halThermoSet", "mqttMessage", "cmdDevice", "hassDevice", "homewizardDevice", "tcpDevice", "udpDevice", "httpDevice", "domoticzDevice", "somfyDevice"
|
||||
|
||||
Filter Ip example:
|
||||
```
|
||||
@@ -341,7 +397,7 @@ 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 "${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.
|
||||
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, "${time.millis}" for inserting a pure timestamp (milliseconds from 1.1.1970), ${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:
|
||||
```
|
||||
|
||||
@@ -395,9 +451,14 @@ There are multiple replacement constructs available to be put into any of the ca
|
||||
|
||||
You can control items that require special calculated values using ${intensity.math(<your expression using "X" as the value to operate on>)} i.e. "${intensity.math(X/4)}".
|
||||
|
||||
For the items that want to have a date time put into the message, utilize ${time.format(yyyy-MM-ddTHH:mm:ssXXX)} where "yyyy-MM-ddTHH:mm:ssXXX" can be any format from the Java SimpleDateFormat documented here: https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html
|
||||
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. Also, there is a ${time.millis} which will put the millis timestamp where this replacement control is located.
|
||||
|
||||
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.
|
||||
Color has been added as a replacement control and the available values are ${color.r}, ${color.g}, ${color.b} which are representations of each color as 0 - 255. There are hex equivalents as well as ${color.rx}, ${color.gx}, ${color.bx} and ${color.rgbx} as 2 place hex representations except for rgbx which is a six place hex representation.
|
||||
|
||||
Special handling for milights is included and is handled by ${color.milight:x}. The usage for that is as follows: udp://ip:port/0x${color.milight:x} where x is a number between 0 and 4 (0 all groups, 1-4 specific group). The group is necessary in case the color turns out to be white. The correct group on must of course be sent before that udp packet.
|
||||
Note that milight can only use 255 colors and white is handled completely separate for the rgbw strips, so setting temperature via ct with milight does something but not really the desired result
|
||||
|
||||
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}", "${device.deviceType}", "${device.requesterAddress}", "${device.description}" and "${device.comments}". 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"}]
|
||||
@@ -410,7 +471,7 @@ e.g.
|
||||
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
@@ -420,16 +481,13 @@ Here is the table of items to use to tell Alexa what you want to do, this has ch
|
||||
|
||||
To do this... | Say this...
|
||||
--------------|------------
|
||||
ON Commands |
|
||||
| Alexa, turn on `<Device Name>`
|
||||
OFF Commands |
|
||||
| Alexa, turn off `<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, set `<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.
|
||||
|
||||
@@ -438,6 +496,8 @@ To view or remove devices that Alexa knows about, you can use the mobile app `Me
|
||||
## 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
|
||||
@@ -468,7 +528,7 @@ New or removed devices are picked up automatically as soon as they are added/rem
|
||||
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"}
|
||||
```
|
||||
@@ -500,8 +560,8 @@ contentBodyOff | string | This is the content body that you would like to send w
|
||||
{
|
||||
"name" : "bedroom light",
|
||||
"deviceType" : "switch",
|
||||
"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"}]
|
||||
"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
|
||||
@@ -511,8 +571,8 @@ e.g.
|
||||
{
|
||||
"name": "entry light",
|
||||
"deviceType": "switch",
|
||||
"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"}]
|
||||
"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.
|
||||
@@ -524,8 +584,8 @@ e.g.
|
||||
{
|
||||
"name": "Thermostat,
|
||||
"deviceType": "custom",
|
||||
"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"}]
|
||||
"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.
|
||||
@@ -537,8 +597,8 @@ e.g:
|
||||
{
|
||||
"name": "test device",
|
||||
"deviceType": "custom",
|
||||
"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\"}]
|
||||
"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
|
||||
@@ -547,8 +607,8 @@ Anything that takes an action as a result of an HTTP request will probably work
|
||||
{
|
||||
"name": "night mode",
|
||||
"deviceType": ""custom",
|
||||
"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"}]
|
||||
"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.
|
||||
@@ -556,8 +616,8 @@ Here is a UDP example that can send binary data.
|
||||
{
|
||||
"name": "UDPPacket",
|
||||
"deviceType": "custom",
|
||||
"offUrl": [{"item":"udp://192.168.1.1:8899/0x460055","type":"udpDevice"}],
|
||||
"onUrl": [{"item":"udp://192.168.1.1:8899/0x450055","type":"udpDevice"}]
|
||||
"offUrl": "[{\"item\":\"udp://192.168.1.1:8899/0x460055\",\"type\":\"udpDevice\"}]",
|
||||
"onUrl": "[{\"item\":\"udp://192.168.1.1:8899/0x450055\",\"type\":\"udpDevice\"}]"
|
||||
}
|
||||
```
|
||||
#### Response
|
||||
@@ -616,8 +676,8 @@ contentBodyOff | string | This is the content body that you would like to send w
|
||||
"id" : "6789",
|
||||
"name" : "table light",
|
||||
"deviceType" : "switch",
|
||||
"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"}]
|
||||
"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
|
||||
@@ -870,7 +930,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.
|
||||
@@ -879,7 +939,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"}
|
||||
```
|
||||
@@ -988,7 +1048,7 @@ 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
|
||||
```
|
||||
|
||||
21
pom.xml
21
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.bwssystems.HABridge</groupId>
|
||||
<artifactId>ha-bridge</artifactId>
|
||||
<version>4.5.0alpha</version>
|
||||
<version>5.2.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HA Bridge</name>
|
||||
@@ -33,7 +33,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>harmony-java-client</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<version>master-SNAPSHOT</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@@ -63,7 +63,7 @@
|
||||
<dependency>
|
||||
<groupId>com.sparkjava</groupId>
|
||||
<artifactId>spark-core</artifactId>
|
||||
<version>2.3</version>
|
||||
<version>2.7.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
@@ -84,12 +84,12 @@
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<version>1.7.24</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.5</version>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
@@ -104,12 +104,12 @@
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<version>4.0-beta4</version>
|
||||
<version>4.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.igniterealtime.smack</groupId>
|
||||
<artifactId>smack-core</artifactId>
|
||||
<version>4.0.7</version>
|
||||
<version>4.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
@@ -119,13 +119,18 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<version>4.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>lifx-sdk-java</artifactId>
|
||||
<version>2.1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>broadlink-java-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import spark.Request;
|
||||
|
||||
public abstract class AuthFramework {
|
||||
private static final String USER_SESSION_ID = "user";
|
||||
|
||||
public AuthFramework() {
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
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) {
|
||||
return request.session().attribute(USER_SESSION_ID);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,12 @@ 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;
|
||||
@@ -15,23 +21,36 @@ 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;
|
||||
|
||||
public class BridgeSecurity extends AuthFramework {
|
||||
import spark.Request;
|
||||
|
||||
public class BridgeSecurity {
|
||||
private static final Logger log = LoggerFactory.getLogger(BridgeSecurity.class);
|
||||
private char[] habridgeKey;
|
||||
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 theData) {
|
||||
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 {
|
||||
@@ -43,7 +62,8 @@ public class BridgeSecurity extends AuthFramework {
|
||||
} catch (IOException e) {
|
||||
anError = e.getMessage();
|
||||
}
|
||||
log.warn("Cound not get security data, using default security (none): " + anError);
|
||||
if(anError != null)
|
||||
log.warn("Cound not get security data, using default security (none): " + anError);
|
||||
}
|
||||
|
||||
if(theData == null || anError != null) {
|
||||
@@ -123,13 +143,8 @@ public class BridgeSecurity extends AuthFramework {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setExecGarden(String theGarden) {
|
||||
securityDescriptor.setExecGarden(theGarden);
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
public String getExecGarden() {
|
||||
return securityDescriptor.getExecGarden();
|
||||
return execGarden;
|
||||
}
|
||||
public void setUseLinkButton(boolean useThis) {
|
||||
securityDescriptor.setUseLinkButton(useThis);
|
||||
@@ -145,7 +160,6 @@ public class BridgeSecurity extends AuthFramework {
|
||||
}
|
||||
public SecurityInfo getSecurityInfo() {
|
||||
SecurityInfo theInfo = new SecurityInfo();
|
||||
theInfo.setExecGarden(getExecGarden());
|
||||
theInfo.setUseLinkButton(isUseLinkButton());
|
||||
theInfo.setSecureHueApi(isSecureHueApi());
|
||||
theInfo.setSecure(isSecure());
|
||||
@@ -187,6 +201,128 @@ public class BridgeSecurity extends AuthFramework {
|
||||
public void setSettingsChanged(boolean settingsChanged) {
|
||||
this.settingsChanged = settingsChanged;
|
||||
}
|
||||
public Map<String, WhitelistEntry> getWhitelist() {
|
||||
return securityDescriptor.getWhitelist();
|
||||
}
|
||||
|
||||
public void setWhitelist(Map<String, WhitelistEntry> aWhitelist) {
|
||||
securityDescriptor.setWhitelist(aWhitelist);
|
||||
settingsChanged = true;
|
||||
return;
|
||||
}
|
||||
|
||||
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("") && !aUser.equals(DEPRACATED_INTERNAL_USER)) {
|
||||
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 strict rules is set to: " + strict + "for user <" + aUser + ">");
|
||||
return HueErrorResponse.createResponse("1", "/api/" + aUser == null ? "" : aUser, "unauthorized user", null, null, null).getTheErrors();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String findWhitelistUserByDeviceType(String aDeviceType) {
|
||||
String validUser = null;
|
||||
boolean found = false;
|
||||
WhitelistEntry anEntry = null;
|
||||
if (aDeviceType != null) {
|
||||
if (securityDescriptor.getWhitelist() != null) {
|
||||
Set<String> theUserIds = securityDescriptor.getWhitelist().keySet();
|
||||
Iterator<String> userIterator = theUserIds.iterator();
|
||||
while (!found && userIterator.hasNext()) {
|
||||
validUser = userIterator.next();
|
||||
anEntry = securityDescriptor.getWhitelist().get(validUser);
|
||||
if (anEntry.getName().equals(aDeviceType)) {
|
||||
found = true;
|
||||
log.debug("findWhitelistUserByDeviceType: found a user <" + validUser + "> for device type <" + aDeviceType + ">");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
validUser = null;
|
||||
|
||||
return validUser;
|
||||
}
|
||||
|
||||
private void newWhitelistUser(String aUser, String userDescription) {
|
||||
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 = null;
|
||||
String theEntry = findWhitelistUserByDeviceType(userDescription);
|
||||
if(theEntry == null) {
|
||||
aUser = getNewUserID();
|
||||
newWhitelistUser(aUser, userDescription);
|
||||
} else {
|
||||
aUser = theEntry;
|
||||
}
|
||||
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();
|
||||
if(pair.getValue().getName().equals(TEST_USER_TYPE)) {
|
||||
it.remove(); // avoids a ConcurrentModificationException
|
||||
setSettingsChanged(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
|
||||
@@ -211,4 +347,47 @@ public class BridgeSecurity extends AuthFramework {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
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();
|
||||
@@ -44,6 +46,12 @@ public class BridgeSecurityDescriptor {
|
||||
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;
|
||||
|
||||
@@ -12,6 +12,8 @@ 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;
|
||||
@@ -29,12 +31,18 @@ public class BridgeSettings extends BackupHandler {
|
||||
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");
|
||||
@@ -50,9 +58,14 @@ public class BridgeSettings extends BackupHandler {
|
||||
public BridgeSecurity getBridgeSecurity() {
|
||||
return bridgeSecurity;
|
||||
}
|
||||
public static String getCurrentDate() {
|
||||
return dateFormat.format(new Date());
|
||||
}
|
||||
|
||||
public void buildSettings() {
|
||||
String addressString = null;
|
||||
String theVeraAddress = null;
|
||||
String theFibaroAddress = null;
|
||||
String theSomfyAddress = null;
|
||||
String theHarmonyAddress = null;
|
||||
String configFileProperty = System.getProperty("config.file");
|
||||
@@ -63,6 +76,7 @@ public class BridgeSettings extends BackupHandler {
|
||||
}
|
||||
String serverPortOverride = System.getProperty("server.port");
|
||||
String serverIpOverride = System.getProperty("server.ip");
|
||||
String upnpStrictOverride = System.getProperty("upnp.strict", "true");
|
||||
if(configFileProperty != null)
|
||||
{
|
||||
log.info("reading from config file: " + configFileProperty);
|
||||
@@ -78,6 +92,7 @@ public class BridgeSettings extends BackupHandler {
|
||||
theBridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DEFAULT_WEB_PORT));
|
||||
theBridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address"));
|
||||
theBridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db"));
|
||||
theBridgeSettings.setUpnpGroupDb(System.getProperty("upnp.group.db"));
|
||||
theBridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT));
|
||||
|
||||
theVeraAddress = System.getProperty("vera.address");
|
||||
@@ -95,6 +110,22 @@ public class BridgeSettings extends BackupHandler {
|
||||
}
|
||||
}
|
||||
theBridgeSettings.setVeraAddress(theVeraList);
|
||||
|
||||
theFibaroAddress = System.getProperty("fibaro.address");
|
||||
IpList theFibaroList = null;
|
||||
if(theFibaroAddress != null) {
|
||||
try {
|
||||
theFibaroList = new Gson().fromJson(theFibaroAddress, IpList.class);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
theFibaroList = new Gson().fromJson("{devices:[{name:default,ip:" + theFibaroAddress + "}]}", IpList.class);
|
||||
} catch (Exception et) {
|
||||
log.error("Cannot parse fibaro.address, not set with message: " + e.getMessage(), e);
|
||||
theFibaroList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
theBridgeSettings.setFibaroAddress(theFibaroList);
|
||||
|
||||
theHarmonyAddress = System.getProperty("harmony.address");
|
||||
IpList theHarmonyList = null;
|
||||
@@ -135,7 +166,7 @@ public class BridgeSettings extends BackupHandler {
|
||||
theBridgeSettings.setNestpwd(System.getProperty("nest.pwd"));
|
||||
}
|
||||
|
||||
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().equals("")) {
|
||||
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);
|
||||
@@ -158,6 +189,9 @@ public class BridgeSettings extends BackupHandler {
|
||||
|
||||
if(theBridgeSettings.getUpnpDeviceDb() == null)
|
||||
theBridgeSettings.setUpnpDeviceDb(Configuration.DEVICE_DB_DIRECTORY);
|
||||
|
||||
if(theBridgeSettings.getUpnpGroupDb() == null)
|
||||
theBridgeSettings.setUpnpGroupDb(Configuration.GROUP_DB_DIRECTORY);
|
||||
|
||||
if(theBridgeSettings.getNumberoflogmessages() == null || theBridgeSettings.getNumberoflogmessages() <= 0)
|
||||
theBridgeSettings.setNumberoflogmessages(new Integer(Configuration.NUMBER_OF_LOG_MESSAGES));
|
||||
@@ -166,6 +200,7 @@ public class BridgeSettings extends BackupHandler {
|
||||
theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP));
|
||||
|
||||
theBridgeSettings.setVeraconfigured(theBridgeSettings.isValidVera());
|
||||
theBridgeSettings.setFibaroconfigured(theBridgeSettings.isValidFibaro());
|
||||
theBridgeSettings.setHarmonyconfigured(theBridgeSettings.isValidHarmony());
|
||||
theBridgeSettings.setNestConfigured(theBridgeSettings.isValidNest());
|
||||
theBridgeSettings.setHueconfigured(theBridgeSettings.isValidHue());
|
||||
@@ -174,19 +209,24 @@ public class BridgeSettings extends BackupHandler {
|
||||
theBridgeSettings.setHassconfigured(theBridgeSettings.isValidHass());
|
||||
theBridgeSettings.setDomoticzconfigured(theBridgeSettings.isValidDomoticz());
|
||||
theBridgeSettings.setSomfyconfigured(theBridgeSettings.isValidSomfy());
|
||||
theBridgeSettings.setHomeWizardConfigured(theBridgeSettings.isValidHomeWizard());
|
||||
theBridgeSettings.setOpenhabconfigured(theBridgeSettings.isValidOpenhab());
|
||||
theBridgeSettings.setFhemconfigured(theBridgeSettings.isValidFhem());
|
||||
// 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);
|
||||
if(upnpStrictOverride != null)
|
||||
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(upnpStrictOverride));
|
||||
setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-");
|
||||
|
||||
setupInternalTestUser();
|
||||
|
||||
String theKey = System.getProperty("security.key");
|
||||
if(theKey == null)
|
||||
theKey = "IWantMyPasswordsToBeAbleToBeDecodedPleaseSeeTheReadme";
|
||||
bridgeSecurity = new BridgeSecurity(theKey.toCharArray(), theBridgeSettings.getSecurityData());
|
||||
bridgeSecurity.setSecurityData(theBridgeSettings.getSecurityData());
|
||||
if(theBridgeSettings.getWhitelist() != null) {
|
||||
bridgeSecurity.convertWhitelist(theBridgeSettings.getWhitelist());
|
||||
theBridgeSettings.removeWhitelist();
|
||||
updateConfigFile();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadConfig() {
|
||||
@@ -219,11 +259,11 @@ public class BridgeSettings extends BackupHandler {
|
||||
try {
|
||||
newBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
log.warn("could not get encoded security data: " + e.getMessage());
|
||||
return;
|
||||
} catch (GeneralSecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
log.warn("could not get encoded security data: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
bridgeSecurity.setSettingsChanged(false);
|
||||
}
|
||||
@@ -237,13 +277,25 @@ public class BridgeSettings extends BackupHandler {
|
||||
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 void configWriter(String content, Path filePath) {
|
||||
private synchronized void configWriter(String content, Path filePath) {
|
||||
if(Files.exists(filePath) && !Files.isWritable(filePath)){
|
||||
log.error("Error file is not writable: " + filePath);
|
||||
return;
|
||||
@@ -260,7 +312,7 @@ 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);
|
||||
@@ -273,7 +325,8 @@ public class BridgeSettings extends BackupHandler {
|
||||
perms.add(PosixFilePermission.OWNER_WRITE);
|
||||
|
||||
try {
|
||||
if(System.getProperty("os.name").toLowerCase().indexOf("win") <= 0)
|
||||
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");
|
||||
@@ -309,6 +362,7 @@ public class BridgeSettings extends BackupHandler {
|
||||
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) {
|
||||
@@ -335,9 +389,4 @@ public class BridgeSettings extends BackupHandler {
|
||||
}
|
||||
return addressString;
|
||||
}
|
||||
private void setupInternalTestUser() {
|
||||
theBridgeSettings.setupInternalTestUser();
|
||||
if(theBridgeSettings.isSettingsChanged())
|
||||
this.updateConfigFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +1,171 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
//import com.bwssystems.HABridge.api.NameValue;
|
||||
import com.bwssystems.HABridge.api.hue.HueConstants;
|
||||
import com.bwssystems.HABridge.api.hue.HueError;
|
||||
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
|
||||
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
|
||||
|
||||
public class BridgeSettingsDescriptor {
|
||||
private static final String DEFAULT_INTERNAL_USER = "thehabridgeuser";
|
||||
private static final String DEFAULT_USER_DESCRIPTION = "default_test_user";
|
||||
@SerializedName("upnpconfigaddress")
|
||||
@Expose
|
||||
private String upnpconfigaddress;
|
||||
@SerializedName("useupnpiface")
|
||||
@Expose
|
||||
private boolean useupnpiface;
|
||||
@SerializedName("userooms")
|
||||
@Expose
|
||||
private boolean userooms;
|
||||
@SerializedName("serverport")
|
||||
@Expose
|
||||
private Integer serverport;
|
||||
@SerializedName("upnpresponseport")
|
||||
@Expose
|
||||
private Integer upnpresponseport;
|
||||
@SerializedName("upnpdevicedb")
|
||||
@Expose
|
||||
private String upnpdevicedb;
|
||||
@SerializedName("upnpgroupdb")
|
||||
@Expose
|
||||
private String upnpgroupdb;
|
||||
@SerializedName("veraaddress")
|
||||
@Expose
|
||||
private IpList veraaddress;
|
||||
@SerializedName("fibaroaddress")
|
||||
@Expose
|
||||
private IpList fibaroaddress;
|
||||
@SerializedName("harmonyaddress")
|
||||
@Expose
|
||||
private IpList harmonyaddress;
|
||||
@SerializedName("buttonsleep")
|
||||
@Expose
|
||||
private Integer buttonsleep;
|
||||
private boolean upnpstrict;
|
||||
@SerializedName("traceupnp")
|
||||
@Expose
|
||||
private boolean traceupnp;
|
||||
@SerializedName("nestuser")
|
||||
@Expose
|
||||
private String nestuser;
|
||||
@SerializedName("nestpwd")
|
||||
@Expose
|
||||
private String nestpwd;
|
||||
private boolean veraconfigured;
|
||||
private boolean harmonyconfigured;
|
||||
private boolean nestconfigured;
|
||||
@SerializedName("farenheit")
|
||||
@Expose
|
||||
private boolean farenheit;
|
||||
@SerializedName("configfile")
|
||||
@Expose
|
||||
private String configfile;
|
||||
@SerializedName("numberoflogmessages")
|
||||
@Expose
|
||||
private Integer numberoflogmessages;
|
||||
@SerializedName("hueaddress")
|
||||
@Expose
|
||||
private IpList hueaddress;
|
||||
private boolean hueconfigured;
|
||||
@SerializedName("haladdress")
|
||||
@Expose
|
||||
private IpList haladdress;
|
||||
private String haltoken;
|
||||
private boolean halconfigured;
|
||||
@SerializedName("whitelist")
|
||||
@Expose
|
||||
private Map<String, WhitelistEntry> whitelist;
|
||||
private boolean settingsChanged;
|
||||
@SerializedName("myechourl")
|
||||
@Expose
|
||||
private String myechourl;
|
||||
@SerializedName("webaddress")
|
||||
@Expose
|
||||
private String webaddress;
|
||||
@SerializedName("mqttaddress")
|
||||
@Expose
|
||||
private IpList mqttaddress;
|
||||
private boolean mqttconfigured;
|
||||
@SerializedName("hassaddress")
|
||||
@Expose
|
||||
private IpList hassaddress;
|
||||
private boolean hassconfigured;
|
||||
private String hubversion;
|
||||
@SerializedName("domoticzaddress")
|
||||
@Expose
|
||||
private IpList domoticzaddress;
|
||||
private boolean domoticzconfigured;
|
||||
@SerializedName("somfyaddress")
|
||||
@Expose
|
||||
private IpList somfyaddress;
|
||||
private boolean somfyconfigured;
|
||||
private boolean lifxconfigured;
|
||||
@SerializedName("openhabaddress")
|
||||
@Expose
|
||||
private IpList openhabaddress;
|
||||
@SerializedName("hubversion")
|
||||
@Expose
|
||||
private String hubversion;
|
||||
@SerializedName("hubmac")
|
||||
@Expose
|
||||
private String hubmac;
|
||||
@SerializedName("securityData")
|
||||
@Expose
|
||||
private String securityData;
|
||||
@SerializedName("homewizardaddress")
|
||||
@Expose
|
||||
private IpList homewizardaddress;
|
||||
@SerializedName("upnpsenddelay")
|
||||
@Expose
|
||||
private Integer upnpsenddelay;
|
||||
@SerializedName("fhemaddress")
|
||||
@Expose
|
||||
private IpList fhemaddress;
|
||||
@SerializedName("lifxconfigured")
|
||||
@Expose
|
||||
private boolean lifxconfigured;
|
||||
@SerializedName("broadlinkconfigured")
|
||||
@Expose
|
||||
private boolean broadlinkconfigured;
|
||||
// @SerializedName("activeloggers")
|
||||
// @Expose
|
||||
// private List<NameValue> activeloggers;
|
||||
|
||||
private boolean settingsChanged;
|
||||
private boolean veraconfigured;
|
||||
private boolean fibaroconfigured;
|
||||
private boolean harmonyconfigured;
|
||||
private boolean hueconfigured;
|
||||
private boolean nestconfigured;
|
||||
private boolean halconfigured;
|
||||
private boolean mqttconfigured;
|
||||
private boolean hassconfigured;
|
||||
private boolean domoticzconfigured;
|
||||
private boolean somfyconfigured;
|
||||
private boolean homewizardconfigured;
|
||||
private boolean openhabconfigured;
|
||||
private boolean fhemconfigured;
|
||||
|
||||
// Deprecated settings
|
||||
private String haltoken;
|
||||
private boolean upnpstrict;
|
||||
|
||||
public BridgeSettingsDescriptor() {
|
||||
super();
|
||||
this.upnpstrict = true;
|
||||
this.useupnpiface = false;
|
||||
this.userooms = false;
|
||||
this.traceupnp = false;
|
||||
this.nestconfigured = false;
|
||||
this.veraconfigured = false;
|
||||
this.fibaroconfigured = false;
|
||||
this.somfyconfigured = false;
|
||||
this.harmonyconfigured = false;
|
||||
this.hueconfigured = false;
|
||||
this.halconfigured = false;
|
||||
this.mqttconfigured = false;
|
||||
this.hassconfigured = false;
|
||||
this.domoticzconfigured = false;
|
||||
this.homewizardconfigured = false;
|
||||
this.lifxconfigured = false;
|
||||
this.openhabconfigured = false;
|
||||
this.farenheit = true;
|
||||
this.whitelist = null;
|
||||
this.securityData = null;
|
||||
this.settingsChanged = false;
|
||||
this.myechourl = "echo.amazon.com/#cards";
|
||||
this.myechourl = "alexa.amazon.com/spa/index.html#cards";
|
||||
this.webaddress = "0.0.0.0";
|
||||
this.hubversion = HueConstants.HUB_VERSION;
|
||||
this.hubmac = null;
|
||||
// this.activeloggers = null;
|
||||
this.upnpsenddelay = Configuration.UPNP_SEND_DELAY;
|
||||
this.broadlinkconfigured = false;
|
||||
}
|
||||
public String getUpnpConfigAddress() {
|
||||
return upnpconfigaddress;
|
||||
@@ -79,6 +173,18 @@ public class BridgeSettingsDescriptor {
|
||||
public void setUpnpConfigAddress(String upnpConfigAddress) {
|
||||
this.upnpconfigaddress = upnpConfigAddress;
|
||||
}
|
||||
public boolean isUseupnpiface() {
|
||||
return useupnpiface;
|
||||
}
|
||||
public void setUseupnpiface(boolean useupnpiface) {
|
||||
this.useupnpiface = useupnpiface;
|
||||
}
|
||||
public boolean isUserooms() {
|
||||
return userooms;
|
||||
}
|
||||
public void setUserooms(boolean userooms) {
|
||||
this.userooms = userooms;
|
||||
}
|
||||
public Integer getServerPort() {
|
||||
return serverport;
|
||||
}
|
||||
@@ -103,18 +209,36 @@ public class BridgeSettingsDescriptor {
|
||||
public void setUpnpDeviceDb(String upnpDeviceDb) {
|
||||
this.upnpdevicedb = upnpDeviceDb;
|
||||
}
|
||||
public String getUpnpGroupDb() {
|
||||
return upnpgroupdb;
|
||||
}
|
||||
public void setUpnpGroupDb(String upnpGroupDb) {
|
||||
this.upnpgroupdb = upnpGroupDb;
|
||||
}
|
||||
public IpList getVeraAddress() {
|
||||
return veraaddress;
|
||||
}
|
||||
public IpList getFibaroAddress() {
|
||||
return fibaroaddress;
|
||||
}
|
||||
public IpList getSomfyAddress() {
|
||||
return somfyaddress;
|
||||
}
|
||||
public IpList getHomeWizardAddress() {
|
||||
return homewizardaddress;
|
||||
}
|
||||
public void setVeraAddress(IpList veraAddress) {
|
||||
this.veraaddress = veraAddress;
|
||||
}
|
||||
public void setFibaroAddress(IpList fibaroAddress) {
|
||||
this.fibaroaddress = fibaroAddress;
|
||||
}
|
||||
public void setSomfyAddress(IpList somfyAddress) {
|
||||
this.somfyaddress = somfyAddress;
|
||||
}
|
||||
public void setHomeWizardAddress(IpList homewizardaddress) {
|
||||
this.homewizardaddress = homewizardaddress;
|
||||
}
|
||||
public IpList getHarmonyAddress() {
|
||||
return harmonyaddress;
|
||||
}
|
||||
@@ -148,15 +272,27 @@ public class BridgeSettingsDescriptor {
|
||||
public boolean isVeraconfigured() {
|
||||
return veraconfigured;
|
||||
}
|
||||
public boolean isFibaroconfigured() {
|
||||
return fibaroconfigured;
|
||||
}
|
||||
public boolean isSomfyconfigured() {
|
||||
return somfyconfigured;
|
||||
}
|
||||
public boolean isHomeWizardConfigured() {
|
||||
return homewizardconfigured;
|
||||
}
|
||||
public void setVeraconfigured(boolean veraconfigured) {
|
||||
this.veraconfigured = veraconfigured;
|
||||
}
|
||||
public void setFibaroconfigured(boolean fibaroconfigured) {
|
||||
this.fibaroconfigured = fibaroconfigured;
|
||||
}
|
||||
public void setSomfyconfigured(boolean somfyconfigured) {
|
||||
this.somfyconfigured = somfyconfigured;
|
||||
}
|
||||
public void setHomeWizardConfigured(boolean homewizardconfigured) {
|
||||
this.homewizardconfigured = homewizardconfigured;
|
||||
}
|
||||
public boolean isHarmonyconfigured() {
|
||||
return harmonyconfigured;
|
||||
}
|
||||
@@ -226,8 +362,8 @@ public class BridgeSettingsDescriptor {
|
||||
public Map<String, WhitelistEntry> getWhitelist() {
|
||||
return whitelist;
|
||||
}
|
||||
public void setWhitelist(Map<String, WhitelistEntry> whitelist) {
|
||||
this.whitelist = whitelist;
|
||||
protected void removeWhitelist() {
|
||||
whitelist = null;
|
||||
}
|
||||
public boolean isSettingsChanged() {
|
||||
return settingsChanged;
|
||||
@@ -271,12 +407,30 @@ public class BridgeSettingsDescriptor {
|
||||
public void setHassconfigured(boolean hassconfigured) {
|
||||
this.hassconfigured = hassconfigured;
|
||||
}
|
||||
public IpList getOpenhabaddress() {
|
||||
return openhabaddress;
|
||||
}
|
||||
public void setOpenhabaddress(IpList openhabaddress) {
|
||||
this.openhabaddress = openhabaddress;
|
||||
}
|
||||
public boolean isOpenhabconfigured() {
|
||||
return openhabconfigured;
|
||||
}
|
||||
public void setOpenhabconfigured(boolean openhabconfigured) {
|
||||
this.openhabconfigured = openhabconfigured;
|
||||
}
|
||||
public String getHubversion() {
|
||||
return hubversion;
|
||||
}
|
||||
public void setHubversion(String hubversion) {
|
||||
this.hubversion = hubversion;
|
||||
}
|
||||
public String getHubmac() {
|
||||
return hubmac;
|
||||
}
|
||||
public void setHubmac(String hubmac) {
|
||||
this.hubmac = hubmac;
|
||||
}
|
||||
public IpList getDomoticzaddress() {
|
||||
return domoticzaddress;
|
||||
}
|
||||
@@ -301,6 +455,36 @@ public class BridgeSettingsDescriptor {
|
||||
public void setSecurityData(String securityData) {
|
||||
this.securityData = securityData;
|
||||
}
|
||||
public Integer getUpnpsenddelay() {
|
||||
return upnpsenddelay;
|
||||
}
|
||||
public void setUpnpsenddelay(Integer upnpsenddelay) {
|
||||
this.upnpsenddelay = upnpsenddelay;
|
||||
}
|
||||
public IpList getFhemaddress() {
|
||||
return fhemaddress;
|
||||
}
|
||||
public void setFhemaddress(IpList fhemaddress) {
|
||||
this.fhemaddress = fhemaddress;
|
||||
}
|
||||
public boolean isFhemconfigured() {
|
||||
return fhemconfigured;
|
||||
}
|
||||
public void setFhemconfigured(boolean fhemconfigured) {
|
||||
this.fhemconfigured = fhemconfigured;
|
||||
}
|
||||
// public List<NameValue> getActiveloggers() {
|
||||
// return activeloggers;
|
||||
// }
|
||||
// public void setActiveloggers(List<NameValue> activeloggers) {
|
||||
// this.activeloggers = activeloggers;
|
||||
// }
|
||||
public boolean isBroadlinkconfigured() {
|
||||
return broadlinkconfigured;
|
||||
}
|
||||
public void setBroadlinkconfigured(boolean broadlinkconfigured) {
|
||||
this.broadlinkconfigured = broadlinkconfigured;
|
||||
}
|
||||
public Boolean isValidVera() {
|
||||
if(this.getVeraAddress() == null || this.getVeraAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
@@ -309,6 +493,14 @@ public class BridgeSettingsDescriptor {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidFibaro() {
|
||||
if(this.getFibaroAddress() == null || this.getFibaroAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getFibaroAddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidHarmony() {
|
||||
if(this.getHarmonyAddress() == null || this.getHarmonyAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
@@ -338,8 +530,10 @@ public class BridgeSettingsDescriptor {
|
||||
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;
|
||||
if(devicesList.get(0).getPassword() == null || devicesList.get(0).getPassword().trim().isEmpty()) {
|
||||
if(this.getHaltoken() == null || this.getHaltoken().equals(""))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidMQTT() {
|
||||
@@ -377,80 +571,48 @@ public class BridgeSettingsDescriptor {
|
||||
public Boolean isValidLifx() {
|
||||
return this.isLifxconfigured();
|
||||
}
|
||||
|
||||
public HueError[] validateWhitelistUser(String aUser, String userDescription, boolean strict) {
|
||||
String validUser = null;
|
||||
boolean found = false;
|
||||
if (aUser != null && !aUser.equalsIgnoreCase("undefined") && !aUser.equalsIgnoreCase("null")
|
||||
&& !aUser.equalsIgnoreCase("")) {
|
||||
if (whitelist != null) {
|
||||
Set<String> theUserIds = whitelist.keySet();
|
||||
Iterator<String> userIterator = theUserIds.iterator();
|
||||
while (userIterator.hasNext()) {
|
||||
validUser = userIterator.next();
|
||||
if (validUser.equals(aUser))
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
public void updateHue(NamedIP aHue) {
|
||||
int indexHue = -1;
|
||||
for( int i = 0; i < hueaddress.getDevices().size(); i++) {
|
||||
if(hueaddress.getDevices().get(i).getName().equals(aHue.getName()))
|
||||
indexHue = i;
|
||||
}
|
||||
|
||||
if(!found && !strict) {
|
||||
newWhitelistUser(aUser, userDescription);
|
||||
|
||||
found = true;
|
||||
if(indexHue >= 0) {
|
||||
hueaddress.getDevices().set(indexHue, aHue);
|
||||
this.setSettingsChanged(true);
|
||||
}
|
||||
}
|
||||
public Boolean isValidHomeWizard() {
|
||||
if(this.getHomeWizardAddress() == null || this.getHomeWizardAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
|
||||
if (!found) {
|
||||
return HueErrorResponse.createResponse("1", "/api/" + aUser, "unauthorized user", null, null, null).getTheErrors();
|
||||
}
|
||||
List<NamedIP> devicesList = this.getHomeWizardAddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
|
||||
return null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void newWhitelistUser(String aUser, String userDescription) {
|
||||
if (whitelist == null) {
|
||||
whitelist = new HashMap<>();
|
||||
}
|
||||
if(userDescription == null)
|
||||
userDescription = "auto insert user";
|
||||
public Boolean isValidOpenhab() {
|
||||
if(this.getOpenhabaddress() == null || this.getOpenhabaddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
|
||||
whitelist.put(aUser, WhitelistEntry.createEntry(userDescription));
|
||||
setSettingsChanged(true);
|
||||
List<NamedIP> devicesList = this.getOpenhabaddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public String createWhitelistUser(String userDescription) {
|
||||
String aUser = getNewUserID();
|
||||
newWhitelistUser(aUser, userDescription);
|
||||
return aUser;
|
||||
public Boolean isValidFhem() {
|
||||
if(this.getFhemaddress() == null || this.getFhemaddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
|
||||
List<NamedIP> devicesList = this.getFhemaddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getNewUserID() {
|
||||
UUID uid = UUID.randomUUID();
|
||||
StringTokenizer st = new StringTokenizer(uid.toString(), "-");
|
||||
String newUser = "";
|
||||
while (st.hasMoreTokens()) {
|
||||
newUser = newUser + st.nextToken();
|
||||
}
|
||||
|
||||
return newUser;
|
||||
}
|
||||
|
||||
public String getInternalTestUser() {
|
||||
return DEFAULT_INTERNAL_USER;
|
||||
}
|
||||
|
||||
public void setupInternalTestUser() {
|
||||
boolean found = false;
|
||||
if(whitelist != null) {
|
||||
for (String key : whitelist.keySet()) {
|
||||
if(key.equals(DEFAULT_INTERNAL_USER)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
newWhitelistUser(DEFAULT_INTERNAL_USER, DEFAULT_USER_DESCRIPTION);
|
||||
}
|
||||
public Boolean isValidBroadlink() {
|
||||
return this.isBroadlinkconfigured();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.bwssystems.HABridge;
|
||||
|
||||
public class Configuration {
|
||||
public final static String DEVICE_DB_DIRECTORY = "data/device.db";
|
||||
public final static String GROUP_DB_DIRECTORY = "data/group.db";
|
||||
public final static String UPNP_RESPONSE_PORT = "50000";
|
||||
public final static String DEFAULT_ADDRESS = "1.1.1.1";
|
||||
public final static String LOOP_BACK_ADDRESS = "127.0.0.1";
|
||||
@@ -13,4 +14,7 @@ public class Configuration {
|
||||
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;
|
||||
public static final int UPNP_SEND_DELAY = 650;
|
||||
public static final int BROADLINK_DISCOVER_PORT = 40000;
|
||||
public static final int BROADLINK_DISCONVER_TIMEOUT = 5000;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ 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[] FIBARO_DEVICE = { "fibaroDevice", "Fibaro Device"};
|
||||
public final static String[] FIBARO_SCENE = { "fibaroScene", "Fibaro 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"};
|
||||
@@ -20,6 +22,7 @@ public class DeviceMapTypes {
|
||||
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[] HOMEWIZARD_DEVICE = { "homewizardDevice", "HomeWizard 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"};
|
||||
@@ -28,6 +31,9 @@ public class DeviceMapTypes {
|
||||
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 String[] OPENHAB_DEVICE = { "openhabDevice", "OpenHAB Device"};
|
||||
public final static String[] FHEM_DEVICE = { "fhemDevice", "FHEM Device"};
|
||||
public final static String[] BROADLINK_DEVICE = { "broadlinkDevice", "Broadlink Device"};
|
||||
|
||||
public final static int typeIndex = 0;
|
||||
public final static int displayIndex = 1;
|
||||
@@ -46,18 +52,23 @@ public class DeviceMapTypes {
|
||||
deviceMapTypes.add(HARMONY_ACTIVITY);
|
||||
deviceMapTypes.add(HARMONY_BUTTON);
|
||||
deviceMapTypes.add(HASS_DEVICE);
|
||||
deviceMapTypes.add(HOMEWIZARD_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(FIBARO_DEVICE);
|
||||
deviceMapTypes.add(FIBARO_SCENE);
|
||||
deviceMapTypes.add(SOMFY_DEVICE);
|
||||
deviceMapTypes.add(OPENHAB_DEVICE);
|
||||
deviceMapTypes.add(FHEM_DEVICE);
|
||||
deviceMapTypes.add(BROADLINK_DEVICE);
|
||||
}
|
||||
public static int getTypeIndex() {
|
||||
return typeIndex;
|
||||
|
||||
@@ -2,11 +2,14 @@ package com.bwssystems.HABridge;
|
||||
|
||||
import static spark.Spark.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.devicemanagmeent.*;
|
||||
import com.bwssystems.HABridge.hue.HueMulator;
|
||||
import com.bwssystems.HABridge.plugins.http.HttpClientPool;
|
||||
import com.bwssystems.HABridge.upnp.UpnpListener;
|
||||
import com.bwssystems.HABridge.upnp.UpnpSettingsResource;
|
||||
import com.bwssystems.HABridge.util.UDPDatagramSender;
|
||||
@@ -39,21 +42,26 @@ public class HABridge {
|
||||
SystemControl theSystem;
|
||||
BridgeSettings bridgeSettings;
|
||||
Version theVersion;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
HttpClientPool thePool;
|
||||
|
||||
log.info("HA Bridge startup sequence...");
|
||||
theVersion = new Version();
|
||||
// Singleton initialization
|
||||
thePool = new HttpClientPool();
|
||||
|
||||
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 initializing....");
|
||||
bridgeSettings.getBridgeSecurity().removeTestUsers();
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") initializing....");
|
||||
// sparkjava config directive to set ip address for the web server to listen on
|
||||
ipAddress(bridgeSettings.getBridgeSettingsDescriptor().getWebaddress());
|
||||
// sparkjava config directive to set port for the web server to listen on
|
||||
port(bridgeSettings.getBridgeSettingsDescriptor().getServerPort());
|
||||
staticFileLocation("/public");
|
||||
initExceptionHandler((e) -> HABridge.theExceptionHandler(e, bridgeSettings.getBridgeSettingsDescriptor().getServerPort()));
|
||||
if(!bridgeSettings.getBridgeControl().isReinit())
|
||||
init();
|
||||
bridgeSettings.getBridgeControl().setReinit(false);
|
||||
@@ -71,23 +79,35 @@ public class HABridge {
|
||||
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 = new HueMulator(bridgeSettings, theResources.getDeviceRepository(), theResources.getGroupRepository(), homeManager);
|
||||
theHueMulator.setupServer();
|
||||
// wait for the sparkjava initialization of the rest api classes to be complete
|
||||
awaitInitialization();
|
||||
|
||||
|
||||
if(bridgeSettings.getBridgeSettingsDescriptor().isTraceupnp())
|
||||
log.info("Traceupnp: upnp config address: " + bridgeSettings.getBridgeSettingsDescriptor().getUpnpConfigAddress() + "-useIface:" +
|
||||
bridgeSettings.getBridgeSettingsDescriptor().isUseupnpiface() + " on web server: " +
|
||||
bridgeSettings.getBridgeSettingsDescriptor().getWebaddress() + ":" + bridgeSettings.getBridgeSettingsDescriptor().getServerPort());
|
||||
// setup the class to handle the upnp response rest api
|
||||
theSettingResponder = new UpnpSettingsResource(bridgeSettings);
|
||||
theSettingResponder.setupServer();
|
||||
|
||||
// start the upnp ssdp discovery listener
|
||||
theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl(), udpSender);
|
||||
if(theUpnpListener.startListening())
|
||||
theUpnpListener = null;
|
||||
try {
|
||||
theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl(), udpSender);
|
||||
} catch (IOException e) {
|
||||
log.error("Could not initialize UpnpListener, exiting....", e);
|
||||
theUpnpListener = null;
|
||||
}
|
||||
if(theUpnpListener != null && theUpnpListener.startListening())
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") reinitialization requessted....");
|
||||
else
|
||||
bridgeSettings.getBridgeControl().setStop(true);
|
||||
if(bridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged())
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
log.info("Going to close all homes");
|
||||
homeManager.closeHomes();
|
||||
udpSender.closeResponseSocket();
|
||||
udpSender = null;
|
||||
@@ -97,12 +117,28 @@ public class HABridge {
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
log.error("Sleep error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
bridgeSettings.getBridgeSecurity().removeTestUsers();
|
||||
if(bridgeSettings.getBridgeSecurity().isSettingsChanged())
|
||||
bridgeSettings.updateConfigFile();
|
||||
try {
|
||||
HttpClientPool.shutdown();
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Error shutting down http pool: " + e.getMessage());;
|
||||
} catch (IOException e) {
|
||||
log.warn("Error shutting down http pool: " + e.getMessage());;
|
||||
}
|
||||
thePool = null;
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting....");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static void theExceptionHandler(Exception e, Integer thePort) {
|
||||
Logger log = LoggerFactory.getLogger(HABridge.class);
|
||||
log.error("Could not start ha-bridge webservice on port [" + thePort + "] due to: " + e.getMessage());
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,24 +4,33 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.devicemanagmeent.ResourceHandler;
|
||||
import com.bwssystems.HABridge.plugins.NestBridge.NestHome;
|
||||
import com.bwssystems.HABridge.plugins.broadlink.BroadlinkHome;
|
||||
import com.bwssystems.HABridge.plugins.domoticz.DomoticzHome;
|
||||
import com.bwssystems.HABridge.plugins.exec.CommandHome;
|
||||
import com.bwssystems.HABridge.plugins.fhem.FHEMHome;
|
||||
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.homewizard.HomeWizardHome;
|
||||
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.openhab.OpenHABHome;
|
||||
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.plugins.fibaro.FibaroHome;
|
||||
import com.bwssystems.HABridge.util.UDPDatagramSender;
|
||||
|
||||
public class HomeManager {
|
||||
private static final Logger log = LoggerFactory.getLogger(HomeManager.class);
|
||||
Map<String, Home> homeList;
|
||||
Map<String, Home> resourceList;
|
||||
|
||||
@@ -33,6 +42,14 @@ public class HomeManager {
|
||||
// factory method
|
||||
public void buildHomes(BridgeSettings bridgeSettings, UDPDatagramSender aUdpDatagramSender) {
|
||||
Home aHome = null;
|
||||
//setup the http handler Home - This must be the first home created for devMode
|
||||
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);
|
||||
homeList.put(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.FIBARO_SCENE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the harmony connection if available
|
||||
aHome = new HarmonyHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex], aHome);
|
||||
@@ -63,16 +80,14 @@ public class HomeManager {
|
||||
aHome = new HassHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
// Setup the HomeWizard configuration if available
|
||||
aHome = new HomeWizardHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.HOMEWIZARD_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);
|
||||
@@ -85,11 +100,15 @@ public class HomeManager {
|
||||
aHome = new VeraHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
|
||||
// Setup Fibaro Home if available
|
||||
aHome = new FibaroHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.FIBARO_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
|
||||
//setup the Somfy configuration if availableOPENHAB
|
||||
aHome = new SomfyHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
@@ -97,6 +116,18 @@ public class HomeManager {
|
||||
aHome = new LifxHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the OpenHAB configuration if available
|
||||
aHome = new OpenHABHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the OpenHAB configuration if available
|
||||
aHome = new FHEMHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the Broadlink configuration if available
|
||||
aHome = new BroadlinkHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.BROADLINK_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.BROADLINK_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
}
|
||||
|
||||
public Home findHome(String type) {
|
||||
@@ -107,8 +138,10 @@ public class HomeManager {
|
||||
}
|
||||
|
||||
public void closeHomes() {
|
||||
log.info("Manager close homes called....");
|
||||
Collection<Home> theHomes = homeList.values();
|
||||
for(Home aHome : theHomes) {
|
||||
log.info("Closing home: " + aHome.getClass().getCanonicalName());
|
||||
aHome.closeHome();
|
||||
}
|
||||
homeList.clear();
|
||||
|
||||
@@ -10,15 +10,18 @@ public class LinkButtonPressed extends TimerTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(LinkButtonPressed.class);
|
||||
private BridgeControlDescriptor linkDescriptor;
|
||||
private Timer myTimer;
|
||||
private boolean isSilent;
|
||||
|
||||
public LinkButtonPressed(BridgeControlDescriptor theDescriptor, Timer aTimer) {
|
||||
public LinkButtonPressed(BridgeControlDescriptor theDescriptor, Timer aTimer, boolean keepSilent) {
|
||||
linkDescriptor = theDescriptor;
|
||||
myTimer = aTimer;
|
||||
isSilent = keepSilent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
log.info("Link button time ended....");
|
||||
if(!isSilent)
|
||||
log.info("Link button time ended....");
|
||||
linkDescriptor.setLinkButton(false);
|
||||
myTimer.cancel();
|
||||
}
|
||||
|
||||
20
src/main/java/com/bwssystems/HABridge/LinkParams.java
Normal file
20
src/main/java/com/bwssystems/HABridge/LinkParams.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class LinkParams {
|
||||
private Integer seconds;
|
||||
private boolean silent;
|
||||
|
||||
public Integer getSeconds() {
|
||||
return seconds;
|
||||
}
|
||||
public void setSeconds(Integer seconds) {
|
||||
this.seconds = seconds;
|
||||
}
|
||||
public boolean isSilent() {
|
||||
return silent;
|
||||
}
|
||||
public void setSilent(boolean silent) {
|
||||
this.silent = silent;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class NamedIP {
|
||||
private String name;
|
||||
private String ip;
|
||||
private String webhook;
|
||||
private String ip;
|
||||
private String webhook;
|
||||
private String port;
|
||||
private String username;
|
||||
private String password;
|
||||
private JsonObject extensions;
|
||||
private Boolean secure;
|
||||
|
||||
public String getName() {
|
||||
@@ -51,4 +54,10 @@ public class NamedIP {
|
||||
public void setSecure(Boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
public JsonObject getExtensions() {
|
||||
return extensions;
|
||||
}
|
||||
public void setExtensions(JsonObject extensions) {
|
||||
this.extensions = extensions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.bwssystems.HABridge;
|
||||
|
||||
public class SecurityInfo {
|
||||
private boolean useLinkButton;
|
||||
private String execGarden;
|
||||
private boolean secureHueApi;
|
||||
private boolean isSecure;
|
||||
|
||||
@@ -12,12 +11,6 @@ public class SecurityInfo {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -8,19 +8,26 @@ import static spark.Spark.before;
|
||||
import static spark.Spark.halt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MulticastSocket;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
//import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Timer;
|
||||
import java.util.Base64;
|
||||
//import java.util.Iterator;
|
||||
//import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
//import com.bwssystems.HABridge.api.NameValue;
|
||||
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
|
||||
import com.bwssystems.HABridge.dao.BackupFilename;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.bwssystems.HABridge.util.TextStringFormatter;
|
||||
@@ -28,10 +35,12 @@ import com.bwssystems.logservices.LoggerInfo;
|
||||
import com.bwssystems.logservices.LoggingForm;
|
||||
import com.bwssystems.logservices.LoggingManager;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.classic.spi.LoggingEvent;
|
||||
//import ch.qos.logback.core.Appender;
|
||||
import ch.qos.logback.core.read.CyclicBufferAppender;
|
||||
|
||||
public class SystemControl {
|
||||
@@ -51,7 +60,7 @@ public class SystemControl {
|
||||
this.version = theVersion;
|
||||
this.lc = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
this.dateFormat = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss.SSS");
|
||||
reacquireCBA();
|
||||
setupLoggerSettings();
|
||||
theLogServiceMgr = new LoggingManager();
|
||||
theLogServiceMgr.init();
|
||||
}
|
||||
@@ -78,21 +87,13 @@ public class SystemControl {
|
||||
return "{\"version\":\"" + version.getVersion() + "\",\"isSecure\":" + bridgeSettings.getBridgeSecurity().isSecure() + "}";
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/habridge/testuser gets the valid test user for calling the api
|
||||
get (SYSTEM_CONTEXT + "/habridge/testuser", (request, response) -> {
|
||||
log.debug("Get HA Bridge testuser: " + bridgeSettings.getBridgeSettingsDescriptor().getInternalTestUser());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return "{\"user\":\"" + bridgeSettings.getBridgeSettingsDescriptor().getInternalTestUser() + "\"}";
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/logmsgs gets the log messages for the bridge
|
||||
get (SYSTEM_CONTEXT + "/logmsgs", (request, response) -> {
|
||||
log.debug("Get logmsgs.");
|
||||
String logMsgs;
|
||||
int count = -1;
|
||||
if(cyclicBufferAppender == null)
|
||||
reacquireCBA();
|
||||
setupLoggerSettings();
|
||||
if (cyclicBufferAppender != null) {
|
||||
count = cyclicBufferAppender.getLength();
|
||||
}
|
||||
@@ -253,10 +254,22 @@ public class SystemControl {
|
||||
});
|
||||
// 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....");
|
||||
LinkParams linkParams = null;
|
||||
if(!request.body().isEmpty()) {
|
||||
linkParams = new Gson().fromJson(request.body(), LinkParams.class);
|
||||
if(linkParams.getSeconds() <= 0)
|
||||
linkParams.setSeconds(1);
|
||||
}
|
||||
else {
|
||||
linkParams = new LinkParams();
|
||||
linkParams.setSilent(false);
|
||||
linkParams.setSeconds(30);
|
||||
}
|
||||
if(!linkParams.isSilent())
|
||||
log.info("Link button pressed....");
|
||||
bridgeSettings.getBridgeControl().setLinkButton(true);
|
||||
Timer theTimer = new Timer();
|
||||
theTimer.schedule(new LinkButtonPressed(bridgeSettings.getBridgeControl(), theTimer), 30000);
|
||||
theTimer.schedule(new LinkButtonPressed(bridgeSettings.getBridgeControl(), theTimer, linkParams.isSilent()), (linkParams.getSeconds() * 1000));
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return "";
|
||||
@@ -283,8 +296,6 @@ public class SystemControl {
|
||||
post(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
|
||||
log.debug("changesecurityinfo....");
|
||||
SecurityInfo theInfo = new Gson().fromJson(request.body(), SecurityInfo.class);
|
||||
if(theInfo.getExecGarden() != null)
|
||||
bridgeSettings.getBridgeSecurity().setExecGarden(theInfo.getExecGarden());
|
||||
bridgeSettings.getBridgeSecurity().setUseLinkButton(theInfo.isUseLinkButton());
|
||||
bridgeSettings.getBridgeSecurity().setSecureHueApi(theInfo.isSecureHueApi());
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
@@ -293,6 +304,36 @@ public class SystemControl {
|
||||
return bridgeSettings.getBridgeSecurity().getSecurityInfo();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/whitelist gets the whitelist for the bridge
|
||||
get (SYSTEM_CONTEXT + "/whitelist", (request, response) -> {
|
||||
log.debug("Get whitelist");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSecurity().getWhitelist();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/setwhitelist CORS request
|
||||
options(SYSTEM_CONTEXT + "/setwhitelist", (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/setwhitelist which sets the whitelist after being managed
|
||||
post(SYSTEM_CONTEXT + "/setwhitelist", (request, response) -> {
|
||||
log.debug("setwhitelist....");
|
||||
Type listType = new TypeToken<Map<String, WhitelistEntry>>() {
|
||||
}.getType();
|
||||
Map<String, WhitelistEntry> aWhitelist = new Gson().fromJson(request.body(), listType);
|
||||
bridgeSettings.getBridgeSecurity().setWhitelist(aWhitelist);
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSecurity().getWhitelist();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/logmgmt/update CORS request
|
||||
options(SYSTEM_CONTEXT + "/logmgmt/update", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
@@ -320,6 +361,8 @@ public class SystemControl {
|
||||
log.debug("bridge settings requested from " + request.ip());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
// if(bridgeSettings.getBridgeSettingsDescriptor().getActiveloggers() == null)
|
||||
// bridgeSettings.getBridgeSettingsDescriptor().setActiveloggers(getLogAppenders());
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
|
||||
@@ -336,6 +379,8 @@ public class SystemControl {
|
||||
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);
|
||||
if(newBridgeSettings.getUpnpsenddelay() > 15000)
|
||||
newBridgeSettings.setUpnpsenddelay(15000);
|
||||
bridgeSettings.save(newBridgeSettings);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
@@ -446,11 +491,42 @@ public class SystemControl {
|
||||
}, new JsonTransformer());
|
||||
}
|
||||
|
||||
void reacquireCBA() {
|
||||
cyclicBufferAppender = (CyclicBufferAppender<ILoggingEvent>) lc.getLogger(
|
||||
private void setupLoggerSettings() {
|
||||
// final ch.qos.logback.classic.Logger logger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
|
||||
|
||||
cyclicBufferAppender = (CyclicBufferAppender<ILoggingEvent>) lc.getLogger(
|
||||
Logger.ROOT_LOGGER_NAME).getAppender(CYCLIC_BUFFER_APPENDER_NAME);
|
||||
cyclicBufferAppender.setMaxSize(bridgeSettings.getBridgeSettingsDescriptor().getNumberoflogmessages());
|
||||
}
|
||||
// if(bridgeSettings.getBridgeSettingsDescriptor().getActiveloggers() != null) {
|
||||
// for (NameValue temp : bridgeSettings.getBridgeSettingsDescriptor().getActiveloggers()) {
|
||||
// if(temp.getValue().equals("false"))
|
||||
// logger.detachAppender(temp.getName());
|
||||
// }
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
// private List<NameValue> getLogAppenders() {
|
||||
// final ch.qos.logback.classic.Logger logger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
|
||||
//
|
||||
// final Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders();
|
||||
//
|
||||
// List<NameValue> theLoggers = new ArrayList<NameValue>();
|
||||
//
|
||||
// while (it.hasNext()) {
|
||||
//
|
||||
// final Appender<ILoggingEvent> appender = it.next();
|
||||
//
|
||||
// if (!(appender instanceof CyclicBufferAppender)) {
|
||||
// NameValue theLogger = new NameValue();
|
||||
// theLogger.setName(appender.getName());
|
||||
// theLogger.setValue("true");
|
||||
// theLoggers.add(theLogger);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return theLoggers;
|
||||
// }
|
||||
|
||||
protected void pingListener() {
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.dao.GroupDescriptor;
|
||||
|
||||
/**
|
||||
* Created by arm on 4/14/15.
|
||||
@@ -14,6 +15,8 @@ public class DeviceResponse {
|
||||
private String luminaireuniqueid;
|
||||
private String uniqueid;
|
||||
private String swversion;
|
||||
private String swconfigid;
|
||||
private String productid;
|
||||
|
||||
public DeviceState getState() {
|
||||
return state;
|
||||
@@ -71,6 +74,23 @@ public class DeviceResponse {
|
||||
this.swversion = swversion;
|
||||
}
|
||||
|
||||
public String getSwconfigid() {
|
||||
return swconfigid;
|
||||
}
|
||||
|
||||
public void setSwconfigid(String swconfigid) {
|
||||
this.swconfigid = swconfigid;
|
||||
}
|
||||
|
||||
public String getProductid() {
|
||||
return productid;
|
||||
}
|
||||
|
||||
public void setProductid(String productid) {
|
||||
this.productid = productid;
|
||||
}
|
||||
|
||||
|
||||
public String getLuminaireuniqueid() {
|
||||
return luminaireuniqueid;
|
||||
}
|
||||
@@ -86,11 +106,41 @@ public class DeviceResponse {
|
||||
response.setName(device.getName());
|
||||
response.setUniqueid(device.getUniqueid());
|
||||
response.setManufacturername("Philips");
|
||||
response.setType("Dimmable light");
|
||||
response.setModelid("LWB004");
|
||||
response.setSwversion("66012040");
|
||||
|
||||
if (device.isColorDevice()) {
|
||||
response.setType("Extended color light");
|
||||
response.setModelid("LCT010");
|
||||
response.setSwversion("1.15.2_r19181");
|
||||
response.setSwconfigid("F921C859");
|
||||
response.setProductid("Philips-LCT010-1-A19ECLv4");
|
||||
} else {
|
||||
response.setType("Dimmable light");
|
||||
response.setModelid("LWB007");
|
||||
response.setSwversion("66012040");
|
||||
}
|
||||
|
||||
response.setLuminaireuniqueid(null);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public static DeviceResponse createResponseForVirtualLight(GroupDescriptor group){
|
||||
DeviceResponse response = new DeviceResponse();
|
||||
response.setState(group.getAction());
|
||||
|
||||
response.setName(group.getName());
|
||||
response.setUniqueid("00:17:88:5E:D3:FF-" + String.format("%02X", Integer.parseInt(group.getId())));
|
||||
response.setManufacturername("Philips");
|
||||
response.setType("Extended color light");
|
||||
response.setModelid("LCT010");
|
||||
response.setSwversion("1.15.2_r19181");
|
||||
response.setSwconfigid("F921C859");
|
||||
response.setProductid("Philips-LCT010-1-A19ECLv4");
|
||||
|
||||
response.setLuminaireuniqueid(null);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
// import java.util.ArrayList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -9,14 +9,15 @@ import java.util.List;
|
||||
public class DeviceState {
|
||||
private boolean on;
|
||||
private int bri;
|
||||
private int hue;
|
||||
private int sat;
|
||||
private Integer hue;
|
||||
private Integer sat;
|
||||
private String effect;
|
||||
private int ct;
|
||||
private List<Double> xy;
|
||||
private Integer ct;
|
||||
private String alert;
|
||||
private String colormode;
|
||||
private boolean reachable;
|
||||
private List<Double> xy;
|
||||
|
||||
// private int transitiontime;
|
||||
|
||||
public boolean isOn() {
|
||||
@@ -36,19 +37,21 @@ public class DeviceState {
|
||||
}
|
||||
|
||||
public int getHue() {
|
||||
return hue;
|
||||
return hue != null ? hue.intValue() : 0;
|
||||
}
|
||||
|
||||
public void setHue(int hue) {
|
||||
this.hue = hue;
|
||||
this.colormode = "hs";
|
||||
}
|
||||
|
||||
public int getSat() {
|
||||
return sat;
|
||||
return sat != null ? sat.intValue() : 0;
|
||||
}
|
||||
|
||||
public void setSat(int sat) {
|
||||
this.sat = sat;
|
||||
this.colormode = "hs";
|
||||
}
|
||||
|
||||
public String getEffect() {
|
||||
@@ -60,11 +63,12 @@ public class DeviceState {
|
||||
}
|
||||
|
||||
public int getCt() {
|
||||
return ct;
|
||||
return ct != null ? ct.intValue() : 0;
|
||||
}
|
||||
|
||||
public void setCt(int ct) {
|
||||
this.ct = ct;
|
||||
this.colormode = "ct";
|
||||
}
|
||||
|
||||
public String getAlert() {
|
||||
@@ -97,6 +101,7 @@ public class DeviceState {
|
||||
|
||||
public void setXy(List<Double> xy) {
|
||||
this.xy = xy;
|
||||
this.colormode = "xy";
|
||||
}
|
||||
// public int getTransitiontime() {
|
||||
// return transitiontime;
|
||||
@@ -106,22 +111,28 @@ public class DeviceState {
|
||||
// this.transitiontime = transitiontime;
|
||||
// }
|
||||
|
||||
public static DeviceState createDeviceState() {
|
||||
public static DeviceState createDeviceState(boolean color) {
|
||||
DeviceState newDeviceState = new DeviceState();
|
||||
newDeviceState.fillIn();
|
||||
// newDeviceState.setColormode("none");
|
||||
// ArrayList<Double> doubleArray = new ArrayList<Double>();
|
||||
// doubleArray.add(new Double(0));
|
||||
// doubleArray.add(new Double(0));
|
||||
// newDeviceState.setXy(doubleArray);
|
||||
|
||||
newDeviceState.fillIn(color);
|
||||
if (color) {
|
||||
newDeviceState.setColormode("xy");
|
||||
newDeviceState.setHue(0);
|
||||
newDeviceState.setSat(0);
|
||||
newDeviceState.setCt(153);
|
||||
ArrayList<Double> doubleArray = new ArrayList<Double>();
|
||||
doubleArray.add(0.3146);
|
||||
doubleArray.add(0.3303);
|
||||
newDeviceState.setXy(doubleArray);
|
||||
}
|
||||
return newDeviceState;
|
||||
}
|
||||
public void fillIn() {
|
||||
public void fillIn(boolean color) {
|
||||
if(this.getAlert() == null)
|
||||
this.setAlert("none");
|
||||
if(this.getEffect() == null)
|
||||
this.setEffect("none");
|
||||
if (color) {
|
||||
if(this.getEffect() == null)
|
||||
this.setEffect("none");
|
||||
}
|
||||
this.setReachable(true);
|
||||
}
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GroupClassTypes {
|
||||
|
||||
public final static String BATHROOM = "Bathroom";
|
||||
public final static String BEDROOM = "Bedroom";
|
||||
public final static String CARPORT = "Carport";
|
||||
public final static String DINING = "Dining";
|
||||
public final static String DRIVEWAY = "Driveway";
|
||||
public final static String FRONT_DOOR = "Front door";
|
||||
public final static String GARAGE = "Garage";
|
||||
public final static String GARDEN = "Garden";
|
||||
public final static String GYM = "Gym";
|
||||
public final static String HALLWAY = "Hallway";
|
||||
public final static String BEDROOM_KIDS = "Kids bedroom";
|
||||
public final static String KITCHEN = "Kitchen";
|
||||
public final static String LIVING_ROOM = "Living room";
|
||||
public final static String NURSERY = "Nursery";
|
||||
public final static String OFFICE = "Office";
|
||||
public final static String OTHER = "Other";
|
||||
public final static String RECREATION = "Recreation";
|
||||
public final static String TERRACE = "Terrace";
|
||||
public final static String TOILET = "Toilet";
|
||||
|
||||
ArrayList<String> groupClassTypes;
|
||||
|
||||
public GroupClassTypes() {
|
||||
groupClassTypes = new ArrayList<String>();
|
||||
groupClassTypes.add(BATHROOM);
|
||||
groupClassTypes.add(BEDROOM);
|
||||
groupClassTypes.add(CARPORT);
|
||||
groupClassTypes.add(DINING);
|
||||
groupClassTypes.add(DRIVEWAY);
|
||||
groupClassTypes.add(FRONT_DOOR);
|
||||
groupClassTypes.add(GARAGE);
|
||||
groupClassTypes.add(GARDEN);
|
||||
groupClassTypes.add(GYM);
|
||||
groupClassTypes.add(HALLWAY);
|
||||
groupClassTypes.add(BEDROOM_KIDS);
|
||||
groupClassTypes.add(KITCHEN);
|
||||
groupClassTypes.add(LIVING_ROOM);
|
||||
groupClassTypes.add(NURSERY);
|
||||
groupClassTypes.add(OFFICE);
|
||||
groupClassTypes.add(OTHER);
|
||||
groupClassTypes.add(RECREATION);
|
||||
groupClassTypes.add(TERRACE);
|
||||
groupClassTypes.add(TOILET);
|
||||
}
|
||||
|
||||
public Boolean validateType(String type) {
|
||||
if(type == null || type.trim().isEmpty())
|
||||
return false;
|
||||
for(String classType : groupClassTypes) {
|
||||
if(type.trim().contentEquals(classType))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.dao.GroupDescriptor;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class GroupResponse {
|
||||
@@ -16,6 +16,8 @@ public class GroupResponse {
|
||||
private String type;
|
||||
@SerializedName("class")
|
||||
String class_name;
|
||||
@SerializedName("state")
|
||||
private GroupState state;
|
||||
|
||||
public DeviceState getAction() {
|
||||
return action;
|
||||
@@ -23,6 +25,14 @@ public class GroupResponse {
|
||||
public void setAction(DeviceState action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public GroupState getState() {
|
||||
return state;
|
||||
}
|
||||
public void setState(GroupState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String[] getLights() {
|
||||
return lights;
|
||||
}
|
||||
@@ -48,34 +58,69 @@ public class GroupResponse {
|
||||
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");
|
||||
|
||||
public static GroupResponse createDefaultGroupResponse(Map<String, DeviceResponse> deviceList) {
|
||||
String[] theList = new String[deviceList.size()];
|
||||
Boolean all_on = true;
|
||||
Boolean any_on = false;
|
||||
int i = 0;
|
||||
for (Map.Entry<String, DeviceResponse> device : deviceList.entrySet()) {
|
||||
if (Integer.parseInt(device.getKey()) >= 10000) { // don't show fake lights for other groups
|
||||
continue;
|
||||
}
|
||||
theList[i] = device.getKey();
|
||||
Boolean is_on = device.getValue().getState().isOn();
|
||||
if (is_on)
|
||||
any_on = true;
|
||||
else
|
||||
all_on = false;
|
||||
i++;
|
||||
}
|
||||
GroupResponse theResponse = new GroupResponse();
|
||||
theResponse.setAction(DeviceState.createDeviceState(true));
|
||||
theResponse.setState(new GroupState(all_on, any_on));
|
||||
theResponse.setName("Group 0");
|
||||
theResponse.setLights(theList);
|
||||
theResponse.setType("LightGroup");
|
||||
return theResponse;
|
||||
}
|
||||
|
||||
return theResponse;
|
||||
}
|
||||
public static GroupResponse createResponse(GroupDescriptor group, Map<String, DeviceResponse> lights){
|
||||
GroupResponse response = new GroupResponse();
|
||||
Boolean all_on = true;
|
||||
Boolean any_on = false;
|
||||
String[] groupLights = null;
|
||||
if (lights == null) {
|
||||
all_on = false;
|
||||
groupLights = group.getLights();
|
||||
} else {
|
||||
for (DeviceResponse light : lights.values()) {
|
||||
Boolean is_on = light.getState().isOn();
|
||||
if (is_on)
|
||||
any_on = true;
|
||||
else
|
||||
all_on = false;
|
||||
}
|
||||
|
||||
// group.getLights() is not filtered by requester, lights is
|
||||
// we want the filtered version but keep the order from group.getLights()
|
||||
groupLights = new String[lights.size()];
|
||||
int i = 0;
|
||||
for (String light : group.getLights()) {
|
||||
if (lights.keySet().contains(light)) {
|
||||
groupLights[i] = light;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.setState(new GroupState(all_on, any_on));
|
||||
response.setAction(group.getAction());
|
||||
response.setName(group.getName());
|
||||
response.setType(group.getGroupType());
|
||||
response.setLights(groupLights);
|
||||
response.setClass_name(group.getGroupClass());
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
/**
|
||||
* Created by Florian Foerderreuther on 07/23/17
|
||||
*/
|
||||
public class GroupState {
|
||||
private boolean all_on;
|
||||
private boolean any_on;
|
||||
|
||||
public boolean isAllOn() {
|
||||
return all_on;
|
||||
}
|
||||
|
||||
public boolean isAnyOn() {
|
||||
return any_on;
|
||||
}
|
||||
|
||||
public void setState(boolean all_on, boolean any_on) {
|
||||
this.all_on = all_on;
|
||||
this.any_on = any_on;
|
||||
}
|
||||
|
||||
public GroupState(boolean all_on, boolean any_on) {
|
||||
this.all_on = all_on;
|
||||
this.any_on = any_on;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GroupState{" +
|
||||
"all_on=" + all_on +
|
||||
", any_on=" + any_on +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,9 @@ public class HueApiResponse {
|
||||
private Map<String, JsonObject> rules;
|
||||
private HueConfig config;
|
||||
|
||||
public HueApiResponse(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist, String emulateHubVersion) {
|
||||
public HueApiResponse(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist, String emulateHubVersion, boolean isLinkButtonPressed, String emulateMAC) {
|
||||
super();
|
||||
this.setConfig(HueConfig.createConfig(name, ipaddress, awhitelist, emulateHubVersion));
|
||||
this.setConfig(HueConfig.createConfig(name, ipaddress, awhitelist, emulateHubVersion, isLinkButtonPressed, emulateMAC));
|
||||
this.setRules(new HashMap<>());
|
||||
this.setSensors(new HashMap<>());
|
||||
this.setSchedules(new HashMap<>());
|
||||
|
||||
@@ -34,7 +34,7 @@ public class HueConfig
|
||||
private String replacesbridgeid;
|
||||
private Map<String, WhitelistEntry> whitelist;
|
||||
|
||||
public static HueConfig createConfig(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist, String emulateHubVersion) {
|
||||
public static HueConfig createConfig(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist, String emulateHubVersion, boolean isLinkButtonPressed, String emulateMAC) {
|
||||
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");
|
||||
@@ -44,7 +44,7 @@ public class HueConfig
|
||||
aConfig.setPortalservices(false);
|
||||
aConfig.setGateway(ipaddress);
|
||||
aConfig.setSwversion(emulateHubVersion);
|
||||
aConfig.setLinkbutton(true);
|
||||
aConfig.setLinkbutton(isLinkButtonPressed);
|
||||
aConfig.setIpaddress(ipaddress);
|
||||
aConfig.setProxyport(0);
|
||||
aConfig.setSwupdate(Swupdate.createSwupdate());
|
||||
@@ -56,7 +56,7 @@ public class HueConfig
|
||||
aConfig.setLocaltime(dateFormat.format(new Date()));
|
||||
aConfig.setTimezone(TimeZone.getDefault().getID());
|
||||
aConfig.setZigbeechannel("6");
|
||||
aConfig.setBridgeid(HuePublicConfig.createConfig(name, ipaddress, emulateHubVersion).getHueBridgeIdFromMac());
|
||||
aConfig.setBridgeid(HuePublicConfig.createConfig(name, ipaddress, emulateHubVersion, emulateMAC).getHueBridgeIdFromMac());
|
||||
aConfig.setModelid(HueConstants.MODEL_ID);
|
||||
aConfig.setFactorynew(false);
|
||||
aConfig.setReplacesbridgeid(null);
|
||||
|
||||
@@ -1,8 +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 HUB_VERSION = "9999999999";
|
||||
public final static String API_VERSION = "1.19.0";
|
||||
public final static String MODEL_ID = "BSB002";
|
||||
public final static String UUID_PREFIX = "2f402f80-da50-11e1-9b23-";
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ public class HuePublicConfig
|
||||
private Boolean factorynew;
|
||||
private String modelid;
|
||||
|
||||
public static HuePublicConfig createConfig(String name, String ipaddress, String emulateHubVersion) {
|
||||
public static HuePublicConfig createConfig(String name, String ipaddress, String emulateHubVersion, String emulateMAC) {
|
||||
HuePublicConfig aConfig = new HuePublicConfig();
|
||||
aConfig.setMac(HuePublicConfig.getMacAddress(ipaddress));
|
||||
aConfig.setMac(HuePublicConfig.getMacAddress(ipaddress, emulateMAC));
|
||||
aConfig.setApiversion(HueConstants.API_VERSION);
|
||||
aConfig.setSwversion(emulateHubVersion);
|
||||
aConfig.setName(name);
|
||||
@@ -32,34 +32,39 @@ public class HuePublicConfig
|
||||
return aConfig;
|
||||
}
|
||||
|
||||
private static String getMacAddress(String addr)
|
||||
private static String getMacAddress(String addr, String aMAC)
|
||||
{
|
||||
InetAddress ip;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
if(aMAC == null || aMAC.trim().length() <= 0) {
|
||||
try {
|
||||
|
||||
ip = InetAddress.getByName(addr);
|
||||
|
||||
ip = InetAddress.getByName(addr);
|
||||
|
||||
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
|
||||
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) {
|
||||
|
||||
byte[] mac = network.getHardwareAddress();
|
||||
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");
|
||||
|
||||
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");
|
||||
|
||||
}
|
||||
else {
|
||||
sb.append(aMAC.trim());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
||||
@@ -38,6 +38,9 @@ public class DeviceDescriptor{
|
||||
@SerializedName("onUrl")
|
||||
@Expose
|
||||
private String onUrl;
|
||||
@SerializedName("colorUrl")
|
||||
@Expose
|
||||
private String colorUrl;
|
||||
@SerializedName("headers")
|
||||
@Expose
|
||||
private String headers;
|
||||
@@ -74,9 +77,13 @@ public class DeviceDescriptor{
|
||||
@SerializedName("comments")
|
||||
@Expose
|
||||
private String comments;
|
||||
|
||||
@SerializedName("deviceState")
|
||||
@Expose
|
||||
private DeviceState deviceState;
|
||||
|
||||
@SerializedName("onFirstDim")
|
||||
@Expose
|
||||
private boolean onFirstDim;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -141,6 +148,14 @@ public class DeviceDescriptor{
|
||||
this.onUrl = onUrl;
|
||||
}
|
||||
|
||||
public String getColorUrl() {
|
||||
return colorUrl;
|
||||
}
|
||||
|
||||
public void setColorUrl(String colorUrl) {
|
||||
this.colorUrl = colorUrl;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -207,7 +222,7 @@ public class DeviceDescriptor{
|
||||
|
||||
public DeviceState getDeviceState() {
|
||||
if(deviceState == null)
|
||||
deviceState = DeviceState.createDeviceState();
|
||||
deviceState = DeviceState.createDeviceState(this.isColorDevice());
|
||||
return deviceState;
|
||||
}
|
||||
|
||||
@@ -263,6 +278,14 @@ public class DeviceDescriptor{
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
public boolean isOnFirstDim() {
|
||||
return onFirstDim;
|
||||
}
|
||||
|
||||
public void setOnFirstDim(boolean onFirstDim) {
|
||||
this.onFirstDim = onFirstDim;
|
||||
}
|
||||
|
||||
public boolean containsType(String aType) {
|
||||
if(aType == null)
|
||||
return false;
|
||||
@@ -281,7 +304,22 @@ public class DeviceDescriptor{
|
||||
|
||||
if(this.offUrl != null && this.offUrl.contains(aType))
|
||||
return true;
|
||||
|
||||
if(this.colorUrl != null && this.colorUrl.contains(aType))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isColorDevice() {
|
||||
boolean color = true;
|
||||
if ((deviceType == null || !deviceType.trim().equals("passthru")) && (colorUrl == null || colorUrl.trim().equals(""))) {
|
||||
color = false;
|
||||
} else if (deviceType != null && deviceType.trim().equals("passthru")) {
|
||||
if (deviceState != null && (deviceState.getColormode() == null || deviceState.getColormode().equals(""))) {
|
||||
color = false;
|
||||
}
|
||||
}
|
||||
return color;
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,21 @@ import javax.xml.bind.DatatypeConverter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.plugins.hue.HueHome;
|
||||
import com.bwssystems.HABridge.util.BackupHandler;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
|
||||
/*
|
||||
* This is an in memory list to manage the configured devices and saves the list as a JSON string to a file for later
|
||||
* loading.
|
||||
@@ -61,6 +69,7 @@ public class DeviceRepository extends BackupHandler {
|
||||
{
|
||||
DeviceDescriptor list[] = gson.fromJson(jsonContent, DeviceDescriptor[].class);
|
||||
for(int i = 0; i < list.length; i++) {
|
||||
list[i].setDeviceState(null);
|
||||
put(list[i].getId(), list[i]);
|
||||
if(Integer.decode(list[i].getId()) > nextId) {
|
||||
nextId = Integer.decode(list[i].getId());
|
||||
@@ -85,6 +94,10 @@ public class DeviceRepository extends BackupHandler {
|
||||
|
||||
public List<DeviceDescriptor> findAllByRequester(String anAddress) {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
return findAllByRequester(anAddress, list);
|
||||
}
|
||||
|
||||
private List<DeviceDescriptor> findAllByRequester(String anAddress, Collection<DeviceDescriptor> list) {
|
||||
List<DeviceDescriptor> theReturnList = new ArrayList<DeviceDescriptor>();
|
||||
Iterator<DeviceDescriptor> anIterator = list.iterator();
|
||||
DeviceDescriptor theDevice;
|
||||
@@ -110,6 +123,45 @@ public class DeviceRepository extends BackupHandler {
|
||||
return theReturnList;
|
||||
}
|
||||
|
||||
public Map<String, DeviceResponse> findAllByGroupWithState(String[] lightsInGroup, String anAddress, HueHome myHueHome, Gson aGsonBuilder) {
|
||||
return findAllByGroupWithState(lightsInGroup, anAddress, myHueHome, aGsonBuilder, false);
|
||||
}
|
||||
|
||||
public Map<String, DeviceResponse> findAllByGroupWithState(String[] lightsInGroup, String anAddress, HueHome myHueHome, Gson aGsonBuilder, boolean ignoreAddress) {
|
||||
Map<String, DeviceResponse> deviceResponseMap = new HashMap<String, DeviceResponse>();
|
||||
Map<String, DeviceDescriptor> lights = new HashMap<String, DeviceDescriptor>(devices);
|
||||
lights.keySet().retainAll(Arrays.asList(lightsInGroup));
|
||||
for (DeviceDescriptor light : (ignoreAddress ? lights.values() : findAllByRequester(anAddress, lights.values()))) {
|
||||
DeviceResponse deviceResponse = null;
|
||||
if(!light.isInactive()) {
|
||||
if (light.containsType(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
|
||||
CallItem[] callItems = null;
|
||||
try {
|
||||
if(light.getOnUrl() != null)
|
||||
callItems = aGsonBuilder.fromJson(light.getOnUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
log.warn("Could not decode Json for url items to get Hue state for device: " + light.getName());
|
||||
callItems = null;
|
||||
}
|
||||
|
||||
for (int i = 0; callItems != null && i < callItems.length; i++) {
|
||||
if((callItems[i].getType() != null && callItems[i].getType().equals(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) ||
|
||||
(callItems[i].getItem() != null && callItems[i].getItem().getAsString() != null && callItems[i].getItem().getAsString().contains("hueName"))) {
|
||||
deviceResponse = myHueHome.getHueDeviceInfo(callItems[i], light);
|
||||
i = callItems.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceResponse == null) {
|
||||
deviceResponse = DeviceResponse.createResponse(light);
|
||||
}
|
||||
deviceResponseMap.put(light.getId(), deviceResponse);
|
||||
}
|
||||
}
|
||||
return (deviceResponseMap.size() == 0) ? null : deviceResponseMap;
|
||||
}
|
||||
|
||||
public DeviceDescriptor findOne(String id) {
|
||||
return devices.get(id);
|
||||
}
|
||||
|
||||
147
src/main/java/com/bwssystems/HABridge/dao/GroupDescriptor.java
Normal file
147
src/main/java/com/bwssystems/HABridge/dao/GroupDescriptor.java
Normal file
@@ -0,0 +1,147 @@
|
||||
package com.bwssystems.HABridge.dao;
|
||||
|
||||
import com.bwssystems.HABridge.api.hue.DeviceState;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.bwssystems.HABridge.api.hue.GroupState;
|
||||
|
||||
/*
|
||||
* Object to handle the device configuration
|
||||
*/
|
||||
public class GroupDescriptor{
|
||||
@SerializedName("id")
|
||||
@Expose
|
||||
private String id;
|
||||
@SerializedName("name")
|
||||
@Expose
|
||||
private String name;
|
||||
@SerializedName("groupType")
|
||||
@Expose
|
||||
private String groupType;
|
||||
@SerializedName("groupClass")
|
||||
@Expose
|
||||
private String groupClass;
|
||||
@SerializedName("requesterAddress")
|
||||
@Expose
|
||||
private String requesterAddress;
|
||||
@SerializedName("inactive")
|
||||
@Expose
|
||||
private boolean inactive;
|
||||
@SerializedName("description")
|
||||
@Expose
|
||||
private String description;
|
||||
@SerializedName("comments")
|
||||
@Expose
|
||||
private String comments;
|
||||
|
||||
private DeviceState action;
|
||||
private GroupState groupState;
|
||||
|
||||
@SerializedName("lights")
|
||||
@Expose
|
||||
private String[] lights;
|
||||
@SerializedName("exposeAsLight")
|
||||
@Expose
|
||||
private String exposeAsLight;
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getGroupType() {
|
||||
return groupType;
|
||||
}
|
||||
|
||||
public void setGroupType(String groupType) {
|
||||
this.groupType = groupType;
|
||||
}
|
||||
|
||||
public String getGroupClass() {
|
||||
return groupClass;
|
||||
}
|
||||
|
||||
public void setGroupClass(String groupClass) {
|
||||
this.groupClass = groupClass;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public GroupState getGroupState() {
|
||||
if(groupState == null)
|
||||
groupState = new GroupState(false,false);
|
||||
return groupState;
|
||||
}
|
||||
|
||||
public void setGroupState(GroupState groupState) {
|
||||
this.groupState = groupState;
|
||||
}
|
||||
|
||||
public DeviceState getAction() {
|
||||
if(action == null)
|
||||
action = DeviceState.createDeviceState(true);
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(DeviceState action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public boolean isInactive() {
|
||||
return inactive;
|
||||
}
|
||||
|
||||
public void setInactive(boolean inactive) {
|
||||
this.inactive = inactive;
|
||||
}
|
||||
|
||||
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 String[] getLights() {
|
||||
return lights;
|
||||
}
|
||||
|
||||
public void setLights(String[] lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
|
||||
public void setExposeAsLight(String exposeFor) {
|
||||
this.exposeAsLight = exposeFor;
|
||||
}
|
||||
|
||||
public String getExposeAsLight() {
|
||||
return exposeAsLight;
|
||||
}
|
||||
}
|
||||
220
src/main/java/com/bwssystems/HABridge/dao/GroupRepository.java
Normal file
220
src/main/java/com/bwssystems/HABridge/dao/GroupRepository.java
Normal file
@@ -0,0 +1,220 @@
|
||||
package com.bwssystems.HABridge.dao;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.dao.GroupDescriptor;
|
||||
import com.bwssystems.HABridge.util.BackupHandler;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import java.util.List;
|
||||
/*
|
||||
* This is an in memory list to manage the configured groups and saves the list as a JSON string to a file for later
|
||||
* loading.
|
||||
*/
|
||||
public class GroupRepository extends BackupHandler {
|
||||
private Map<String, GroupDescriptor> groups;
|
||||
private Path repositoryPath;
|
||||
private Gson gson;
|
||||
private Integer nextId;
|
||||
private Logger log = LoggerFactory.getLogger(GroupRepository.class);
|
||||
|
||||
public GroupRepository(String groupDb) {
|
||||
super();
|
||||
gson =
|
||||
new GsonBuilder()
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.create();
|
||||
nextId = 0;
|
||||
try {
|
||||
repositoryPath = null;
|
||||
repositoryPath = Paths.get(groupDb);
|
||||
setupParams(repositoryPath, ".bk", "group.db-");
|
||||
_loadRepository(repositoryPath);
|
||||
} catch (Exception ex) {
|
||||
groups = new HashMap<String, GroupDescriptor>();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadRepository() {
|
||||
if(repositoryPath != null)
|
||||
_loadRepository(repositoryPath);
|
||||
}
|
||||
private void _loadRepository(Path aPath){
|
||||
String jsonContent = repositoryReader(aPath);
|
||||
groups = new HashMap<String, GroupDescriptor>();
|
||||
|
||||
if(jsonContent != null)
|
||||
{
|
||||
GroupDescriptor list[] = gson.fromJson(jsonContent, GroupDescriptor[].class);
|
||||
for(int i = 0; i < list.length; i++) {
|
||||
list[i].setGroupState(null);
|
||||
put(list[i].getId(), list[i]);
|
||||
if(Integer.decode(list[i].getId()) > nextId) {
|
||||
nextId = Integer.decode(list[i].getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<GroupDescriptor> findAll() {
|
||||
List<GroupDescriptor> list = new ArrayList<GroupDescriptor>(groups.values());
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<GroupDescriptor> findActive() {
|
||||
List<GroupDescriptor> list = new ArrayList<GroupDescriptor>();
|
||||
for(GroupDescriptor aGroup : new ArrayList<GroupDescriptor>(groups.values())) {
|
||||
if(!aGroup.isInactive())
|
||||
list.add(aGroup);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<GroupDescriptor> findAllByRequester(String anAddress) {
|
||||
List<GroupDescriptor> list = new ArrayList<GroupDescriptor>(groups.values());
|
||||
List<GroupDescriptor> theReturnList = new ArrayList<GroupDescriptor>();
|
||||
Iterator<GroupDescriptor> anIterator = list.iterator();
|
||||
GroupDescriptor theGroup;
|
||||
String theRequesterAddress;
|
||||
|
||||
HashMap<String,String > addressMap;
|
||||
while (anIterator.hasNext()) {
|
||||
theGroup = anIterator.next();
|
||||
theRequesterAddress = theGroup.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(theGroup);
|
||||
}
|
||||
return theReturnList;
|
||||
}
|
||||
|
||||
public List<GroupDescriptor> findVirtualLights(String anAddress) {
|
||||
List<GroupDescriptor> list = new ArrayList<GroupDescriptor>();
|
||||
for (GroupDescriptor group : groups.values()) {
|
||||
String expose = group.getExposeAsLight();
|
||||
if (expose != null && expose.contains(anAddress)) {
|
||||
list.add(group);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public GroupDescriptor findOne(String id) {
|
||||
return groups.get(id);
|
||||
}
|
||||
|
||||
private void put(String id, GroupDescriptor aDescriptor) {
|
||||
groups.put(id, aDescriptor);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
save(groups.values().toArray(new GroupDescriptor[0]));
|
||||
}
|
||||
|
||||
public void save(GroupDescriptor[] descriptors) {
|
||||
String theNames = "";
|
||||
for(int i = 0; i < descriptors.length; i++) {
|
||||
if(descriptors[i].getId() != null && descriptors[i].getId().length() > 0)
|
||||
groups.remove(descriptors[i].getId());
|
||||
else {
|
||||
nextId++;
|
||||
descriptors[i].setId(String.valueOf(nextId));
|
||||
}
|
||||
|
||||
put(descriptors[i].getId(), descriptors[i]);
|
||||
theNames = theNames + " " + descriptors[i].getName() + ", ";
|
||||
}
|
||||
String jsonValue = gson.toJson(findAll());
|
||||
repositoryWriter(jsonValue, repositoryPath);
|
||||
log.debug("Save group(s): " + theNames);
|
||||
}
|
||||
|
||||
public Integer getNewId() {
|
||||
return nextId + 1;
|
||||
}
|
||||
|
||||
public String delete(GroupDescriptor aDescriptor) {
|
||||
if (aDescriptor != null) {
|
||||
groups.remove(aDescriptor.getId());
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
String jsonValue = aRenderer.render(findAll());
|
||||
repositoryWriter(jsonValue, repositoryPath);
|
||||
return "Group with id '" + aDescriptor.getId() + "' deleted";
|
||||
} else {
|
||||
return "Group not found";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void repositoryWriter(String content, Path filePath) {
|
||||
if(Files.exists(filePath) && !Files.isWritable(filePath)){
|
||||
log.error("Error file is not writable: " + filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Files.notExists(filePath.getParent())) {
|
||||
try {
|
||||
Files.createDirectories(filePath.getParent());
|
||||
} catch (IOException e) {
|
||||
log.error("Error creating the directory: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Path target = null;
|
||||
if(Files.exists(filePath)) {
|
||||
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "group.db.old");
|
||||
Files.move(filePath, target);
|
||||
}
|
||||
Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE);
|
||||
if(target != null)
|
||||
Files.delete(target);
|
||||
} catch (IOException e) {
|
||||
log.error("Error writing the file: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String repositoryReader(Path filePath) {
|
||||
|
||||
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) {
|
||||
log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -25,6 +25,7 @@ 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.GroupRepository;
|
||||
import com.bwssystems.HABridge.dao.ErrorMessage;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
@@ -38,6 +39,7 @@ public class DeviceResource {
|
||||
private static final String API_CONTEXT = "/api/devices";
|
||||
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
|
||||
private DeviceRepository deviceRepository;
|
||||
private GroupRepository groupRepository;
|
||||
private HomeManager homeManager;
|
||||
private BridgeSettings bridgeSettings;
|
||||
private Gson aGsonHandler;
|
||||
@@ -46,6 +48,7 @@ public class DeviceResource {
|
||||
public DeviceResource(BridgeSettings theSettings, HomeManager aHomeManager) {
|
||||
bridgeSettings = theSettings;
|
||||
this.deviceRepository = new DeviceRepository(bridgeSettings.getBridgeSettingsDescriptor().getUpnpDeviceDb());
|
||||
this.groupRepository = new GroupRepository(bridgeSettings.getBridgeSettingsDescriptor().getUpnpGroupDb());
|
||||
homeManager = aHomeManager;
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
setupEndpoints();
|
||||
@@ -55,6 +58,10 @@ public class DeviceResource {
|
||||
return deviceRepository;
|
||||
}
|
||||
|
||||
public GroupRepository getGroupRepository() {
|
||||
return groupRepository;
|
||||
}
|
||||
|
||||
private void setupEndpoints() {
|
||||
log.info("HABridge device management service started.... ");
|
||||
before(API_CONTEXT + "/*", (request, response) -> {
|
||||
@@ -84,6 +91,7 @@ public class DeviceResource {
|
||||
else {
|
||||
devices = new Gson().fromJson("[" + request.body() + "]", DeviceDescriptor[].class);
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
CallItem[] callItems = null;
|
||||
String errorMessage = null;
|
||||
for(int i = 0; i < devices.length; i++) {
|
||||
@@ -122,6 +130,15 @@ public class DeviceResource {
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
try {
|
||||
if(devices[i].getColorUrl() != null && !devices[i].getColorUrl().isEmpty())
|
||||
callItems = aGsonHandler.fromJson(devices[i].getColorUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "Bad color URL JSON in create device(s) for name: " + devices[i].getName() + " with color URL: " + devices[i].getColorUrl();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
deviceRepository.save(devices);
|
||||
@@ -214,6 +231,18 @@ public class DeviceResource {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/fibaro/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get fibaro devices");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/fibaro/scenes", "application/json", (request, response) -> {
|
||||
log.debug("Get fibaro scenes");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.FIBARO_SCENE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/harmony/activities", "application/json", (request, response) -> {
|
||||
log.debug("Get harmony activities");
|
||||
@@ -262,6 +291,12 @@ public class DeviceResource {
|
||||
return homeManager.findResource(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/homewizard/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get HomeWizard Clients");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/domoticz/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get Domoticz Clients");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
@@ -280,11 +315,37 @@ public class DeviceResource {
|
||||
return homeManager.findResource(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/openhab/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get OpenHAB devices");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/fhem/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get FHEM devices");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/broadlink/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get Broadlink devices");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.BROADLINK_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.BROADLINK_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());
|
||||
|
||||
get (API_CONTEXT + "/refresh/:typeIndex", "application/json", (request, response) -> {
|
||||
String typeIndex = request.params(":typeIndex");
|
||||
log.debug("Refresh Home: " + typeIndex);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
homeManager.findResource(typeIndex).refresh();
|
||||
return null;
|
||||
}, 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);
|
||||
|
||||
@@ -2,4 +2,5 @@ package com.bwssystems.HABridge.devicemanagmeent;
|
||||
|
||||
public interface ResourceHandler {
|
||||
public Object getItems(String type);
|
||||
public void refresh();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ 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;
|
||||
@@ -83,7 +84,7 @@ public class BrightnessDecode {
|
||||
replaceTarget = INTENSITY_PERCENT_HEX;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_DECIMAL_PERCENT)) {
|
||||
replaceValue = String.format("%1.2f", decimalBrightness);
|
||||
replaceValue = String.format(Locale.ROOT, "%1.2f", decimalBrightness);
|
||||
replaceTarget = INTENSITY_DECIMAL_PERCENT;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_MATH_CLOSE)) {
|
||||
@@ -110,11 +111,7 @@ public class BrightnessDecode {
|
||||
|
||||
Integer endResult = calculateMath(variables, mathDescriptor);
|
||||
if(endResult != null) {
|
||||
if (isHex) {
|
||||
replaceValue = convertToHex(endResult);
|
||||
} else {
|
||||
replaceValue = endResult.toString();
|
||||
}
|
||||
replaceValue = convertToHex(endResult);
|
||||
replaceTarget = INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE_HEX;
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
22
src/main/java/com/bwssystems/HABridge/hue/ColorData.java
Normal file
22
src/main/java/com/bwssystems/HABridge/hue/ColorData.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
public class ColorData {
|
||||
public enum ColorMode { XY, CT, HS}
|
||||
|
||||
private ColorMode mode;
|
||||
private Object data;
|
||||
|
||||
public ColorData(ColorMode mode, Object value) {
|
||||
this.mode = mode;
|
||||
this.data = value;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public ColorMode getColorMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +1,274 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.hue.ColorData;
|
||||
|
||||
public class ColorDecode {
|
||||
private static final Logger log = LoggerFactory.getLogger(ColorDecode.class);
|
||||
private static final String COLOR_R = "${color.r}";
|
||||
private static final String COLOR_G = "${color.g}";
|
||||
private static final String COLOR_B = "${color.b}";
|
||||
private static final String COLOR_RX = "${color.rx}";
|
||||
private static final String COLOR_GX = "${color.gx}";
|
||||
private static final String COLOR_BX = "${color.bx}";
|
||||
private static final String COLOR_RGBX = "${color.rgbx}";
|
||||
private static final Pattern COLOR_MILIGHT = Pattern.compile("\\$\\{color.milight\\:([01234])\\}");
|
||||
|
||||
public static String convertCIEtoRGB(List<Double> xy) {
|
||||
double x;
|
||||
double y;
|
||||
double Y;
|
||||
public static List<Integer> convertCIEtoRGB(List<Double> xy, int brightness) {
|
||||
List<Integer> rgb;
|
||||
double x = xy.get(0); // the given x value
|
||||
double y = xy.get(1); // the given y value
|
||||
double z = 1.0 - x - y;
|
||||
double Y = (double)brightness/(double)254.00; // The given brightness value
|
||||
double X = (Y / y) * x;
|
||||
double Z = (Y / y) * z;
|
||||
|
||||
double r = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
|
||||
double g = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
|
||||
double b = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
|
||||
|
||||
if (r > b && r > g && r > 1.0) {
|
||||
|
||||
g = g / r;
|
||||
b = b / r;
|
||||
r = 1.0;
|
||||
}
|
||||
else if (g > b && g > r && g > 1.0) {
|
||||
|
||||
r = r / g;
|
||||
b = b / g;
|
||||
g = 1.0;
|
||||
}
|
||||
else if (b > r && b > g && b > 1.0) {
|
||||
|
||||
r = r / b;
|
||||
g = g / b;
|
||||
b = 1.0;
|
||||
}
|
||||
|
||||
|
||||
r = r <= 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055;
|
||||
g = g <= 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055;
|
||||
b = b <= 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055;
|
||||
|
||||
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);
|
||||
if (r > b && r > g) {
|
||||
// red is biggest
|
||||
if (r > 1.0) {
|
||||
g = g / r;
|
||||
b = b / r;
|
||||
r = 1.0;
|
||||
}
|
||||
}
|
||||
else if (g > b && g > r) {
|
||||
// green is biggest
|
||||
if (g > 1.0) {
|
||||
r = r / g;
|
||||
b = b / g;
|
||||
g = 1.0;
|
||||
}
|
||||
}
|
||||
else if (b > r && b > g) {
|
||||
// blue is biggest
|
||||
if (b > 1.0) {
|
||||
r = r / b;
|
||||
g = g / b;
|
||||
b = 1.0;
|
||||
}
|
||||
}
|
||||
if(r < 0.0)
|
||||
r = 0;
|
||||
if(g < 0.0)
|
||||
g = 0;
|
||||
if(b < 0.0)
|
||||
b = 0;
|
||||
|
||||
rgb = new ArrayList<Integer>();
|
||||
rgb.add((int)Math.round(r * 255));
|
||||
rgb.add((int)Math.round(g * 255));
|
||||
rgb.add((int)Math.round(b * 255));
|
||||
log.debug("Color change with XY: " + x + " " + y + " Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + rgb.get(2));
|
||||
return rgb;
|
||||
}
|
||||
|
||||
// took that approximation from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
|
||||
public static List<Integer> convertCTtoRGB(Integer ct) {
|
||||
double temperature = 1000000.0 / (double)ct;
|
||||
temperature /= 100;
|
||||
double r,g,b;
|
||||
if (temperature <= 66) {
|
||||
r = 255;
|
||||
g = temperature;
|
||||
g = 99.4708025861 * Math.log(g) - 161.1195681661;
|
||||
} else {
|
||||
r = temperature - 60;
|
||||
r = 329.698727446 * (Math.pow(r, -0.1332047592));
|
||||
g = temperature - 60;
|
||||
g = 288.1221695283 * (Math.pow(g, -0.0755148492));
|
||||
}
|
||||
|
||||
if (temperature >= 66) {
|
||||
b = 255;
|
||||
} else {
|
||||
if (temperature <= 19) {
|
||||
b = 0;
|
||||
} else {
|
||||
b = temperature - 10;
|
||||
b = 138.5177312231 * Math.log(b) - 305.0447927307;
|
||||
}
|
||||
}
|
||||
r = assureBounds(r);
|
||||
g = assureBounds(g);
|
||||
b = assureBounds(b);
|
||||
List<Integer> rgb = new ArrayList<Integer>();
|
||||
rgb.add((int)Math.round(r));
|
||||
rgb.add((int)Math.round(g));
|
||||
rgb.add((int)Math.round(b));
|
||||
log.debug("Color change with CT: " + ct + ". Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + rgb.get(2));
|
||||
return rgb;
|
||||
}
|
||||
|
||||
private static double assureBounds(double value) {
|
||||
if (value < 0.0) {
|
||||
value = 0;
|
||||
}
|
||||
if (value > 255.0) {
|
||||
value = 255;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static String replaceColorData(String request, ColorData colorData, int setIntensity, boolean isHex) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
if (colorData == null) {
|
||||
return request;
|
||||
}
|
||||
boolean notDone = true;
|
||||
ColorData.ColorMode colorMode = colorData.getColorMode();
|
||||
List<Integer> rgb = null;
|
||||
if (colorMode == ColorData.ColorMode.XY) {
|
||||
rgb = convertCIEtoRGB((List<Double>)colorData.getData(), setIntensity);
|
||||
} else if (colorMode == ColorData.ColorMode.CT) {
|
||||
rgb = convertCTtoRGB((Integer)colorData.getData());
|
||||
}
|
||||
|
||||
return null;
|
||||
while(notDone) {
|
||||
notDone = false;
|
||||
if (request.contains(COLOR_R)) {
|
||||
request = request.replace(COLOR_R, isHex ? String.format("%02X", rgb.get(0)) : String.valueOf(rgb.get(0)));
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(COLOR_G)) {
|
||||
request = request.replace(COLOR_G, isHex ? String.format("%02X", rgb.get(1)) : String.valueOf(rgb.get(1)));
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(COLOR_B)) {
|
||||
request = request.replace(COLOR_B, isHex ? String.format("%02X", rgb.get(2)) : String.valueOf(rgb.get(2)));
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(COLOR_RX)) {
|
||||
request = request.replace(COLOR_RX, String.format("%02X", rgb.get(0)));
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(COLOR_GX)) {
|
||||
request = request.replace(COLOR_GX, String.format("%02X", rgb.get(1)));
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(COLOR_BX)) {
|
||||
request = request.replace(COLOR_BX, String.format("%02X", rgb.get(2)));
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(COLOR_RGBX)) {
|
||||
request = request.replace(COLOR_RGBX, String.format("%02X%02X%02X", rgb.get(0), rgb.get(1), rgb.get(2)));
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
Matcher m = COLOR_MILIGHT.matcher(request);
|
||||
while (m.find()) {
|
||||
int group = Integer.parseInt(m.group(1));
|
||||
request = m.replaceFirst(getMilightV5FromRgb(rgb, group));
|
||||
m.reset(request);
|
||||
}
|
||||
|
||||
log.debug("Request <<" + request + ">>, not done: " + notDone);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private static String getMilightV5FromRgb(List<Integer> rgb, int group) {
|
||||
double r = (double)rgb.get(0);
|
||||
double g = (double)rgb.get(1);
|
||||
double b = (double)rgb.get(2);
|
||||
if (r > 245 && g > 245 && b > 245) { // it's white
|
||||
String retVal = "";
|
||||
if (group == 0) {
|
||||
retVal += "C2";
|
||||
} else if (group == 1) {
|
||||
retVal += "C5";
|
||||
} else if (group == 2) {
|
||||
retVal += "C7";
|
||||
} else if (group == 3) {
|
||||
retVal += "C9";
|
||||
} else if (group == 4) {
|
||||
retVal += "CB";
|
||||
}
|
||||
log.debug("Convert RGB to Milight. Result: WHITE. RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + rgb.get(2));
|
||||
return retVal + "0055";
|
||||
} else { // normal color
|
||||
r /= (double)0xFF;
|
||||
g /= (double)0xFF;
|
||||
b /= (double)0xFF;
|
||||
double max = Math.max(Math.max(r, g), b), min = Math.min(Math.min(r, g), b);
|
||||
double h = 0;
|
||||
double d = max - min;
|
||||
|
||||
if (max == min) {
|
||||
h = 0;
|
||||
} else {
|
||||
if (max == r) {
|
||||
h = ((g - b) / d + (g < b ? 6 : 0));
|
||||
} else if (max == g) {
|
||||
h = ((b - r) / d + 2);
|
||||
} else if (max == b){
|
||||
h = ((r - g) / d + 4);
|
||||
}
|
||||
h = Math.round(h * 60);
|
||||
}
|
||||
int milight = (int)((256 + 176 - Math.floor(h / 360.0 * 255.0)) % 256);
|
||||
log.debug("Convert RGB to Milight. Result: " + milight + " RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + rgb.get(2));
|
||||
return "40" + String.format("%02X", milight) + "55";
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static int getIntRGB(ColorData colorData, int setIntensity) {
|
||||
ColorData.ColorMode colorMode = colorData.getColorMode();
|
||||
List<Integer> rgb = null;
|
||||
if (colorMode == ColorData.ColorMode.XY) {
|
||||
rgb = convertCIEtoRGB((List<Double>) colorData.getData(), setIntensity);
|
||||
} else if (colorMode == ColorData.ColorMode.CT) {
|
||||
rgb = convertCTtoRGB((Integer) colorData.getData());
|
||||
}
|
||||
|
||||
int rgbIntVal = Integer.parseInt(String.format("%02X%02X%02X", rgb.get(0), rgb.get(1), rgb.get(2)), 16);
|
||||
log.debug("Convert RGB to int. Result: " + rgbIntVal + " RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " "
|
||||
+ rgb.get(2));
|
||||
return rgbIntVal;
|
||||
}
|
||||
}
|
||||
|
||||
75
src/main/java/com/bwssystems/HABridge/hue/ColorMap.java
Normal file
75
src/main/java/com/bwssystems/HABridge/hue/ColorMap.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
public class ColorMap {
|
||||
private double red;
|
||||
private double green;
|
||||
private double blue;
|
||||
private long R;
|
||||
private long G;
|
||||
private long B;
|
||||
private double X;
|
||||
private double Y;
|
||||
private Double Z;
|
||||
private double z;
|
||||
public double getRed() {
|
||||
return red;
|
||||
}
|
||||
public void setRed(double red) {
|
||||
this.red = red;
|
||||
}
|
||||
public double getGreen() {
|
||||
return green;
|
||||
}
|
||||
public void setGreen(double green) {
|
||||
this.green = green;
|
||||
}
|
||||
public double getBlue() {
|
||||
return blue;
|
||||
}
|
||||
public void setBlue(double blue) {
|
||||
this.blue = blue;
|
||||
}
|
||||
public long getR() {
|
||||
return R;
|
||||
}
|
||||
public void setR(long r) {
|
||||
R = r;
|
||||
}
|
||||
public long getG() {
|
||||
return G;
|
||||
}
|
||||
public void setG(long g) {
|
||||
G = g;
|
||||
}
|
||||
public long getB() {
|
||||
return B;
|
||||
}
|
||||
public void setB(long b) {
|
||||
B = b;
|
||||
}
|
||||
public double getX() {
|
||||
return X;
|
||||
}
|
||||
public void setX(double x) {
|
||||
X = x;
|
||||
}
|
||||
public double getY() {
|
||||
return Y;
|
||||
}
|
||||
public void setY(double y) {
|
||||
Y = y;
|
||||
}
|
||||
public Double getZ() {
|
||||
return Z;
|
||||
}
|
||||
public void setZ(Double z) {
|
||||
Z = z;
|
||||
}
|
||||
public double getz() {
|
||||
return z;
|
||||
}
|
||||
public void setz(double z) {
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,9 @@ public class DeviceDataDecode {
|
||||
private static final String DEVICE_MAPTYPE = "${device.mapType}";
|
||||
private static final String DEVICE_DEVICETYPE = "${device.deviceType}";
|
||||
private static final String DEVICE_TARGETDEVICE = "${device.targetDevice}";
|
||||
private static final String DEVICE_REQUESTERADDRESS = "${device.requesterAddress}";
|
||||
private static final String DEVICE_DESCRIPTION = "${device.description}";
|
||||
private static final String DEVICE_COMMENTS = "${device.comments}";
|
||||
|
||||
public static String replaceDeviceData(String request, DeviceDescriptor device) {
|
||||
if (request == null) {
|
||||
@@ -58,6 +61,21 @@ public class DeviceDataDecode {
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_REQUESTERADDRESS)) {
|
||||
request = request.replace(DEVICE_REQUESTERADDRESS, device.getRequesterAddress());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_DESCRIPTION)) {
|
||||
request = request.replace(DEVICE_DESCRIPTION, device.getDescription());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_COMMENTS)) {
|
||||
request = request.replace(DEVICE_COMMENTS, device.getComments());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
log.debug("Request <<" + request + ">>, not done: " + notDone);
|
||||
}
|
||||
return request;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,8 @@ package com.bwssystems.HABridge.hue;
|
||||
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.ColorData;
|
||||
|
||||
public interface HueMulatorHandler {
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body);
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ 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 TIMESTAMP = "${time.millis}";
|
||||
private static final String TIME_FORMAT_CLOSE = ")}";
|
||||
|
||||
/*
|
||||
@@ -38,6 +39,10 @@ public class TimeDecode {
|
||||
log.warn("Could not format current time: " + timeFormatDescriptor, e);
|
||||
}
|
||||
}
|
||||
if (request.contains(TIMESTAMP)) {
|
||||
request = request.replace(TIMESTAMP, String.valueOf(System.currentTimeMillis()));
|
||||
notDone = true;
|
||||
}
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ 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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.nest.controller.Home;
|
||||
import com.bwssystems.nest.controller.Nest;
|
||||
@@ -31,10 +32,13 @@ public class NestHome implements com.bwssystems.HABridge.Home {
|
||||
private Gson aGsonHandler;
|
||||
private Boolean isFarenheit;
|
||||
private Boolean validNest;
|
||||
private boolean closed;
|
||||
|
||||
public NestHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,17 +96,23 @@ public class NestHome implements com.bwssystems.HABridge.Home {
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
if(theSession != null) {
|
||||
theNest.endNestSession();
|
||||
}
|
||||
theNest = null;
|
||||
theSession = null;
|
||||
nestItems = null;
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Integer targetBri,Integer targetBriInc, ColorData colorData, 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) {
|
||||
@@ -187,5 +197,10 @@ public class NestHome implements com.bwssystems.HABridge.Home {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.bwssystems.HABridge.plugins.broadlink;
|
||||
|
||||
public class BroadlinkEntry {
|
||||
private String name;
|
||||
private String id;
|
||||
private String ipAddr;
|
||||
private String macAddr;
|
||||
private String command;
|
||||
private String data;
|
||||
private String type;
|
||||
private String baseType;
|
||||
private String desc;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public String getIpAddr() {
|
||||
return ipAddr;
|
||||
}
|
||||
public void setIpAddr(String ipAddr) {
|
||||
this.ipAddr = ipAddr;
|
||||
}
|
||||
public String getMacAddr() {
|
||||
return macAddr;
|
||||
}
|
||||
public void setMacAddr(String macAddr) {
|
||||
this.macAddr = macAddr;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
public String getBaseType() {
|
||||
return baseType;
|
||||
}
|
||||
public void setBaseType(String baseType) {
|
||||
this.baseType = baseType;
|
||||
}
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
public boolean hasIpAndMac() {
|
||||
boolean deviceOk = true;
|
||||
if(ipAddr == null || ipAddr.trim().isEmpty())
|
||||
deviceOk = false;
|
||||
else if(macAddr == null || macAddr.trim().isEmpty())
|
||||
deviceOk = false;
|
||||
else if(type == null || type.trim().isEmpty())
|
||||
deviceOk = false;
|
||||
return deviceOk;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
package com.bwssystems.HABridge.plugins.broadlink;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.BindException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.Configuration;
|
||||
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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.ColorDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.github.mob41.blapi.BLDevice;
|
||||
import com.github.mob41.blapi.MP1Device;
|
||||
import com.github.mob41.blapi.SP1Device;
|
||||
import com.github.mob41.blapi.SP2Device;
|
||||
import com.github.mob41.blapi.mac.Mac;
|
||||
import com.github.mob41.blapi.mac.MacFormatException;
|
||||
import com.github.mob41.blapi.pkt.cmd.rm2.SendDataCmdPayload;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class BroadlinkHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(BroadlinkHome.class);
|
||||
private Map<String, BLDevice> broadlinkMap;
|
||||
private Boolean validBroadlink;
|
||||
private boolean closed;
|
||||
private Boolean isDevMode;
|
||||
private BridgeSettingsDescriptor bridgeSettingsDesc;
|
||||
|
||||
public BroadlinkHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
broadlinkMap = null;
|
||||
bridgeSettingsDesc = bridgeSettings.getBridgeSettingsDescriptor();
|
||||
validBroadlink = bridgeSettings.getBridgeSettingsDescriptor().isValidBroadlink();
|
||||
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
|
||||
if (isDevMode)
|
||||
validBroadlink = true;
|
||||
|
||||
if(validBroadlink)
|
||||
broadlinkDiscover();
|
||||
|
||||
log.info("Broadlink Home created." + (validBroadlink ? "" : " No Broadlinks configured.") + (isDevMode ? " DevMode is set." : ""));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
List<BroadlinkEntry> deviceList = new ArrayList<BroadlinkEntry>();
|
||||
if(!validBroadlink || broadlinkMap == null)
|
||||
return deviceList;
|
||||
BroadlinkEntry theResponse = null;
|
||||
log.debug("consolidating devices for Broadlink");
|
||||
Iterator<String> keys = broadlinkMap.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = toEntry(broadlinkMap.get(key));
|
||||
if(theResponse != null)
|
||||
deviceList.add(theResponse);
|
||||
else {
|
||||
log.warn("Cannot get BroadlinkDevice with name: " + key + ", skipping this Broadlink.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
if(validBroadlink)
|
||||
broadlinkDiscover();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
String theReturn = null;
|
||||
boolean changeState = false;
|
||||
String theStringData = null;
|
||||
log.debug("executing HUE api request to send message to BroadlinkDevice: " + anItem.getItem().toString());
|
||||
if(!validBroadlink) {
|
||||
log.warn("Should not get here, no Broadlinks configured");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no LifxDevices configured\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
|
||||
} else {
|
||||
BroadlinkEntry broadlinkCommand = null;
|
||||
broadlinkCommand = new Gson().fromJson(anItem.getItem().getAsString(), BroadlinkEntry.class);
|
||||
BLDevice theDevice = null;
|
||||
if(broadlinkMap != null && !broadlinkMap.isEmpty())
|
||||
theDevice = broadlinkMap.get(broadlinkCommand.getId());
|
||||
|
||||
if (theDevice == null) {
|
||||
if(broadlinkCommand.hasIpAndMac()) {
|
||||
byte[] intBytes = DatatypeConverter.parseHexBinary(broadlinkCommand.getType());
|
||||
BigInteger theBig = new BigInteger(intBytes);
|
||||
int theType = theBig.intValue();
|
||||
try {
|
||||
theDevice = BLDevice.createInstance((short)theType, broadlinkCommand.getIpAddr(), new Mac(broadlinkCommand.getMacAddr()));
|
||||
} catch (MacFormatException e) {
|
||||
log.warn("Could not initialize BroadlinkDevice device due to Mac (" + broadlinkCommand.getId() + ") format exception: " + e.getMessage());
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Could not initialize BroadlinkDevice device due to Mac format exception\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not initialize BroadlinkDevice device due to IP Address (" + broadlinkCommand.getId() + ") exception: " + e.getMessage());
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Could not initialize BroadlinkDevice device due to IP Address exception\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not initialize BroadlinkDevice device due to (" + broadlinkCommand.getId() + ") exception: " + e.getMessage());
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Could not initialize BroadlinkDevice device due to exception\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
}
|
||||
|
||||
if(broadlinkMap == null)
|
||||
broadlinkMap = new HashMap<String, BLDevice>();
|
||||
|
||||
String newId = theDevice.getHost() + "-" + String.format("%04x", theDevice.getDeviceType());
|
||||
if(broadlinkMap.get(newId) == null)
|
||||
broadlinkMap.put(newId, theDevice);
|
||||
}
|
||||
}
|
||||
if (theDevice == null) {
|
||||
log.warn("Should not get here, no BroadlinkDevice not available");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no Broadlinks available\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
log.debug("calling BroadlinkDevice: " + broadlinkCommand.getName());
|
||||
try {
|
||||
if(!isDevMode) {
|
||||
if(!theDevice.auth()) {
|
||||
log.error("Call to " + broadlinkCommand.getId() + " device authorization failed.");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"" + broadlinkCommand.getId() + " device auth error.\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
}
|
||||
}
|
||||
switch (theDevice.getDeviceType()) {
|
||||
case BLDevice.DEV_A1:
|
||||
log.debug("Broadlink A1 device called and not supported. No Action, device name = " + device.getName() + ", id= " + broadlinkCommand.getId());
|
||||
break;
|
||||
case BLDevice.DEV_MP1:
|
||||
if(broadlinkCommand.getCommand().equals("on"))
|
||||
changeState = true;
|
||||
else
|
||||
changeState = false;
|
||||
((MP1Device) theDevice).setState(Integer.parseInt(broadlinkCommand.getData()), changeState);
|
||||
break;
|
||||
case BLDevice.DEV_SP2:
|
||||
case BLDevice.DEV_SP2_HONEYWELL_ALT1:
|
||||
case BLDevice.DEV_SP2_HONEYWELL_ALT2:
|
||||
case BLDevice.DEV_SP2_HONEYWELL_ALT3:
|
||||
case BLDevice.DEV_SP2_HONEYWELL_ALT4:
|
||||
case BLDevice.DEV_SPMINI:
|
||||
case BLDevice.DEV_SP3:
|
||||
case BLDevice.DEV_SPMINI2:
|
||||
case BLDevice.DEV_SPMINI_OEM_ALT1:
|
||||
case BLDevice.DEV_SPMINI_OEM_ALT2:
|
||||
case BLDevice.DEV_SPMINI_PLUS:
|
||||
if(broadlinkCommand.getCommand().equals("on"))
|
||||
changeState = true;
|
||||
else
|
||||
changeState = false;
|
||||
((SP2Device) theDevice).setState(changeState);
|
||||
break;
|
||||
case BLDevice.DEV_SP1:
|
||||
if(broadlinkCommand.getCommand().equals("on"))
|
||||
changeState = true;
|
||||
else
|
||||
changeState = false;
|
||||
((SP1Device) theDevice).setPower(changeState);
|
||||
break;
|
||||
case BLDevice.DEV_RM_2:
|
||||
case BLDevice.DEV_RM_MINI:
|
||||
case BLDevice.DEV_RM_PRO_PHICOMM:
|
||||
case BLDevice.DEV_RM_2_HOME_PLUS:
|
||||
case BLDevice.DEV_RM_2_2HOME_PLUS_GDT:
|
||||
case BLDevice.DEV_RM_2_PRO_PLUS:
|
||||
case BLDevice.DEV_RM_2_PRO_PLUS_2:
|
||||
case BLDevice.DEV_RM_2_PRO_PLUS_2_BL:
|
||||
case BLDevice.DEV_RM_MINI_SHATE:
|
||||
if(broadlinkCommand.getData() != null && !broadlinkCommand.getData().trim().isEmpty()) {
|
||||
theStringData = broadlinkCommand.getData().trim();
|
||||
if(targetBri != null || targetBriInc != null) {
|
||||
theStringData = BrightnessDecode.calculateReplaceIntensityValue(theStringData, intensity, targetBri, targetBriInc, true);
|
||||
}
|
||||
if(colorData != null) {
|
||||
theStringData = ColorDecode.replaceColorData(theStringData, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), true);
|
||||
}
|
||||
theStringData = DeviceDataDecode.replaceDeviceData(theStringData, device);
|
||||
theStringData = TimeDecode.replaceTimeValue(theStringData);
|
||||
byte[] theData = DatatypeConverter.parseHexBinary(theStringData);
|
||||
SendDataCmdPayload thePayload = new SendDataCmdPayload(theData);
|
||||
|
||||
DatagramPacket thePacket = theDevice.sendCmdPkt(Configuration.BROADLINK_DISCONVER_TIMEOUT, thePayload);
|
||||
String returnData = null;
|
||||
if(thePacket != null)
|
||||
returnData = DatatypeConverter.printHexBinary(thePacket.getData());
|
||||
else
|
||||
returnData = "No Data - null";
|
||||
log.debug("RM2 Device data return: <<<" + returnData + ">>>");
|
||||
}
|
||||
else {
|
||||
log.error("Call to " + broadlinkCommand.getId() + " with no data, noop");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"" + broadlinkCommand.getId() + " could not call device without data.\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Call to " + broadlinkCommand.getId() + " device failed with exception: " + e.getMessage(), e);
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"" + broadlinkCommand.getId() + " device call error.\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return theReturn;
|
||||
}
|
||||
|
||||
private BroadlinkEntry toEntry(BLDevice broadlinkObject) {
|
||||
short baseType = 0;
|
||||
switch (broadlinkObject.getDeviceType()) {
|
||||
case BLDevice.DEV_MP1:
|
||||
baseType = BLDevice.DEV_MP1;
|
||||
break;
|
||||
case BLDevice.DEV_SP2:
|
||||
case BLDevice.DEV_SP2_HONEYWELL_ALT1:
|
||||
case BLDevice.DEV_SP2_HONEYWELL_ALT2:
|
||||
case BLDevice.DEV_SP2_HONEYWELL_ALT3:
|
||||
case BLDevice.DEV_SP2_HONEYWELL_ALT4:
|
||||
case BLDevice.DEV_SPMINI:
|
||||
case BLDevice.DEV_SP3:
|
||||
case BLDevice.DEV_SPMINI2:
|
||||
case BLDevice.DEV_SPMINI_OEM_ALT1:
|
||||
case BLDevice.DEV_SPMINI_OEM_ALT2:
|
||||
case BLDevice.DEV_SPMINI_PLUS:
|
||||
baseType = BLDevice.DEV_SP2;
|
||||
break;
|
||||
case BLDevice.DEV_SP1:
|
||||
baseType = BLDevice.DEV_SP1;
|
||||
break;
|
||||
case BLDevice.DEV_RM_2:
|
||||
case BLDevice.DEV_RM_MINI:
|
||||
case BLDevice.DEV_RM_PRO_PHICOMM:
|
||||
case BLDevice.DEV_RM_2_HOME_PLUS:
|
||||
case BLDevice.DEV_RM_2_2HOME_PLUS_GDT:
|
||||
case BLDevice.DEV_RM_2_PRO_PLUS:
|
||||
case BLDevice.DEV_RM_2_PRO_PLUS_2:
|
||||
case BLDevice.DEV_RM_2_PRO_PLUS_2_BL:
|
||||
case BLDevice.DEV_RM_MINI_SHATE:
|
||||
baseType = BLDevice.DEV_RM_2;
|
||||
break;
|
||||
}
|
||||
BroadlinkEntry anEntry = new BroadlinkEntry();
|
||||
anEntry.setId(broadlinkObject.getHost() + "-" + String.format("%04x", broadlinkObject.getDeviceType()));
|
||||
anEntry.setName(broadlinkObject.getDeviceDescription());
|
||||
anEntry.setType(String.format("%04x", broadlinkObject.getDeviceType()));
|
||||
anEntry.setBaseType(String.format("%04x", baseType));
|
||||
anEntry.setDesc(BLDevice.getDescOfType(broadlinkObject.getDeviceType()));
|
||||
anEntry.setIpAddr(broadlinkObject.getHost());
|
||||
anEntry.setMacAddr(broadlinkObject.getMac().getMacString());
|
||||
return anEntry;
|
||||
}
|
||||
|
||||
public BLDevice[] broadlinkDiscover () {
|
||||
BLDevice[] clients = null;
|
||||
int aDiscoverPort = Configuration.BROADLINK_DISCOVER_PORT;
|
||||
broadlinkMap = new HashMap<String, BLDevice>();
|
||||
while(aDiscoverPort > 0) {
|
||||
try {
|
||||
log.info("Broadlink discover....");
|
||||
if(isDevMode) {
|
||||
clients = TestBLDevice.discoverDevices(InetAddress.getByName(bridgeSettingsDesc.getUpnpConfigAddress()), aDiscoverPort, Configuration.BROADLINK_DISCONVER_TIMEOUT);
|
||||
}
|
||||
else
|
||||
clients = BLDevice.discoverDevices(InetAddress.getByName(bridgeSettingsDesc.getUpnpConfigAddress()), aDiscoverPort, Configuration.BROADLINK_DISCONVER_TIMEOUT);
|
||||
for(int i = 0; i < clients.length; i++) {
|
||||
if(clients[i].getDeviceType() != BLDevice.DEV_A1 && broadlinkMap.get(clients[i].getHost() + "-" + String.format("%04x", clients[i].getDeviceType())) == null) {
|
||||
broadlinkMap.put(clients[i].getHost() + "-" + String.format("%04x", clients[i].getDeviceType()), clients[i]);
|
||||
log.debug("Adding Device to Map - host: " + clients[i].getHost() + ", device Type: " + clients[i].getDeviceDescription() + ", mac: " + (clients[i].getMac() == null ? "no Mac in client" : clients[i].getMac().getMacString()));
|
||||
} else {
|
||||
log.debug("Ignoring Device (already in the list or an A1 Device) - host: " + clients[i].getHost() + ", device Type: " + clients[i].getDeviceDescription() + ", mac: " + (clients[i].getMac() == null ? "no Mac in client" : clients[i].getMac().getMacString()));
|
||||
}
|
||||
}
|
||||
aDiscoverPort = 0;
|
||||
} catch (BindException e) {
|
||||
log.warn("Could not discover Broadlinks, Port in use, increasing by 11");
|
||||
aDiscoverPort += 11;
|
||||
if(aDiscoverPort > Configuration.BROADLINK_DISCOVER_PORT + 110)
|
||||
aDiscoverPort = 0;
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not discover Broadlinks, with IO Exception", e);
|
||||
broadlinkMap = null;
|
||||
aDiscoverPort = 0;
|
||||
}
|
||||
}
|
||||
if(clients == null || clients.length <= 0) {
|
||||
log.warn("Did not discover any Broadlinks.");
|
||||
broadlinkMap = null;
|
||||
} else {
|
||||
log.info("Broadlink discover found " + clients.length + " clients.");
|
||||
}
|
||||
return clients;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validBroadlink)
|
||||
return;
|
||||
log.debug("Closing Home.");
|
||||
if(broadlinkMap != null) {
|
||||
broadlinkMap.clear();
|
||||
broadlinkMap = null;
|
||||
}
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.bwssystems.HABridge.plugins.broadlink;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.github.mob41.blapi.BLDevice;
|
||||
import com.github.mob41.blapi.mac.Mac;
|
||||
import com.github.mob41.blapi.pkt.CmdPayload;
|
||||
|
||||
public class TestBLDevice extends BLDevice {
|
||||
private static final Logger log = LoggerFactory.getLogger(TestBLDevice.class);
|
||||
|
||||
protected TestBLDevice(short deviceType, String deviceDesc, String host, Mac mac) throws IOException {
|
||||
super(deviceType, deviceDesc, host, mac);
|
||||
}
|
||||
|
||||
public void setState(boolean aState) {
|
||||
log.info("setState called with " + aState);
|
||||
}
|
||||
|
||||
public void setState(int anIndex, boolean aState) {
|
||||
log.info("setState called with index " + anIndex + " and state " + aState);
|
||||
}
|
||||
|
||||
public void setPower(boolean aState) {
|
||||
log.info("setPower called with " + aState);
|
||||
}
|
||||
|
||||
public DatagramPacket sendCmdPkt(int timeout, CmdPayload aCmd) {
|
||||
log.info("sendCmdPkt called with " + DatatypeConverter.printHexBinary(aCmd.getPayload().getData()));
|
||||
return null;
|
||||
}
|
||||
|
||||
public static BLDevice[] discoverDevices(InetAddress theAddress, int aport, int timeout) {
|
||||
TestMP1Device mp1Device = null;
|
||||
TestSP1Device sp1Device = null;
|
||||
TestSP2Device sp2Device = null;
|
||||
TestRM2Device rm2Device = null;
|
||||
try {
|
||||
mp1Device = new TestMP1Device("mp1host", null);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
sp1Device = new TestSP1Device("sp1host", null);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
sp2Device = new TestSP2Device("sp2host", null);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
rm2Device = new TestRM2Device("rm2host", null);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
BLDevice[] devices = { mp1Device, sp1Device, sp2Device, rm2Device };
|
||||
log.info("Created test devices");
|
||||
return devices;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.bwssystems.HABridge.plugins.broadlink;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.github.mob41.blapi.MP1Device;
|
||||
import com.github.mob41.blapi.mac.Mac;
|
||||
import com.github.mob41.blapi.pkt.CmdPayload;
|
||||
|
||||
public class TestMP1Device extends MP1Device {
|
||||
private static final Logger log = LoggerFactory.getLogger(TestMP1Device.class);
|
||||
|
||||
protected TestMP1Device(String host, Mac mac) throws IOException {
|
||||
super(host, mac);
|
||||
}
|
||||
|
||||
public void setState(int anIndex, boolean aState) {
|
||||
log.info("setState called with index " + anIndex + " and state " + aState);
|
||||
}
|
||||
|
||||
public DatagramPacket sendCmdPkt(int timeout, CmdPayload aCmd) {
|
||||
log.info("sendCmdPkt called with " + DatatypeConverter.printHexBinary(aCmd.getPayload().getData()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.bwssystems.HABridge.plugins.broadlink;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.github.mob41.blapi.RM2Device;
|
||||
import com.github.mob41.blapi.mac.Mac;
|
||||
import com.github.mob41.blapi.pkt.CmdPayload;
|
||||
import com.github.mob41.blapi.pkt.cmd.rm2.SendDataCmdPayload;
|
||||
|
||||
public class TestRM2Device extends RM2Device {
|
||||
private static final Logger log = LoggerFactory.getLogger(TestRM2Device.class);
|
||||
|
||||
protected TestRM2Device(String host, Mac mac) throws IOException {
|
||||
super(host, mac);
|
||||
}
|
||||
|
||||
public DatagramPacket sendCmdPkt(int timeout, CmdPayload aCmd) {
|
||||
log.info("sendCmdPkt called with " + DatatypeConverter.printHexBinary(((SendDataCmdPayload)aCmd).getData()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.bwssystems.HABridge.plugins.broadlink;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.github.mob41.blapi.SP1Device;
|
||||
import com.github.mob41.blapi.mac.Mac;
|
||||
import com.github.mob41.blapi.pkt.CmdPayload;
|
||||
|
||||
public class TestSP1Device extends SP1Device {
|
||||
private static final Logger log = LoggerFactory.getLogger(TestSP1Device.class);
|
||||
|
||||
protected TestSP1Device(String host, Mac mac) throws IOException {
|
||||
super(host, mac);
|
||||
}
|
||||
|
||||
public void setPower(boolean aState) {
|
||||
log.info("setPower called with " + aState);
|
||||
}
|
||||
|
||||
public DatagramPacket sendCmdPkt(int timeout, CmdPayload aCmd) {
|
||||
log.info("sendCmdPkt called with " + DatatypeConverter.printHexBinary(aCmd.getPayload().getData()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.bwssystems.HABridge.plugins.broadlink;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.github.mob41.blapi.SP2Device;
|
||||
import com.github.mob41.blapi.mac.Mac;
|
||||
import com.github.mob41.blapi.pkt.CmdPayload;
|
||||
|
||||
public class TestSP2Device extends SP2Device {
|
||||
private static final Logger log = LoggerFactory.getLogger(TestSP2Device.class);
|
||||
|
||||
protected TestSP2Device(String host, Mac mac) throws IOException {
|
||||
super(host, mac);
|
||||
}
|
||||
|
||||
public void setState(boolean aState) {
|
||||
log.info("setState called with " + aState);
|
||||
}
|
||||
|
||||
public DatagramPacket sendCmdPkt(int timeout, CmdPayload aCmd) {
|
||||
log.info("sendCmdPkt called with " + DatatypeConverter.printHexBinary(aCmd.getPayload().getData()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,13 @@ 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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.ColorDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHome;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class DomoticzHome implements Home {
|
||||
@@ -26,17 +31,20 @@ public class DomoticzHome implements Home {
|
||||
private Map<String, DomoticzHandler> domoticzs;
|
||||
private Boolean validDomoticz;
|
||||
private HTTPHandler httpClient;
|
||||
private boolean closed;
|
||||
|
||||
public DomoticzHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
if(!validDomoticz)
|
||||
return null;
|
||||
log.debug("consolidating devices for hues");
|
||||
log.debug("consolidating devices for Domoticzs");
|
||||
List<DomoticzDevice> theResponse = null;
|
||||
Iterator<String> keys = domoticzs.keySet().iterator();
|
||||
List<DomoticzDevice> deviceList = new ArrayList<DomoticzDevice>();
|
||||
@@ -58,6 +66,11 @@ public class DomoticzHome implements Home {
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
|
||||
private Boolean addDomoticzDevices(List<DomoticzDevice> theDeviceList, List<DomoticzDevice> theSourceList, String theKey) {
|
||||
if(!validDomoticz)
|
||||
return null;
|
||||
@@ -71,7 +84,7 @@ public class DomoticzHome implements Home {
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
Devices theDomoticzApiResponse = null;
|
||||
String responseString = null;
|
||||
|
||||
@@ -90,7 +103,23 @@ public class DomoticzHome implements Home {
|
||||
String theData;
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
theData = httpClient.doHttpRequest(theHandler.buildUrl(anUrl), null, null, null, theHandler.buildHeaders());
|
||||
if (colorData != null) {
|
||||
anUrl = ColorDecode.replaceColorData(anUrl, colorData, BrightnessDecode.calculateIntensity(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);
|
||||
if (colorData != null) {
|
||||
aBody = ColorDecode.replaceColorData(aBody, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
|
||||
}
|
||||
aBody = DeviceDataDecode.replaceDeviceData(aBody, device);
|
||||
aBody = TimeDecode.replaceTimeValue(aBody);
|
||||
}
|
||||
theData = httpClient.doHttpRequest(theHandler.buildUrl(anUrl), null, null, aBody, theHandler.buildHeaders());
|
||||
try {
|
||||
theDomoticzApiResponse = new Gson().fromJson(theData, Devices.class);
|
||||
if(theDomoticzApiResponse.getStatus().equals("OK"))
|
||||
@@ -128,7 +157,7 @@ public class DomoticzHome implements Home {
|
||||
log.info("Domoticz Home created." + (validDomoticz ? "" : " No Domoticz devices configured."));
|
||||
if(!validDomoticz)
|
||||
return null;
|
||||
httpClient = new HTTPHandler();
|
||||
httpClient = HTTPHome.getHandler();
|
||||
domoticzs = new HashMap<String, DomoticzHandler>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getDomoticzaddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
@@ -161,8 +190,16 @@ public class DomoticzHome implements Home {
|
||||
}
|
||||
@Override
|
||||
public void closeHome() {
|
||||
log.debug("Closing Home.");
|
||||
if(closed || !validDomoticz) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
|
||||
if(httpClient != null)
|
||||
httpClient.closeHandler();
|
||||
|
||||
|
||||
domoticzs = null;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bwssystems.HABridge.plugins.exec;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -10,36 +11,42 @@ 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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.ColorDecode;
|
||||
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 String execGarden;;
|
||||
private BridgeSettings theSettings;
|
||||
private boolean closed;
|
||||
|
||||
public CommandHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@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());
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, ColorData colorData, 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 = BrightnessDecode.calculateReplaceIntensityValue(intermediate, intensity, targetBri, targetBriInc, false);
|
||||
if (colorData != null) {
|
||||
intermediate = ColorDecode.replaceColorData(intermediate, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
|
||||
}
|
||||
intermediate = DeviceDataDecode.replaceDeviceData(intermediate, device);
|
||||
intermediate = TimeDecode.replaceTimeValue(intermediate);
|
||||
if(execGarden != null) {
|
||||
if(System.getProperty("os.name").toLowerCase().indexOf("win") > 0)
|
||||
intermediate = execGarden + "\\" + intermediate;
|
||||
else
|
||||
intermediate = execGarden + "/" + intermediate;
|
||||
String execGarden = theSettings.getBridgeSecurity().getExecGarden();
|
||||
if(execGarden != null && !execGarden.trim().isEmpty()) {
|
||||
intermediate = new File(execGarden.trim(), intermediate).getAbsolutePath();
|
||||
}
|
||||
|
||||
String anError = doExecRequest(intermediate, lightId);
|
||||
@@ -57,7 +64,7 @@ public class CommandHome implements Home {
|
||||
Process p = Runtime.getRuntime().exec(anItem);
|
||||
log.debug("Process running: " + p.isAlive());
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not execute request: " + anItem, 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\"}}]";
|
||||
@@ -75,7 +82,7 @@ public class CommandHome implements Home {
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
log.info("Command Home for system program execution created.");
|
||||
this.execGarden = bridgeSettings.getBridgeSecurity().getExecGarden();
|
||||
this.theSettings = bridgeSettings;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -85,10 +92,20 @@ public class CommandHome implements Home {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// noop
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
|
||||
closed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.bwssystems.HABridge.plugins.fhem;
|
||||
|
||||
public class FHEMCommand {
|
||||
private String url;
|
||||
private String command;
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.bwssystems.HABridge.plugins.fhem;
|
||||
|
||||
public class FHEMDevice {
|
||||
|
||||
private String address;
|
||||
private String name;
|
||||
private Result item;
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public Result getItem() {
|
||||
return item;
|
||||
}
|
||||
public void setItem(Result item) {
|
||||
this.item = item;
|
||||
}
|
||||
}
|
||||
213
src/main/java/com/bwssystems/HABridge/plugins/fhem/FHEMHome.java
Normal file
213
src/main/java/com/bwssystems/HABridge/plugins/fhem/FHEMHome.java
Normal file
@@ -0,0 +1,213 @@
|
||||
package com.bwssystems.HABridge.plugins.fhem;
|
||||
|
||||
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.DeviceMapTypes;
|
||||
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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.ColorDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHome;
|
||||
import com.bwssystems.HABridge.plugins.http.HttpTestHandler;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class FHEMHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(FHEMHome.class);
|
||||
private Map<String, FHEMInstance> fhemMap;
|
||||
private Boolean validFhem;
|
||||
private Boolean isDevMode;
|
||||
private HTTPHandler httpClient;
|
||||
private boolean closed;
|
||||
|
||||
public FHEMHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
String responseString = null;
|
||||
|
||||
if(theUrl != null && !theUrl.isEmpty()) {
|
||||
FHEMCommand theCommand = null;
|
||||
try {
|
||||
theCommand = new Gson().fromJson(theUrl, FHEMCommand.class);
|
||||
} catch(Exception e) {
|
||||
log.warn("Cannot parse command to FHEM <<<" + theUrl + ">>>", e);
|
||||
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;
|
||||
}
|
||||
String intermediate = theCommand.getUrl().substring(theCommand.getUrl().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;
|
||||
FHEMInstance theHandler = findHandlerByAddress(hostAddr);
|
||||
if(theHandler != null) {
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
if (colorData != null) {
|
||||
anUrl = ColorDecode.replaceColorData(anUrl, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
|
||||
}
|
||||
anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device);
|
||||
anUrl = TimeDecode.replaceTimeValue(anUrl);
|
||||
|
||||
String aCommand = null;
|
||||
if(theCommand.getCommand() != null && !theCommand.getCommand().isEmpty()) {
|
||||
aCommand = BrightnessDecode.calculateReplaceIntensityValue(theCommand.getCommand(),
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
if (colorData != null) {
|
||||
aCommand = ColorDecode.replaceColorData(aCommand, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
|
||||
}
|
||||
aCommand = DeviceDataDecode.replaceDeviceData(aCommand, device);
|
||||
aCommand = TimeDecode.replaceTimeValue(aCommand);
|
||||
}
|
||||
try {
|
||||
theHandler.callCommand(anUrl, aCommand, httpClient);
|
||||
} catch (Exception e) {
|
||||
log.warn("Cannot send comand to FHEM", e);
|
||||
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("FHEM 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("FHEM 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;
|
||||
}
|
||||
|
||||
private FHEMInstance findHandlerByAddress(String hostAddress) {
|
||||
FHEMInstance aHandler = null;
|
||||
boolean found = false;
|
||||
Iterator<String> keys = fhemMap.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
aHandler = fhemMap.get(key);
|
||||
if(aHandler != null && aHandler.getFhemAddress().getIp().equals(hostAddress)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
aHandler = null;
|
||||
return aHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
|
||||
if(!validFhem)
|
||||
return null;
|
||||
log.debug("consolidating devices for FHEM");
|
||||
List<FHEMDevice> theResponse = null;
|
||||
Iterator<String> keys = fhemMap.keySet().iterator();
|
||||
List<FHEMDevice> deviceList = new ArrayList<FHEMDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = fhemMap.get(key).getDevices(httpClient);
|
||||
if(theResponse != null)
|
||||
addFHEMDevices(deviceList, theResponse, key);
|
||||
else {
|
||||
log.warn("Cannot get devices for FHEM: " + key + ", skipping this FHEM.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
|
||||
private Boolean addFHEMDevices(List<FHEMDevice> theDeviceList, List<FHEMDevice> theSourceList, String theKey) {
|
||||
Iterator<FHEMDevice> devices = theSourceList.iterator();
|
||||
while(devices.hasNext()) {
|
||||
FHEMDevice theDevice = devices.next();
|
||||
theDeviceList.add(theDevice);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
|
||||
fhemMap = null;
|
||||
validFhem = bridgeSettings.getBridgeSettingsDescriptor().isValidFhem();
|
||||
log.info("FHEM Home created." + (validFhem ? "" : " No FHEMs configured.") + (isDevMode ? " DevMode is set." : ""));
|
||||
if(validFhem) {
|
||||
fhemMap = new HashMap<String,FHEMInstance>();
|
||||
httpClient = HTTPHome.getHandler();
|
||||
if(isDevMode) {
|
||||
httpClient = new HttpTestHandler();
|
||||
((HttpTestHandler)httpClient).setTheData("cmd=jsonlist2", FHEMTestData.TestData);
|
||||
((HttpTestHandler)httpClient).setTheData("set", "FHEM Command Received");
|
||||
((HttpTestHandler)httpClient).setTheData(null, "FHEM no match");
|
||||
}
|
||||
httpClient.setCallType(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getFhemaddress().getDevices().iterator();
|
||||
while(theList.hasNext() && validFhem) {
|
||||
NamedIP aFhem = theList.next();
|
||||
try {
|
||||
fhemMap.put(aFhem.getName(), new FHEMInstance(aFhem));
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot get FHEM (" + aFhem.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
|
||||
validFhem = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
log.debug("Closing Home.");
|
||||
if(!closed && validFhem) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
|
||||
if(httpClient != null)
|
||||
httpClient.closeHandler();
|
||||
|
||||
fhemMap = null;
|
||||
closed = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.bwssystems.HABridge.plugins.fhem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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 FHEMInstance {
|
||||
private static final Logger log = LoggerFactory.getLogger(FHEMInstance.class);
|
||||
private NamedIP theFhem;
|
||||
|
||||
public FHEMInstance(NamedIP fhemLocation) {
|
||||
super();
|
||||
theFhem = fhemLocation;
|
||||
}
|
||||
|
||||
public NamedIP getFhemAddress() {
|
||||
return theFhem;
|
||||
}
|
||||
|
||||
public void setFhemAddress(NamedIP fhemAddress) {
|
||||
this.theFhem = fhemAddress;
|
||||
}
|
||||
|
||||
public Boolean callCommand(String aCommand, String commandData, HTTPHandler httpClient) {
|
||||
String aUrl = null;
|
||||
NameValue[] headers = null;
|
||||
if(theFhem.getSecure() != null && theFhem.getSecure())
|
||||
aUrl = "https://";
|
||||
else
|
||||
aUrl = "http://";
|
||||
if(theFhem.getUsername() != null && !theFhem.getUsername().isEmpty() && theFhem.getPassword() != null && !theFhem.getPassword().isEmpty()) {
|
||||
aUrl = aUrl + theFhem.getUsername() + ":" + theFhem.getPassword() + "@";
|
||||
}
|
||||
aUrl = aUrl + theFhem.getIp() + ":" + theFhem.getPort() + "/" + aCommand + commandData;
|
||||
log.debug("calling FHEM: " + aUrl);
|
||||
String theData = httpClient.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "text/plain", null, headers);
|
||||
if(theData != null)
|
||||
log.debug("doHttpRequest returned data: <" + theData + ">");
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<FHEMDevice> getDevices(HTTPHandler httpClient) {
|
||||
List<FHEMDevice> deviceList = null;
|
||||
FHEMItem theFhemStates;
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
NameValue[] headers = null;
|
||||
if(theFhem.getSecure() != null && theFhem.getSecure())
|
||||
theUrl = "https://";
|
||||
else
|
||||
theUrl = "http://";
|
||||
if(theFhem.getUsername() != null && !theFhem.getUsername().isEmpty() && theFhem.getPassword() != null && !theFhem.getPassword().isEmpty()) {
|
||||
theUrl = theUrl + theFhem.getUsername() + ":" + theFhem.getPassword() + "@";
|
||||
}
|
||||
theUrl = theUrl + theFhem.getIp() + ":" + theFhem.getPort() + "/fhem?cmd=jsonlist2";
|
||||
if(theFhem.getWebhook() != null && !theFhem.getWebhook().trim().isEmpty())
|
||||
theUrl = theUrl + "%20room=" + theFhem.getWebhook().trim();
|
||||
theData = httpClient.doHttpRequest(theUrl, HttpGet.METHOD_NAME, "application/json", null, headers);
|
||||
if(theData != null) {
|
||||
log.debug("GET FHEM States - data: " + theData);
|
||||
theData = getJSONData(theData);
|
||||
theFhemStates = new Gson().fromJson(theData, FHEMItem.class);
|
||||
if(theFhemStates == null) {
|
||||
log.warn("Cannot get any devices for FHEM " + theFhem.getName() + " as response is not parsable.");
|
||||
}
|
||||
else {
|
||||
deviceList = new ArrayList<FHEMDevice>();
|
||||
|
||||
for (Result aResult:theFhemStates.getResults()) {
|
||||
String name = aResult.getName();
|
||||
if(name.contains("<a href=")) {
|
||||
name = name.substring(name.indexOf("<a href=") + name.indexOf(">"));
|
||||
name = name.substring(1, name.indexOf("</a"));
|
||||
aResult.setName(name);
|
||||
}
|
||||
FHEMDevice aNewFhemDeviceDevice = new FHEMDevice();
|
||||
aNewFhemDeviceDevice.setItem(aResult);
|
||||
aNewFhemDeviceDevice.setAddress(theFhem.getIp() + ":" + theFhem.getPort());
|
||||
aNewFhemDeviceDevice.setName(theFhem.getName());
|
||||
deviceList.add(aNewFhemDeviceDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
log.warn("Cannot get an devices for FHEM " + theFhem.getName() + " http call failed.");
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
public String getJSONData(String response) {
|
||||
String theData;
|
||||
theData = response.substring(response.indexOf("<pre>") + 4);
|
||||
theData = theData.substring(1, theData.indexOf("</pre>") - 1);
|
||||
theData = theData.replace("\n", "");
|
||||
theData = theData.replace("\r", "");
|
||||
theData = theData.replace("<a href=\"", "<a href=\\\"");
|
||||
theData = theData.replace("\">", "\\\">");
|
||||
return theData;
|
||||
}
|
||||
|
||||
protected void closeClient() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.fhem;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class FHEMItem {
|
||||
|
||||
@SerializedName("Arg")
|
||||
@Expose
|
||||
private String arg;
|
||||
@SerializedName("Results")
|
||||
@Expose
|
||||
private List<Result> results = null;
|
||||
@SerializedName("totalResultsReturned")
|
||||
@Expose
|
||||
private Integer totalResultsReturned;
|
||||
|
||||
public String getArg() {
|
||||
return arg;
|
||||
}
|
||||
|
||||
public void setArg(String arg) {
|
||||
this.arg = arg;
|
||||
}
|
||||
|
||||
public List<Result> getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public void setResults(List<Result> results) {
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
public Integer getTotalResultsReturned() {
|
||||
return totalResultsReturned;
|
||||
}
|
||||
|
||||
public void setTotalResultsReturned(Integer totalResultsReturned) {
|
||||
this.totalResultsReturned = totalResultsReturned;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
package com.bwssystems.HABridge.plugins.fhem;
|
||||
|
||||
public class FHEMTestData {
|
||||
public final static String TestData = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" +
|
||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +
|
||||
" <head root=\"/fhem\">\n" +
|
||||
" <title>Home, Sweet Home</title>\n" +
|
||||
" <link rel=\"shortcut icon\" href=\"/fhem/icons/favicon\" />\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n" +
|
||||
" <link href=\"/fhem/pgm2/style.css?v=1513026539\" rel=\"stylesheet\"/>\n" +
|
||||
" <link href=\"/fhem/pgm2/jquery-ui.min.css\" rel=\"stylesheet\"/>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/jquery.min.js\"></script>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/jquery-ui.min.js\"></script>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb.js\"></script>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_colorpicker.js\"></script>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_fbcalllist.js\"></script>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_knob.js\"></script>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_readingsGroup.js\"></script>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_readingsHistory.js\"></script>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_sortable.js\"></script>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_uzsu.js\"></script>\n" +
|
||||
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_weekprofile.js\"></script>\n" +
|
||||
" </head>\n" +
|
||||
" <body name='Home, Sweet Home' fw_id='7880' generated=\"1513272732\" longpoll=\"1\" data-confirmDelete='1' data-confirmJSError='1' data-webName='haBridgeWeb '>\n" +
|
||||
" <div id=\"menuScrollArea\">\n" +
|
||||
" <div>\n" +
|
||||
" <a href=\"/fhem?\">\n" +
|
||||
" <div id=\"logo\"></div>\n" +
|
||||
" </a>\n" +
|
||||
" </div>\n" +
|
||||
" <div id=\"menu\">\n" +
|
||||
" <table>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <table class=\"room roomBlock1\">\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div class=\"menu_Save_config\">\n" +
|
||||
" <a href=\"/fhem?cmd=save\">Save config</a>\n" +
|
||||
" <a id=\"saveCheck\" class=\"changed\" style=\"visibility:visible\">?</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" </table>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <table class=\"room roomBlock2\">\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div class=\"menu_Alexa\">\n" +
|
||||
" <a href=\"/fhem?room=Alexa\">Alexa</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div class=\"menu_System\">\n" +
|
||||
" <a href=\"/fhem?room=System\">System</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div class=\"menu_WG_Zimmer\">\n" +
|
||||
" <a href=\"/fhem?room=WG%2dZimmer\">WG-Zimmer</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div class=\"menu_habridge\">\n" +
|
||||
" <a href=\"/fhem?room=habridge\">habridge</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div class=\"menu_Everything\">\n" +
|
||||
" <a href=\"/fhem?room=all\">\n" +
|
||||
" <img class='icon icoEverything' src=\"/fhem/images/default/icoEverything.png\" alt=\"icoEverything\" title=\"icoEverything\"> Everything\n" +
|
||||
" </a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" </table>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <table class=\"room roomBlock3\">\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div class=\"menu_Logfile\">\n" +
|
||||
" <a href=\"/fhem/FileLog_logWrapper?dev=Logfile&type=text&file=fhem-2017-12.log\">Logfile</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div>\n" +
|
||||
" <a href=\"/fhem/docs/commandref.html\" target=\"_blank\" >Commandref</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div>\n" +
|
||||
" <a href=\"http://fhem.de/fhem.html#Documentation\" target=\"_blank\" >Remote doc</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div class=\"menu_Edit_files\">\n" +
|
||||
" <a href=\"/fhem?cmd=style%20list\">Edit files</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div class=\"menu_Select_style\">\n" +
|
||||
" <a href=\"/fhem?cmd=style%20select\">Select style</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" <tr>\n" +
|
||||
" <td>\n" +
|
||||
" <div class=\"menu_Event_monitor\">\n" +
|
||||
" <a href=\"/fhem?cmd=style%20eventMonitor\">Event monitor</a>\n" +
|
||||
" </div>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" </table>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" </table>\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
" <div id=\"hdr\">\n" +
|
||||
" <table border=\"0\" class=\"header\">\n" +
|
||||
" <tr>\n" +
|
||||
" <td style=\"padding:0\">\n" +
|
||||
" <form method=\"post\" action=\"/fhem\">\n" +
|
||||
" <input type=\"hidden\" name=\"fw_id\" value=\"7880\"/>\n" +
|
||||
" <input type=\"text\" name=\"cmd\" class=\"maininput\" size=\"40\" value=\"\"/>\n" +
|
||||
" </form>\n" +
|
||||
" </td>\n" +
|
||||
" </tr>\n" +
|
||||
" </table>\n" +
|
||||
" </div>\n" +
|
||||
" <div id='content' >\n" +
|
||||
" <pre>{ \n" +
|
||||
" \"Arg\":\"room=habridge\", \n" +
|
||||
" \"Results\": [ \n" +
|
||||
" { \n" +
|
||||
" \"Name\":\"Arbeitslicht\", \n" +
|
||||
" \"PossibleSets\":\"on off\", \n" +
|
||||
" \"PossibleAttrs\":\"alias comment:textField-long eventMap group room suppressReading userReadings:textField-long verbose:0,1,2,3,4,5 readingList setList useSetExtensions disable disabledForIntervals event-on-change-reading event-on-update-reading event-aggregator event-min-interval stateFormat:textField-long timestamp-on-change-reading alexaName alexaRoom cmdIcon devStateIcon devStateStyle fhem_widget_command fhem_widget_command_2 fhem_widget_command_3 genericDeviceType:security,ignore,switch,outlet,light,blind,thermometer,thermostat,contact,garage,window,lock homebridgeMapping:textField-long icon sortby webCmd widgetOverride userattr\", \n" +
|
||||
" \"Internals\": { \n" +
|
||||
" \"NAME\": \"Arbeitslicht\", \n" +
|
||||
" \"NR\": \"28\", \n" +
|
||||
" \"STATE\": \"-\", \n" +
|
||||
" \"TYPE\": \"dummy\" \n" +
|
||||
" }, \n" +
|
||||
" \"Readings\": { \"state\": { \"Value\":\"on\", \"Time\":\"2017-12-14 15:41:05\" } }, \n" +
|
||||
" \"Attributes\": { \n" +
|
||||
" \"alexaName\": \"Arbeitslicht\", \n" +
|
||||
" \"alexaRoom\": \"alexaroom\", \n" +
|
||||
" \"fhem_widget_command\": \"{ \\u0022allowed_values\\u0022 : [ \\u0022on\\u0022 ], \\u0022order\\u0022 : 0}\", \n" +
|
||||
" \"icon\": \"scene_office\", \n" +
|
||||
" \"room\": \"Alexa,habridge\", \n" +
|
||||
" \"setList\": \"on off\", \n" +
|
||||
" \"stateFormat\": \"-\", \n" +
|
||||
" \"webCmd\": \"on\" \n" +
|
||||
" } \n" +
|
||||
" }, \n" +
|
||||
" { \n" +
|
||||
" \"Name\":\"DeckenlampeLinks\", \n" +
|
||||
" \"PossibleSets\":\"on off dim dimup dimdown HSV RGB sync pair unpair\", \n" +
|
||||
" \"PossibleAttrs\":\"alias comment:textField-long eventMap group room suppressReading userReadings:textField-long verbose:0,1,2,3,4,5 gamma dimStep defaultColor defaultRamp colorCast whitePoint event-on-change-reading event-on-update-reading event-aggregator event-min-interval stateFormat:textField-long timestamp-on-change-reading alexaName alexaRoom cmdIcon devStateIcon devStateStyle fhem_widget_command fhem_widget_command_2 fhem_widget_command_3 genericDeviceType:security,ignore,switch,outlet,light,blind,thermometer,thermostat,contact,garage,window,lock homebridgeMapping:textField-long icon sortby webCmd widgetOverride \n" +
|
||||
" <a href=\"/fhem?detail=AlleLampen\">AlleLampen</a> AlleLampen_map\n" +
|
||||
" <a href=\"/fhem?detail=DeckenLampen\">DeckenLampen</a> DeckenLampen_map structexclude userattr\", \n" +
|
||||
" \"Internals\": { \n" +
|
||||
" \"CONNECTION\": \"bridge-V3\", \n" +
|
||||
" \"DEF\": \"RGBW2 bridge-V3:10.2.3.3\", \n" +
|
||||
" \"IP\": \"10.2.3.3\", \n" +
|
||||
" \"LEDTYPE\": \"RGBW2\", \n" +
|
||||
" \"NAME\": \"DeckenlampeLinks\", \n" +
|
||||
" \"NR\": \"18\", \n" +
|
||||
" \"NTFY_ORDER\": \"50-DeckenlampeLinks\", \n" +
|
||||
" \"PORT\": \"8899\", \n" +
|
||||
" \"PROTO\": \"0\", \n" +
|
||||
" \"SLOT\": \"5\", \n" +
|
||||
" \"STATE\": \"off\", \n" +
|
||||
" \"TYPE\": \"WifiLight\" \n" +
|
||||
" }, \n" +
|
||||
" \"Readings\": { \n" +
|
||||
" \"RGB\": { \"Value\":\"000000\", \"Time\":\"2017-12-14 15:41:10\" }, \n" +
|
||||
" \"brightness\": { \"Value\":\"0\", \"Time\":\"2017-12-14 15:41:10\" }, \n" +
|
||||
" \"hue\": { \"Value\":\"0\", \"Time\":\"2017-12-14 15:41:10\" }, \n" +
|
||||
" \"saturation\": { \"Value\":\"0\", \"Time\":\"2017-12-14 15:41:10\" }, \n" +
|
||||
" \"state\": { \"Value\":\"off\", \"Time\":\"2017-12-14 15:41:10\" } \n" +
|
||||
" }, \n" +
|
||||
" \"Attributes\": { \n" +
|
||||
" \"AlleLampen\": \"AlleLampen\", \n" +
|
||||
" \"DeckenLampen\": \"DeckenLampen\", \n" +
|
||||
" \"fhem_widget_command\": \"{ \\u0022locations\\u0022 : [ \\u0022APP\\u0022, \\u0022WATCH\\u0022, \\u0022WIDGET\\u0022 ], \\u0022allowed_values\\u0022 : [ \\u0022off\\u0022, \\u0022on\\u0022 ], \\u0022order\\u0022 : 6}\", \n" +
|
||||
" \"room\": \"habridge,Alexa,WG-Zimmer\", \n" +
|
||||
" \"userattr\": \"AlleLampen AlleLampen_map\n" +
|
||||
" <a href=\"/fhem?detail=DeckenLampen\">DeckenLampen</a> DeckenLampen_map structexclude\", \n" +
|
||||
" \"webCmd\": \"RGB\", \n" +
|
||||
" \"widgetOverride\": \"RGB:colorpicker,RGB\" \n" +
|
||||
" } \n" +
|
||||
" } ], \n" +
|
||||
" \"totalResultsReturned\":2 \n" +
|
||||
"}\n" +
|
||||
" </pre>\n" +
|
||||
" </div>\n" +
|
||||
" </body>\n" +
|
||||
"</html>";
|
||||
|
||||
public final static String TestData2 = " <div id='content' >\n" +
|
||||
" <pre>\n" + "{ \"Arg\":\"room=HaBridge\", \"Results\": [ { \"Name\":\"wifi_steckdose3\", \"PossibleSets\":\"on:noArg off:noArg off on toggle\", \"PossibleAttrs\":\"alias comment:textField-long eventMap group room suppressReading userReadings:textField-long verbose:0,1,2,3,4,5 IODev qos retain publishSet publishSet_.* subscribeReading_.* autoSubscribeReadings event-on-change-reading event-on-update-reading event-aggregator event-min-interval stateFormat:textField-long timestamp-on-change-reading alarmDevice:Actor,Sensor alarmSettings cmdIcon devStateIcon devStateStyle icon lightSceneParamsToSave lightSceneRestoreOnlyIfChanged:1,0 sortby structexclude webCmd webCmdLabel:textField-long widgetOverride userattr\", \"Internals\": { \"CHANGED\": \"null\", \"NAME\": \"wifi_steckdose3\", \"NR\": \"270\", \"STATE\": \"off\", \"TYPE\": \"MQTT_DEVICE\", \"retain\": \"*:1 \" }, \"Readings\": { \"state\": { \"Value\":\"OFF\", \"Time\":\"2018-01-01 23:01:21\" }, \"transmission-state\": { \"Value\":\"subscription acknowledged\", \"Time\":\"2018-01-03 22:34:00\" } }, \"Attributes\": { \"IODev\": \"myBroker\", \"alias\": \"Stecki\", \"devStateIcon\": \"on:black_Steckdose.on off:black_Steckdose.off\", \"event-on-change-reading\": \"state\", \"eventMap\": \"ON:on OFF:off\", \"publishSet\": \"on off toggle /SmartHome/az/stecker/cmnd/POWER\", \"retain\": \"1\", \"room\": \"HaBridge,Arbeitszimmer,mqtt\", \"stateFormat\": \"state\", \"subscribeReading_state\": \"/SmartHome/az/stecker/stat/POWER\", \"webCmd\": \"on:off:toggle\" } } ], \"totalResultsReturned\":1 }" +
|
||||
" </pre>\n" +
|
||||
" </div>\n" +
|
||||
" </body>\n" +
|
||||
"</html>";
|
||||
public final static String TestData3 ="DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" +
|
||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +
|
||||
"<head root=\"/fhem\">\n" +
|
||||
"<title>Home, Sweet Home</title>\n" +
|
||||
"<link rel=\"shortcut icon\" href=\"/fhem/icons/favicon\" />\n" +
|
||||
"<meta charset=\"UTF-8\">\n" +
|
||||
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n" +
|
||||
"<link href=\"/fhem/pgm2/style.css?v=1515015198\" rel=\"stylesheet\"/>\n" +
|
||||
"<link href=\"/fhem/pgm2/jquery-ui.min.css\" rel=\"stylesheet\"/>\n" +
|
||||
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/jquery.min.js\"></script>\n" +
|
||||
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/jquery-ui.min.js\"></script>\n" +
|
||||
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb.js\"></script>\n" +
|
||||
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/doif.js\"></script>\n" +
|
||||
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fronthemEditor.js\"></script>\n" +
|
||||
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_readingsGroup.js\"></script>\n" +
|
||||
"</head>\n" +
|
||||
"<body name='Home, Sweet Home' fw_id='1490' generated=\"1515770038\" longpoll=\"websocket\" data-confirmDelete='1' data-confirmJSError='1' data-addHtmlTitle='1' data-availableJs='sortable,iconLabel,readingsHistory,colorpicker,iconButtons,fbcalllist,knob,weekprofile,iconRadio,readingsGroup,iconSwitch,uzsu' data-webName='WEB '>\n" +
|
||||
"<div id=\"menuScrollArea\">\n" +
|
||||
"</div>\n" +
|
||||
"<div id='content' >\n" +
|
||||
"<pre>{\n" +
|
||||
"\"Arg\":\"room=HaBridge\",\n" +
|
||||
"\"Results\": [\n" +
|
||||
"{\n" +
|
||||
"\"Name\":\"<a href='/fhem?detail=wifi_steckdose3'>wifi_steckdose3</a>\",\n" +
|
||||
"\"PossibleSets\":\"on:noArg off:noArg off on toggle\",\n" +
|
||||
"\"PossibleAttrs\":\"alias comment:textField-long eventMap group room suppressReading userReadings:textField-long verbose:0,1,2,3,4,5 IODev qos retain publishSet publishSet_.* subscribeReading_.* autoSubscribeReadings event-on-change-reading event-on-update-reading event-aggregator event-min-interval stateFormat:textField-long timestamp-on-change-reading alarmDevice:Actor,Sensor alarmSettings cmdIcon devStateIcon devStateStyle icon lightSceneParamsToSave lightSceneRestoreOnlyIfChanged:1,0 sortby structexclude webCmd webCmdLabel:textField-long widgetOverride userattr\",\n" +
|
||||
"\"Internals\": {\n" +
|
||||
"\"NAME\": \"<a href='/fhem?detail=wifi_steckdose3'>wifi_steckdose3</a>\",\n" +
|
||||
"\"NR\": \"270\",\n" +
|
||||
"\"STATE\": \"off\",\n" +
|
||||
"\"TYPE\": \"MQTT_DEVICE\",\n" +
|
||||
"\"retain\": \"*:1 \"\n" +
|
||||
"},\n" +
|
||||
"\"Readings\": {\n" +
|
||||
"\"state\": { \"Value\":\"OFF\", \"Time\":\"2018-01-07 05:16:01\" },\n" +
|
||||
"\"transmission-state\": { \"Value\":\"incoming publish received\", \"Time\":\"2018-01-07 05:16:01\" }\n" +
|
||||
"},\n" +
|
||||
"\"Attributes\": {\n" +
|
||||
"\"IODev\": \"<a href='/fhem?detail=myBroker'>myBroker</a>\",\n" +
|
||||
"\"alias\": \"Stecki\",\n" +
|
||||
"\"devStateIcon\": \"on:black_Steckdose.on off:black_Steckdose.off\",\n" +
|
||||
"\"event-on-change-reading\": \"state\",\n" +
|
||||
"\"eventMap\": \"ON:on OFF:off\",\n" +
|
||||
"\"publishSet\": \"on off toggle /SmartHome/az/stecker/cmnd/POWER\",\n" +
|
||||
"\"retain\": \"1\",\n" +
|
||||
"\"room\": \"HaBridge,Arbeitszimmer,mqtt\",\n" +
|
||||
"\"stateFormat\": \"state\",\n" +
|
||||
"\"subscribeReading_state\": \"/SmartHome/az/stecker/stat/POWER\",\n" +
|
||||
"\"webCmd\": \"on:off:toggle\"\n" +
|
||||
"}\n" +
|
||||
"} ],\n" +
|
||||
"\"totalResultsReturned\":1\n" +
|
||||
"}\n" +
|
||||
"</pre>\n" +
|
||||
"</div>\n" +
|
||||
"</body></html>";
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.fhem;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Result {
|
||||
|
||||
@SerializedName("Name")
|
||||
@Expose
|
||||
private String name;
|
||||
@SerializedName("PossibleSets")
|
||||
@Expose
|
||||
private String possibleSets;
|
||||
@SerializedName("PossibleAttrs")
|
||||
@Expose
|
||||
private String possibleAttrs;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPossibleSets() {
|
||||
return possibleSets;
|
||||
}
|
||||
|
||||
public void setPossibleSets(String possibleSets) {
|
||||
this.possibleSets = possibleSets;
|
||||
}
|
||||
|
||||
public String getPossibleAttrs() {
|
||||
return possibleAttrs;
|
||||
}
|
||||
|
||||
public void setPossibleAttrs(String possibleAttrs) {
|
||||
this.possibleAttrs = possibleAttrs;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.bwssystems.HABridge.plugins.fibaro;
|
||||
|
||||
public class FibaroFilter {
|
||||
boolean useSaveLogs;
|
||||
boolean useUserDescription;
|
||||
boolean scenesLiliCmddOnly;
|
||||
boolean replaceTrash;
|
||||
|
||||
public boolean isUseSaveLogs() {
|
||||
return useSaveLogs;
|
||||
}
|
||||
public void setUseSaveLogs(boolean useSaveLogs) {
|
||||
this.useSaveLogs = useSaveLogs;
|
||||
}
|
||||
public boolean isUseUserDescription() {
|
||||
return useUserDescription;
|
||||
}
|
||||
public void setUseUserDescription(boolean useUserDescription) {
|
||||
this.useUserDescription = useUserDescription;
|
||||
}
|
||||
public boolean isReplaceTrash() {
|
||||
return replaceTrash;
|
||||
}
|
||||
public void setReplaceTrash(boolean replaceTrash) {
|
||||
this.replaceTrash = replaceTrash;
|
||||
}
|
||||
public boolean isScenesLiliCmddOnly() {
|
||||
return scenesLiliCmddOnly;
|
||||
}
|
||||
public void setScenesLiliCmddOnly(boolean scenesLiliCmddOnly) {
|
||||
this.scenesLiliCmddOnly = scenesLiliCmddOnly;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.bwssystems.HABridge.plugins.fibaro;
|
||||
|
||||
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.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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.plugins.fibaro.json.Device;
|
||||
import com.bwssystems.HABridge.plugins.fibaro.json.Scene;
|
||||
|
||||
public class FibaroHome implements Home
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(FibaroHome.class);
|
||||
private Map<String, FibaroInfo> fibaros;
|
||||
private Boolean validFibaro;
|
||||
private boolean closed;
|
||||
|
||||
public FibaroHome(BridgeSettings bridgeSettings)
|
||||
{
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
public List<Device> getDevices()
|
||||
{
|
||||
log.debug("consolidating devices for fibaros");
|
||||
Iterator<String> keys = fibaros.keySet().iterator();
|
||||
ArrayList<Device> deviceList = new ArrayList<>();
|
||||
while(keys.hasNext())
|
||||
{
|
||||
String key = keys.next();
|
||||
for(Device device : fibaros.get(key).getDevices())
|
||||
deviceList.add(device);
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
public List<Scene> getScenes()
|
||||
{
|
||||
log.debug("consolidating scenes for fibaros");
|
||||
Iterator<String> keys = fibaros.keySet().iterator();
|
||||
ArrayList<Scene> sceneList = new ArrayList<>();
|
||||
while(keys.hasNext())
|
||||
{
|
||||
String key = keys.next();
|
||||
for(Scene scene : fibaros.get(key).getScenes())
|
||||
sceneList.add(scene);
|
||||
}
|
||||
return sceneList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body)
|
||||
{
|
||||
// Not a device handler
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type)
|
||||
{
|
||||
if(validFibaro)
|
||||
{
|
||||
if(type.equalsIgnoreCase(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex]))
|
||||
return getDevices();
|
||||
if(type.equalsIgnoreCase(DeviceMapTypes.FIBARO_SCENE[DeviceMapTypes.typeIndex]))
|
||||
return getScenes();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings)
|
||||
{
|
||||
validFibaro = bridgeSettings.getBridgeSettingsDescriptor().isValidFibaro();
|
||||
log.info("Fibaro Home created." + (validFibaro ? "" : " No Fibaros configured."));
|
||||
if(validFibaro)
|
||||
{
|
||||
fibaros = new HashMap<String, FibaroInfo>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getFibaroAddress().getDevices().iterator();
|
||||
while(theList.hasNext())
|
||||
{
|
||||
NamedIP aFibaro = theList.next();
|
||||
fibaros.put(aFibaro.getName(), new FibaroInfo(aFibaro));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome()
|
||||
{
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
fibaros = null;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package com.bwssystems.HABridge.plugins.fibaro;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.plugins.fibaro.json.Device;
|
||||
import com.bwssystems.HABridge.plugins.fibaro.json.Room;
|
||||
import com.bwssystems.HABridge.plugins.fibaro.json.Scene;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class FibaroInfo
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(FibaroInfo.class);
|
||||
|
||||
private final NamedIP fibaroAddress;
|
||||
private final String fibaroAuth;
|
||||
private final Gson gson;
|
||||
private Boolean isDevMode;
|
||||
private FibaroFilter theFilters;
|
||||
|
||||
public FibaroInfo(NamedIP addressName)
|
||||
{
|
||||
super();
|
||||
fibaroAddress = addressName;
|
||||
fibaroAuth = "Basic " + new String(Base64.encodeBase64((addressName.getUsername() + ":" + addressName.getPassword()).getBytes()));
|
||||
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
|
||||
gson = new Gson();
|
||||
theFilters = null;
|
||||
if(fibaroAddress.getExtensions() != null) {
|
||||
try {
|
||||
theFilters = gson.fromJson(fibaroAddress.getExtensions(), FibaroFilter.class);
|
||||
} catch(Exception e) {
|
||||
log.warn("Could not read fibaro filters - continuing with defaults.");
|
||||
theFilters = null;
|
||||
}
|
||||
}
|
||||
|
||||
if(theFilters == null) {
|
||||
theFilters = new FibaroFilter();
|
||||
theFilters.setUseSaveLogs(false);
|
||||
theFilters.setUseUserDescription(false);
|
||||
theFilters.setScenesLiliCmddOnly(false);
|
||||
theFilters.setReplaceTrash(true);
|
||||
}
|
||||
}
|
||||
|
||||
private String request(String request)
|
||||
{
|
||||
String result = null;
|
||||
try
|
||||
{
|
||||
URL url = new URL("http://" + fibaroAddress.getIp() + ":" + fibaroAddress.getPort() + request);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("Authorization", fibaroAuth);
|
||||
connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
|
||||
connection.connect();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
String line;
|
||||
while((line = br.readLine()) != null)
|
||||
{
|
||||
buffer.append(line).append("\n");
|
||||
}
|
||||
br.close();
|
||||
result = buffer.toString();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
log.warn("Error while get getJson: {} ", request, e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String replaceTrash(String name)
|
||||
{
|
||||
String sanitizedName = name.replaceAll("[0-9:/-]", "");
|
||||
sanitizedName = name.replaceAll("\\s+", " ");
|
||||
return sanitizedName.trim();
|
||||
}
|
||||
|
||||
private Room[] getRooms()
|
||||
{
|
||||
String result = null;
|
||||
if(isDevMode)
|
||||
result = FibaroTestData.RoomTestData;
|
||||
else
|
||||
result = request("/api/rooms");
|
||||
log.debug("getRooms response: <<<" + result + ">>>");
|
||||
Room[] rooms = result == null ? new Room[0] : gson.fromJson(result, Room[].class);
|
||||
if(theFilters.isReplaceTrash())
|
||||
for(Room r : rooms)
|
||||
r.setName(replaceTrash(r.getName()));
|
||||
return rooms;
|
||||
}
|
||||
|
||||
public Device[] getDevices()
|
||||
{
|
||||
Room[] rooms = getRooms();
|
||||
|
||||
log.debug("getDevices Found: " + rooms.length + " rooms");
|
||||
|
||||
String result = null;
|
||||
if(isDevMode)
|
||||
result = FibaroTestData.DeviceTestData;
|
||||
else
|
||||
result = request("/api/devices?enabled=true&visible=true");
|
||||
log.debug("getDevices response: <<<" + result + ">>>");
|
||||
Device[] all_devices = result == null ? new Device[0] : gson.fromJson(result, Device[].class);
|
||||
|
||||
int count = 0;
|
||||
for(Device d : all_devices)
|
||||
if(d.getRoomID() > 0 && (theFilters.isUseSaveLogs() ? "true".equals(d.getProperties().getSaveLogs()) : true))
|
||||
count++;
|
||||
|
||||
Device[] devices = new Device[count];
|
||||
int i = 0;
|
||||
for(Device d : all_devices)
|
||||
if(d.getRoomID() > 0 && (theFilters.isUseSaveLogs() ? "true".equals(d.getProperties().getSaveLogs()) : true))
|
||||
{
|
||||
if(theFilters.isUseUserDescription() && d.getProperties().getUserDescription() != null && !d.getProperties().getUserDescription().isEmpty())
|
||||
d.setName(d.getProperties().getUserDescription());
|
||||
if(theFilters.isReplaceTrash())
|
||||
d.setName(replaceTrash(d.getName()));
|
||||
|
||||
devices[i++] = d;
|
||||
|
||||
for(Room room : rooms)
|
||||
if(d.getRoomID() == room.getId())
|
||||
d.setRoomName(room.getName());
|
||||
|
||||
d.fibaroaddress = fibaroAddress.getIp();
|
||||
d.fibaroport = fibaroAddress.getPort();
|
||||
d.fibaroAuth = fibaroAuth;
|
||||
d.fibaroname = fibaroAddress.getName();
|
||||
}
|
||||
|
||||
log.debug("getDevices Found: " + devices.length + " devices");
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
public Scene[] getScenes()
|
||||
{
|
||||
Room[] rooms = getRooms();
|
||||
|
||||
String result = null;
|
||||
if(isDevMode)
|
||||
result = FibaroTestData.SceneTestData;
|
||||
else
|
||||
result = request("/api/scenes?enabled=true&visible=true");
|
||||
log.debug("getScenes response: <<<" + result + ">>>");
|
||||
Scene[] all_scenes = result == null ? new Scene[0] : gson.fromJson(result, Scene[].class);
|
||||
|
||||
int count = 0;
|
||||
for(Scene s : all_scenes)
|
||||
if(!theFilters.isScenesLiliCmddOnly() || s.getLiliStartCommand() != null && !s.getLiliStartCommand().isEmpty())
|
||||
count++;
|
||||
Scene[] scenes = new Scene[count];
|
||||
int i = 0;
|
||||
for(Scene s : all_scenes)
|
||||
if(!theFilters.isScenesLiliCmddOnly() || s.getLiliStartCommand() != null && !s.getLiliStartCommand().isEmpty())
|
||||
{
|
||||
if(theFilters.isReplaceTrash())
|
||||
s.setName(replaceTrash(s.getName()));
|
||||
|
||||
scenes[i++] = s;
|
||||
|
||||
for(Room room : rooms)
|
||||
if(s.getRoomID() == room.getId())
|
||||
s.setRoomName(room.getName());
|
||||
|
||||
s.fibaroaddress = fibaroAddress.getIp();
|
||||
s.fibaroport = fibaroAddress.getPort();
|
||||
s.fibaroAuth = fibaroAuth;
|
||||
s.fibaroname = fibaroAddress.getName();
|
||||
}
|
||||
log.debug("getScenes Found: " + count + " scenes");
|
||||
return scenes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.bwssystems.HABridge.plugins.fibaro;
|
||||
|
||||
public class FibaroTestData {
|
||||
public final static String DeviceTestData = "[{\"id\":7,\"name\":\"Deckenspots\",\"roomID\":12,\"type\":\"com.fibaro.FGD212\",\"baseType\":\"com.fibaro.multilevelSwitch\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":5,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"energy\",\"levelChange\",\"light\",\"power\",\"zwave\",\"zwaveAlarm\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"3.5\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"alarmLevel\":\"0\",\"alarmType\":\"0\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"23\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"15\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"1\",\"energy\":\"7.23\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"2\",\"parametersTemplate\":\"796\",\"power\":\"5.80\",\"productInfo\":\"1,15,1,2,16,0,3,5\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"25\",\"serialNumber\":\"h'000000000001c1b4\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"5\"},\"actions\":{\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stopLevelChange\":0,\"turnOff\":0,\"turnOn\":0},\"created\":1516643104,\"modified\":1516643104,\"sortOrder\":1},{\"id\":13,\"name\":\"Licht Küche\",\"roomID\":13,\"type\":\"com.fibaro.binarySwitch\",\"baseType\":\"com.fibaro.actor\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":10,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":fals" +
|
||||
"e,\"interfaces\":[\"deviceGrouping\",\"energy\",\"light\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"3.2\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"2\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"2\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"1\",\"energy\":\"9.51\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"3\",\"parametersTemplate\":\"781\",\"power\":\"16.00\",\"productInfo\":\"1,15,2,3,16,0,3,2\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"serialNumber\":\"h'000000000000450d\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"true\"},\"actions\":{\"reconfigure\":0,\"reset\":0,\"turnOff\":0,\"turnOn\":0},\"created\":1516643104,\"modified\":1516643104,\"sortOrder\":2},{\"id\":14,\"name\":\"Lampe Tisch\",\"roomID\":14,\"type\":\"com.fibaro.binarySwitch\",\"baseType\":\"com.fibaro.actor\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":10,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"energy\",\"light\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"3.2\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"2\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"2\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"2\",\"energy\":\"29.15\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":" +
|
||||
"\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"3\",\"parametersTemplate\":\"781\",\"power\":\"11.50\",\"productInfo\":\"1,15,2,3,16,0,3,2\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"serialNumber\":\"h'000000000000450d\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"true\"},\"actions\":{\"reconfigure\":0,\"reset\":0,\"turnOff\":0,\"turnOn\":0},\"created\":1516643104,\"modified\":1516643104,\"sortOrder\":3},{\"id\":18,\"name\":\"Licht\",\"roomID\":16,\"type\":\"com.fibaro.binarySwitch\",\"baseType\":\"com.fibaro.actor\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":15,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"energy\",\"light\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"3.2\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"2\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"2\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"1\",\"energy\":\"2.70\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"4\",\"parametersTemplate\":\"781\",\"power\":\"0.00\",\"productInfo\":\"1,15,2,3,16,0,3,2\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"serialNumber\":\"h'00000000000044b4\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTempl" +
|
||||
"ate\":\"true\",\"userDescription\":\"\",\"value\":\"false\"},\"actions\":{\"reconfigure\":0,\"reset\":0,\"turnOff\":0,\"turnOn\":0},\"created\":1516643104,\"modified\":1516643104,\"sortOrder\":4},{\"id\":26,\"name\":\"Licht\",\"roomID\":5,\"type\":\"com.fibaro.FGD212\",\"baseType\":\"com.fibaro.multilevelSwitch\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":24,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"energy\",\"levelChange\",\"light\",\"power\",\"zwave\",\"zwaveAlarm\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"3.5\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"alarmLevel\":\"0\",\"alarmType\":\"0\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"23\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"15\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"1\",\"energy\":\"28.02\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"7\",\"parametersTemplate\":\"796\",\"power\":\"0.00\",\"productInfo\":\"1,15,1,2,16,0,3,5\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"0\",\"serialNumber\":\"h'0000000000001fec\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\"},\"actions\":{\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stopLevelChange\":0,\"turnOff\":0,\"turnOn\":0},\"created\":1516643105,\"modified\":1516643105,\"sortOrder\":5},{\"id\":57,\"name\":\"Fenster rechts\",\"roomID\":14,\"type\":\"com.fibaro.FGRM222\",\"baseType\":\"com.fibaro.FGR\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":56,\"remoteGatewayId\"" +
|
||||
":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"energy\",\"levelChange\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"25.25\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"54\",\"deviceIcon\":\"87\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"0.78\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"19\",\"parametersTemplate\":\"721\",\"power\":\"0.00\",\"productInfo\":\"1,15,3,2,16,0,25,25\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionLocal\":\"0\",\"protectionLocalSupport\":\"5\",\"protectionRF\":\"0\",\"protectionRFSupport\":\"3\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"0\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\",\"value2\":\"0\"},\"actions\":{\"close\":0,\"open\":0,\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"setValue2\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stop\":0,\"stopLevelChange\":0},\"created\":1516643105,\"modified\":1516643105,\"sortOrder\":12},{\"id\":60,\"name\":\"Fenster mitte\",\"roomID\":14,\"type\":\"com.fibaro.FGRM222\",\"baseType\":\"com.fibaro.FGR\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":59,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"energy\",\"levelChange\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"25.25\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlT" +
|
||||
"ype\":\"54\",\"deviceIcon\":\"87\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"0.71\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"20\",\"parametersTemplate\":\"721\",\"power\":\"0.00\",\"productInfo\":\"1,15,3,2,16,0,25,25\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionLocal\":\"0\",\"protectionLocalSupport\":\"5\",\"protectionRF\":\"0\",\"protectionRFSupport\":\"3\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"26\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\",\"value2\":\"0\"},\"actions\":{\"close\":0,\"open\":0,\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"setValue2\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stop\":0,\"stopLevelChange\":0},\"created\":1516643105,\"modified\":1516643105,\"sortOrder\":13},{\"id\":74,\"name\":\"Fenster links\",\"roomID\":14,\"type\":\"com.fibaro.FGRM222\",\"baseType\":\"com.fibaro.FGR\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":73,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"energy\",\"levelChange\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"25.25\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"54\",\"deviceIcon\":\"87\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"0.64\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"21\",\"parametersTemplate\":\"721\",\"power\":\"0.00\",\"productInfo\":\"1,15,3,2,16,0" +
|
||||
",25,25\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionLocal\":\"0\",\"protectionLocalSupport\":\"5\",\"protectionRF\":\"0\",\"protectionRFSupport\":\"3\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"0\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\",\"value2\":\"0\"},\"actions\":{\"close\":0,\"open\":0,\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"setValue2\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stop\":0,\"stopLevelChange\":0},\"created\":1516643106,\"modified\":1516643106,\"sortOrder\":14},{\"id\":77,\"name\":\"Fenster Sideboard\",\"roomID\":14,\"type\":\"com.fibaro.FGRM222\",\"baseType\":\"com.fibaro.FGR\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":76,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"energy\",\"levelChange\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"25.25\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"54\",\"deviceIcon\":\"87\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"0.63\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"22\",\"parametersTemplate\":\"721\",\"power\":\"0.00\",\"productInfo\":\"1,15,3,2,16,0,25,25\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionLocal\":\"0\",\"protectionLocalSupport\":\"5\",\"protectionRF\":\"0\",\"protectionRFSupport\":\"3\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneAc" +
|
||||
"tivation\":\"0\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\",\"value2\":\"0\"},\"actions\":{\"close\":0,\"open\":0,\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"setValue2\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stop\":0,\"stopLevelChange\":0},\"created\":1516643106,\"modified\":1516643106,\"sortOrder\":15},{\"id\":112,\"name\":\"Stehlampe\",\"roomID\":12,\"type\":\"com.fibaro.multilevelSwitch\",\"baseType\":\"com.fibaro.binarySwitch\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":111,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"fibaroFirmwareUpdate\",\"levelChange\",\"light\",\"zwave\",\"zwaveSwitchAll\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Domitech Products\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"5.14\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"23\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"15\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"firmwareUpdate\":\"{\\\"info\\\":\\\"\\\",\\\"progress\\\":0,\\\"status\\\":\\\"UpToDate\\\",\\\"updateVersion\\\":\\\"5.14\\\"}\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"27\",\"parametersTemplate\":\"807\",\"productInfo\":\"2,14,76,66,49,52,5,14\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"serialNumber\":\"\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"switchAllMode\":\"SWITCH_ALL_INCLUDED_IN_THE_ALL_ON_ALL_OFF_FUNCTIONALITY\",\"updateVersion\":\"\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\"},\"actions\":{\"abortUpdate\":1,\"reconfigure\":0,\"retryUpdate\":1,\"setValue\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"startUpdate\":1,\"stopLevelChange\":0,\"turnOff\":0,\"turnOn\":0,\"updateFirmware\":1},\"created\":1516643107,\"modified\":1516643107,\"sortOrder\":6},{\"id\":114,\"name\":\"RGBW\",\"roomID\":12,\"type\":\"com.fibaro.FGRGBW441M\",\"baseType\":\"com.fibaro.colorController\",\"enabled\":true,\"visible\":tr" +
|
||||
"ue,\"isPlugin\":false,\"parentId\":113,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"energy\",\"fibaroFirmwareUpdate\",\"levelChange\",\"light\",\"power\",\"zwave\",\"zwaveSwitchAll\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"26.25\",\"associationMode\":\"0\",\"brightness\":\"0\",\"buttonType\":\"0\",\"color\":\"0,0,0,0\",\"configured\":true,\"currentProgram\":\"0\",\"currentProgramID\":\"0\",\"dead\":\"false\",\"deviceControlType\":\"50\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"15\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"1.72\",\"favoriteProgram\":\"0\",\"firmwareUpdate\":\"{\\\"info\\\":\\\"\\\",\\\"progress\\\":0,\\\"status\\\":\\\"UpToDate\\\",\\\"updateVersion\\\":\\\"26.25\\\"}\",\"isLight\":\"true\",\"lastColorSet\":\"0,0,0,0\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"mode\":\"0\",\"model\":\"\",\"nodeId\":\"28\",\"parametersTemplate\":\"231\",\"power\":\"0.00\",\"productInfo\":\"1,15,9,0,16,0,26,25\",\"programsSortOrder\":\"1,2,3,4,5\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"rememberColor\":\"false\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"switchAllMode\":\"SWITCH_ALL_INCLUDED_IN_THE_ALL_ON_ALL_OFF_FUNCTIONALITY\",\"updateVersion\":\"\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\"},\"actions\":{\"abortUpdate\":1,\"reconfigure\":0,\"reset\":0,\"retryUpdate\":1,\"setB\":1,\"setBrightness\":1,\"setColor\":1,\"setFavoriteProgram\":2,\"setG\":1,\"setR\":1,\"setValue\":1,\"setW\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"startProgram\":1,\"startUpdate\":1,\"stopLevelChange\":0,\"turnOff\":0,\"turnOn\":0,\"updateFirmware\":1},\"created\":1516643107,\"modified\":1516643107,\"sortOrder\":7},{\"id\":122,\"name\":\"Fenster\",\"roomID\":12,\"type\":\"com.fibaro.FGRM222\",\"baseType\":\"com.fibaro.FGR\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":121,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"energy\",\"fibaroFirmwareUpdate\",\"levelChange\",\"powe" +
|
||||
"r\",\"zwave\",\"zwaveConfiguration\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"24.24\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"54\",\"deviceIcon\":\"87\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"0.17\",\"firmwareUpdate\":\"{\\\"info\\\":\\\"\\\",\\\"progress\\\":0,\\\"status\\\":\\\"UpToDate\\\",\\\"updateVersion\\\":\\\"24.24\\\"}\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"29\",\"parametersTemplate\":\"249\",\"power\":\"0.00\",\"productInfo\":\"1,15,3,1,16,1,24,24\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"0\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"updateVersion\":\"\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\",\"value2\":\"0\"},\"actions\":{\"abortUpdate\":1,\"close\":0,\"getParameter\":1,\"open\":0,\"reconfigure\":0,\"reset\":0,\"retryUpdate\":1,\"sceneActivationSet\":0,\"setParameter\":2,\"setValue\":1,\"setValue2\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"startUpdate\":1,\"stop\":0,\"stopLevelChange\":0,\"updateFirmware\":1},\"created\":1516643108,\"modified\":1516643108,\"sortOrder\":16}]";
|
||||
|
||||
public final static String RoomTestData = "[{\"id\":4,\"name\":\"Dachboden\",\"sectionID\":4,\"icon\":\"room_bedroom\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":1},{\"id\":5,\"name\":\"Badezimmer\",\"sectionID\":5,\"icon\":\"room_wanna\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":2},{\"id\":6,\"name\":\"Büro\",\"sectionID\":5,\"icon\":\"room_office\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":3},{\"id\":7,\"name\":\"Flur\",\"sectionID\":5,\"icon\":\"room_schody2\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":4},{\"id\":8,\"name\":\"Schlafzimmer\",\"sectionID\":5,\"icon\":\"room_bedroom\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":5},{\"id\":9,\"name\":\"Emelie\",\"sectionID\":5,\"icon\":\"room_wardrobe\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":6},{\"id\":10,\"name\":\"Mino\",\"sectionID\":5,\"icon\":\"room_garage\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":7},{\"id\":11,\"name\":\"Flur\",\"sectionID\":6,\"icon\":\"room_drzwiwejsciowe\",\"defaultSensors\":{\"temperature\":181,\"humidity\":0,\"light\":182},\"defaultThermostat\":0,\"sortOrder\":8},{\"id\":12,\"name\":\"Wohnzimmer\",\"sectionID\":6,\"icon\":\"room_sofa\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":9},{\"id\":13,\"name\":\"Küche\",\"sectionID\":6,\"icon\":\"room_dining\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":10},{\"id\":14,\"name\":\"Esszimmer\",\"sectionID\":6,\"icon\":\"room_jadalnia\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":11},{\"id\":15,\"name\":\"HWR\",\"sectionID\":6,\"icon\":\"room_laundry\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":12},{\"id\":16,\"name\":\"Gäste WC\",\"sectionID\":6," +
|
||||
"\"icon\":\"room_toilet\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":13},{\"id\":17,\"name\":\"Haus\",\"sectionID\":7,\"icon\":\"room_bedroom\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":14},{\"id\":18,\"name\":\"Terrasse\",\"sectionID\":7,\"icon\":\"room_bedroom\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":15}]";
|
||||
|
||||
public final static String SceneTestData = "[" +
|
||||
"{\"id\":33,\"name\":\"Fernsehlicht aus\",\"type\":\"com.fibaro.luaScene\",\"roomID\":12,\"iconID\":6,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":true,\"autostart\":true,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":4,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":true,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":17}," +
|
||||
"{\"id\":68,\"name\":\"Rollos runterfahren\",\"type\":\"com.fibaro.luaScene\",\"roomID\":14,\"iconID\":6,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":true,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":true,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":8}," +
|
||||
"{\"id\":69,\"name\":\"Rollos hochfahren\",\"type\":\"com.fibaro.luaScene\",\"roomID\":14,\"iconID\":6,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":true,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":true,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":9}," +
|
||||
"{\"id\":93,\"name\":\"Alles aus Haus\",\"type\":\"com.fibaro.magicScene\",\"roomID\":12,\"runConfig\":\"TRIGGER_AND_MANUAL\",\"alexaProhibited\":false,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":false,\"properties\":\"{\\\"conditionDeviceId\\\":163,\\\"conditionObjectType\\\":\\\"device\\\",\\\"conditionId\\\":\\\"condition_centralSceneEvent1HeldDown_163\\\",\\\"conditionLua\\\":\\\"true\\\",\\\"conditionValue\\\":\\\"\\\",\\\"conditionRooms\\\":[],\\\"trigger\\\":\\\"163 CentralSceneEvent 1 HeldDown\\\\n163 CentralSceneEvent 1 Released\\\",\\\"actionDeviceId\\\":92,\\\"actionObjectType\\\":\\\"scene\\\",\\\"actionId\\\":\\\"action_runScene_92\\\",\\\"actionLua\\\":\\\"fibaro:startScene(92);\\\",\\\"actionValue\\\":\\\"\\\",\\\"actionRooms\\\":[],\\\"looping\\\":false}\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[{\"deviceId\":163,\"eventName\":\"CentralSceneEvent\",\"args\":[\"1\",\"Released\"]}],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[92],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":18}," +
|
||||
"{\"id\":94,\"name\":\"Fernsehlicht an\",\"type\":\"com.fibaro.blockScene\",\"roomID\":12,\"iconID\":5,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":false,\"autostart\":true,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":false,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[112,114],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":10}," +
|
||||
"{\"id\":95,\"name\":\"Fernsehlicht + aus\",\"type\":\"com.fibaro.blockScene\",\"roomID\":12,\"iconID\":5,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":false,\"autostart\":true,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":false,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[7,10,11,12,13,14,57,60,74,77,97,112,114],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":19}," +
|
||||
"{\"id\":96,\"name\":\"Fernsehlicht Wohnzim\",\"type\":\"com.fibaro.magicScene\",\"roomID\":12,\"runConfig\":\"TRIGGER_AND_MANUAL\",\"alexaProhibited\":false,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":false,\"properties\":\"{\\\"conditionDeviceId\\\":163,\\\"conditionObjectType\\\":\\\"device\\\",\\\"conditionId\\\":\\\"condition_centralSceneEvent2Pressed_163\\\",\\\"conditionLua\\\":\\\"true\\\",\\\"conditionValue\\\":\\\"\\\",\\\"conditionRooms\\\":[],\\\"trigger\\\":\\\"163 CentralSceneEvent 2 Pressed\\\",\\\"actionDeviceId\\\":94,\\\"actionObjectType\\\":\\\"scene\\\",\\\"actionId\\\":\\\"action_runScene_94\\\",\\\"actionLua\\\":\\\"fibaro:startScene(94);\\\",\\\"actionValue\\\":\\\"\\\",\\\"actionRooms\\\":[],\\\"looping\\\":false}\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[{\"deviceId\":163,\"eventName\":\"CentralSceneEvent\",\"args\":[\"2\",\"Pressed\"]}],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[94],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":20}," +
|
||||
"{\"id\":98,\"name\":\"Fersehlicht blau\",\"type\":\"com.fibaro.blockScene\",\"roomID\":12,\"iconID\":5,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":false,\"autostart\":true,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":false,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[112,114],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":11}," +
|
||||
"{\"id\":99,\"name\":\"Fernsehlicht blau+au\",\"type\":\"com.fibaro.blockScene\",\"roomID\":12,\"iconID\":5,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":false,\"autostart\":true,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":false,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[7,10,11,12,13,14,112,114],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":21}," +
|
||||
"{\"id\":100,\"name\":\"Fernsehlicht blau+au\",\"type\":\"com.fibaro.magicScene\",\"roomID\":12,\"runConfig\":\"TRIGGER_AND_MANUAL\",\"alexaProhibited\":false,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":false,\"properties\":\"{\\\"conditionDeviceId\\\":163,\\\"conditionObjectType\\\":\\\"device\\\",\\\"conditionId\\\":\\\"condition_centralSceneEvent3HeldDown_163\\\",\\\"conditionLua\\\":\\\"true\\\",\\\"conditionValue\\\":\\\"\\\",\\\"conditionRooms\\\":[],\\\"trigger\\\":\\\"163 CentralSceneEvent 3 HeldDown\\\\n163 CentralSceneEvent 3 Released\\\",\\\"actionDeviceId\\\":99,\\\"actionObjectType\\\":\\\"scene\\\",\\\"actionId\\\":\\\"action_runScene_99\\\",\\\"actionLua\\\":\\\"fibaro:startScene(99);\\\",\\\"actionValue\\\":\\\"\\\",\\\"actionRooms\\\":[],\\\"looping\\\":false}\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[{\"deviceId\":163,\"eventName\":\"CentralSceneEvent\",\"args\":[\"3\",\"Released\"]}],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[99],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":22}," +
|
||||
"{\"id\":101,\"name\":\"Fersehlicht blau Woh\",\"type\":\"com.fibaro.magicScene\",\"roomID\":12,\"runConfig\":\"TRIGGER_AND_MANUAL\",\"alexaProhibited\":false,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":false,\"properties\":\"{\\\"conditionDeviceId\\\":163,\\\"conditionObjectType\\\":\\\"device\\\",\\\"conditionId\\\":\\\"condition_centralSceneEvent3Pressed_163\\\",\\\"conditionLua\\\":\\\"true\\\",\\\"conditionValue\\\":\\\"\\\",\\\"conditionRooms\\\":[],\\\"trigger\\\":\\\"163 CentralSceneEvent 3 Pressed\\\",\\\"actionDeviceId\\\":98,\\\"actionObjectType\\\":\\\"scene\\\",\\\"actionId\\\":\\\"action_runScene_98\\\",\\\"actionLua\\\":\\\"fibaro:startScene(98);\\\",\\\"actionValue\\\":\\\"\\\",\\\"actionRooms\\\":[],\\\"looping\\\":false}\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[{\"deviceId\":163,\"eventName\":\"CentralSceneEvent\",\"args\":[\"3\",\"Pressed\"]}],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[98],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":23}" +
|
||||
"]";
|
||||
|
||||
public final static String CallActionTestData = "Fiabro action received";
|
||||
public final static String SceneControlTestData = "Fiabro scene control received";
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.bwssystems.HABridge.plugins.fibaro.json;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Device {
|
||||
private String roomName;
|
||||
|
||||
@SerializedName("id")
|
||||
private String id;
|
||||
|
||||
@SerializedName("name")
|
||||
private String name;
|
||||
|
||||
@SerializedName("roomID")
|
||||
private int roomID;
|
||||
|
||||
@SerializedName("type")
|
||||
private String type;
|
||||
|
||||
@SerializedName("properties")
|
||||
private DeviceProperties properties;
|
||||
|
||||
public String getRoomName() {
|
||||
return roomName;
|
||||
}
|
||||
|
||||
public void setRoomName(String roomName) {
|
||||
this.roomName = roomName;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getRoomID() {
|
||||
return roomID;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public DeviceProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public boolean isThermostat() {
|
||||
return type.equals("com.fibaro.setPoint") || type.equals("com.fibaro.thermostatDanfoss")
|
||||
|| type.equals("com.fibaro.thermostatHorstmann");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + id + ", " + name + "}";
|
||||
}
|
||||
|
||||
public String fibaroaddress;
|
||||
public String fibaroport;
|
||||
public String fibaroAuth;
|
||||
public String fibaroname;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.bwssystems.HABridge.plugins.fibaro.json;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class DeviceProperties {
|
||||
@SerializedName("value")
|
||||
private String value;
|
||||
|
||||
@SerializedName("saveLogs")
|
||||
private String saveLogs;
|
||||
|
||||
@SerializedName("userDescription")
|
||||
private String userDescription;
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
public String getSaveLogs() {
|
||||
return saveLogs;
|
||||
}
|
||||
public String getUserDescription() {
|
||||
return userDescription;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.bwssystems.HABridge.plugins.fibaro.json;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Room {
|
||||
@SerializedName("id")
|
||||
private int id;
|
||||
|
||||
@SerializedName("name")
|
||||
private String name;
|
||||
|
||||
@SerializedName("sectionID")
|
||||
private int sectionID;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getSectionID()
|
||||
{
|
||||
return sectionID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.bwssystems.HABridge.plugins.fibaro.json;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Scene {
|
||||
private String roomName;
|
||||
|
||||
@SerializedName("id")
|
||||
private String id;
|
||||
|
||||
@SerializedName("name")
|
||||
private String name;
|
||||
|
||||
@SerializedName("type")
|
||||
private String type;
|
||||
|
||||
@SerializedName("roomID")
|
||||
private int roomID;
|
||||
|
||||
@SerializedName("liliStartCommand")
|
||||
private String liliStartCommand;
|
||||
|
||||
public String getRoomName() {
|
||||
return roomName;
|
||||
}
|
||||
|
||||
public void setRoomName(String roomName) {
|
||||
this.roomName = roomName;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getRoomID() {
|
||||
return roomID;
|
||||
}
|
||||
|
||||
public String getLiliStartCommand()
|
||||
{
|
||||
return liliStartCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + id + ", " + name + "}";
|
||||
}
|
||||
|
||||
public String fibaroaddress;
|
||||
public String fibaroport;
|
||||
public String fibaroAuth;
|
||||
public String fibaroname;
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
package com.bwssystems.HABridge.plugins.hal;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
|
||||
public class HalDevice {
|
||||
private String haldevicetype;
|
||||
private String haldevicename;
|
||||
private String haladdress;
|
||||
private String halname;
|
||||
private NamedIP haladdress;
|
||||
private DeviceElements buttons;
|
||||
public String getHaldevicetype() {
|
||||
return haldevicetype;
|
||||
@@ -18,18 +19,12 @@ public class HalDevice {
|
||||
public void setHaldevicename(String haldevicename) {
|
||||
this.haldevicename = haldevicename;
|
||||
}
|
||||
public String getHaladdress() {
|
||||
public NamedIP getHaladdress() {
|
||||
return haladdress;
|
||||
}
|
||||
public void setHaladdress(String haladdress) {
|
||||
public void setHaladdress(NamedIP haladdress) {
|
||||
this.haladdress = haladdress;
|
||||
}
|
||||
public String getHalname() {
|
||||
return halname;
|
||||
}
|
||||
public void setHalname(String halname) {
|
||||
this.halname = halname;
|
||||
}
|
||||
public DeviceElements getButtons() {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
@@ -13,24 +13,34 @@ 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.ColorData;
|
||||
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;
|
||||
private boolean closed;
|
||||
|
||||
public HalHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
if(!validHal)
|
||||
return null;
|
||||
log.debug("consolidating devices for hues");
|
||||
log.debug("consolidating devices for HALs");
|
||||
List<HalDevice> theResponse = null;
|
||||
Iterator<String> keys = hals.keySet().iterator();
|
||||
List<HalDevice> deviceList = new ArrayList<HalDevice>();
|
||||
@@ -103,11 +113,71 @@ public class HalHome implements Home {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
// Not a device handler
|
||||
return null;
|
||||
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
boolean halFound = false;
|
||||
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 = null;
|
||||
|
||||
anUrl = BrightnessDecode.calculateReplaceIntensityValue(intermediate, 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(hostPortion)) {
|
||||
halFound = true;
|
||||
if(entry.getValue().getHalAddress().getSecure()!= null && entry.getValue().getHalAddress().getSecure())
|
||||
anUrl = "https://" + anUrl;
|
||||
else
|
||||
anUrl = "http://" + anUrl;
|
||||
|
||||
if(!anUrl.contains("?Token="))
|
||||
anUrl = anUrl + "?Token=" + entry.getValue().getHalAddress().getPassword();
|
||||
|
||||
log.debug("executing HUE api request to Http "
|
||||
+ (anItem.getHttpVerb() == null ? "GET" : anItem.getHttpVerb()) + ": "
|
||||
+ anUrl);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!halFound) {
|
||||
log.warn("No HAL found to call: " + theUrl);
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"No HAL found.", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,7 +202,13 @@ public class HalHome implements Home {
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// noop
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
|
||||
hals = null;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHome;
|
||||
import com.bwssystems.HABridge.util.TextStringFormatter;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
@@ -35,13 +36,13 @@ public class HalInfo {
|
||||
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();
|
||||
httpClient = HTTPHome.getHandler();
|
||||
halAddress = addressName;
|
||||
theToken = aGivenToken;
|
||||
if(halAddress.getPassword() == null || halAddress.getPassword().trim().isEmpty())
|
||||
halAddress.setPassword(aGivenToken);
|
||||
}
|
||||
|
||||
public List<HalDevice> getLights() {
|
||||
@@ -98,12 +99,16 @@ public class HalInfo {
|
||||
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
theUrl = "http://" + halAddress.getIp() + apiType + theToken;
|
||||
if(halAddress.getSecure()!= null && halAddress.getSecure())
|
||||
theUrl = "https://";
|
||||
else
|
||||
theUrl = "http://";
|
||||
theUrl = theUrl + halAddress.getIp() + apiType + halAddress.getPassword();
|
||||
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) {
|
||||
if(theHalApiResponse == null || 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.");
|
||||
@@ -121,8 +126,10 @@ public class HalInfo {
|
||||
HalDevice aNewHalDevice = new HalDevice();
|
||||
aNewHalDevice.setHaldevicetype(deviceType);
|
||||
aNewHalDevice.setHaldevicename(theDevice.getDeviceName());
|
||||
aNewHalDevice.setHaladdress(halAddress.getIp());
|
||||
aNewHalDevice.setHalname(halAddress.getName());
|
||||
NamedIP theaddress = new NamedIP();
|
||||
theaddress.setIp(halAddress.getIp());
|
||||
theaddress.setName(halAddress.getName());
|
||||
aNewHalDevice.setHaladdress(theaddress);
|
||||
deviceList.add(aNewHalDevice);
|
||||
|
||||
}
|
||||
@@ -145,7 +152,11 @@ public class HalInfo {
|
||||
deviceList = new ArrayList<HalDevice>();
|
||||
while (theHalDevices.hasNext()) {
|
||||
HalDevice theHalDevice = theHalDevices.next();
|
||||
theUrl = "http://" + halAddress.getIp() + IRBUTTON_REQUEST + TextStringFormatter.forQuerySpaceUrl(theHalDevice.getHaldevicename()) + TOKEN_REQUEST + theToken;
|
||||
if(halAddress.getSecure()!= null && halAddress.getSecure())
|
||||
theUrl = "https://";
|
||||
else
|
||||
theUrl = "http://";
|
||||
theUrl = theUrl + halAddress.getIp() + IRBUTTON_REQUEST + TextStringFormatter.forQuerySpaceUrl(theHalDevice.getHaldevicename()) + TOKEN_REQUEST + halAddress.getPassword();
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
|
||||
if (theData != null) {
|
||||
log.debug("GET IrData for IR Device " + theHalDevice.getHaldevicename() + " HalApiResponse - data: " + theData);
|
||||
@@ -177,6 +188,12 @@ public class HalInfo {
|
||||
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;
|
||||
}
|
||||
@@ -190,6 +207,5 @@ public class HalInfo {
|
||||
httpClient.closeHandler();
|
||||
httpClient = null;
|
||||
halAddress = null;
|
||||
theToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ public class ButtonPress {
|
||||
private Integer delay;
|
||||
private Integer count;
|
||||
private String hub;
|
||||
private Integer pressTime;
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
@@ -43,4 +44,10 @@ public class ButtonPress {
|
||||
public void setHub(String hub) {
|
||||
this.hub = hub;
|
||||
}
|
||||
public Integer getPressTime() {
|
||||
return pressTime;
|
||||
}
|
||||
public void setPressTime(Integer pressTime) {
|
||||
this.pressTime = pressTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class HarmonyHandler {
|
||||
}
|
||||
|
||||
public Boolean startActivity(RunActivity anActivity) {
|
||||
log.debug("Harmony api start activity requested for: " + anActivity.getName() + " noop mode: " + noopCalls);
|
||||
log.debug("Harmony api start activity requested for: " + anActivity.getName() + " for a hub: " + anActivity.getHub() + " noop mode: " + noopCalls);
|
||||
if (anActivity.isValid()) {
|
||||
try {
|
||||
if (noopCalls || devMode) {
|
||||
@@ -72,7 +72,7 @@ public class HarmonyHandler {
|
||||
devResponse.setCurrentActivity(devResponse.getConfig().getActivityByName(anActivity.getName()));
|
||||
}
|
||||
|
||||
log.info("noop mode: Harmony api start activity requested for: " + anActivity.getName());
|
||||
log.info("noop mode: Harmony api start activity requested for: " + anActivity.getName() + " for a hub: " + anActivity.getHub());
|
||||
}
|
||||
else
|
||||
harmonyClient.startActivity(Integer.parseInt(anActivity.getName()));
|
||||
@@ -81,12 +81,12 @@ public class HarmonyHandler {
|
||||
if (!noopCalls)
|
||||
harmonyClient.startActivityByName(anActivity.getName());
|
||||
} catch (IllegalArgumentException ei) {
|
||||
log.error("Error in finding activity: " + anActivity.getName());
|
||||
log.error("Error in finding activity: " + anActivity.getName() + " for a hub: " + anActivity.getHub());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("Error in finding activity: " + anActivity.getName());
|
||||
log.error("Error in finding activity: " + anActivity.getName() + " for a hub: " + anActivity.getHub());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -94,26 +94,30 @@ public class HarmonyHandler {
|
||||
}
|
||||
|
||||
public Boolean pressButton(ButtonPress aDeviceButton) {
|
||||
log.debug("Harmony api press a button requested for device: " + aDeviceButton.getDevice() + " and a for button: " + aDeviceButton.getButton() + " noop mode: " + noopCalls);
|
||||
log.debug("Harmony api press a button requested for device: " + aDeviceButton.getDevice() + " and a for button: " + aDeviceButton.getButton() + " with pressTime of: " + aDeviceButton.getPressTime() + " for a hub: " + aDeviceButton.getHub() + " noop mode: " + noopCalls);
|
||||
if (aDeviceButton.isValid()) {
|
||||
try {
|
||||
if (noopCalls || devMode) {
|
||||
log.info("noop mode: Harmony api press a button requested for device: " + aDeviceButton.getDevice() + " and a for button: " + aDeviceButton.getButton());
|
||||
log.info("noop mode: Harmony api press a button requested for device: " + aDeviceButton.getDevice() + " and a for button: " + aDeviceButton.getButton() +
|
||||
" with a pressTime of: " + aDeviceButton.getPressTime() + " for a hub: " + aDeviceButton.getHub());
|
||||
}
|
||||
else
|
||||
harmonyClient.pressButton(Integer.parseInt(aDeviceButton.getDevice()), aDeviceButton.getButton());
|
||||
|
||||
else {
|
||||
if(aDeviceButton.getPressTime() != null && aDeviceButton.getPressTime() > 0)
|
||||
harmonyClient.pressButton(Integer.parseInt(aDeviceButton.getDevice()), aDeviceButton.getButton(), aDeviceButton.getPressTime());
|
||||
else
|
||||
harmonyClient.pressButton(Integer.parseInt(aDeviceButton.getDevice()), aDeviceButton.getButton());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
if (!noopCalls)
|
||||
harmonyClient.pressButton(aDeviceButton.getDevice(), aDeviceButton.getButton());
|
||||
} catch (IllegalArgumentException ei) {
|
||||
log.error("Error in finding device: " + aDeviceButton.getDevice() +" and a button: " + aDeviceButton.getButton());
|
||||
log.error("Error in finding device: " + aDeviceButton.getDevice() +" and a button: " + aDeviceButton.getButton() + " for a hub: " + aDeviceButton.getHub());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("Error in finding device: " + aDeviceButton.getDevice() +" and a button: " + aDeviceButton.getButton());
|
||||
log.error("Error in finding device: " + aDeviceButton.getDevice() +" and a button: " + aDeviceButton.getButton() + " for a hub: " + aDeviceButton.getHub());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ 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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
@@ -31,16 +32,24 @@ public class HarmonyHome implements Home {
|
||||
private Boolean isDevMode;
|
||||
private Boolean validHarmony;
|
||||
private Gson aGsonHandler;
|
||||
private boolean closed;
|
||||
|
||||
public HarmonyHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validHarmony)
|
||||
return;
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
if(isDevMode || hubs == null)
|
||||
return;
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
@@ -50,6 +59,7 @@ public class HarmonyHome implements Home {
|
||||
}
|
||||
|
||||
hubs = null;
|
||||
closed = true;
|
||||
}
|
||||
|
||||
public HarmonyHandler getHarmonyHandler(String aName) {
|
||||
@@ -125,7 +135,7 @@ public class HarmonyHome implements Home {
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
log.debug("executing HUE api request to change " + anItem.getType() + " to Harmony: " + device.getName());
|
||||
if(!validHarmony) {
|
||||
@@ -181,7 +191,7 @@ public class HarmonyHome implements Home {
|
||||
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));
|
||||
log.debug("pressing button: " + deviceButtons[z].getDevice() + " - " + deviceButtons[z].getButton() + " with pressTime of: " + deviceButtons[z].getPressTime() + " - 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());
|
||||
@@ -259,4 +269,10 @@ public class HarmonyHome implements Home {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import static java.lang.String.format;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHome;
|
||||
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -44,7 +46,7 @@ public class HarmonyServer {
|
||||
dummyProvider = null;
|
||||
myNameAndIP = theHarmonyAddress;
|
||||
isDevMode = false;
|
||||
httpClient = new HTTPHandler();
|
||||
httpClient = HTTPHome.getHandler();
|
||||
}
|
||||
|
||||
public static HarmonyServer setup(
|
||||
|
||||
@@ -15,6 +15,7 @@ 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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
@@ -25,10 +26,13 @@ public class HassHome implements Home {
|
||||
private Map<String, HomeAssistant> hassMap;
|
||||
private Boolean validHass;
|
||||
private Gson aGsonHandler;
|
||||
private boolean closed;
|
||||
|
||||
public HassHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -113,9 +117,14 @@ public class HassHome implements Home {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
String theReturn = null;
|
||||
log.debug("executing HUE api request to send message to HomeAssistant: " + anItem.getItem().toString());
|
||||
if(!validHass) {
|
||||
@@ -151,6 +160,11 @@ public class HassHome implements Home {
|
||||
public void closeHome() {
|
||||
if(!validHass)
|
||||
return;
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
if(hassMap == null)
|
||||
return;
|
||||
Iterator<String> keys = hassMap.keySet().iterator();
|
||||
@@ -160,5 +174,6 @@ public class HassHome implements Home {
|
||||
}
|
||||
|
||||
hassMap = null;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHome;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class HomeAssistant {
|
||||
@@ -21,7 +22,7 @@ public class HomeAssistant {
|
||||
|
||||
public HomeAssistant(NamedIP addressName) {
|
||||
super();
|
||||
anHttpHandler = new HTTPHandler();
|
||||
anHttpHandler = HTTPHome.getHandler();
|
||||
hassAddress = addressName;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.bwssystems.HABridge.plugins.homewizard;
|
||||
|
||||
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.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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
|
||||
/**
|
||||
* Control HomeWizard devices over HomeWizard Cloud
|
||||
*
|
||||
* @author Björn Rennfanz (bjoern@fam-rennfanz.de)
|
||||
*
|
||||
*/
|
||||
public class HomeWizardHome implements Home {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(HomeWizardHome.class);
|
||||
|
||||
private Map<String, HomeWizzardSmartPlugInfo> plugGateways;
|
||||
private Boolean validHomeWizard;
|
||||
private boolean closed;
|
||||
|
||||
public HomeWizardHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
|
||||
String responseString = null;
|
||||
if (!validHomeWizard) {
|
||||
|
||||
log.warn("Should not get here, no HomeWizard smart plug available");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no HomeWizard smart plug available\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
|
||||
if (anItem.getType() != null && anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex])) {
|
||||
|
||||
log.debug("Executing HUE api request to change activity to HomeWizard smart plug: " + anItem.getItem().toString());
|
||||
String jsonToPost = anItem.getItem().toString();
|
||||
|
||||
HomeWizzardSmartPlugInfo homeWizzardHandler = getHomeWizzardHandler(device.getTargetDevice());
|
||||
if(homeWizzardHandler == null) {
|
||||
log.warn("Should not get here, no HomeWizard smart plug configured");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no HomeWizard smart plug configured\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
try {
|
||||
homeWizzardHandler.execApply(jsonToPost);
|
||||
} catch (Exception e) {
|
||||
|
||||
log.warn("Error posting request to HomeWizard smart plug");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Error posting request to HomeWizard smart plug\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return responseString;
|
||||
}
|
||||
|
||||
public HomeWizzardSmartPlugInfo getHomeWizzardHandler(String plugName) {
|
||||
return plugGateways.get(plugName);
|
||||
}
|
||||
|
||||
public List<HomeWizardSmartPlugDevice> getDevices() {
|
||||
|
||||
log.debug("consolidating devices for HomeWizard plug gateways");
|
||||
Iterator<String> keys = plugGateways.keySet().iterator();
|
||||
ArrayList<HomeWizardSmartPlugDevice> deviceList = new ArrayList<>();
|
||||
|
||||
while(keys.hasNext())
|
||||
{
|
||||
String key = keys.next();
|
||||
for(HomeWizardSmartPlugDevice device : plugGateways.get(key).getDevices())
|
||||
deviceList.add(device);
|
||||
}
|
||||
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
|
||||
if (validHomeWizard)
|
||||
{
|
||||
if (type.equalsIgnoreCase(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex]))
|
||||
{
|
||||
return getDevices();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
|
||||
validHomeWizard = bridgeSettings.getBridgeSettingsDescriptor().isValidHomeWizard();
|
||||
log.info("HomeWizard Home created. " + (validHomeWizard ? "" : "No HomeWizard gateways configured."));
|
||||
|
||||
if (validHomeWizard)
|
||||
{
|
||||
plugGateways = new HashMap<>();
|
||||
Iterator<NamedIP> gatewaysList = bridgeSettings.getBridgeSettingsDescriptor().getHomeWizardAddress().getDevices().iterator();
|
||||
|
||||
while(gatewaysList.hasNext()) {
|
||||
|
||||
NamedIP gateway = gatewaysList.next();
|
||||
plugGateways.put(gateway.getName(), new HomeWizzardSmartPlugInfo(gateway, gateway.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
|
||||
plugGateways = null;
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.bwssystems.HABridge.plugins.homewizard;
|
||||
|
||||
/**
|
||||
* Control HomeWizard devices over HomeWizard Cloud
|
||||
*
|
||||
* @author Björn Rennfanz (bjoern@fam-rennfanz.de)
|
||||
*
|
||||
*/
|
||||
public class HomeWizardSmartPlugDevice {
|
||||
|
||||
private String name;
|
||||
private String gateway;
|
||||
private String id;
|
||||
private String typeName;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getGateway() {
|
||||
return gateway;
|
||||
}
|
||||
|
||||
public void setGateway(String gateway) {
|
||||
this.gateway = gateway;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTypeName() {
|
||||
return this.typeName;
|
||||
}
|
||||
|
||||
public void setTypeName(String typeName) {
|
||||
this.typeName = typeName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package com.bwssystems.HABridge.plugins.homewizard;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.plugins.homewizard.json.Device;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* Control HomeWizard devices over HomeWizard Cloud
|
||||
*
|
||||
* @author Björn Rennfanz (bjoern@fam-rennfanz.de)
|
||||
*
|
||||
*/
|
||||
public class HomeWizzardSmartPlugInfo {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(HomeWizardHome.class);
|
||||
|
||||
private static final String HOMEWIZARD_LOGIN_URL = "https://cloud.homewizard.com/account/login";
|
||||
private static final String HOMEWIZARD_API_URL = "https://plug.homewizard.com/plugs";
|
||||
private static final String EMPTY_STRING = "";
|
||||
|
||||
private final String cloudAuth;
|
||||
private final Gson gson;
|
||||
|
||||
private String cloudSessionId;
|
||||
private String cloudPlugName;
|
||||
private String cloudPlugId;
|
||||
|
||||
public HomeWizzardSmartPlugInfo(NamedIP gateway, String name) {
|
||||
|
||||
super();
|
||||
|
||||
cloudAuth = "Basic " + new String(Base64.encodeBase64((gateway.getUsername() + ":" + DigestUtils.sha1Hex(gateway.getPassword())).getBytes()));
|
||||
cloudPlugName = name;
|
||||
gson = new Gson();
|
||||
}
|
||||
|
||||
public boolean login()
|
||||
{
|
||||
try
|
||||
{
|
||||
URL url = new URL(HOMEWIZARD_LOGIN_URL);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("Authorization", cloudAuth);
|
||||
connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
|
||||
connection.connect();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
String line;
|
||||
|
||||
while((line = br.readLine()) != null)
|
||||
{
|
||||
buffer.append(line).append("\n");
|
||||
}
|
||||
br.close();
|
||||
|
||||
// Get session id from result JSON
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject json = parser.parse(buffer.toString()).getAsJsonObject();
|
||||
|
||||
cloudSessionId = json.get("session").getAsString();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
log.warn("Error while login to cloud service ", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String requestJson(String request)
|
||||
{
|
||||
String result = null;
|
||||
|
||||
// Check login was successful
|
||||
if (login()) {
|
||||
|
||||
// Request JSON from Cloud service
|
||||
try
|
||||
{
|
||||
URL url = new URL(HOMEWIZARD_API_URL + request);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("X-Session-Token", cloudSessionId);
|
||||
connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
|
||||
connection.connect();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
String line;
|
||||
|
||||
while((line = br.readLine()) != null)
|
||||
{
|
||||
buffer.append(line).append("\n");
|
||||
}
|
||||
|
||||
br.close();
|
||||
|
||||
result = buffer.toString();
|
||||
result = StringUtils.strip(result, "[]");
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
log.warn("Error while get json request: {} ", request, e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean sendAction(String request, String action)
|
||||
{
|
||||
boolean result = true;
|
||||
|
||||
// Check login was successful
|
||||
if (login()) {
|
||||
|
||||
// Post action into Cloud service
|
||||
try
|
||||
{
|
||||
URL url = new URL(HOMEWIZARD_API_URL + request);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
JsonObject actionJson = new JsonObject();
|
||||
actionJson.addProperty("action", StringUtils.capitalize(action));
|
||||
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestProperty("X-Session-Token", cloudSessionId);
|
||||
connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
|
||||
|
||||
OutputStream os = connection.getOutputStream();
|
||||
os.write(actionJson.toString().getBytes("UTF-8"));
|
||||
os.close();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
String line;
|
||||
|
||||
while((line = br.readLine()) != null)
|
||||
{
|
||||
buffer.append(line).append("\n");
|
||||
}
|
||||
|
||||
br.close();
|
||||
connection.disconnect();
|
||||
|
||||
// Check if request was Ok
|
||||
if (!buffer.toString().contains("Success"))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
log.warn("Error while post json action: {} ", request, e);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<HomeWizardSmartPlugDevice> getDevices()
|
||||
{
|
||||
List<HomeWizardSmartPlugDevice> homewizardDevices = new ArrayList<>();
|
||||
try {
|
||||
|
||||
String result = requestJson(EMPTY_STRING);
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject resultJson = parser.parse(result).getAsJsonObject();
|
||||
cloudPlugId = resultJson.get("id").getAsString();
|
||||
|
||||
String all_devices_json = resultJson.get("devices").toString();
|
||||
Device[] devices = gson.fromJson(all_devices_json, Device[].class);
|
||||
|
||||
// Fix names from JSON
|
||||
for (Device device : devices) {
|
||||
device.setTypeName(StringUtils.capitalize(device.getTypeName().replace("_", " ")));
|
||||
homewizardDevices.add(mapDeviceToHomeWizardSmartPlugDevice(device));
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
log.warn("Error while get devices from cloud service ", e);
|
||||
}
|
||||
|
||||
log.info("Found: " + homewizardDevices.size() + " devices");
|
||||
return homewizardDevices;
|
||||
}
|
||||
|
||||
public void execApply(String jsonToPost) throws Exception {
|
||||
|
||||
// Extract
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject resultJson = parser.parse(jsonToPost).getAsJsonObject();
|
||||
String deviceId = resultJson.get("deviceid").getAsString();
|
||||
String action = resultJson.get("action").getAsString();
|
||||
|
||||
// Check if we have an plug id stored
|
||||
if (StringUtils.isBlank(cloudPlugId)) {
|
||||
getDevices();
|
||||
}
|
||||
|
||||
// Send request to HomeWizard cloud
|
||||
if (!sendAction("/" + cloudPlugId + "/devices/" + deviceId + "/action", action))
|
||||
{
|
||||
throw new IOException("Send action to HomeWizard Cloud failed.");
|
||||
}
|
||||
}
|
||||
|
||||
protected HomeWizardSmartPlugDevice mapDeviceToHomeWizardSmartPlugDevice(Device device) {
|
||||
HomeWizardSmartPlugDevice homewizardDevice = new HomeWizardSmartPlugDevice();
|
||||
homewizardDevice.setId(device.getId());
|
||||
homewizardDevice.setGateway(cloudPlugName);
|
||||
homewizardDevice.setName(device.getName());
|
||||
homewizardDevice.setTypeName(device.getTypeName());
|
||||
|
||||
return homewizardDevice;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.bwssystems.HABridge.plugins.homewizard.json;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Control HomeWizard devices over HomeWizard Cloud
|
||||
*
|
||||
* @author Björn Rennfanz (bjoern@fam-rennfanz.de)
|
||||
*
|
||||
*/
|
||||
public class Device {
|
||||
|
||||
@SerializedName("id")
|
||||
@Expose
|
||||
private String id;
|
||||
|
||||
@SerializedName("name")
|
||||
@Expose
|
||||
private String name;
|
||||
|
||||
@SerializedName("typeName")
|
||||
@Expose
|
||||
private String typeName;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getTypeName() {
|
||||
return this.typeName;
|
||||
}
|
||||
|
||||
public void setTypeName(String typeName) {
|
||||
this.typeName = typeName;
|
||||
}
|
||||
}
|
||||
@@ -5,44 +5,50 @@ 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.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
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.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
|
||||
public class HTTPHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(HTTPHandler.class);
|
||||
private CloseableHttpClient httpClient;
|
||||
private RequestConfig globalConfig;
|
||||
|
||||
private String callType;
|
||||
|
||||
public HTTPHandler() {
|
||||
globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
|
||||
httpClient = HttpClients.custom().setDefaultRequestConfig(globalConfig).build();
|
||||
super();
|
||||
callType = null;
|
||||
}
|
||||
|
||||
|
||||
public HTTPHandler(String type) {
|
||||
super();
|
||||
callType = type;
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
log.debug("doHttpRequest with url <<<" + url + ">>>, verb: " + httpVerb + ", contentType: " + contentType + ", body <<<" + body + ">>>" );
|
||||
if(headers != null && headers.length > 0)
|
||||
for(int i = 0; i < headers.length; i++)
|
||||
log.debug("header index " + i + " name: <<<" + headers[i].getName() + ">>>, value: <<<" + headers[i].getValue() + ">>>");
|
||||
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)
|
||||
@@ -54,6 +60,7 @@ public class HTTPHandler {
|
||||
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);
|
||||
@@ -67,93 +74,86 @@ public class HTTPHandler {
|
||||
if (requestBody != null)
|
||||
putRequest.setEntity(requestBody);
|
||||
request = putRequest;
|
||||
}
|
||||
else
|
||||
} 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);
|
||||
log.debug("Making outbound call in doHttpRequest: <<<" + request.toString() + ">>>");
|
||||
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);
|
||||
CloseableHttpResponse response = null;
|
||||
for (int retryCount = 0; retryCount < 2; retryCount++) {
|
||||
try {
|
||||
response = HttpClientPool.getClient().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());
|
||||
if (response != null && response.getEntity() != null) {
|
||||
try {
|
||||
String someContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); // read
|
||||
// content
|
||||
// for
|
||||
// data
|
||||
|
||||
theContent = 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 + ">>>");
|
||||
// inputstream
|
||||
// ignore
|
||||
// content
|
||||
} catch (Exception e) {
|
||||
//noop
|
||||
log.debug("Error ocurred in handling response entity after successful call, still responding success. "
|
||||
+ e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (response != null && response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300) {
|
||||
if(theContent == null)
|
||||
theContent = "";
|
||||
log.debug("Successfull response - The http response is <<<" + theContent + ">>>");
|
||||
retryCount = 2;
|
||||
} else if (callType != null && callType == DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex] && response.getStatusLine().getStatusCode() == 302) {
|
||||
if(theContent == null)
|
||||
theContent = "";
|
||||
log.debug("Successfull response - The http response is <<<" + theContent + ">>>");
|
||||
retryCount = 2;
|
||||
} else if (response != null) {
|
||||
log.warn("HTTP response code was not an expected successful response of between 200 - 299, the code was: "
|
||||
+ response.getStatusLine() + " with the content of <<<" + theContent + ">>>");
|
||||
if (response.getStatusLine().getStatusCode() == 504) {
|
||||
log.warn("HTTP response code was 504, retrying...");
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e1) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
else
|
||||
log.debug("The 504 error content is <<<" + theContent + ">>>");
|
||||
theContent = null;
|
||||
} else
|
||||
retryCount = 2;
|
||||
}
|
||||
|
||||
} catch (ClientProtocolException e) {
|
||||
log.warn("Client Protocol Exception received, retyring....");
|
||||
}catch (IOException e) {
|
||||
log.warn("Error calling out to HA gateway: IOException in log: " + e.getMessage());
|
||||
retryCount = 2;
|
||||
}
|
||||
|
||||
if(retryCount < 2) {
|
||||
theContent = null;
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e1) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
} 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 setCallType(String callType) {
|
||||
this.callType = callType;
|
||||
}
|
||||
|
||||
|
||||
public void closeHandler() {
|
||||
try {
|
||||
httpClient.close();
|
||||
} catch (IOException e) {
|
||||
// noop
|
||||
}
|
||||
httpClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,23 +11,37 @@ 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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.ColorDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.bwssystems.HABridge.plugins.fibaro.FibaroTestData;
|
||||
import com.bwssystems.HABridge.plugins.vera.VeraTestData;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class HTTPHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(HTTPHome.class);
|
||||
private HTTPHandler anHttpHandler;
|
||||
private static HTTPHandler anHttpHandler = null;
|
||||
private Boolean isDevMode;
|
||||
private boolean closed;
|
||||
|
||||
public HTTPHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
public static HTTPHandler getHandler() {
|
||||
if(anHttpHandler == null)
|
||||
anHttpHandler = new HTTPHandler();
|
||||
return anHttpHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
@@ -50,6 +64,9 @@ public class HTTPHome implements Home {
|
||||
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrl,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
if (colorData != null) {
|
||||
anUrl = ColorDecode.replaceColorData(anUrl, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
|
||||
}
|
||||
anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device);
|
||||
anUrl = TimeDecode.replaceTimeValue(anUrl);
|
||||
|
||||
@@ -57,17 +74,23 @@ public class HTTPHome implements Home {
|
||||
if(anItem.getHttpBody()!= null && !anItem.getHttpBody().isEmpty()) {
|
||||
aBody = BrightnessDecode.calculateReplaceIntensityValue(anItem.getHttpBody(),
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
if (colorData != null) {
|
||||
aBody = ColorDecode.replaceColorData(aBody, colorData, BrightnessDecode.calculateIntensity(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) {
|
||||
String httpReply = anHttpHandler.doHttpRequest(anUrl, anItem.getHttpVerb(), anItem.getContentType(), aBody, new Gson().fromJson(anItem.getHttpHeaders(), NameValue[].class));
|
||||
if (httpReply == 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);
|
||||
}
|
||||
|
||||
if(isDevMode)
|
||||
log.info("Dev Mode response dump <<<" + httpReply +">>>");
|
||||
} 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,
|
||||
@@ -80,8 +103,21 @@ public class HTTPHome implements Home {
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
anHttpHandler = new HTTPHandler();
|
||||
log.info("Http Home created.");
|
||||
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
|
||||
log.info("HTTP Home created." + (isDevMode ? " DevMode is set." : ""));
|
||||
if(isDevMode) {
|
||||
anHttpHandler = new HttpTestHandler();
|
||||
((HttpTestHandler)anHttpHandler).setTheData("id=sdata", VeraTestData.SDataTestData);
|
||||
((HttpTestHandler)anHttpHandler).setTheData("action=SetLoadLevelTarget", VeraTestData.SetLoadLevelTargetData);
|
||||
((HttpTestHandler)anHttpHandler).setTheData("action=SetTarget", VeraTestData.SetTargetData);
|
||||
((HttpTestHandler)anHttpHandler).setTheData("action=RunScene", VeraTestData.RunSceneData);
|
||||
((HttpTestHandler)anHttpHandler).setTheData("/api/callAction", FibaroTestData.CallActionTestData);
|
||||
((HttpTestHandler)anHttpHandler).setTheData("/api/sceneControl", FibaroTestData.SceneControlTestData);
|
||||
((HttpTestHandler)anHttpHandler).setTheData(null, "generic treply - no match");
|
||||
|
||||
}
|
||||
if(anHttpHandler == null)
|
||||
anHttpHandler = new HTTPHandler();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -93,9 +129,19 @@ public class HTTPHome implements Home {
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
if(anHttpHandler != null)
|
||||
anHttpHandler.closeHandler();
|
||||
anHttpHandler = null;
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package com.bwssystems.HABridge.plugins.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class HttpClientPool {
|
||||
private static final Logger log = LoggerFactory.getLogger(HttpClientPool.class);
|
||||
|
||||
// Single-element enum to implement Singleton.
|
||||
private static enum Singleton {
|
||||
// Just one of me so constructor will be called once.
|
||||
Client;
|
||||
// The thread-safe client.
|
||||
private final CloseableHttpClient threadSafeClient;
|
||||
// The pool monitor.
|
||||
private final IdleConnectionMonitorThread monitor;
|
||||
|
||||
// The constructor creates it - thus late
|
||||
private Singleton() {
|
||||
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
|
||||
// Increase max total connection to 200
|
||||
cm.setMaxTotal(200);
|
||||
// Increase default max connection per route to 20
|
||||
cm.setDefaultMaxPerRoute(20);
|
||||
// Build the client.
|
||||
threadSafeClient = HttpClients.custom()
|
||||
.setConnectionManager(cm)
|
||||
.build();
|
||||
// Start up an eviction thread.
|
||||
monitor = new IdleConnectionMonitorThread(cm);
|
||||
// Don't stop quitting.
|
||||
monitor.setDaemon(true);
|
||||
monitor.start();
|
||||
}
|
||||
|
||||
public CloseableHttpClient get() {
|
||||
return threadSafeClient;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static CloseableHttpClient getClient() {
|
||||
// The thread safe client is held by the singleton.
|
||||
return Singleton.Client.get();
|
||||
}
|
||||
|
||||
// Watches for stale connections and evicts them.
|
||||
private static class IdleConnectionMonitorThread extends Thread {
|
||||
// The manager to watch.
|
||||
private final PoolingHttpClientConnectionManager cm;
|
||||
// Use a BlockingQueue to stop everything.
|
||||
private final BlockingQueue<Stop> stopSignal = new ArrayBlockingQueue<Stop>(1);
|
||||
|
||||
// Pushed up the queue.
|
||||
private static class Stop {
|
||||
// The return queue.
|
||||
private final BlockingQueue<Stop> stop = new ArrayBlockingQueue<Stop>(1);
|
||||
|
||||
// Called by the process that is being told to stop.
|
||||
public void stopped() {
|
||||
// Push me back up the queue to indicate we are now stopped.
|
||||
stop.add(this);
|
||||
}
|
||||
|
||||
// Called by the process requesting the stop.
|
||||
public void waitForStopped() throws InterruptedException {
|
||||
// Wait until the callee acknowledges that it has stopped.
|
||||
stop.take();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IdleConnectionMonitorThread(PoolingHttpClientConnectionManager cm) {
|
||||
super();
|
||||
this.cm = cm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Holds the stop request that stopped the process.
|
||||
Stop stopRequest;
|
||||
// Every 5 seconds.
|
||||
while ((stopRequest = stopSignal.poll(5, TimeUnit.SECONDS)) == null) {
|
||||
// Close expired connections
|
||||
cm.closeExpiredConnections();
|
||||
// Optionally, close connections that have been idle too long.
|
||||
cm.closeIdleConnections(60, TimeUnit.SECONDS);
|
||||
// Look at pool stats.
|
||||
log.debug("Stats: {}", cm.getTotalStats());
|
||||
}
|
||||
// Acknowledge the stop request.
|
||||
stopRequest.stopped();
|
||||
} catch (InterruptedException ex) {
|
||||
// terminate
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() throws InterruptedException, IOException {
|
||||
log.info("Shutting down client pool");
|
||||
// Signal the stop to the thread.
|
||||
Stop stop = new Stop();
|
||||
stopSignal.add(stop);
|
||||
// Wait for the stop to complete.
|
||||
stop.waitForStopped();
|
||||
// Close the pool - Added
|
||||
Singleton.Client.threadSafeClient.close();
|
||||
// Close the connection manager.
|
||||
cm.close();
|
||||
log.info("Client pool shut down");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void shutdown() throws InterruptedException, IOException {
|
||||
// Shutdown the monitor.
|
||||
Singleton.Client.monitor.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.bwssystems.HABridge.plugins.http;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
|
||||
public class HttpTestHandler extends HTTPHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(HttpTestHandler.class);
|
||||
private List<NameValue> theData;
|
||||
|
||||
public void setTheData(String compareValue, String testData) {
|
||||
if( this.theData == null )
|
||||
this.theData = new ArrayList<NameValue>();
|
||||
NameValue aValueSet = new NameValue();
|
||||
aValueSet.setName(compareValue);
|
||||
aValueSet.setValue(testData);
|
||||
this.theData.add(aValueSet);
|
||||
}
|
||||
|
||||
public void updateTheData(String compareValue, String testData) {
|
||||
if( this.theData == null ) {
|
||||
this.theData = new ArrayList<NameValue>();
|
||||
NameValue aValueSet = new NameValue();
|
||||
aValueSet.setName(compareValue);
|
||||
aValueSet.setValue(testData);
|
||||
this.theData.add(aValueSet);
|
||||
}
|
||||
else {
|
||||
for(NameValue aTest:this.theData) {
|
||||
if(aTest.getName().equals(compareValue));
|
||||
aTest.setValue(testData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String doHttpRequest(String url, String httpVerb, String contentType, String body, NameValue[] headers) {
|
||||
log.info("doHttpRequest with url <<<" + url + ">>>, verb: " + httpVerb + ", contentType: " + contentType + ", body <<<" + body + ">>>" );
|
||||
if(headers != null && headers.length > 0)
|
||||
for(int i = 0; i < headers.length; i++)
|
||||
log.info("header index " + i + " name: <<<" + headers[i].getName() + ">>>, value: <<<" + headers[i].getValue() + ">>>");
|
||||
String responseData = null;
|
||||
for(NameValue aTest:theData) {
|
||||
if(aTest.getName() == null)
|
||||
responseData = aTest.getValue();
|
||||
else {
|
||||
if(url.contains(aTest.getName()))
|
||||
responseData = aTest.getValue();
|
||||
else if(aTest.getName() == null || aTest.getName().isEmpty())
|
||||
responseData = aTest.getValue();
|
||||
}
|
||||
|
||||
if(responseData != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if(responseData == null)
|
||||
responseData = "No data was set for HttpTestHandler for your request url.";
|
||||
return responseData;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -15,6 +16,7 @@ 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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
@@ -24,10 +26,15 @@ public class HueHome implements Home {
|
||||
private Map<String, HueInfo> hues;
|
||||
private Boolean validHue;
|
||||
private Gson aGsonHandler;
|
||||
private BridgeSettings theBridgeSettings;
|
||||
private boolean closed;
|
||||
|
||||
public HueHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
theBridgeSettings = bridgeSettings;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,7 +91,7 @@ public class HueHome implements Home {
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
if(!validHue)
|
||||
return null;
|
||||
String responseString = null;
|
||||
@@ -113,17 +120,29 @@ public class HueHome implements Home {
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHueaddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHue = theList.next();
|
||||
hues.put(aHue.getName(), new HueInfo(aHue));
|
||||
hues.put(aHue.getName(), new HueInfo(aHue, this));
|
||||
}
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void updateHue(NamedIP aHue) {
|
||||
theBridgeSettings.getBridgeSettingsDescriptor().updateHue(aHue);
|
||||
if(theBridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged()) {
|
||||
theBridgeSettings.updateConfigFile();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validHue)
|
||||
return;
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
if(hues == null)
|
||||
return;
|
||||
Iterator<String> keys = hues.keySet().iterator();
|
||||
@@ -132,5 +151,11 @@ public class HueHome implements Home {
|
||||
hues.get(key).closeHue();;
|
||||
}
|
||||
hues = null;
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
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;
|
||||
|
||||
@@ -20,19 +13,22 @@ 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.bwssystems.HABridge.plugins.http.HTTPHome;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
|
||||
public class HueInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(HueInfo.class);
|
||||
private HTTPHandler httpClient;
|
||||
private HTTPHandler httpHandler;
|
||||
private NamedIP hueAddress;
|
||||
private HueHome myHome;
|
||||
public static final String HUE_REQUEST = "/api";
|
||||
|
||||
public HueInfo(NamedIP addressName) {
|
||||
public HueInfo(NamedIP addressName, HueHome theHome) {
|
||||
super();
|
||||
httpClient = new HTTPHandler();
|
||||
httpHandler = HTTPHome.getHandler();
|
||||
hueAddress = addressName;
|
||||
myHome = theHome;
|
||||
}
|
||||
|
||||
public HueApiResponse getHueApiResponse() {
|
||||
@@ -62,8 +58,8 @@ public class HueInfo {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
theUrl = "http://" + hueAddress.getIp() + HueUtil.HUE_REQUEST + "/" + hueAddress.getUsername();
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
|
||||
theUrl = "http://" + hueAddress.getIp() + HUE_REQUEST + "/" + hueAddress.getUsername();
|
||||
theData = httpHandler.doHttpRequest(theUrl, null, null, null, null);
|
||||
if(theData != null) {
|
||||
log.debug("GET HueApiResponse - data: " + theData);
|
||||
if(theData.contains("[{\"error\":")) {
|
||||
@@ -96,34 +92,25 @@ public class HueInfo {
|
||||
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")) {
|
||||
|
||||
String aMessage = httpHandler.doHttpRequest("http://" + hueAddress.getIp() + HUE_REQUEST, HttpPost.METHOD_NAME, "application/json", new Gson().toJson(theLogin), null);
|
||||
|
||||
log.debug("registerWithHue - POST execute on " + hueAddress.getName() + "URL responded: " + aMessage);
|
||||
if(!aMessage.isEmpty()){
|
||||
log.debug("registerWithHue response data: " + aMessage);
|
||||
if(aMessage.contains("[{\"error\":")) {
|
||||
if(aMessage.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);
|
||||
log.warn("registerWithHue returned an unexpected error: " + aMessage);
|
||||
}
|
||||
else {
|
||||
SuccessUserResponse[] theResponses = new Gson().fromJson(theBody, SuccessUserResponse[].class); //read content for data, SuccessUserResponse[].class);
|
||||
SuccessUserResponse[] theResponses = new Gson().fromJson(aMessage, SuccessUserResponse[].class); //read content for data, SuccessUserResponse[].class);
|
||||
hueAddress.setUsername(theResponses[0].getSuccess().getUsername());
|
||||
myHome.updateHue(hueAddress);
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -135,7 +122,7 @@ public class HueInfo {
|
||||
registerWithHue();
|
||||
if (hueAddress.getUsername() != null) {
|
||||
// make call
|
||||
responseString = httpClient.doHttpRequest(
|
||||
responseString = httpHandler.doHttpRequest(
|
||||
"http://" + hueAddress.getIp() + "/api/" + hueAddress.getUsername()
|
||||
+ "/lights/" + hueDeviceId,
|
||||
HttpGet.METHOD_NAME, "application/json", null, null);
|
||||
@@ -164,7 +151,7 @@ public class HueInfo {
|
||||
if(hueAddress.getUsername() == null)
|
||||
registerWithHue();
|
||||
if (hueAddress.getUsername() != null) {
|
||||
responseString = httpClient.doHttpRequest(
|
||||
responseString = httpHandler.doHttpRequest(
|
||||
"http://" + deviceId.getIpAddress() + "/api/" + hueAddress.getUsername()
|
||||
+ "/lights/" + deviceId.getDeviceId() + "/state",
|
||||
HttpPut.METHOD_NAME, "application/json", body, null);
|
||||
@@ -185,8 +172,8 @@ public class HueInfo {
|
||||
}
|
||||
|
||||
public void closeHue() {
|
||||
httpClient.closeHandler();
|
||||
httpClient = null;
|
||||
httpHandler.closeHandler();
|
||||
httpHandler = null;
|
||||
}
|
||||
|
||||
public NamedIP getHueAddress() {
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
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.HttpPost;
|
||||
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.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(HTTPHandler anHttpHandler, String ipAddress, String aName, String theUser) {
|
||||
UserCreateRequest theLogin = new UserCreateRequest();
|
||||
theLogin.setDevicetype("HABridge#MyMachine");
|
||||
HttpPost postRequest = new HttpPost("http://" + ipAddress + 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 = anHttpHandler.getHttpClient();
|
||||
try {
|
||||
response = anHttpClient.execute(postRequest);
|
||||
log.debug("POST execute on 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: " + aName);
|
||||
}
|
||||
else
|
||||
log.warn("registerWithHue returned an unexpected error: " + theBody);
|
||||
}
|
||||
else {
|
||||
SuccessUserResponse[] theResponses = new Gson().fromJson(theBody, SuccessUserResponse[].class); //read content for data, SuccessUserResponse[].class);
|
||||
theUser = 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 theUser;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -19,6 +20,8 @@ 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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.ColorDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.github.besherman.lifx.LFXClient;
|
||||
import com.github.besherman.lifx.LFXGroup;
|
||||
@@ -37,10 +40,14 @@ public class LifxHome implements Home {
|
||||
private LFXClient client;
|
||||
private Boolean validLifx;
|
||||
private Gson aGsonHandler;
|
||||
private InetAddress configuredAddress;
|
||||
private boolean closed;
|
||||
|
||||
public LifxHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,47 +57,17 @@ public class LifxHome implements Home {
|
||||
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;
|
||||
aGsonHandler =
|
||||
new GsonBuilder()
|
||||
.create();
|
||||
try {
|
||||
configuredAddress = InetAddress.getByName(bridgeSettings.getBridgeSettingsDescriptor().getUpnpConfigAddress());
|
||||
} catch (UnknownHostException e) {
|
||||
log.warn("Could not get Inet Address for Lifx broadcast.");
|
||||
validLifx = false;
|
||||
return this;
|
||||
}
|
||||
broadcastDiscover();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -112,7 +89,7 @@ public class LifxHome implements Home {
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
log.debug("consolidating devices for lifx");
|
||||
if(!validLifx)
|
||||
if(!validLifx || lifxMap == null)
|
||||
return null;
|
||||
LifxEntry theResponse = null;
|
||||
Iterator<String> keys = lifxMap.keySet().iterator();
|
||||
@@ -130,6 +107,7 @@ public class LifxHome implements Home {
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Boolean addLifxLights(LFXLightCollection theDeviceList) {
|
||||
if(!validLifx)
|
||||
return false;
|
||||
@@ -142,6 +120,7 @@ public class LifxHome implements Home {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Boolean addLifxGroups(LFXGroupCollection theDeviceList) {
|
||||
if(!validLifx)
|
||||
return false;
|
||||
@@ -156,7 +135,7 @@ public class LifxHome implements Home {
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
String theReturn = null;
|
||||
float aBriValue;
|
||||
float theValue;
|
||||
@@ -194,6 +173,10 @@ public class LifxHome implements Home {
|
||||
theValue = (float)0.99;
|
||||
theLight.setBrightness(theValue);
|
||||
}
|
||||
if(colorData != null) {
|
||||
int rgbVal = ColorDecode.getIntRGB(colorData, intensity);
|
||||
theLight.setColor(rgbVal);
|
||||
}
|
||||
} else if (theDevice.getType().equals(LifxDevice.GROUP_TYPE)) {
|
||||
LFXGroup theGroup = (LFXGroup)theDevice.getLifxObject();
|
||||
if(body.contains("true"))
|
||||
@@ -210,7 +193,14 @@ public class LifxHome implements Home {
|
||||
public void closeHome() {
|
||||
if(!validLifx)
|
||||
return;
|
||||
client.close();
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
if(client != null)
|
||||
client.close();
|
||||
closed = true;
|
||||
}
|
||||
private static class MyLightListener implements LFXLightCollectionListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(MyLightListener.class);
|
||||
@@ -254,4 +244,47 @@ public class LifxHome implements Home {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void broadcastDiscover() {
|
||||
try {
|
||||
log.info("Open Lifx client....");
|
||||
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);
|
||||
} else {
|
||||
log.warn("Could not open LIFX, no bcast addr available, check your upnp config address.");
|
||||
client = null;
|
||||
return;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not open LIFX, with IO Exception", e);
|
||||
client = null;
|
||||
return;
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Could not open LIFX, with Interruprted Exception", e);
|
||||
client = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
if(client == null)
|
||||
broadcastDiscover();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ 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;
|
||||
@@ -13,11 +12,12 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
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();
|
||||
@@ -40,20 +40,30 @@ public class MQTTHandler {
|
||||
}
|
||||
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) {
|
||||
public void publishMessage(String topic, String content, Integer qos, Boolean retain) {
|
||||
MqttMessage message = new MqttMessage(StringEscapeUtils.unescapeJava(content).getBytes());
|
||||
message.setQos(qos);
|
||||
|
||||
message.setQos(Optional.ofNullable(qos).orElse(1));
|
||||
message.setRetained(Optional.ofNullable(retain).orElse(false));
|
||||
|
||||
try {
|
||||
if(!myClient.isConnected()) {
|
||||
try {
|
||||
myClient.connect();
|
||||
} catch (MqttSecurityException e1) {
|
||||
log.error("Could not retry connect to MQTT client for name: " + myConfig.getName() + " and ip: " + myConfig.getIp() + " with message: " + e1.getMessage());
|
||||
return;
|
||||
} catch (MqttException e1) {
|
||||
log.error("Could not retry connect to MQTT client for name: " + myConfig.getName() + " and ip: " + myConfig.getIp() + " with message: " + e1.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ 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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.ColorDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
@@ -25,25 +27,32 @@ public class MQTTHome implements Home {
|
||||
private Map<String, MQTTHandler> handlers;
|
||||
private Boolean validMqtt;
|
||||
private Gson aGsonHandler;
|
||||
private boolean closed;
|
||||
|
||||
public MQTTHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validMqtt)
|
||||
return;
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
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();
|
||||
for (String key : handlers.keySet()) {
|
||||
handlers.get(key).shutdown();
|
||||
}
|
||||
}
|
||||
handlers = null;
|
||||
closed = false;
|
||||
}
|
||||
|
||||
public MQTTHandler getMQTTHandler(String aName) {
|
||||
@@ -78,7 +87,7 @@ public class MQTTHome implements Home {
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
log.debug("executing HUE api request to send message to MQTT broker: " + anItem.getItem().toString());
|
||||
if (validMqtt) {
|
||||
@@ -92,6 +101,9 @@ public class MQTTHome implements Home {
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
mqttObject = DeviceDataDecode.replaceDeviceData(mqttObject, device);
|
||||
mqttObject = TimeDecode.replaceTimeValue(mqttObject);
|
||||
if (colorData != null) {
|
||||
mqttObject = ColorDecode.replaceColorData(mqttObject, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
|
||||
}
|
||||
if (mqttObject.substring(0, 1).equalsIgnoreCase("{"))
|
||||
mqttObject = "[" + mqttObject + "]";
|
||||
MQTTMessage[] mqttMessages = aGsonHandler.fromJson(mqttObject, MQTTMessage[].class);
|
||||
@@ -108,7 +120,7 @@ public class MQTTHome implements Home {
|
||||
if (mqttHandler == null) {
|
||||
log.warn("Should not get here, no mqtt hanlder available");
|
||||
} else {
|
||||
mqttHandler.publishMessage(mqttMessages[y].getTopic(), mqttMessages[y].getMessage());
|
||||
mqttHandler.publishMessage(mqttMessages[y].getTopic(), mqttMessages[y].getMessage(), mqttMessages[y].getQos(), mqttMessages[y].getRetain());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,15 +142,17 @@ public class MQTTHome implements Home {
|
||||
aGsonHandler =
|
||||
new GsonBuilder()
|
||||
.create();
|
||||
handlers = new HashMap<String, MQTTHandler>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getMqttaddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aClientConfig = theList.next();
|
||||
handlers = new HashMap<>();
|
||||
for (NamedIP aClientConfig : bridgeSettings.getBridgeSettingsDescriptor().getMqttaddress().getDevices()) {
|
||||
MQTTHandler aHandler = new MQTTHandler(aClientConfig);
|
||||
if(aHandler != null)
|
||||
handlers.put(aClientConfig.getName(), aHandler);
|
||||
handlers.put(aClientConfig.getName(), aHandler);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ public class MQTTMessage {
|
||||
private String message;
|
||||
private Integer delay;
|
||||
private Integer count;
|
||||
private Integer qos;
|
||||
private Boolean retain;
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
@@ -36,4 +38,19 @@ public class MQTTMessage {
|
||||
public void setCount(Integer count) {
|
||||
this.count = count;
|
||||
}
|
||||
public Integer getQos() {
|
||||
return qos;
|
||||
}
|
||||
|
||||
public void setQos(Integer qos) {
|
||||
this.qos = qos;
|
||||
}
|
||||
|
||||
public Boolean getRetain() {
|
||||
return retain;
|
||||
}
|
||||
|
||||
public void setRetain(Boolean retain) {
|
||||
this.retain = retain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.bwssystems.HABridge.plugins.openhab;
|
||||
|
||||
public class OpenHABCommand {
|
||||
private String url;
|
||||
private String command;
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.bwssystems.HABridge.plugins.openhab;
|
||||
|
||||
public class OpenHABDevice {
|
||||
|
||||
private String address;
|
||||
private String name;
|
||||
private OpenHABItem item;
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public OpenHABItem getItem() {
|
||||
return item;
|
||||
}
|
||||
public void setItem(OpenHABItem item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
package com.bwssystems.HABridge.plugins.openhab;
|
||||
|
||||
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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.ColorDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHome;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class OpenHABHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(OpenHABHome.class);
|
||||
private Map<String, OpenHABInstance> openhabMap;
|
||||
private Boolean validOpenhab;
|
||||
private HTTPHandler httpClient;
|
||||
private boolean closed;
|
||||
|
||||
public OpenHABHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
String responseString = null;
|
||||
|
||||
if(theUrl != null && !theUrl.isEmpty()) {
|
||||
OpenHABCommand theCommand = null;
|
||||
try {
|
||||
theCommand = new Gson().fromJson(theUrl, OpenHABCommand.class);
|
||||
} catch(Exception e) {
|
||||
log.warn("Cannot parse command to OpenHAB <<<" + theUrl + ">>>", e);
|
||||
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;
|
||||
}
|
||||
String intermediate = theCommand.getUrl().substring(theCommand.getUrl().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;
|
||||
OpenHABInstance theHandler = findHandlerByAddress(hostAddr);
|
||||
if(theHandler != null) {
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
if (colorData != null) {
|
||||
anUrl = ColorDecode.replaceColorData(anUrl, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
|
||||
}
|
||||
anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device);
|
||||
anUrl = TimeDecode.replaceTimeValue(anUrl);
|
||||
|
||||
String aCommand = null;
|
||||
if(theCommand.getCommand() != null && !theCommand.getCommand().isEmpty()) {
|
||||
aCommand = BrightnessDecode.calculateReplaceIntensityValue(theCommand.getCommand(),
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
if (colorData != null) {
|
||||
aCommand = ColorDecode.replaceColorData(aCommand, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
|
||||
}
|
||||
aCommand = DeviceDataDecode.replaceDeviceData(aCommand, device);
|
||||
aCommand = TimeDecode.replaceTimeValue(aCommand);
|
||||
}
|
||||
try {
|
||||
boolean success = theHandler.callCommand(anUrl, aCommand, httpClient);
|
||||
if(!success) {
|
||||
log.warn("Comand had error to OpenHAB");
|
||||
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 send comand to OpenHAB", e);
|
||||
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("OpenHAB 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("OpenHAB 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 Object getItems(String type) {
|
||||
|
||||
if(!validOpenhab)
|
||||
return null;
|
||||
log.debug("consolidating devices for OpenHAB");
|
||||
List<OpenHABDevice> theResponse = null;
|
||||
Iterator<String> keys = openhabMap.keySet().iterator();
|
||||
List<OpenHABDevice> deviceList = new ArrayList<OpenHABDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = openhabMap.get(key).getDevices(httpClient);
|
||||
if(theResponse != null)
|
||||
addOpenhabDevices(deviceList, theResponse, key);
|
||||
else {
|
||||
log.warn("Cannot get devices for OpenHAB with name: " + key + ", skipping this OpenHAB.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
private Boolean addOpenhabDevices(List<OpenHABDevice> theDeviceList, List<OpenHABDevice> theSourceList, String theKey) {
|
||||
Iterator<OpenHABDevice> devices = theSourceList.iterator();
|
||||
while(devices.hasNext()) {
|
||||
OpenHABDevice theDevice = devices.next();
|
||||
theDeviceList.add(theDevice);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
openhabMap = null;
|
||||
validOpenhab = bridgeSettings.getBridgeSettingsDescriptor().isValidOpenhab();
|
||||
log.info("OpenHAB Home created." + (validOpenhab ? "" : " No OpenHABs configured."));
|
||||
if(validOpenhab) {
|
||||
openhabMap = new HashMap<String,OpenHABInstance>();
|
||||
httpClient = HTTPHome.getHandler();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getOpenhabaddress().getDevices().iterator();
|
||||
while(theList.hasNext() && validOpenhab) {
|
||||
NamedIP anOpenhab = theList.next();
|
||||
try {
|
||||
openhabMap.put(anOpenhab.getName(), new OpenHABInstance(anOpenhab));
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot get OpenHAB (" + anOpenhab.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
|
||||
validOpenhab = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private OpenHABInstance findHandlerByAddress(String hostAddress) {
|
||||
OpenHABInstance aHandler = null;
|
||||
boolean found = false;
|
||||
Iterator<String> keys = openhabMap.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
aHandler = openhabMap.get(key);
|
||||
if(aHandler != null && aHandler.getOpenHABAddress().getIp().equals(hostAddress)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
aHandler = null;
|
||||
return aHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
log.debug("Closing Home.");
|
||||
if(!closed && validOpenhab) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
|
||||
if(httpClient != null)
|
||||
httpClient.closeHandler();
|
||||
|
||||
openhabMap = null;
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.bwssystems.HABridge.plugins.openhab;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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 OpenHABInstance {
|
||||
private static final Logger log = LoggerFactory.getLogger(OpenHABInstance.class);
|
||||
private NamedIP theOpenHAB;
|
||||
|
||||
public OpenHABInstance(NamedIP openhabLocation) {
|
||||
super();
|
||||
theOpenHAB = openhabLocation;
|
||||
}
|
||||
|
||||
public NamedIP getOpenHABAddress() {
|
||||
return theOpenHAB;
|
||||
}
|
||||
|
||||
public void setOpenHABAddress(NamedIP openhabAddress) {
|
||||
this.theOpenHAB = openhabAddress;
|
||||
}
|
||||
|
||||
public Boolean callCommand(String aCommand, String commandData, HTTPHandler httpClient) {
|
||||
log.debug("calling OpenHAB: " + theOpenHAB.getIp() + ":" + theOpenHAB.getPort() + aCommand);
|
||||
String aUrl = null;
|
||||
NameValue[] headers = null;
|
||||
if(theOpenHAB.getSecure() != null && theOpenHAB.getSecure())
|
||||
aUrl = "https://";
|
||||
else
|
||||
aUrl = "http://";
|
||||
if(theOpenHAB.getUsername() != null && !theOpenHAB.getUsername().isEmpty() && theOpenHAB.getPassword() != null && !theOpenHAB.getPassword().isEmpty()) {
|
||||
aUrl = aUrl + theOpenHAB.getUsername() + ":" + theOpenHAB.getPassword() + "@";
|
||||
}
|
||||
aUrl = aUrl + theOpenHAB.getIp() + ":" + theOpenHAB.getPort() + "/" + aCommand;
|
||||
String theData = httpClient.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "text/plain", commandData, headers);
|
||||
log.debug("call Command return is: <" + theData + ">");
|
||||
if(theData.contains("error") || theData.contains("ERROR") || theData.contains("Error"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<OpenHABDevice> getDevices(HTTPHandler httpClient) {
|
||||
List<OpenHABDevice> deviceList = null;
|
||||
OpenHABItem[] theOpenhabStates;
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
NameValue[] headers = null;
|
||||
if(theOpenHAB.getSecure() != null && theOpenHAB.getSecure())
|
||||
theUrl = "https://";
|
||||
else
|
||||
theUrl = "http://";
|
||||
if(theOpenHAB.getUsername() != null && !theOpenHAB.getUsername().isEmpty() && theOpenHAB.getPassword() != null && !theOpenHAB.getPassword().isEmpty()) {
|
||||
theUrl = theUrl + theOpenHAB.getUsername() + ":" + theOpenHAB.getPassword() + "@";
|
||||
}
|
||||
theUrl = theUrl + theOpenHAB.getIp() + ":" + theOpenHAB.getPort() + "/rest/items?recursive=false";
|
||||
theData = httpClient.doHttpRequest(theUrl, HttpGet.METHOD_NAME, "application/json", null, headers);
|
||||
if(theData != null) {
|
||||
log.debug("GET OpenHAB States - data: " + theData);
|
||||
try {
|
||||
theOpenhabStates = new Gson().fromJson(theData, OpenHABItem[].class);
|
||||
if(theOpenhabStates == null) {
|
||||
log.warn("Cannot get any devices for OpenHAB " + theOpenHAB.getName() + " as response is not parsable.");
|
||||
}
|
||||
else {
|
||||
deviceList = new ArrayList<OpenHABDevice>();
|
||||
|
||||
for (int i = 0; i < theOpenhabStates.length; i++) {
|
||||
OpenHABDevice aNewOpenHABDeviceDevice = new OpenHABDevice();
|
||||
aNewOpenHABDeviceDevice.setItem(theOpenhabStates[i]);
|
||||
aNewOpenHABDeviceDevice.setAddress(theOpenHAB.getIp() + ":" + theOpenHAB.getPort());
|
||||
aNewOpenHABDeviceDevice.setName(theOpenHAB.getName());
|
||||
deviceList.add(aNewOpenHABDeviceDevice);
|
||||
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Cannot get an devices for OpenHAB " + theOpenHAB.getName() + " Gson Parse Error.");
|
||||
}
|
||||
}
|
||||
else
|
||||
log.warn("Cannot get an devices for OpenHAB " + theOpenHAB.getName() + " http call failed.");
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
|
||||
protected void closeClient() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.openhab;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class OpenHABItem {
|
||||
|
||||
@SerializedName("link")
|
||||
@Expose
|
||||
private String link;
|
||||
@SerializedName("state")
|
||||
@Expose
|
||||
private String state;
|
||||
@SerializedName("type")
|
||||
@Expose
|
||||
private String type;
|
||||
@SerializedName("name")
|
||||
@Expose
|
||||
private String name;
|
||||
@SerializedName("label")
|
||||
@Expose
|
||||
private String label;
|
||||
@SerializedName("category")
|
||||
@Expose
|
||||
private String category;
|
||||
@SerializedName("tags")
|
||||
@Expose
|
||||
private List<Object> tags = null;
|
||||
@SerializedName("groupNames")
|
||||
@Expose
|
||||
private List<Object> groupNames = null;
|
||||
@SerializedName("stateDescription")
|
||||
@Expose
|
||||
private StateDescription stateDescription;
|
||||
|
||||
public String getLink() {
|
||||
return link;
|
||||
}
|
||||
|
||||
public void setLink(String link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public List<Object> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(List<Object> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public List<Object> getGroupNames() {
|
||||
return groupNames;
|
||||
}
|
||||
|
||||
public void setGroupNames(List<Object> groupNames) {
|
||||
this.groupNames = groupNames;
|
||||
}
|
||||
|
||||
public StateDescription getStateDescription() {
|
||||
return stateDescription;
|
||||
}
|
||||
|
||||
public void setStateDescription(StateDescription stateDescription) {
|
||||
this.stateDescription = stateDescription;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.openhab;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Option {
|
||||
|
||||
@SerializedName("value")
|
||||
@Expose
|
||||
private String value;
|
||||
@SerializedName("label")
|
||||
@Expose
|
||||
private String label;
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.openhab;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class StateDescription {
|
||||
|
||||
@SerializedName("pattern")
|
||||
@Expose
|
||||
private String pattern;
|
||||
@SerializedName("readOnly")
|
||||
@Expose
|
||||
private Boolean readOnly;
|
||||
@SerializedName("options")
|
||||
@Expose
|
||||
private List<Option> options = null;
|
||||
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public void setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public Boolean getReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
public void setReadOnly(Boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
public List<Option> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public void setOptions(List<Option> options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ 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.ColorData;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -30,10 +31,13 @@ public class SomfyHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(SomfyHome.class);
|
||||
private Map<String, SomfyInfo> somfys;
|
||||
private Boolean validSomfy;
|
||||
private boolean closed;
|
||||
|
||||
public SomfyHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
closed = true;
|
||||
createHome(bridgeSettings);
|
||||
|
||||
closed = false;
|
||||
}
|
||||
|
||||
public SomfyInfo getSomfyHandler(String somfyName) {
|
||||
@@ -62,7 +66,7 @@ public class SomfyHome implements Home {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
if (!validSomfy) {
|
||||
log.warn("Should not get here, no somfy hub available");
|
||||
@@ -113,6 +117,17 @@ public class SomfyHome implements Home {
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
log.debug("Closing Home.");
|
||||
if(closed) {
|
||||
log.debug("Home is already closed....");
|
||||
return;
|
||||
}
|
||||
somfys = null;
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ 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.http.HTTPHome;
|
||||
import com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup.Device;
|
||||
import com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup.GetSetup;
|
||||
import com.google.gson.Gson;
|
||||
@@ -39,7 +40,7 @@ public class SomfyInfo {
|
||||
|
||||
private void initHttpClient() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
|
||||
if(httpClient==null) {
|
||||
httpClient = new HTTPHandler();
|
||||
httpClient = HTTPHome.getHandler();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ public class SomfyInfo {
|
||||
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);
|
||||
log.debug("Somfy login response <<<" + response + ">>>");
|
||||
}
|
||||
|
||||
private NameValue[] getHttpHeaders() {
|
||||
@@ -88,7 +89,7 @@ public class SomfyInfo {
|
||||
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);
|
||||
log.debug("Somfy getSetup response <<<" + response + ">>>");
|
||||
GetSetup setupData = new Gson().fromJson(response, GetSetup.class);
|
||||
return setupData;
|
||||
}
|
||||
@@ -97,7 +98,7 @@ public class SomfyInfo {
|
||||
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);
|
||||
log.debug("Somfy exec reply response <<<" + response + ">>>");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user