mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-19 08:28:46 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
416b4d3fda | ||
|
|
9666273840 | ||
|
|
774bc8a36b | ||
|
|
74d4548beb | ||
|
|
eecf0f9875 | ||
|
|
ed96b5ad81 | ||
|
|
9f7d3ea331 | ||
|
|
68de92bb74 | ||
|
|
392a46c3d8 | ||
|
|
568569248a | ||
|
|
d61d10b5b6 | ||
|
|
7294dbf175 | ||
|
|
6c99358f95 | ||
|
|
eee0394f20 | ||
|
|
d87f3bc541 | ||
|
|
e38374f749 |
87
README.md
87
README.md
@@ -1,31 +1,40 @@
|
|||||||
# ha-bridge
|
# ha-bridge
|
||||||
Emulates philips hue api to other home automation gateways. The Amazon echo now supports wemo and philip hue.
|
Emulates philips hue api to other home automation gateways. The Amazon echo now supports wemo and philips hue.
|
||||||
Build
|
## Build
|
||||||
-----
|
|
||||||
|
|
||||||
To customize and build it yourself, build a new jar with maven:
|
To customize and build it yourself, build a new jar with maven:
|
||||||
```
|
```
|
||||||
mvn install
|
mvn install
|
||||||
```
|
```
|
||||||
|
Otherwise go to http://www.bwssystems.com/apps.html to download the latest jar file.
|
||||||
|
## Run
|
||||||
Then locate the jar and start the server with:
|
Then locate the jar and start the server with:
|
||||||
```
|
```
|
||||||
java -jar -Dvera.address=192.168.X.Y ha-bridge-0.X.Y.jar
|
java -jar -Dvera.address=192.168.X.Y ha-bridge-0.X.Y.jar
|
||||||
```
|
```
|
||||||
|
## Available Arguments
|
||||||
|
### -Dvera.address=`<ip address>`
|
||||||
The argument for the vera address should be given as it the system does not have a way to find the address. Supply -Dvera.address=X.Y.Z.A on the command line to provide it.
|
The argument for the vera address should be given as it the system does not have a way to find the address. Supply -Dvera.address=X.Y.Z.A on the command line to provide it.
|
||||||
|
### -Dupnp.config.address=`<ip address>`
|
||||||
The server defaults to the first available address on the host. Replace the -Dupnp.config.address=<ip address> value with the server ipv4 address you would like to use.
|
The server defaults to the first available address on the host. Replace the -Dupnp.config.address=`<ip address>` value with the server ipv4 address you would like to use.
|
||||||
|
### -Dserver.port=`<port>`
|
||||||
The server defaults to running on port 8080. If you're already running a server (like openHAB) on 8080, -Dserver.port=<port> on the command line.
|
The server defaults to running on port 8080. If you're already running a server (like openHAB) on 8080, -Dserver.port=`<port>` on the command line.
|
||||||
|
### -Dupnp.device.db=`<filepath>`
|
||||||
The default location for the db to contain the devices as they are added is "data/devices.db". If you would like a different filename or directory, specify -Dupnp.devices.db=<directory>/<filename> or <filename> if it is the same directory.
|
The default location for the db to contain the devices as they are added is "data/devices.db". If you would like a different filename or directory, specify -Dupnp.devices.db=`<directory>/<filename> or <filename>` if it is the same directory.
|
||||||
|
### -Dupnp.response.port=`<port>`
|
||||||
The default upnp response port will be 50000 otherwise it can be set with -Dupnp.response.port=<port>.
|
The upnp response port that will be used. The default is 50000.
|
||||||
|
### -Dupnp.strict=`<true|false>`
|
||||||
Then configure by going to the url for the host you are running on or localhost:
|
Upnp has been very closed on this platform to try and respond as a hue and there is now a setting to control if it is more open or strict, Add -Dupnp.strict=`<true|false>` to your command line to have the emulator respond to what it thinks is an echo to a hue or any other device. The default is upnp.strict=false.
|
||||||
|
### -Dtrace.upnp=`<true|false>`
|
||||||
|
Turn on tracing for upnp discovery messages. The default is false.
|
||||||
|
### -Dvtwo.compatibility=`<true|false>`
|
||||||
|
Turns on compatibility for upnp detection and response as it was in the original version of amazon-echo-ha-bridge. The default is true.
|
||||||
|
## Web Config
|
||||||
|
Configure by going to the url for the host you are running on or localhost with port you have assigned:
|
||||||
```
|
```
|
||||||
http://192.168.1.240:8080
|
http://<ip address>:<port>
|
||||||
```
|
```
|
||||||
or Register a device, via REST by binding some sort of on/off (vera style) url
|
## Command line configure
|
||||||
|
Register a device via REST by by using a tool and the url, for example:
|
||||||
```
|
```
|
||||||
POST http://host:8080/api/devices
|
POST http://host:8080/api/devices
|
||||||
{
|
{
|
||||||
@@ -35,9 +44,55 @@ POST http://host:8080/api/devices
|
|||||||
"offUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41"
|
"offUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
## Dimming
|
||||||
|
Dimming is also supported by using the expessions ${intensity.percent} or ${intensity.byte} for 0-100 and 0-255 respectively.
|
||||||
|
e.g.
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "entry light",
|
||||||
|
"deviceType": "switch",
|
||||||
|
"offUrl": "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=31",
|
||||||
|
"onUrl": "http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=31&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.percent}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
See the echo's documentation for the dimming phrase.
|
||||||
|
|
||||||
|
## POST/PUT support
|
||||||
|
added optional fields
|
||||||
|
* contentType (currently un-validated)
|
||||||
|
* httpVerb (POST/PUT/GET only supported)
|
||||||
|
* contentBody your post/put body here
|
||||||
|
|
||||||
|
This will allow control of any other application that may need more then GET.
|
||||||
|
e.g:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "test device",
|
||||||
|
"deviceType": "switch",
|
||||||
|
"offUrl": "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=31",
|
||||||
|
"onUrl": "http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=31&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.percent}",
|
||||||
|
"contentType" : "application/json",
|
||||||
|
"httpVerb":"POST",
|
||||||
|
"contentBody" : "{\"fooBar\":\"baz\"}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Anything that takes an action as a result of an HTTP request will probably work - like putting Vera in and out of night mode:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "night mode",
|
||||||
|
"deviceType": "switch",
|
||||||
|
"offUrl": "http://192.168.1.201:3480/data_request?id=lu_action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=SetHouseMode&Mode=1",
|
||||||
|
"onUrl": "http://192.168.1.201:3480/data_request?id=lu_action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=SetHouseMode&Mode=3"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Ask Alexa
|
||||||
After this Tell Alexa: "Alexa, discover my devices"
|
After this Tell Alexa: "Alexa, discover my devices"
|
||||||
|
|
||||||
Then you can say "Alexa, Turn on the office light" or whatever name you have given your configured devices.
|
Then you can say "Alexa, Turn on the office light" or whatever name you have given your configured devices.
|
||||||
|
|
||||||
To view or remove devices that Alexa knows about, you can use the mobile app Menu / Settings / Connected Home
|
To view or remove devices that Alexa knows about, you can use the mobile app Menu / Settings / Connected Home
|
||||||
|
## Debugging
|
||||||
|
To turn on debugging for the bridge, use the following extra parm in the command line:
|
||||||
|
```
|
||||||
|
-Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG
|
||||||
|
```
|
||||||
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>com.bwssystems.HABridge</groupId>
|
<groupId>com.bwssystems.HABridge</groupId>
|
||||||
<artifactId>ha-bridge</artifactId>
|
<artifactId>ha-bridge</artifactId>
|
||||||
<version>0.3.2</version>
|
<version>0.4.4</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>HA Bridge</name>
|
<name>HA Bridge</name>
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ public class BridgeSettings {
|
|||||||
private String upnpresponseport;
|
private String upnpresponseport;
|
||||||
private String upnpdevicedb;
|
private String upnpdevicedb;
|
||||||
private String veraaddress;
|
private String veraaddress;
|
||||||
|
private boolean upnpstrict;
|
||||||
|
private boolean traceupnp;
|
||||||
|
private boolean vtwocompatibility;
|
||||||
|
|
||||||
public String getUpnpConfigAddress() {
|
public String getUpnpConfigAddress() {
|
||||||
return upnpconfigaddress;
|
return upnpconfigaddress;
|
||||||
@@ -38,5 +41,24 @@ public class BridgeSettings {
|
|||||||
this.veraaddress = veraAddress;
|
this.veraaddress = veraAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUpnpStrict() {
|
||||||
|
return upnpstrict;
|
||||||
|
}
|
||||||
|
public void setUpnpStrict(boolean upnpStrict) {
|
||||||
|
this.upnpstrict = upnpStrict;
|
||||||
|
}
|
||||||
|
public boolean isTraceupnp() {
|
||||||
|
return traceupnp;
|
||||||
|
}
|
||||||
|
public void setTraceupnp(boolean traceupnp) {
|
||||||
|
this.traceupnp = traceupnp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVtwocompatibility() {
|
||||||
|
return vtwocompatibility;
|
||||||
|
}
|
||||||
|
public void setVtwocompatibility(boolean vtwocompatibility) {
|
||||||
|
this.vtwocompatibility = vtwocompatibility;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import com.bwssystems.HABridge.devicemanagmeent.*;
|
|||||||
import com.bwssystems.HABridge.hue.HueMulator;
|
import com.bwssystems.HABridge.hue.HueMulator;
|
||||||
import com.bwssystems.HABridge.upnp.UpnpListener;
|
import com.bwssystems.HABridge.upnp.UpnpListener;
|
||||||
import com.bwssystems.HABridge.upnp.UpnpSettingsResource;
|
import com.bwssystems.HABridge.upnp.UpnpSettingsResource;
|
||||||
import com.bwssystems.vera.VeraInfo;
|
|
||||||
|
|
||||||
public class HABridge {
|
public class HABridge {
|
||||||
|
|
||||||
@@ -55,6 +54,9 @@ public class HABridge {
|
|||||||
bridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db", "data/device.db"));
|
bridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db", "data/device.db"));
|
||||||
bridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", "50000"));
|
bridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", "50000"));
|
||||||
bridgeSettings.setVeraAddress(System.getProperty("vera.address", "192.168.1.100"));
|
bridgeSettings.setVeraAddress(System.getProperty("vera.address", "192.168.1.100"));
|
||||||
|
bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "false")));
|
||||||
|
bridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
|
||||||
|
bridgeSettings.setVtwocompatibility(Boolean.parseBoolean(System.getProperty("vtwo.compatibility", "true")));
|
||||||
|
|
||||||
// sparkjava config directive to set ip address for the web server to listen on
|
// sparkjava config directive to set ip address for the web server to listen on
|
||||||
// ipAddress("0.0.0.0"); // not used
|
// ipAddress("0.0.0.0"); // not used
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
package com.bwssystems.HABridge.api;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by arm on 4/13/15.
|
|
||||||
*/
|
|
||||||
public class Device {
|
|
||||||
private String name;
|
|
||||||
private String deviceType;
|
|
||||||
private String offUrl;
|
|
||||||
private String onUrl;
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDeviceType() {
|
|
||||||
return deviceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeviceType(String deviceType) {
|
|
||||||
this.deviceType = deviceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOffUrl() {
|
|
||||||
return offUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOffUrl(String offUrl) {
|
|
||||||
this.offUrl = offUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOnUrl() {
|
|
||||||
return onUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnUrl(String onUrl) {
|
|
||||||
this.onUrl = onUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.bwssystems.HABridge.api.hue;
|
package com.bwssystems.HABridge.api.hue;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
||||||
@@ -9,6 +10,16 @@ import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
|||||||
*/
|
*/
|
||||||
public class HueApiResponse {
|
public class HueApiResponse {
|
||||||
private Map<String, DeviceResponse> lights;
|
private Map<String, DeviceResponse> lights;
|
||||||
|
private Map<String, String> scenes;
|
||||||
|
private Map<String, String> groups;
|
||||||
|
private HueConfig config;
|
||||||
|
|
||||||
|
public HueApiResponse(String name, String ipaddress, String username, String userid) {
|
||||||
|
super();
|
||||||
|
this.setConfig(HueConfig.createConfig(name, ipaddress, username, userid));
|
||||||
|
this.setGroups(new HashMap<>());
|
||||||
|
this.setScenes(new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, DeviceResponse> getLights() {
|
public Map<String, DeviceResponse> getLights() {
|
||||||
return lights;
|
return lights;
|
||||||
@@ -17,4 +28,28 @@ public class HueApiResponse {
|
|||||||
public void setLights(Map<String, DeviceResponse> lights) {
|
public void setLights(Map<String, DeviceResponse> lights) {
|
||||||
this.lights = lights;
|
this.lights = lights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getScenes() {
|
||||||
|
return scenes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScenes(Map<String, String> scenes) {
|
||||||
|
this.scenes = scenes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getGroups() {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroups(Map<String, String> groups) {
|
||||||
|
this.groups = groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HueConfig getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfig(HueConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
198
src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java
Normal file
198
src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
package com.bwssystems.HABridge.api.hue;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class HueConfig
|
||||||
|
{
|
||||||
|
private Boolean portalservices;
|
||||||
|
private String gateway;
|
||||||
|
private String mac;
|
||||||
|
private String swversion;
|
||||||
|
private String apiversion;
|
||||||
|
private Boolean linkbutton;
|
||||||
|
private String ipaddress;
|
||||||
|
private Integer proxyport;
|
||||||
|
private Swupdate swupdate;
|
||||||
|
private String netmask;
|
||||||
|
private String name;
|
||||||
|
private Boolean dhcp;
|
||||||
|
private String UTC;
|
||||||
|
private String proxyaddress;
|
||||||
|
private String localtime;
|
||||||
|
private String timezone;
|
||||||
|
private String zigbeechannel;
|
||||||
|
private Map<String, WhitelistEntry> whitelist;
|
||||||
|
|
||||||
|
public static HueConfig createConfig(String name, String ipaddress, String devicetype, String userid) {
|
||||||
|
HueConfig aConfig = new HueConfig();
|
||||||
|
aConfig.setApiversion("1.4.0");
|
||||||
|
aConfig.setPortalservices(false);
|
||||||
|
aConfig.setGateway("192.168.1.1");
|
||||||
|
aConfig.setMac("00:00:88:00:bb:ee");
|
||||||
|
aConfig.setSwversion("01005215");
|
||||||
|
aConfig.setLinkbutton(false);
|
||||||
|
aConfig.setIpaddress(ipaddress);
|
||||||
|
aConfig.setProxyport(0);
|
||||||
|
aConfig.setSwupdate(Swupdate.createSwupdate());
|
||||||
|
aConfig.setNetmask("255.255.255.0");
|
||||||
|
aConfig.setName(name);
|
||||||
|
aConfig.setDhcp(true);
|
||||||
|
aConfig.setUtc("2014-07-17T09:27:35");
|
||||||
|
aConfig.setProxyaddress("0.0.0.0");
|
||||||
|
aConfig.setLocaltime("2014-07-17T11:27:35");
|
||||||
|
aConfig.setTimezone("America/Chicago");
|
||||||
|
aConfig.setZigbeechannel("6");
|
||||||
|
Map<String, WhitelistEntry> awhitelist = new HashMap<>();
|
||||||
|
awhitelist.put(userid, WhitelistEntry.createEntry(devicetype));
|
||||||
|
aConfig.setWhitelist(awhitelist);
|
||||||
|
|
||||||
|
|
||||||
|
return aConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Boolean getPortalservices() {
|
||||||
|
return portalservices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortalservices(Boolean portalservices) {
|
||||||
|
this.portalservices = portalservices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGateway() {
|
||||||
|
return gateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGateway(String gateway) {
|
||||||
|
this.gateway = gateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMac() {
|
||||||
|
return mac;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMac(String mac) {
|
||||||
|
this.mac = mac;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSwversion() {
|
||||||
|
return swversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSwversion(String swversion) {
|
||||||
|
this.swversion = swversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getLinkbutton() {
|
||||||
|
return linkbutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLinkbutton(Boolean linkbutton) {
|
||||||
|
this.linkbutton = linkbutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIpaddress() {
|
||||||
|
return ipaddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIpaddress(String ipaddress) {
|
||||||
|
this.ipaddress = ipaddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getProxyport() {
|
||||||
|
return proxyport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyport(Integer proxyport) {
|
||||||
|
this.proxyport = proxyport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Swupdate getSwupdate() {
|
||||||
|
return swupdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSwupdate(Swupdate swupdate) {
|
||||||
|
this.swupdate = swupdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNetmask() {
|
||||||
|
return netmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNetmask(String netmask) {
|
||||||
|
this.netmask = netmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getDhcp() {
|
||||||
|
return dhcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDhcp(Boolean dhcp) {
|
||||||
|
this.dhcp = dhcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUtc() {
|
||||||
|
return UTC;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUtc(String utc) {
|
||||||
|
this.UTC = utc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProxyaddress() {
|
||||||
|
return proxyaddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyaddress(String proxyaddress) {
|
||||||
|
this.proxyaddress = proxyaddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, WhitelistEntry> getWhitelist() {
|
||||||
|
return whitelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWhitelist(Map<String, WhitelistEntry> whitelist) {
|
||||||
|
this.whitelist = whitelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApiversion() {
|
||||||
|
return apiversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApiversion(String apiversion) {
|
||||||
|
this.apiversion = apiversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocaltime() {
|
||||||
|
return localtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocaltime(String localtime) {
|
||||||
|
this.localtime = localtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTimezone() {
|
||||||
|
return timezone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimezone(String timezone) {
|
||||||
|
this.timezone = timezone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getZigbeechannel() {
|
||||||
|
return zigbeechannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZigbeechannel(String zigbeechannel) {
|
||||||
|
this.zigbeechannel = zigbeechannel;
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/main/java/com/bwssystems/HABridge/api/hue/Swupdate.java
Normal file
52
src/main/java/com/bwssystems/HABridge/api/hue/Swupdate.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package com.bwssystems.HABridge.api.hue;
|
||||||
|
|
||||||
|
|
||||||
|
public class Swupdate
|
||||||
|
{
|
||||||
|
private String text;
|
||||||
|
private Boolean notify;
|
||||||
|
private Integer updatestate;
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
public static Swupdate createSwupdate() {
|
||||||
|
Swupdate aSwupdate = new Swupdate();
|
||||||
|
aSwupdate.setNotify(false);
|
||||||
|
aSwupdate.setText("");
|
||||||
|
aSwupdate.setUpdatestate(0);
|
||||||
|
aSwupdate.setUrl("");
|
||||||
|
return aSwupdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getNotify() {
|
||||||
|
return notify;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotify(Boolean notify) {
|
||||||
|
this.notify = notify;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getUpdatestate() {
|
||||||
|
return updatestate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdatestate(Integer updatestate) {
|
||||||
|
this.updatestate = updatestate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.bwssystems.HABridge.api.hue;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class WhitelistEntry
|
||||||
|
{
|
||||||
|
private String lastUseDate;
|
||||||
|
private String createDate;
|
||||||
|
private String name;
|
||||||
|
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||||
|
|
||||||
|
public static WhitelistEntry createEntry(String devicetype) {
|
||||||
|
WhitelistEntry anEntry = new WhitelistEntry();
|
||||||
|
anEntry.setName(devicetype);
|
||||||
|
anEntry.setCreateDate(getCurrentDate());
|
||||||
|
anEntry.setLastUseDate(getCurrentDate());
|
||||||
|
return anEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCurrentDate() {
|
||||||
|
return dateFormat.format(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastUseDate() {
|
||||||
|
return lastUseDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastUseDate(String lastUseDate) {
|
||||||
|
this.lastUseDate = lastUseDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCreateDate() {
|
||||||
|
return createDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateDate(String createDate) {
|
||||||
|
this.createDate = createDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,6 +8,9 @@ public class DeviceDescriptor{
|
|||||||
private String deviceType;
|
private String deviceType;
|
||||||
private String offUrl;
|
private String offUrl;
|
||||||
private String onUrl;
|
private String onUrl;
|
||||||
|
private String httpVerb;
|
||||||
|
private String contentType;
|
||||||
|
private String contentBody;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
@@ -48,4 +51,30 @@ public class DeviceDescriptor{
|
|||||||
public void setId(String id) {
|
public void setId(String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getHttpVerb() {
|
||||||
|
return httpVerb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHttpVerb(String httpVerb) {
|
||||||
|
this.httpVerb = httpVerb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentType(String contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentBody() {
|
||||||
|
return contentBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentBody(String contentBody) {
|
||||||
|
this.contentBody = contentBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,6 +179,15 @@ public class DeviceRepository {
|
|||||||
} else if (name.equals("onUrl")) {
|
} else if (name.equals("onUrl")) {
|
||||||
deviceEntry.setOnUrl(reader.nextString());
|
deviceEntry.setOnUrl(reader.nextString());
|
||||||
log.debug("Read a Device - device json on URL:" + deviceEntry.getOnUrl());
|
log.debug("Read a Device - device json on URL:" + deviceEntry.getOnUrl());
|
||||||
|
} else if (name.equals("httpVerb")) {
|
||||||
|
deviceEntry.setHttpVerb(reader.nextString());
|
||||||
|
log.debug("Read a Device - device json httpVerb:" + deviceEntry.getHttpVerb());
|
||||||
|
} else if (name.equals("contentType")) {
|
||||||
|
deviceEntry.setContentType(reader.nextString());
|
||||||
|
log.debug("Read a Device - device json contentType:" + deviceEntry.getContentType());
|
||||||
|
} else if (name.equals("contentBody")) {
|
||||||
|
deviceEntry.setContentBody(reader.nextString());
|
||||||
|
log.debug("Read a Device - device json contentBody:" + deviceEntry.getContentBody());
|
||||||
} else {
|
} else {
|
||||||
reader.skipValue();
|
reader.skipValue();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,12 @@ import static spark.Spark.post;
|
|||||||
import static spark.Spark.put;
|
import static spark.Spark.put;
|
||||||
import static spark.Spark.delete;
|
import static spark.Spark.delete;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -27,7 +31,7 @@ public class DeviceResource {
|
|||||||
|
|
||||||
private DeviceRepository deviceRepository;
|
private DeviceRepository deviceRepository;
|
||||||
private VeraInfo veraInfo;
|
private VeraInfo veraInfo;
|
||||||
|
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
|
||||||
|
|
||||||
public DeviceResource(BridgeSettings theSettings) {
|
public DeviceResource(BridgeSettings theSettings) {
|
||||||
super();
|
super();
|
||||||
@@ -45,30 +49,33 @@ public class DeviceResource {
|
|||||||
post(API_CONTEXT, "application/json", (request, response) -> {
|
post(API_CONTEXT, "application/json", (request, response) -> {
|
||||||
log.debug("Create a Device - request body: " + request.body());
|
log.debug("Create a Device - request body: " + request.body());
|
||||||
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
|
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
|
||||||
DeviceDescriptor deviceEntry = new DeviceDescriptor();
|
if(device.getContentBody() != null ) {
|
||||||
deviceEntry.setName(device.getName());
|
if (device.getContentType() == null || device.getHttpVerb() == null || !supportedVerbs.contains(device.getHttpVerb().toLowerCase())) {
|
||||||
log.debug("Create a Device - device json name: " + deviceEntry.getName());
|
device = null;
|
||||||
deviceEntry.setDeviceType(device.getDeviceType());
|
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||||
log.debug("Create a Device - device json type:" + deviceEntry.getDeviceType());
|
log.debug("Bad http verb in create a Device: " + request.body());
|
||||||
deviceEntry.setOnUrl(device.getOnUrl());
|
return device;
|
||||||
log.debug("Create a Device - device json on URL:" + deviceEntry.getOnUrl());
|
}
|
||||||
deviceEntry.setOffUrl(device.getOffUrl());
|
}
|
||||||
log.debug("Create a Device - device json off URL:" + deviceEntry.getOffUrl());
|
|
||||||
|
|
||||||
deviceRepository.save(deviceEntry);
|
deviceRepository.save(device);
|
||||||
log.debug("Created a Device: " + request.body());
|
log.debug("Created a Device: " + request.body());
|
||||||
|
|
||||||
response.status(201);
|
response.status(HttpStatus.SC_CREATED);
|
||||||
return deviceEntry;
|
|
||||||
|
return device;
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|
||||||
put (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
put (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
||||||
|
log.debug("Edit a Device - request body: " + request.body());
|
||||||
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
|
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
|
||||||
DeviceDescriptor deviceEntry = deviceRepository.findOne(request.params(":id"));
|
DeviceDescriptor deviceEntry = deviceRepository.findOne(request.params(":id"));
|
||||||
if(deviceEntry == null){
|
if(deviceEntry == null){
|
||||||
log.debug("Could not save an edited Device Id: " + request.params(":id"));
|
log.debug("Could not save an edited Device Id: " + request.params(":id"));
|
||||||
return null;
|
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
log.debug("Saving an edited Device: " + deviceEntry.getName());
|
log.debug("Saving an edited Device: " + deviceEntry.getName());
|
||||||
|
|
||||||
deviceEntry.setName(device.getName());
|
deviceEntry.setName(device.getName());
|
||||||
@@ -76,8 +83,13 @@ public class DeviceResource {
|
|||||||
deviceEntry.setDeviceType(device.getDeviceType());
|
deviceEntry.setDeviceType(device.getDeviceType());
|
||||||
deviceEntry.setOnUrl(device.getOnUrl());
|
deviceEntry.setOnUrl(device.getOnUrl());
|
||||||
deviceEntry.setOffUrl(device.getOffUrl());
|
deviceEntry.setOffUrl(device.getOffUrl());
|
||||||
|
deviceEntry.setHttpVerb(device.getHttpVerb());
|
||||||
|
deviceEntry.setContentType(device.getContentType());
|
||||||
|
deviceEntry.setContentBody(device.getContentBody());
|
||||||
|
|
||||||
deviceRepository.save(deviceEntry);
|
deviceRepository.save(deviceEntry);
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
|
}
|
||||||
return deviceEntry;
|
return deviceEntry;
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|
||||||
@@ -87,25 +99,30 @@ public class DeviceResource {
|
|||||||
JsonTransformer aRenderer = new JsonTransformer();
|
JsonTransformer aRenderer = new JsonTransformer();
|
||||||
String theStream = aRenderer.render(deviceList);
|
String theStream = aRenderer.render(deviceList);
|
||||||
log.debug("The Device List: " + theStream);
|
log.debug("The Device List: " + theStream);
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
return deviceList;
|
return deviceList;
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|
||||||
get (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
get (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
||||||
log.debug("Get a device");
|
log.debug("Get a device");
|
||||||
DeviceDescriptor descriptor = deviceRepository.findOne(request.params(":id"));
|
DeviceDescriptor descriptor = deviceRepository.findOne(request.params(":id"));
|
||||||
if(descriptor == null){
|
if(descriptor == null)
|
||||||
return null;
|
response.status(HttpStatus.SC_NOT_FOUND);
|
||||||
}
|
else
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|
||||||
delete (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
delete (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
||||||
log.debug("Delete a device");
|
log.debug("Delete a device");
|
||||||
DeviceDescriptor deleted = deviceRepository.findOne(request.params(":id"));
|
DeviceDescriptor deleted = deviceRepository.findOne(request.params(":id"));
|
||||||
if(deleted == null){
|
if(deleted == null)
|
||||||
return null;
|
response.status(HttpStatus.SC_NOT_FOUND);
|
||||||
}
|
else
|
||||||
|
{
|
||||||
deviceRepository.delete(deleted);
|
deviceRepository.delete(deleted);
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|
||||||
@@ -113,8 +130,11 @@ public class DeviceResource {
|
|||||||
log.debug("Get vera devices");
|
log.debug("Get vera devices");
|
||||||
Sdata sData = veraInfo.getSdata();
|
Sdata sData = veraInfo.getSdata();
|
||||||
if(sData == null){
|
if(sData == null){
|
||||||
|
response.status(HttpStatus.SC_NOT_FOUND);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
return sData.getDevices();
|
return sData.getDevices();
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|
||||||
@@ -122,8 +142,10 @@ public class DeviceResource {
|
|||||||
log.debug("Get vera scenes");
|
log.debug("Get vera scenes");
|
||||||
Sdata sData = veraInfo.getSdata();
|
Sdata sData = veraInfo.getSdata();
|
||||||
if(sData == null){
|
if(sData == null){
|
||||||
|
response.status(HttpStatus.SC_NOT_FOUND);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
return sData.getScenes();
|
return sData.getScenes();
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,14 @@ import static spark.Spark.post;
|
|||||||
import static spark.Spark.put;
|
import static spark.Spark.put;
|
||||||
|
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.methods.HttpPut;
|
||||||
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
import org.apache.http.impl.client.HttpClients;
|
import org.apache.http.impl.client.HttpClients;
|
||||||
import org.apache.http.util.EntityUtils;
|
import org.apache.http.util.EntityUtils;
|
||||||
|
|
||||||
@@ -44,7 +50,7 @@ public class HueMulator {
|
|||||||
|
|
||||||
|
|
||||||
public HueMulator(DeviceRepository aDeviceRepository){
|
public HueMulator(DeviceRepository aDeviceRepository){
|
||||||
httpClient = HttpClients.createMinimal();
|
httpClient = HttpClients.createDefault();
|
||||||
mapper = new ObjectMapper(); //armzilla: work around Echo incorrect content type and breaking mapping. Map manually
|
mapper = new ObjectMapper(); //armzilla: work around Echo incorrect content type and breaking mapping. Map manually
|
||||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
repository = aDeviceRepository;
|
repository = aDeviceRepository;
|
||||||
@@ -64,20 +70,58 @@ public class HueMulator {
|
|||||||
DeviceResponse deviceResponse = DeviceResponse.createResponse(device.getName(), device.getId());
|
DeviceResponse deviceResponse = DeviceResponse.createResponse(device.getName(), device.getId());
|
||||||
deviceResponseMap.put(device.getId(), deviceResponse);
|
deviceResponseMap.put(device.getId(), deviceResponse);
|
||||||
}
|
}
|
||||||
response.status(200);
|
response.type("application/json; charset=utf-8");
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
return deviceResponseMap;
|
return deviceResponseMap;
|
||||||
} , new JsonTransformer());
|
} , new JsonTransformer());
|
||||||
|
|
||||||
// http://ip_address:port/api with body of user request returns json object for a success of user add
|
// http://ip_address:port/api with body of user request returns json object for a success of user add
|
||||||
post(HUE_CONTEXT, "application/json", (request, response) -> {
|
post(HUE_CONTEXT, "application/json", (request, response) -> {
|
||||||
|
UserCreateRequest aNewUser = null;
|
||||||
|
String newUser = null;
|
||||||
|
String aDeviceType = null;
|
||||||
|
|
||||||
log.debug("hue api user create requested: " + request.body() + " from " + request.ip());
|
log.debug("hue api user create requested: " + request.body() + " from " + request.ip());
|
||||||
UserCreateRequest aNewUser = new Gson().fromJson(request.body(), UserCreateRequest.class);
|
|
||||||
String newUser = aNewUser.getUsername();
|
if(request.body() != null && !request.body().isEmpty()) {
|
||||||
|
aNewUser = new Gson().fromJson(request.body(), UserCreateRequest.class);
|
||||||
|
newUser = aNewUser.getUsername();
|
||||||
|
aDeviceType = aNewUser.getDevicetype();
|
||||||
|
}
|
||||||
if(newUser == null)
|
if(newUser == null)
|
||||||
newUser = "lightssystem";
|
newUser = "lightssystem";
|
||||||
log.debug("hue api user create requested for device type: " + aNewUser.getDevicetype() + " and username: " + newUser);
|
|
||||||
|
|
||||||
response.status(200);
|
if(aDeviceType == null)
|
||||||
|
aDeviceType = "<not given>";
|
||||||
|
log.debug("hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
|
||||||
|
|
||||||
|
response.type("application/json; charset=utf-8");
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
|
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
|
||||||
|
} );
|
||||||
|
|
||||||
|
// http://ip_address:port/api/* with body of user request returns json object for a success of user add - This method is for Harmony Hub
|
||||||
|
post(HUE_CONTEXT + "/*", "application/json", (request, response) -> {
|
||||||
|
UserCreateRequest aNewUser = null;
|
||||||
|
String newUser = null;
|
||||||
|
String aDeviceType = null;
|
||||||
|
|
||||||
|
log.info("HH trace: hue api user create requested: " + request.body() + " from " + request.ip());
|
||||||
|
|
||||||
|
if(request.body() != null && !request.body().isEmpty()) {
|
||||||
|
aNewUser = new Gson().fromJson(request.body(), UserCreateRequest.class);
|
||||||
|
newUser = aNewUser.getUsername();
|
||||||
|
aDeviceType = aNewUser.getDevicetype();
|
||||||
|
}
|
||||||
|
if(newUser == null)
|
||||||
|
newUser = "lightssystem";
|
||||||
|
|
||||||
|
if(aDeviceType == null)
|
||||||
|
aDeviceType = "<not given>";
|
||||||
|
log.debug("HH trace: hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
|
||||||
|
|
||||||
|
response.type("application/json; charset=utf-8");
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
|
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -87,7 +131,7 @@ public class HueMulator {
|
|||||||
log.debug("hue api full state requested: " + userId + " from " + request.ip());
|
log.debug("hue api full state requested: " + userId + " from " + request.ip());
|
||||||
List<DeviceDescriptor> descriptorList = repository.findAll();
|
List<DeviceDescriptor> descriptorList = repository.findAll();
|
||||||
if (descriptorList == null) {
|
if (descriptorList == null) {
|
||||||
response.status(404);
|
response.status(HttpStatus.SC_NOT_FOUND);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Map<String, DeviceResponse> deviceList = new HashMap<>();
|
Map<String, DeviceResponse> deviceList = new HashMap<>();
|
||||||
@@ -97,10 +141,11 @@ public class HueMulator {
|
|||||||
deviceList.put(descriptor.getId(), deviceResponse);
|
deviceList.put(descriptor.getId(), deviceResponse);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
HueApiResponse apiResponse = new HueApiResponse();
|
HueApiResponse apiResponse = new HueApiResponse("Philips hue", request.ip(), "My App", userId);
|
||||||
apiResponse.setLights(deviceList);
|
apiResponse.setLights(deviceList);
|
||||||
|
|
||||||
response.status(200);
|
response.type("application/json; charset=utf-8");
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
return apiResponse;
|
return apiResponse;
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|
||||||
@@ -111,14 +156,15 @@ public class HueMulator {
|
|||||||
log.debug("hue light requested: " + lightId + " for user: " + userId + " from " + request.ip());
|
log.debug("hue light requested: " + lightId + " for user: " + userId + " from " + request.ip());
|
||||||
DeviceDescriptor device = repository.findOne(lightId);
|
DeviceDescriptor device = repository.findOne(lightId);
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
response.status(404);
|
response.status(HttpStatus.SC_NOT_FOUND);
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
log.debug("found device named: " + device.getName());
|
log.debug("found device named: " + device.getName());
|
||||||
}
|
}
|
||||||
DeviceResponse lightResponse = DeviceResponse.createResponse(device.getName(), device.getId());
|
DeviceResponse lightResponse = DeviceResponse.createResponse(device.getName(), device.getId());
|
||||||
|
|
||||||
response.status(200);
|
response.type("application/json; charset=utf-8");
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
return lightResponse;
|
return lightResponse;
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|
||||||
@@ -137,13 +183,13 @@ public class HueMulator {
|
|||||||
state = mapper.readValue(request.body(), DeviceState.class);
|
state = mapper.readValue(request.body(), DeviceState.class);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Object mapper barfed on input of body.", e);
|
log.error("Object mapper barfed on input of body.", e);
|
||||||
response.status(400);
|
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceDescriptor device = repository.findOne(lightId);
|
DeviceDescriptor device = repository.findOne(lightId);
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
response.status(404);
|
response.status(HttpStatus.SC_NOT_FOUND);
|
||||||
log.error("Could not find devcie: " + lightId + " for hue state change request: " + userId + " from " + request.ip() + " body: " + request.body());
|
log.error("Could not find devcie: " + lightId + " for hue state change request: " + userId + " from " + request.ip() + " body: " + request.body());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -158,6 +204,22 @@ public class HueMulator {
|
|||||||
url = device.getOffUrl();
|
url = device.getOffUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//quick template
|
||||||
|
url = replaceIntensityValue(url, state.getBri());
|
||||||
|
String body = replaceIntensityValue(device.getContentBody(), state.getBri());
|
||||||
|
//make call
|
||||||
|
if(!doHttpRequest(url, device.getHttpVerb(), device.getContentType(), body)){
|
||||||
|
response.status(HttpStatus.SC_SERVICE_UNAVAILABLE);
|
||||||
|
log.error("Error on calling url to change device state: " + url);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.type("application/json; charset=utf-8");
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
|
return responseString;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/* light weight templating here, was going to use free marker but it was a bit too
|
/* light weight templating here, was going to use free marker but it was a bit too
|
||||||
* heavy for what we were trying to do.
|
* heavy for what we were trying to do.
|
||||||
*
|
*
|
||||||
@@ -165,37 +227,45 @@ public class HueMulator {
|
|||||||
* intensity.byte : 0-255 brightness. this is raw from the echo
|
* intensity.byte : 0-255 brightness. this is raw from the echo
|
||||||
* intensity.percent : 0-100, adjusted for the vera
|
* intensity.percent : 0-100, adjusted for the vera
|
||||||
*/
|
*/
|
||||||
if(url.contains(INTENSITY_BYTE)){
|
protected String replaceIntensityValue(String request, int intensity){
|
||||||
String intensityByte = String.valueOf(state.getBri());
|
if(request == null){
|
||||||
url = url.replace(INTENSITY_BYTE, intensityByte);
|
return "";
|
||||||
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/bri\":"+ String.valueOf(state.getBri()) + "}}]";
|
}
|
||||||
}else if(url.contains(INTENSITY_PERCENT)){
|
if(request.contains(INTENSITY_BYTE)){
|
||||||
int percentBrightness = (int) Math.round(state.getBri()/255.0*100);
|
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);
|
||||||
String intensityPercent = String.valueOf(percentBrightness);
|
String intensityPercent = String.valueOf(percentBrightness);
|
||||||
url = url.replace(INTENSITY_PERCENT, intensityPercent);
|
request = request.replace(INTENSITY_PERCENT, intensityPercent);
|
||||||
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/bri\":"+ String.valueOf(state.getBri()) + "}}]";
|
}
|
||||||
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
//make call
|
|
||||||
if(!doHttpGETRequest(url)){
|
|
||||||
response.status(503);
|
|
||||||
log.error("Error on calling url to change device state: " + url);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
response.status(200);
|
|
||||||
return responseString;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function executes the url from the device repository against the vera
|
// This function executes the url from the device repository against the vera
|
||||||
protected boolean doHttpGETRequest(String url) {
|
protected boolean doHttpRequest(String url, String httpVerb, String contentType, String body) {
|
||||||
log.debug("calling GET on URL: " + url);
|
HttpUriRequest request = null;
|
||||||
HttpGet httpGet = new HttpGet(url);
|
if(HttpGet.METHOD_NAME.equalsIgnoreCase(httpVerb) || httpVerb == null) {
|
||||||
|
request = new HttpGet(url);
|
||||||
|
}else if(HttpPost.METHOD_NAME.equalsIgnoreCase(httpVerb)){
|
||||||
|
HttpPost postRequest = new HttpPost(url);
|
||||||
|
ContentType parsedContentType = ContentType.parse(contentType);
|
||||||
|
StringEntity requestBody = new StringEntity(body, parsedContentType);
|
||||||
|
postRequest.setEntity(requestBody);
|
||||||
|
request = postRequest;
|
||||||
|
}else if(HttpPut.METHOD_NAME.equalsIgnoreCase(httpVerb)){
|
||||||
|
HttpPut putRequest = new HttpPut(url);
|
||||||
|
ContentType parsedContentType = ContentType.parse(contentType);
|
||||||
|
StringEntity requestBody = new StringEntity(body, parsedContentType);
|
||||||
|
putRequest.setEntity(requestBody);
|
||||||
|
request = putRequest;
|
||||||
|
}
|
||||||
|
log.debug("Making outbound call in doHttpRequest: " + request);
|
||||||
try {
|
try {
|
||||||
HttpResponse response = httpClient.execute(httpGet);
|
HttpResponse response = httpClient.execute(request);
|
||||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||||
log.debug("GET on URL responded: " + response.getStatusLine().getStatusCode());
|
log.debug("Execute on URL responded: " + response.getStatusLine().getStatusCode());
|
||||||
if(response.getStatusLine().getStatusCode() == 200){
|
if(response.getStatusLine().getStatusCode() == 200){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,24 @@ public class UpnpListener {
|
|||||||
|
|
||||||
private String responseAddress;
|
private String responseAddress;
|
||||||
|
|
||||||
|
private boolean strict;
|
||||||
|
|
||||||
|
private boolean traceupnp;
|
||||||
|
|
||||||
|
private boolean vTwoCompatibility;
|
||||||
|
|
||||||
public UpnpListener(BridgeSettings theSettings) {
|
public UpnpListener(BridgeSettings theSettings) {
|
||||||
super();
|
super();
|
||||||
upnpResponsePort = Integer.valueOf(theSettings.getUpnpResponsePort());
|
upnpResponsePort = Integer.valueOf(theSettings.getUpnpResponsePort());
|
||||||
httpServerPort = Integer.valueOf(theSettings.getServerPort());
|
httpServerPort = Integer.valueOf(theSettings.getServerPort());
|
||||||
responseAddress = theSettings.getUpnpConfigAddress();
|
responseAddress = theSettings.getUpnpConfigAddress();
|
||||||
|
strict = theSettings.isUpnpStrict();
|
||||||
|
traceupnp = theSettings.isTraceupnp();
|
||||||
|
vTwoCompatibility = theSettings.isVtwocompatibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startListening(){
|
public void startListening(){
|
||||||
log.info("UPNP Discovery Listener started....");
|
log.info("UPNP Discovery Listener starting....");
|
||||||
|
|
||||||
try (DatagramSocket responseSocket = new DatagramSocket(upnpResponsePort);
|
try (DatagramSocket responseSocket = new DatagramSocket(upnpResponsePort);
|
||||||
MulticastSocket upnpMulticastSocket = new MulticastSocket(UPNP_DISCOVERY_PORT);) {
|
MulticastSocket upnpMulticastSocket = new MulticastSocket(UPNP_DISCOVERY_PORT);) {
|
||||||
@@ -46,6 +55,9 @@ public class UpnpListener {
|
|||||||
|
|
||||||
while (addrs.hasMoreElements()) {
|
while (addrs.hasMoreElements()) {
|
||||||
InetAddress addr = addrs.nextElement();
|
InetAddress addr = addrs.nextElement();
|
||||||
|
if(traceupnp)
|
||||||
|
log.info("Traceupnp: " + name + " ... has addr " + addr);
|
||||||
|
else
|
||||||
log.debug(name + " ... has addr " + addr);
|
log.debug(name + " ... has addr " + addr);
|
||||||
if (InetAddressUtils.isIPv4Address(addr.getHostAddress())) {
|
if (InetAddressUtils.isIPv4Address(addr.getHostAddress())) {
|
||||||
IPsPerNic++;
|
IPsPerNic++;
|
||||||
@@ -54,29 +66,33 @@ public class UpnpListener {
|
|||||||
log.debug("Checking " + name + " to our interface set");
|
log.debug("Checking " + name + " to our interface set");
|
||||||
if (IPsPerNic > 0) {
|
if (IPsPerNic > 0) {
|
||||||
upnpMulticastSocket.joinGroup(socketAddress, xface);
|
upnpMulticastSocket.joinGroup(socketAddress, xface);
|
||||||
|
if(traceupnp)
|
||||||
|
log.info("Traceupnp: Adding " + name + " to our interface set");
|
||||||
|
else
|
||||||
log.debug("Adding " + name + " to our interface set");
|
log.debug("Adding " + name + " to our interface set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("UPNP Discovery Listener running and ready....");
|
||||||
|
|
||||||
while(true){ //trigger shutdown here
|
while(true){ //trigger shutdown here
|
||||||
byte[] buf = new byte[1024];
|
byte[] buf = new byte[1024];
|
||||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||||
upnpMulticastSocket.receive(packet);
|
upnpMulticastSocket.receive(packet);
|
||||||
String packetString = new String(packet.getData());
|
String packetString = new String(packet.getData());
|
||||||
if(isSSDPDiscovery(packetString)){
|
if(packetString != null && packetString.contains("M-SEARCH")) {
|
||||||
try {
|
if(traceupnp)
|
||||||
Thread.sleep(3000);
|
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||||
} catch (InterruptedException e) {
|
else
|
||||||
// TODO Auto-generated catch block
|
log.debug("Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||||
log.error("could not sleep");
|
|
||||||
}
|
}
|
||||||
log.debug("Got SSDP Discovery packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
|
if(isSSDPDiscovery(packetString)){
|
||||||
sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort());
|
sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("UpnpListener encountered an error. Shutting down", e);
|
log.error("UpnpListener encountered an error opening sockets. Shutting down", e);
|
||||||
|
|
||||||
}
|
}
|
||||||
log.info("UPNP Discovery Listener Stopped");
|
log.info("UPNP Discovery Listener Stopped");
|
||||||
@@ -91,9 +107,24 @@ public class UpnpListener {
|
|||||||
protected boolean isSSDPDiscovery(String body){
|
protected boolean isSSDPDiscovery(String body){
|
||||||
// log.debug("Check if this is a MAN ssdp-discover packet for a upnp basic device: " + body);
|
// log.debug("Check if this is a MAN ssdp-discover packet for a upnp basic device: " + body);
|
||||||
//Only respond to discover request for upnp basic device from echo, the others are for the wemo
|
//Only respond to discover request for upnp basic device from echo, the others are for the wemo
|
||||||
if(body != null && body.startsWith("M-SEARCH * HTTP/1.1") && body.contains("MAN: \"ssdp:discover\"")&& body.contains("ST: urn:schemas-upnp-org:device:basic:1")){
|
if(body != null && body.contains("M-SEARCH") && body.contains("\"ssdp:discover\"")){
|
||||||
|
if(traceupnp)
|
||||||
|
log.info("Traceupnp: isSSDPDiscovery found message to be an M-SEARCH message.");
|
||||||
|
if(strict && body.startsWith("M-SEARCH * HTTP/1.1") && body.contains("MAN: \"ssdp:discover\"") && (body.contains("ST: urn:schemas-upnp-org:device:basic:1") || body.contains("ST: upnp:rootdevice") || body.contains("ST: ssdp:all")))
|
||||||
|
{
|
||||||
|
if(traceupnp)
|
||||||
|
log.info("Traceupnp: isSSDPDiscovery found message to be valid under strict rules - strict: " + strict + ", vTwo.Compatibility: " + vTwoCompatibility);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (!strict || vTwoCompatibility)
|
||||||
|
{
|
||||||
|
if(traceupnp)
|
||||||
|
log.info("Traceupnp: isSSDPDiscovery found message to be valid under loose rules - strict: " + strict + ", vTwo.Compatibility: " + vTwoCompatibility);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(traceupnp)
|
||||||
|
log.info("Traceupnp: isSSDPDiscovery found message to not be valid - strict: " + strict + ", vTwo.Compatibility: " + vTwoCompatibility);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,9 +135,24 @@ public class UpnpListener {
|
|||||||
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1\r\n" +
|
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1\r\n" +
|
||||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
|
"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";
|
"USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n";
|
||||||
|
String discoveryTemplateVTwo = "HTTP/1.1 200 OK\r\n" +
|
||||||
|
"CACHE-CONTROL: max-age=86400\r\n" +
|
||||||
|
"EXT:\r\n" +
|
||||||
|
"LOCATION: http://%s:%s/description.xml\r\n" +
|
||||||
|
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" +
|
||||||
|
"01-NLS: %s\r\n" +
|
||||||
|
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
|
||||||
|
"USN: uuid:Socket-1_0-221438K0100073::urn:Belkin:device:**\r\n\r\n";
|
||||||
protected void sendUpnpResponse(DatagramSocket socket, InetAddress requester, int sourcePort) throws IOException {
|
protected void sendUpnpResponse(DatagramSocket socket, InetAddress requester, int sourcePort) throws IOException {
|
||||||
String discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort, getRandomUUIDString());
|
String discoveryResponse = null;
|
||||||
log.debug("sndUpnpResponse: " + discoveryResponse);
|
if(vTwoCompatibility)
|
||||||
|
discoveryResponse = String.format(discoveryTemplateVTwo, responseAddress, httpServerPort, getRandomUUIDString());
|
||||||
|
else
|
||||||
|
discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort, getRandomUUIDString());
|
||||||
|
if(traceupnp)
|
||||||
|
log.info("Traceupnp: sendUpnpResponse: " + discoveryResponse);
|
||||||
|
else
|
||||||
|
log.debug("sendUpnpResponse: " + discoveryResponse);
|
||||||
DatagramPacket response = new DatagramPacket(discoveryResponse.getBytes(), discoveryResponse.length(), requester, sourcePort);
|
DatagramPacket response = new DatagramPacket(discoveryResponse.getBytes(), discoveryResponse.length(), requester, sourcePort);
|
||||||
socket.send(response);
|
socket.send(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ public class UpnpSettingsResource {
|
|||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(UpnpSettingsResource.class);
|
private Logger log = LoggerFactory.getLogger(UpnpSettingsResource.class);
|
||||||
|
|
||||||
private String hueTemplate = "<?xml version=\"1.0\"?>\n" + "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
|
private BridgeSettings theSettings;
|
||||||
|
|
||||||
|
private String hueTemplate = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
|
||||||
+ "<specVersion>\n" + "<major>1</major>\n" + "<minor>0</minor>\n" + "</specVersion>\n"
|
+ "<specVersion>\n" + "<major>1</major>\n" + "<minor>0</minor>\n" + "</specVersion>\n"
|
||||||
+ "<URLBase>http://%s:%s/</URLBase>\n" + // hostname string
|
+ "<URLBase>http://%s:%s/</URLBase>\n" + // hostname string
|
||||||
"<device>\n" + "<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\n"
|
"<device>\n" + "<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\n"
|
||||||
@@ -26,7 +28,7 @@ public class UpnpSettingsResource {
|
|||||||
+ "<modelDescription>Hue Emulator for HA bridge</modelDescription>\n"
|
+ "<modelDescription>Hue Emulator for HA bridge</modelDescription>\n"
|
||||||
+ "<modelName>Philips hue bridge 2012</modelName>\n" + "<modelNumber>929000226503</modelNumber>\n"
|
+ "<modelName>Philips hue bridge 2012</modelName>\n" + "<modelNumber>929000226503</modelNumber>\n"
|
||||||
+ "<modelURL>http://www.bwssystems.com/apps.html</modelURL>\n"
|
+ "<modelURL>http://www.bwssystems.com/apps.html</modelURL>\n"
|
||||||
+ "<serialNumber>01189998819991197253</serialNumber>\n"
|
+ "<serialNumber>0017880ae670</serialNumber>\n"
|
||||||
+ "<UDN>uuid:88f6698f-2c83-4393-bd03-cd54a9f8595</UDN>\n" + "<serviceList>\n" + "<service>\n"
|
+ "<UDN>uuid:88f6698f-2c83-4393-bd03-cd54a9f8595</UDN>\n" + "<serviceList>\n" + "<service>\n"
|
||||||
+ "<serviceType>(null)</serviceType>\n" + "<serviceId>(null)</serviceId>\n"
|
+ "<serviceType>(null)</serviceType>\n" + "<serviceId>(null)</serviceId>\n"
|
||||||
+ "<controlURL>(null)</controlURL>\n" + "<eventSubURL>(null)</eventSubURL>\n"
|
+ "<controlURL>(null)</controlURL>\n" + "<eventSubURL>(null)</eventSubURL>\n"
|
||||||
@@ -38,20 +40,89 @@ public class UpnpSettingsResource {
|
|||||||
+ "<depth>24</depth>\n" + "<url>hue_logo_3.png</url>\n" + "</icon>\n" + "</iconList>\n" + "</device>\n"
|
+ "<depth>24</depth>\n" + "<url>hue_logo_3.png</url>\n" + "</icon>\n" + "</iconList>\n" + "</device>\n"
|
||||||
+ "</root>\n";
|
+ "</root>\n";
|
||||||
|
|
||||||
|
private String hueTemplateVTwo = "<?xml version=\"1.0\"?>\n" +
|
||||||
|
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n" +
|
||||||
|
"<specVersion>\n" +
|
||||||
|
"<major>1</major>\n" +
|
||||||
|
"<minor>0</minor>\n" +
|
||||||
|
"</specVersion>\n" +
|
||||||
|
"<URLBase>http://%s:%s/</URLBase>\n" + //hostname string
|
||||||
|
"<device>\n" +
|
||||||
|
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\n" +
|
||||||
|
"<friendlyName>Amazon-Echo-HA-Bridge (%s)</friendlyName>\n" +
|
||||||
|
"<manufacturer>Royal Philips Electronics</manufacturer>\n" +
|
||||||
|
"<manufacturerURL>http://www.bwssystems.com</manufacturerURL>\n" +
|
||||||
|
"<modelDescription>Hue Emulator for Amazon Echo bridge</modelDescription>\n" +
|
||||||
|
"<modelName>Philips hue bridge 2012</modelName>\n" +
|
||||||
|
"<modelNumber>929000226503</modelNumber>\n" +
|
||||||
|
"<modelURL>http://www.bwssystems.com/apps.html</modelURL>\n" +
|
||||||
|
"<serialNumber>01189998819991197253</serialNumber>\n" +
|
||||||
|
"<UDN>uuid:88f6698f-2c83-4393-bd03-cd54a9f8595</UDN>\n" +
|
||||||
|
"<serviceList>\n" +
|
||||||
|
"<service>\n" +
|
||||||
|
"<serviceType>(null)</serviceType>\n" +
|
||||||
|
"<serviceId>(null)</serviceId>\n" +
|
||||||
|
"<controlURL>(null)</controlURL>\n" +
|
||||||
|
"<eventSubURL>(null)</eventSubURL>\n" +
|
||||||
|
"<SCPDURL>(null)</SCPDURL>\n" +
|
||||||
|
"</service>\n" +
|
||||||
|
"</serviceList>\n" +
|
||||||
|
"<presentationURL>index.html</presentationURL>\n" +
|
||||||
|
"<iconList>\n" +
|
||||||
|
"<icon>\n" +
|
||||||
|
"<mimetype>image/png</mimetype>\n" +
|
||||||
|
"<height>48</height>\n" +
|
||||||
|
"<width>48</width>\n" +
|
||||||
|
"<depth>24</depth>\n" +
|
||||||
|
"<url>hue_logo_0.png</url>\n" +
|
||||||
|
"</icon>\n" +
|
||||||
|
"<icon>\n" +
|
||||||
|
"<mimetype>image/png</mimetype>\n" +
|
||||||
|
"<height>120</height>\n" +
|
||||||
|
"<width>120</width>\n" +
|
||||||
|
"<depth>24</depth>\n" +
|
||||||
|
"<url>hue_logo_3.png</url>\n" +
|
||||||
|
"</icon>\n" +
|
||||||
|
"</iconList>\n" +
|
||||||
|
"</device>\n" +
|
||||||
|
"</root>\n";
|
||||||
|
|
||||||
public UpnpSettingsResource(BridgeSettings theSettings) {
|
public UpnpSettingsResource(BridgeSettings theSettings) {
|
||||||
super();
|
super();
|
||||||
setupListener(theSettings);
|
this.theSettings = theSettings;
|
||||||
|
setupListener(this.theSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupListener (BridgeSettings theSettings) {
|
private void setupListener (BridgeSettings theSettings) {
|
||||||
log.info("Hue description service started....");
|
log.info("Hue description service started....");
|
||||||
// http://ip_adress:port/description.xml which returns the xml configuration for the hue emulator
|
// http://ip_adress:port/description.xml which returns the xml configuration for the hue emulator
|
||||||
get("/description.xml", "application/xml", (request, response) -> {
|
get("/description.xml", "application/xml; charset=utf-8", (request, response) -> {
|
||||||
log.debug("upnp device settings requested: " + request.params(":id") + " from " + request.ip());
|
if(theSettings.isTraceupnp())
|
||||||
|
log.info("Traceupnp: upnp device settings requested: " + request.params(":id") + " from " + request.ip() + ":" + request.port());
|
||||||
|
else
|
||||||
|
log.debug("upnp device settings requested: " + request.params(":id") + " from " + request.ip() + ":" + request.port());
|
||||||
String portNumber = Integer.toString(request.port());
|
String portNumber = Integer.toString(request.port());
|
||||||
String filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress());
|
String filledTemplate = null;
|
||||||
|
if(theSettings.isVtwocompatibility())
|
||||||
|
filledTemplate = String.format(hueTemplateVTwo, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress());
|
||||||
|
else
|
||||||
|
filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress());
|
||||||
|
if(theSettings.isTraceupnp())
|
||||||
|
log.info("Traceupnp: upnp device settings response: " + filledTemplate);
|
||||||
|
else
|
||||||
log.debug("upnp device settings response: " + filledTemplate);
|
log.debug("upnp device settings response: " + filledTemplate);
|
||||||
response.status(201);
|
// response.header("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
|
||||||
|
// response.header("Pragma", "no-cache");
|
||||||
|
// response.header("Expires", "Mon, 1 Aug 2011 09:00:00 GMT");
|
||||||
|
// response.header("Connection", "close"); // Not sure if the server will actually close the connections by just setting the header
|
||||||
|
// response.header("Access-Control-Max-Age", "0");
|
||||||
|
// response.header("Access-Control-Allow-Origin", "*");
|
||||||
|
// response.header("Access-Control-Allow-Credentials", "true");
|
||||||
|
// response.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
|
||||||
|
// response.header("Access-Control-Allow-Headers", "Content-Type");
|
||||||
|
// response.header("Content-Type", "application/xml; charset=utf-8");
|
||||||
|
response.type("application/xml; charset=utf-8");
|
||||||
|
response.status(200);
|
||||||
|
|
||||||
return filledTemplate;
|
return filledTemplate;
|
||||||
} );
|
} );
|
||||||
@@ -60,7 +131,7 @@ public class UpnpSettingsResource {
|
|||||||
get(UPNP_CONTEXT + "/settings", "application/json", (request, response) -> {
|
get(UPNP_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||||
log.debug("bridge settings requested from " + request.ip());
|
log.debug("bridge settings requested from " + request.ip());
|
||||||
|
|
||||||
response.status(201);
|
response.status(200);
|
||||||
|
|
||||||
return theSettings;
|
return theSettings;
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public class VeraInfo {
|
|||||||
theDevice.setRoom(roomMap.get(theDevice.getRoom()).getName());
|
theDevice.setRoom(roomMap.get(theDevice.getRoom()).getName());
|
||||||
else
|
else
|
||||||
theDevice.setRoom("no room");
|
theDevice.setRoom("no room");
|
||||||
|
|
||||||
if(theDevice.getCategory() != null && categoryMap.get(theDevice.getCategory()) != null)
|
if(theDevice.getCategory() != null && categoryMap.get(theDevice.getCategory()) != null)
|
||||||
theDevice.setCategory(categoryMap.get(theDevice.getCategory()).getName());
|
theDevice.setCategory(categoryMap.get(theDevice.getCategory()).getName());
|
||||||
else
|
else
|
||||||
@@ -71,7 +72,10 @@ public class VeraInfo {
|
|||||||
Scene theScene = null;
|
Scene theScene = null;
|
||||||
while (theSecneIter.hasNext()) {
|
while (theSecneIter.hasNext()) {
|
||||||
theScene = theSecneIter.next();
|
theScene = theSecneIter.next();
|
||||||
|
if(theScene.getRoom() != null && roomMap.get(theScene.getRoom()) != null)
|
||||||
theScene.setRoom(roomMap.get(theScene.getRoom()).getName());
|
theScene.setRoom(roomMap.get(theScene.getRoom()).getName());
|
||||||
|
else
|
||||||
|
theScene.setRoom("no room");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
src/main/resources/public/css/main.css
Normal file
11
src/main/resources/public/css/main.css
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
body {
|
||||||
|
padding-top: 60px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortorder:after {
|
||||||
|
content: '\25b2';
|
||||||
|
}
|
||||||
|
.sortorder.reverse:after {
|
||||||
|
content: '\25bc';
|
||||||
|
}
|
||||||
@@ -5,12 +5,7 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>HA Bridge</title>
|
<title>HA Bridge</title>
|
||||||
<style>
|
<link href="css/main.css" rel="stylesheet">
|
||||||
body {
|
|
||||||
padding-top: 60px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
@@ -41,7 +36,7 @@
|
|||||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||||
<li><a href="http://www.bwssystems.com" target="_blank">Developed by BWS Systems</a></li>
|
<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="http://www.amazon.com/echo" target="_blank">Amazon Echo</a></li>
|
||||||
<li><a href="#">HA Bridge Version 0.3.2</a></li>
|
<li><a href="#">HA Bridge Version 0.4.4</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ app.factory('BridgeSettings', function() {
|
|||||||
BridgeSettings.upnpdevicedb = "";
|
BridgeSettings.upnpdevicedb = "";
|
||||||
BridgeSettings.upnpresponseport = "";
|
BridgeSettings.upnpresponseport = "";
|
||||||
BridgeSettings.veraaddress = "";
|
BridgeSettings.veraaddress = "";
|
||||||
|
BridgeSettings.upnpstrict = "";
|
||||||
|
BridgeSettings.traceupnp = "";
|
||||||
|
BridgeSettings.vtwocompatibility = "";
|
||||||
|
|
||||||
BridgeSettings.setupnpconfigaddress = function(aconfigaddress){
|
BridgeSettings.setupnpconfigaddress = function(aconfigaddress){
|
||||||
BridgeSettings.upnpconfigaddress = aconfigaddress;
|
BridgeSettings.upnpconfigaddress = aconfigaddress;
|
||||||
@@ -56,6 +59,15 @@ app.factory('BridgeSettings', function() {
|
|||||||
BridgeSettings.setveraaddress = function(averaaddress){
|
BridgeSettings.setveraaddress = function(averaaddress){
|
||||||
BridgeSettings.veraaddress = averaaddress;
|
BridgeSettings.veraaddress = averaaddress;
|
||||||
};
|
};
|
||||||
|
BridgeSettings.setupnpstrict = function(aupnpstrict){
|
||||||
|
BridgeSettings.upnpstrict = aupnpstrict;
|
||||||
|
};
|
||||||
|
BridgeSettings.settraceupnp = function(atraceupnp){
|
||||||
|
BridgeSettings.traceupnp = atraceupnp;
|
||||||
|
};
|
||||||
|
BridgeSettings.setvtwocompatibility = function(avtwocompatibility){
|
||||||
|
BridgeSettings.vtwocompatibility = avtwocompatibility;
|
||||||
|
};
|
||||||
|
|
||||||
return BridgeSettings;
|
return BridgeSettings;
|
||||||
});
|
});
|
||||||
@@ -92,6 +104,9 @@ app.service('bridgeService', function ($http, BridgeSettings) {
|
|||||||
self.BridgeSettings.setupnpdevicedb(response.data.upnpdevicedb);
|
self.BridgeSettings.setupnpdevicedb(response.data.upnpdevicedb);
|
||||||
self.BridgeSettings.setupnpresponseport(response.data.upnpresponseport);
|
self.BridgeSettings.setupnpresponseport(response.data.upnpresponseport);
|
||||||
self.BridgeSettings.setveraaddress(response.data.veraaddress);
|
self.BridgeSettings.setveraaddress(response.data.veraaddress);
|
||||||
|
self.BridgeSettings.settraceupnp(response.data.traceupnp);
|
||||||
|
self.BridgeSettings.setupnpstrict(response.data.upnpstrict);
|
||||||
|
self.BridgeSettings.setvtwocompatibility(response.data.vtwocompatibility);
|
||||||
},
|
},
|
||||||
function (error) {
|
function (error) {
|
||||||
if (error.data) {
|
if (error.data) {
|
||||||
@@ -141,7 +156,7 @@ app.service('bridgeService', function ($http, BridgeSettings) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addDevice = function (id, name, type, onUrl, offUrl) {
|
this.addDevice = function (id, name, type, onUrl, offUrl, httpVerb, contentType, contentBody) {
|
||||||
this.state.error = "";
|
this.state.error = "";
|
||||||
if (id) {
|
if (id) {
|
||||||
var putUrl = this.state.base + "/" + id;
|
var putUrl = this.state.base + "/" + id;
|
||||||
@@ -150,7 +165,10 @@ app.service('bridgeService', function ($http, BridgeSettings) {
|
|||||||
name: name,
|
name: name,
|
||||||
deviceType: type,
|
deviceType: type,
|
||||||
onUrl: onUrl,
|
onUrl: onUrl,
|
||||||
offUrl: offUrl
|
offUrl: offUrl,
|
||||||
|
httpVerb: httpVerb,
|
||||||
|
contentType: contentType,
|
||||||
|
contentBody: contentBody
|
||||||
}).then(
|
}).then(
|
||||||
function (response) {
|
function (response) {
|
||||||
self.viewDevices();
|
self.viewDevices();
|
||||||
@@ -167,7 +185,10 @@ app.service('bridgeService', function ($http, BridgeSettings) {
|
|||||||
name: name,
|
name: name,
|
||||||
deviceType: type,
|
deviceType: type,
|
||||||
onUrl: onUrl,
|
onUrl: onUrl,
|
||||||
offUrl: offUrl
|
offUrl: offUrl,
|
||||||
|
httpVerb: httpVerb,
|
||||||
|
contentType: contentType,
|
||||||
|
contentBody: contentBody
|
||||||
}).then(
|
}).then(
|
||||||
function (response) {
|
function (response) {
|
||||||
self.viewDevices();
|
self.viewDevices();
|
||||||
@@ -197,8 +218,8 @@ app.service('bridgeService', function ($http, BridgeSettings) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.editDevice = function (id, name, onUrl, offUrl) {
|
this.editDevice = function (id, name, onUrl, offUrl, httpVerb, contentType, contentBody) {
|
||||||
self.state.device = {id: id, name: name, onUrl: onUrl, offUrl: offUrl};
|
self.state.device = {id: id, name: name, onUrl: onUrl, offUrl: offUrl, httpVerb: httpVerb, contentType: contentType, contentBody: contentBody};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -207,6 +228,12 @@ app.controller('ViewingController', function ($scope, $location, bridgeService,
|
|||||||
$scope.BridgeSettings = bridgeService.BridgeSettings;
|
$scope.BridgeSettings = bridgeService.BridgeSettings;
|
||||||
bridgeService.viewDevices();
|
bridgeService.viewDevices();
|
||||||
$scope.bridge = bridgeService.state;
|
$scope.bridge = bridgeService.state;
|
||||||
|
$scope.predicate = '';
|
||||||
|
$scope.reverse = true;
|
||||||
|
$scope.order = function(predicate) {
|
||||||
|
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
|
||||||
|
$scope.predicate = predicate;
|
||||||
|
};
|
||||||
$scope.deleteDevice = function (device) {
|
$scope.deleteDevice = function (device) {
|
||||||
bridgeService.deleteDevice(device.id);
|
bridgeService.deleteDevice(device.id);
|
||||||
};
|
};
|
||||||
@@ -218,12 +245,12 @@ app.controller('ViewingController', function ($scope, $location, bridgeService,
|
|||||||
bridgeService.viewDevices();
|
bridgeService.viewDevices();
|
||||||
};
|
};
|
||||||
$scope.editDevice = function (device) {
|
$scope.editDevice = function (device) {
|
||||||
bridgeService.editDevice(device.id, device.name, device.onUrl, device.offUrl);
|
bridgeService.editDevice(device.id, device.name, device.onUrl, device.offUrl, device.httpVerb, device.contentType, device.contentBody);
|
||||||
$location.path('/editdevice');
|
$location.path('/editdevice');
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
app.controller('AddingController', function ($scope, bridgeService, BridgeSettings) {
|
app.controller('AddingController', function ($scope, $location, bridgeService, BridgeSettings) {
|
||||||
|
|
||||||
$scope.device = {id: "", name: "", deviceType: "switch", onUrl: "", offUrl: ""};
|
$scope.device = {id: "", name: "", deviceType: "switch", onUrl: "", offUrl: ""};
|
||||||
$scope.vera = {base: "", port: "3480", id: ""};
|
$scope.vera = {base: "", port: "3480", id: ""};
|
||||||
@@ -233,6 +260,12 @@ app.controller('AddingController', function ($scope, bridgeService, BridgeSettin
|
|||||||
bridgeService.viewVeraScenes();
|
bridgeService.viewVeraScenes();
|
||||||
$scope.bridge = bridgeService.state;
|
$scope.bridge = bridgeService.state;
|
||||||
$scope.device = bridgeService.state.device;
|
$scope.device = bridgeService.state.device;
|
||||||
|
$scope.predicate = '';
|
||||||
|
$scope.reverse = true;
|
||||||
|
$scope.order = function(predicate) {
|
||||||
|
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
|
||||||
|
$scope.predicate = predicate;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.buildUrlsUsingDevice = function () {
|
$scope.buildUrlsUsingDevice = function () {
|
||||||
if ($scope.vera.base.indexOf("http") < 0) {
|
if ($scope.vera.base.indexOf("http") < 0) {
|
||||||
@@ -263,6 +296,7 @@ app.controller('AddingController', function ($scope, bridgeService, BridgeSettin
|
|||||||
if ($scope.vera.base.indexOf("http") < 0) {
|
if ($scope.vera.base.indexOf("http") < 0) {
|
||||||
$scope.vera.base = "http://" + $scope.vera.base;
|
$scope.vera.base = "http://" + $scope.vera.base;
|
||||||
}
|
}
|
||||||
|
$scope.device.deviceType = "switch";
|
||||||
$scope.device.name = veradevice.name;
|
$scope.device.name = veradevice.name;
|
||||||
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
|
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
|
||||||
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum="
|
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum="
|
||||||
@@ -291,13 +325,17 @@ app.controller('AddingController', function ($scope, bridgeService, BridgeSettin
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.addDevice = function () {
|
$scope.addDevice = function () {
|
||||||
bridgeService.addDevice($scope.device.id, $scope.device.name, $scope.device.deviceType, $scope.device.onUrl, $scope.device.offUrl).then(
|
bridgeService.addDevice($scope.device.id, $scope.device.name, $scope.device.deviceType, $scope.device.onUrl, $scope.device.offUrl, $scope.device.httpVerb, $scope.device.contentType, $scope.device.contentBody).then(
|
||||||
function () {
|
function () {
|
||||||
$scope.device.id = "";
|
$scope.device.id = "";
|
||||||
$scope.device.name = "";
|
$scope.device.name = "";
|
||||||
$scope.device.onUrl = "";
|
$scope.device.onUrl = "";
|
||||||
$scope.device.deviceType = "switch";
|
$scope.device.deviceType = "switch";
|
||||||
$scope.device.offUrl = "";
|
$scope.device.offUrl = "";
|
||||||
|
$scope.device.httpVerb = null;
|
||||||
|
$scope.device.contentType = null;
|
||||||
|
$scope.device.contentBody = null;
|
||||||
|
$location.path('/#');
|
||||||
},
|
},
|
||||||
function (error) {
|
function (error) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,18 @@
|
|||||||
<td>vera.address</td>
|
<td>vera.address</td>
|
||||||
<td>{{BridgeSettings.veraaddress}}</td>
|
<td>{{BridgeSettings.veraaddress}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>upnp.strict</td>
|
||||||
|
<td>{{BridgeSettings.upnpstrict}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>trace.upnp</td>
|
||||||
|
<td>{{BridgeSettings.traceupnp}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>vtwo.compatibility</td>
|
||||||
|
<td>{{BridgeSettings.vtwocompatibility}}</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,7 +82,6 @@
|
|||||||
<div ng-show='bridge.error != ""'>{{bridge.error}}</div>
|
<div ng-show='bridge.error != ""'>{{bridge.error}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h2 class="panel-title">Current devices</h2>
|
<h2 class="panel-title">Current devices</h2>
|
||||||
@@ -78,13 +89,19 @@
|
|||||||
<table class="table table-bordered table-striped table-hover">
|
<table class="table table-bordered table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>
|
||||||
<th>Name</th>
|
<a href="" ng-click="order('id')">ID</a>
|
||||||
<th>Type</th>
|
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span></th>
|
||||||
|
<th>
|
||||||
|
<a href="" ng-click="order('name')">Name</a>
|
||||||
|
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span></th>
|
||||||
|
<th>
|
||||||
|
<a href="" ng-click="order('deviceType')">Type</a>
|
||||||
|
<span class="sortorder" ng-show="predicate === 'deviceType'" ng-class="{reverse:reverse}"></span></th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr ng-repeat="device in bridge.devices">
|
<tr ng-repeat="device in bridge.devices | orderBy:predicate:reverse">
|
||||||
<td>{{device.id}}</td>
|
<td>{{device.id}}</td>
|
||||||
<td>{{device.name}}</td>
|
<td>{{device.name}}</td>
|
||||||
<td>{{device.deviceType}}</td>
|
<td>{{device.deviceType}}</td>
|
||||||
|
|||||||
@@ -25,27 +25,67 @@
|
|||||||
Update Device</button>
|
Update Device</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
|
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
|
||||||
URL </label>
|
URL </label>
|
||||||
|
|
||||||
<div class="col-xs-8 col-sm-7">
|
<div class="col-xs-8 col-sm-7">
|
||||||
<input type="text" class="form-control" id="device-on-url"
|
<textarea rows="3" class="form-control" id="device-on-url"
|
||||||
ng-model="device.onUrl" placeholder="URL to turn device on">
|
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
||||||
ng-click="testUrl(device.onUrl)">Test</button>
|
ng-click="testUrl(device.onUrl)">Test</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label"
|
<label class="col-xs-12 col-sm-2 control-label"
|
||||||
for="device-off-url">Off URL </label>
|
for="device-off-url">Off URL </label>
|
||||||
|
|
||||||
<div class="col-xs-8 col-sm-7">
|
<div class="col-xs-8 col-sm-7">
|
||||||
<input type="text" class="form-control" id="device-off-url"
|
<textarea rows="3" class="form-control" id="device-off-url"
|
||||||
ng-model="device.offUrl" placeholder="URL to turn device off">
|
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
||||||
ng-click="testUrl(device.offUrl)">Test</button>
|
ng-click="testUrl(device.offUrl)">Test</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-xs-12 col-sm-2 control-label" for="device-http-verb">Http Verb
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="col-xs-8 col-sm-7">
|
||||||
|
<input type="text" class="form-control" id="device-http-verb"
|
||||||
|
ng-model="device.httpVerb" placeholder="Http Verb, i.e. GET/PUT/POST">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-xs-12 col-sm-2 control-label" for="device-content-type">Name
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="col-xs-8 col-sm-7">
|
||||||
|
<input type="text" class="form-control" id="device-content-type"
|
||||||
|
ng-model="device.contentType" placeholder="Content type, i.e. application/json">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-xs-12 col-sm-2 control-label"
|
||||||
|
for="device-content-body">Content Body </label>
|
||||||
|
|
||||||
|
<div class="col-xs-8 col-sm-7">
|
||||||
|
<textarea rows="3" class="form-control" id="device-content-body"
|
||||||
|
ng-model="device.contentBody" placeholder="Content Body for specific GET/PUT/POST type"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -42,11 +42,13 @@
|
|||||||
<input type="text" class="form-control" id="vera-id"
|
<input type="text" class="form-control" id="vera-id"
|
||||||
ng-model="vera.id" placeholder="ID">
|
ng-model="vera.id" placeholder="ID">
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
<button type="submit" ng-click="buildUrlsUsingDevice()"
|
<button type="submit" ng-click="buildUrlsUsingDevice()"
|
||||||
class="col-xs-4 col-sm-2 btn btn-success">Generate Device
|
class="col-xs-2 col-sm-2 col-xs-offset-2 col-sm-offset-2 btn btn-success">Generate Device
|
||||||
URLs</button>
|
URLs</button>
|
||||||
<button type="submit" ng-click="buildUrlsUsingScene()"
|
<button type="submit" ng-click="buildUrlsUsingScene()"
|
||||||
class="col-xs-4 col-sm-2 btn btn-success">Generate Scene
|
class="col-xs-2 col-sm-2 col-xs-offset-2 col-sm-offset-2 btn btn-success">Generate Scene
|
||||||
URLs</button>
|
URLs</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -61,6 +63,7 @@
|
|||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<form class="form-horizontal" ng-submit="addDevice()">
|
<form class="form-horizontal" ng-submit="addDevice()">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
|
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -71,28 +74,69 @@
|
|||||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
|
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
|
||||||
Add Device</button>
|
Add Device</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
|
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
|
||||||
URL </label>
|
URL </label>
|
||||||
|
|
||||||
<div class="col-xs-8 col-sm-7">
|
<div class="col-xs-8 col-sm-7">
|
||||||
<input type="text" class="form-control" id="device-on-url"
|
<textarea rows="3" class="form-control" id="device-on-url"
|
||||||
ng-model="device.onUrl" placeholder="URL to turn device on">
|
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
||||||
ng-click="testUrl(device.onUrl)">Test</button>
|
ng-click="testUrl(device.onUrl)">Test</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label"
|
<label class="col-xs-12 col-sm-2 control-label"
|
||||||
for="device-off-url">Off URL </label>
|
for="device-off-url">Off URL </label>
|
||||||
|
|
||||||
<div class="col-xs-8 col-sm-7">
|
<div class="col-xs-8 col-sm-7">
|
||||||
<input type="text" class="form-control" id="device-off-url"
|
<textarea rows="3" class="form-control" id="device-off-url"
|
||||||
ng-model="device.offUrl" placeholder="URL to turn device off">
|
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
||||||
ng-click="testUrl(device.offUrl)">Test</button>
|
ng-click="testUrl(device.offUrl)">Test</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-xs-12 col-sm-2 control-label" for="device-http-verb">Http Verb
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="col-xs-8 col-sm-7">
|
||||||
|
<input type="text" class="form-control" id="device-http-verb"
|
||||||
|
ng-model="device.httpVerb" placeholder="Http Verb, i.e. GET/PUT/POST">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-xs-12 col-sm-2 control-label" for="device-content-type">Name
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="col-xs-8 col-sm-7">
|
||||||
|
<input type="text" class="form-control" id="device-content-type"
|
||||||
|
ng-model="device.contentType" placeholder="Content type, i.e. application/json">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-xs-12 col-sm-2 control-label"
|
||||||
|
for="device-content-body">Content Body </label>
|
||||||
|
|
||||||
|
<div class="col-xs-8 col-sm-7">
|
||||||
|
<textarea rows="3" class="form-control" id="device-content-body"
|
||||||
|
ng-model="device.contentBody" placeholder="Content Body for specific GET/PUT/POST type"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -17,14 +17,26 @@
|
|||||||
<table class="table table-bordered table-striped table-hover">
|
<table class="table table-bordered table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>
|
||||||
<th>Id</th>
|
<a href="" ng-click="order('name')">Name</a>
|
||||||
<th>Category</th>
|
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||||
<th>Room</th>
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href="" ng-click="order('id')">Id</a>
|
||||||
|
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href="" ng-click="order('category')">Category</a>
|
||||||
|
<span class="sortorder" ng-show="predicate === 'category'" ng-class="{reverse:reverse}"></span>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href="" ng-click="order('room')">Room</a>
|
||||||
|
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
|
||||||
|
</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr ng-repeat="veradevice in bridge.veradevices">
|
<tr ng-repeat="veradevice in bridge.veradevices | orderBy:predicate:reverse">
|
||||||
<td>{{veradevice.name}}</td>
|
<td>{{veradevice.name}}</td>
|
||||||
<td>{{veradevice.id}}</td>
|
<td>{{veradevice.id}}</td>
|
||||||
<td>{{veradevice.category}}</td>
|
<td>{{veradevice.category}}</td>
|
||||||
@@ -58,27 +70,33 @@
|
|||||||
Add Device</button>
|
Add Device</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
|
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
|
||||||
URL </label>
|
URL </label>
|
||||||
|
|
||||||
<div class="col-xs-8 col-sm-7">
|
<div class="col-xs-8 col-sm-7">
|
||||||
<input type="text" class="form-control" id="device-on-url"
|
<textarea rows="3" class="form-control" id="device-on-url"
|
||||||
ng-model="device.onUrl" placeholder="URL to turn device on">
|
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
||||||
ng-click="testUrl(device.onUrl)">Test</button>
|
ng-click="testUrl(device.onUrl)">Test</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label"
|
<label class="col-xs-12 col-sm-2 control-label"
|
||||||
for="device-off-url">Off URL </label>
|
for="device-off-url">Off URL </label>
|
||||||
|
|
||||||
<div class="col-xs-8 col-sm-7">
|
<div class="col-xs-8 col-sm-7">
|
||||||
<input type="text" class="form-control" id="device-off-url"
|
<textarea rows="3" class="form-control" id="device-off-url"
|
||||||
ng-model="device.offUrl" placeholder="URL to turn device off">
|
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
||||||
ng-click="testUrl(device.offUrl)">Test</button>
|
ng-click="testUrl(device.offUrl)">Test</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -17,13 +17,22 @@
|
|||||||
<table class="table table-bordered table-striped table-hover">
|
<table class="table table-bordered table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>
|
||||||
<th>Id</th>
|
<a href="" ng-click="order('name')">Name</a>
|
||||||
<th>Room</th>
|
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href="" ng-click="order('id')">Id</a>
|
||||||
|
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href="" ng-click="order('room')">Room</a>
|
||||||
|
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
|
||||||
|
</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr ng-repeat="verascene in bridge.verascenes">
|
<tr ng-repeat="verascene in bridge.verascenes | orderBy:predicate:reverse">
|
||||||
<td>{{verascene.name}}</td>
|
<td>{{verascene.name}}</td>
|
||||||
<td>{{verascene.id}}</td>
|
<td>{{verascene.id}}</td>
|
||||||
<td>{{verascene.room}}</td>
|
<td>{{verascene.room}}</td>
|
||||||
@@ -56,27 +65,33 @@
|
|||||||
Add Scene</button>
|
Add Scene</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
|
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
|
||||||
URL </label>
|
URL </label>
|
||||||
|
|
||||||
<div class="col-xs-8 col-sm-7">
|
<div class="col-xs-8 col-sm-7">
|
||||||
<input type="text" class="form-control" id="device-on-url"
|
<textarea rows="3" class="form-control" id="device-on-url"
|
||||||
ng-model="device.onUrl" placeholder="URL to turn device on">
|
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
||||||
ng-click="testUrl(device.onUrl)">Test</button>
|
ng-click="testUrl(device.onUrl)">Test</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label"
|
<label class="col-xs-12 col-sm-2 control-label"
|
||||||
for="device-off-url">Off URL </label>
|
for="device-off-url">Off URL </label>
|
||||||
|
|
||||||
<div class="col-xs-8 col-sm-7">
|
<div class="col-xs-8 col-sm-7">
|
||||||
<input type="text" class="form-control" id="device-off-url"
|
<textarea rows="3" class="form-control" id="device-off-url"
|
||||||
ng-model="device.offUrl" placeholder="URL to turn device off">
|
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
|
||||||
ng-click="testUrl(device.offUrl)">Test</button>
|
ng-click="testUrl(device.offUrl)">Test</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
Reference in New Issue
Block a user