mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-18 16:17:30 +00:00
Compare commits
88 Commits
v4.1.1
...
v4.5.0alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0737c54a0e | ||
|
|
a7e516925c | ||
|
|
2f1adf9d4b | ||
|
|
cd5417c2e0 | ||
|
|
ba621fcb85 | ||
|
|
7442b0d0ca | ||
|
|
c275926117 | ||
|
|
895a9ec99b | ||
|
|
6dfd70dfee | ||
|
|
0bdb321fd7 | ||
|
|
b508a8a16a | ||
|
|
ddee3a42a9 | ||
|
|
b000215b26 | ||
|
|
a2b652907f | ||
|
|
6bf1bbc8a8 | ||
|
|
1a402e425e | ||
|
|
59ef6e88de | ||
|
|
7d4f953c89 | ||
|
|
3bcec27861 | ||
|
|
bb0ffeb570 | ||
|
|
0c4292bfd7 | ||
|
|
0083c5854f | ||
|
|
c13b9bd8f4 | ||
|
|
ca5a6c6667 | ||
|
|
c9c6d6e66d | ||
|
|
c8b1827150 | ||
|
|
d7d83e866e | ||
|
|
fb24e9d1a3 | ||
|
|
d15a1c58d0 | ||
|
|
08c87eb3aa | ||
|
|
7da4bf13e0 | ||
|
|
cd701ca02e | ||
|
|
1b676632fe | ||
|
|
62e366c028 | ||
|
|
838b86a266 | ||
|
|
ed8fc95782 | ||
|
|
f6cb41b880 | ||
|
|
a578aa9fd8 | ||
|
|
7b97bd75ae | ||
|
|
4de14217b4 | ||
|
|
02918dc49d | ||
|
|
3a8c64aac6 | ||
|
|
10df2f1c3e | ||
|
|
c7cf48bb6b | ||
|
|
54e9303708 | ||
|
|
6b4344bbe8 | ||
|
|
745986f08f | ||
|
|
5e59b33ed7 | ||
|
|
c45cb24b20 | ||
|
|
adc34ddaa6 | ||
|
|
88f34f3221 | ||
|
|
15223630eb | ||
|
|
2f456aa0d0 | ||
|
|
201aaa8bca | ||
|
|
6e7b48aa5b | ||
|
|
afd1af4094 | ||
|
|
61156e9820 | ||
|
|
6116d37675 | ||
|
|
f8474f5f41 | ||
|
|
0305646b4f | ||
|
|
3fea7f4f1a | ||
|
|
44fbaa68f8 | ||
|
|
dd0032a567 | ||
|
|
470f6b3c15 | ||
|
|
3016712ad8 | ||
|
|
2fbf26a5fa | ||
|
|
6b3ae1b971 | ||
|
|
71258c7e52 | ||
|
|
ee066f1449 | ||
|
|
e5871e61b5 | ||
|
|
6b8a714959 | ||
|
|
cf3ec7cfe4 | ||
|
|
805aac9fde | ||
|
|
1221df4c96 | ||
|
|
1c897e3b36 | ||
|
|
0ac8061118 | ||
|
|
65b0d6e470 | ||
|
|
99e2243e2d | ||
|
|
7a354619d0 | ||
|
|
2dc245bb96 | ||
|
|
c679548bbd | ||
|
|
16a248ba8e | ||
|
|
0ce23c0f00 | ||
|
|
babf81ea31 | ||
|
|
1c7260600a | ||
|
|
f1592a1998 | ||
|
|
e36145f216 | ||
|
|
8cd571c183 |
143
README.md
143
README.md
@@ -1,6 +1,32 @@
|
||||
# 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.
|
||||
|
||||
Here are some diagrams to put this software in perspective.
|
||||
|
||||
The Echo Path looks like this:
|
||||
```
|
||||
+------------------------+ +------------------------+
|
||||
+-------------+ | H A +------------------| | A +------------------+ |
|
||||
| Amazon Echo |----->| U P | ha-bridge core |--->| P | Device to control| |
|
||||
+-------------+ | E I +------------------| | I +------------------+ |
|
||||
+------------------------+ +------------------------+
|
||||
```
|
||||
The Google Home Path looks like this:
|
||||
```
|
||||
+------------------------+ +------------------------+
|
||||
+-------------+ | H A +------------------| | A +------------------+ |
|
||||
| Google Home |----->| U P | ha-bridge core |--->| P | Device to control| |
|
||||
+-------------+ | E I +------------------| | I +------------------+ |
|
||||
+------------------------+ +------------------------+
|
||||
```
|
||||
THe Harmony Hub Path looks like this:
|
||||
```
|
||||
+------------------------+ +------------------------+
|
||||
+-------------+ | H A +------------------| | A +------------------+ |
|
||||
| Harmony Hub |----->| U P | ha-bridge core |--->| P | Device to control| |
|
||||
+-------------+ | E I +------------------| | I +------------------+ |
|
||||
+------------------------+ +------------------------+
|
||||
```
|
||||
**SECURITY RISK: If you are unsure on how this software operates and what it exposes to your network, please make sure you understand that it can allow root access to your system. It is best practice to not open this to the Internet through your router as there are no security protocols in place to protect the system. The License agreement states specifically that you use this at your own risk.**
|
||||
|
||||
**ATTENTION: This requires a physical Amazon Echo, Dot or Tap and does not work with prototype devices built using the Alexa Voice Service e.g. Amazon's Alexa AVS Sample App and Sam Machin's AlexaPi. The AVS version does not have any capability for Hue Bridge discovery!**
|
||||
@@ -11,7 +37,7 @@ Emulates Philips Hue api to other home automation gateways such as an Amazon Ech
|
||||
|
||||
**FAQ: Please look here for the current FAQs! https://github.com/bwssytems/ha-bridge/wiki/HA-Bridge-FAQs**
|
||||
|
||||
In the cases of systems that require authorization and/or have API's that cannot be handled in the current method, a module may need to be built. The Harmony Hub is such a module and so is the Nest module. The Bridge has helpers to build devices for the gateway for the Logitech Harmony Hub, Vera, Vera Lite or Vera Edge, Nest and the ability to proxy all of your real Hue bridges behind this bridge.
|
||||
In the cases of systems that require authorization and/or have API's that cannot be handled in the current method, a module may need to be built. The Harmony Hub is such a module and so is the Nest module. The Bridge has helpers to build devices for the gateway for the Logitech Harmony Hub, Vera, Vera Lite or Vera Edge, Nest, Somfy Tahoma and the ability to proxy all of your real Hue bridges behind this bridge.
|
||||
|
||||
Alternatively the Bridge supports custom calls as well using http/https/udp and tcp such as the LimitlessLED/MiLight bulbs using the UDP protocol. Binary data is supported with UDP/TCP.
|
||||
|
||||
@@ -33,23 +59,23 @@ ATTENTION: This requires JDK 1.8 to run
|
||||
ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below.
|
||||
|
||||
```
|
||||
java -jar ha-bridge-4.1.1.jar
|
||||
java -jar ha-bridge-4.5.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.1.1.jar is in your /home/pi/habridge directory.
|
||||
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.1.1/ha-bridge-4.1.1.jar
|
||||
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.1.1.jar is in your /home/pi/habridge directory.
|
||||
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.1.1/ha-bridge-4.1.1.jar
|
||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.5.0/ha-bridge-4.5.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
|
||||
@@ -68,8 +94,8 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.1.1.jar
|
||||
WorkingDirectory=/home/pi/habridge
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.5.0.jar
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -104,7 +130,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.1.1.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
|
||||
nohup java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.5.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
|
||||
|
||||
chmod 777 /home/pi/habridge/habridge-log.txt
|
||||
```
|
||||
@@ -149,54 +175,6 @@ Added the following lines to my Apache config file “000-default”
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
service apache2 restart
|
||||
### lighthttpd Example
|
||||
```
|
||||
server.modules += ( "mod_proxy" )
|
||||
proxy.server = (
|
||||
"/api" =>
|
||||
(
|
||||
( "host" => "127.0.0.1",
|
||||
"port" => "8080"
|
||||
)
|
||||
)
|
||||
)
|
||||
```
|
||||
### nginx Example
|
||||
```
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8080/api;
|
||||
}
|
||||
```
|
||||
## Run ha-bridge alongside web server already on port 80
|
||||
These examples will help you proxy your current webserver requests to the ha-bridge running on a different port, such as 8080.
|
||||
### Apache Example
|
||||
Reverse proxy with Apache on Ubuntu linux:
|
||||
|
||||
a2enmod proxy
|
||||
a2enmod proxy_http
|
||||
a2enmod headers
|
||||
|
||||
Added the following lines to my Apache config file “000-default”
|
||||
|
||||
```
|
||||
<VirtualHost *:80>
|
||||
ProxyPass /api http://localhost:8080/api nocanon
|
||||
ProxyPassReverse /api http://localhost:8080/api
|
||||
ProxyRequests Off
|
||||
AllowEncodedSlashes NoDecode
|
||||
|
||||
# Local reverse proxy authorization override
|
||||
# Most unix distribution deny proxy by default (ie /etc/apache2/mods-enabled/proxy.conf in Ubuntu)
|
||||
<Proxy http://localhost:8080/api*>
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</Proxy>
|
||||
|
||||
….. (the rest of the VirtualHost config section) …..
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
service apache2 restart
|
||||
### lighthttpd Example
|
||||
```
|
||||
@@ -235,6 +213,11 @@ 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:
|
||||
```
|
||||
java -jar -Dsecurity.key=Xfawer354WertSdf321234asd ha-bridge-W.X.Y.jar
|
||||
```
|
||||
## HA Bridge Usage and Configuration
|
||||
This section will cover the basics of configuration and where this configuration can be done. This requires that you have started your bridge process and then have pointed your
|
||||
favorite web interface by going to the http://<my ip address>:<port> or http://localhost:<port> with port you have assigned. The default quick link is http://localhost for your reference.
|
||||
@@ -248,6 +231,8 @@ This is where all of the configuration occurs for what ports and IP's the bridge
|
||||
This field is used to test the bridge server with the UPNP IP Address and to make sure that the bridge is responding.
|
||||
#### Bridge Control Buttons
|
||||
These buttons are for managing the bridge. The Save button is enabled when there is a change to the configuration. The Bridge Reinitialize button will recycle the internal running of the bridge in the java process. The Stop button will stop the java process. The Refresh button will refresh the page and settings.
|
||||
#### The Security Dialog
|
||||
This is where you can set the different security settings for the ha-bridge.
|
||||
#### 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
|
||||
@@ -261,13 +246,9 @@ The server defaults to running on port 80. To override what the default is, spec
|
||||
#### UPNP Response Port
|
||||
The upnp response port that will be used. The default is 50000.
|
||||
#### Vera Names and IP Addresses
|
||||
Provide IP Addresses of your Veras that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Vera and device/scene you configure.
|
||||
Provide IP Addresses of your Veras that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Vera and device/scene you configure.
|
||||
#### Harmony Names and IP Addresses
|
||||
Provide IP Addresses of your Harmony Hubs that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the activity or buttons by the call it receives and send it to the target Harmony Hub and activity/button you configure.
|
||||
#### Harmony Username
|
||||
deprecated
|
||||
#### Harmony Password
|
||||
deprecated
|
||||
Provide IP Addresses of your Harmony Hubs that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the activity or buttons by the call it receives and send it to the target Harmony Hub and activity/button you configure. Also, an option of webhook can be called when the activity changes on the harmony hub that will send an HTTP GET call to the the address of your choosing. This can contain the replacement variables of ${activity.id} and/or ${activity.label}. Example : http://192.168.0.1/activity/${activity.id}/${activity.label} OR http://hook?a=${activity.label}
|
||||
#### Hue Names and IP Addresses
|
||||
Provide IP Addresses of your Hue Bridges that you want to proxy through the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will passthru the call it receives to the target Hue and device you configure.
|
||||
|
||||
@@ -284,6 +265,10 @@ The user name of the home.nest.com account for the Nest user. This needs to be g
|
||||
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
|
||||
#### 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
|
||||
@@ -314,7 +299,7 @@ There is a new format for the on/dim/off URL areas. The new editor handles the i
|
||||
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
|
||||
String or JsonElement | item | This is the payload that will be called for devices | Required
|
||||
Integer | count | This is how many times this items will be executed | Optional
|
||||
Integer | delay | This is how long we will wait until the next call after | Optional
|
||||
String | type | This is the type of device we are executing | Required
|
||||
@@ -329,12 +314,14 @@ Example from device.db:
|
||||
[{"item":<a String that is quoted or another JSON object>,"type":"<atype>"."count":X."delay":X."filterIPs":"<comma separated list of IP addresses that are valid>"."httpVerb":"<GET,PUT,POST>","httpBody":"<body info>","httpHeaders":[{"name":"header name","value":"header value"},{"name":"another header","value":"another value"}],"contentType":"<http content type i.e application/json>"},{"item":<another item>,"type":"<aType>"}]
|
||||
```
|
||||
|
||||
The Add/Edit tab will show you the fields to fill in for the above in a form, when you hcae completed putting in the things you want, make sure to hit the `Add` button at the right.
|
||||
The format of the example is in JSON where the JSON tags equate to the UI labels of the On Items/Dim Items/Off Items. i.e.: JSON item = UI Target Item, JSON type = UI Type, etc...
|
||||
|
||||
The Add/Edit tab will show you the fields to fill in for the above in a form, when you have completed putting in the things you want, make sure to hit the `Add` button at the right.
|
||||
|
||||
The format of the item can be the default HTTP request which executes the URLs formatted as `http://<your stuff here>` as a GET. Other options to this are to select the HTTP Verb and add the data type and add a body that is passed with the request. Secure https is supported as well, just use `https://<your secure call here>`. When using POST and PUT, you have the ability to specify the body that will be sent with the request as well as the application type for the http call.
|
||||
|
||||
The valid device types are: "custom", "veraDevice", "veraScene", "harmonyActivity", "harmonyButton", "nestHomeAway", "nestThermoSet", "hueDevice", "halDevice",
|
||||
"halButton", "halHome", "halThermoSet", "mqttMessage", "cmdDevice", "hassDevice", "tcpDevice", "udpDevice", "httpDevice", "domoticzDevice"
|
||||
"halButton", "halHome", "halThermoSet", "mqttMessage", "cmdDevice", "hassDevice", "tcpDevice", "udpDevice", "httpDevice", "domoticzDevice", "somfyDevice"
|
||||
|
||||
Filter Ip example:
|
||||
```
|
||||
@@ -354,7 +341,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} for 0-100 or ${intensity.byte} for 0-255 for straight pass through of the value or items that require special calculated values using ${intensity.math()} i.e. "${intensity.math(X/4)}". See Value Passing Controls Below.
|
||||
You can also use the value replacement constructs within these statements. Such as using the expressions "${time.format(Java time format string)}" for inserting a date/time stamp, ${intensity.percent} or ${intensity.percent.hex} for 0-100 or ${intensity.decimal_percent} for 0.00-1.00 or ${intensity.byte} or ${intensity.byte.hex} for 0-255 for straight pass through of the value or items that require special calculated values using ${intensity.math()} i.e. "${intensity.math(X/4)}" or "${intensity.math(X/4).hex}". See Value Passing Controls Below.
|
||||
Examples:
|
||||
```
|
||||
|
||||
@@ -399,18 +386,18 @@ To configure this type of manual add, you will need to select the Device type of
|
||||
|
||||
In the URL areas, the format of the execution is just providing what command line you would like to run, or using the multiple call item construct described above.
|
||||
```
|
||||
[{"item":"C:\\Users\\John\\Documents\\Applications\\putty.exe 192.168.1.1","type":"cmdDevice"},
|
||||
{"item":"notepad.exe","type":"cmdDevice"}]
|
||||
|
||||
OR
|
||||
|
||||
[{"item":"exec://notepad.exe","type":"cmdDevice"}]
|
||||
[{"item":"exec://C:\\Users\\John\\Documents\\Applications\\putty.exe 192.168.1.1","type":"cmdDevice"},{"item":"exec://notepad.exe","type":"cmdDevice"}]
|
||||
|
||||
[{"item":"/home/pi/scripts/dothisscript.sh","type":"cmdDevice"}]
|
||||
```
|
||||
#### Value Passing Controls
|
||||
There are multiple replacement constructs available to be put into any of the calls except Harmony items, Net Items and HAL items. These constructs are: "${time.format(Java time format string)}", "${intensity.percent}", "${intensity.byte}" and "${intensity.math(using X in your calc)}".
|
||||
There are multiple replacement constructs available to be put into any of the calls except Harmony items, Net Items and HAL items. These constructs are: "${time.format(Java time format string)}", "${intensity.percent}", "${intensity.percent.hex}", "${intensity.decimal_percent}", "${intensity.byte}", "${intensity.byte.hex}", "${intensity.math(using X in your calc)}" and "${intensity.math(using X in your calc).hex}".
|
||||
|
||||
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
|
||||
|
||||
Also, device data can be inserted into your payloads by the use of "${device.name}", "${device.id}", "${device.uniqueid}", "${device.targetDevice}", "${device.mapId}", "${device.mapType}" and "${device.deviceType}". These work just like the dimming value replacements.
|
||||
e.g.
|
||||
```
|
||||
[{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=10&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.math(X/4)}","type":"httpDevice"}]
|
||||
@@ -472,8 +459,8 @@ Dim a light | "Dim the <light name>"
|
||||
Brighten a light | "Brighten the <light name>"
|
||||
Set a light brightness to a certain percentage | "Set <light name> to 50%"
|
||||
Dim/Brighten lights by a certain percentage | "Dim/Brighten <light name> by 50%"
|
||||
Turn on/off all lights in room | “Turn on/off lights in <room name>"
|
||||
Turn on/off all lights | “Turn on/off all of the lightsâ€<EFBFBD>
|
||||
Turn on/off all lights in room | "Turn on/off lights in <room name>"
|
||||
Turn on/off all lights | "Turn on/off all of the lights"
|
||||
|
||||
To see what Home thinks you said, you can ask "Hey Google, What did I say?" or check the history in the app.
|
||||
|
||||
@@ -518,7 +505,7 @@ contentBodyOff | string | This is the content body that you would like to send w
|
||||
}
|
||||
```
|
||||
#### Dimming Control Example
|
||||
Dimming is also supported by using the expressions ${intensity.percent} for 0-100 or ${intensity.byte} for 0-255 for straight pass through of the value.
|
||||
Dimming is also supported by using the expressions ${intensity.percent} for 0-100 or ${intensity.decimal_percent} for 0.00-1.00 or ${intensity.byte} for 0-255 for straight pass through of the value.
|
||||
e.g.
|
||||
```
|
||||
{
|
||||
@@ -692,7 +679,7 @@ This call returns a null json "{}".
|
||||
### Get HA Bridge Version
|
||||
Get current version of the HA bridge software.
|
||||
```
|
||||
GET http://host:port/api/devices/habridge/version
|
||||
GET http://host:port/system/habridge/version
|
||||
```
|
||||
#### Response
|
||||
Name | Type | Description
|
||||
|
||||
144
kodivolume.md
144
kodivolume.md
@@ -1,144 +0,0 @@
|
||||
# Kodi volume control using dim commands & JSON-RPC
|
||||
|
||||
You can use HA Bridge to adjust the Kodi software volume output. This allows you to use dim commands and set the volume level with percentage values.
|
||||
|
||||
### What is JSON-RPC?
|
||||
|
||||
The short answer is JSON-RPC an interface to communicate with Kodi. [See the official Kodi Wiki for more info](http://kodi.wiki/view/JSON-RPC_API)
|
||||
|
||||
### Setup Kodi to allow control through JSON-RPC
|
||||
|
||||
In Kodi navigate to Settings/Services/Control ([screenshot](http://kodi.wiki/view/Settings/Services/Control))
|
||||
|
||||
Turn **ON** the following:
|
||||
- Allow control of Kodi via HTTP
|
||||
- Allow remote control from applications on this system
|
||||
- Allow remote control from applications on other systems
|
||||
|
||||
Change the **username** to something unique and set a strong **password**.
|
||||
|
||||
Make a note of the **PORT**
|
||||
### Adding the device to HA Bridge
|
||||
|
||||
Access the HA Bridge Configuration in your browser and open the **Manual Add** tab.
|
||||
#### Name
|
||||
|
||||
Give the device a unique name that doesn’t include **“volume”** as it will cause conflicts with the Echo’s built in volume controls. A device name of **“cody sound”** works well.
|
||||
#### Device type
|
||||
|
||||
Select **TCP** in the dropdown
|
||||
### URLs
|
||||
|
||||
This section might seem a little long winded and if you know what you are doing then feel free to jump ahead.
|
||||
#### Building the URL
|
||||
|
||||
We need to log into the Kodi web server without having to fill in the popup each time. You can do this by putting the username and password in the URL. It is not a good idea to do this on other websites as it does put your password in clear view, but for your local network it is fine.
|
||||
|
||||
Use the example below replacing the relevant sections with the details that you defined in the Kodi settings screen in the first step. Replacing the IP with the address of the machine that Kodi is running on.
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc
|
||||
```
|
||||
##### Testing the URL in a browser
|
||||
|
||||
Before you continue, open your custom URL in a browser (making sure Kodi is running). If all is working as it should you will see a big page of JSON that starts with:
|
||||
|
||||
``` json
|
||||
{
|
||||
"description": "JSON-RPC API of XBMC",
|
||||
"id": "http://xbmc.org/jsonrpc/ServiceDescription.json",
|
||||
```
|
||||
|
||||
If you don’t see something that looks like the code above, then go back and double check your settings.
|
||||
#### The JSON request
|
||||
|
||||
The URL is what connects you to Kodi, JSON is what is used to communicate with it. The JSON that is used to set the volume level is:
|
||||
|
||||
``` json
|
||||
{"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":100},"id":1}
|
||||
```
|
||||
##### Joining the URL and JSON
|
||||
|
||||
Join the two together by adding `?request=` to the end of the URL and then add the JSON to the end of the request. You will end up with something like:
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request={"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":100},"id":1}
|
||||
```
|
||||
### Testing the request in a browser
|
||||
|
||||
Go ahead and test the combined URL/JSON in a browser changing **100** to whatever level you want to set. Kodi should adjust the volume accordingly, try a few different levels to be sure it is working correctly.
|
||||
|
||||
The browser will reformat the URL each time you press return so don’t build the URL in the browser bar without making a copy first.
|
||||
|
||||
Ideally build the URL in a text document which you can easily edit and then copy/paste each time.
|
||||
|
||||
### Prepare the three URLs
|
||||
|
||||
You want to end up with three full URLs in your text file, one for each of the commands.
|
||||
|
||||
**ON**
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request={"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":100},"id":1}
|
||||
```
|
||||
|
||||
**DIM**
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request={"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":45},"id":1}
|
||||
```
|
||||
|
||||
**OFF**
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request={"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":0},"id":1}
|
||||
```
|
||||
|
||||
### Test the three URLS
|
||||
|
||||
You should now be able to control the volume of Kodi using the structured URL you built above in a browser.
|
||||
|
||||
If you can’t get it to work in a browser then you won’t be able to get it to work in HA Bridge.
|
||||
|
||||
|
||||
### Manually adding the device
|
||||
|
||||
Add a new manual device and give it a name e.g. “Cody Sound”
|
||||
|
||||
Set `Device type` to `Custom`
|
||||
|
||||
Use the same URL for all three (ON, OFF, DIM)
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request=
|
||||
```
|
||||
|
||||
* `HTTP Verb` to `POST`
|
||||
* `Content type` to `application/json`
|
||||
|
||||
**Content body On**
|
||||
```json
|
||||
{"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":100},"id":1}
|
||||
```
|
||||
**Content body Dim**
|
||||
```json
|
||||
{"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":${intensity.percent}},"id":1}
|
||||
```
|
||||
**Content body Off**
|
||||
```json
|
||||
{"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":0},"id":1}
|
||||
```
|
||||
|
||||
|
||||
### HA Bridge
|
||||
|
||||
Save and test the button in the Bridge Devices tab and hopefully it should turn the volume up in Kodi.
|
||||
|
||||
### Controlling the Device
|
||||
|
||||
You can use the commands as listed in the [README](https://github.com/bwssytems/ha-bridge#ask-alexa)
|
||||
|
||||
“Set Cody Sound to 50 percent”
|
||||
“Cody Sound to 70 percent”
|
||||
|
||||
Remembering that “Turn on Cody Sound” will set the volume to 100%, and “Turn off Cody Sound” will mute.
|
||||
14
pom.xml
14
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.bwssystems.HABridge</groupId>
|
||||
<artifactId>ha-bridge</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<version>4.5.0alpha-3</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HA Bridge</name>
|
||||
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>nest-controller</artifactId>
|
||||
<version>1.0.13</version>
|
||||
<version>1.0.14</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@@ -121,6 +121,16 @@
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>lifx-sdk-java</artifactId>
|
||||
<version>2.1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.bwssystems.HABridge;
|
||||
public class BridgeControlDescriptor {
|
||||
private boolean reinit;
|
||||
private boolean stop;
|
||||
private boolean linkButton;
|
||||
|
||||
public BridgeControlDescriptor() {
|
||||
super();
|
||||
@@ -22,4 +23,12 @@ public class BridgeControlDescriptor {
|
||||
public void setStop(boolean stop) {
|
||||
this.stop = stop;
|
||||
}
|
||||
|
||||
public boolean isLinkButton() {
|
||||
return linkButton;
|
||||
}
|
||||
|
||||
public void setLinkButton(boolean linkButton) {
|
||||
this.linkButton = linkButton;
|
||||
}
|
||||
}
|
||||
|
||||
263
src/main/java/com/bwssystems/HABridge/BridgeSecurity.java
Normal file
263
src/main/java/com/bwssystems/HABridge/BridgeSecurity.java
Normal file
@@ -0,0 +1,263 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.PBEParameterSpec;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import spark.Request;
|
||||
|
||||
public class BridgeSecurity {
|
||||
private static final Logger log = LoggerFactory.getLogger(BridgeSecurity.class);
|
||||
private static final String USER_SESSION_ID = "user";
|
||||
private static final byte[] SALT = {
|
||||
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
|
||||
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
|
||||
};
|
||||
private char[] habridgeKey;
|
||||
private BridgeSecurityDescriptor securityDescriptor;
|
||||
private boolean settingsChanged;
|
||||
|
||||
public BridgeSecurity(char[] theKey) {
|
||||
habridgeKey = theKey;
|
||||
securityDescriptor = null;
|
||||
settingsChanged = false;
|
||||
}
|
||||
|
||||
public void setSecurityData(String theData) {
|
||||
String anError = null;
|
||||
if(theData != null && !theData.isEmpty()) {
|
||||
try {
|
||||
securityDescriptor = new Gson().fromJson(decrypt(theData), BridgeSecurityDescriptor.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
anError = e.getMessage();
|
||||
} catch (GeneralSecurityException e) {
|
||||
anError = e.getMessage();
|
||||
} catch (IOException e) {
|
||||
anError = e.getMessage();
|
||||
}
|
||||
log.warn("Cound not get security data, using default security (none): " + anError);
|
||||
}
|
||||
|
||||
if(theData == null || anError != null) {
|
||||
securityDescriptor = new BridgeSecurityDescriptor();
|
||||
}
|
||||
}
|
||||
|
||||
public String getSecurityDescriptorData() throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
return encrypt(new Gson().toJson(securityDescriptor));
|
||||
}
|
||||
|
||||
public boolean isUseLinkButton() {
|
||||
return securityDescriptor.isUseLinkButton();
|
||||
}
|
||||
|
||||
public String setPassword(User aUser) throws IOException {
|
||||
String error = null;
|
||||
if(aUser != null) {
|
||||
error = aUser.validate();
|
||||
if(error == null) {
|
||||
if(securityDescriptor.getUsers() != null) {
|
||||
User theUser = securityDescriptor.getUsers().get(aUser.getUsername());
|
||||
if(theUser != null) {
|
||||
theUser.setPassword(aUser.getPassword());
|
||||
theUser.setPassword2(null);
|
||||
settingsChanged = true;
|
||||
}
|
||||
else
|
||||
error = "User not found";
|
||||
}
|
||||
else
|
||||
error = "User not found";
|
||||
}
|
||||
}
|
||||
else
|
||||
error = "invalid user object given";
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
public String addUser(User aUser) throws IOException {
|
||||
String error = null;
|
||||
if(aUser != null) {
|
||||
error = aUser.validate();
|
||||
if(error == null) {
|
||||
if(securityDescriptor.getUsers() == null)
|
||||
securityDescriptor.setUsers(new HashMap<String, User>());
|
||||
if(securityDescriptor.getUsers().get(aUser.getUsername()) == null) {
|
||||
securityDescriptor.getUsers().put(aUser.getUsername(), aUser);
|
||||
settingsChanged = true;
|
||||
}
|
||||
else
|
||||
error = "Invalid request";
|
||||
}
|
||||
}
|
||||
else
|
||||
error = "invalid user object given";
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
public String delUser(User aUser) throws IOException {
|
||||
String error = null;
|
||||
if(aUser != null) {
|
||||
if(securityDescriptor.getUsers() != null) {
|
||||
if(securityDescriptor.getUsers().get(aUser.getUsername()) != null) {
|
||||
securityDescriptor.getUsers().remove(aUser.getUsername());
|
||||
settingsChanged = true;
|
||||
}
|
||||
else
|
||||
error = "User not found";
|
||||
}
|
||||
}
|
||||
else
|
||||
error = "invalid user object given";
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setExecGarden(String theGarden) {
|
||||
securityDescriptor.setExecGarden(theGarden);
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
public String getExecGarden() {
|
||||
return securityDescriptor.getExecGarden();
|
||||
}
|
||||
public void setUseLinkButton(boolean useThis) {
|
||||
securityDescriptor.setUseLinkButton(useThis);
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
public boolean isSecureHueApi() {
|
||||
return securityDescriptor.isSecureHueApi();
|
||||
}
|
||||
|
||||
public void setSecureHueApi(boolean theState) {
|
||||
securityDescriptor.setSecureHueApi(theState);
|
||||
}
|
||||
public SecurityInfo getSecurityInfo() {
|
||||
SecurityInfo theInfo = new SecurityInfo();
|
||||
theInfo.setExecGarden(getExecGarden());
|
||||
theInfo.setUseLinkButton(isUseLinkButton());
|
||||
theInfo.setSecureHueApi(isSecureHueApi());
|
||||
theInfo.setSecure(isSecure());
|
||||
return theInfo;
|
||||
}
|
||||
public LoginResult validatePassword(User targetUser) throws IOException {
|
||||
LoginResult result = new LoginResult();
|
||||
if(targetUser != null && targetUser.getUsername() != null) {
|
||||
if(securityDescriptor.getUsers() != null && securityDescriptor.getUsers().get(targetUser.getUsername()) != null) {
|
||||
User theUser = securityDescriptor.getUsers().get(targetUser.getUsername());
|
||||
if(theUser.getPassword() != null) {
|
||||
theUser.setPassword2(targetUser.getPassword());
|
||||
if(theUser.validatePassword()) {
|
||||
theUser.setPassword2(null);
|
||||
result.setUser(targetUser);
|
||||
}
|
||||
else
|
||||
result.setError("user or password not correct");
|
||||
} else {
|
||||
result.setError("input password is not set....");
|
||||
}
|
||||
}
|
||||
else
|
||||
result.setError("user or password not correct");
|
||||
}
|
||||
else
|
||||
result.setError("input user not given");
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
return securityDescriptor.isSecure();
|
||||
}
|
||||
|
||||
public boolean isSettingsChanged() {
|
||||
return settingsChanged;
|
||||
}
|
||||
|
||||
public void setSettingsChanged(boolean settingsChanged) {
|
||||
this.settingsChanged = settingsChanged;
|
||||
}
|
||||
|
||||
private String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
|
||||
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(habridgeKey));
|
||||
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
|
||||
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
|
||||
return base64Encode(pbeCipher.doFinal(property.getBytes("UTF-8")));
|
||||
}
|
||||
|
||||
private static String base64Encode(byte[] bytes) {
|
||||
return Base64.getEncoder().encodeToString(bytes);
|
||||
}
|
||||
|
||||
private String decrypt(String property) throws GeneralSecurityException, IOException {
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
|
||||
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(habridgeKey));
|
||||
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
|
||||
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
|
||||
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
|
||||
}
|
||||
|
||||
private static byte[] base64Decode(String property) throws IOException {
|
||||
return Base64.getDecoder().decode(property);
|
||||
}
|
||||
|
||||
public void addAuthenticatedUser(Request request, User u) {
|
||||
request.session().attribute(USER_SESSION_ID, u);
|
||||
|
||||
}
|
||||
|
||||
public void removeAuthenticatedUser(Request request) {
|
||||
request.session().removeAttribute(USER_SESSION_ID);
|
||||
|
||||
}
|
||||
|
||||
public User getAuthenticatedUser(Request request) {
|
||||
User theUser = request.session().attribute(USER_SESSION_ID);
|
||||
if(theUser == null) {
|
||||
String authHeader = request.headers("Authorization");
|
||||
if(authHeader != null) {
|
||||
byte[] authData;
|
||||
try {
|
||||
authData = base64Decode(authHeader.substring(6));
|
||||
} catch (IOException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
return theUser;
|
||||
}
|
||||
String[] credentials = new String(authData).split(":");
|
||||
String username = credentials[0];
|
||||
String password = credentials[1];
|
||||
theUser = new User();
|
||||
theUser.setUsername(username);
|
||||
theUser.setPassword(password);
|
||||
LoginResult theResult = null;
|
||||
try {
|
||||
theResult = validatePassword(theUser);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
return null;
|
||||
}
|
||||
if(theResult != null && theResult.getError() == null) {
|
||||
addAuthenticatedUser(request, theUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
return theUser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class BridgeSecurityDescriptor {
|
||||
private Map<String, User> users;
|
||||
private boolean useLinkButton;
|
||||
private String execGarden;
|
||||
private boolean secureHueApi;
|
||||
|
||||
public BridgeSecurityDescriptor() {
|
||||
super();
|
||||
this.setUseLinkButton(false);
|
||||
}
|
||||
|
||||
public Map<String, User> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public void setUsers(Map<String, User> users) {
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
public boolean isUseLinkButton() {
|
||||
return useLinkButton;
|
||||
}
|
||||
|
||||
public void setUseLinkButton(boolean useLinkButton) {
|
||||
this.useLinkButton = useLinkButton;
|
||||
}
|
||||
|
||||
public String getExecGarden() {
|
||||
return execGarden;
|
||||
}
|
||||
|
||||
public void setExecGarden(String execGarden) {
|
||||
this.execGarden = execGarden;
|
||||
}
|
||||
|
||||
public boolean isSecureHueApi() {
|
||||
return secureHueApi;
|
||||
}
|
||||
|
||||
public void setSecureHueApi(boolean secureHueApi) {
|
||||
this.secureHueApi = secureHueApi;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
boolean secureFlag = false;
|
||||
if(users != null && !users.isEmpty()) {
|
||||
for (Map.Entry<String, User> entry : users.entrySet())
|
||||
{
|
||||
if(entry.getValue().getPassword() != null && !entry.getValue().getPassword().isEmpty()) {
|
||||
secureFlag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return secureFlag;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
@@ -10,6 +11,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -26,12 +28,18 @@ public class BridgeSettings extends BackupHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
private BridgeSettingsDescriptor theBridgeSettings;
|
||||
private BridgeControlDescriptor bridgeControl;
|
||||
private BridgeSecurity bridgeSecurity;
|
||||
|
||||
public BridgeSettings() {
|
||||
super();
|
||||
bridgeControl = new BridgeControlDescriptor();
|
||||
theBridgeSettings = new BridgeSettingsDescriptor();
|
||||
String ipV6Stack = System.getProperty("ipV6Stack");
|
||||
bridgeSecurity = null;
|
||||
String theKey = System.getProperty("security.key");
|
||||
if(theKey == null)
|
||||
theKey = "IWantMyPasswordsToBeAbleToBeDecodedPleaseSeeTheReadme";
|
||||
bridgeSecurity = new BridgeSecurity(theKey.toCharArray());
|
||||
String ipV6Stack = System.getProperty("ipV6Stack");
|
||||
if(ipV6Stack == null || !ipV6Stack.equalsIgnoreCase("true")) {
|
||||
System.setProperty("java.net.preferIPv4Stack" , "true");
|
||||
}
|
||||
@@ -43,9 +51,13 @@ public class BridgeSettings extends BackupHandler {
|
||||
public BridgeSettingsDescriptor getBridgeSettingsDescriptor() {
|
||||
return theBridgeSettings;
|
||||
}
|
||||
public BridgeSecurity getBridgeSecurity() {
|
||||
return bridgeSecurity;
|
||||
}
|
||||
public void buildSettings() {
|
||||
String addressString = null;
|
||||
String theVeraAddress = null;
|
||||
String theSomfyAddress = null;
|
||||
String theHarmonyAddress = null;
|
||||
String configFileProperty = System.getProperty("config.file");
|
||||
if(configFileProperty == null) {
|
||||
@@ -103,6 +115,23 @@ public class BridgeSettings extends BackupHandler {
|
||||
}
|
||||
}
|
||||
theBridgeSettings.setHarmonyAddress(theHarmonyList);
|
||||
|
||||
theSomfyAddress = System.getProperty("somfy.address");
|
||||
IpList theSomfyList = null;
|
||||
if(theSomfyAddress != null) {
|
||||
try {
|
||||
theSomfyList = new Gson().fromJson(theSomfyAddress, IpList.class);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
theSomfyList = new Gson().fromJson("{devices:[{name:default,ip:" + theSomfyAddress + "}]}", IpList.class);
|
||||
} catch (Exception et) {
|
||||
log.error("Cannot parse somfy.address, not set with message: " + e.getMessage(), e);
|
||||
theSomfyList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
theBridgeSettings.setSomfyAddress(theSomfyList);
|
||||
|
||||
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
|
||||
theBridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
|
||||
theBridgeSettings.setButtonsleep(Integer.parseInt(System.getProperty("button.sleep", Configuration.DEFAULT_BUTTON_SLEEP)));
|
||||
@@ -148,11 +177,17 @@ public class BridgeSettings extends BackupHandler {
|
||||
theBridgeSettings.setMqttconfigured(theBridgeSettings.isValidMQTT());
|
||||
theBridgeSettings.setHassconfigured(theBridgeSettings.isValidHass());
|
||||
theBridgeSettings.setDomoticzconfigured(theBridgeSettings.isValidDomoticz());
|
||||
theBridgeSettings.setSomfyconfigured(theBridgeSettings.isValidSomfy());
|
||||
// Lifx is either configured or not, so it does not need an update.
|
||||
if(serverPortOverride != null)
|
||||
theBridgeSettings.setServerPort(serverPortOverride);
|
||||
if(serverIpOverride != null)
|
||||
theBridgeSettings.setWebaddress(serverIpOverride);
|
||||
setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-");
|
||||
|
||||
setupInternalTestUser();
|
||||
|
||||
bridgeSecurity.setSecurityData(theBridgeSettings.getSecurityData());
|
||||
}
|
||||
|
||||
public void loadConfig() {
|
||||
@@ -181,12 +216,34 @@ 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 {
|
||||
newBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (GeneralSecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
bridgeSecurity.setSettingsChanged(false);
|
||||
}
|
||||
String jsonValue = aRenderer.render(newBridgeSettings);
|
||||
configWriter(jsonValue, configPath);
|
||||
_loadConfig(configPath);
|
||||
}
|
||||
|
||||
|
||||
public void updateConfigFile() {
|
||||
log.debug("Save HA Bridge settings.");
|
||||
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
String jsonValue = aRenderer.render(theBridgeSettings);
|
||||
configWriter(jsonValue, configPath);
|
||||
_loadConfig(configPath);
|
||||
}
|
||||
|
||||
|
||||
private void configWriter(String content, Path filePath) {
|
||||
if(Files.exists(filePath) && !Files.isWritable(filePath)){
|
||||
log.error("Error file is not writable: " + filePath);
|
||||
@@ -217,7 +274,9 @@ public class BridgeSettings extends BackupHandler {
|
||||
perms.add(PosixFilePermission.OWNER_WRITE);
|
||||
|
||||
try {
|
||||
Files.setPosixFilePermissions(filePath, perms);
|
||||
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");
|
||||
}
|
||||
@@ -278,4 +337,9 @@ public class BridgeSettings extends BackupHandler {
|
||||
}
|
||||
return addressString;
|
||||
}
|
||||
private void setupInternalTestUser() {
|
||||
theBridgeSettings.setupInternalTestUser();
|
||||
if(theBridgeSettings.isSettingsChanged())
|
||||
this.updateConfigFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.bwssystems.HABridge.api.hue.HueConstants;
|
||||
import com.bwssystems.HABridge.api.hue.HueError;
|
||||
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
|
||||
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
|
||||
|
||||
public class BridgeSettingsDescriptor {
|
||||
private static final String DEFAULT_INTERNAL_USER = "thehabridgeuser";
|
||||
private static final String DEFAULT_USER_DESCRIPTION = "default_test_user";
|
||||
private String upnpconfigaddress;
|
||||
private Integer serverport;
|
||||
private Integer upnpresponseport;
|
||||
@@ -40,6 +49,10 @@ public class BridgeSettingsDescriptor {
|
||||
private String hubversion;
|
||||
private IpList domoticzaddress;
|
||||
private boolean domoticzconfigured;
|
||||
private IpList somfyaddress;
|
||||
private boolean somfyconfigured;
|
||||
private boolean lifxconfigured;
|
||||
private String securityData;
|
||||
|
||||
public BridgeSettingsDescriptor() {
|
||||
super();
|
||||
@@ -47,6 +60,7 @@ public class BridgeSettingsDescriptor {
|
||||
this.traceupnp = false;
|
||||
this.nestconfigured = false;
|
||||
this.veraconfigured = false;
|
||||
this.somfyconfigured = false;
|
||||
this.harmonyconfigured = false;
|
||||
this.hueconfigured = false;
|
||||
this.halconfigured = false;
|
||||
@@ -92,9 +106,15 @@ public class BridgeSettingsDescriptor {
|
||||
public IpList getVeraAddress() {
|
||||
return veraaddress;
|
||||
}
|
||||
public IpList getSomfyAddress() {
|
||||
return somfyaddress;
|
||||
}
|
||||
public void setVeraAddress(IpList veraAddress) {
|
||||
this.veraaddress = veraAddress;
|
||||
}
|
||||
public void setSomfyAddress(IpList somfyAddress) {
|
||||
this.somfyaddress = somfyAddress;
|
||||
}
|
||||
public IpList getHarmonyAddress() {
|
||||
return harmonyaddress;
|
||||
}
|
||||
@@ -128,9 +148,15 @@ public class BridgeSettingsDescriptor {
|
||||
public boolean isVeraconfigured() {
|
||||
return veraconfigured;
|
||||
}
|
||||
public boolean isSomfyconfigured() {
|
||||
return somfyconfigured;
|
||||
}
|
||||
public void setVeraconfigured(boolean veraconfigured) {
|
||||
this.veraconfigured = veraconfigured;
|
||||
}
|
||||
public void setSomfyconfigured(boolean somfyconfigured) {
|
||||
this.somfyconfigured = somfyconfigured;
|
||||
}
|
||||
public boolean isHarmonyconfigured() {
|
||||
return harmonyconfigured;
|
||||
}
|
||||
@@ -263,6 +289,18 @@ public class BridgeSettingsDescriptor {
|
||||
public void setDomoticzconfigured(boolean domoticzconfigured) {
|
||||
this.domoticzconfigured = domoticzconfigured;
|
||||
}
|
||||
public boolean isLifxconfigured() {
|
||||
return lifxconfigured;
|
||||
}
|
||||
public void setLifxconfigured(boolean lifxconfigured) {
|
||||
this.lifxconfigured = lifxconfigured;
|
||||
}
|
||||
public String getSecurityData() {
|
||||
return securityData;
|
||||
}
|
||||
public void setSecurityData(String securityData) {
|
||||
this.securityData = securityData;
|
||||
}
|
||||
public Boolean isValidVera() {
|
||||
if(this.getVeraAddress() == null || this.getVeraAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
@@ -328,4 +366,91 @@ public class BridgeSettingsDescriptor {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidSomfy() {
|
||||
if(this.getSomfyAddress() == null || this.getSomfyAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getSomfyAddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidLifx() {
|
||||
return this.isLifxconfigured();
|
||||
}
|
||||
|
||||
public HueError[] validateWhitelistUser(String aUser, String userDescription, boolean strict) {
|
||||
String validUser = null;
|
||||
boolean found = false;
|
||||
if (aUser != null && !aUser.equalsIgnoreCase("undefined") && !aUser.equalsIgnoreCase("null")
|
||||
&& !aUser.equalsIgnoreCase("")) {
|
||||
if (whitelist != null) {
|
||||
Set<String> theUserIds = whitelist.keySet();
|
||||
Iterator<String> userIterator = theUserIds.iterator();
|
||||
while (userIterator.hasNext()) {
|
||||
validUser = userIterator.next();
|
||||
if (validUser.equals(aUser))
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found && !strict) {
|
||||
newWhitelistUser(aUser, userDescription);
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return HueErrorResponse.createResponse("1", "/api/" + aUser, "unauthorized user", null, null, null).getTheErrors();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void newWhitelistUser(String aUser, String userDescription) {
|
||||
if (whitelist == null) {
|
||||
whitelist = new HashMap<>();
|
||||
}
|
||||
if(userDescription == null)
|
||||
userDescription = "auto insert user";
|
||||
|
||||
whitelist.put(aUser, WhitelistEntry.createEntry(userDescription));
|
||||
setSettingsChanged(true);
|
||||
}
|
||||
|
||||
public String createWhitelistUser(String userDescription) {
|
||||
String aUser = getNewUserID();
|
||||
newWhitelistUser(aUser, userDescription);
|
||||
return aUser;
|
||||
}
|
||||
|
||||
private String getNewUserID() {
|
||||
UUID uid = UUID.randomUUID();
|
||||
StringTokenizer st = new StringTokenizer(uid.toString(), "-");
|
||||
String newUser = "";
|
||||
while (st.hasMoreTokens()) {
|
||||
newUser = newUser + st.nextToken();
|
||||
}
|
||||
|
||||
return newUser;
|
||||
}
|
||||
|
||||
public String getInternalTestUser() {
|
||||
return DEFAULT_INTERNAL_USER;
|
||||
}
|
||||
|
||||
public void setupInternalTestUser() {
|
||||
boolean found = false;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +1,87 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DeviceMapTypes {
|
||||
|
||||
public final static String[] CUSTOM_DEVICE = { "custom", "Custom"};
|
||||
public final static String[] VERA_DEVICE = { "veraDevice", "Vera Device"};
|
||||
public final static String[] VERA_SCENE = { "veraScene", "Vera Scene"};
|
||||
public final static String[] HARMONY_ACTIVITY = { "harmonyActivity", "Harmony Activity"};
|
||||
public final static String[] HARMONY_BUTTON = { "harmonyButton", "Harmony Button"};
|
||||
public final static String[] NEST_HOMEAWAY = { "nestHomeAway", "Nest Home Status"};
|
||||
public final static String[] NEST_THERMO_SET = { "nestThermoSet", "Nest Thermostat"};
|
||||
public final static String[] HUE_DEVICE = { "hueDevice", "Hue Device"};
|
||||
public final static String[] HAL_DEVICE = { "halDevice", "HAL Device"};
|
||||
public final static String[] HAL_BUTTON = { "halButton", "HAL Button"};
|
||||
public final static String[] HAL_HOME = { "halHome", "HAL Home Status"};
|
||||
public final static String[] HAL_THERMO_SET = { "halThermoSet", "HAL Thermostat"};
|
||||
public final static String[] MQTT_MESSAGE = { "mqttMessage", "MQTT Message"};
|
||||
public final static String[] EXEC_DEVICE_COMPAT = { "exec", "Execute Script/Program"};
|
||||
public final static String[] CMD_DEVICE = { "cmdDevice", "Execute Command/Script/Program"};
|
||||
public final static String[] HASS_DEVICE = { "hassDevice", "HomeAssistant Device"};
|
||||
public final static String[] TCP_DEVICE = { "tcpDevice", "TCP Device"};
|
||||
public final static String[] TCP_DEVICE_COMPAT = { "TCP", "TCP Device"};
|
||||
public final static String[] UDP_DEVICE = { "udpDevice", "UDP Device"};
|
||||
public final static String[] UDP_DEVICE_COMPAT = { "UDP", "UDP Device"};
|
||||
public final static String[] HTTP_DEVICE = { "httpDevice", "HTTP Device"};
|
||||
public final static String[] DOMOTICZ_DEVICE = { "domoticzDevice", "Domoticz Device"};
|
||||
|
||||
public final static int typeIndex = 0;
|
||||
public final static int displayIndex = 1;
|
||||
|
||||
ArrayList<String[]> deviceMapTypes;
|
||||
|
||||
public DeviceMapTypes() {
|
||||
super();
|
||||
deviceMapTypes = new ArrayList<String[]>();
|
||||
deviceMapTypes.add(CMD_DEVICE);
|
||||
deviceMapTypes.add(DOMOTICZ_DEVICE);
|
||||
deviceMapTypes.add(HAL_DEVICE);
|
||||
deviceMapTypes.add(HAL_HOME);
|
||||
deviceMapTypes.add(HAL_THERMO_SET);
|
||||
deviceMapTypes.add(HAL_BUTTON);
|
||||
deviceMapTypes.add(HARMONY_ACTIVITY);
|
||||
deviceMapTypes.add(HARMONY_BUTTON);
|
||||
deviceMapTypes.add(HASS_DEVICE);
|
||||
deviceMapTypes.add(HTTP_DEVICE);
|
||||
deviceMapTypes.add(HUE_DEVICE);
|
||||
deviceMapTypes.add(MQTT_MESSAGE);
|
||||
deviceMapTypes.add(NEST_HOMEAWAY);
|
||||
deviceMapTypes.add(NEST_THERMO_SET);
|
||||
deviceMapTypes.add(TCP_DEVICE);
|
||||
deviceMapTypes.add(UDP_DEVICE);
|
||||
deviceMapTypes.add(VERA_DEVICE);
|
||||
deviceMapTypes.add(VERA_SCENE);
|
||||
}
|
||||
public static int getTypeIndex() {
|
||||
return typeIndex;
|
||||
}
|
||||
public static int getDisplayIndex() {
|
||||
return displayIndex;
|
||||
}
|
||||
public ArrayList<String[]> getDeviceMapTypes() {
|
||||
return deviceMapTypes;
|
||||
}
|
||||
|
||||
public Boolean validateType(String type) {
|
||||
if(type == null || type.trim().isEmpty())
|
||||
return false;
|
||||
for(String[] mapType : deviceMapTypes) {
|
||||
if(type.trim().contentEquals(mapType[typeIndex]))
|
||||
return true;
|
||||
}
|
||||
if(type.trim().contentEquals(EXEC_DEVICE_COMPAT[typeIndex]))
|
||||
return true;
|
||||
if(type.trim().contentEquals(TCP_DEVICE_COMPAT[typeIndex]))
|
||||
return true;
|
||||
if(type.trim().contentEquals(UDP_DEVICE_COMPAT[typeIndex]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DeviceMapTypes {
|
||||
|
||||
public final static String[] CUSTOM_DEVICE = { "custom", "Custom"};
|
||||
public final static String[] VERA_DEVICE = { "veraDevice", "Vera Device"};
|
||||
public final static String[] VERA_SCENE = { "veraScene", "Vera Scene"};
|
||||
public final static String[] HARMONY_ACTIVITY = { "harmonyActivity", "Harmony Activity"};
|
||||
public final static String[] HARMONY_BUTTON = { "harmonyButton", "Harmony Button"};
|
||||
public final static String[] NEST_HOMEAWAY = { "nestHomeAway", "Nest Home Status"};
|
||||
public final static String[] NEST_THERMO_SET = { "nestThermoSet", "Nest Thermostat"};
|
||||
public final static String[] HUE_DEVICE = { "hueDevice", "Hue Device"};
|
||||
public final static String[] HAL_DEVICE = { "halDevice", "HAL Device"};
|
||||
public final static String[] HAL_BUTTON = { "halButton", "HAL Button"};
|
||||
public final static String[] HAL_HOME = { "halHome", "HAL Home Status"};
|
||||
public final static String[] HAL_THERMO_SET = { "halThermoSet", "HAL Thermostat"};
|
||||
public final static String[] MQTT_MESSAGE = { "mqttMessage", "MQTT Message"};
|
||||
public final static String[] EXEC_DEVICE_COMPAT = { "exec", "Execute Script/Program"};
|
||||
public final static String[] CMD_DEVICE = { "cmdDevice", "Execute Command/Script/Program"};
|
||||
public final static String[] HASS_DEVICE = { "hassDevice", "HomeAssistant Device"};
|
||||
public final static String[] TCP_DEVICE = { "tcpDevice", "TCP Device"};
|
||||
public final static String[] TCP_DEVICE_COMPAT = { "TCP", "TCP Device"};
|
||||
public final static String[] UDP_DEVICE = { "udpDevice", "UDP Device"};
|
||||
public final static String[] UDP_DEVICE_COMPAT = { "UDP", "UDP Device"};
|
||||
public final static String[] HTTP_DEVICE = { "httpDevice", "HTTP Device"};
|
||||
public final static String[] DOMOTICZ_DEVICE = { "domoticzDevice", "Domoticz Device"};
|
||||
public final static String[] SOMFY_DEVICE = { "somfyDevice", "Somfy Device"};
|
||||
public final static String[] LIFX_DEVICE = { "lifxDevice", "LIFX Device"};
|
||||
|
||||
public final static int typeIndex = 0;
|
||||
public final static int displayIndex = 1;
|
||||
|
||||
ArrayList<String[]> deviceMapTypes;
|
||||
|
||||
public DeviceMapTypes() {
|
||||
super();
|
||||
deviceMapTypes = new ArrayList<String[]>();
|
||||
deviceMapTypes.add(CMD_DEVICE);
|
||||
deviceMapTypes.add(DOMOTICZ_DEVICE);
|
||||
deviceMapTypes.add(HAL_DEVICE);
|
||||
deviceMapTypes.add(HAL_HOME);
|
||||
deviceMapTypes.add(HAL_THERMO_SET);
|
||||
deviceMapTypes.add(HAL_BUTTON);
|
||||
deviceMapTypes.add(HARMONY_ACTIVITY);
|
||||
deviceMapTypes.add(HARMONY_BUTTON);
|
||||
deviceMapTypes.add(HASS_DEVICE);
|
||||
deviceMapTypes.add(HTTP_DEVICE);
|
||||
deviceMapTypes.add(HUE_DEVICE);
|
||||
deviceMapTypes.add(LIFX_DEVICE);
|
||||
deviceMapTypes.add(MQTT_MESSAGE);
|
||||
deviceMapTypes.add(NEST_HOMEAWAY);
|
||||
deviceMapTypes.add(NEST_THERMO_SET);
|
||||
deviceMapTypes.add(SOMFY_DEVICE);
|
||||
deviceMapTypes.add(TCP_DEVICE);
|
||||
deviceMapTypes.add(UDP_DEVICE);
|
||||
deviceMapTypes.add(VERA_DEVICE);
|
||||
deviceMapTypes.add(VERA_SCENE);
|
||||
deviceMapTypes.add(SOMFY_DEVICE);
|
||||
}
|
||||
public static int getTypeIndex() {
|
||||
return typeIndex;
|
||||
}
|
||||
public static int getDisplayIndex() {
|
||||
return displayIndex;
|
||||
}
|
||||
public ArrayList<String[]> getDeviceMapTypes() {
|
||||
return deviceMapTypes;
|
||||
}
|
||||
|
||||
public Boolean validateType(String type) {
|
||||
if(type == null || type.trim().isEmpty())
|
||||
return false;
|
||||
for(String[] mapType : deviceMapTypes) {
|
||||
if(type.trim().contentEquals(mapType[typeIndex]))
|
||||
return true;
|
||||
}
|
||||
if(type.trim().contentEquals(EXEC_DEVICE_COMPAT[typeIndex]))
|
||||
return true;
|
||||
if(type.trim().contentEquals(TCP_DEVICE_COMPAT[typeIndex]))
|
||||
return true;
|
||||
if(type.trim().contentEquals(UDP_DEVICE_COMPAT[typeIndex]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,8 @@ public class HABridge {
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") starting....");
|
||||
|
||||
bridgeSettings = new BridgeSettings();
|
||||
// sparkjava config directive to set html static file location for Jetty
|
||||
staticFileLocation("/public");
|
||||
while(!bridgeSettings.getBridgeControl().isStop()) {
|
||||
bridgeSettings.buildSettings();
|
||||
log.info("HA Bridge initializing....");
|
||||
@@ -52,8 +54,9 @@ public class HABridge {
|
||||
ipAddress(bridgeSettings.getBridgeSettingsDescriptor().getWebaddress());
|
||||
// sparkjava config directive to set port for the web server to listen on
|
||||
port(bridgeSettings.getBridgeSettingsDescriptor().getServerPort());
|
||||
// sparkjava config directive to set html static file location for Jetty
|
||||
staticFileLocation("/public");
|
||||
if(!bridgeSettings.getBridgeControl().isReinit())
|
||||
init();
|
||||
bridgeSettings.getBridgeControl().setReinit(false);
|
||||
// setup system control api first
|
||||
theSystem = new SystemControl(bridgeSettings, theVersion);
|
||||
theSystem.setupServer();
|
||||
@@ -65,14 +68,14 @@ public class HABridge {
|
||||
else {
|
||||
//Setup the device connection homes through the manager
|
||||
homeManager = new HomeManager();
|
||||
homeManager.buildHomes(bridgeSettings.getBridgeSettingsDescriptor(), udpSender);
|
||||
homeManager.buildHomes(bridgeSettings, udpSender);
|
||||
// setup the class to handle the resource setup rest api
|
||||
theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), homeManager);
|
||||
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.getBridgeSettingsDescriptor(), theResources.getDeviceRepository(), homeManager);
|
||||
theHueMulator = new HueMulator(bridgeSettings, theResources.getDeviceRepository(), homeManager);
|
||||
theHueMulator.setupServer();
|
||||
// wait for the sparkjava initialization of the rest api classes to be complete
|
||||
awaitInitialization();
|
||||
@@ -87,9 +90,17 @@ public class HABridge {
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
homeManager.closeHomes();
|
||||
udpSender.closeResponseSocket();
|
||||
udpSender = null;
|
||||
}
|
||||
bridgeSettings.getBridgeControl().setReinit(false);
|
||||
stop();
|
||||
if(!bridgeSettings.getBridgeControl().isStop()) {
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting....");
|
||||
System.exit(0);
|
||||
|
||||
@@ -4,6 +4,6 @@ import com.bwssystems.HABridge.devicemanagmeent.ResourceHandler;
|
||||
import com.bwssystems.HABridge.hue.HueMulatorHandler;
|
||||
|
||||
public interface Home extends HueMulatorHandler, ResourceHandler {
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings);
|
||||
public Home createHome(BridgeSettings bridgeSettings);
|
||||
public void closeHome();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -12,7 +13,9 @@ import com.bwssystems.HABridge.plugins.harmony.HarmonyHome;
|
||||
import com.bwssystems.HABridge.plugins.hass.HassHome;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHome;
|
||||
import com.bwssystems.HABridge.plugins.hue.HueHome;
|
||||
import com.bwssystems.HABridge.plugins.lifx.LifxHome;
|
||||
import com.bwssystems.HABridge.plugins.mqtt.MQTTHome;
|
||||
import com.bwssystems.HABridge.plugins.somfy.SomfyHome;
|
||||
import com.bwssystems.HABridge.plugins.tcp.TCPHome;
|
||||
import com.bwssystems.HABridge.plugins.udp.UDPHome;
|
||||
import com.bwssystems.HABridge.plugins.vera.VeraHome;
|
||||
@@ -28,7 +31,7 @@ public class HomeManager {
|
||||
}
|
||||
|
||||
// factory method
|
||||
public void buildHomes(BridgeSettingsDescriptor bridgeSettings, UDPDatagramSender aUdpDatagramSender) {
|
||||
public void buildHomes(BridgeSettings bridgeSettings, UDPDatagramSender aUdpDatagramSender) {
|
||||
Home aHome = null;
|
||||
//setup the harmony connection if available
|
||||
aHome = new HarmonyHome(bridgeSettings);
|
||||
@@ -70,7 +73,6 @@ public class HomeManager {
|
||||
homeList.put(DeviceMapTypes.CUSTOM_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the tcp handler Home
|
||||
aHome = new TCPHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.TCP_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
@@ -79,13 +81,22 @@ public class HomeManager {
|
||||
aHome = new UDPHome(bridgeSettings, aUdpDatagramSender);
|
||||
homeList.put(DeviceMapTypes.UDP_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.UDP_DEVICE_COMPAT[DeviceMapTypes.typeIndex], aHome);
|
||||
|
||||
// Setup Vera Home if available
|
||||
aHome = new VeraHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the HomeAssistant configuration if available
|
||||
//setup the Domoticz configuration if available
|
||||
aHome = new DomoticzHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the Somfy configuration if available
|
||||
aHome = new SomfyHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the Lifx configuration if available
|
||||
aHome = new LifxHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
}
|
||||
|
||||
public Home findHome(String type) {
|
||||
@@ -96,6 +107,11 @@ public class HomeManager {
|
||||
}
|
||||
|
||||
public void closeHomes() {
|
||||
|
||||
Collection<Home> theHomes = homeList.values();
|
||||
for(Home aHome : theHomes) {
|
||||
aHome.closeHome();
|
||||
}
|
||||
homeList.clear();
|
||||
homeList = null;
|
||||
}
|
||||
}
|
||||
|
||||
26
src/main/java/com/bwssystems/HABridge/LinkButtonPressed.java
Normal file
26
src/main/java/com/bwssystems/HABridge/LinkButtonPressed.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LinkButtonPressed extends TimerTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(LinkButtonPressed.class);
|
||||
private BridgeControlDescriptor linkDescriptor;
|
||||
private Timer myTimer;
|
||||
|
||||
public LinkButtonPressed(BridgeControlDescriptor theDescriptor, Timer aTimer) {
|
||||
linkDescriptor = theDescriptor;
|
||||
myTimer = aTimer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
log.info("Link button time ended....");
|
||||
linkDescriptor.setLinkButton(false);
|
||||
myTimer.cancel();
|
||||
}
|
||||
|
||||
}
|
||||
22
src/main/java/com/bwssystems/HABridge/LoginResult.java
Normal file
22
src/main/java/com/bwssystems/HABridge/LoginResult.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class LoginResult {
|
||||
|
||||
private String error;
|
||||
|
||||
private User user;
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,8 @@ package com.bwssystems.HABridge;
|
||||
|
||||
public class NamedIP {
|
||||
private String name;
|
||||
private String ip;
|
||||
private String ip;
|
||||
private String webhook;
|
||||
private String port;
|
||||
private String username;
|
||||
private String password;
|
||||
@@ -20,7 +21,13 @@ public class NamedIP {
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
public String getPort() {
|
||||
public String getWebhook() {
|
||||
return webhook;
|
||||
}
|
||||
public void setWebhook(final String webhook) {
|
||||
this.webhook = webhook;
|
||||
}
|
||||
public String getPort() {
|
||||
return port;
|
||||
}
|
||||
public void setPort(String port) {
|
||||
|
||||
33
src/main/java/com/bwssystems/HABridge/SecurityInfo.java
Normal file
33
src/main/java/com/bwssystems/HABridge/SecurityInfo.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class SecurityInfo {
|
||||
private boolean useLinkButton;
|
||||
private String execGarden;
|
||||
private boolean secureHueApi;
|
||||
private boolean isSecure;
|
||||
|
||||
public boolean isUseLinkButton() {
|
||||
return useLinkButton;
|
||||
}
|
||||
public void setUseLinkButton(boolean useLinkButton) {
|
||||
this.useLinkButton = useLinkButton;
|
||||
}
|
||||
public String getExecGarden() {
|
||||
return execGarden;
|
||||
}
|
||||
public void setExecGarden(String execGarden) {
|
||||
this.execGarden = execGarden;
|
||||
}
|
||||
public boolean isSecureHueApi() {
|
||||
return secureHueApi;
|
||||
}
|
||||
public void setSecureHueApi(boolean secureHueApi) {
|
||||
this.secureHueApi = secureHueApi;
|
||||
}
|
||||
public boolean isSecure() {
|
||||
return isSecure;
|
||||
}
|
||||
public void setSecure(boolean isSecure) {
|
||||
this.isSecure = isSecure;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import static spark.Spark.get;
|
||||
import static spark.Spark.options;
|
||||
import static spark.Spark.post;
|
||||
import static spark.Spark.put;
|
||||
import static spark.Spark.before;
|
||||
import static spark.Spark.halt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
@@ -12,6 +14,8 @@ import java.net.MulticastSocket;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Timer;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.slf4j.Logger;
|
||||
@@ -55,17 +59,36 @@ public class SystemControl {
|
||||
// This function sets up the sparkjava rest calls for the hue api
|
||||
public void setupServer() {
|
||||
log.info("System control service started....");
|
||||
before(SYSTEM_CONTEXT + "/*", (request, response) -> {
|
||||
if(bridgeSettings.getBridgeSecurity().isSecure()) {
|
||||
String pathInfo = request.pathInfo();
|
||||
if(pathInfo == null || (!pathInfo.equals(SYSTEM_CONTEXT + "/login") && !pathInfo.equals(SYSTEM_CONTEXT + "/habridge/version"))) {
|
||||
User authUser = bridgeSettings.getBridgeSecurity().getAuthenticatedUser(request);
|
||||
if(authUser == null) {
|
||||
halt(401, "{\"message\":\"User not authenticated\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// http://ip_address:port/system/habridge/version gets the version of this bridge instance
|
||||
get (SYSTEM_CONTEXT + "/habridge/version", "application/json", (request, response) -> {
|
||||
get (SYSTEM_CONTEXT + "/habridge/version", (request, response) -> {
|
||||
log.debug("Get HA Bridge version: v" + version.getVersion());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return "{\"version\":\"" + version.getVersion() + "\"}";
|
||||
response.type("application/json");
|
||||
return "{\"version\":\"" + version.getVersion() + "\",\"isSecure\":" + bridgeSettings.getBridgeSecurity().isSecure() + "}";
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/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", "application/json", (request, response) -> {
|
||||
get (SYSTEM_CONTEXT + "/logmsgs", (request, response) -> {
|
||||
log.debug("Get logmsgs.");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
String logMsgs;
|
||||
int count = -1;
|
||||
if(cyclicBufferAppender == null)
|
||||
@@ -86,24 +109,192 @@ public class SystemControl {
|
||||
}
|
||||
}
|
||||
logMsgs = logMsgs + "]";
|
||||
response.status(200);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return logMsgs;
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/logmgmt/loggers gets the logger info for the bridge
|
||||
get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", "application/json", (request, response) -> {
|
||||
get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", (request, response) -> {
|
||||
log.debug("Get loggers info with showAll argument: " + request.params(":all"));
|
||||
Boolean showAll = false;
|
||||
if(request.params(":all").equals("true"))
|
||||
showAll = true;
|
||||
theLogServiceMgr.setShowAll(showAll);
|
||||
theLogServiceMgr.init();
|
||||
response.status(200);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return theLogServiceMgr.getConfiguredLoggers();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/setpassword CORS request
|
||||
options(SYSTEM_CONTEXT + "/setpassword", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/setpassword which sets a password for a given user
|
||||
post(SYSTEM_CONTEXT + "/setpassword", (request, response) -> {
|
||||
log.debug("setpassword....");
|
||||
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
|
||||
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
|
||||
String errorMessage = bridgeSettings.getBridgeSecurity().setPassword(theUser);
|
||||
if(errorMessage != null) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
|
||||
} else {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
}
|
||||
|
||||
if(errorMessage == null)
|
||||
errorMessage = "{}";
|
||||
response.type("application/json");
|
||||
return errorMessage;
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/adduser CORS request
|
||||
options(SYSTEM_CONTEXT + "/adduser", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/adduser which adds a new user
|
||||
put(SYSTEM_CONTEXT + "/adduser", (request, response) -> {
|
||||
log.debug("adduser....");
|
||||
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
|
||||
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
|
||||
String errorMessage = theUser.validate();
|
||||
if(errorMessage != null) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
|
||||
} else {
|
||||
errorMessage = bridgeSettings.getBridgeSecurity().addUser(theUser);
|
||||
if(errorMessage == null) {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
} else {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
|
||||
}
|
||||
}
|
||||
|
||||
if(errorMessage == null)
|
||||
errorMessage = "{}";
|
||||
response.type("application/json");
|
||||
return errorMessage;
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/deluser CORS request
|
||||
options(SYSTEM_CONTEXT + "/deluser", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/deluser which dels a user
|
||||
put(SYSTEM_CONTEXT + "/deluser", (request, response) -> {
|
||||
log.debug("deluser....");
|
||||
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
|
||||
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
|
||||
String errorMessage = bridgeSettings.getBridgeSecurity().delUser(theUser);
|
||||
if(errorMessage != null) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
|
||||
} else {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
}
|
||||
|
||||
if(errorMessage == null)
|
||||
errorMessage = "{}";
|
||||
response.type("application/json");
|
||||
return errorMessage;
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/login CORS request
|
||||
options(SYSTEM_CONTEXT + "/login", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/login validates the login
|
||||
post(SYSTEM_CONTEXT + "/login", (request, response) -> {
|
||||
log.debug("login....");
|
||||
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
|
||||
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
|
||||
LoginResult result = bridgeSettings.getBridgeSecurity().validatePassword(theUser);
|
||||
if(result.getUser() != null)
|
||||
bridgeSettings.getBridgeSecurity().addAuthenticatedUser(request, theUser);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return result;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/presslinkbutton CORS request
|
||||
options(SYSTEM_CONTEXT + "/presslinkbutton", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/presslinkbutton which sets the link button for device registration
|
||||
put(SYSTEM_CONTEXT + "/presslinkbutton", (request, response) -> {
|
||||
log.info("Link button pressed....");
|
||||
bridgeSettings.getBridgeControl().setLinkButton(true);
|
||||
Timer theTimer = new Timer();
|
||||
theTimer.schedule(new LinkButtonPressed(bridgeSettings.getBridgeControl(), theTimer), 30000);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return "";
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/securityinfo gets the security info for the bridge
|
||||
get (SYSTEM_CONTEXT + "/securityinfo", (request, response) -> {
|
||||
log.debug("Get security info");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSecurity().getSecurityInfo();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/changesecurityinfo CORS request
|
||||
options(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/changesecurityinfo which sets the security settings other than passwords and users
|
||||
post(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
|
||||
log.debug("changesecurityinfo....");
|
||||
SecurityInfo theInfo = new Gson().fromJson(request.body(), SecurityInfo.class);
|
||||
if(theInfo.getExecGarden() != null)
|
||||
bridgeSettings.getBridgeSecurity().setExecGarden(theInfo.getExecGarden());
|
||||
bridgeSettings.getBridgeSecurity().setUseLinkButton(theInfo.isUseLinkButton());
|
||||
bridgeSettings.getBridgeSecurity().setSecureHueApi(theInfo.isSecureHueApi());
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSecurity().getSecurityInfo();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/logmgmt/update CORS request
|
||||
options(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/logmgmt/update", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
@@ -112,28 +303,28 @@ public class SystemControl {
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/logmgmt/update which changes logging parameters for the process
|
||||
put(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
|
||||
put(SYSTEM_CONTEXT + "/logmgmt/update", (request, response) -> {
|
||||
log.debug("update loggers: " + request.body());
|
||||
response.status(200);
|
||||
LoggerInfo updateLoggers[];
|
||||
updateLoggers = new Gson().fromJson(request.body(), LoggerInfo[].class);
|
||||
LoggingForm theModel = theLogServiceMgr.getModel();
|
||||
theModel.setUpdatedLoggers(Arrays.asList(updateLoggers));
|
||||
theLogServiceMgr.updateLogLevels();
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return theLogServiceMgr.getConfiguredLoggers();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/settings which returns the bridge configuration settings
|
||||
get(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
get(SYSTEM_CONTEXT + "/settings", (request, response) -> {
|
||||
log.debug("bridge settings requested from " + request.ip());
|
||||
|
||||
response.status(200);
|
||||
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/settings CORS request
|
||||
options(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/settings", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
@@ -142,17 +333,17 @@ public class SystemControl {
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/settings which returns the bridge configuration settings
|
||||
put(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
put(SYSTEM_CONTEXT + "/settings", (request, response) -> {
|
||||
log.debug("save bridge settings requested from " + request.ip() + " with body: " + request.body());
|
||||
BridgeSettingsDescriptor newBridgeSettings = new Gson().fromJson(request.body(), BridgeSettingsDescriptor.class);
|
||||
bridgeSettings.save(newBridgeSettings);
|
||||
response.status(200);
|
||||
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/control/reinit CORS request
|
||||
options(SYSTEM_CONTEXT + "/control/reinit", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/control/reinit", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
@@ -161,12 +352,14 @@ public class SystemControl {
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/control/reinit sets the parameter reinit the server
|
||||
put(SYSTEM_CONTEXT + "/control/reinit", "application/json", (request, response) -> {
|
||||
put(SYSTEM_CONTEXT + "/control/reinit", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return reinit();
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/control/stop CORS request
|
||||
options(SYSTEM_CONTEXT + "/control/stop", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/control/stop", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
@@ -175,19 +368,22 @@ public class SystemControl {
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/control/stop sets the parameter stop the server
|
||||
put(SYSTEM_CONTEXT + "/control/stop", "application/json", (request, response) -> {
|
||||
put(SYSTEM_CONTEXT + "/control/stop", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return stop();
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/backup/available returns a list of config backup filenames
|
||||
get (SYSTEM_CONTEXT + "/backup/available", "application/json", (request, response) -> {
|
||||
get (SYSTEM_CONTEXT + "/backup/available", (request, response) -> {
|
||||
log.debug("Get backup filenames");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBackups();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/create CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/backup/create", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "PUT");
|
||||
@@ -195,16 +391,18 @@ public class SystemControl {
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
put (SYSTEM_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
put (SYSTEM_CONTEXT + "/backup/create", (request, response) -> {
|
||||
log.debug("Create backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
BackupFilename returnFilename = new BackupFilename();
|
||||
returnFilename.setFilename(bridgeSettings.backup(aFilename.getFilename()));
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return returnFilename;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/delete CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/backup/delete", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
@@ -212,18 +410,20 @@ public class SystemControl {
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (SYSTEM_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
post (SYSTEM_CONTEXT + "/backup/delete", (request, response) -> {
|
||||
log.debug("Delete backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null)
|
||||
bridgeSettings.deleteBackup(aFilename.getFilename());
|
||||
else
|
||||
log.warn("No filename given for delete backup.");
|
||||
return null;
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return "";
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/restore CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
options(SYSTEM_CONTEXT + "/backup/restore", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
@@ -231,7 +431,7 @@ public class SystemControl {
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (SYSTEM_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
post (SYSTEM_CONTEXT + "/backup/restore", (request, response) -> {
|
||||
log.debug("Restore backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null) {
|
||||
@@ -240,6 +440,8 @@ public class SystemControl {
|
||||
}
|
||||
else
|
||||
log.warn("No filename given for restore backup.");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.type("application/json");
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
}
|
||||
|
||||
65
src/main/java/com/bwssystems/HABridge/User.java
Normal file
65
src/main/java/com/bwssystems/HABridge/User.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import spark.utils.StringUtils;
|
||||
|
||||
public class User {
|
||||
private int id;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String password2;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPassword2() {
|
||||
return password2;
|
||||
}
|
||||
|
||||
public void setPassword2(String password2) {
|
||||
this.password2 = password2;
|
||||
}
|
||||
|
||||
public String validate() {
|
||||
String error = null;
|
||||
|
||||
if(StringUtils.isEmpty(username)) {
|
||||
error = "You have to enter a username";
|
||||
} else if(StringUtils.isEmpty(password)) {
|
||||
error = "You have to enter a password";
|
||||
} else if(!password.equals(password2)) {
|
||||
error = "The two passwords do not match";
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
public boolean validatePassword() {
|
||||
if(password != null && password2 != null)
|
||||
return password.equals(password2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,18 @@ public class DeviceDescriptor{
|
||||
@SerializedName("noState")
|
||||
@Expose
|
||||
private boolean noState;
|
||||
@SerializedName("offState")
|
||||
@Expose
|
||||
private boolean offState;
|
||||
@SerializedName("requesterAddress")
|
||||
@Expose
|
||||
private String requesterAddress;
|
||||
@SerializedName("description")
|
||||
@Expose
|
||||
private String description;
|
||||
@SerializedName("comments")
|
||||
@Expose
|
||||
private String comments;
|
||||
|
||||
private DeviceState deviceState;
|
||||
|
||||
@@ -219,20 +231,55 @@ public class DeviceDescriptor{
|
||||
this.noState = noState;
|
||||
}
|
||||
|
||||
public boolean isOffState() {
|
||||
return offState;
|
||||
}
|
||||
|
||||
public void setOffState(boolean offState) {
|
||||
this.offState = offState;
|
||||
}
|
||||
|
||||
public String getRequesterAddress() {
|
||||
return requesterAddress;
|
||||
}
|
||||
|
||||
public void setRequesterAddress(String requesterAddress) {
|
||||
this.requesterAddress = requesterAddress;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public void setComments(String comments) {
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
public boolean containsType(String aType) {
|
||||
if(this.mapType.contains(aType))
|
||||
if(aType == null)
|
||||
return false;
|
||||
|
||||
if(this.mapType != null && this.mapType.contains(aType))
|
||||
return true;
|
||||
|
||||
if(this.deviceType.contains(aType))
|
||||
if(this.deviceType != null && this.deviceType.contains(aType))
|
||||
return true;
|
||||
|
||||
if(this.onUrl.contains(aType))
|
||||
if(this.onUrl != null && this.onUrl.contains(aType))
|
||||
return true;
|
||||
|
||||
if(this.dimUrl.contains(aType))
|
||||
if(this.dimUrl != null && this.dimUrl.contains(aType))
|
||||
return true;
|
||||
|
||||
if(this.offUrl.contains(aType))
|
||||
if(this.offUrl != null && this.offUrl.contains(aType))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
@@ -83,6 +83,33 @@ public class DeviceRepository extends BackupHandler {
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<DeviceDescriptor> findAllByRequester(String anAddress) {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
List<DeviceDescriptor> theReturnList = new ArrayList<DeviceDescriptor>();
|
||||
Iterator<DeviceDescriptor> anIterator = list.iterator();
|
||||
DeviceDescriptor theDevice;
|
||||
String theRequesterAddress;
|
||||
|
||||
HashMap<String,String > addressMap;
|
||||
while (anIterator.hasNext()) {
|
||||
theDevice = anIterator.next();
|
||||
theRequesterAddress = theDevice.getRequesterAddress();
|
||||
addressMap = new HashMap<String, String>();
|
||||
if(theRequesterAddress != null) {
|
||||
if (theRequesterAddress.contains(",")) {
|
||||
String[] theArray = theRequesterAddress.split(",");
|
||||
for (String v : theArray) {
|
||||
addressMap.put(v.trim(), v.trim());
|
||||
}
|
||||
} else
|
||||
addressMap.put(theRequesterAddress, theRequesterAddress);
|
||||
}
|
||||
if (theRequesterAddress == null || theRequesterAddress.length() == 0 || addressMap.containsKey(anAddress))
|
||||
theReturnList.add(theDevice);
|
||||
}
|
||||
return theReturnList;
|
||||
}
|
||||
|
||||
public DeviceDescriptor findOne(String id) {
|
||||
return devices.get(id);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.bwssystems.HABridge.devicemanagmeent;
|
||||
|
||||
import static spark.Spark.get;
|
||||
import static spark.Spark.halt;
|
||||
import static spark.Spark.options;
|
||||
import static spark.Spark.post;
|
||||
import static spark.Spark.put;
|
||||
import static spark.Spark.before;
|
||||
import static spark.Spark.delete;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -15,15 +17,19 @@ import org.apache.http.HttpStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.HomeManager;
|
||||
import com.bwssystems.HABridge.User;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.BackupFilename;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.dao.DeviceRepository;
|
||||
import com.bwssystems.HABridge.dao.ErrorMessage;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
spark core server for bridge configuration
|
||||
@@ -33,11 +39,15 @@ public class DeviceResource {
|
||||
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
|
||||
private DeviceRepository deviceRepository;
|
||||
private HomeManager homeManager;
|
||||
private BridgeSettings bridgeSettings;
|
||||
private Gson aGsonHandler;
|
||||
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
|
||||
|
||||
public DeviceResource(BridgeSettingsDescriptor theSettings, HomeManager aHomeManager) {
|
||||
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
|
||||
public DeviceResource(BridgeSettings theSettings, HomeManager aHomeManager) {
|
||||
bridgeSettings = theSettings;
|
||||
this.deviceRepository = new DeviceRepository(bridgeSettings.getBridgeSettingsDescriptor().getUpnpDeviceDb());
|
||||
homeManager = aHomeManager;
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
setupEndpoints();
|
||||
}
|
||||
|
||||
@@ -47,6 +57,15 @@ public class DeviceResource {
|
||||
|
||||
private void setupEndpoints() {
|
||||
log.info("HABridge device management service started.... ");
|
||||
before(API_CONTEXT + "/*", (request, response) -> {
|
||||
// This never gets called as the HueMulator class covers this path. This is here for backup
|
||||
if(bridgeSettings.getBridgeSecurity().isSecure()) {
|
||||
User authUser = bridgeSettings.getBridgeSecurity().getAuthenticatedUser(request);
|
||||
if(authUser == null) {
|
||||
halt(401, "{\"message\":\"User not authenticated\"}");
|
||||
}
|
||||
}
|
||||
});
|
||||
// http://ip_address:port/api/devices CORS request
|
||||
options(API_CONTEXT, "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
@@ -65,14 +84,44 @@ public class DeviceResource {
|
||||
else {
|
||||
devices = new Gson().fromJson("[" + request.body() + "]", DeviceDescriptor[].class);
|
||||
}
|
||||
CallItem[] callItems = null;
|
||||
String errorMessage = null;
|
||||
for(int i = 0; i < devices.length; i++) {
|
||||
if(devices[i].getContentBody() != null ) {
|
||||
if (devices[i].getContentType() == null || devices[i].getHttpVerb() == null || !supportedVerbs.contains(devices[i].getHttpVerb().toLowerCase())) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
log.debug("Bad http verb in create a Device(s): " + request.body());
|
||||
return new ErrorMessage("Bad http verb in create a Device(s): " + request.body() + " ");
|
||||
errorMessage = "Bad http verb in create device(s) for name: " + devices[i].getName() + " with verb: " + devices[i].getHttpVerb();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
try {
|
||||
if(devices[i].getOnUrl() != null && !devices[i].getOnUrl().isEmpty())
|
||||
callItems = aGsonHandler.fromJson(devices[i].getOnUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "Bad on URL JSON in create device(s) for name: " + devices[i].getName() + " with on URL: " + devices[i].getOnUrl();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
try {
|
||||
if(devices[i].getDimUrl() != null && !devices[i].getDimUrl().isEmpty())
|
||||
callItems = aGsonHandler.fromJson(devices[i].getDimUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "Bad dim URL JSON in create device(s) for name: " + devices[i].getName() + " with dim URL: " + devices[i].getDimUrl();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
try {
|
||||
if(devices[i].getOffUrl() != null && !devices[i].getOffUrl().isEmpty())
|
||||
callItems = aGsonHandler.fromJson(devices[i].getOffUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "Bad off URL JSON in create device(s) for name: " + devices[i].getName() + " with off URL: " + devices[i].getOffUrl();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
deviceRepository.save(devices);
|
||||
@@ -219,7 +268,19 @@ public class DeviceResource {
|
||||
return homeManager.findResource(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/map/types", "application/json", (request, response) -> {
|
||||
get (API_CONTEXT + "/lifx/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get LIFX devices");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/somfy/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get somfy devices");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/map/types", "application/json", (request, response) -> {
|
||||
log.debug("Get map types");
|
||||
return new DeviceMapTypes().getDeviceMapTypes();
|
||||
}, new JsonTransformer());
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.lang3.Conversion;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.java.dev.eval.Expression;
|
||||
|
||||
public class BrightnessDecode {
|
||||
private static final Logger log = LoggerFactory.getLogger(BrightnessDecode.class);
|
||||
private static final String INTENSITY_PERCENT = "${intensity.percent}";
|
||||
private static final String INTENSITY_DECIMAL_PERCENT = "${intensity.decimal_percent}";
|
||||
private static final String INTENSITY_BYTE = "${intensity.byte}";
|
||||
private static final String INTENSITY_MATH = "${intensity.math(";
|
||||
private static final String INTENSITY_MATH_VALUE = "X";
|
||||
private static final String INTENSITY_MATH_CLOSE = ")}";
|
||||
private static final String INTENSITY_MATH_CLOSE_HEX = ").hex}";
|
||||
private static final String INTENSITY_PERCENT_HEX = "${intensity.percent.hex}";
|
||||
private static final String INTENSITY_BYTE_HEX = "${intensity.byte.hex}";
|
||||
|
||||
public static int calculateIntensity(int setIntensity, Integer targetBri, Integer targetBriInc) {
|
||||
if (targetBri != null) {
|
||||
@@ -47,51 +48,79 @@ public class BrightnessDecode {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
if (request.contains(INTENSITY_BYTE)) {
|
||||
if (isHex) {
|
||||
BigInteger bigInt = BigInteger.valueOf(intensity);
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||
request = request.replace(INTENSITY_BYTE, hexValue);
|
||||
} else {
|
||||
String intensityByte = String.valueOf(intensity);
|
||||
request = request.replace(INTENSITY_BYTE, intensityByte);
|
||||
}
|
||||
} else if (request.contains(INTENSITY_PERCENT)) {
|
||||
int percentBrightness = (int) Math.round(intensity / 255.0 * 100);
|
||||
if (isHex) {
|
||||
BigInteger bigInt = BigInteger.valueOf(percentBrightness);
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||
request = request.replace(INTENSITY_PERCENT, hexValue);
|
||||
} else {
|
||||
String intensityPercent = String.valueOf(percentBrightness);
|
||||
request = request.replace(INTENSITY_PERCENT, intensityPercent);
|
||||
}
|
||||
} else if (request.contains(INTENSITY_MATH)) {
|
||||
Map<String, BigDecimal> variables = new HashMap<String, BigDecimal>();
|
||||
String mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(),
|
||||
request.indexOf(INTENSITY_MATH_CLOSE));
|
||||
variables.put(INTENSITY_MATH_VALUE, new BigDecimal(intensity));
|
||||
|
||||
try {
|
||||
boolean notDone = true;
|
||||
String replaceValue = null;
|
||||
String replaceTarget = null;
|
||||
int percentBrightness = (int) Math.round(intensity / 255.0 * 100);
|
||||
float decimalBrightness = (float) (intensity / 255.0);
|
||||
Map<String, BigDecimal> variables = new HashMap<String, BigDecimal>();
|
||||
String mathDescriptor = null;
|
||||
|
||||
while(notDone) {
|
||||
notDone = false;
|
||||
if (request.contains(INTENSITY_BYTE)) {
|
||||
if (isHex) {
|
||||
replaceValue = convertToHex(intensity);
|
||||
} else {
|
||||
replaceValue = String.valueOf(intensity);
|
||||
}
|
||||
replaceTarget = INTENSITY_BYTE;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_BYTE_HEX)) {
|
||||
replaceValue = convertToHex(intensity);
|
||||
replaceTarget = INTENSITY_BYTE_HEX;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_PERCENT)) {
|
||||
if (isHex) {
|
||||
replaceValue = convertToHex(percentBrightness);
|
||||
} else {
|
||||
replaceValue = String.valueOf(percentBrightness);
|
||||
}
|
||||
replaceTarget = INTENSITY_PERCENT;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_PERCENT_HEX)) {
|
||||
replaceValue = convertToHex(percentBrightness);
|
||||
replaceTarget = INTENSITY_PERCENT_HEX;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_DECIMAL_PERCENT)) {
|
||||
replaceValue = String.format("%1.2f", decimalBrightness);
|
||||
replaceTarget = INTENSITY_DECIMAL_PERCENT;
|
||||
notDone = true;
|
||||
} else if (request.contains(INTENSITY_MATH_CLOSE)) {
|
||||
mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(),
|
||||
request.indexOf(INTENSITY_MATH_CLOSE));
|
||||
variables.put(INTENSITY_MATH_VALUE, new BigDecimal(intensity));
|
||||
|
||||
log.debug("Math eval is: " + mathDescriptor + ", Where " + INTENSITY_MATH_VALUE + " is: "
|
||||
+ String.valueOf(intensity));
|
||||
Expression exp = new Expression(mathDescriptor);
|
||||
BigDecimal result = exp.eval(variables);
|
||||
Integer endResult = Math.round(result.floatValue());
|
||||
if (isHex) {
|
||||
BigInteger bigInt = BigInteger.valueOf(endResult);
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||
request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE, hexValue);
|
||||
} else {
|
||||
request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE,
|
||||
endResult.toString());
|
||||
Integer endResult = calculateMath(variables, mathDescriptor);
|
||||
if(endResult != null) {
|
||||
if (isHex) {
|
||||
replaceValue = convertToHex(endResult);
|
||||
} else {
|
||||
replaceValue = endResult.toString();
|
||||
}
|
||||
replaceTarget = INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE;
|
||||
notDone = true;
|
||||
}
|
||||
} else if (request.contains(INTENSITY_MATH_CLOSE_HEX)) {
|
||||
mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(),
|
||||
request.indexOf(INTENSITY_MATH_CLOSE_HEX));
|
||||
variables.put(INTENSITY_MATH_VALUE, new BigDecimal(intensity));
|
||||
|
||||
Integer endResult = calculateMath(variables, mathDescriptor);
|
||||
if(endResult != null) {
|
||||
if (isHex) {
|
||||
replaceValue = convertToHex(endResult);
|
||||
} else {
|
||||
replaceValue = endResult.toString();
|
||||
}
|
||||
replaceTarget = INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE_HEX;
|
||||
notDone = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not execute Math: " + mathDescriptor, e);
|
||||
}
|
||||
if(notDone)
|
||||
request = request.replace(replaceTarget, replaceValue);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
@@ -100,4 +129,28 @@ public class BrightnessDecode {
|
||||
public static String calculateReplaceIntensityValue(String request, int theIntensity, Integer targetBri, Integer targetBriInc, boolean isHex) {
|
||||
return replaceIntensityValue(request, calculateIntensity(theIntensity, targetBri, targetBriInc), isHex);
|
||||
}
|
||||
}
|
||||
|
||||
// Apache Commons Conversion utils likes little endian too much
|
||||
private static String convertToHex(int theValue) {
|
||||
String destHex = "00";
|
||||
String hexValue = Conversion.intToHex(theValue, 0, destHex, 0, 2);
|
||||
byte[] theBytes = hexValue.getBytes();
|
||||
byte[] newBytes = new byte[2];
|
||||
newBytes[0] = theBytes[1];
|
||||
newBytes[1] = theBytes[0];
|
||||
return new String(newBytes);
|
||||
}
|
||||
|
||||
private static Integer calculateMath(Map<String, BigDecimal> variables, String mathDescriptor) {
|
||||
Integer endResult = null;
|
||||
try {
|
||||
Expression exp = new Expression(mathDescriptor);
|
||||
BigDecimal result = exp.eval(variables);
|
||||
endResult = Math.round(result.floatValue());
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not execute Math: " + mathDescriptor, e);
|
||||
endResult = null;
|
||||
}
|
||||
return endResult;
|
||||
}
|
||||
}
|
||||
21
src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java
Normal file
21
src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ColorDecode {
|
||||
|
||||
public static String convertCIEtoRGB(List<Double> xy) {
|
||||
double x;
|
||||
double y;
|
||||
double Y;
|
||||
|
||||
x = xy.get(0) * 100;
|
||||
y = xy.get(1) * 100;
|
||||
Y= y;
|
||||
double R = 3.240479*((x*Y)/y) + -1.537150*Y + -0.498535*(((1-x-y)*Y)/y);
|
||||
double G = -0.969256*((x*Y)/y) + 1.875992*Y + 0.041556*(((1-x-y)*Y)/y);
|
||||
double B = 0.055648*((x*Y)/y) + -0.204043*Y + 1.057311*(((1-x-y)*Y)/y);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
|
||||
public class DeviceDataDecode {
|
||||
private static final Logger log = LoggerFactory.getLogger(DeviceDataDecode.class);
|
||||
private static final String DEVICE_ID = "${device.id}";
|
||||
private static final String DEVICE_UNIQUEID = "${device.uniqueid}";
|
||||
private static final String DEVICE_NAME = "${device.name}";
|
||||
private static final String DEVICE_MAPID = "${device.mapId}";
|
||||
private static final String DEVICE_MAPTYPE = "${device.mapType}";
|
||||
private static final String DEVICE_DEVICETYPE = "${device.deviceType}";
|
||||
private static final String DEVICE_TARGETDEVICE = "${device.targetDevice}";
|
||||
|
||||
public static String replaceDeviceData(String request, DeviceDescriptor device) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
boolean notDone = true;
|
||||
|
||||
while(notDone) {
|
||||
notDone = false;
|
||||
if (request.contains(DEVICE_ID)) {
|
||||
request = request.replace(DEVICE_ID, device.getId());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_UNIQUEID)) {
|
||||
request = request.replace(DEVICE_UNIQUEID, device.getUniqueid());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_NAME)) {
|
||||
request = request.replace(DEVICE_NAME, device.getName());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_MAPID)) {
|
||||
request = request.replace(DEVICE_MAPID, device.getMapId());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_MAPTYPE)) {
|
||||
request = request.replace(DEVICE_MAPTYPE, device.getMapType());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_DEVICETYPE)) {
|
||||
request = request.replace(DEVICE_DEVICETYPE, device.getDeviceType());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
if (request.contains(DEVICE_TARGETDEVICE)) {
|
||||
request = request.replace(DEVICE_TARGETDEVICE, device.getTargetDevice());
|
||||
notDone = true;
|
||||
}
|
||||
|
||||
log.debug("Request <<" + request + ">>, not done: " + notDone);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.HomeManager;
|
||||
import com.bwssystems.HABridge.User;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.api.UserCreateRequest;
|
||||
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
||||
@@ -14,16 +16,16 @@ import com.bwssystems.HABridge.api.hue.HueError;
|
||||
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
|
||||
import com.bwssystems.HABridge.api.hue.HuePublicConfig;
|
||||
import com.bwssystems.HABridge.api.hue.StateChangeBody;
|
||||
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
|
||||
import com.bwssystems.HABridge.dao.*;
|
||||
import com.bwssystems.HABridge.plugins.hue.HueDeviceIdentifier;
|
||||
import com.bwssystems.HABridge.plugins.hue.HueHome;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import static spark.Spark.before;
|
||||
import static spark.Spark.get;
|
||||
import static spark.Spark.halt;
|
||||
import static spark.Spark.options;
|
||||
import static spark.Spark.post;
|
||||
import static spark.Spark.put;
|
||||
@@ -34,12 +36,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Based on Armzilla's HueMulator - a Philips Hue emulator using sparkjava rest server
|
||||
@@ -53,13 +51,15 @@ public class HueMulator {
|
||||
private HomeManager homeManager;
|
||||
private HueHome myHueHome;
|
||||
private BridgeSettingsDescriptor bridgeSettings;
|
||||
private BridgeSettings bridgeSettingMaster;
|
||||
private Gson aGsonHandler;
|
||||
private DeviceMapTypes validMapTypes;
|
||||
|
||||
public HueMulator(BridgeSettingsDescriptor theBridgeSettings, DeviceRepository aDeviceRepository, HomeManager aHomeManager) {
|
||||
public HueMulator(BridgeSettings bridgeMaster, DeviceRepository aDeviceRepository, HomeManager aHomeManager) {
|
||||
repository = aDeviceRepository;
|
||||
validMapTypes = new DeviceMapTypes();
|
||||
bridgeSettings = theBridgeSettings;
|
||||
bridgeSettingMaster = bridgeMaster;
|
||||
bridgeSettings = bridgeSettingMaster.getBridgeSettingsDescriptor();
|
||||
homeManager= aHomeManager;
|
||||
myHueHome = (HueHome) homeManager.findHome(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
@@ -68,6 +68,22 @@ public class HueMulator {
|
||||
// This function sets up the sparkjava rest calls for the hue api
|
||||
public void setupServer() {
|
||||
log.info("Hue emulator service started....");
|
||||
before(HUE_CONTEXT + "/*", (request, response) -> {
|
||||
if(bridgeSettingMaster.getBridgeSecurity().isSecure()) {
|
||||
String pathInfo = request.pathInfo();
|
||||
if(pathInfo != null && pathInfo.contains(HUE_CONTEXT + "/devices")) {
|
||||
User authUser = bridgeSettingMaster.getBridgeSecurity().getAuthenticatedUser(request);
|
||||
if(authUser == null) {
|
||||
halt(401, "{\"message\":\"User not authenticated\"}");
|
||||
}
|
||||
} else if (bridgeSettingMaster.getBridgeSecurity().isSecureHueApi()) {
|
||||
User authUser = bridgeSettingMaster.getBridgeSecurity().getAuthenticatedUser(request);
|
||||
if(authUser == null) {
|
||||
halt(401, "{\"message\":\"User not authenticated\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// http://ip_address:port/api/{userId}/groups returns json objects of
|
||||
// all groups configured
|
||||
get(HUE_CONTEXT + "/:userid/groups", "application/json", (request, response) -> {
|
||||
@@ -394,7 +410,7 @@ public class HueMulator {
|
||||
}
|
||||
|
||||
private String formatSuccessHueResponse(StateChangeBody stateChanges, String body, String lightId,
|
||||
DeviceState deviceState, Integer targetBri, Integer targetBriInc) {
|
||||
DeviceState deviceState, Integer targetBri, Integer targetBriInc, boolean offState) {
|
||||
|
||||
String responseString = "[";
|
||||
boolean notFirstChange = false;
|
||||
@@ -409,6 +425,8 @@ public class HueMulator {
|
||||
deviceState.setOn(stateChanges.isOn());
|
||||
if(!deviceState.isOn() && deviceState.getBri() == 254)
|
||||
deviceState.setBri(0);
|
||||
if(!deviceState.isOn() && offState)
|
||||
deviceState.setBri(0);
|
||||
}
|
||||
notFirstChange = true;
|
||||
}
|
||||
@@ -557,50 +575,6 @@ public class HueMulator {
|
||||
return responseString;
|
||||
}
|
||||
|
||||
private String getNewUserID() {
|
||||
UUID uid = UUID.randomUUID();
|
||||
StringTokenizer st = new StringTokenizer(uid.toString(), "-");
|
||||
String newUser = "";
|
||||
while (st.hasMoreTokens()) {
|
||||
newUser = newUser + st.nextToken();
|
||||
}
|
||||
|
||||
return newUser;
|
||||
}
|
||||
private HueError[] validateWhitelistUser(String aUser, boolean strict) {
|
||||
String validUser = null;
|
||||
boolean found = false;
|
||||
if (aUser != null && !aUser.equalsIgnoreCase("undefined") && !aUser.equalsIgnoreCase("null")
|
||||
&& !aUser.equalsIgnoreCase("")) {
|
||||
if (bridgeSettings.getWhitelist() != null) {
|
||||
Set<String> theUserIds = bridgeSettings.getWhitelist().keySet();
|
||||
Iterator<String> userIterator = theUserIds.iterator();
|
||||
while (userIterator.hasNext()) {
|
||||
validUser = userIterator.next();
|
||||
if (validUser.equals(aUser))
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && !strict) {
|
||||
if (bridgeSettings.getWhitelist() == null) {
|
||||
Map<String, WhitelistEntry> awhitelist = new HashMap<>();
|
||||
bridgeSettings.setWhitelist(awhitelist);
|
||||
}
|
||||
bridgeSettings.getWhitelist().put(aUser, WhitelistEntry.createEntry("auto insert user"));
|
||||
bridgeSettings.setSettingsChanged(true);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
log.debug("Validate user, No User supplied");
|
||||
return HueErrorResponse.createResponse("1", "/api/" + aUser, "unauthorized user", null, null, null).getTheErrors();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Boolean filterByRequester(String requesterFilterList, String anAddress) {
|
||||
if (requesterFilterList == null || requesterFilterList.length() == 0)
|
||||
return true;
|
||||
@@ -621,9 +595,13 @@ public class HueMulator {
|
||||
|
||||
private String basicListHandler(String type, String userId, String requestIp) {
|
||||
log.debug("hue " + type + " list requested: " + userId + " from " + requestIp);
|
||||
HueError[] theErrors = validateWhitelistUser(userId, false);
|
||||
if (theErrors != null)
|
||||
HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
|
||||
if (theErrors != null) {
|
||||
if(bridgeSettings.isSettingsChanged())
|
||||
bridgeSettingMaster.updateConfigFile();
|
||||
|
||||
return aGsonHandler.toJson(theErrors);
|
||||
}
|
||||
|
||||
return "{}";
|
||||
}
|
||||
@@ -631,8 +609,11 @@ public class HueMulator {
|
||||
log.debug("hue group list requested: " + userId + " from " + requestIp);
|
||||
HueError[] theErrors = null;
|
||||
Map<String, GroupResponse> groupResponseMap = null;
|
||||
theErrors = validateWhitelistUser(userId, false);
|
||||
theErrors = bridgeSettings.validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
|
||||
if (theErrors == null) {
|
||||
if(bridgeSettings.isSettingsChanged())
|
||||
bridgeSettingMaster.updateConfigFile();
|
||||
|
||||
groupResponseMap = new HashMap<String, GroupResponse>();
|
||||
groupResponseMap.put("1", (GroupResponse) this.groupsIdHandler("1", userId, requestIp));
|
||||
return groupResponseMap;
|
||||
@@ -645,8 +626,11 @@ public class HueMulator {
|
||||
private Object groupsIdHandler(String groupId, String userId, String requestIp) {
|
||||
log.debug("hue group id: <" + groupId + "> requested: " + userId + " from " + requestIp);
|
||||
HueError[] theErrors = null;
|
||||
theErrors = validateWhitelistUser(userId, false);
|
||||
theErrors = bridgeSettings.validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
|
||||
if (theErrors == null) {
|
||||
if(bridgeSettings.isSettingsChanged())
|
||||
bridgeSettingMaster.updateConfigFile();
|
||||
|
||||
if (groupId.equalsIgnoreCase("0")) {
|
||||
GroupResponse theResponse = GroupResponse.createDefaultGroupResponse(repository.findActive());
|
||||
return theResponse;
|
||||
@@ -667,34 +651,40 @@ public class HueMulator {
|
||||
if (bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue lights list requested: " + userId + " from " + requestIp);
|
||||
log.debug("hue lights list requested: " + userId + " from " + requestIp);
|
||||
theErrors = validateWhitelistUser(userId, false);
|
||||
theErrors = bridgeSettings.validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
|
||||
if (theErrors == null) {
|
||||
List<DeviceDescriptor> deviceList = repository.findActive();
|
||||
if(bridgeSettings.isSettingsChanged())
|
||||
bridgeSettingMaster.updateConfigFile();
|
||||
|
||||
List<DeviceDescriptor> deviceList = repository.findAllByRequester(requestIp);
|
||||
// List<DeviceDescriptor> deviceList = repository.findActive();
|
||||
deviceResponseMap = new HashMap<String, DeviceResponse>();
|
||||
for (DeviceDescriptor device : deviceList) {
|
||||
DeviceResponse deviceResponse = null;
|
||||
if (device.containsType(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
|
||||
CallItem[] callItems = null;
|
||||
try {
|
||||
if(device.getOnUrl() != null)
|
||||
callItems = aGsonHandler.fromJson(device.getOnUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
log.warn("Could not decode Json for url items to get Hue state for device: " + device.getName());
|
||||
callItems = null;
|
||||
}
|
||||
|
||||
for (int i = 0; callItems != null && i < callItems.length; i++) {
|
||||
if((callItems[i].getType() != null && callItems[i].getType().equals(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) ||
|
||||
(callItems[i].getItem().getAsString().contains("hueName"))) {
|
||||
deviceResponse = myHueHome.getHueDeviceInfo(callItems[i], device);
|
||||
i = callItems.length;
|
||||
if(!device.isInactive()) {
|
||||
if (device.containsType(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
|
||||
CallItem[] callItems = null;
|
||||
try {
|
||||
if(device.getOnUrl() != null)
|
||||
callItems = aGsonHandler.fromJson(device.getOnUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
log.warn("Could not decode Json for url items to get Hue state for device: " + device.getName());
|
||||
callItems = null;
|
||||
}
|
||||
|
||||
for (int i = 0; callItems != null && i < callItems.length; i++) {
|
||||
if((callItems[i].getType() != null && callItems[i].getType().equals(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) ||
|
||||
(callItems[i].getItem().getAsString().contains("hueName"))) {
|
||||
deviceResponse = myHueHome.getHueDeviceInfo(callItems[i], device);
|
||||
i = callItems.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceResponse == null)
|
||||
deviceResponse = DeviceResponse.createResponse(device);
|
||||
deviceResponseMap.put(device.getId(), deviceResponse);
|
||||
}
|
||||
|
||||
if (deviceResponse == null)
|
||||
deviceResponse = DeviceResponse.createResponse(device);
|
||||
deviceResponseMap.put(device.getId(), deviceResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,42 +698,58 @@ public class HueMulator {
|
||||
UserCreateRequest aNewUser = null;
|
||||
String newUser = null;
|
||||
String aDeviceType = null;
|
||||
boolean toContinue = false;
|
||||
|
||||
if (bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue api user create requested: " + body + " from " + ipAddress);
|
||||
log.debug("hue api user create requested: " + body + " from " + ipAddress);
|
||||
|
||||
if (body != null && !body.isEmpty()) {
|
||||
try {
|
||||
aNewUser = aGsonHandler.fromJson(body, UserCreateRequest.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not add user. Request garbled: " + body);
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("2", "/",
|
||||
"Could not add user.", null, null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
newUser = aNewUser.getUsername();
|
||||
aDeviceType = aNewUser.getDevicetype();
|
||||
}
|
||||
if (newUser == null)
|
||||
newUser = getNewUserID();
|
||||
|
||||
validateWhitelistUser(newUser, false);
|
||||
if (aDeviceType == null)
|
||||
aDeviceType = "<not given>";
|
||||
if (bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue api user create requested for device type: " + aDeviceType + " and username: "
|
||||
+ newUser + (followingSlash ? " /api/ called" : ""));
|
||||
log.debug("hue api user create requested for device type: " + aDeviceType + " and username: " + newUser + (followingSlash ? " /api/ called" : ""));
|
||||
|
||||
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
|
||||
|
||||
if(bridgeSettingMaster.getBridgeSecurity().isUseLinkButton() && bridgeSettingMaster.getBridgeControl().isLinkButton())
|
||||
toContinue = true;
|
||||
else if(!bridgeSettingMaster.getBridgeSecurity().isUseLinkButton())
|
||||
toContinue = true;
|
||||
|
||||
if(toContinue) {
|
||||
log.debug("hue api user create requested: " + body + " from " + ipAddress);
|
||||
|
||||
if (body != null && !body.isEmpty()) {
|
||||
try {
|
||||
aNewUser = aGsonHandler.fromJson(body, UserCreateRequest.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not add user. Request garbled: " + body);
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("2", "/",
|
||||
"Could not add user.", null, null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
newUser = aNewUser.getUsername();
|
||||
aDeviceType = aNewUser.getDevicetype();
|
||||
}
|
||||
|
||||
if (aDeviceType == null)
|
||||
aDeviceType = "<not given>";
|
||||
|
||||
if (newUser == null) {
|
||||
newUser = bridgeSettings.createWhitelistUser(aDeviceType);
|
||||
}
|
||||
else {
|
||||
bridgeSettings.validateWhitelistUser(newUser, aDeviceType, false);
|
||||
}
|
||||
|
||||
if(bridgeSettings.isSettingsChanged())
|
||||
bridgeSettingMaster.updateConfigFile();
|
||||
|
||||
if (bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue api user create requested for device type: " + aDeviceType + " and username: "
|
||||
+ newUser + (followingSlash ? " /api/ called" : ""));
|
||||
log.debug("hue api user create requested for device type: " + aDeviceType + " and username: " + newUser + (followingSlash ? " /api/ called" : ""));
|
||||
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
|
||||
}
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("1", "/api/", "unauthorized user", null, null, null).getTheErrors());
|
||||
}
|
||||
|
||||
private Object getConfig(String userId, String ipAddress) {
|
||||
if (bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue api/:userid/config config requested: " + userId + " from " + ipAddress);
|
||||
log.debug("hue api config requested: " + userId + " from " + ipAddress);
|
||||
if (validateWhitelistUser(userId, true) != null) {
|
||||
if (bridgeSettings.validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton()) != null) {
|
||||
log.debug("hue api config requested, No User supplied, returning public config");
|
||||
HuePublicConfig apiResponse = HuePublicConfig.createConfig("Philips hue",
|
||||
bridgeSettings.getUpnpConfigAddress(), bridgeSettings.getHubversion());
|
||||
@@ -759,7 +765,7 @@ public class HueMulator {
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object getFullState(String userId, String ipAddress) {
|
||||
log.debug("hue api full state requested: " + userId + " from " + ipAddress);
|
||||
HueError[] theErrors = validateWhitelistUser(userId, false);
|
||||
HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
|
||||
if (theErrors != null)
|
||||
return theErrors;
|
||||
|
||||
@@ -773,7 +779,7 @@ public class HueMulator {
|
||||
|
||||
private Object getLight(String userId, String lightId, String ipAddress) {
|
||||
log.debug("hue light requested: " + lightId + " for user: " + userId + " from " + ipAddress);
|
||||
HueError[] theErrors = validateWhitelistUser(userId, false);
|
||||
HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
|
||||
if (theErrors != null)
|
||||
return theErrors;
|
||||
|
||||
@@ -817,7 +823,7 @@ public class HueMulator {
|
||||
Integer targetBri = null;
|
||||
Integer targetBriInc = null;
|
||||
log.debug("Update state requested: " + userId + " from " + ipAddress + " body: " + body);
|
||||
HueError[] theErrors = validateWhitelistUser(userId, false);
|
||||
HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
|
||||
if (theErrors != null)
|
||||
return aGsonHandler.toJson(theErrors);
|
||||
try {
|
||||
@@ -849,7 +855,7 @@ public class HueMulator {
|
||||
if (state == null)
|
||||
state = DeviceState.createDeviceState();
|
||||
|
||||
responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, state, targetBri, targetBriInc);
|
||||
responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, state, targetBri, targetBriInc, device.isOffState());
|
||||
device.setDeviceState(state);
|
||||
|
||||
return responseString;
|
||||
@@ -867,7 +873,7 @@ public class HueMulator {
|
||||
aMultiUtil.setDelayDefault(bridgeSettings.getButtonsleep());
|
||||
aMultiUtil.setSetCount(1);
|
||||
log.debug("hue state change requested: " + userId + " from " + ipAddress + " body: " + body);
|
||||
HueError[] theErrors = validateWhitelistUser(userId, false);
|
||||
HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
|
||||
if (theErrors != null)
|
||||
return aGsonHandler.toJson(theErrors);
|
||||
try {
|
||||
@@ -945,8 +951,8 @@ public class HueMulator {
|
||||
}
|
||||
|
||||
for (int i = 0; callItems != null && i < callItems.length; i++) {
|
||||
if(!filterByRequester(callItems[i].getFilterIPs(), ipAddress)) {
|
||||
log.debug("filter for requester address not present in list: " + callItems[i].getFilterIPs() + " with request ip of: " + ipAddress);
|
||||
if(!filterByRequester(device.getRequesterAddress(), ipAddress) || !filterByRequester(callItems[i].getFilterIPs(), ipAddress)) {
|
||||
log.warn("filter for requester address not present in: (device)" + device.getRequesterAddress() + " OR then (item)" + callItems[i].getFilterIPs() + " with request ip of: " + ipAddress);
|
||||
continue;
|
||||
}
|
||||
if (callItems[i].getCount() != null && callItems[i].getCount() > 0)
|
||||
@@ -977,6 +983,9 @@ public class HueMulator {
|
||||
else
|
||||
aMultiUtil.setTheDelay(aMultiUtil.getDelayDefault());
|
||||
responseString = homeManager.findHome(callItems[i].getType().trim()).deviceHandler(callItems[i], aMultiUtil, lightId, state.getBri(), targetBri, targetBriInc, device, body);
|
||||
if(responseString != null && responseString.contains("{\"error\":")) {
|
||||
x = aMultiUtil.getSetCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -989,11 +998,11 @@ public class HueMulator {
|
||||
|
||||
if (responseString == null || !responseString.contains("[{\"error\":")) {
|
||||
if(!device.isNoState()) {
|
||||
responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, state, targetBri, targetBriInc);
|
||||
responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, state, targetBri, targetBriInc, device.isOffState());
|
||||
device.setDeviceState(state);
|
||||
} else {
|
||||
DeviceState dummyState = DeviceState.createDeviceState();
|
||||
responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, dummyState, targetBri, targetBriInc);
|
||||
responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, dummyState, targetBri, targetBriInc, device.isOffState());
|
||||
}
|
||||
}
|
||||
return responseString;
|
||||
|
||||
@@ -21,16 +21,22 @@ public class TimeDecode {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
if (request.contains(TIME_FORMAT)) {
|
||||
String timeFormatDescriptor = request.substring(request.indexOf(TIME_FORMAT) + TIME_FORMAT.length(),
|
||||
request.indexOf(TIME_FORMAT_CLOSE));
|
||||
|
||||
try {
|
||||
log.debug("Time eval is: " + timeFormatDescriptor);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat(timeFormatDescriptor);
|
||||
request = request.replace(TIME_FORMAT + timeFormatDescriptor + TIME_FORMAT_CLOSE, dateFormat.format(new Date()));
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not format current time: " + timeFormatDescriptor, e);
|
||||
boolean notDone = true;
|
||||
|
||||
while(notDone) {
|
||||
notDone = false;
|
||||
if (request.contains(TIME_FORMAT)) {
|
||||
String timeFormatDescriptor = request.substring(request.indexOf(TIME_FORMAT) + TIME_FORMAT.length(),
|
||||
request.indexOf(TIME_FORMAT_CLOSE));
|
||||
|
||||
try {
|
||||
log.debug("Time eval is: " + timeFormatDescriptor);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat(timeFormatDescriptor);
|
||||
request = request.replace(TIME_FORMAT + timeFormatDescriptor + TIME_FORMAT_CLOSE, dateFormat.format(new Date()));
|
||||
notDone = true;
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not format current time: " + timeFormatDescriptor, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return request;
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.Set;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
@@ -32,7 +32,7 @@ public class NestHome implements com.bwssystems.HABridge.Home {
|
||||
private Boolean isFarenheit;
|
||||
private Boolean validNest;
|
||||
|
||||
public NestHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public NestHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
@@ -94,10 +94,10 @@ public class NestHome implements com.bwssystems.HABridge.Home {
|
||||
public void closeHome() {
|
||||
if(theSession != null) {
|
||||
theNest.endNestSession();
|
||||
theNest = null;
|
||||
theSession = null;
|
||||
nestItems = null;
|
||||
}
|
||||
theNest = null;
|
||||
theSession = null;
|
||||
nestItems = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,20 +164,20 @@ public class NestHome implements com.bwssystems.HABridge.Home {
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.bwssystems.HABridge.Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public com.bwssystems.HABridge.Home createHome(BridgeSettings bridgeSettings) {
|
||||
theSession = null;
|
||||
theNest = null;
|
||||
nestItems = null;
|
||||
validNest = bridgeSettings.isValidNest();
|
||||
validNest = bridgeSettings.getBridgeSettingsDescriptor().isValidNest();
|
||||
aGsonHandler = null;
|
||||
log.info("Nest Home created." + (validNest ? "" : " No Nest configured."));
|
||||
|
||||
if(validNest) {
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
|
||||
isFarenheit = bridgeSettings.isFarenheit();
|
||||
isFarenheit = bridgeSettings.getBridgeSettingsDescriptor().isFarenheit();
|
||||
try {
|
||||
theSession = new NestSession(bridgeSettings.getNestuser(), bridgeSettings.getNestpwd());
|
||||
theSession = new NestSession(bridgeSettings.getBridgeSettingsDescriptor().getNestuser(), bridgeSettings.getBridgeSettingsDescriptor().getNestpwd());
|
||||
theNest = new Nest(theSession);
|
||||
} catch (LoginException e) {
|
||||
log.error("Caught Login Exception, setting Nest to invalid....");
|
||||
|
||||
@@ -3,11 +3,12 @@ package com.bwssystems.HABridge.plugins.domoticz;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.Base64;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
@@ -16,32 +17,33 @@ public class DomoticzHandler {
|
||||
private static final String GET_REQUEST = "/json.htm?type=";
|
||||
private static final String DEVICES_TYPE = "devices";
|
||||
private static final String SCENES_TYPE = "scenes";
|
||||
private static final String FILTER_USED = "&used=";
|
||||
private HTTPHandler httpClient;
|
||||
private static final String FILTER_USED = "&used=true";
|
||||
private NamedIP domoticzAddress;
|
||||
|
||||
public DomoticzHandler(NamedIP addressName) {
|
||||
super();
|
||||
httpClient = new HTTPHandler();
|
||||
domoticzAddress = addressName;
|
||||
}
|
||||
|
||||
public List<DomoticzDevice> getDevices() {
|
||||
return getDomoticzDevices(GET_REQUEST, DEVICES_TYPE, FILTER_USED);
|
||||
public List<DomoticzDevice> getDevices(HTTPHandler httpClient) {
|
||||
return getDomoticzDevices(GET_REQUEST, DEVICES_TYPE, FILTER_USED, httpClient);
|
||||
}
|
||||
|
||||
public List<DomoticzDevice> getScenes() {
|
||||
return getDomoticzDevices(GET_REQUEST, SCENES_TYPE, null);
|
||||
public List<DomoticzDevice> getScenes(HTTPHandler httpClient) {
|
||||
return getDomoticzDevices(GET_REQUEST, SCENES_TYPE, null, httpClient);
|
||||
}
|
||||
|
||||
private List<DomoticzDevice> getDomoticzDevices(String rootRequest, String type, String postpend) {
|
||||
private List<DomoticzDevice> getDomoticzDevices(String rootRequest, String type, String postpend, HTTPHandler httpClient) {
|
||||
Devices theDomoticzApiResponse = null;
|
||||
List<DomoticzDevice> deviceList = null;
|
||||
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
theUrl = "http://" + domoticzAddress.getIp() + ":" + domoticzAddress.getPort() + rootRequest + type;
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
|
||||
if(postpend != null && !postpend.isEmpty())
|
||||
theUrl = buildUrl(rootRequest + type + postpend);
|
||||
else
|
||||
theUrl = buildUrl(rootRequest + type);
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, buildHeaders());
|
||||
if(theData != null) {
|
||||
log.debug("GET " + type + " DomoticzApiResponse - data: " + theData);
|
||||
theDomoticzApiResponse = new Gson().fromJson(theData, Devices.class);
|
||||
@@ -70,6 +72,44 @@ public class DomoticzHandler {
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
public String buildUrl(String thePayload) {
|
||||
String newUrl = null;
|
||||
|
||||
if(thePayload != null && !thePayload.isEmpty()) {
|
||||
if(domoticzAddress.getSecure() != null && domoticzAddress.getSecure())
|
||||
newUrl = "https://";
|
||||
else
|
||||
newUrl = "http://";
|
||||
|
||||
newUrl = newUrl + domoticzAddress.getIp();
|
||||
|
||||
if(domoticzAddress.getPort() != null && !domoticzAddress.getPort().isEmpty())
|
||||
newUrl = newUrl + ":" + domoticzAddress.getPort();
|
||||
|
||||
if(thePayload.startsWith("/"))
|
||||
newUrl = newUrl + thePayload;
|
||||
else
|
||||
newUrl = newUrl + "/" + thePayload;
|
||||
}
|
||||
|
||||
return newUrl;
|
||||
}
|
||||
|
||||
public NameValue[] buildHeaders() {
|
||||
NameValue[] headers = null;
|
||||
|
||||
if(domoticzAddress.getUsername() != null && !domoticzAddress.getUsername().isEmpty()
|
||||
&& domoticzAddress.getPassword() != null && !domoticzAddress.getPassword().isEmpty()) {
|
||||
NameValue theAuth = new NameValue();
|
||||
theAuth.setName("Authorization");
|
||||
String encoding = Base64.getEncoder().encodeToString((domoticzAddress.getUsername() + ":" + domoticzAddress.getPassword()).getBytes());
|
||||
theAuth.setValue("Basic " + encoding);
|
||||
headers = new NameValue[1];
|
||||
headers[0] = theAuth;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
public NamedIP getDomoticzAddress() {
|
||||
return domoticzAddress;
|
||||
}
|
||||
|
||||
@@ -9,19 +9,25 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.api.hue.HueError;
|
||||
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class DomoticzHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(DomoticzHome.class);
|
||||
private Map<String, DomoticzHandler> domoticzs;
|
||||
private Boolean validDomoticz;
|
||||
private HTTPHandler httpClient;
|
||||
|
||||
public DomoticzHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public DomoticzHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
@@ -36,14 +42,14 @@ public class DomoticzHome implements Home {
|
||||
List<DomoticzDevice> deviceList = new ArrayList<DomoticzDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = domoticzs.get(key).getDevices();
|
||||
theResponse = domoticzs.get(key).getDevices(httpClient);
|
||||
if(theResponse != null)
|
||||
addDomoticzDevices(deviceList, theResponse, key);
|
||||
else {
|
||||
log.warn("Cannot get lights for Domoticz with name: " + key + ", skipping this Domoticz.");
|
||||
continue;
|
||||
}
|
||||
theResponse = domoticzs.get(key).getScenes();
|
||||
theResponse = domoticzs.get(key).getScenes(httpClient);
|
||||
if(theResponse != null)
|
||||
addDomoticzDevices(deviceList, theResponse, key);
|
||||
else
|
||||
@@ -66,18 +72,65 @@ 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) {
|
||||
// Not a device handler
|
||||
return null;
|
||||
Devices theDomoticzApiResponse = null;
|
||||
String responseString = null;
|
||||
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && (theUrl.startsWith("http://") || theUrl.startsWith("https://"))) {
|
||||
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
DomoticzHandler theHandler = findHandlerByAddress(hostAddr);
|
||||
if(theHandler != null){
|
||||
String theData;
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
theData = httpClient.doHttpRequest(theHandler.buildUrl(anUrl), null, null, null, theHandler.buildHeaders());
|
||||
try {
|
||||
theDomoticzApiResponse = new Gson().fromJson(theData, Devices.class);
|
||||
if(theDomoticzApiResponse.getStatus().equals("OK"))
|
||||
responseString = null;
|
||||
else {
|
||||
log.warn("Call failed for Domoticz " + theHandler.getDomoticzAddress().getName() + " with status " + theDomoticzApiResponse.getStatus() + " for item " + theDomoticzApiResponse.getTitle());
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Cannot interrpret result from call for Domoticz " + theHandler.getDomoticzAddress().getName() + " as response is not parsable.");
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
} else {
|
||||
log.warn("Domoticz Call could not complete, no address found: " + theUrl);
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
} else {
|
||||
log.warn("Domoticz Call to be presented as http(s)://<ip_address>(:<port>)/payload, format of request unknown: " + theUrl);
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
validDomoticz = bridgeSettings.isValidDomoticz();
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
validDomoticz = bridgeSettings.getBridgeSettingsDescriptor().isValidDomoticz();
|
||||
log.info("Domoticz Home created." + (validDomoticz ? "" : " No Domoticz devices configured."));
|
||||
if(!validDomoticz)
|
||||
return null;
|
||||
httpClient = new HTTPHandler();
|
||||
domoticzs = new HashMap<String, DomoticzHandler>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getDomoticzaddress().getDevices().iterator();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getDomoticzaddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aDomoticz = theList.next();
|
||||
try {
|
||||
@@ -90,9 +143,26 @@ public class DomoticzHome implements Home {
|
||||
return this;
|
||||
}
|
||||
|
||||
private DomoticzHandler findHandlerByAddress(String hostAddress) {
|
||||
DomoticzHandler aHandler = null;
|
||||
boolean found = false;
|
||||
Iterator<String> keys = domoticzs.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
aHandler = domoticzs.get(key);
|
||||
if(aHandler != null && aHandler.getDomoticzAddress().getIp().equals(hostAddress)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
aHandler = null;
|
||||
return aHandler;
|
||||
}
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// noop
|
||||
if(httpClient != null)
|
||||
httpClient.closeHandler();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,25 +5,27 @@ import java.io.IOException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
|
||||
public class CommandHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(CommandHome.class);
|
||||
private BridgeSettings theSettings;
|
||||
|
||||
public CommandHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public CommandHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int itensity, Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
log.debug("Exec Request called with url: " + anItem.getItem().getAsString());
|
||||
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://"))
|
||||
@@ -31,7 +33,17 @@ public class CommandHome implements Home {
|
||||
else
|
||||
intermediate = anItem.getItem().getAsString();
|
||||
intermediate = BrightnessDecode.calculateReplaceIntensityValue(intermediate, itensity, targetBri, targetBriInc, false);
|
||||
intermediate = DeviceDataDecode.replaceDeviceData(intermediate, device);
|
||||
intermediate = TimeDecode.replaceTimeValue(intermediate);
|
||||
String execGarden = theSettings.getBridgeSecurity().getExecGarden();
|
||||
execGarden = execGarden.trim();
|
||||
if(execGarden != null && !execGarden.isEmpty()) {
|
||||
if(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
|
||||
intermediate = execGarden + "\\" + intermediate;
|
||||
else
|
||||
intermediate = execGarden + "/" + intermediate;
|
||||
}
|
||||
|
||||
String anError = doExecRequest(intermediate, lightId);
|
||||
if (anError != null) {
|
||||
responseString = anError;
|
||||
@@ -47,7 +59,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\"}}]";
|
||||
@@ -63,8 +75,9 @@ public class CommandHome implements Home {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
log.info("Command Home for system program execution created.");
|
||||
this.theSettings = bridgeSettings;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
@@ -21,7 +21,7 @@ public class HalHome implements Home {
|
||||
private Map<String, HalInfo> hals;
|
||||
private Boolean validHal;
|
||||
|
||||
public HalHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public HalHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
@@ -111,17 +111,17 @@ public class HalHome implements Home {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
validHal = bridgeSettings.isValidHal();
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
validHal = bridgeSettings.getBridgeSettingsDescriptor().isValidHal();
|
||||
log.info("HAL Home created." + (validHal ? "" : " No HAL devices configured."));
|
||||
if(!validHal)
|
||||
return null;
|
||||
hals = new HashMap<String, HalInfo>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHaladdress().getDevices().iterator();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHaladdress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHal = theList.next();
|
||||
try {
|
||||
hals.put(aHal.getName(), new HalInfo(aHal, bridgeSettings.getHaltoken()));
|
||||
hals.put(aHal.getName(), new HalInfo(aHal, bridgeSettings.getBridgeSettingsDescriptor().getHaltoken()));
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot get hal client (" + aHal.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
|
||||
return null;
|
||||
|
||||
@@ -149,8 +149,12 @@ public class HalInfo {
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
|
||||
if (theData != null) {
|
||||
log.debug("GET IrData for IR Device " + theHalDevice.getHaldevicename() + " HalApiResponse - data: " + theData);
|
||||
theHalApiResponse = new Gson().fromJson(theData, DeviceElements.class);
|
||||
if (theHalApiResponse.getDeviceElements() == null) {
|
||||
try {
|
||||
theHalApiResponse = new Gson().fromJson(theData, DeviceElements.class);
|
||||
} catch (Exception e) {
|
||||
theHalApiResponse = null;
|
||||
}
|
||||
if (theHalApiResponse == null || theHalApiResponse.getDeviceElements() == null) {
|
||||
StatusDescription theStatus = new Gson().fromJson(theData, StatusDescription.class);
|
||||
if (theStatus.getStatus() == null) {
|
||||
log.warn("Cannot get buttons for IR Device " + theHalDevice.getHaldevicename() + " for hal "
|
||||
@@ -181,4 +185,11 @@ public class HalInfo {
|
||||
this.halAddress = halAddress;
|
||||
}
|
||||
|
||||
public void closeInfo() {
|
||||
if(httpClient != null)
|
||||
httpClient.closeHandler();
|
||||
httpClient = null;
|
||||
halAddress = null;
|
||||
theToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,14 @@ import java.util.Set;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.IpList;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
@@ -31,7 +32,7 @@ public class HarmonyHome implements Home {
|
||||
private Boolean validHarmony;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public HarmonyHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public HarmonyHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
@@ -161,6 +162,8 @@ public class HarmonyHome implements Home {
|
||||
if (url.substring(0, 1).equalsIgnoreCase("{")) {
|
||||
url = "[" + url + "]";
|
||||
}
|
||||
|
||||
url = BrightnessDecode.calculateReplaceIntensityValue(url, intensity, targetBri, targetBriInc, false);
|
||||
ButtonPress[] deviceButtons = aGsonHandler.fromJson(url, ButtonPress[].class);
|
||||
Integer theCount = 1;
|
||||
for(int z = 0; z < deviceButtons.length; z++) {
|
||||
@@ -194,9 +197,9 @@ public class HarmonyHome implements Home {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
|
||||
validHarmony = bridgeSettings.isValidHarmony();
|
||||
validHarmony = bridgeSettings.getBridgeSettingsDescriptor().isValidHarmony();
|
||||
log.info("Harmony Home created." + (validHarmony ? "" : " No Harmony devices configured.") + (isDevMode ? " DevMode is set." : ""));
|
||||
if(validHarmony || isDevMode) {
|
||||
hubs = new HashMap<String, HarmonyServer>();
|
||||
@@ -211,16 +214,16 @@ public class HarmonyHome implements Home {
|
||||
theList.add(devModeIp);
|
||||
IpList thedevList = new IpList();
|
||||
thedevList.setDevices(theList);
|
||||
bridgeSettings.setHarmonyAddress(thedevList);
|
||||
bridgeSettings.getBridgeSettingsDescriptor().setHarmonyAddress(thedevList);
|
||||
}
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHarmonyAddress().getDevices().iterator();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHarmonyAddress().getDevices().iterator();
|
||||
while(theList.hasNext() && validHarmony) {
|
||||
NamedIP aHub = theList.next();
|
||||
boolean loopControl = true;
|
||||
int retryCount = 0;
|
||||
while(loopControl) {
|
||||
try {
|
||||
hubs.put(aHub.getName(), HarmonyServer.setup(bridgeSettings, isDevMode, aHub));
|
||||
hubs.put(aHub.getName(), HarmonyServer.setup(bridgeSettings.getBridgeSettingsDescriptor(), isDevMode, aHub));
|
||||
loopControl = false;
|
||||
} catch (Exception e) {
|
||||
if(retryCount > 3) {
|
||||
|
||||
@@ -4,6 +4,8 @@ import static java.lang.String.format;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -18,69 +20,98 @@ import net.whistlingfish.harmony.HarmonyClientModule;
|
||||
import net.whistlingfish.harmony.config.Activity;
|
||||
import net.whistlingfish.harmony.protocol.OAReplyProvider;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
public class HarmonyServer {
|
||||
|
||||
private static final String ACTIVIY_ID = "${activity.id}";
|
||||
private static final String ACTIVIY_LABEL = "${activity.label}";
|
||||
|
||||
@Inject
|
||||
private HarmonyClient harmonyClient;
|
||||
|
||||
|
||||
private HarmonyHandler myHarmony;
|
||||
private DevModeResponse devResponse;
|
||||
private OAReplyProvider dummyProvider;
|
||||
private NamedIP myNameAndIP;
|
||||
private Boolean isDevMode;
|
||||
private HTTPHandler httpClient;
|
||||
private Logger log = LoggerFactory.getLogger(HarmonyServer.class);
|
||||
|
||||
public HarmonyServer(NamedIP theHarmonyAddress) {
|
||||
super();
|
||||
myHarmony = null;
|
||||
dummyProvider = null;
|
||||
myNameAndIP = theHarmonyAddress;
|
||||
isDevMode = false;
|
||||
}
|
||||
public HarmonyServer(NamedIP theHarmonyAddress) {
|
||||
super();
|
||||
myHarmony = null;
|
||||
dummyProvider = null;
|
||||
myNameAndIP = theHarmonyAddress;
|
||||
isDevMode = false;
|
||||
httpClient = new HTTPHandler();
|
||||
}
|
||||
|
||||
public static HarmonyServer setup(BridgeSettingsDescriptor bridgeSettings, Boolean harmonyDevMode, NamedIP theHarmonyAddress) throws Exception {
|
||||
if(!bridgeSettings.isValidHarmony() && harmonyDevMode) {
|
||||
return new HarmonyServer(theHarmonyAddress);
|
||||
}
|
||||
Injector injector = null;
|
||||
if(!harmonyDevMode)
|
||||
injector = Guice.createInjector(new HarmonyClientModule());
|
||||
HarmonyServer mainObject = new HarmonyServer(theHarmonyAddress);
|
||||
if(!harmonyDevMode)
|
||||
injector.injectMembers(mainObject);
|
||||
mainObject.execute(bridgeSettings, harmonyDevMode);
|
||||
return mainObject;
|
||||
}
|
||||
|
||||
private void execute(BridgeSettingsDescriptor mySettings, Boolean harmonyDevMode) throws Exception {
|
||||
Boolean noopCalls = Boolean.parseBoolean(System.getProperty("noop.calls", "false"));
|
||||
isDevMode = harmonyDevMode;
|
||||
String modeString = "";
|
||||
if(dummyProvider != null)
|
||||
log.debug("something is very wrong as dummyProvider is not null...");
|
||||
if(isDevMode)
|
||||
modeString = " (development mode)";
|
||||
else if(noopCalls)
|
||||
modeString = " (no op calls to harmony)";
|
||||
log.info("setup initiated " + modeString + "....");
|
||||
if(isDevMode)
|
||||
{
|
||||
harmonyClient = null;
|
||||
devResponse = new DevModeResponse();
|
||||
public static HarmonyServer setup(
|
||||
BridgeSettingsDescriptor bridgeSettings,
|
||||
Boolean harmonyDevMode,
|
||||
NamedIP theHarmonyAddress
|
||||
) throws Exception {
|
||||
if (!bridgeSettings.isValidHarmony() && harmonyDevMode) {
|
||||
return new HarmonyServer(theHarmonyAddress);
|
||||
}
|
||||
else {
|
||||
devResponse = null;
|
||||
harmonyClient.addListener(new ActivityChangeListener() {
|
||||
@Override
|
||||
public void activityStarted(Activity activity) {
|
||||
log.info(format("activity changed: [%d] %s", activity.getId(), activity.getLabel()));
|
||||
}
|
||||
});
|
||||
harmonyClient.connect(myNameAndIP.getIp());
|
||||
Injector injector = null;
|
||||
if (!harmonyDevMode) {
|
||||
injector = Guice.createInjector(new HarmonyClientModule());
|
||||
}
|
||||
HarmonyServer mainObject = new HarmonyServer(theHarmonyAddress);
|
||||
if (!harmonyDevMode) {
|
||||
injector.injectMembers(mainObject);
|
||||
}
|
||||
mainObject.execute(bridgeSettings, harmonyDevMode);
|
||||
return mainObject;
|
||||
}
|
||||
|
||||
private void execute(BridgeSettingsDescriptor mySettings, Boolean harmonyDevMode) throws Exception {
|
||||
Boolean noopCalls = Boolean.parseBoolean(System.getProperty("noop.calls", "false"));
|
||||
isDevMode = harmonyDevMode;
|
||||
String modeString = "";
|
||||
if (dummyProvider != null) {
|
||||
log.debug("something is very wrong as dummyProvider is not null...");
|
||||
}
|
||||
if (isDevMode) {
|
||||
modeString = " (development mode)";
|
||||
} else if (noopCalls) {
|
||||
modeString = " (no op calls to harmony)";
|
||||
}
|
||||
log.info("setup initiated " + modeString + "....");
|
||||
if (isDevMode) {
|
||||
harmonyClient = null;
|
||||
devResponse = new DevModeResponse();
|
||||
} else {
|
||||
devResponse = null;
|
||||
harmonyClient.addListener(new ActivityChangeListener() {
|
||||
@Override
|
||||
public void activityStarted(Activity activity) {
|
||||
String webhook = myNameAndIP.getWebhook();
|
||||
if(webhook != null) {
|
||||
try {
|
||||
// Replacing variables
|
||||
webhook = webhook.replace(ACTIVIY_ID, activity.getId().toString());
|
||||
webhook = webhook.replace(ACTIVIY_LABEL, URLEncoder.encode(activity.getLabel(), "UTF-8"));
|
||||
|
||||
log.info(format("calling webhook: %s", webhook));
|
||||
|
||||
// Calling webhook
|
||||
httpClient.doHttpRequest(webhook, HttpGet.METHOD_NAME, null, null, null);
|
||||
} catch (Exception e) {
|
||||
log.warn("could not call webhook: " + webhook, e);
|
||||
}
|
||||
}
|
||||
log.info(format("activity changed: [%d] %s", activity.getId(), activity.getLabel()));
|
||||
}
|
||||
});
|
||||
harmonyClient.connect(myNameAndIP.getIp());
|
||||
}
|
||||
myHarmony = new HarmonyHandler(harmonyClient, noopCalls, devResponse);
|
||||
}
|
||||
}
|
||||
|
||||
public HarmonyHandler getMyHarmony() {
|
||||
return myHarmony;
|
||||
}
|
||||
public HarmonyHandler getMyHarmony() {
|
||||
return myHarmony;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
@@ -26,23 +26,23 @@ public class HassHome implements Home {
|
||||
private Boolean validHass;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public HassHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public HassHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
hassMap = null;
|
||||
aGsonHandler = null;
|
||||
validHass = bridgeSettings.isValidHass();
|
||||
validHass = bridgeSettings.getBridgeSettingsDescriptor().isValidHass();
|
||||
log.info("HomeAssistant Home created." + (validHass ? "" : " No HomeAssistants configured."));
|
||||
if(validHass) {
|
||||
hassMap = new HashMap<String,HomeAssistant>();
|
||||
aGsonHandler =
|
||||
new GsonBuilder()
|
||||
.create();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHassaddress().getDevices().iterator();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHassaddress().getDevices().iterator();
|
||||
while(theList.hasNext() && validHass) {
|
||||
NamedIP aHass = theList.next();
|
||||
try {
|
||||
@@ -151,10 +151,14 @@ public class HassHome implements Home {
|
||||
public void closeHome() {
|
||||
if(!validHass)
|
||||
return;
|
||||
if(hassMap == null)
|
||||
return;
|
||||
Iterator<String> keys = hassMap.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
hassMap.get(key).closeClient();
|
||||
}
|
||||
|
||||
hassMap = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,12 @@ public class HomeAssistant {
|
||||
aUrl = "https";
|
||||
else
|
||||
aUrl = "http";
|
||||
aUrl = aUrl + "://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/services/" + aCommand.getEntityId().substring(0, aCommand.getEntityId().indexOf("."));
|
||||
String domain = aCommand.getEntityId().substring(0, aCommand.getEntityId().indexOf("."));
|
||||
aUrl = aUrl + "://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/services/";
|
||||
if(domain.equals("group"))
|
||||
aUrl = aUrl + "homeassistant";
|
||||
else
|
||||
aUrl = aUrl + domain;
|
||||
String aBody = "{\"entity_id\":\"" + aCommand.getEntityId() + "\"";
|
||||
NameValue[] headers = null;
|
||||
if(hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
|
||||
@@ -62,6 +67,7 @@ public class HomeAssistant {
|
||||
aUrl = aUrl + "/turn_off";
|
||||
aBody = aBody + "}";
|
||||
}
|
||||
log.debug("Calling HomeAssistant with url: " + aUrl);
|
||||
String theData = anHttpHandler.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "application/json", aBody, headers);
|
||||
log.debug("call Command return is: <" + theData + ">");
|
||||
return true;
|
||||
@@ -104,5 +110,6 @@ public class HomeAssistant {
|
||||
|
||||
protected void closeClient() {
|
||||
anHttpHandler.closeHandler();
|
||||
anHttpHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,27 +4,18 @@ import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.CookieSpecs;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
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.ssl.SSLContexts;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -33,23 +24,13 @@ import com.bwssystems.HABridge.api.NameValue;
|
||||
|
||||
public class HTTPHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(HTTPHandler.class);
|
||||
private HttpClient httpClient;
|
||||
// private CloseableHttpClient httpclientSSL;
|
||||
// private SSLContext sslcontext;
|
||||
// private SSLConnectionSocketFactory sslsf;
|
||||
// private RequestConfig globalConfig;
|
||||
private CloseableHttpClient httpClient;
|
||||
private RequestConfig globalConfig;
|
||||
|
||||
|
||||
public HTTPHandler() {
|
||||
httpClient = HttpClients.createDefault();
|
||||
// Removed Specific SSL as Apache HttpClient automatically uses SSL if the URI starts with https://
|
||||
// Trust own CA and all self-signed certs
|
||||
// sslcontext = SSLContexts.createDefault();
|
||||
// Allow TLSv1 protocol only
|
||||
// sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLS" }, null,
|
||||
// SSLConnectionSocketFactory.getDefaultHostnameVerifier());
|
||||
// globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
|
||||
// httpclientSSL = HttpClients.custom().setSSLSocketFactory(sslsf).setDefaultRequestConfig(globalConfig).build();
|
||||
globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
|
||||
httpClient = HttpClients.custom().setDefaultRequestConfig(globalConfig).build();
|
||||
}
|
||||
|
||||
|
||||
@@ -100,33 +81,56 @@ public class HTTPHandler {
|
||||
request.setHeader(headers[i].getName(), headers[i].getValue());
|
||||
}
|
||||
}
|
||||
HttpResponse response;
|
||||
try {
|
||||
HttpResponse response;
|
||||
// Removed Specific SSL as Apache HttpClient automatically uses SSL if the URI starts with https://
|
||||
// if (url.startsWith("xyzhttps"))
|
||||
// response = httpclientSSL.execute(request);
|
||||
// else
|
||||
for(int retryCount = 0; retryCount < 2; retryCount++) {
|
||||
response = httpClient.execute(request);
|
||||
log.debug((httpVerb == null ? "GET" : httpVerb) + " execute 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((httpVerb == null ? "GET" : httpVerb) + " execute (" + retryCount + ") on URL responded: "
|
||||
+ response.getStatusLine().getStatusCode());
|
||||
if (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300) {
|
||||
if (response.getEntity() != null) {
|
||||
try {
|
||||
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); // read
|
||||
// content
|
||||
// for
|
||||
// data
|
||||
EntityUtils.consume(response.getEntity()); // close out
|
||||
// inputstream
|
||||
// ignore
|
||||
// content
|
||||
} catch (Exception e) {
|
||||
log.debug("Error ocurred in handling response entity after successful call, still responding success. "
|
||||
+ e.getMessage(), e);
|
||||
}
|
||||
log.debug("Successfull response - The http response is <<<" + theContent + ">>>");
|
||||
}
|
||||
retryCount = 2;
|
||||
} else {
|
||||
log.warn("HTTP response code was not an expected successful response of between 200 - 299, the code was: " + response.getStatusLine());
|
||||
try {
|
||||
String someContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); // read
|
||||
// content
|
||||
// for
|
||||
// data
|
||||
EntityUtils.consume(response.getEntity()); // close out
|
||||
// inputstream
|
||||
// ignore
|
||||
// content
|
||||
log.debug("Unsuccessfull response - The http response is <<<" + someContent + ">>>");
|
||||
} catch (Exception e) {
|
||||
//noop
|
||||
}
|
||||
if (response.getStatusLine().getStatusCode() == 504) {
|
||||
log.warn("HTTP response code was 504, retrying...");
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e1) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
else
|
||||
retryCount = 2;
|
||||
}
|
||||
if (theContent == null)
|
||||
theContent = "";
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Error calling out to HA gateway: IOException in log", e);
|
||||
@@ -134,23 +138,22 @@ public class HTTPHandler {
|
||||
return theContent;
|
||||
}
|
||||
|
||||
public HttpClient getHttpClient() {
|
||||
// public HttpClient getHttpClient() {
|
||||
// return httpClient;
|
||||
// }
|
||||
|
||||
|
||||
public CloseableHttpClient getHttpClient() {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
|
||||
// public CloseableHttpClient getHttpclientSSL() {
|
||||
// return httpclientSSL;
|
||||
// }
|
||||
|
||||
|
||||
public void closeHandler() {
|
||||
try {
|
||||
httpClient.close();
|
||||
} catch (IOException e) {
|
||||
// noop
|
||||
}
|
||||
httpClient = null;
|
||||
// try {
|
||||
// httpclientSSL.close();
|
||||
// } catch (IOException e) {
|
||||
// // noop
|
||||
// }
|
||||
// httpclientSSL = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.bwssystems.HABridge.plugins.http;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
@@ -11,6 +11,7 @@ import com.bwssystems.HABridge.api.hue.HueError;
|
||||
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.google.gson.Gson;
|
||||
@@ -19,7 +20,7 @@ public class HTTPHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(HTTPHome.class);
|
||||
private HTTPHandler anHttpHandler;
|
||||
|
||||
public HTTPHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public HTTPHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
@@ -29,30 +30,34 @@ public class HTTPHome implements Home {
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
|
||||
//Backwards Compatibility Items
|
||||
if(anItem.getHttpVerb() == null || anItem.getHttpVerb().isEmpty())
|
||||
{
|
||||
if(device.getHttpVerb() != null && !device.getHttpVerb().isEmpty())
|
||||
anItem.setHttpVerb(device.getHttpVerb());
|
||||
}
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && (theUrl.startsWith("http://") || theUrl.startsWith("https://"))) {
|
||||
//Backwards Compatibility Items
|
||||
if(anItem.getHttpVerb() == null || anItem.getHttpVerb().isEmpty())
|
||||
{
|
||||
if(device.getHttpVerb() != null && !device.getHttpVerb().isEmpty())
|
||||
anItem.setHttpVerb(device.getHttpVerb());
|
||||
}
|
||||
|
||||
if(anItem.getHttpHeaders() == null || anItem.getHttpHeaders().isEmpty()) {
|
||||
if(device.getHeaders() != null && !device.getHeaders().isEmpty() )
|
||||
anItem.setHttpHeaders(device.getHeaders());
|
||||
}
|
||||
|
||||
log.debug("executing HUE api request to Http "
|
||||
+ (anItem.getHttpVerb() == null ? "GET" : anItem.getHttpVerb()) + ": "
|
||||
+ anItem.getItem().getAsString());
|
||||
|
||||
if(anItem.getHttpHeaders() == null || anItem.getHttpHeaders().isEmpty()) {
|
||||
if(device.getHeaders() != null && !device.getHeaders().isEmpty() )
|
||||
anItem.setHttpHeaders(device.getHeaders());
|
||||
}
|
||||
|
||||
log.debug("executing HUE api request to Http "
|
||||
+ (anItem.getHttpVerb() == null ? "GET" : anItem.getHttpVerb()) + ": "
|
||||
+ anItem.getItem().getAsString());
|
||||
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(anItem.getItem().getAsString(),
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrl,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
|
||||
anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device);
|
||||
anUrl = TimeDecode.replaceTimeValue(anUrl);
|
||||
|
||||
String aBody = null;
|
||||
if(anItem.getHttpBody()!= null && !anItem.getHttpBody().isEmpty()) {
|
||||
aBody = BrightnessDecode.calculateReplaceIntensityValue(anItem.getHttpBody(),
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
aBody = DeviceDataDecode.replaceDeviceData(aBody, device);
|
||||
aBody = TimeDecode.replaceTimeValue(aBody);
|
||||
}
|
||||
// make call
|
||||
@@ -63,11 +68,18 @@ public class HTTPHome implements Home {
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
} else {
|
||||
log.warn("HTTP Call to be presented as http(s)://<ip_address>(:<port>)/payload, format of request unknown: " + theUrl);
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
anHttpHandler = new HTTPHandler();
|
||||
log.info("Http Home created.");
|
||||
return this;
|
||||
@@ -81,7 +93,9 @@ public class HTTPHome implements Home {
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
anHttpHandler.closeHandler();
|
||||
if(anHttpHandler != null)
|
||||
anHttpHandler.closeHandler();
|
||||
anHttpHandler = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
@@ -25,7 +25,7 @@ public class HueHome implements Home {
|
||||
private Boolean validHue;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public HueHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public HueHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
@@ -105,12 +105,12 @@ public class HueHome implements Home {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
validHue = bridgeSettings.isValidHue();
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
validHue = bridgeSettings.getBridgeSettingsDescriptor().isValidHue();
|
||||
log.info("Hue passthru Home created." + (validHue ? "" : " No Hue passtrhu systems configured."));
|
||||
if(validHue) {
|
||||
hues = new HashMap<String, HueInfo>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHueaddress().getDevices().iterator();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHueaddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHue = theList.next();
|
||||
hues.put(aHue.getName(), new HueInfo(aHue));
|
||||
@@ -124,10 +124,13 @@ public class HueHome implements Home {
|
||||
public void closeHome() {
|
||||
if(!validHue)
|
||||
return;
|
||||
if(hues == null)
|
||||
return;
|
||||
Iterator<String> keys = hues.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
hues.get(key).closeHue();;
|
||||
}
|
||||
hues = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.bwssystems.HABridge.plugins.lifx;
|
||||
|
||||
import com.github.besherman.lifx.LFXGroup;
|
||||
import com.github.besherman.lifx.LFXLight;
|
||||
|
||||
public class LifxDevice {
|
||||
private Object lifxObject;
|
||||
private String type;
|
||||
public final static String LIGHT_TYPE = "Light";
|
||||
public final static String GROUP_TYPE = "Group";
|
||||
|
||||
public LifxDevice(Object lifxObject, String type) {
|
||||
super();
|
||||
this.lifxObject = lifxObject;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public LifxEntry toEntry() {
|
||||
LifxEntry anEntry = null;
|
||||
if(type.equals(LIGHT_TYPE)) {
|
||||
anEntry = new LifxEntry();
|
||||
anEntry.setId(((LFXLight)lifxObject).getID());
|
||||
anEntry.setName(((LFXLight)lifxObject).getLabel());
|
||||
anEntry.setType(LIGHT_TYPE);
|
||||
}
|
||||
if(type.equals(GROUP_TYPE)) {
|
||||
anEntry = new LifxEntry();
|
||||
anEntry.setId("na");
|
||||
anEntry.setName(((LFXGroup)lifxObject).getLabel());
|
||||
anEntry.setType(GROUP_TYPE);
|
||||
}
|
||||
return anEntry;
|
||||
}
|
||||
public Object getLifxObject() {
|
||||
return lifxObject;
|
||||
}
|
||||
|
||||
public void setLifxObject(Object lifxObject) {
|
||||
this.lifxObject = lifxObject;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.bwssystems.HABridge.plugins.lifx;
|
||||
|
||||
public class LifxEntry {
|
||||
private String name;
|
||||
private String id;
|
||||
private String type;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
257
src/main/java/com/bwssystems/HABridge/plugins/lifx/LifxHome.java
Normal file
257
src/main/java/com/bwssystems/HABridge/plugins/lifx/LifxHome.java
Normal file
@@ -0,0 +1,257 @@
|
||||
package com.bwssystems.HABridge.plugins.lifx;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.github.besherman.lifx.LFXClient;
|
||||
import com.github.besherman.lifx.LFXGroup;
|
||||
import com.github.besherman.lifx.LFXGroupCollection;
|
||||
import com.github.besherman.lifx.LFXGroupCollectionListener;
|
||||
import com.github.besherman.lifx.LFXLight;
|
||||
import com.github.besherman.lifx.LFXLightCollection;
|
||||
import com.github.besherman.lifx.LFXLightCollectionListener;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
public class LifxHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(LifxHome.class);
|
||||
private static final float DIM_DIVISOR = (float)254.00;
|
||||
private Map<String, LifxDevice> lifxMap;
|
||||
private LFXClient client;
|
||||
private Boolean validLifx;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public LifxHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
lifxMap = null;
|
||||
aGsonHandler = null;
|
||||
validLifx = bridgeSettings.getBridgeSettingsDescriptor().isValidLifx();
|
||||
log.info("LifxDevice Home created." + (validLifx ? "" : " No LifxDevices configured."));
|
||||
if(validLifx) {
|
||||
try {
|
||||
log.info("Open Lifx client....");
|
||||
InetAddress configuredAddress = InetAddress.getByName(bridgeSettings.getBridgeSettingsDescriptor().getUpnpConfigAddress());
|
||||
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(configuredAddress);
|
||||
InetAddress bcastInetAddr = null;
|
||||
if (networkInterface != null) {
|
||||
for (InterfaceAddress ifaceAddr : networkInterface.getInterfaceAddresses()) {
|
||||
InetAddress addr = ifaceAddr.getAddress();
|
||||
if (addr instanceof Inet4Address) {
|
||||
bcastInetAddr = ifaceAddr.getBroadcast();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bcastInetAddr != null) {
|
||||
lifxMap = new HashMap<String, LifxDevice>();
|
||||
log.info("Opening LFX Client with broadcast address: " + bcastInetAddr.getHostAddress());
|
||||
client = new LFXClient(bcastInetAddr.getHostAddress());
|
||||
client.getLights().addLightCollectionListener(new MyLightListener(lifxMap));
|
||||
client.getGroups().addGroupCollectionListener(new MyGroupListener(lifxMap));
|
||||
client.open(false);
|
||||
aGsonHandler =
|
||||
new GsonBuilder()
|
||||
.create();
|
||||
} else {
|
||||
log.warn("Could not open LIFX, no bcast addr available, check your upnp config address.");
|
||||
client = null;
|
||||
validLifx = false;
|
||||
return this;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not open LIFX, with IO Exception", e);
|
||||
client = null;
|
||||
validLifx = false;
|
||||
return this;
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Could not open LIFX, with Interruprted Exception", e);
|
||||
client = null;
|
||||
validLifx = false;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LifxDevice getLifxDevice(String aName) {
|
||||
if(!validLifx)
|
||||
return null;
|
||||
LifxDevice aLifxDevice = null;
|
||||
if(aName == null || aName.equals("")) {
|
||||
log.debug("Cannot get LifxDevice for name as it is empty.");
|
||||
}
|
||||
else {
|
||||
aLifxDevice = lifxMap.get(aName);
|
||||
log.debug("Retrieved a LifxDevice for name: " + aName);
|
||||
}
|
||||
return aLifxDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
log.debug("consolidating devices for lifx");
|
||||
if(!validLifx)
|
||||
return null;
|
||||
LifxEntry theResponse = null;
|
||||
Iterator<String> keys = lifxMap.keySet().iterator();
|
||||
List<LifxEntry> deviceList = new ArrayList<LifxEntry>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = lifxMap.get(key).toEntry();
|
||||
if(theResponse != null)
|
||||
deviceList.add(theResponse);
|
||||
else {
|
||||
log.warn("Cannot get LifxDevice with name: " + key + ", skipping this Lifx.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
private Boolean addLifxLights(LFXLightCollection theDeviceList) {
|
||||
if(!validLifx)
|
||||
return false;
|
||||
Iterator<LFXLight> devices = theDeviceList.iterator();;
|
||||
while(devices.hasNext()) {
|
||||
LFXLight theDevice = devices.next();
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(theDevice, LifxDevice.LIGHT_TYPE);
|
||||
lifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Boolean addLifxGroups(LFXGroupCollection theDeviceList) {
|
||||
if(!validLifx)
|
||||
return false;
|
||||
Iterator<LFXGroup> devices = theDeviceList.iterator();;
|
||||
while(devices.hasNext()) {
|
||||
LFXGroup theDevice = devices.next();
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(theDevice, LifxDevice.GROUP_TYPE);
|
||||
lifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String theReturn = null;
|
||||
float aBriValue;
|
||||
float theValue;
|
||||
log.debug("executing HUE api request to send message to LifxDevice: " + anItem.getItem().toString());
|
||||
if(!validLifx) {
|
||||
log.warn("Should not get here, no LifxDevice clients configured");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no LifxDevices configured\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
|
||||
} else {
|
||||
LifxEntry lifxCommand = null;
|
||||
if(anItem.getItem().isJsonObject())
|
||||
lifxCommand = aGsonHandler.fromJson(anItem.getItem(), LifxEntry.class);
|
||||
else
|
||||
lifxCommand = aGsonHandler.fromJson(anItem.getItem().getAsString(), LifxEntry.class);
|
||||
LifxDevice theDevice = getLifxDevice(lifxCommand.getName());
|
||||
if (theDevice == null) {
|
||||
log.warn("Should not get here, no LifxDevices available");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no Lifx clients available\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
log.debug("calling LifxDevice: " + lifxCommand.getName());
|
||||
if(theDevice.getType().equals(LifxDevice.LIGHT_TYPE)) {
|
||||
LFXLight theLight = (LFXLight)theDevice.getLifxObject();
|
||||
if(body.contains("true"))
|
||||
theLight.setPower(true);
|
||||
if(body.contains("false"))
|
||||
theLight.setPower(false);
|
||||
if(targetBri != null || targetBriInc != null) {
|
||||
aBriValue = (float)BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc);
|
||||
theValue = aBriValue/DIM_DIVISOR;
|
||||
if(theValue > (float)1.0)
|
||||
theValue = (float)0.99;
|
||||
theLight.setBrightness(theValue);
|
||||
}
|
||||
} else if (theDevice.getType().equals(LifxDevice.GROUP_TYPE)) {
|
||||
LFXGroup theGroup = (LFXGroup)theDevice.getLifxObject();
|
||||
if(body.contains("true"))
|
||||
theGroup.setPower(true);
|
||||
if(body.contains("false"))
|
||||
theGroup.setPower(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return theReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validLifx)
|
||||
return;
|
||||
client.close();
|
||||
}
|
||||
private static class MyLightListener implements LFXLightCollectionListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(MyLightListener.class);
|
||||
private Map<String, LifxDevice> aLifxMap;
|
||||
public MyLightListener(Map<String, LifxDevice> theMap) {
|
||||
aLifxMap = theMap;
|
||||
}
|
||||
@Override
|
||||
public void lightAdded(LFXLight light) {
|
||||
log.debug("Light added, label: " + light.getLabel() + " and id: " + light.getID());
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(light, LifxDevice.LIGHT_TYPE);
|
||||
aLifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lightRemoved(LFXLight light) {
|
||||
log.debug("Light removed, label: " + light.getLabel() + " and id: " + light.getID());
|
||||
aLifxMap.remove(light.getLabel());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
private static class MyGroupListener implements LFXGroupCollectionListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(MyLightListener.class);
|
||||
private Map<String, LifxDevice> aLifxMap;
|
||||
public MyGroupListener(Map<String, LifxDevice> theMap) {
|
||||
aLifxMap = theMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void groupAdded(LFXGroup group) {
|
||||
log.debug("Group: " + group.getLabel() + " added: " + group.size());
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(group, LifxDevice.GROUP_TYPE);
|
||||
aLifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void groupRemoved(LFXGroup group) {
|
||||
log.debug("Group: " + group.getLabel() + " removed");
|
||||
aLifxMap.remove(group.getLabel());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bwssystems.HABridge.plugins.mqtt;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.eclipse.paho.client.mqttv3.MqttClient;
|
||||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
@@ -47,7 +48,7 @@ public class MQTTHandler {
|
||||
}
|
||||
|
||||
public void publishMessage(String topic, String content) {
|
||||
MqttMessage message = new MqttMessage(content.getBytes());
|
||||
MqttMessage message = new MqttMessage(StringEscapeUtils.unescapeJava(content).getBytes());
|
||||
message.setQos(qos);
|
||||
try {
|
||||
myClient.publish(topic, message);
|
||||
@@ -68,5 +69,6 @@ public class MQTTHandler {
|
||||
} catch (MqttException e) {
|
||||
log.warn("Could not disconnect MQTT client for name: " + myConfig.getName() + " and ip: " + myConfig.getIp());
|
||||
}
|
||||
myClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,13 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.google.gson.Gson;
|
||||
@@ -25,7 +26,7 @@ public class MQTTHome implements Home {
|
||||
private Boolean validMqtt;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public MQTTHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public MQTTHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
@@ -42,6 +43,7 @@ public class MQTTHome implements Home {
|
||||
handlers.get(key).shutdown();
|
||||
}
|
||||
}
|
||||
handlers = null;
|
||||
}
|
||||
|
||||
public MQTTHandler getMQTTHandler(String aName) {
|
||||
@@ -88,6 +90,7 @@ public class MQTTHome implements Home {
|
||||
mqttObject =anItem.getItem().getAsString();
|
||||
mqttObject = BrightnessDecode.calculateReplaceIntensityValue(mqttObject,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
mqttObject = DeviceDataDecode.replaceDeviceData(mqttObject, device);
|
||||
mqttObject = TimeDecode.replaceTimeValue(mqttObject);
|
||||
if (mqttObject.substring(0, 1).equalsIgnoreCase("{"))
|
||||
mqttObject = "[" + mqttObject + "]";
|
||||
@@ -120,15 +123,15 @@ public class MQTTHome implements Home {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
validMqtt = bridgeSettings.isValidMQTT();
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
validMqtt = bridgeSettings.getBridgeSettingsDescriptor().isValidMQTT();
|
||||
log.info("MQTT Home created." + (validMqtt ? "" : " No MQTT Clients configured."));
|
||||
if(validMqtt) {
|
||||
aGsonHandler =
|
||||
new GsonBuilder()
|
||||
.create();
|
||||
handlers = new HashMap<String, MQTTHandler>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getMqttaddress().getDevices().iterator();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getMqttaddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aClientConfig = theList.next();
|
||||
MQTTHandler aHandler = new MQTTHandler(aClientConfig);
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.bwssystems.HABridge.plugins.somfy;
|
||||
|
||||
public class SomfyDevice {
|
||||
private String id;
|
||||
private String room;
|
||||
private String category;
|
||||
private String somfyname;
|
||||
private String name;
|
||||
private String deviceUrl;
|
||||
private String deviceType;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setRoom(String room) {
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
public String getRoom() {
|
||||
return room;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setSomfyname(String somfyname) {
|
||||
this.somfyname = somfyname;
|
||||
}
|
||||
|
||||
public String getSomfyname() {
|
||||
return somfyname;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setDeviceUrl(String deviceUrl) {
|
||||
this.deviceUrl = deviceUrl;
|
||||
}
|
||||
|
||||
public String getDeviceUrl() {
|
||||
return deviceUrl;
|
||||
}
|
||||
|
||||
public void setDeviceType(String deviceType) {
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public String getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.bwssystems.HABridge.plugins.somfy;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Support for Somfy Tahoma hub which allows control of IO Homecontrol devices such as Velux windows.
|
||||
* Currently supports 'turn on' for open window, and 'turn off' for close.
|
||||
*
|
||||
* Known issues:
|
||||
* //TODO - Fix bug on UI where bulk update seems to add the single device twice if 'update' is clicked (this is a general bug with Vera too I think)
|
||||
* Enhancements:
|
||||
* //TODO - support 'dimming' for partial window opening.
|
||||
*
|
||||
*/
|
||||
public class SomfyHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(SomfyHome.class);
|
||||
private Map<String, SomfyInfo> somfys;
|
||||
private Boolean validSomfy;
|
||||
|
||||
public SomfyHome(BridgeSettings bridgeSettings) {
|
||||
createHome(bridgeSettings);
|
||||
|
||||
}
|
||||
|
||||
public SomfyInfo getSomfyHandler(String somfyName) {
|
||||
return somfys.get(somfyName);
|
||||
}
|
||||
|
||||
public List<SomfyDevice> getDevices() {
|
||||
log.debug("consolidating devices for somfy");
|
||||
Iterator<String> keys = somfys.keySet().iterator();
|
||||
ArrayList<SomfyDevice> deviceList = new ArrayList<>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
List<SomfyDevice> devices = somfys.get(key).getSomfyDevices();
|
||||
deviceList.addAll(devices);
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
if(validSomfy) {
|
||||
if(type.equalsIgnoreCase(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex]))
|
||||
return getDevices();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
if (!validSomfy) {
|
||||
log.warn("Should not get here, no somfy hub available");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no somfy hub available\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
if (anItem.getType() != null && anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex])) {
|
||||
|
||||
log.debug("executing HUE api request to change activity to Somfy: " + anItem.getItem().toString());
|
||||
String jsonToPost = anItem.getItem().toString();
|
||||
|
||||
SomfyInfo somfyHandler = getSomfyHandler(device.getTargetDevice());
|
||||
if(somfyHandler == null) {
|
||||
log.warn("Should not get here, no Somfy configured");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no somfy configured\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
try {
|
||||
somfyHandler.execApply(jsonToPost);
|
||||
} catch (Exception e) {
|
||||
log.warn("Error posting request to Somfy");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Error posting request to SomfyTahoma\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
validSomfy = bridgeSettings.getBridgeSettingsDescriptor().isValidSomfy();
|
||||
log.info("Somfy Home created." + (validSomfy ? "" : " No Somfys configured."));
|
||||
if(validSomfy) {
|
||||
somfys = new HashMap<>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getSomfyAddress().getDevices().iterator();
|
||||
while (theList.hasNext()) {
|
||||
NamedIP aSomfy = theList.next();
|
||||
somfys.put(aSomfy.getName(), new SomfyInfo(aSomfy, aSomfy.getName()));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
somfys = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.bwssystems.HABridge.plugins.somfy;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup.Device;
|
||||
import com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup.GetSetup;
|
||||
import com.google.gson.Gson;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class SomfyInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(SomfyInfo.class);
|
||||
private final String somfyName;
|
||||
private final NamedIP namedIP;
|
||||
private HTTPHandler httpClient;
|
||||
private static final String CONNECT_HOST = "https://www.tahomalink.com/";
|
||||
private static final String BASE_URL = CONNECT_HOST + "enduser-mobile-web/externalAPI/";
|
||||
private static final String BASE_URL_ENDUSER = CONNECT_HOST + "enduser-mobile-web/enduserAPI/";
|
||||
|
||||
public SomfyInfo(NamedIP namedIP, String somfyName) {
|
||||
super();
|
||||
this.somfyName = somfyName;
|
||||
this.namedIP = namedIP;
|
||||
}
|
||||
|
||||
private void initHttpClient() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
|
||||
if(httpClient==null) {
|
||||
httpClient = new HTTPHandler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<SomfyDevice> getSomfyDevices() {
|
||||
|
||||
List<SomfyDevice> somfyDevices = new ArrayList<>();
|
||||
try {
|
||||
login(namedIP.getUsername(), namedIP.getPassword());
|
||||
GetSetup setupData = getSetup();
|
||||
for(Device device : setupData.getSetup().getDevices()) {
|
||||
somfyDevices.add(mapDeviceToSomfyDevice(device));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Could not get Somfy devices", e);
|
||||
}
|
||||
return somfyDevices;
|
||||
}
|
||||
|
||||
|
||||
public void login(String username, String password) throws Exception {
|
||||
|
||||
initHttpClient();
|
||||
NameValue[] httpHeader = getHttpHeaders();
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("userId", username));
|
||||
nvps.add(new BasicNameValuePair("userPassword", password));
|
||||
log.debug("Making SOMFY http login call");
|
||||
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(nvps);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
urlEncodedFormEntity.writeTo(bos);
|
||||
String body = bos.toString();
|
||||
String response = httpClient.doHttpRequest(BASE_URL + "json/login",HttpPost.METHOD_NAME, "application/x-www-form-urlencoded", body,httpHeader);
|
||||
log.debug(response);
|
||||
}
|
||||
|
||||
private NameValue[] getHttpHeaders() {
|
||||
NameValue userAgentHeader = new NameValue();
|
||||
userAgentHeader.setName("User-Agent");
|
||||
userAgentHeader.setValue("mine");
|
||||
return new NameValue[]{userAgentHeader};
|
||||
}
|
||||
|
||||
public GetSetup getSetup() throws IOException {
|
||||
NameValue[] httpHeader = getHttpHeaders();
|
||||
log.info("Making SOMFY http setup call");
|
||||
String response = httpClient.doHttpRequest(BASE_URL + "json/getSetup", HttpGet.METHOD_NAME, "", "", httpHeader );
|
||||
log.debug(response);
|
||||
GetSetup setupData = new Gson().fromJson(response, GetSetup.class);
|
||||
return setupData;
|
||||
}
|
||||
|
||||
public void execApply(String jsonToPost) throws Exception {
|
||||
login(namedIP.getUsername(), namedIP.getPassword());
|
||||
log.info("Making SOMFY http exec call");
|
||||
String response = httpClient.doHttpRequest(BASE_URL_ENDUSER + "exec/apply", HttpPost.METHOD_NAME, "application/json;charset=UTF-8", jsonToPost, getHttpHeaders());
|
||||
log.info(response);
|
||||
}
|
||||
|
||||
|
||||
protected SomfyDevice mapDeviceToSomfyDevice(Device device) {
|
||||
SomfyDevice somfyDevice = new SomfyDevice();
|
||||
somfyDevice.setId(device.getOid());
|
||||
somfyDevice.setCategory(device.getUiClass());
|
||||
somfyDevice.setRoom("");
|
||||
somfyDevice.setSomfyname(somfyName);
|
||||
somfyDevice.setName(device.getLabel());
|
||||
somfyDevice.setDeviceUrl(device.getDeviceURL());
|
||||
somfyDevice.setDeviceType(device.getWidget());
|
||||
return somfyDevice;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Device {
|
||||
|
||||
@SerializedName("label")
|
||||
@Expose
|
||||
private String label;
|
||||
@SerializedName("deviceURL")
|
||||
@Expose
|
||||
private String deviceURL;
|
||||
@SerializedName("widget")
|
||||
@Expose
|
||||
private String widget;
|
||||
@SerializedName("oid")
|
||||
@Expose
|
||||
private String oid;
|
||||
@SerializedName("uiClass")
|
||||
@Expose
|
||||
private String uiClass;
|
||||
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getDeviceURL() {
|
||||
return deviceURL;
|
||||
}
|
||||
|
||||
public void setDeviceURL(String deviceURL) {
|
||||
this.deviceURL = deviceURL;
|
||||
}
|
||||
|
||||
|
||||
public String getWidget() {
|
||||
return widget;
|
||||
}
|
||||
|
||||
public void setWidget(String widget) {
|
||||
this.widget = widget;
|
||||
}
|
||||
|
||||
|
||||
public String getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(String oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public String getUiClass() {
|
||||
return uiClass;
|
||||
}
|
||||
|
||||
public void setUiClass(String uiClass) {
|
||||
this.uiClass = uiClass;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class GetSetup {
|
||||
|
||||
@SerializedName("setup")
|
||||
@Expose
|
||||
private Setup setup;
|
||||
|
||||
public Setup getSetup() {
|
||||
return setup;
|
||||
}
|
||||
|
||||
public void setSetup(Setup setup) {
|
||||
this.setup = setup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
package com.bwssystems.HABridge.plugins.somfy.jsonschema2pojo.getsetup;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Setup {
|
||||
|
||||
@SerializedName("id")
|
||||
@Expose
|
||||
private String id;
|
||||
@SerializedName("devices")
|
||||
@Expose
|
||||
private List<Device> devices = null;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<Device> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
public void setDevices(List<Device> devices) {
|
||||
this.devices = devices;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,29 +1,40 @@
|
||||
package com.bwssystems.HABridge.plugins.tcp;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
public class TCPHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(TCPHome.class);
|
||||
private byte[] sendData;
|
||||
private Map<String, Socket> theSockets;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
|
||||
public TCPHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public TCPHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
@@ -31,47 +42,90 @@ public class TCPHome implements Home {
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Socket dataSendSocket = null;
|
||||
log.debug("executing HUE api request to TCP: " + anItem.getItem().getAsString());
|
||||
String intermediate = anItem.getItem().getAsString().substring(anItem.getItem().getAsString().indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
InetAddress IPAddress = null;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
|
||||
if(theUrl != null && !theUrl.isEmpty () && theUrl.contains("tcp://")) {
|
||||
if(!theUrl.startsWith("{\"tcpDevice\""))
|
||||
theUrl = "{\"tcpDevice\":\"" + theUrl + "\"}";
|
||||
TcpDevice theDevice = aGsonHandler.fromJson(theUrl, TcpDevice.class);
|
||||
String intermediate = theDevice.getTcpDevice().substring(theDevice.getTcpDevice().indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
InetAddress IPAddress = null;
|
||||
dataSendSocket = theSockets.get(hostPortion);
|
||||
if(dataSendSocket == null) {
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
try {
|
||||
IPAddress = InetAddress.getByName(hostAddr);
|
||||
} catch (UnknownHostException e) {
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("901", null, "Cannot connect, Unknown Host", null, "/lights/" + device.getId(), null).getTheErrors());
|
||||
}
|
||||
|
||||
try {
|
||||
dataSendSocket = new Socket(IPAddress, Integer.parseInt(port));
|
||||
if(theDevice.isPersistent())
|
||||
theSockets.put(hostPortion, dataSendSocket);
|
||||
} catch (Exception e) {
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("901", null, "Cannot connect, Socket Creation issue", null, "/lights/" + device.getId(), null).getTheErrors());
|
||||
}
|
||||
}
|
||||
|
||||
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
|
||||
if (theUrlBody.startsWith("0x")) {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
|
||||
theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device);
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
} else {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, false);
|
||||
theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device);
|
||||
theUrlBody = StringEscapeUtils.unescapeJava(theUrlBody);
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
|
||||
try {
|
||||
DataOutputStream outToClient = new DataOutputStream(dataSendSocket.getOutputStream());
|
||||
outToClient.write(sendData);
|
||||
outToClient.flush();
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not send data to TCP socket <<<" + e.getMessage() + ">>>, closing socket: " + theUrl);
|
||||
try {
|
||||
dataSendSocket.close();
|
||||
} catch (IOException e1) {
|
||||
// noop
|
||||
}
|
||||
dataSendSocket = null;
|
||||
if(theDevice.isPersistent())
|
||||
theSockets.remove(hostPortion);
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("901", null, "Cannot send data", null, "/lights/" + device.getId(), null).getTheErrors());
|
||||
}
|
||||
|
||||
if(!theDevice.isPersistent()) {
|
||||
try {
|
||||
if(dataSendSocket != null)
|
||||
dataSendSocket.close();
|
||||
} catch (IOException e1) {
|
||||
// noop
|
||||
}
|
||||
dataSendSocket = null;
|
||||
}
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
try {
|
||||
IPAddress = InetAddress.getByName(hostAddr);
|
||||
} catch (UnknownHostException e) {
|
||||
// noop
|
||||
}
|
||||
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
|
||||
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
|
||||
if (theUrlBody.startsWith("0x")) {
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
} else {
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
|
||||
try {
|
||||
Socket dataSendSocket = new Socket(IPAddress, Integer.parseInt(port));
|
||||
DataOutputStream outToClient = new DataOutputStream(dataSendSocket.getOutputStream());
|
||||
outToClient.write(sendData);
|
||||
outToClient.flush();
|
||||
dataSendSocket.close();
|
||||
} catch (Exception e) {
|
||||
// noop
|
||||
}
|
||||
log.warn("Tcp Call to be presented as tcp://<ip_address>:<port>/payload, format of request unknown: " + theUrl);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
log.info("TCP Home created.");
|
||||
theSockets = new HashMap<String, Socket>();
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -83,8 +137,18 @@ public class TCPHome implements Home {
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// noop
|
||||
|
||||
log.debug("Shutting down TCP sockets.");
|
||||
if(theSockets != null && !theSockets.isEmpty()) {
|
||||
Iterator<String> keys = theSockets.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
try {
|
||||
theSockets.get(key).close();
|
||||
} catch (IOException e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bwssystems.HABridge.plugins.tcp;
|
||||
|
||||
public class TcpDevice {
|
||||
private String tcpDevice;
|
||||
private boolean persistent;
|
||||
public String getTcpDevice() {
|
||||
return tcpDevice;
|
||||
}
|
||||
public void setTcpDevice(String tcpDevice) {
|
||||
this.tcpDevice = tcpDevice;
|
||||
}
|
||||
public boolean isPersistent() {
|
||||
return persistent;
|
||||
}
|
||||
public void setPersistent(boolean persistent) {
|
||||
this.persistent = persistent;
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,16 @@ import java.net.UnknownHostException;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.DeviceDataDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
import com.bwssystems.HABridge.util.UDPDatagramSender;
|
||||
@@ -23,7 +25,7 @@ public class UDPHome implements Home {
|
||||
private UDPDatagramSender theUDPDatagramSender;
|
||||
private byte[] sendData;
|
||||
|
||||
public UDPHome(BridgeSettingsDescriptor bridgeSettings, UDPDatagramSender aUDPDatagramSender) {
|
||||
public UDPHome(BridgeSettings bridgeSettings, UDPDatagramSender aUDPDatagramSender) {
|
||||
super();
|
||||
theUDPDatagramSender = aUDPDatagramSender;
|
||||
createHome(bridgeSettings);
|
||||
@@ -33,43 +35,52 @@ public class UDPHome implements Home {
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
log.debug("executing HUE api request to UDP: " + anItem.getItem().getAsString());
|
||||
String intermediate = anItem.getItem().getAsString().substring(anItem.getItem().getAsString().indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
InetAddress IPAddress = null;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && theUrl.startsWith("udp://")) {
|
||||
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
InetAddress IPAddress = null;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
try {
|
||||
IPAddress = InetAddress.getByName(hostAddr);
|
||||
} catch (UnknownHostException e) {
|
||||
log.warn("Udp Call, unknown host, continuing...");
|
||||
return null;
|
||||
}
|
||||
|
||||
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
|
||||
if (theUrlBody.startsWith("0x")) {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
|
||||
theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device);
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
} else {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, false);
|
||||
theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device);
|
||||
theUrlBody = StringEscapeUtils.unescapeJava(theUrlBody);
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
try {
|
||||
theUDPDatagramSender.sendUDPResponse(sendData, IPAddress, Integer.parseInt(port));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("Udp Call, Number format exception on port, continuing...");
|
||||
} catch (IOException e) {
|
||||
log.warn("IO exception on udp call, continuing...");
|
||||
}
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
try {
|
||||
IPAddress = InetAddress.getByName(hostAddr);
|
||||
} catch (UnknownHostException e) {
|
||||
log.warn("Udp Call, unknown host, continuing...");
|
||||
return null;
|
||||
}
|
||||
log.warn("Udp Call to be presented as udp://<ip_address>:<port>/payload, format of request unknown: " + theUrl);
|
||||
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
|
||||
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
|
||||
if (theUrlBody.startsWith("0x")) {
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
} else {
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
try {
|
||||
theUDPDatagramSender.sendUDPResponse(sendData, IPAddress, Integer.parseInt(port));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("Udp Call, Number format exception on port, continuing...");
|
||||
} catch (IOException e) {
|
||||
log.warn("IO exception on udp call, continuing...");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
log.info("UDP Home created.");
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
@@ -25,7 +25,7 @@ public class VeraHome implements Home {
|
||||
private Map<String, VeraInfo> veras;
|
||||
private Boolean validVera;
|
||||
|
||||
public VeraHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
public VeraHome(BridgeSettings bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
@@ -90,12 +90,12 @@ public class VeraHome implements Home {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
validVera = bridgeSettings.isValidVera();
|
||||
public Home createHome(BridgeSettings bridgeSettings) {
|
||||
validVera = bridgeSettings.getBridgeSettingsDescriptor().isValidVera();
|
||||
log.info("Vera Home created." + (validVera ? "" : " No Veras configured."));
|
||||
if(validVera) {
|
||||
veras = new HashMap<String, VeraInfo>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getVeraAddress().getDevices().iterator();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getVeraAddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aVera = theList.next();
|
||||
veras.put(aVera.getName(), new VeraInfo(aVera));
|
||||
@@ -106,7 +106,6 @@ public class VeraHome implements Home {
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
veras = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
[ng\:cloak],
|
||||
[ng-cloak],
|
||||
[data-ng-cloak],
|
||||
[x-ng-cloak],
|
||||
.ng-cloak,
|
||||
.x-ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
body {
|
||||
padding-top: 60px;
|
||||
padding-bottom: 20px;
|
||||
@@ -8,4 +16,55 @@ body {
|
||||
}
|
||||
.sortorder.reverse:after {
|
||||
content: '\25bc';
|
||||
}
|
||||
}
|
||||
.form-container {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.form-container form > div {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.form-container form > button {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
legend.form-label {
|
||||
font-size: 24pt;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.form-control.error {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
font-size: 10pt;
|
||||
line-height: 12pt;
|
||||
margin: -5px auto 5px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.form-hint.error {
|
||||
color: #C00;
|
||||
font-weight: bold;
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
.password-count {
|
||||
float: right;
|
||||
position: relative;
|
||||
bottom: 24px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.msg-block {
|
||||
margin-top:5px;
|
||||
}
|
||||
.msg-error {
|
||||
color:#F00;
|
||||
font-size:14px;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
.scrollableContainer {
|
||||
max-height: 436px; /* sets max-height value for all standards-compliant browsers */
|
||||
height: 310px;
|
||||
position: relative;
|
||||
padding-top: 35px;
|
||||
overflow: hidden;
|
||||
@@ -28,17 +28,17 @@
|
||||
}
|
||||
|
||||
.scrollArea {
|
||||
_height: expression( this.scrollHeight > 599 ? "600px" : "auto" ); /* sets max-height for IE6 */
|
||||
max-height: 400px; /* sets max-height value for all standards-compliant browsers */
|
||||
height: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #d5d5d5;
|
||||
/* the implementation of this is still quite buggy; specifically, it doesn't like the
|
||||
absolutely positioned headers within
|
||||
-webkit-overflow-scrolling: touch; */
|
||||
-webkit-overflow-scrolling: auto;
|
||||
}
|
||||
.scrollArea table {
|
||||
overflow-x: hidden;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 0;
|
||||
width: 100%;
|
||||
@@ -48,7 +48,7 @@
|
||||
.scrollArea table th {
|
||||
padding: 0 !important;
|
||||
border: none !important;
|
||||
min-width: 60px;
|
||||
min-width: 40px;
|
||||
}
|
||||
.scrollArea table .th-inner {
|
||||
overflow: hidden;
|
||||
|
||||
11
src/main/resources/public/css/strength-meter.min.css
vendored
Normal file
11
src/main/resources/public/css/strength-meter.min.css
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
* @copyright Copyright © Kartik Visweswaran, Krajee.com, 2015
|
||||
* @package yii2-password
|
||||
* @version 1.1.3
|
||||
*
|
||||
* Password Strength Meter
|
||||
* Modified and built for Yii Framework 2.0
|
||||
* Author: Kartik Visweswaran
|
||||
* Year: 2015
|
||||
* For more Yii related demos visit http://demos.krajee.com
|
||||
*/.kv-strength-container{width:100%;margin:0;padding:0;border:0}.kv-strength-container td{vertical-align:middle}.kv-meter-container{width:130px}.kv-meter{text-align:center}.kv-disabled{opacity:.65;cursor:not-allowed}.kv-scorebar-border{background:none repeat scroll 0 0 #333;border:1px solid #333;height:16px;width:100px;margin:0 auto;border-radius:4px}.kv-scorebar{background-image:url(../img/bg_strength_gradient.jpg);background-repeat:no-repeat;background-position:0 0;position:absolute;width:98px;height:14px;z-index:0;border-radius:2px}.kv-score{font-weight:700;font-size:75%;position:absolute;width:98px;z-index:10;border-radius:2px}.kv-score-0,.kv-score-1,.kv-score-5{color:#fff}.kv-score-2,.kv-score-3,.kv-score-4{color:#333}.kv-verdict{width:100%}
|
||||
BIN
src/main/resources/public/img/bg_strength_gradient.jpg
Normal file
BIN
src/main/resources/public/img/bg_strength_gradient.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 676 B |
@@ -13,6 +13,7 @@
|
||||
<link href="css/ngDialog.min.css" rel="stylesheet">
|
||||
<link href="css/ngDialog-theme-default.min.css" rel="stylesheet">
|
||||
<link href="css/scrollable-table.css" rel="stylesheet">
|
||||
<link href="css/strength-meter.min.css" rel="stylesheet">
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script type="text/javascript" src="js/html5shiv.min.js"></script>
|
||||
@@ -49,7 +50,7 @@
|
||||
<ul class="dropdown-menu" aria-labelledby="dLabel">
|
||||
<li><a href="http://www.bwssystems.com" target="_blank">Developed by BWS Systems</a></li>
|
||||
<li><a href="http://www.amazon.com/echo" target="_blank">Amazon Echo</a></li>
|
||||
<li><a href="">HA Bridge Version {{bridge.habridgeversion}}</a></li>
|
||||
<li><a href="">HA Bridge Version {{bridge.habridgeversion.version}}</a></li>
|
||||
<li><center>
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
@@ -62,12 +63,13 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#!/login">Login/Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div ng-view>
|
||||
<div ng-view prerender-action="authorized()">
|
||||
|
||||
|
||||
</div>
|
||||
@@ -81,6 +83,10 @@
|
||||
<script src="js/rzslider.min.js"></script>
|
||||
<script src="js/ngDialog.min.js"></script>
|
||||
<script src="js/angular-scrollable-table.min.js"></script>
|
||||
<script src="js/strength-meter.min.js"></script>
|
||||
<script src="js/angular-base64.min.js"></script>
|
||||
<script src="js/angular-resource.min.js"></script>
|
||||
<script src="js/ngStorage.min.js"></script>
|
||||
<script src="scripts/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
1
src/main/resources/public/js/angular-base64.min.js
vendored
Normal file
1
src/main/resources/public/js/angular-base64.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!function(){"use strict";angular.module("base64",[]).constant("$base64",function(){function a(a,b){var c=f.indexOf(a.charAt(b));if(-1==c)throw"Cannot decode base64";return c}function b(b){b=""+b;var c,d,f,g=b.length;if(0==g)return b;if(0!=g%4)throw"Cannot decode base64";c=0,b.charAt(g-1)==e&&(c=1,b.charAt(g-2)==e&&(c=2),g-=4);var h=[];for(d=0;g>d;d+=4)f=a(b,d)<<18|a(b,d+1)<<12|a(b,d+2)<<6|a(b,d+3),h.push(String.fromCharCode(f>>16,255&f>>8,255&f));switch(c){case 1:f=a(b,d)<<18|a(b,d+1)<<12|a(b,d+2)<<6,h.push(String.fromCharCode(f>>16,255&f>>8));break;case 2:f=a(b,d)<<18|a(b,d+1)<<12,h.push(String.fromCharCode(f>>16))}return h.join("")}function c(a,b){var c=a.charCodeAt(b);if(c>255)throw"INVALID_CHARACTER_ERR: DOM Exception 5";return c}function d(a){if(1!=arguments.length)throw"SyntaxError: Not enough arguments";var b,d,g=[];a=""+a;var h=a.length-a.length%3;if(0==a.length)return a;for(b=0;h>b;b+=3)d=c(a,b)<<16|c(a,b+1)<<8|c(a,b+2),g.push(f.charAt(d>>18)),g.push(f.charAt(63&d>>12)),g.push(f.charAt(63&d>>6)),g.push(f.charAt(63&d));switch(a.length-h){case 1:d=c(a,b)<<16,g.push(f.charAt(d>>18)+f.charAt(63&d>>12)+e+e);break;case 2:d=c(a,b)<<16|c(a,b+1)<<8,g.push(f.charAt(d>>18)+f.charAt(63&d>>12)+f.charAt(63&d>>6)+e)}return g.join("")}var e="=",f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";return{encode:d,decode:b}}())}();
|
||||
15
src/main/resources/public/js/angular-resource.min.js
vendored
Normal file
15
src/main/resources/public/js/angular-resource.min.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
AngularJS v1.6.1
|
||||
(c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(W,b){'use strict';function K(q,g){g=g||{};b.forEach(g,function(b,h){delete g[h]});for(var h in q)!q.hasOwnProperty(h)||"$"===h.charAt(0)&&"$"===h.charAt(1)||(g[h]=q[h]);return g}var B=b.$$minErr("$resource"),Q=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;b.module("ngResource",["ng"]).provider("$resource",function(){var q=/^https?:\/\/\[[^\]]*][^/]*/,g=this;this.defaults={stripTrailingSlashes:!0,cancellable:!1,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},
|
||||
"delete":{method:"DELETE"}}};this.$get=["$http","$log","$q","$timeout",function(h,P,L,M){function C(b,e){this.template=b;this.defaults=p({},g.defaults,e);this.urlParams={}}function x(D,e,u,m){function c(a,d){var c={};d=p({},e,d);t(d,function(d,l){y(d)&&(d=d(a));var f;if(d&&d.charAt&&"@"===d.charAt(0)){f=a;var k=d.substr(1);if(null==k||""===k||"hasOwnProperty"===k||!Q.test("."+k))throw B("badmember",k);for(var k=k.split("."),e=0,g=k.length;e<g&&b.isDefined(f);e++){var h=k[e];f=null!==f?f[h]:void 0}}else f=
|
||||
d;c[l]=f});return c}function R(a){return a.resource}function l(a){K(a||{},this)}var q=new C(D,m);u=p({},g.defaults.actions,u);l.prototype.toJSON=function(){var a=p({},this);delete a.$promise;delete a.$resolved;return a};t(u,function(a,d){var b=/^(POST|PUT|PATCH)$/i.test(a.method),e=a.timeout,g=N(a.cancellable)?a.cancellable:q.defaults.cancellable;e&&!S(e)&&(P.debug("ngResource:\n Only numeric values are allowed as `timeout`.\n Promises are not supported in $resource, because the same value would be used for multiple requests. If you are looking for a way to cancel requests, you should use the `cancellable` option."),
|
||||
delete a.timeout,e=null);l[d]=function(f,k,m,D){function u(a){r.catch(E);z.resolve(a)}var G={},v,w,A;switch(arguments.length){case 4:A=D,w=m;case 3:case 2:if(y(k)){if(y(f)){w=f;A=k;break}w=k;A=m}else{G=f;v=k;w=m;break}case 1:y(f)?w=f:b?v=f:G=f;break;case 0:break;default:throw B("badargs",arguments.length);}var F=this instanceof l,n=F?v:a.isArray?[]:new l(v),s={},C=a.interceptor&&a.interceptor.response||R,x=a.interceptor&&a.interceptor.responseError||void 0,H=!!A,I=!!x,z,J;t(a,function(a,d){switch(d){default:s[d]=
|
||||
T(a);case "params":case "isArray":case "interceptor":case "cancellable":}});!F&&g&&(z=L.defer(),s.timeout=z.promise,e&&(J=M(z.resolve,e)));b&&(s.data=v);q.setUrlParams(s,p({},c(v,a.params||{}),G),a.url);var r=h(s).then(function(f){var c=f.data;if(c){if(O(c)!==!!a.isArray)throw B("badcfg",d,a.isArray?"array":"object",O(c)?"array":"object",s.method,s.url);if(a.isArray)n.length=0,t(c,function(a){"object"===typeof a?n.push(new l(a)):n.push(a)});else{var b=n.$promise;K(c,n);n.$promise=b}}f.resource=n;
|
||||
return f}),r=r["finally"](function(){n.$resolved=!0;!F&&g&&(n.$cancelRequest=E,M.cancel(J),z=J=s.timeout=null)}),r=r.then(function(a){var d=C(a);(w||E)(d,a.headers,a.status,a.statusText);return d},H||I?function(a){H&&A(a);return I?x(a):L.reject(a)}:void 0);H&&!I&&r.catch(E);return F?r:(n.$promise=r,n.$resolved=!1,g&&(n.$cancelRequest=u),n)};l.prototype["$"+d]=function(a,c,b){y(a)&&(b=c,c=a,a={});a=l[d].call(this,a,this,c,b);return a.$promise||a}});l.bind=function(a){a=p({},e,a);return x(D,a,u,m)};
|
||||
return l}var E=b.noop,t=b.forEach,p=b.extend,T=b.copy,O=b.isArray,N=b.isDefined,y=b.isFunction,S=b.isNumber,U=b.$$encodeUriQuery,V=b.$$encodeUriSegment;C.prototype={setUrlParams:function(b,e,g){var m=this,c=g||m.template,h,l,p="",a=m.urlParams=Object.create(null);t(c.split(/\W/),function(d){if("hasOwnProperty"===d)throw B("badname");!/^\d+$/.test(d)&&d&&(new RegExp("(^|[^\\\\]):"+d+"(\\W|$)")).test(c)&&(a[d]={isQueryParamValue:(new RegExp("\\?.*=:"+d+"(?:\\W|$)")).test(c)})});c=c.replace(/\\:/g,":");
|
||||
c=c.replace(q,function(a){p=a;return""});e=e||{};t(m.urlParams,function(a,b){h=e.hasOwnProperty(b)?e[b]:m.defaults[b];N(h)&&null!==h?(l=a.isQueryParamValue?U(h,!0):V(h),c=c.replace(new RegExp(":"+b+"(\\W|$)","g"),function(a,b){return l+b})):c=c.replace(new RegExp("(/?):"+b+"(\\W|$)","g"),function(a,b,d){return"/"===d.charAt(0)?d:b+d})});m.defaults.stripTrailingSlashes&&(c=c.replace(/\/+$/,"")||"/");c=c.replace(/\/\.(?=\w+($|\?))/,".");b.url=p+c.replace(/\/\\\./,"/.");t(e,function(a,c){m.urlParams[c]||
|
||||
(b.params=b.params||{},b.params[c]=a)})}};return x}]})})(window,window.angular);
|
||||
//# sourceMappingURL=angular-resource.min.js.map
|
||||
8
src/main/resources/public/js/angular-resource.min.js.map
Normal file
8
src/main/resources/public/js/angular-resource.min.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/main/resources/public/js/ngStorage.min.js
vendored
Normal file
1
src/main/resources/public/js/ngStorage.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/*! ngstorage 0.3.10 | Copyright (c) 2016 Gias Kay Lee | MIT License */!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):a.hasOwnProperty("angular")?b(a.angular):"object"==typeof exports&&(module.exports=b(require("angular")))}(this,function(a){"use strict";function b(a,b){var c;try{c=a[b]}catch(d){c=!1}if(c){var e="__"+Math.round(1e7*Math.random());try{a[b].setItem(e,e),a[b].removeItem(e,e)}catch(d){c=!1}}return c}function c(c){var d=b(window,c);return function(){var e="ngStorage-";this.setKeyPrefix=function(a){if("string"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setKeyPrefix() expects a String.");e=a};var f=a.toJson,g=a.fromJson;this.setSerializer=function(a){if("function"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setSerializer expects a function.");f=a},this.setDeserializer=function(a){if("function"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setDeserializer expects a function.");g=a},this.supported=function(){return!!d},this.get=function(a){return d&&g(d.getItem(e+a))},this.set=function(a,b){return d&&d.setItem(e+a,f(b))},this.remove=function(a){d&&d.removeItem(e+a)},this.$get=["$rootScope","$window","$log","$timeout","$document",function(d,h,i,j,k){var l,m,n=e.length,o=b(h,c),p=o||(i.warn("This browser does not support Web Storage!"),{setItem:a.noop,getItem:a.noop,removeItem:a.noop}),q={$default:function(b){for(var c in b)a.isDefined(q[c])||(q[c]=a.copy(b[c]));return q.$sync(),q},$reset:function(a){for(var b in q)"$"===b[0]||delete q[b]&&p.removeItem(e+b);return q.$default(a)},$sync:function(){for(var a,b=0,c=p.length;c>b;b++)(a=p.key(b))&&e===a.slice(0,n)&&(q[a.slice(n)]=g(p.getItem(a)))},$apply:function(){var b;if(m=null,!a.equals(q,l)){b=a.copy(l),a.forEach(q,function(c,d){a.isDefined(c)&&"$"!==d[0]&&(p.setItem(e+d,f(c)),delete b[d])});for(var c in b)p.removeItem(e+c);l=a.copy(q)}},$supported:function(){return!!o}};return q.$sync(),l=a.copy(q),d.$watch(function(){m||(m=j(q.$apply,100,!1))}),h.addEventListener&&h.addEventListener("storage",function(b){if(b.key){var c=k[0];c.hasFocus&&c.hasFocus()||e!==b.key.slice(0,n)||(b.newValue?q[b.key.slice(n)]=g(b.newValue):delete q[b.key.slice(n)],l=a.copy(q),d.$apply())}}),h.addEventListener&&h.addEventListener("beforeunload",function(){q.$apply()}),q}]}}return a=a&&a.module?a:window.angular,a.module("ngStorage",[]).provider("$localStorage",c("localStorage")).provider("$sessionStorage",c("sessionStorage"))});
|
||||
8
src/main/resources/public/js/strength-meter.min.js
vendored
Normal file
8
src/main/resources/public/js/strength-meter.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1,144 +1,149 @@
|
||||
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#!/">Bridge
|
||||
Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">Current devices ({{bridge.devices.length}})</h1>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<p>
|
||||
<button class="btn btn-primary" type="submit" ng-click="renumberDevices()">Renumber Devices</button>
|
||||
</p>
|
||||
|
||||
<scrollable-table watch="bridge.devices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorUniqueId">ID</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="deviceType">Type</th>
|
||||
<th sortable-header col="targetDevice">Target</th>
|
||||
<th sortable-header col="inactive">Inactive</th>
|
||||
<th sortable-header col="noState">No State</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="device in bridge.devices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.id}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.deviceType}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.inactive}}</td>
|
||||
<td>{{device.noState}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-info" type="submit"
|
||||
ng-click="testUrl(device, 'on')">Test ON</button>
|
||||
<button class="btn btn-info" type="submit"
|
||||
ng-click="testUrl(device, 'dim')">Test Dim</button>
|
||||
<button class="btn btn-info" type="submit"
|
||||
ng-click="testUrl(device, 'off')">Test OFF</button>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit/Copy</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">
|
||||
Bridge Device DB Backup <a ng-click="toggleBk()"><span
|
||||
class={{imgBkUrl}} aria-hidden="true"></span></a>
|
||||
</h1>
|
||||
</div>
|
||||
<div ng-if="visibleBk" class="panel-body">
|
||||
<p>Control your backups from this area. Use the default name by hitting backup or specify your own.</p>
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="backup-name">Backup
|
||||
File Name</label>
|
||||
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<input id="backup-name" class="form-control" type="text"
|
||||
ng-model="optionalbackupname" placeholder="Optional">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"
|
||||
ng-click="backupDeviceDb(optionalbackupname)">Backup
|
||||
Device DB</button>
|
||||
</div>
|
||||
</form>
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Filename</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="backup in bridge.backups">
|
||||
<td>{{backup}}</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="restoreBackup(backup)">Restore</button>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="deleteBackup(backup)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/ng-template" id="valueDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Select value</h2>
|
||||
<p>
|
||||
<input type="radio" ng-model="valueType" value="percentage" ng-change="changeScale()"> Percentage
|
||||
<input type="radio" ng-model="valueType" value="raw" ng-change="changeScale()"> Raw
|
||||
</p>
|
||||
<p>
|
||||
<rzslider rz-slider-model="slider.value" rz-slider-options="slider.options"></rzslider>
|
||||
</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-primary" ng-click="setValue()">Set</button>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/ng-template" id="deleteDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device to Delete?</h2>
|
||||
<p>{{device.name}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteDevice(device)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#!/">Bridge
|
||||
Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
<div postrender-action="goToRow()">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">Current devices ({{bridge.devices.length}})</h1>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<p>
|
||||
<button class="btn btn-primary" type="submit" ng-click="renumberDevices()">Renumber Devices</button>
|
||||
<button ng-if="bridge.securityInfo.useLinkButton" class="btn btn-primary" type="submit" ng-click="pushLinkButton()">Link</button>
|
||||
</p>
|
||||
<scrollable-table watch="bridge.devices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorUniqueId">ID</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="description">Description</th>
|
||||
<th sortable-header col="deviceType">Type</th>
|
||||
<th sortable-header col="targetDevice">Target</th>
|
||||
<th sortable-header col="inactive">Inactive</th>
|
||||
<th sortable-header col="noState">No State</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="device in bridge.devices" row-id="{{device.id}}" ng-class="{info: bridge.viewDevId == device.id}" >
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.id}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td class="cr">{{device.description}}</td>
|
||||
<td>{{device.deviceType}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.inactive}}</td>
|
||||
<td>{{device.noState}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-info" type="submit"
|
||||
ng-click="testUrl(device, 'on')">Test ON</button>
|
||||
<button class="btn btn-info" type="submit"
|
||||
ng-click="testUrl(device, 'dim')">Test Dim</button>
|
||||
<button class="btn btn-info" type="submit"
|
||||
ng-click="testUrl(device, 'off')">Test OFF</button>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit/Copy</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">
|
||||
Bridge Device DB Backup <a ng-click="toggleBk()"><span
|
||||
class={{imgBkUrl}} aria-hidden="true"></span></a>
|
||||
</h1>
|
||||
</div>
|
||||
<div ng-if="visibleBk" class="panel-body">
|
||||
<p>Control your backups from this area. Use the default name by hitting backup or specify your own.</p>
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="backup-name">Backup
|
||||
File Name</label>
|
||||
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<input id="backup-name" class="form-control" type="text"
|
||||
ng-model="optionalbackupname" placeholder="Optional">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"
|
||||
ng-click="backupDeviceDb(optionalbackupname)">Backup
|
||||
Device DB</button>
|
||||
</div>
|
||||
</form>
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Filename</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="backup in bridge.backups">
|
||||
<td>{{backup}}</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="restoreBackup(backup)">Restore</button>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="deleteBackup(backup)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/ng-template" id="valueDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Select value</h2>
|
||||
<p>
|
||||
<input type="radio" ng-model="valueType" value="percentage" ng-change="changeScale()"> Percentage
|
||||
<input type="radio" ng-model="valueType" value="raw" ng-change="changeScale()"> Raw
|
||||
</p>
|
||||
<p>
|
||||
<rzslider rz-slider-model="slider.value" rz-slider-options="slider.options"></rzslider>
|
||||
</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-primary" ng-click="setValue()">Set</button>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/ng-template" id="deleteDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device to Delete?</h2>
|
||||
<p>{{device.name}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteDevice(device)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -35,17 +37,18 @@
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Domoticz Devices' list below will show what
|
||||
is already setup for your Domoticz.</p>
|
||||
<p>
|
||||
<p class="text-muted">
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.decimal_percent}">Decimal Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on and off or dim and
|
||||
off if selected with the name of the device from the Domoticz.</p>
|
||||
@@ -75,7 +78,7 @@
|
||||
<td>{{domoticzdevice.domoticzname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(domoticzdevice, device_dim_control)">Build Item</button>
|
||||
ng-click="buildDeviceUrls(domoticzdevice, device_dim_control,false)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
<li ng-if="bridge.showHass" role="presentation"><a
|
||||
href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -32,7 +34,7 @@
|
||||
fields the bridge uses. Please use care when updating these fields as
|
||||
you may break the settings used by the bridge to call a specific end
|
||||
point device.</p>
|
||||
<p class="text-muted">This area allows you to create any http or
|
||||
<p class="text-muted">This area allows you to create any http, tcp or
|
||||
udp call to an endpoint. You can use the default GET or select the
|
||||
http verb type below and configure a payload for either on, dim or
|
||||
off methods. For Execution of a
|
||||
@@ -40,10 +42,10 @@
|
||||
can use Json notation of array with [{"item":"the
|
||||
payload"},{"item":"another payload"}] to
|
||||
execute multiple entries. Adding the value replacements
|
||||
(${intensity..byte},${intensity.percent},${intensity.math(X*1)}) will
|
||||
(${intensity.byte},${intensity.percent},${intensity.decimal_percent},${intensity.math(X*1)}) will
|
||||
also work. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command.</p>
|
||||
<p>When copying, update the name and select the "Add Bridge
|
||||
<p class="text-muted">When copying, update the name and select the "Add Bridge
|
||||
Device" Button.</p>
|
||||
|
||||
<form name="form">
|
||||
@@ -64,17 +66,40 @@
|
||||
ng-model="device.name" placeholder="Device Name"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Inactive</td>
|
||||
<td><label>Description</label></td>
|
||||
|
||||
<td><input type="text" class="form-control" id="device-description"
|
||||
ng-model="device.description" placeholder="Device Description"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Comments</label></td>
|
||||
|
||||
<td><input type="text" class="form-control" id="device-comments"
|
||||
ng-model="device.comments" placeholder="Device Comments"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Inactive</label></td>
|
||||
<td><input type="checkbox"
|
||||
ng-model="device.inactive" ng-true-value=true
|
||||
ng-false-value=false> {{device.inactive}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No State (Do not update state for device)</td>
|
||||
<td><label>No State (Do not update state for device)</label></td>
|
||||
<td><input type="checkbox"
|
||||
ng-model="device.noState" ng-true-value=true
|
||||
ng-false-value=false> {{device.noState}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Off State Resets Bri</label></td>
|
||||
<td><input type="checkbox"
|
||||
ng-model="device.offState" ng-true-value=true
|
||||
ng-false-value=false> {{device.offState}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Filter Address (comma separated list)</label></td>
|
||||
<td><input type="text" class="form-control" id="device-requester-addr"
|
||||
ng-model="device.requesterAddress" placeholder="Only use if you want to restrict this device to a specific caller(s)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Target</label></td>
|
||||
|
||||
@@ -129,6 +154,7 @@
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<th>Type</th>
|
||||
<th>Target Item</th>
|
||||
<th>Delay</th>
|
||||
@@ -139,19 +165,21 @@
|
||||
<th>Http Headers</th>
|
||||
<th>Content Type</th>
|
||||
<th>Manage</th>
|
||||
</div>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="onItem in onDevices">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="onItem.type"></select></td>
|
||||
<td><textarea rows="1" cols="20" class="form-control"
|
||||
<td><textarea rows="1" class="form-control"
|
||||
id="item-target" ng-model="onItem.item" placeholder="The Call"></textarea></td>
|
||||
<td><textarea rows="1" cols="4" class="form-control"
|
||||
<td><textarea rows="1" class="form-control"
|
||||
id="item-delay" ng-model="onItem.delay" placeholder="millis"></textarea></td>
|
||||
<td><textarea rows="1" cols="2" class="form-control"
|
||||
<td><textarea rows="1" class="form-control"
|
||||
id="item-count" ng-model="onItem.count" placeholder="number"></textarea></td>
|
||||
<td><textarea rows="1" cols="16" class="form-control"
|
||||
<td><textarea rows="1" class="form-control"
|
||||
id="item-filterIPs" ng-model="onItem.filterIPs"
|
||||
placeholder="restrict IPs"></textarea></td>
|
||||
<td><select name="item-http-verb" id="item-http-verb"
|
||||
@@ -162,7 +190,7 @@
|
||||
<option value="PUT">PUT</option>
|
||||
<option value="POST">POST</option>
|
||||
</select></td>
|
||||
<td><textarea rows="1" cols="16" class="form-control"
|
||||
<td><textarea rows="1" class="form-control"
|
||||
id="item-httpBody" ng-model="onItem.httpBody"
|
||||
placeholder="body args"></textarea></td>
|
||||
<td><textarea rows="1" cols="16" class="form-control"
|
||||
@@ -188,8 +216,10 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="removeItemOn(onItem)">Del</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="newOnItem.type"></select></td>
|
||||
@@ -239,6 +269,7 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-success" type="submit"
|
||||
ng-click="addItemOn(newOnItem)">Add</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table></td>
|
||||
@@ -250,6 +281,7 @@
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<th>Type</th>
|
||||
<th>Target Item</th>
|
||||
<th>Delay</th>
|
||||
@@ -260,9 +292,11 @@
|
||||
<th>Http Headers</th>
|
||||
<th>Content Type</th>
|
||||
<th>Manage</th>
|
||||
</div>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="dimItem in dimDevices">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="dimItem.type"></select></td>
|
||||
@@ -310,8 +344,10 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="removeItemDim(dimItem)">Del</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="newDimItem.type"></select></td>
|
||||
@@ -361,6 +397,7 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-success" type="submit"
|
||||
ng-click="addItemDim(newDimItem)">Add</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table></td>
|
||||
@@ -372,6 +409,7 @@
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<th>Type</th>
|
||||
<th>Target Item</th>
|
||||
<th>Delay</th>
|
||||
@@ -382,9 +420,11 @@
|
||||
<th>Http Headers</th>
|
||||
<th>Content Type</th>
|
||||
<th>Manage</th>
|
||||
</div>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="offItem in offDevices">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="offItem.type"></select></td>
|
||||
@@ -432,8 +472,10 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="removeItemOff(offItem)">Del</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="newOffItem.type"></select></td>
|
||||
@@ -483,6 +525,7 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-success" type="submit"
|
||||
ng-click="addItemOff(newOffItem)">Add</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table></td>
|
||||
|
||||
@@ -1,176 +1,179 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/haldevices">HAL
|
||||
Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">HAL Device List
|
||||
({{bridge.haldevices.length}})</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any HAL Device, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured HAL Devices' list below will show what
|
||||
is already setup for your HAL.</p>
|
||||
<p>
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on and off or dim and
|
||||
off if selected with the name of the device from the HAL.</p>
|
||||
<scrollable-table watch="bridge.haldevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">
|
||||
<span><input type="checkbox" name="selectAll"
|
||||
value="{{selectAll}}"
|
||||
ng-checked="selectAll"
|
||||
ng-click="toggleSelectAll()"> Name</span></th>
|
||||
<th sortable-header col="category">Category</th>
|
||||
<th sortable-header col="halname">HAL</th>
|
||||
<th>On Button</th>
|
||||
<th>Off Button</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="haldevice in bridge.haldevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]"
|
||||
value="{{haldevice.haldevicename}}"
|
||||
ng-checked="bulk.devices.indexOf(haldevice.haldevicename) > -1"
|
||||
ng-click="toggleSelection(haldevice.haldevicename)">
|
||||
{{haldevice.haldevicename}}</td>
|
||||
<td>{{haldevice.haldevicetype}}</td>
|
||||
<td>{{haldevice.halname}}</td>
|
||||
<td>
|
||||
<select name="button-on" id="button-on" ng-model="button_on">
|
||||
<option ng-repeat="aButtonOn in haldevice.buttons.DeviceElements"
|
||||
value="{{aButtonOn}}">{{aButtonOn.DeviceName}}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select name="button-off" id="button-off" ng-model="button_off">
|
||||
<option ng-repeat="aButtonOff in haldevice.buttons.DeviceElements"
|
||||
value="{{aButtonOff}}">{{aButtonOff.DeviceName}}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button ng-if="haldevice.haldevicetype != 'Home' && haldevice.haldevicetype != 'HVAC' && haldevice.haldevicetype != 'IrData'" class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(haldevice, device_dim_control)">Build Item</button>
|
||||
<button ng-if="haldevice.haldevicetype == 'Home'" class="btn btn-success" type="submit"
|
||||
ng-click="buildHALHomeUrls(haldevice)">Build Home/Away</button>
|
||||
<button ng-if="haldevice.haldevicetype == 'IrData'" class="btn btn-success" type="submit"
|
||||
ng-click="buildButtonUrls(haldevice, button_on, button_off)">Build
|
||||
A Button</button>
|
||||
<ul ng-if="haldevice.haldevicetype == 'HVAC'" class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALHeatUrls(haldevice)">Heat</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALCoolUrls(haldevice)">Cool</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALAutoUrls(haldevice)">Auto</button>
|
||||
</p>
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALOffUrls(haldevice)">Off</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALFanUrls(haldevice)">Fan</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkAddDevices(device_dim_control)">Bulk Add
|
||||
({{bulk.devices.length}})</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured HAL Devices <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
|
||||
<scrollable-table watch="bridge.haldevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="category">Category</th>
|
||||
<th sortable-header col="halname">HAL</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredHalItems">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.deviceType}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/haldevices">HAL
|
||||
Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">HAL Device List
|
||||
({{bridge.haldevices.length}})</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any HAL Device, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured HAL Devices' list below will show what
|
||||
is already setup for your HAL.</p>
|
||||
<p class="text-muted">
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.decimal_percent}">Decimal Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on and off or dim and
|
||||
off if selected with the name of the device from the HAL.</p>
|
||||
<scrollable-table watch="bridge.haldevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">
|
||||
<span><input type="checkbox" name="selectAll"
|
||||
value="{{selectAll}}"
|
||||
ng-checked="selectAll"
|
||||
ng-click="toggleSelectAll()"> Name</span></th>
|
||||
<th sortable-header col="category">Category</th>
|
||||
<th sortable-header col="halname">HAL</th>
|
||||
<th>On Button</th>
|
||||
<th>Off Button</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="haldevice in bridge.haldevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]"
|
||||
value="{{haldevice.haldevicename}}"
|
||||
ng-checked="bulk.devices.indexOf(haldevice.haldevicename) > -1"
|
||||
ng-click="toggleSelection(haldevice.haldevicename)">
|
||||
{{haldevice.haldevicename}}</td>
|
||||
<td>{{haldevice.haldevicetype}}</td>
|
||||
<td>{{haldevice.halname}}</td>
|
||||
<td>
|
||||
<select name="button-on" id="button-on" ng-model="button_on">
|
||||
<option ng-repeat="aButtonOn in haldevice.buttons.DeviceElements"
|
||||
value="{{aButtonOn}}">{{aButtonOn.DeviceName}}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select name="button-off" id="button-off" ng-model="button_off">
|
||||
<option ng-repeat="aButtonOff in haldevice.buttons.DeviceElements"
|
||||
value="{{aButtonOff}}">{{aButtonOff.DeviceName}}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button ng-if="haldevice.haldevicetype != 'Home' && haldevice.haldevicetype != 'HVAC' && haldevice.haldevicetype != 'IrData'" class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(haldevice, device_dim_control, false)">Build Item</button>
|
||||
<button ng-if="haldevice.haldevicetype == 'Home'" class="btn btn-success" type="submit"
|
||||
ng-click="buildHALHomeUrls(haldevice, false)">Build Home/Away</button>
|
||||
<button ng-if="haldevice.haldevicetype == 'IrData'" class="btn btn-success" type="submit"
|
||||
ng-click="buildButtonUrls(haldevice, button_on, button_off, false)">Build
|
||||
A Button</button>
|
||||
<ul ng-if="haldevice.haldevicetype == 'HVAC'" class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALHeatUrls(haldevice, false)">Heat</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALCoolUrls(haldevice, false)">Cool</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALAutoUrls(haldevice, false)">Auto</button>
|
||||
</p>
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALOffUrls(haldevice, false)">Off</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALFanUrls(haldevice, false)">Fan</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkAddDevices(device_dim_control)">Bulk Add
|
||||
({{bulk.devices.length}})</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured HAL Devices <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
|
||||
<scrollable-table watch="bridge.haldevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="category">Category</th>
|
||||
<th sortable-header col="halname">HAL</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredHalItems">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.deviceType}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
@@ -1,111 +1,113 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li role="presentation" class="active"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li role="presentation"><a href="#!/harmonydevices">Harmony
|
||||
Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Harmony Activity List</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Harmony Activity, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen.
|
||||
Then you can modify the name to anything you want that
|
||||
will be the keyword for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to
|
||||
finish that selection setup. The 'Already Configured Activities' list
|
||||
below will show what is already setup for your Harmony Hubs.</p>
|
||||
|
||||
<scrollable-table watch="bridge.harmonyactivities">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="label" comparator-fn="comparatorLabel">Name</th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorNumber">Id</th>
|
||||
<th sortable-header col="hub" comparator-fn="comparatorHub">Hub</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="harmonyactivity in bridge.harmonyactivities">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{harmonyactivity.activity.label}}</td>
|
||||
<td>{{harmonyactivity.activity.id}}</td>
|
||||
<td>{{harmonyactivity.hub}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildActivityUrls(harmonyactivity)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Activities <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.harmonyactivities">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Hub</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredHarmonyActivities">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li role="presentation" class="active"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li role="presentation"><a href="#!/harmonydevices">Harmony
|
||||
Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Harmony Activity List</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Harmony Activity, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen.
|
||||
Then you can modify the name to anything you want that
|
||||
will be the keyword for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to
|
||||
finish that selection setup. The 'Already Configured Activities' list
|
||||
below will show what is already setup for your Harmony Hubs.</p>
|
||||
|
||||
<scrollable-table watch="bridge.harmonyactivities">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="label" comparator-fn="comparatorLabel">Name</th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorNumber">Id</th>
|
||||
<th sortable-header col="hub" comparator-fn="comparatorHub">Hub</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="harmonyactivity in bridge.harmonyactivities">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{harmonyactivity.activity.label}}</td>
|
||||
<td>{{harmonyactivity.activity.id}}</td>
|
||||
<td>{{harmonyactivity.hub}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildActivityUrls(harmonyactivity)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Activities <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.harmonyactivities">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Hub</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredHarmonyActivities">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
@@ -1,130 +1,132 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li role="presentation"><a href="#!/harmonyactivities">Harmony
|
||||
Activities</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/harmonydevices">Harmony
|
||||
Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Harmony Device List</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Harmony Device and Buttons, use the
|
||||
build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then you can modify the name
|
||||
to anything you want that will be the keyword for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the
|
||||
'Add Bridge Device' to finish that selection setup. The 'Already
|
||||
Configured Harmony Buttons' list below will show what is already
|
||||
setup for your Harmony Hubs.</p>
|
||||
|
||||
<scrollable-table watch="bridge.harmonydevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="label" comparator-fn="comparatorLabel">Name</th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorNumber">Id</th>
|
||||
<th sortable-header col="hub" comparator-fn="comparatorHub">Hub</th>
|
||||
<th>On Button</th>
|
||||
<th>Off Button</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="harmonydevice in bridge.harmonydevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{harmonydevice.device.label}}</td>
|
||||
<td>{{harmonydevice.device.id}}</td>
|
||||
<td>{{harmonydevice.hub}}</td>
|
||||
<td><select name="device-ctrlon" id="device-ctrlon"
|
||||
ng-model="devicectrlon">
|
||||
<optgroup ng-repeat="ctrlon in harmonydevice.device.controlGroup"
|
||||
label="{{ctrlon.name}}">
|
||||
<option ng-repeat="funcon in ctrlon.function"
|
||||
value="{{funcon.action}}">{{funcon.label}}</option>
|
||||
</optgroup>
|
||||
</select></td>
|
||||
<td><select name="device-ctrloff" id="device-ctrloff"
|
||||
ng-model="devicectrloff">
|
||||
<optgroup ng-repeat="ctrloff in harmonydevice.device.controlGroup"
|
||||
label="{{ctrloff.name}}">
|
||||
<option ng-repeat="funcoff in ctrloff.function"
|
||||
value="{{funcoff.action}}">{{funcoff.label}}</option>
|
||||
</optgroup>
|
||||
</select></td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildButtonUrls(harmonydevice, devicectrlon, devicectrloff)">Build
|
||||
A Button</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Harmony Buttons <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.harmonydevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Hub</th>
|
||||
<th>Harmony Device-Button On-Button Off</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredHarmonyButtons | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li role="presentation"><a href="#!/harmonyactivities">Harmony
|
||||
Activities</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/harmonydevices">Harmony
|
||||
Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Harmony Device List</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Harmony Device and Buttons, use the
|
||||
build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then you can modify the name
|
||||
to anything you want that will be the keyword for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the
|
||||
'Add Bridge Device' to finish that selection setup. The 'Already
|
||||
Configured Harmony Buttons' list below will show what is already
|
||||
setup for your Harmony Hubs.</p>
|
||||
|
||||
<scrollable-table watch="bridge.harmonydevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="label" comparator-fn="comparatorLabel">Name</th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorNumber">Id</th>
|
||||
<th sortable-header col="hub" comparator-fn="comparatorHub">Hub</th>
|
||||
<th>On Button</th>
|
||||
<th>Off Button</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="harmonydevice in bridge.harmonydevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{harmonydevice.device.label}}</td>
|
||||
<td>{{harmonydevice.device.id}}</td>
|
||||
<td>{{harmonydevice.hub}}</td>
|
||||
<td><select name="device-ctrlon" id="device-ctrlon"
|
||||
ng-model="devicectrlon">
|
||||
<optgroup ng-repeat="ctrlon in harmonydevice.device.controlGroup"
|
||||
label="{{ctrlon.name}}">
|
||||
<option ng-repeat="funcon in ctrlon.function"
|
||||
value="{{funcon.action}}">{{funcon.label}}</option>
|
||||
</optgroup>
|
||||
</select></td>
|
||||
<td><select name="device-ctrloff" id="device-ctrloff"
|
||||
ng-model="devicectrloff">
|
||||
<optgroup ng-repeat="ctrloff in harmonydevice.device.controlGroup"
|
||||
label="{{ctrloff.name}}">
|
||||
<option ng-repeat="funcoff in ctrloff.function"
|
||||
value="{{funcoff.action}}">{{funcoff.label}}</option>
|
||||
</optgroup>
|
||||
</select></td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildButtonUrls(harmonydevice, devicectrlon, devicectrloff)">Build
|
||||
A Button</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Harmony Buttons <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.harmonydevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Hub</th>
|
||||
<th>Harmony Device-Button On-Button Off</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredHarmonyButtons | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
@@ -13,11 +13,13 @@
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation" class="active"><a href="#!/haldevices">HAL
|
||||
<li ng-if="bridge.showHal" role="presentation"><a href="#!/haldevices">HAL
|
||||
Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -35,17 +37,18 @@
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured HomeAssistant Devices' list below will show what
|
||||
is already setup for your HomeAssitant.</p>
|
||||
<p>
|
||||
<p class="text-muted">
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated, BUT for Home Assistant, the selection should be Pass-thru: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.decimal_percent}">Decimal Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on and off or dim and
|
||||
off if selected with the name of the device from the HomeAssitant.</p>
|
||||
@@ -72,26 +75,8 @@
|
||||
{{hassdevice.deviceState.entity_id}}</td>
|
||||
<td>{{hassdevice.hassname}}</td>
|
||||
<td>
|
||||
<button ng-if="hassdevice.domain != 'climate' && hassdevice.domain != 'sensor' && hassdevice.domain != 'sun'" class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(hassdevice, device_dim_control)">Build Item</button>
|
||||
<ul ng-if="hassdevice.domain == 'climate'" class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildhassHeatUrls(hassdevice)">Heat</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildhassCoolUrls(hassdevice)">Cool</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildhassAutoUrls(hassdevice)">Auto</button>
|
||||
</p>
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildhassOffUrls(hassdevice)">Off</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildhassFanUrls(hassdevice)">Fan</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<button ng-if="hassdevice.domain != 'sensor' && hassdevice.domain != 'sun'" class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(hassdevice, device_dim_control, false)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -1,126 +1,128 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/huedevices">Hue
|
||||
Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Hue Device List
|
||||
({{bridge.huedevices.length}})</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Hue Device, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Hue Devices' list below will show what
|
||||
is already setup for your Hue.</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items, then click bulk add below. Your items
|
||||
will be added with on and off or dim and off if selected with the
|
||||
name of the device from the Hue.</p>
|
||||
<scrollable-table watch="bridge.huedevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name"><span><input type="checkbox" name="selectAll"
|
||||
value="{{selectAll}}"
|
||||
ng-checked="selectAll"
|
||||
ng-click="toggleSelectAll()"> Name</span></th>
|
||||
<th sortable-header col="id">Id</th>
|
||||
<th sortable-header col="huename">Hue</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="huedevice in bridge.huedevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]"
|
||||
value="{{huedevice.device.uniqueid}}"
|
||||
ng-checked="bulk.devices.indexOf(huedevice.device.uniqueid) > -1"
|
||||
ng-click="toggleSelection(huedevice.device.uniqueid)">
|
||||
{{huedevice.device.name}}</td>
|
||||
<td>{{huedevice.device.uniqueid}}</td>
|
||||
<td>{{huedevice.huename}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(huedevice)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkAddDevices()">Bulk Add
|
||||
({{bulk.devices.length}})</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Hue Devices <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.huedevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">hue</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredHueItems">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/huedevices">Hue
|
||||
Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Hue Device List
|
||||
({{bridge.huedevices.length}})</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Hue Device, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Hue Devices' list below will show what
|
||||
is already setup for your Hue.</p>
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items, then click bulk add below. Your items
|
||||
will be added with on, off and dim with the
|
||||
name of the device from the Hue.</p>
|
||||
<scrollable-table watch="bridge.huedevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name"><span><input type="checkbox" name="selectAll"
|
||||
value="{{selectAll}}"
|
||||
ng-checked="selectAll"
|
||||
ng-click="toggleSelectAll()"> Name</span></th>
|
||||
<th sortable-header col="id">Id</th>
|
||||
<th sortable-header col="huename">Hue</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="huedevice in bridge.huedevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]"
|
||||
value="{{huedevice.device.uniqueid}}"
|
||||
ng-checked="bulk.devices.indexOf(huedevice.device.uniqueid) > -1"
|
||||
ng-click="toggleSelection(huedevice.device.uniqueid)">
|
||||
{{huedevice.device.name}}</td>
|
||||
<td>{{huedevice.device.uniqueid}}</td>
|
||||
<td>{{huedevice.huename}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(huedevice, false)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkAddDevices()">Bulk Add
|
||||
({{bulk.devices.length}})</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Hue Devices <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.huedevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">hue</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredHueItems">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
123
src/main/resources/public/views/lifxdevice.html
Normal file
123
src/main/resources/public/views/lifxdevice.html
Normal file
@@ -0,0 +1,123 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">LIFX Devices</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any LIFX Device, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured LIFX Devices' list below will show what
|
||||
is already setup for your LIFX.</p>
|
||||
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on, off and dim
|
||||
with the name of the device from the LIFX device.</p>
|
||||
<scrollable-table watch="bridge.lifxdevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">
|
||||
<span><input type="checkbox" name="selectAll"
|
||||
value="{{selectAll}}"
|
||||
ng-checked="selectAll"
|
||||
ng-click="toggleSelectAll()"> Name</span></th>
|
||||
<th sortable-header col="id">ID</th>
|
||||
<th sortable-header col="type">Type</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="lifxdev in bridge.lifxdevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]"
|
||||
value="{{lifxdev.name}}"
|
||||
ng-checked="bulk.devices.indexOf(lifxdev.name) > -1"
|
||||
ng-click="toggleSelection(lifxdev.name)">{{lifxdev.name}}</td>
|
||||
<td>{{lifxdev.id}}</td>
|
||||
<td>{{lifxdev.type}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(lifxdev, false)">Build
|
||||
Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkAddDevices(device_dim_control)">Bulk Add
|
||||
({{bulk.devices.length}})</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured LIFX Devices <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.lifxdevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Light Name</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredLifxItems | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
29
src/main/resources/public/views/login.html
Normal file
29
src/main/resources/public/views/login.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Login</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div ng-if="!loggedIn" class="form-container">
|
||||
|
||||
<form name="loginForm" role="form">
|
||||
<legend class="form-label">Enter Credentials</legend>
|
||||
<div class="form-group">
|
||||
<label>User</label> <input id="username" name="username"
|
||||
class="form-control" type="text" ng-model="username"
|
||||
placeholder="someone" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password</label> <input id="password" name="password"
|
||||
class="form-control" type="password" ng-model="password" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="button" class="btn btn-success" ng-click="login(username, password)">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div ng-if="loggedIn">
|
||||
<button type="button" class="btn btn-danger" ng-click="logout()">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,88 +1,90 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">Log Messages</h1>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
<button class="btn btn-primary" type="submit" ng-click="updateLogs()">Update
|
||||
Log</button>
|
||||
</p>
|
||||
<scrollable-table watch="bridge.logMsgs">
|
||||
<table class="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th sortable-header col="time">Time</th>
|
||||
<th sortable-header col="level">Level</th>
|
||||
<th sortable-header col="message">Message</th>
|
||||
<th sortable-header col="component">Component</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="logMessage in bridge.logMsgs">
|
||||
<td>{{logMessage.time}}</td>
|
||||
<td>{{logMessage.level}}</td>
|
||||
<td>{{logMessage.message}}</td>
|
||||
<td>{{logMessage.component}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">
|
||||
Logging Configuration <a ng-click="toggle()"><span
|
||||
class={{imgUrl}} aria-hidden="true"></a>
|
||||
</h1>
|
||||
</div>
|
||||
<div ng-if="visible" class="panel-body">
|
||||
<p>
|
||||
<button class="btn btn-primary" type="submit"
|
||||
ng-click="updateLoggers()">Update Log Levels</button>
|
||||
Show All Loggers <input type="checkbox" ng-model="bridge.logShowAll"
|
||||
ng-change="reloadLoggers()" ng-true-value=true ng-false-value=false>
|
||||
{{bridge.logShowAll}}
|
||||
</p>
|
||||
<scrollable-table watch="bridge.loggerInfo">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th sortable-header col="logLevel">Log Level</th>
|
||||
<th sortable-header col="loggerName">Component</th>
|
||||
<th>New Log Level</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="logInfo in bridge.loggerInfo">
|
||||
<td>{{logInfo.logLevel.substr(0,logInfo.logLevel.indexOf("_"))}}</td>
|
||||
<td>{{logInfo.loggerName}}</td>
|
||||
<td><select name="new-log-level" id="new-log-level"
|
||||
ng-change="addToUpdate(logInfo)" ng-model="logInfo.newLogLevel">
|
||||
<option ng-repeat="alevel in levels" value="{{alevel}}">{{alevel.substr(0,alevel.indexOf("_"))}}</option>
|
||||
</select></td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">Log Messages</h1>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
<button class="btn btn-primary" type="submit" ng-click="updateLogs()">Update
|
||||
Log</button>
|
||||
</p>
|
||||
<scrollable-table watch="bridge.logMsgs">
|
||||
<table class="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th sortable-header col="time">Time</th>
|
||||
<th sortable-header col="level">Level</th>
|
||||
<th sortable-header col="message">Message</th>
|
||||
<th sortable-header col="component">Component</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="logMessage in bridge.logMsgs">
|
||||
<td>{{logMessage.time}}</td>
|
||||
<td>{{logMessage.level}}</td>
|
||||
<td>{{logMessage.message}}</td>
|
||||
<td>{{logMessage.component}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">
|
||||
Logging Configuration <a ng-click="toggle()"><span
|
||||
class={{imgUrl}} aria-hidden="true"></a>
|
||||
</h1>
|
||||
</div>
|
||||
<div ng-if="visible" class="panel-body">
|
||||
<p>
|
||||
<button class="btn btn-primary" type="submit"
|
||||
ng-click="updateLoggers()">Update Log Levels</button>
|
||||
Show All Loggers <input type="checkbox" ng-model="bridge.logShowAll"
|
||||
ng-change="reloadLoggers()" ng-true-value=true ng-false-value=false>
|
||||
{{bridge.logShowAll}}
|
||||
</p>
|
||||
<scrollable-table watch="bridge.loggerInfo">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th sortable-header col="logLevel">Log Level</th>
|
||||
<th sortable-header col="loggerName">Component</th>
|
||||
<th>New Log Level</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="logInfo in bridge.loggerInfo">
|
||||
<td>{{logInfo.logLevel.substr(0,logInfo.logLevel.indexOf("_"))}}</td>
|
||||
<td>{{logInfo.loggerName}}</td>
|
||||
<td><select name="new-log-level" id="new-log-level"
|
||||
ng-change="addToUpdate(logInfo)" ng-model="logInfo.newLogLevel">
|
||||
<option ng-repeat="alevel in levels" value="{{alevel}}">{{alevel.substr(0,alevel.indexOf("_"))}}</option>
|
||||
</select></td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,116 +1,118 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a href="#!/haldevices">HAL Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">MQTT Messages</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any MQTT Broker, use the
|
||||
build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen.
|
||||
You can add topic and content in the text areas provided
|
||||
then selecting the publish generation. Then you can modify the name
|
||||
to anything you want that will be the keyword for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the
|
||||
'Add Bridge Device' to finish that selection setup. The 'Already
|
||||
Configured MQTT Publish messages' list below will show what is already
|
||||
setup for your MQTT Brokers.</p>
|
||||
|
||||
<scrollable-table watch="bridge.mqttbrokers">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">ClientID</th>
|
||||
<th sortable-header col="ip">IP</th>
|
||||
<th>Topic</th>
|
||||
<th>Content</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="mqttbroker in bridge.mqttbrokers">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{mqttbroker.clientId}}</td>
|
||||
<td>{{mqttbroker.ip}}</td>
|
||||
<td>
|
||||
<textarea rows="2" class="form-control" id="mqtt-topic"
|
||||
ng-model="mqtttopic" placeholder="The MQTT Topic"></textarea>
|
||||
</td>
|
||||
<td>
|
||||
<textarea rows="2" class="form-control" id="mqtt-content"
|
||||
ng-model="mqttcontent" placeholder="The MQTT Message Content"></textarea>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildMQTTPublish(mqttbroker, mqtttopic, mqttcontent)">Build
|
||||
publish Message</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured MQTT Messages <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.mqttbrokers">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="nameclientid">Name</th>
|
||||
<th sortable-header col="targetDevice">ClientID</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredMqttMsgs | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a href="#!/haldevices">HAL Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">MQTT Messages</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any MQTT Broker, use the
|
||||
build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen.
|
||||
You can add topic and content in the text areas provided
|
||||
then selecting the publish generation. Then you can modify the name
|
||||
to anything you want that will be the keyword for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the
|
||||
'Add Bridge Device' to finish that selection setup. The 'Already
|
||||
Configured MQTT Publish messages' list below will show what is already
|
||||
setup for your MQTT Brokers.</p>
|
||||
|
||||
<scrollable-table watch="bridge.mqttbrokers">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">ClientID</th>
|
||||
<th sortable-header col="ip">IP</th>
|
||||
<th>Topic</th>
|
||||
<th>Content</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="mqttbroker in bridge.mqttbrokers">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{mqttbroker.clientId}}</td>
|
||||
<td>{{mqttbroker.ip}}</td>
|
||||
<td>
|
||||
<textarea rows="2" class="form-control" id="mqtt-topic"
|
||||
ng-model="mqtttopic" placeholder="The MQTT Topic"></textarea>
|
||||
</td>
|
||||
<td>
|
||||
<textarea rows="2" class="form-control" id="mqtt-content"
|
||||
ng-model="mqttcontent" placeholder="The MQTT Message Content"></textarea>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildMQTTPublish(mqttbroker, mqtttopic, mqttcontent)">Build
|
||||
publish Message</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured MQTT Messages <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.mqttbrokers">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="nameclientid">Name</th>
|
||||
<th sortable-header col="targetDevice">ClientID</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredMqttMsgs | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
@@ -1,132 +1,134 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Nest Items List</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Nest Item, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Nest Items' list below will show what
|
||||
is already setup for your Nest.</p>
|
||||
|
||||
<scrollable-table watch="bridge.nestitems">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="type">Type</th>
|
||||
<th sortable-header col="location">Location</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="nestitem in bridge.nestitems | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{nestitem.name}}</td>
|
||||
<td>{{nestitem.type}}</td>
|
||||
<td>{{nestitem.location}}</td>
|
||||
<td>
|
||||
<ul class="list-group">
|
||||
<li ng-if="nestitem.type ==='Home' " class="list-group-item">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestHomeUrls(nestitem)">Home/Away</button>
|
||||
</li>
|
||||
<li ng-if="nestitem.type ==='Thermostat' " class="list-group-item">
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestTempUrls(nestitem)">Temp</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestHeatUrls(nestitem)">Heat</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestCoolUrls(nestitem)">Cool</button>
|
||||
</p>
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestRangeUrls(nestitem)">Range</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestOffUrls(nestitem)">Off</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestFanUrls(nestitem)">Fan</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Nest Items <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.nestitems">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="id">Location</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="device in bridge.devices | configuredNestItems | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a
|
||||
href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Nest Items List</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Nest Item, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Nest Items' list below will show what
|
||||
is already setup for your Nest.</p>
|
||||
|
||||
<scrollable-table watch="bridge.nestitems">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="type">Type</th>
|
||||
<th sortable-header col="location">Location</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="nestitem in bridge.nestitems | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{nestitem.name}}</td>
|
||||
<td>{{nestitem.type}}</td>
|
||||
<td>{{nestitem.location}}</td>
|
||||
<td>
|
||||
<ul class="list-group">
|
||||
<li ng-if="nestitem.type ==='Home' " class="list-group-item">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestHomeUrls(nestitem)">Home/Away</button>
|
||||
</li>
|
||||
<li ng-if="nestitem.type ==='Thermostat' " class="list-group-item">
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestTempUrls(nestitem)">Temp</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestHeatUrls(nestitem)">Heat</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestCoolUrls(nestitem)">Cool</button>
|
||||
</p>
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestRangeUrls(nestitem)">Range</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestOffUrls(nestitem)">Off</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestFanUrls(nestitem)">Fan</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Nest Items <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.nestitems">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="id">Location</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="device in bridge.devices | configuredNestItems | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
57
src/main/resources/public/views/securitydialog.html
Normal file
57
src/main/resources/public/views/securitydialog.html
Normal file
@@ -0,0 +1,57 @@
|
||||
<div class="form-container ngdialog-message" ng-controller="SecurityDialogCtrl">
|
||||
|
||||
<form name="securityForm" role="form">
|
||||
<legend class="form-label">Update Security Settings</legend>
|
||||
<div class="form-group">
|
||||
<label>Use Link Button</label>
|
||||
<input type="checkbox"
|
||||
ng-model="useLinkButton" ng-true-value=true
|
||||
ng-false-value=false> {{useLinkButton}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Use username/password for HUE Api</label>
|
||||
<input type="checkbox"
|
||||
ng-model="secureHueApi" ng-true-value=true
|
||||
ng-false-value=false> {{secureHueApi}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Secure Folder for scripts/executables</label>
|
||||
<input id="exec-garden" class="form-control"
|
||||
type="text" ng-model="execGarden"
|
||||
placeholder="/home/pi/protectedscripts">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="button" class="btn btn-primary" ng-click="setSecurityInfo()">Update</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Add/Delete User</label>
|
||||
<input id="new-user" name="new-user" class="form-control"
|
||||
type="text" ng-model="newUser"
|
||||
placeholder="someone" nu-check="new-user">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="button" class="btn btn-danger" ng-click="delUser(newUser)">Delete</button>
|
||||
</div>
|
||||
<div ng-if="showPassword" postrender-action="setBlankPassword('password-1')">
|
||||
<div class="form-group">
|
||||
<label>Change Password for {{username}}</label>
|
||||
<input id="password-1" name="password-1" type="password" class="form-control strength" ng-model="password" data-toggle-title="Display Password" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Confirm Password</label>
|
||||
<input id="password-2" name="password-2" class="form-control" type="password" ng-model="password2" pw-check="password-1" />
|
||||
<div class="msg-block" ng-show="securityForm.$error">
|
||||
<span class="msg-error" ng-show="securityForm.$error.pwmatch">Passwords don't match.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="matched" class="form-group">
|
||||
<button ng-if="!addingUser" class="btn btn-warning" ng-click="changePassword(password, password2)">Change Password</button>
|
||||
<button ng-if="addingUser" class="btn btn-success" ng-click="addUser(newUser, password, password2)">Add User</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="button" class="btn btn-success" ng-click="dismissDialog()">Dismiss</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
143
src/main/resources/public/views/somfydevice.html
Normal file
143
src/main/resources/public/views/somfydevice.html
Normal file
@@ -0,0 +1,143 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li role="presentation"><a href="#!/veradevices">Vera
|
||||
Devices</a></li>
|
||||
<li role="presentation"><a href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation" class="active"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Somfy Device List
|
||||
({{bridge.somfydevices.length}})</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Somfy Device, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device
|
||||
and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Somfy Devices' list below will show
|
||||
what is already setup for your Somfy.</p>
|
||||
<!-- TODO - dim support for partial window opening.. --<p>
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p-->
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items, then click
|
||||
bulk add below. Your items will be added with the name of the device from the Somfy Tahoma.</p>
|
||||
</div>
|
||||
<scrollable-table watch="bridge.somfydevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name"><span><input type="checkbox" name="selectAll"
|
||||
value="{{selectAll}}"
|
||||
ng-checked="selectAll"
|
||||
ng-click="toggleSelectAll()"> Name</span></th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorUniqueId">Id</th>
|
||||
<th sortable-header col="category">Category</th>
|
||||
<th sortable-header col="room">Room</th>
|
||||
<th sortable-header col="somfyname">Somfy</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="somfydevice in bridge.somfydevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]"
|
||||
value="{{somfydevice.id}}"
|
||||
ng-checked="bulk.devices.indexOf(somfydevice.id) > -1"
|
||||
ng-click="toggleSelection(somfydevice.id)">
|
||||
{{somfydevice.name}}</td>
|
||||
<td>{{somfydevice.id}}</td>
|
||||
<td>{{somfydevice.category}}</td>
|
||||
<td>{{somfydevice.room}}</td>
|
||||
<td>{{somfydevice.somfyname}}</td>
|
||||
<td>
|
||||
<!--TODO - taken device_dim_control out of here since not yet used-->
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(somfydevice)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkAddDevices(device_dim_control)">Bulk Add
|
||||
({{bulk.devices.length}})</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Somfy Devices <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.devices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Somfy</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices |configuredSomfyDevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
@@ -19,6 +19,8 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -46,16 +48,11 @@
|
||||
<button ng-disabled="bridge.isInControl"
|
||||
class="btn btn-success" type="submit" ng-click="saveSettings()">Save</button>
|
||||
<button ng-disabled="bridge.isInControl" class="btn btn-warning"
|
||||
type="submit" ng-click="bridgeReinit()">Bridge
|
||||
Reinitialize</button>
|
||||
type="submit" ng-click="bridgeReinit()">Bridge Reinitialize</button>
|
||||
<button ng-disabled="bridge.isInControl" class="btn btn-danger"
|
||||
type="submit" ng-click="bridgeStop()">Bridge Stop</button>
|
||||
<button class="btn btn-primary" type="submit" onclick="myRefresh()">Refresh</button>
|
||||
<script>
|
||||
function myRefresh() {
|
||||
location.reload();
|
||||
}
|
||||
</script>
|
||||
<button class="btn btn-warning"
|
||||
type="submit" ng-click="changeSeuritySettings()">Update Security Settings</button>
|
||||
</p>
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
@@ -140,25 +137,30 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>IP</th>
|
||||
<th>IP</th>
|
||||
<th>Webhook</th>
|
||||
<th>Manage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="harmony in bridge.settings.harmonyaddress.devices">
|
||||
<td>{{harmony.name}}</td>
|
||||
<td>{{harmony.ip}}</td>
|
||||
<td>{{harmony.ip}}</td>
|
||||
<td>{{harmony.webhook}}</td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="removeHarmonytoSettings(harmony.name, harmony.ip)">Del</button></td>
|
||||
ng-click="removeHarmonytoSettings(harmony.name, harmony.ip, harmony.webhook)">Del</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input id="bridge-settings-next-harmony-name"
|
||||
class="form-control" type="text" ng-model="newharmonyname"
|
||||
placeholder="A Harmony"></td>
|
||||
<td><input id="bridge-settings-next-harmony-ip"
|
||||
class="form-control" type="text" ng-model="newharmonyip"
|
||||
placeholder="192.168.1.3"></td>
|
||||
<td><input id="bridge-settings-next-harmony-ip"
|
||||
class="form-control" type="text" ng-model="newharmonyip"
|
||||
placeholder="192.168.1.3"></td>
|
||||
<td><input id="bridge-settings-next-harmony-webhook"
|
||||
class="form-control" type="text" ng-model="newharmonywebhook"
|
||||
placeholder="http://hook?a=${activity.label}"></td>
|
||||
<td><button class="btn btn-success" type="submit"
|
||||
ng-click="addHarmonytoSettings(newharmonyname, newharmonyip)">Add</button></td>
|
||||
ng-click="addHarmonytoSettings(newharmonyname, newharmonyip, newharmonywebhook)">Add</button></td>
|
||||
</tr>
|
||||
</table></td>
|
||||
</tr>
|
||||
@@ -357,6 +359,46 @@
|
||||
</tr>
|
||||
</table></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Somfy Names and IP Addresses</td>
|
||||
<td><table
|
||||
class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>IP/hostname</th>
|
||||
<th>Username</th>
|
||||
<th>Password </th>
|
||||
<th>Manage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="somfy in bridge.settings.somfyaddress.devices">
|
||||
<td>{{somfy.name}}</td>
|
||||
<td>{{somfy.ip}}</td>
|
||||
<td>{{somfy.username}}</td>
|
||||
<td ng-if="somfy.password">*******</td>
|
||||
<td ng-if="!somfy.password"> </td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="removeSomfytoSettings(somfy.name, somfy.ip)">Del</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input id="bridge-settings-next-somfy-name"
|
||||
class="form-control" type="text" ng-model="newsomfyname"
|
||||
placeholder="A Somfy"></td>
|
||||
<td><input id="bridge-settings-next-somfy-ip"
|
||||
class="form-control" type="text" ng-model="newsomfyip"
|
||||
placeholder="https://www.tahomalink.com"></td>
|
||||
<td><input id="bridge-settings-next-somfy-username"
|
||||
class="form-control" type="text" ng-model="newsomfyusername"
|
||||
placeholder="Somfy username"></td>
|
||||
<td><input id="bridge-settings-next-somfy-password"
|
||||
class="form-control" type="password" ng-model="newsomfypassword"
|
||||
placeholder="Somfy password"></td>
|
||||
<td><button class="btn btn-success" type="submit"
|
||||
ng-click="addSomfytoSettings(newsomfyname, newsomfyip, newsomfyusername, newsomfypassword)">Add</button></td>
|
||||
</tr>
|
||||
</table></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nest Username</td>
|
||||
<td><input id="bridge-settings-nestuser" class="form-control"
|
||||
@@ -375,6 +417,12 @@
|
||||
ng-model="bridge.settings.farenheit" ng-true-value=true
|
||||
ng-false-value=false> {{bridge.settings.farenheit}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>LIFX Support</td>
|
||||
<td><input type="checkbox"
|
||||
ng-model="bridge.settings.lifxconfigured" ng-true-value=true
|
||||
ng-false-value=false> {{bridge.settings.lifxconfigured}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Emulate Hue Hub Version</td>
|
||||
<td><input id="bridge-settings-hubversion" class="form-control"
|
||||
|
||||
@@ -1,141 +1,144 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/veradevices">Vera
|
||||
Devices</a></li>
|
||||
<li role="presentation"><a href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Vera Device List
|
||||
({{bridge.veradevices.length}})</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Vera Device, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device
|
||||
and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Vera Devices' list below will show
|
||||
what is already setup for your Vera.</p>
|
||||
<p>
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on and off or dim and
|
||||
off if selected with the name of the device from the Vera.</p>
|
||||
<scrollable-table watch="bridge.veradevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name"><span><input type="checkbox" name="selectAll"
|
||||
value="{{selectAll}}"
|
||||
ng-checked="selectAll"
|
||||
ng-click="toggleSelectAll()"> Name</span></th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorUniqueId">Id</th>
|
||||
<th sortable-header col="category">Category</th>
|
||||
<th sortable-header col="room">Room</th>
|
||||
<th sortable-header col="veraname">Vera</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="veradevice in bridge.veradevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]"
|
||||
value="{{veradevice.id}}"
|
||||
ng-checked="bulk.devices.indexOf(veradevice.id) > -1"
|
||||
ng-click="toggleSelection(veradevice.id)">
|
||||
{{veradevice.name}}</td>
|
||||
<td>{{veradevice.id}}</td>
|
||||
<td>{{veradevice.category}}</td>
|
||||
<td>{{veradevice.room}}</td>
|
||||
<td>{{veradevice.veraname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(veradevice, device_dim_control)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkAddDevices(device_dim_control)">Bulk Add
|
||||
({{bulk.devices.length}})</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Vera Devices <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.devices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Vera</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredVeraDevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/veradevices">Vera
|
||||
Devices</a></li>
|
||||
<li role="presentation"><a href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Vera Device List
|
||||
({{bridge.veradevices.length}})</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Vera Device, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device
|
||||
and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Vera Devices' list below will show
|
||||
what is already setup for your Vera.</p>
|
||||
<p class="text-muted">
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.decimal_percent}">Decimal Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on and off or dim and
|
||||
off if selected with the name of the device from the Vera.</p>
|
||||
<scrollable-table watch="bridge.veradevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name"><span><input type="checkbox" name="selectAll"
|
||||
value="{{selectAll}}"
|
||||
ng-checked="selectAll"
|
||||
ng-click="toggleSelectAll()"> Name</span></th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorUniqueId">Id</th>
|
||||
<th sortable-header col="category">Category</th>
|
||||
<th sortable-header col="room">Room</th>
|
||||
<th sortable-header col="veraname">Vera</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="veradevice in bridge.veradevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]"
|
||||
value="{{veradevice.id}}"
|
||||
ng-checked="bulk.devices.indexOf(veradevice.id) > -1"
|
||||
ng-click="toggleSelection(veradevice.id)">
|
||||
{{veradevice.name}}</td>
|
||||
<td>{{veradevice.id}}</td>
|
||||
<td>{{veradevice.category}}</td>
|
||||
<td>{{veradevice.room}}</td>
|
||||
<td>{{veradevice.veraname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(veradevice, device_dim_control, false)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkAddDevices(device_dim_control)">Bulk Add
|
||||
({{bulk.devices.length}})</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Vera Devices <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.devices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Vera</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredVeraDevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
@@ -1,111 +1,113 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li role="presentation"><a href="#!/veradevices">Vera Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/verascenes">Vera
|
||||
Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Vera Scene List</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Vera Scene, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Vera Scenes' list below will show what
|
||||
is already setup for your Vera.</p>
|
||||
|
||||
<scrollable-table watch="bridge.verascenes">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorUniqueId">Id</th>
|
||||
<th sortable-header col="room">Room</th>
|
||||
<th sortable-header col="veraname">Vera</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="verascene in bridge.verascenes">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{verascene.name}}</td>
|
||||
<td>{{verascene.id}}</td>
|
||||
<td>{{verascene.room}}</td>
|
||||
<td>{{verascene.veraname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildSceneUrls(verascene)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Vera Scenes <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.verascenes">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Vera</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredVeraScenes">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li role="presentation"><a href="#!/veradevices">Vera Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/verascenes">Vera
|
||||
Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a
|
||||
href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a
|
||||
href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Vera Scene List</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any Vera Scene, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Vera Scenes' list below will show what
|
||||
is already setup for your Vera.</p>
|
||||
|
||||
<scrollable-table watch="bridge.verascenes">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="id" comparator-fn="comparatorUniqueId">Id</th>
|
||||
<th sortable-header col="room">Room</th>
|
||||
<th sortable-header col="veraname">Vera</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="verascene in bridge.verascenes">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{verascene.name}}</td>
|
||||
<td>{{verascene.id}}</td>
|
||||
<td>{{verascene.room}}</td>
|
||||
<td>{{verascene.veraname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildSceneUrls(verascene)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured Vera Scenes <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.verascenes">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Vera</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredVeraScenes">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.bwssystems.color.test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.bwssystems.HABridge.hue.ColorDecode;
|
||||
|
||||
public class ConvertCIEColorTestCase {
|
||||
|
||||
@Test
|
||||
public void testColorConversion() {
|
||||
ArrayList<Double> xy = new ArrayList<Double>(Arrays.asList(new Double(0.3972), new Double(0.4564)));
|
||||
|
||||
String colorDecode = ColorDecode.convertCIEtoRGB(xy);
|
||||
Assert.assertEquals(colorDecode, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.hass.test;
|
||||
package com.bwssystems.domoticz.test;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssystems.hass.test;
|
||||
package com.bwssystems.domoticz.test;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
Reference in New Issue
Block a user