mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-19 08:28:46 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51ce10cfc7 | ||
|
|
b5f7144c9c | ||
|
|
86a931d383 | ||
|
|
3e890721c5 | ||
|
|
62d1c64a3d | ||
|
|
c025b186cd | ||
|
|
e999c3a969 | ||
|
|
351403e611 | ||
|
|
c773477a43 | ||
|
|
5d1f0ce3b6 | ||
|
|
7e0fd6c21b | ||
|
|
3bf52f5da0 | ||
|
|
bd856d8f9e | ||
|
|
73b2be752e | ||
|
|
dda7a7a34a | ||
|
|
f238e05533 | ||
|
|
9a1924422e |
81
README.md
81
README.md
@@ -21,10 +21,10 @@ Then locate the jar and start the server with:
|
||||
ATTENTION: This requires JDK 1.8 to run
|
||||
|
||||
```
|
||||
java -jar ha-bridge-W.X.Y.jar
|
||||
java -jar ha-bridge-2.0.6.jar
|
||||
```
|
||||
### Automation on Linux systems
|
||||
To have this conigured and running automatically ther eare a few resources to use. One is using Docker and a docker containerahs been built for this and can be gotten here: https://github.com/aptalca/docker-ha-bridge
|
||||
To have this configured and running automatically there are a few resources to use. One is using Docker and a docker container has been built for this and can be gotten here: https://github.com/aptalca/docker-ha-bridge
|
||||
|
||||
For next gen Linux systems, here is a systemctl unit file that you can install. Here is a link on how to do this: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
|
||||
```
|
||||
@@ -35,11 +35,43 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/amazon-echo/data/habridge.config /home/pi/amazon-echo/ha-bridge-2.0.0.jar
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/amazon-echo/data/habridge.config /home/pi/amazon-echo/ha-bridge-2.0.6.jar
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Basic script setup to run the bridge on a pi.
|
||||
|
||||
Create the directory and make sure that ha-bridge-2.0.6.jar is in your /home/pi/habridge directory.
|
||||
```
|
||||
pi@raspberrypi:~ $ mkdir habridge
|
||||
pi@raspberrypi:~ $ cd habridge
|
||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v2.0.6/ha-bridge-2.0.6.jar
|
||||
```
|
||||
Edit the shell script for starting:
|
||||
```
|
||||
pi@raspberrypi:~/habridge $ nano starthabridge.sh
|
||||
```
|
||||
Then cut and past this, modify any locations that are not correct
|
||||
```
|
||||
cd /home/pi/habridge
|
||||
rm /home/pi/habridge/habridge-log.txt
|
||||
nohup java -jar /home/pi/habridge/ha-bridge-2.0.6.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
|
||||
chmod 777 /home/pi/habridge/habridge-log.txt
|
||||
```
|
||||
Exit and save the file with ctrl-X and follow the prompts and then execute on the command line:
|
||||
```
|
||||
pi@raspberrypi:~/habridge $ chmod u+x starthabridge.sh
|
||||
```
|
||||
Then execute the script:
|
||||
```
|
||||
pi@raspberrypi:~/habridge $ ./starthabridge.sh
|
||||
```
|
||||
You should now be running the bridge. Check for errors:
|
||||
```
|
||||
pi@raspberrypi:~/habridge $ tail -f habridge-log.txt
|
||||
```
|
||||
## Available Arguments
|
||||
Arguments are now deprecated. The ha-bridge will use the old -D arguments and populate the configuration screen, Brisge Control Tab, which can now be saved to a file and will not be needed. There is only one optional argument that overrides and that is the location of the configuration file. The default is the relative path "data/habridge.config".
|
||||
### -Dconfig.file=`<filepath>`
|
||||
@@ -73,7 +105,7 @@ The default location for the db to contain the devices as they are added is "dat
|
||||
#### UPNP IP Address
|
||||
The server defaults to the first available address on the host if this is not given. This default may NOT be the correct IP that is your public IP for your host on the network. It is best to set this parameter to not have discovery issues. Replace this value with the server ipv4 address you would like to use as the address that any upnp device will call after discovery.
|
||||
#### Web Server Port
|
||||
The server defaults to running on port 8080. To override what the default is, specify a different number.
|
||||
The server defaults to running on port 8080. To override what the default is, specify a different number. ATTENTION: If you want to use any of the apps made for the Hue to control this bridge, you should set this port to 80.
|
||||
#### UPNP Response Port
|
||||
The upnp response port that will be used. The default is 50000.
|
||||
#### Vera Names and IP Addresses
|
||||
@@ -86,6 +118,8 @@ The user name of the MyHarmony.com account for the Harmony Hub. This needs to be
|
||||
The password for the user name of the MyHarmony.com account for the Harmony Hub. This needs to be given if you are using the Harmony Hub Features.
|
||||
#### Hue Names and IP Addresses
|
||||
Provide IP Addresses of your Hue Bridges that you want to proxy through the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will passthru the call it receives to the target Hue and device you configure.
|
||||
|
||||
Don't forget - You will need to push the link button when you got to the Hue Tab the first time ater the process comes up. (The user name is not persistent when the process comes up.)
|
||||
#### Nest Username
|
||||
The user name of the home.nest.com account for the Nest user. This needs to be given if you are using the Nest features. There is no need to give any ip address or host information as this contacts your cloud account.
|
||||
#### Nest Password
|
||||
@@ -105,9 +139,9 @@ At the bottom of the screen is the "Bridge Settings Backup" which can be accesse
|
||||
### The Logs Tab
|
||||
This screen displays the last 512 or number of rows defined in the config screen of the log so you don't have to go to the output of your process. The `Update Log` button refreshes the log as this screen does not auto refresh. FYI, when the trace upnp setting is turned on in the configuration, the messages will show here.
|
||||
|
||||
The bottom part of the Logs Screen has configuration to change the logging levels as it is running. The ROOT is the basic setting and will turn on only top level logging. To set logging at a lower level, select the `Show All Loggers` checkbox and then you can set the explicit level on each of the processes components. The most helpful logger would be setting DBUG for com.bwssystems.HABridge.hue.HueMulator component. Changing this and then selecting the `Update Log Levels` button applies the new log settings.
|
||||
The bottom part of the Logs Screen has configuration to change the logging levels as it is running. The ROOT is the basic setting and will turn on only top level logging. To set logging at a lower level, select the `Show All Loggers` checkbox and then you can set the explicit level on each of the processes components. The most helpful logger would be setting DEBUG for com.bwssystems.HABridge.hue.HueMulator component. Changing this and then selecting the `Update Log Levels` button applies the new log settings.
|
||||
### Bridge Device Additions
|
||||
You must configure devices before you will have any thing for the Echo or other contoller that is connected to the ha-bridge to receive.
|
||||
You must configure devices before you will have any thing for the Echo or other controller that is connected to the ha-bridge to receive.
|
||||
#### Helpers
|
||||
The easy way to get devices configured is with the use of the helpers for the Vera or Harmony, Nest and Hue to create devices that the bridge will present.
|
||||
|
||||
@@ -117,7 +151,7 @@ The helper tabs will also show you what you have already configured for that tar
|
||||
#### The Manual Add Tab
|
||||
Another way to add a device is through the Manual Add Tab. This allows you to manually enter the name, the on and off URLs and select if there are custom handling with the type of call that can be made. This allows for control of anything that has a distinct request that can be executed so you are not limited to the Vera, Harmony, Nest or other Hue.
|
||||
|
||||
The format of these can be the default HTTP request which executes the URLs formatted as http://<your stuff here> as a GET. Other options to this are to select the HTTP Verb and add the data type and add a body that is passed with the request. Secure https is supported as well, just use https://<your secure call here>. When using POST and PUT, you have the ability to specify the body that will be sent with the request as well as the application type for the http call.
|
||||
The format of these can be the default HTTP request which executes the URLs formatted as `http://<your stuff here>` as a GET. Other options to this are to select the HTTP Verb and add the data type and add a body that is passed with the request. Secure https is supported as well, just use `https://<your secure call here>`. When using POST and PUT, you have the ability to specify the body that will be sent with the request as well as the application type for the http call.
|
||||
|
||||
Headers can be added as well using a Json construct [{"name":"header type name","value":"the header value"}] with the format example:
|
||||
```
|
||||
@@ -125,7 +159,7 @@ Headers can be added as well using a Json construct [{"name":"header type name",
|
||||
{"name":"Pragma","value":"no-cache"}]
|
||||
```
|
||||
|
||||
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.
|
||||
Another option that is detected by the bridge is to use UDP or TCP direct calls such as `udp://<ip_address>:<port>/<your stuff here>` to send a UDP request. TCP calls are handled the same way as `tcp://<ip_address>:<port>/<your stuff here>`. If your data for the UDP or TCP request is formatted as "0x00F009B9" lexical hex format, the bridge will convert the data into a binary stream to send.
|
||||
|
||||
You can also use the value replacement constructs within these statements. Such as using the expressions ${intensity.percent} for 0-100 or ${intensity.byte} for 0-255 for straight pass through of the value or items that require special calculated values using ${intensity.math()} i.e. "${intensity.math(X/4)}".
|
||||
Examples:
|
||||
@@ -138,10 +172,18 @@ http://192.168.1.1:8280/set/this
|
||||
ContentBody: {"someValue":"${intensity..byte}"}
|
||||
|
||||
udp://192.168.1.1:5000/0x45${intensity.percent}55
|
||||
|
||||
udp://192.168.2.2:6000/fireoffthismessage\n
|
||||
|
||||
tcp://192.168.3.3:9000/sendthismessage
|
||||
|
||||
tcp://192.168.4.4:10000/0x435f12dd${intensity.math((X -4)*50)}438c
|
||||
|
||||
tcp://192.168.5.5:110000/0x
|
||||
```
|
||||
|
||||
#### Multiple Call Construct
|
||||
Also available is the ability to specify multiple commands in the On URL, Dim URL and Off URL areas by adding Json constructs listed here.
|
||||
Also available is the ability to specify multiple commands in the On URL, Dim URL and Off URL areas by adding Json constructs listed here. This is only for the types of tcp, udp, http, https or a new exec type.
|
||||
Format Example in the URL areas:
|
||||
```
|
||||
[{"item":"http://192.168.1.1:8180/do/this/thing"},
|
||||
@@ -155,10 +197,11 @@ Format Example in the URL areas:
|
||||
[{"item":"udp://192.168.1.1:5000/0x450555"},
|
||||
{"item":"http://192.168.1.1:8180/do/this/thing"},
|
||||
{"item":"tcp://192.168.2.1/sendthisdata"},
|
||||
{"item":"https://192.168.12.1/do/this/secure/thing"}]
|
||||
{"item":"https://192.168.12.1/do/this/secure/thing"},
|
||||
{"item":"exec://notepad.exe"}]
|
||||
```
|
||||
#### Script or Command Execution
|
||||
The release as of v2.0.0 will no support the execution of a local script or program. This will blindly fire off a process to run and is bound by the privileges of the java process.
|
||||
The release as of v2.0.0 will now support the execution of a local script or program. This will blindly fire off a process to run and is bound by the privileges of the java process.
|
||||
|
||||
To configure this type of manual add, you will need to select the Device type of "Execute Script/Program".
|
||||
|
||||
@@ -175,6 +218,10 @@ OR
|
||||
|
||||
/home/me/startsomething.sh
|
||||
|
||||
OR
|
||||
|
||||
[{"item":"exec://notepad.exe"}]
|
||||
|
||||
```
|
||||
|
||||
Also, you may want to use the REST API's listed below to configure your devices.
|
||||
@@ -207,13 +254,15 @@ OFF Commands |
|
||||
DIM Commands |
|
||||
| Alexa, brighten `<Device Name>` to `<Position>`
|
||||
| Alexa, dim `<Device Name> to <Position>`
|
||||
| Alexa, brighten `<Device Name>`
|
||||
| Alexa, dim `<Device Name>`
|
||||
| Alexa, raise `<Device Name>` to `<Position>`
|
||||
| Alexa, lower `<Device Name>` to `<Position>`
|
||||
| Alexa, set `<Device Name>` to `<Position>`
|
||||
| Alexa, turn up `<Device Name>` to `<Position>`
|
||||
| Alexa, turn down `<Device Name>` to `<Position>`
|
||||
|
||||
To see what Alexa thinks you said, you can check in the home page for your alexa.
|
||||
To see what Alexa thinks you said, you can check in the home page for your Alexa.
|
||||
|
||||
To view or remove devices that Alexa knows about, you can use the mobile app `Menu / Settings / Connected Home` or go to http://echo.amazon.com/#cards.
|
||||
## Configuration REST API Usage
|
||||
@@ -254,7 +303,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 trhough of the value.
|
||||
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.
|
||||
e.g.
|
||||
```
|
||||
{
|
||||
@@ -862,7 +911,7 @@ rules | object | A collection of all rules and their attributes. This is not giv
|
||||
## UPNP Emulation of HUE
|
||||
This section will discuss the UPNP implementation of this bridge based as much as can be for the HUE.
|
||||
### UPNP listening
|
||||
The HA Bridge default UPNP listner is started on port 1900 on the upnp multicast address of 239.255.255.250. All ethernet interfaces that are active are bound to and the repsonse port is set to the one given on the command line above or the default of 50000.
|
||||
The HA Bridge default UPNP listener is started on port 1900 on the upnp multicast address of 239.255.255.250. All ethernet interfaces that are active are bound to and the response port is set to the one given on the command line above or the default of 50000.
|
||||
|
||||
The listener will respond to the following body packet that contain the following minimal information:
|
||||
|
||||
@@ -888,7 +937,7 @@ ST: urn:schemas-upnp-org:device:basic:1\r\n
|
||||
"USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n
|
||||
```
|
||||
### UPNP description service
|
||||
The bridge provides the description service which is used by the calling app to interogate access details after it has decided the upnp multicast repsonse is the correct device.
|
||||
The bridge provides the description service which is used by the calling app to interogate access details after it has decided the upnp multicast response is the correct device.
|
||||
#### Get Description
|
||||
```
|
||||
GET http://host:8080/description.xml
|
||||
@@ -951,4 +1000,4 @@ To turn on debugging for the bridge, use the following extra parm in the command
|
||||
To turn on development mode so that it will not need an Harmony Hub for testing, use the following extra parm in the command line and the harmony ip and login info will not be needed:
|
||||
```
|
||||
java -jar -Ddev.mode=true ha-bridge-0.X.Y.jar
|
||||
```
|
||||
```
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.bwssystems.HABridge</groupId>
|
||||
<artifactId>ha-bridge</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<version>2.0.7</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HA Bridge</name>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
// import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -7,7 +8,7 @@ import java.util.List;
|
||||
*/
|
||||
public class DeviceState {
|
||||
private boolean on;
|
||||
private int bri = 255;
|
||||
private int bri;
|
||||
private int hue;
|
||||
private int sat;
|
||||
private String effect;
|
||||
@@ -96,7 +97,24 @@ public class DeviceState {
|
||||
public void setXy(List<Double> xy) {
|
||||
this.xy = xy;
|
||||
}
|
||||
|
||||
public static DeviceState createDeviceState() {
|
||||
DeviceState newDeviceState = new DeviceState();
|
||||
newDeviceState.fillIn();
|
||||
// newDeviceState.setColormode("none");
|
||||
// ArrayList<Double> doubleArray = new ArrayList<Double>();
|
||||
// doubleArray.add(new Double(0));
|
||||
// doubleArray.add(new Double(0));
|
||||
// newDeviceState.setXy(doubleArray);
|
||||
|
||||
return newDeviceState;
|
||||
}
|
||||
public void fillIn() {
|
||||
if(this.getAlert() == null)
|
||||
this.setAlert("none");
|
||||
if(this.getEffect() == null)
|
||||
this.setEffect("none");
|
||||
this.setReachable(true);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DeviceState{" +
|
||||
|
||||
@@ -166,6 +166,8 @@ public class DeviceDescriptor{
|
||||
}
|
||||
|
||||
public DeviceState getDeviceState() {
|
||||
if(deviceState == null)
|
||||
deviceState = DeviceState.createDeviceState();
|
||||
return deviceState;
|
||||
}
|
||||
|
||||
|
||||
@@ -317,12 +317,15 @@ public class HueMulator implements HueErrorStringSet {
|
||||
String lightId = request.params(":id");
|
||||
String responseString = null;
|
||||
DeviceState state = null;
|
||||
boolean stateHasOn = false;
|
||||
log.debug("Update state requested: " + userId + " from " + request.ip() + " body: " + request.body());
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.type("application/json; charset=utf-8");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
try {
|
||||
state = mapper.readValue(request.body(), DeviceState.class);
|
||||
if(request.body().contains("\"on\""))
|
||||
stateHasOn = true;
|
||||
} catch (IOException e) {
|
||||
log.warn("Object mapper barfed on input of body.", e);
|
||||
responseString = "[{\"error\":{\"type\": 2, \"address\": \"/lights/" + lightId + "\",\"description\": \"Object mapper barfed on input of body.\"}}]";
|
||||
@@ -335,7 +338,7 @@ public class HueMulator implements HueErrorStringSet {
|
||||
return responseString;
|
||||
}
|
||||
|
||||
responseString = this.formatSuccessHueResponse(state, request.body(), lightId);
|
||||
responseString = this.formatSuccessHueResponse(state, request.body(), stateHasOn, lightId);
|
||||
device.setDeviceState(state);
|
||||
|
||||
return responseString;
|
||||
@@ -362,6 +365,8 @@ public class HueMulator implements HueErrorStringSet {
|
||||
String url = null;
|
||||
NameValue[] theHeaders = null;
|
||||
DeviceState state = null;
|
||||
boolean stateHasBri = false;
|
||||
boolean stateHasOn = false;
|
||||
log.debug("hue state change requested: " + userId + " from " + request.ip() + " body: " + request.body());
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.type("application/json; charset=utf-8");
|
||||
@@ -369,6 +374,10 @@ public class HueMulator implements HueErrorStringSet {
|
||||
|
||||
try {
|
||||
state = mapper.readValue(request.body(), DeviceState.class);
|
||||
if(request.body().contains("\"bri\""))
|
||||
stateHasBri = true;
|
||||
if(request.body().contains("\"on\""))
|
||||
stateHasOn = true;
|
||||
} catch (IOException e) {
|
||||
log.warn("Object mapper barfed on input of body.", e);
|
||||
responseString = "[{\"error\":{\"type\": 2, \"address\": \"/lights/" + lightId + "\",\"description\": \"Object mapper barfed on input of body.\"}}]";
|
||||
@@ -381,9 +390,10 @@ public class HueMulator implements HueErrorStringSet {
|
||||
responseString = "[{\"error\":{\"type\": 3, \"address\": \"/lights/" + lightId + "\",\"description\": \"Could not find device\", \"resource\": \"/lights/" + lightId + "\"}}]";
|
||||
return responseString;
|
||||
}
|
||||
state.fillIn();
|
||||
|
||||
theHeaders = new Gson().fromJson(device.getHeaders(), NameValue[].class);
|
||||
responseString = this.formatSuccessHueResponse(state, request.body(), lightId);
|
||||
responseString = this.formatSuccessHueResponse(state, request.body(), stateHasOn, lightId);
|
||||
|
||||
if(device.getDeviceType().toLowerCase().contains("hue") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("hueDevice")))
|
||||
{
|
||||
@@ -418,11 +428,14 @@ public class HueMulator implements HueErrorStringSet {
|
||||
else
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"No HUE configured\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
|
||||
return responseString;
|
||||
return responseString;
|
||||
}
|
||||
|
||||
if(request.body().contains("bri"))
|
||||
if(stateHasBri)
|
||||
{
|
||||
if(state.getBri() > 0 && !state.isOn())
|
||||
state.setOn(true);
|
||||
|
||||
url = device.getDimUrl();
|
||||
|
||||
if(url == null || url.length() == 0)
|
||||
@@ -432,8 +445,9 @@ public class HueMulator implements HueErrorStringSet {
|
||||
{
|
||||
if (state.isOn()) {
|
||||
url = device.getOnUrl();
|
||||
state.setBri(255);
|
||||
} else if (request.body().contains("false")) {
|
||||
if(state.getBri() <= 0)
|
||||
state.setBri(255);
|
||||
} else {
|
||||
url = device.getOffUrl();
|
||||
state.setBri(0);
|
||||
}
|
||||
@@ -556,18 +570,19 @@ public class HueMulator implements HueErrorStringSet {
|
||||
if( i > 0) {
|
||||
Thread.sleep(bridgeSettings.getButtonsleep());
|
||||
}
|
||||
try {
|
||||
log.debug("Executing request: " + callItems[i].getItem());
|
||||
Process p = Runtime.getRuntime().exec(replaceIntensityValue(callItems[i].getItem(), state.getBri(), false));
|
||||
log.debug("Process running: " + p.isAlive());
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not execute request: " + callItems[i].getItem(), e);
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
String intermediate;
|
||||
if(callItems[i].getItem().contains("exec://"))
|
||||
intermediate = callItems[i].getItem().substring(callItems[i].getItem().indexOf("://") + 3);
|
||||
else
|
||||
intermediate = callItems[i].getItem();
|
||||
String anError = doExecRequest(intermediate, state, lightId);
|
||||
if(anError != null) {
|
||||
responseString = anError;
|
||||
i = callItems.length+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // This section allows the usage of http/tcp/udp calls in a given set of items
|
||||
else // This section allows the usage of http/tcp/udp/exec calls in a given set of items
|
||||
{
|
||||
log.debug("executing HUE api request for network call: " + url);
|
||||
if(!url.startsWith("[")) {
|
||||
@@ -582,41 +597,51 @@ public class HueMulator implements HueErrorStringSet {
|
||||
Thread.sleep(bridgeSettings.getButtonsleep());
|
||||
}
|
||||
try {
|
||||
String intermediate = callItems[i].getItem().substring(callItems[i].getItem().indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/')+1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
if(hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
if(callItems[i].getItem().contains("udp://") || callItems[i].getItem().contains("tcp://")) {
|
||||
String intermediate = callItems[i].getItem().substring(callItems[i].getItem().indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/')+1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
if(hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
}
|
||||
else
|
||||
hostAddr = hostPortion;
|
||||
InetAddress IPAddress = InetAddress.getByName(hostAddr);;
|
||||
if(theUrlBody.startsWith("0x")) {
|
||||
theUrlBody = replaceIntensityValue(theUrlBody, state.getBri(), true);
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
}
|
||||
else {
|
||||
theUrlBody = replaceIntensityValue(theUrlBody, state.getBri(), false);
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
if(callItems[i].getItem().contains("udp://")) {
|
||||
log.debug("executing HUE api request to UDP: " + callItems[i].getItem());
|
||||
DatagramSocket responseSocket = new DatagramSocket(Integer.parseInt(port));
|
||||
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, Integer.parseInt(port));
|
||||
responseSocket.send(sendPacket);
|
||||
responseSocket.close();
|
||||
}
|
||||
else if(callItems[i].getItem().contains("tcp://"))
|
||||
{
|
||||
log.debug("executing HUE api request to TCP: " + callItems[i].getItem());
|
||||
Socket dataSendSocket = new Socket(IPAddress, Integer.parseInt(port));
|
||||
DataOutputStream outToClient = new DataOutputStream(dataSendSocket.getOutputStream());
|
||||
outToClient.write(sendData);
|
||||
outToClient.flush();
|
||||
dataSendSocket.close();
|
||||
}
|
||||
}
|
||||
else
|
||||
hostAddr = hostPortion;
|
||||
InetAddress IPAddress = InetAddress.getByName(hostAddr);;
|
||||
if(theUrlBody.startsWith("0x")) {
|
||||
theUrlBody = replaceIntensityValue(theUrlBody, state.getBri(), true);
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
}
|
||||
else {
|
||||
theUrlBody = replaceIntensityValue(theUrlBody, state.getBri(), false);
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
if(callItems[i].getItem().contains("udp://")) {
|
||||
log.debug("executing HUE api request to UDP: " + callItems[i].getItem());
|
||||
DatagramSocket responseSocket = new DatagramSocket(Integer.parseInt(port));
|
||||
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, Integer.parseInt(port));
|
||||
responseSocket.send(sendPacket);
|
||||
responseSocket.close();
|
||||
}
|
||||
else if(callItems[i].getItem().contains("tcp://"))
|
||||
{
|
||||
log.debug("executing HUE api request to TCP: " + callItems[i].getItem());
|
||||
Socket dataSendSocket = new Socket(IPAddress, Integer.parseInt(port));
|
||||
DataOutputStream outToClient = new DataOutputStream(dataSendSocket.getOutputStream());
|
||||
outToClient.write(sendData);
|
||||
outToClient.flush();
|
||||
dataSendSocket.close();
|
||||
else if(callItems[i].getItem().contains("exec://")) {
|
||||
String intermediate = callItems[i].getItem().substring(callItems[i].getItem().indexOf("://") + 3);
|
||||
String anError = doExecRequest(intermediate, state, lightId);
|
||||
if(anError != null) {
|
||||
responseString = anError;
|
||||
i = callItems.length+1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.debug("executing HUE api request to Http " + (device.getHttpVerb() == null?"GET":device.getHttpVerb()) + ": " + callItems[i].getItem());
|
||||
@@ -749,82 +774,105 @@ public class HueMulator implements HueErrorStringSet {
|
||||
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){
|
||||
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); //read content for data
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
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);
|
||||
}
|
||||
}
|
||||
if(theContent == null)
|
||||
theContent = "";
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Error calling out to HA gateway: IOException in log", e);
|
||||
}
|
||||
return theContent;
|
||||
}
|
||||
|
||||
private String doExecRequest(String anItem, DeviceState state, String lightId) {
|
||||
log.debug("Executing request: " + anItem);
|
||||
String responseString = null;
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec(replaceIntensityValue(anItem, state.getBri(), false));
|
||||
log.debug("Process running: " + p.isAlive());
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not execute request: " + anItem, e);
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
private String formatSuccessHueResponse(DeviceState state, String body, String lightId) {
|
||||
private String formatSuccessHueResponse(DeviceState state, String body, boolean stateHasOn, String lightId) {
|
||||
|
||||
String responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":";
|
||||
boolean justState = true;
|
||||
String responseString = "[";
|
||||
boolean justState = false;
|
||||
if(stateHasOn)
|
||||
{
|
||||
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/on\":";
|
||||
if (state.isOn()) {
|
||||
responseString = responseString + "true}}";
|
||||
if(state.getBri() <= 0)
|
||||
state.setBri(255);
|
||||
} else {
|
||||
responseString = responseString + "false}}";
|
||||
state.setBri(0);
|
||||
}
|
||||
justState = true;
|
||||
}
|
||||
|
||||
if(body.contains("bri"))
|
||||
{
|
||||
if(justState)
|
||||
responseString = responseString + "true}}";
|
||||
responseString = responseString + ",{\"success\":{\"/lights/" + lightId + "/state/bri\":" + state.getBri() + "}}";
|
||||
justState = false;
|
||||
responseString = responseString + ",";
|
||||
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/bri\":" + state.getBri() + "}}";
|
||||
justState = true;
|
||||
}
|
||||
|
||||
if(body.contains("ct"))
|
||||
{
|
||||
if(justState)
|
||||
responseString = responseString + "true}}";
|
||||
responseString = responseString + ",{\"success\":{\"/lights/" + lightId + "/state/ct\":" + state.getCt() + "}}";
|
||||
justState = false;
|
||||
responseString = responseString + ",";
|
||||
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/ct\":" + state.getCt() + "}}";
|
||||
justState = true;
|
||||
}
|
||||
|
||||
if(body.contains("xy"))
|
||||
{
|
||||
if(justState)
|
||||
responseString = responseString + "true}}";
|
||||
responseString = responseString + ",{\"success\":{\"/lights/" + lightId + "/state/xy\":" + state.getXy() + "}}";
|
||||
justState = false;
|
||||
responseString = responseString + ",";
|
||||
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/xy\":" + state.getXy() + "}}";
|
||||
justState = true;
|
||||
}
|
||||
|
||||
if(body.contains("hue"))
|
||||
{
|
||||
if(justState)
|
||||
responseString = responseString + "true}}";
|
||||
responseString = responseString + ",{\"success\":{\"/lights/" + lightId + "/state/hue\":" + state.getHue() + "}}";
|
||||
justState = false;
|
||||
responseString = responseString + ",";
|
||||
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/hue\":" + state.getHue() + "}}";
|
||||
justState = true;
|
||||
}
|
||||
|
||||
if(body.contains("sat"))
|
||||
{
|
||||
if(justState)
|
||||
responseString = responseString + "true}}";
|
||||
responseString = responseString + ",{\"success\":{\"/lights/" + lightId + "/state/sat\":" + state.getSat() + "}}";
|
||||
justState = false;
|
||||
responseString = responseString + ",";
|
||||
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/sat\":" + state.getSat() + "}}";
|
||||
justState = true;
|
||||
}
|
||||
|
||||
if(body.contains("colormode"))
|
||||
{
|
||||
if(justState)
|
||||
responseString = responseString + "true}}";
|
||||
responseString = responseString + ",{\"success\":{\"/lights/" + lightId + "/state/colormode\":" + state.getColormode() + "}}";
|
||||
justState = false;
|
||||
}
|
||||
|
||||
if(justState)
|
||||
{
|
||||
if (state.isOn()) {
|
||||
responseString = responseString + "true}}";
|
||||
state.setBri(255);
|
||||
} else if (body.contains("false")) {
|
||||
responseString = responseString + "false}}";
|
||||
state.setBri(0);
|
||||
}
|
||||
responseString = responseString + ",";
|
||||
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/colormode\":" + state.getColormode() + "}}";
|
||||
justState = true;
|
||||
}
|
||||
|
||||
responseString = responseString + "]";
|
||||
|
||||
return responseString;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,11 +21,7 @@ public class HueUtil {
|
||||
|
||||
public static final String registerWithHue(HttpClient anHttpClient, String ipAddress, String aName, String theUser, HueErrorStringSet errorStringSet) {
|
||||
UserCreateRequest theLogin = new UserCreateRequest();
|
||||
theLogin.setDevicetype("HA Bridge");
|
||||
if(theUser == null)
|
||||
theLogin.setUsername("habridge");
|
||||
else
|
||||
theLogin.setUsername(theUser);
|
||||
theLogin.setDevicetype("HABridge#MyMachine");
|
||||
HttpPost postRequest = new HttpPost("http://" + ipAddress + HUE_REQUEST);
|
||||
ContentType parsedContentType = ContentType.parse("application/json");
|
||||
StringEntity requestBody = new StringEntity(new Gson().toJson(theLogin), parsedContentType);
|
||||
|
||||
@@ -47,7 +47,7 @@ app.run( function (bridgeService) {
|
||||
|
||||
app.service('bridgeService', function ($http, $window, ngToast) {
|
||||
var self = this;
|
||||
this.state = {base: window.location.origin + "/api/devices", bridgelocation: window.location.origin, systemsbase: window.location.origin + "/system", huebase: window.location.origin + "/api", configs: [], backups: [], devices: [], device: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, habridgeversion: ""};
|
||||
this.state = {base: window.location.origin + "/api/devices", bridgelocation: window.location.origin, systemsbase: window.location.origin + "/system", huebase: window.location.origin + "/api", configs: [], backups: [], devices: [], device: [], mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, habridgeversion: ""};
|
||||
|
||||
this.displayWarn = function(errorTitle, error) {
|
||||
var toastContent = errorTitle;
|
||||
@@ -324,6 +324,7 @@ app.service('bridgeService', function ($http, $window, ngToast) {
|
||||
this.bulkAddDevice = function (devices) {
|
||||
return $http.post(this.state.base, devices).then(
|
||||
function (response) {
|
||||
self.displaySuccess("Bulk device add successful.");
|
||||
},
|
||||
function (error) {
|
||||
self.displayWarn("Bulk Add new Device Error: ", error);
|
||||
@@ -724,7 +725,12 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
|
||||
bridgeService.testUrl(device, type);
|
||||
};
|
||||
$scope.deleteDevice = function (device) {
|
||||
bridgeService.deleteDevice(device.id);
|
||||
$scope.bridge.device = device;
|
||||
ngDialog.open({
|
||||
template: 'deleteDialog',
|
||||
controller: 'DeleteDialogCtrl',
|
||||
className: 'ngdialog-theme-default'
|
||||
});
|
||||
};
|
||||
$scope.editDevice = function (device) {
|
||||
bridgeService.editDevice(device);
|
||||
@@ -789,7 +795,36 @@ app.controller('ValueDialogCtrl', function ($scope, bridgeService, ngDialog) {
|
||||
};
|
||||
});
|
||||
|
||||
app.controller('VeraController', function ($scope, $location, $http, bridgeService) {
|
||||
app.controller('DeleteDialogCtrl', function ($scope, bridgeService, ngDialog) {
|
||||
$scope.bridge = bridgeService.state;
|
||||
$scope.device = $scope.bridge.device;
|
||||
$scope.deleteDevice = function (device) {
|
||||
ngDialog.close('ngdialog1');
|
||||
bridgeService.deleteDevice(device.id);
|
||||
bridgeService.viewDevices();
|
||||
$scope.bridge.device = null;
|
||||
$scope.bridge.type = "";
|
||||
};
|
||||
});
|
||||
|
||||
app.controller('DeleteMapandIdDialogCtrl', function ($scope, bridgeService, ngDialog) {
|
||||
$scope.bridge = bridgeService.state;
|
||||
$scope.mapandid = $scope.bridge.mapandid;
|
||||
$scope.deleteMapandId = function (mapandid) {
|
||||
ngDialog.close('ngdialog1');
|
||||
bridgeService.deleteDeviceByMapId(mapandid.id, mapandid.mapType);
|
||||
bridgeService.viewDevices();
|
||||
bridgeService.viewVeraDevices();
|
||||
bridgeService.viewVeraScenes();
|
||||
bridgeService.viewHarmonyActivities();
|
||||
bridgeService.viewHarmonyDevices();
|
||||
bridgeService.viewNestItems();
|
||||
bridgeService.viewHueDevices();
|
||||
$scope.bridge.mapandid = null;
|
||||
};
|
||||
});
|
||||
|
||||
app.controller('VeraController', function ($scope, $location, $http, bridgeService, ngDialog) {
|
||||
$scope.bridge = bridgeService.state;
|
||||
$scope.device = $scope.bridge.device;
|
||||
$scope.device_dim_control = "";
|
||||
@@ -919,15 +954,17 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
|
||||
};
|
||||
|
||||
$scope.deleteDeviceByMapId = function (id, mapType) {
|
||||
bridgeService.deleteDeviceByMapId(id, mapType);
|
||||
bridgeService.viewDevices();
|
||||
bridgeService.viewVeraDevices();
|
||||
bridgeService.viewVeraScenes();
|
||||
$scope.bridge.mapandid = { id, mapType };
|
||||
ngDialog.open({
|
||||
template: 'deleteMapandIdDialog',
|
||||
controller: 'DeleteMapandIdDialogCtrl',
|
||||
className: 'ngdialog-theme-default'
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
app.controller('HarmonyController', function ($scope, $location, $http, bridgeService) {
|
||||
app.controller('HarmonyController', function ($scope, $location, $http, bridgeService, ngDialog) {
|
||||
$scope.bridge = bridgeService.state;
|
||||
$scope.device = $scope.bridge.device;
|
||||
bridgeService.viewHarmonyActivities();
|
||||
@@ -951,21 +988,22 @@ app.controller('HarmonyController', function ($scope, $location, $http, bridgeSe
|
||||
};
|
||||
|
||||
$scope.buildButtonUrls = function (harmonydevice, onbutton, offbutton) {
|
||||
bridgeService.clearDevice();
|
||||
var currentOn = $scope.device.onUrl;
|
||||
var currentOff = $scope.device.offUrl;
|
||||
var actionOn = angular.fromJson(onbutton);
|
||||
var actionOff = angular.fromJson(offbutton);
|
||||
if( $scope.device.mapType == "harmonyButton") {
|
||||
$scope.device.mapId = $scope.device.mapId + "-" + actionOn.command;
|
||||
$scope.device.onUrl = currentOn.substr(0, currentOn.indexOf("]")) + ",{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + actionOn.command + "\"}]";
|
||||
$scope.device.offUrl = currentOff.substr(0, currentOff.indexOf("]")) + ",{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + actionOff.command + "\"}]";
|
||||
}
|
||||
else if ($scope.device.mapType == null || $scope.device.mapType == "") {
|
||||
bridgeService.clearDevice();
|
||||
$scope.device.deviceType = "button";
|
||||
$scope.device.targetDevice = harmonydevice.hub;
|
||||
$scope.device.name = harmonydevice.device.label;
|
||||
$scope.device.mapType = "harmonyButton";
|
||||
$scope.device.mapId = harmonydevice.device.id + "-" + actionOn.command + "-" + actionOff.command;
|
||||
$scope.device.mapId = harmonydevice.device.id + "-" + actionOn.command;
|
||||
$scope.device.onUrl = "[{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + actionOn.command + "\"}]";
|
||||
$scope.device.offUrl = "[{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + actionOff.command + "\"}]";
|
||||
}
|
||||
@@ -996,15 +1034,17 @@ app.controller('HarmonyController', function ($scope, $location, $http, bridgeSe
|
||||
};
|
||||
|
||||
$scope.deleteDeviceByMapId = function (id, mapType) {
|
||||
bridgeService.deleteDeviceByMapId(id, mapType);
|
||||
bridgeService.viewDevices();
|
||||
bridgeService.viewHarmonyActivities();
|
||||
bridgeService.viewHarmonyDevices();
|
||||
$scope.bridge.mapandid = { id, mapType };
|
||||
ngDialog.open({
|
||||
template: 'deleteMapandIdDialog',
|
||||
controller: 'DeleteMapandIdDialogCtrl',
|
||||
className: 'ngdialog-theme-default'
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
app.controller('NestController', function ($scope, $location, $http, bridgeService) {
|
||||
app.controller('NestController', function ($scope, $location, $http, bridgeService, ngDialog) {
|
||||
$scope.bridge = bridgeService.state;
|
||||
$scope.device = $scope.bridge.device;
|
||||
bridgeService.viewNestItems();
|
||||
@@ -1116,14 +1156,17 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
|
||||
};
|
||||
|
||||
$scope.deleteDeviceByMapId = function (id, mapType) {
|
||||
bridgeService.deleteDeviceByMapId(id, mapType);
|
||||
bridgeService.viewDevices();
|
||||
bridgeService.viewNestItems();
|
||||
$scope.bridge.mapandid = { id, mapType };
|
||||
ngDialog.open({
|
||||
template: 'deleteMapandIdDialog',
|
||||
controller: 'DeleteMapandIdDialogCtrl',
|
||||
className: 'ngdialog-theme-default'
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
app.controller('HueController', function ($scope, $location, $http, bridgeService) {
|
||||
app.controller('HueController', function ($scope, $location, $http, bridgeService, ngDialog) {
|
||||
$scope.bridge = bridgeService.state;
|
||||
$scope.device = $scope.bridge.device;
|
||||
$scope.bulk = { devices: [] };
|
||||
@@ -1168,7 +1211,7 @@ app.controller('HueController', function ($scope, $location, $http, bridgeServic
|
||||
var devicesList = [];
|
||||
for(var i = 0; i < $scope.bulk.devices.length; i++) {
|
||||
for(var x = 0; x < bridgeService.state.huedevices.length; x++) {
|
||||
if(bridgeService.state.huedevices[x].id == $scope.bulk.devices[i]) {
|
||||
if(bridgeService.state.huedevices[x].device.uniqueid == $scope.bulk.devices[i]) {
|
||||
$scope.buildDeviceUrls(bridgeService.state.huedevices[x]);
|
||||
devicesList[i] = {
|
||||
name: $scope.device.name,
|
||||
@@ -1217,9 +1260,12 @@ app.controller('HueController', function ($scope, $location, $http, bridgeServic
|
||||
};
|
||||
|
||||
$scope.deleteDeviceByMapId = function (id, mapType) {
|
||||
bridgeService.deleteDeviceByMapId(id, mapType);
|
||||
bridgeService.viewDevices();
|
||||
bridgeService.viewHueDevices();
|
||||
$scope.bridge.mapandid = { id, mapType };
|
||||
ngDialog.open({
|
||||
template: 'deleteMapandIdDialog',
|
||||
controller: 'DeleteMapandIdDialogCtrl',
|
||||
className: 'ngdialog-theme-default'
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -103,4 +103,14 @@
|
||||
<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>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Harmony Activity List</h2>
|
||||
</div>
|
||||
@@ -75,7 +75,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add a Bridge Device for a Harmony Activity</h2>
|
||||
</div>
|
||||
@@ -121,3 +121,13 @@
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Harmony Device List</h2>
|
||||
</div>
|
||||
@@ -96,7 +96,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add a Bridge Device for Harmony Buttons</h2>
|
||||
</div>
|
||||
@@ -142,3 +142,13 @@
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Hue Device List ({{bridge.huedevices.length}})</h2>
|
||||
</div>
|
||||
@@ -36,7 +36,7 @@
|
||||
</thead>
|
||||
<tr ng-repeat="huedevice in bridge.huedevices | availableHueDeviceId">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]" value="{{huedevice.id}}" ng-checked="bulk.devices.indexOf(huedevice.id) > -1" ng-click="toggleSelection(huedevice.id)"> {{huedevice.device.name}}</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>
|
||||
@@ -48,7 +48,7 @@
|
||||
</scrollable-table>
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkHueDevices()">Bulk Add ({{bulk.devices.length}})</button>
|
||||
ng-click="bulkAddDevices()">Bulk Add ({{bulk.devices.length}})</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -75,7 +75,7 @@
|
||||
<td>{{huedevice.huename}}</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDeviceByMapId(huedevice.uniqueid, 'hueDevice')">Delete</button>
|
||||
ng-click="deleteDeviceByMapId(huedevice.device.uniqueid, 'hueDevice')">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -83,7 +83,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add Bridge Device for a Hue Device</h2>
|
||||
</div>
|
||||
@@ -117,3 +117,13 @@
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Nest Items List</h2>
|
||||
</div>
|
||||
@@ -97,7 +97,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add a Bridge Device for a Nest Item</h2>
|
||||
</div>
|
||||
@@ -143,3 +143,13 @@
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Vera Device List ({{bridge.veradevices.length}})</h2>
|
||||
</div>
|
||||
@@ -24,7 +24,7 @@
|
||||
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.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
@@ -100,7 +100,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add Bridge Device for a Vera Device</h2>
|
||||
</div>
|
||||
@@ -155,3 +155,13 @@
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Vera Scene List</h2>
|
||||
</div>
|
||||
@@ -81,7 +81,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add a Bridge Device for a Vera scene</h2>
|
||||
</div>
|
||||
@@ -125,3 +125,13 @@
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
||||
|
||||
Reference in New Issue
Block a user