Compare commits

..

9 Commits

Author SHA1 Message Date
Admin
e29f12905d Finalized handling for barmony. Added a version call for use in api.
Updated version handling in the pom.xml and code. Removed
vtwocompatibility as it should not be used.
2015-11-03 15:16:01 -06:00
Admin
408b79d5d8 Finished coding need to test. 2015-11-02 16:46:48 -06:00
Admin
bf5ad2e23c Adding button selection control for harmony. in progress.... 2015-10-30 16:30:30 -05:00
Admin
59f1db285d Updating pom configuration for adding harmony control. 2015-10-29 15:59:00 -05:00
Admin
203ed0b5d3 Updated pom to use new harmony client version. 2015-10-28 16:42:23 -05:00
Admin
b443d16a11 First Beta of Harmony configuration for testing. 2015-10-27 16:39:13 -05:00
Admin
295b1e1a30 Adding Harmony Hub Control directly into the Bridge. First iteration
updates the ui harness. Also, updated settings in the configuration
page.
2015-10-26 16:35:51 -05:00
Admin
c872f3543d Updated Hue Device emulation to be Lux bulbs as most things this bridge
will emulate will not be color friendly....
2015-10-23 16:16:18 -05:00
Admin
23f2d2716d Update Readme.md, typo with bracket placement. 2015-10-13 16:36:53 -05:00
27 changed files with 1040 additions and 211 deletions

View File

@@ -1,15 +1,15 @@
# ha-bridge
Emulates Philips Hue api to other home automation gateways such as an Amazon Echo. The Bridge has helpers to build devices for the gateway for the Vera, Vera Lite or Vera Edge. Alternatively the Bridge supports custom calls as well. The Bridge handles basic commands such as "On", "Off" and "brightness" commands of the hue protocol.
Emulates Philips Hue api to other home automation gateways such as an Amazon Echo. The Bridge has helpers to build devices for the gateway for the Logitech Harmony Hub, Vera, Vera Lite or Vera Edge. Alternatively the Bridge supports custom calls as well. The Bridge handles basic commands such as "On", "Off" and "brightness" commands of the hue protocol.
## Build
To customize and build it yourself, build a new jar with maven:
```
mvn install
```
Otherwise go to http://www.bwssystems.com/apps.html to download the latest jar file.
Otherwise, downloads are available at https://github.com/bwssytems/ha-bridge/releases.
## Run
Then locate the jar and start the server with:
```
java -jar -Dvera.address=X.Y.Z.A ha-bridge-0.X.Y.jar
java -jar -Dvera.address=X.Y.Z.A -Dharmony.address=X.Y.Z.A -Dharmony.user=myself -Dharmony.pwd=passwd ha-bridge-0.X.Y.jar
```
## Available Arguments
### -Dvera.address=`<ip address>`
@@ -22,12 +22,16 @@ The server defaults to running on port 8080. If you're already running a server
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 upnp response port that will be used. The default is 50000.
### -Dharmony.address=`<ip address>`
The argument for the Harmony Hub address should be given as the system does not have a way to find the address. Supply -Dharmony.address=X.Y.Z.A on the command line to provide it. If a Harmony Hub is not used, do not set it.
### -Dharmony.user=`<username>`
The user name of the MyHarmony.com account for the Harmony Hub. This needs to be given if you are using the Harmony Hub Features, provide -Dharmony.user=`<username>` on the command line.
### -Dharmony.pwd=`<password>`
The password for the user name of the MyHarmony.com account for the Harmony Hub. This needs to be given if you are using the Harmony Hub Features, provide -Dharmony.pwd=`<password>` on the command line.
### -Dupnp.strict=`<true|false>`
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=true.
### -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 false.
## Web Config
Configure by going to the url for the host you are running on or localhost with port you have assigned:
```
@@ -45,7 +49,7 @@ POST http://host:8080/api/devices
}
```
## Dimming and value passing control
Dimming is also supported by using the expressions ${intensity.percent} for 0-100 or ${intensity.byte} for 0-255 or custom values using ${intensity.math(<your expression using "X" as the value to operate on>)} i.e. "{$intensity.math(X/4)}".
Dimming is also supported by using the expressions ${intensity.percent} for 0-100 or ${intensity.byte} for 0-255 or custom values using ${intensity.math(<your expression using "X" as the value to operate on>)} i.e. "${intensity.math(X/4)}".
e.g.
```
{
@@ -98,4 +102,9 @@ To view or remove devices that Alexa knows about, you can use the mobile app Men
To turn on debugging for the bridge, use the following extra parm in the command line:
```
-Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG
```
## Development Mode
To turn on development mode so that it will not need an Harmony Hub for testing, use the following extra parm in the command line and the harmony ip and login info will not be needed:
```
java -jar -Ddev.mode=true ha-bridge-0.X.Y.jar
```

72
pom.xml
View File

@@ -5,11 +5,11 @@
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>0.4.9</version>
<version>1.0.5</version>
<packaging>jar</packaging>
<name>HA Bridge</name>
<description>Emulates a Philips Hue bridge to allow the Amazon Echo to hook up to other HA systems, i.e. Vera, using lightweight frameworks</description>
<description>Emulates a Philips Hue bridge to allow the Amazon Echo to hook up to other HA systems, i.e. Vera or Harmony Hub, using lightweight frameworks</description>
<properties>
<java.version>1.8</java.version>
@@ -17,7 +17,19 @@
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.bwssytems</groupId>
<artifactId>harmony-java-client</artifactId>
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
@@ -58,9 +70,40 @@
<artifactId>eval</artifactId>
<version>0.5</version>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.0-beta4</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-core</artifactId>
<version>4.0.7</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-debug</artifactId>
<version>4.0.7</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>version.properties</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>version.properties</exclude>
</excludes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -86,21 +129,20 @@
<exclude>META-INF/services/**</exclude>
<exclude>META-INF/DEPENDENCIES</exclude>
<exclude>about_files/**</exclude>
<exclude>*.properties</exclude>
</excludes>
</filter>
<filter>
<artifact>org.slf4j:*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>commons-logging:commons-logging</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.slf4j:*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>commons-logging:commons-logging</artifact>
<includes>
<include>**</include>
</includes>
</filter>
</filters>
<transformers>
<transformer

View File

@@ -6,9 +6,12 @@ public class BridgeSettings {
private String upnpresponseport;
private String upnpdevicedb;
private String veraaddress;
private String harmonyaddress;
private String harmonyuser;
private String harmonypwd;
private boolean upnpstrict;
private boolean traceupnp;
private boolean vtwocompatibility;
private boolean devmode;
public String getUpnpConfigAddress() {
return upnpconfigaddress;
@@ -40,7 +43,24 @@ public class BridgeSettings {
public void setVeraAddress(String veraAddress) {
this.veraaddress = veraAddress;
}
public String getHarmonyAddress() {
return harmonyaddress;
}
public void setHarmonyAddress(String harmonyaddress) {
this.harmonyaddress = harmonyaddress;
}
public String getHarmonyUser() {
return harmonyuser;
}
public void setHarmonyUser(String harmonyuser) {
this.harmonyuser = harmonyuser;
}
public String getHarmonyPwd() {
return harmonypwd;
}
public void setHarmonyPwd(String harmonypwd) {
this.harmonypwd = harmonypwd;
}
public boolean isUpnpStrict() {
return upnpstrict;
}
@@ -53,17 +73,24 @@ public class BridgeSettings {
public void setTraceupnp(boolean traceupnp) {
this.traceupnp = traceupnp;
}
public boolean isVtwocompatibility() {
return vtwocompatibility;
public boolean isDevMode() {
return devmode;
}
public void setVtwocompatibility(boolean vtwocompatibility) {
this.vtwocompatibility = vtwocompatibility;
public void setDevMode(boolean devmode) {
this.devmode = devmode;
}
public Boolean isValidVera() {
if(this.veraaddress.contains(Configuration.DEFAULT_VERA_ADDRESS))
return false;
return true;
}
public Boolean isValidHarmony() {
if(this.harmonyaddress.contains(Configuration.DEFAULT_HARMONY_ADDRESS))
return false;
if(this.harmonypwd == null || this.harmonypwd == "")
return false;
if(this.harmonyuser == null || this.harmonyuser == "")
return false;
return true;
}
}

View File

@@ -4,5 +4,8 @@ public class Configuration {
public final static String DEVICE_DB_DIRECTORY = "data/device.db";
public final static String UPNP_RESPONSE_PORT = "50000";
public final static String DEFAULT_VERA_ADDRESS = "1.1.1.1";
public final static String DEFAULT_HARMONY_ADDRESS = "1.1.1.1";
public final static String DEFAULT_HARMONY_USER = "";
public final static String DEFAULT_HARMONY_PWD = "";
public final static String DFAULT_WEB_PORT = "8080";
}

View File

@@ -12,6 +12,7 @@ import com.bwssystems.HABridge.devicemanagmeent.*;
import com.bwssystems.HABridge.hue.HueMulator;
import com.bwssystems.HABridge.upnp.UpnpListener;
import com.bwssystems.HABridge.upnp.UpnpSettingsResource;
import com.bwssystems.harmony.HarmonyServer;
public class HABridge {
@@ -33,13 +34,18 @@ public class HABridge {
public static void main(String[] args) {
Logger log = LoggerFactory.getLogger(HABridge.class);
DeviceResource theResources;
HarmonyServer myHarmonyServer;
HueMulator theHueMulator;
UpnpSettingsResource theSettingResponder;
UpnpListener theUpnpListener;
InetAddress address;
String addressString;
BridgeSettings bridgeSettings;
Version theVersion;
theVersion = new Version();
log.info("HA Bridge (v" + theVersion.getVersion() + ") starting setup....");
//get ip address for upnp requests
try {
address = InetAddress.getLocalHost();
@@ -50,26 +56,35 @@ public class HABridge {
}
bridgeSettings = new BridgeSettings();
bridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DFAULT_WEB_PORT));
bridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address", addressString));
bridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db", Configuration.DEVICE_DB_DIRECTORY));
bridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT));
bridgeSettings.setVeraAddress(System.getProperty("vera.address", Configuration.DEFAULT_VERA_ADDRESS));
bridgeSettings.setHarmonyAddress(System.getProperty("harmony.address", Configuration.DEFAULT_HARMONY_ADDRESS));
bridgeSettings.setHarmonyUser(System.getProperty("harmony.user", Configuration.DEFAULT_HARMONY_USER));
bridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd", Configuration.DEFAULT_HARMONY_PWD));
bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
bridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
bridgeSettings.setVtwocompatibility(Boolean.parseBoolean(System.getProperty("vtwo.compatibility", "false")));
bridgeSettings.setDevMode(Boolean.parseBoolean(System.getProperty("dev.mode", "false")));
// sparkjava config directive to set ip address for the web server to listen on
// ipAddress("0.0.0.0"); // not used
// sparkjava config directive to set port for the web server to listen on
bridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DFAULT_WEB_PORT));
port(Integer.valueOf(bridgeSettings.getServerPort()));
// sparkjava config directive to set html static file location for Jetty
staticFileLocation("/public");
log.info("Starting setup....");
//setup the harmony connection if available
try {
myHarmonyServer = HarmonyServer.setup(bridgeSettings);
} catch (Exception e) {
log.error("Cannot get harmony client setup, Exiting with message: " + e.getMessage(), e);
return;
}
// setup the class to handle the resource setup rest api
theResources = new DeviceResource(bridgeSettings);
theResources = new DeviceResource(bridgeSettings, theVersion, myHarmonyServer.getMyHarmony());
// setup the class to handle the hue emulator rest api
theHueMulator = new HueMulator(theResources.getDeviceRepository());
theHueMulator = new HueMulator(theResources.getDeviceRepository(), myHarmonyServer.getMyHarmony());
theHueMulator.setupServer();
// setup the class to handle the upnp response rest api
theSettingResponder = new UpnpSettingsResource(bridgeSettings);

View File

@@ -1,8 +1,6 @@
package com.bwssystems.HABridge.api.hue;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
@@ -104,20 +102,13 @@ public class DeviceResponse {
deviceState.setEffect("none");
deviceState.setAlert("none");
deviceState.setBri(254);
deviceState.setHue(15823);
deviceState.setSat(88);
deviceState.setCt(313);
deviceState.setSat(254);
List<Double> xv = new LinkedList<>();
xv.add(Double.valueOf("0.4255"));
xv.add(Double.valueOf("0.3998"));
deviceState.setXy(xv);
deviceState.setColormode("ct");
response.setName(name);
response.setUniqueid(id);
response.setManufacturername("Philips");
response.setType("Extended color light");
response.setModelid("LCT001");
response.setType("Dimmable light");
response.setModelid("LWB004");
response.setSwversion("65003148");
return response;

View File

@@ -16,8 +16,10 @@ import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.JsonTransformer;
import com.bwssystems.HABridge.Version;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.dao.DeviceRepository;
import com.bwssystems.harmony.HarmonyHandler;
import com.bwssystems.luupRequests.Sdata;
import com.bwssystems.vera.VeraInfo;
import com.google.gson.Gson;
@@ -31,12 +33,16 @@ public class DeviceResource {
private DeviceRepository deviceRepository;
private VeraInfo veraInfo;
private Version version;
private HarmonyHandler myHarmonyHandler;
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
public DeviceResource(BridgeSettings theSettings) {
public DeviceResource(BridgeSettings theSettings, Version theVersion, HarmonyHandler myHarmony) {
super();
deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
veraInfo = new VeraInfo(theSettings.getVeraAddress(), theSettings.isValidVera());
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
this.veraInfo = new VeraInfo(theSettings.getVeraAddress(), theSettings.isValidVera());
this.myHarmonyHandler = myHarmony;
this.version = theVersion;
setupEndpoints();
}
@@ -127,6 +133,12 @@ public class DeviceResource {
return null;
}, new JsonTransformer());
get (API_CONTEXT + "/habridge/version", "application/json", (request, response) -> {
log.debug("Get HA Bridge version: v" + version.getVersion());
response.status(HttpStatus.SC_OK);
return "{\"version\":\"" + version.getVersion() + "\"}";
});
get (API_CONTEXT + "/vera/devices", "application/json", (request, response) -> {
log.debug("Get vera devices");
Sdata sData = veraInfo.getSdata();
@@ -150,5 +162,35 @@ public class DeviceResource {
return sData.getScenes();
}, new JsonTransformer());
get (API_CONTEXT + "/harmony/activities", "application/json", (request, response) -> {
log.debug("Get harmony activities");
if(myHarmonyHandler == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return myHarmonyHandler.getActivities();
}, new JsonTransformer());
get (API_CONTEXT + "/harmony/show", "application/json", (request, response) -> {
log.debug("Get harmony current activity");
if(myHarmonyHandler == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return myHarmonyHandler.getCurrentActivity();
}, new JsonTransformer());
get (API_CONTEXT + "/harmony/devices", "application/json", (request, response) -> {
log.debug("Get harmony devices");
if(myHarmonyHandler == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return myHarmonyHandler.getDevices();
}, new JsonTransformer());
}
}

View File

@@ -6,6 +6,9 @@ import com.bwssystems.HABridge.api.hue.DeviceResponse;
import com.bwssystems.HABridge.api.hue.DeviceState;
import com.bwssystems.HABridge.api.hue.HueApiResponse;
import com.bwssystems.HABridge.dao.*;
import com.bwssystems.harmony.ButtonPress;
import com.bwssystems.harmony.HarmonyHandler;
import com.bwssystems.harmony.RunActivity;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
@@ -51,15 +54,17 @@ public class HueMulator {
private static final String HUE_CONTEXT = "/api";
private DeviceRepository repository;
private HarmonyHandler myHarmony;
private HttpClient httpClient;
private ObjectMapper mapper;
public HueMulator(DeviceRepository aDeviceRepository){
public HueMulator(DeviceRepository aDeviceRepository, HarmonyHandler theHandler){
httpClient = HttpClients.createDefault();
mapper = new ObjectMapper(); //armzilla: work around Echo incorrect content type and breaking mapping. Map manually
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
repository = aDeviceRepository;
myHarmony = theHandler;
}
// This function sets up the sparkjava rest calls for the hue api
@@ -208,19 +213,35 @@ public class HueMulator {
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":false}}]";
url = device.getOffUrl();
}
//quick template
String body;
url = replaceIntensityValue(url, state.getBri());
if (state.isOn())
body = replaceIntensityValue(device.getContentBody(), state.getBri());
if(device.getDeviceType().contains("activity"))
{
log.debug("executing activity to Harmony: " + url);
RunActivity anActivity = new Gson().fromJson(url, RunActivity.class);
myHarmony.startActivity(anActivity);
}
else if(device.getDeviceType().contains("button"))
{
log.debug("executing button press to Harmony: " + url);
ButtonPress aDeviceButton = new Gson().fromJson(url, ButtonPress.class);
myHarmony.pressButton(aDeviceButton);
}
else
body = replaceIntensityValue(device.getContentBodyOff(), 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;
{
log.debug("executing activity to Http: " + url);
// quick template
String body;
url = replaceIntensityValue(url, state.getBri());
if (state.isOn())
body = replaceIntensityValue(device.getContentBody(), state.getBri());
else
body = replaceIntensityValue(device.getContentBodyOff(), 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");

View File

@@ -27,8 +27,6 @@ public class UpnpListener {
private boolean traceupnp;
private boolean vTwoCompatibility;
public UpnpListener(BridgeSettings theSettings) {
super();
upnpResponsePort = Integer.valueOf(theSettings.getUpnpResponsePort());
@@ -36,7 +34,6 @@ public class UpnpListener {
responseAddress = theSettings.getUpnpConfigAddress();
strict = theSettings.isUpnpStrict();
traceupnp = theSettings.isTraceupnp();
vTwoCompatibility = theSettings.isVtwocompatibility();
}
public void startListening(){
@@ -113,18 +110,18 @@ public class UpnpListener {
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);
log.info("Traceupnp: isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
return true;
}
else if (!strict || vTwoCompatibility)
else if (!strict)
{
if(traceupnp)
log.info("Traceupnp: isSSDPDiscovery found message to be valid under loose rules - strict: " + strict + ", vTwo.Compatibility: " + vTwoCompatibility);
log.info("Traceupnp: isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
return true;
}
}
if(traceupnp)
log.info("Traceupnp: isSSDPDiscovery found message to not be valid - strict: " + strict + ", vTwo.Compatibility: " + vTwoCompatibility);
log.info("Traceupnp: isSSDPDiscovery found message to not be valid - strict: " + strict);
return false;
}
@@ -135,20 +132,9 @@ public class UpnpListener {
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1\r\n" +
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
"USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n";
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 {
String discoveryResponse = null;
if(vTwoCompatibility)
discoveryResponse = String.format(discoveryTemplateVTwo, responseAddress, httpServerPort, getRandomUUIDString());
else
discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort, getRandomUUIDString());
discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort, getRandomUUIDString());
if(traceupnp)
log.info("Traceupnp: sendUpnpResponse: " + discoveryResponse);
else

View File

@@ -40,53 +40,6 @@ public class UpnpSettingsResource {
+ "<depth>24</depth>\n" + "<url>hue_logo_3.png</url>\n" + "</icon>\n" + "</iconList>\n" + "</device>\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) {
super();
this.theSettings = theSettings;
@@ -102,10 +55,7 @@ public class UpnpSettingsResource {
log.debug("upnp device settings requested: " + request.params(":id") + " from " + request.ip() + ":" + request.port());
String portNumber = Integer.toString(request.port());
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());
filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress());
if(theSettings.isTraceupnp())
log.info("Traceupnp: upnp device settings response: " + filledTemplate);
else

View File

@@ -0,0 +1,25 @@
package com.bwssystems.harmony;
public class ButtonPress {
private String device;
private String button;
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
public String getButton() {
return button;
}
public void setButton(String button) {
this.button = button;
}
public Boolean isValid() {
if (device != null && !device.isEmpty()){
if (button != null && !button.isEmpty())
return true;
}
return false;
}
}

View File

@@ -0,0 +1,69 @@
package com.bwssystems.harmony;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.whistlingfish.harmony.config.Activity;
import net.whistlingfish.harmony.config.Device;
import net.whistlingfish.harmony.config.HarmonyConfig;
public class DevModeResponse {
final Logger log = LoggerFactory.getLogger(DevModeResponse.class);
private final static String powerOff = "PowerOff";
private HarmonyConfig harmonyConfig;
private Activity currentActivity;
public DevModeResponse() {
super();
harmonyConfig = HarmonyConfig.parse(dataReader("/config.data"));
this.currentActivity = harmonyConfig.getActivityByName(powerOff);
}
public Activity getCurrentActivity() {
return currentActivity;
}
public void setCurrentActivity(Activity currentActivity) {
this.currentActivity = currentActivity;
}
public List<Activity> getActivities() {
return harmonyConfig.getActivities();
}
public List<Device> getDevices() {
return harmonyConfig.getDevices();
}
public HarmonyConfig getConfig() {
return harmonyConfig;
}
private String dataReader(String filePath) {
String content = null;
try {
InputStream input = getClass().getResourceAsStream(filePath);
OutputStream out = new ByteArrayOutputStream();
int read;
byte[] bytes = new byte[1024];
while ((read = input.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
content = out.toString();
} catch (IOException e) {
log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e);
}
return content;
}
}

View File

@@ -0,0 +1,122 @@
package com.bwssystems.harmony;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.whistlingfish.harmony.HarmonyClient;
import net.whistlingfish.harmony.config.Activity;
import net.whistlingfish.harmony.config.Device;
import net.whistlingfish.harmony.config.HarmonyConfig;
public class HarmonyHandler {
private static final Logger log = LoggerFactory.getLogger(HarmonyHandler.class);
private HarmonyClient harmonyClient;
private Boolean noopCalls;
private Boolean devMode;
private DevModeResponse devResponse;
public HarmonyHandler(HarmonyClient theClient, Boolean noopCallsSetting, DevModeResponse devResponseSetting) {
super();
noopCalls = noopCallsSetting;
devMode = Boolean.TRUE;
devResponse = null;
if(devResponseSetting == null)
devMode = Boolean.FALSE;
else
devResponse = devResponseSetting;
harmonyClient = theClient;
}
public List<Activity> getActivities() {
log.debug("Harmony api activities list requested.");
if(devMode)
return devResponse.getActivities();
return harmonyClient.getConfig().getActivities();
}
public List<Device> getDevices() {
log.debug("Harmony api device list requested.");
if(devMode)
return devResponse.getDevices();
return harmonyClient.getConfig().getDevices();
}
public HarmonyConfig getConfig() {
log.debug("Harmony api config requested.");
if(devMode)
return devResponse.getConfig();
return harmonyClient.getConfig();
}
public Activity getCurrentActivity() {
log.debug("Harmony api current sctivity requested.");
if(devMode)
return devResponse.getCurrentActivity();
return harmonyClient.getCurrentActivity();
}
public Boolean startActivity(RunActivity anActivity) {
log.debug("Harmony api start activity requested for: " + anActivity.getName() + " noop mode: " + noopCalls);
if (anActivity.isValid()) {
try {
if (noopCalls || devMode) {
if(devMode)
{
if(anActivity != null)
devResponse.setCurrentActivity(devResponse.getConfig().getActivityByName(anActivity.getName()));
}
log.info("noop mode: Harmony api start activity requested for: " + anActivity.getName());
}
else
harmonyClient.startActivity(Integer.parseInt(anActivity.getName()));
} catch (IllegalArgumentException e) {
try {
if (!noopCalls)
harmonyClient.startActivityByName(anActivity.getName());
} catch (IllegalArgumentException ei) {
log.error("Error in finding activity: " + anActivity.getName());
return false;
}
}
} else {
log.error("Error in finding activity: " + anActivity.getName());
return false;
}
return true;
}
public Boolean pressButton(ButtonPress aDeviceButton) {
log.debug("Harmony api press a button requested for device: " + aDeviceButton.getDevice() + " and a for button: " + aDeviceButton.getButton() + " noop mode: " + noopCalls);
if (aDeviceButton.isValid()) {
try {
if (noopCalls || devMode) {
log.info("noop mode: Harmony api press a button requested for device: " + aDeviceButton.getDevice() + " and a for button: " + aDeviceButton.getButton());
}
else
harmonyClient.pressButton(Integer.parseInt(aDeviceButton.getDevice()), aDeviceButton.getButton());
} catch (IllegalArgumentException e) {
try {
if (!noopCalls)
harmonyClient.pressButton(aDeviceButton.getDevice(), aDeviceButton.getButton());
} catch (IllegalArgumentException ei) {
log.error("Error in finding device: " + aDeviceButton.getDevice() +" and a button: " + aDeviceButton.getButton());
return false;
}
}
} else {
log.error("Error in finding device: " + aDeviceButton.getDevice() +" and a button: " + aDeviceButton.getButton());
return false;
}
return true;
}
}

View File

@@ -0,0 +1,76 @@
package com.bwssystems.harmony;
import static java.lang.String.format;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.google.inject.Guice;
import com.google.inject.Injector;
import net.whistlingfish.harmony.ActivityChangeListener;
import net.whistlingfish.harmony.HarmonyClient;
import net.whistlingfish.harmony.HarmonyClientModule;
import net.whistlingfish.harmony.config.Activity;
public class HarmonyServer {
@Inject
private HarmonyClient harmonyClient;
private HarmonyHandler myHarmony;
private DevModeResponse devResponse;
private Logger log = LoggerFactory.getLogger(HarmonyServer.class);
public HarmonyServer() {
super();
myHarmony = null;
}
public static HarmonyServer setup(BridgeSettings bridgeSettings) throws Exception {
if(!bridgeSettings.isValidHarmony()) {
return new HarmonyServer();
}
Injector injector = null;
if(!bridgeSettings.isDevMode())
injector = Guice.createInjector(new HarmonyClientModule());
HarmonyServer mainObject = new HarmonyServer();
if(!bridgeSettings.isDevMode())
injector.injectMembers(mainObject);
mainObject.execute(bridgeSettings);
return mainObject;
}
private void execute(BridgeSettings mySettings) throws Exception {
Boolean noopCalls = Boolean.parseBoolean(System.getProperty("noop.calls", "false"));
String modeString = "";
if(mySettings.isDevMode())
modeString = " (development mode)";
if(noopCalls)
modeString = " (no op calls to harmony)";
log.info("setup initiated " + modeString + "....");
if(mySettings.isDevMode())
{
harmonyClient = null;
devResponse = new DevModeResponse();
}
else {
devResponse = null;
harmonyClient.addListener(new ActivityChangeListener() {
@Override
public void activityStarted(Activity activity) {
log.info(format("activity changed: [%d] %s", activity.getId(), activity.getLabel()));
}
});
harmonyClient.connect(mySettings.getHarmonyAddress(), mySettings.getHarmonyUser(), mySettings.getHarmonyPwd());
}
myHarmony = new HarmonyHandler(harmonyClient, noopCalls, devResponse);
}
public HarmonyHandler getMyHarmony() {
return myHarmony;
}
}

View File

@@ -0,0 +1,18 @@
package com.bwssystems.harmony;
public class RunActivity {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean isValid() {
if (name != null && !name.isEmpty())
return true;
return false;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -16,7 +16,7 @@
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="container" ng-controller="VersionController">
<div class="navbar-header">
<button type="button" class="navbar-toggle">
<span class="sr-only">Toggle navigation</span>
@@ -36,7 +36,7 @@
<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.amazon.com/echo" target="_blank">Amazon Echo</a></li>
<li><a href="">HA Bridge Version 0.4.9</a></li>
<li><a href="">HA Bridge Version {{bridge.habridgeversion}}</a></li>
</ul>
</li>
</ul>

View File

@@ -4,8 +4,11 @@ var app = angular.module('habridge', [
app.config(function ($routeProvider) {
$routeProvider.when('/#', {
templateUrl: 'views/configuration.html',
templateUrl: 'views/nonconfiguration.html',
controller: 'ViewingController'
}).when('/show', {
templateUrl: 'views/configuration.html',
controller: 'ViewingController'
}).when('/editor', {
templateUrl: 'views/editor.html',
controller: 'AddingController'
@@ -18,8 +21,14 @@ app.config(function ($routeProvider) {
}).when('/verascenes', {
templateUrl: 'views/verascene.html',
controller: 'AddingController'
}).when('/harmonydevices', {
templateUrl: 'views/harmonydevice.html',
controller: 'AddingController'
}).when('/harmonyactivities', {
templateUrl: 'views/harmonyactivity.html',
controller: 'AddingController'
}).otherwise({
templateUrl: 'views/configuration.html',
templateUrl: 'views/nonconfiguration.html',
controller: 'ViewingController'
})
});
@@ -27,6 +36,8 @@ app.config(function ($routeProvider) {
app.run( function (bridgeService) {
bridgeService.loadBridgeSettings();
bridgeService.updateShowVera();
bridgeService.updateShowHarmony();
bridgeService.getHABridgeVersion();
});
app.factory('BridgeSettings', function() {
@@ -37,9 +48,10 @@ app.factory('BridgeSettings', function() {
BridgeSettings.upnpdevicedb = "";
BridgeSettings.upnpresponseport = "";
BridgeSettings.veraaddress = "";
BridgeSettings.harmonyaddress = "";
BridgeSettings.upnpstrict = "";
BridgeSettings.traceupnp = "";
BridgeSettings.vtwocompatibility = "";
BridgeSettings.devmode = "";
BridgeSettings.setupnpconfigaddress = function(aconfigaddress){
BridgeSettings.upnpconfigaddress = aconfigaddress;
@@ -60,14 +72,17 @@ app.factory('BridgeSettings', function() {
BridgeSettings.setveraaddress = function(averaaddress){
BridgeSettings.veraaddress = averaaddress;
};
BridgeSettings.setharmonyaddress = function(aharmonyaddress){
BridgeSettings.harmonyaddress = aharmonyaddress;
};
BridgeSettings.setupnpstrict = function(aupnpstrict){
BridgeSettings.upnpstrict = aupnpstrict;
};
BridgeSettings.settraceupnp = function(atraceupnp){
BridgeSettings.traceupnp = atraceupnp;
};
BridgeSettings.setvtwocompatibility = function(avtwocompatibility){
BridgeSettings.vtwocompatibility = avtwocompatibility;
BridgeSettings.setdevmode = function(adevmode){
BridgeSettings.devmode = adevmode;
};
return BridgeSettings;
@@ -76,7 +91,7 @@ app.factory('BridgeSettings', function() {
app.service('bridgeService', function ($http, $window, BridgeSettings) {
var self = this;
self.BridgeSettings = BridgeSettings;
this.state = {base: window.location.origin + "/api/devices", upnpbase: window.location.origin + "/upnp/settings", devices: [], device: [], error: "", showVera: false};
this.state = {base: window.location.origin + "/api/devices", upnpbase: window.location.origin + "/upnp/settings", huebase: window.location.origin + "/api", devices: [], device: [], error: "", showVera: false, showHarmony: false, habridgeversion: ""};
this.viewDevices = function () {
this.state.error = "";
@@ -96,6 +111,23 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
);
};
this.getHABridgeVersion = function () {
this.state.error = "";
return $http.get(this.state.base + "/habridge/version").then(
function (response) {
self.state.habridgeversion = response.data.version;
},
function (error) {
if (error.data) {
self.state.error = error.data.message;
} else {
self.state.error = "cannot get version";
}
console.log(error);
}
);
};
this.loadBridgeSettings = function () {
this.state.error = "";
return $http.get(this.state.upnpbase).then(
@@ -105,13 +137,18 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
self.BridgeSettings.setupnpdevicedb(response.data.upnpdevicedb);
self.BridgeSettings.setupnpresponseport(response.data.upnpresponseport);
self.BridgeSettings.setveraaddress(response.data.veraaddress);
self.BridgeSettings.setharmonyaddress(response.data.harmonyaddress);
self.BridgeSettings.settraceupnp(response.data.traceupnp);
self.BridgeSettings.setupnpstrict(response.data.upnpstrict);
self.BridgeSettings.setvtwocompatibility(response.data.vtwocompatibility);
self.BridgeSettings.setdevmode(response.data.devmode);
if(self.BridgeSettings.veraaddress == "1.1.1.1" || self.BridgeSettings.veraaddress == "")
self.state.showVera = false;
else
self.state.showVera = true;
if(self.BridgeSettings.harmonyaddress == "1.1.1.1" || self.BridgeSettings.harmonyaddress == "")
self.state.showHarmony = false;
else
self.state.showHarmony = true;
},
function (error) {
if (error.data) {
@@ -132,9 +169,17 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
return;
}
this.updateShowHarmony = function () {
if(self.BridgeSettings.harmonyaddress == "1.1.1.1" || self.BridgeSettings.harmonyaddress == "")
this.state.showHarmony = false;
else
this.state.showHarmony = true;
return;
}
this.viewVeraDevices = function () {
this.state.error = "";
if(BridgeSettings.veraaddress == "1.1.1.1" || BridgeSettings.veraaddress == "")
if(!this.state.showVera)
return;
this.state.error = "";
return $http.get(this.state.base + "/vera/devices").then(
@@ -153,7 +198,7 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
this.viewVeraScenes = function () {
this.state.error = "";
if(BridgeSettings.veraaddress == "1.1.1.1" || BridgeSettings.veraaddress == "")
if(!this.state.showVera)
return;
return $http.get(this.state.base + "/vera/scenes").then(
function (response) {
@@ -169,8 +214,46 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
);
};
this.viewHarmonyActivities = function () {
this.state.error = "";
if(!this.state.showHarmony)
return;
return $http.get(this.state.base + "/harmony/activities").then(
function (response) {
self.state.harmonyactivities = response.data;
},
function (error) {
if (error.data) {
$window.alert("Get Harmony Activities Error: " + error.data.message);
} else {
$window.alert("Get Harmony Activities Error: unknown");
}
}
);
};
this.viewHarmonyDevices = function () {
this.state.error = "";
if(!this.state.showHarmony)
return;
return $http.get(this.state.base + "/harmony/devices").then(
function (response) {
self.state.harmonydevices = response.data;
},
function (error) {
if (error.data) {
$window.alert("Get Harmony Devices Error: " + error.data.message);
} else {
$window.alert("Get Harmony Devices Error: unknown");
}
}
);
};
this.addDevice = function (id, name, type, onUrl, offUrl, httpVerb, contentType, contentBody, contentBodyOff) {
this.state.error = "";
if(httpVerb != null && httpVerb != "")
type = "custom";
if (id) {
var putUrl = this.state.base + "/" + id;
return $http.put(putUrl, {
@@ -197,6 +280,8 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
} else {
if(type == null || type == "")
type = "switch";
if(httpVerb != null && httpVerb != "")
type = "custom";
return $http.post(this.state.base, {
name: name,
deviceType: type,
@@ -246,6 +331,7 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
bridgeService.viewDevices();
$scope.bridge = bridgeService.state;
bridgeService.updateShowVera();
bridgeService.updateShowHarmony();
$scope.predicate = '';
$scope.reverse = true;
$scope.order = function(predicate) {
@@ -257,6 +343,18 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
};
$scope.testUrl = function (device, type) {
if(type == "on") {
if(device.deviceType == "activity" || device.deviceType == "button") {
$http.put($scope.bridge.huebase + "/test/lights/" + device.id + "/state", "{\"on\":true}").then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
return;
}
if(device.httpVerb == "PUT")
$http.put(device.onUrl, device.contentBody).then(
function (response) {
@@ -279,6 +377,18 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
window.open(device.onUrl, "_blank");
}
else {
if(device.deviceType == "activity" || device.deviceType == "button") {
$http.put($scope.bridge.huebase + "/test/lights/" + device.id + "/state", "{\"on\":false}").then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
return;
}
if(device.httpVerb == "PUT")
$http.put(device.offUrl, device.contentBodyOff).then(
function (response) {
@@ -319,8 +429,11 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
bridgeService.device = $scope.device;
bridgeService.viewVeraDevices();
bridgeService.viewVeraScenes();
bridgeService.viewHarmonyActivities();
bridgeService.viewHarmonyDevices();
$scope.bridge = bridgeService.state;
bridgeService.updateShowVera();
bridgeService.updateShowHarmony();
$scope.device = bridgeService.state.device;
$scope.predicate = '';
$scope.reverse = true;
@@ -397,8 +510,34 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
+ verascene.id;
};
$scope.testUrl = function (url) {
$scope.buildActivityUrls = function (harmonyactivity) {
$scope.device.deviceType = "activity";
$scope.device.name = harmonyactivity.label;
$scope.device.onUrl = "{\"name\":\"" + harmonyactivity.id + "\"}";
$scope.device.offUrl = "{\"name\":\"-1\"}";
};
$scope.buildButtonUrls = function (harmonydevice, onbutton, offbutton) {
$scope.device.deviceType = "button";
$scope.device.name = harmonydevice.label;
$scope.device.onUrl = "{\"device\":\"" + harmonydevice.id + "\",\"button\":\"" + onbutton + "\"}";
$scope.device.offUrl = "{\"device\":\"" + harmonydevice.id + "\",\"button\":\"" + offbutton + "\"}";
};
$scope.testUrl = function (device, type) {
if(type == "on") {
if(device.deviceType == "activity" || device.deviceType == "button") {
$http.put($scope.bridge.huebase + "/test/lights/" + device.id + "/state", "{\"on\":true}").then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
return;
}
if(device.httpVerb == "PUT")
$http.put(device.onUrl, device.contentBody).then(
function (response) {
@@ -421,6 +560,18 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
window.open(device.onUrl, "_blank");
}
else {
if(device.deviceType == "activity" || device.deviceType == "button") {
$http.put($scope.bridge.huebase + "/test/lights/" + device.id + "/state", "{\"on\":false}").then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
return;
}
if(device.httpVerb == "PUT")
$http.put(device.offUrl, device.contentBodyOff).then(
function (response) {
@@ -467,4 +618,8 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
app.controller('ErrorsController', function ($scope, bridgeService) {
$scope.bridge = bridgeService.state;
});
});
app.controller('VersionController', function ($scope, bridgeService) {
$scope.bridge = bridgeService.state;
});

View File

@@ -2,72 +2,11 @@
<li role="presentation" class="active"><a href="#">Configuration</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
<div class="panel panel-default bridgeServer">
<div class="panel-heading">
<h1 class="panel-title">Bridge settings</h1>
</div>
<div class="panel-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-3 control-label" for="bridge-base">Bridge
server</label>
<div class="col-xs-8 col-sm-7">
<input id="bridge-base" class="form-control" type="text"
ng-model="bridge.base" placeholder="URL to bridge">
</div>
<button type="submit" class="col-xs-2 col-sm-1 btn btn-primary"
ng-click="setBridgeUrl(bridge.base)">Load</button>
<button type="submit" class="col-xs-2 col-sm-1 btn btn-primary"
ng-click="testUrl(bridge.base)">Go</button>
</div>
</form>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Setting</th>
<th>Value</th>
</tr>
</thead>
<tr>
<td>upnp.config.address</td>
<td>{{BridgeSettings.upnpconfigaddress}}</td>
</tr>
<tr>
<td>server.port</td>
<td>{{BridgeSettings.serverport}}</td>
</tr>
<tr>
<td>upnp.devices.db</td>
<td>{{BridgeSettings.upnpdevicedb}}</td>
</tr>
<tr>
<td>upnp.response.port</td>
<td>{{BridgeSettings.upnpresponseport}}</td>
</tr>
<tr>
<td>vera.address</td>
<td>{{BridgeSettings.veraaddress}}</td>
</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>
</div>
</div>
<div ng-controller="ErrorsController">
<div ng-if="bridge.error"
@@ -117,4 +56,73 @@
</td>
</tr>
</table>
</div>
</div>
<div class="panel panel-default bridgeServer">
<div class="panel-heading">
<a href="#/"><span class="glyphicon glyphicon-minus" aria-hidden="true"></span></a>
<h1 class="panel-title">Bridge settings</h1>
</div>
<div class="panel-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-3 control-label" for="bridge-base">Bridge
server</label>
<div class="col-xs-8 col-sm-7">
<input id="bridge-base" class="form-control" type="text"
ng-model="bridge.base" placeholder="URL to bridge">
</div>
<button type="submit" class="col-xs-2 col-sm-1 btn btn-primary"
ng-click="setBridgeUrl(bridge.base)">Load</button>
<button type="submit" class="col-xs-2 col-sm-1 btn btn-primary"
ng-click="testUrl(bridge.base)">Go</button>
</div>
</form>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Setting</th>
<th>Value</th>
</tr>
</thead>
<tr>
<td>upnp.config.address</td>
<td>{{BridgeSettings.upnpconfigaddress}}</td>
</tr>
<tr>
<td>server.port</td>
<td>{{BridgeSettings.serverport}}</td>
</tr>
<tr>
<td>upnp.devices.db</td>
<td>{{BridgeSettings.upnpdevicedb}}</td>
</tr>
<tr>
<td>upnp.response.port</td>
<td>{{BridgeSettings.upnpresponseport}}</td>
</tr>
<tr>
<td>vera.address</td>
<td>{{BridgeSettings.veraaddress}}</td>
</tr>
<tr>
<td>harmony.address</td>
<td>{{BridgeSettings.harmonyaddress}}</td>
</tr>
<tr>
<td>upnp.strict</td>
<td>{{BridgeSettings.upnpstrict}}</td>
</tr>
<tr>
<td>trace.upnp</td>
<td>{{BridgeSettings.traceupnp}}</td>
</tr>
<tr>
<td>dev.mode</td>
<td>{{BridgeSettings.devmode}}</td>
</tr>
</table>
</div>
</div>

View File

@@ -2,6 +2,8 @@
<li role="presentation"><a href="#">Configuration</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
<li role="presentation" class="active"><a href="#/editdevice">Edit Device</a></li>
</ul>

View File

@@ -2,10 +2,12 @@
<li role="presentation"><a href="#">Configuration</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li role="presentation" class="active"><a href="#/editor">Manual Add</a></li>
</ul>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel panel-default bridgeServer" ng-if="bridge.showVera">
<div class="panel-heading">
<h2 class="panel-title">Generate a new device/scene/control point</h2>
</div>

View File

@@ -0,0 +1,89 @@
<ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Configuration</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li role="presentation" class="active"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading">
<h2 class="panel-title">Harmony Activity List</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
<p class="text-muted">You can select a Harmony Activity and generate
the add activity box selections automatically.</p>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>
<a href="" ng-click="order('label')">Name</a>
<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>Actions</th>
</tr>
</thead>
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | orderBy:predicate:reverse">
<td>{{harmonyactivity.label}}</td>
<td>{{harmonyactivity.id}}</td>
<td>
<button class="btn btn-success" type="submit"
ng-click="buildActivityUrls(harmonyactivity)">Generate
Activity URLs</button>
</td>
</tr>
</table>
</li>
</ul>
</div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading">
<h2 class="panel-title">Add a Harmony Activity</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
</label>
<div class="col-xs-8 col-sm-7">
<input type="text" class="form-control" id="device-name"
ng-model="device.name" placeholder="Device Name">
</div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Activity</button>
</div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
URL </label>
<div class="col-xs-8 col-sm-7">
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label"
for="device-off-url">Off URL </label>
<div class="col-xs-8 col-sm-7">
<textarea rows="3" class="form-control" id="device-off-url"
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
</div>
</div>
</div>
</form>
</li>
</ul>
</div>

View File

@@ -0,0 +1,105 @@
<ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Configuration</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li role="presentation" class="active"><a href="#/harmonydevices">Harmony Devices</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading">
<h2 class="panel-title">Harmony Device List</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
<p class="text-muted">You can select a Harmony Device and Button and generate
the add button box selections automatically.</p>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>
<a href="" ng-click="order('label')">Name</a>
<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>On Button</th>
<th>Off Button</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="harmonydevice in bridge.harmonydevices | orderBy:predicate:reverse">
<td>{{harmonydevice.label}}</td>
<td>{{harmonydevice.id}}</td>
<td>
<select name="device-ctrlon" id="device-ctrlon" ng-model="devicectrlon">
<optgroup ng-repeat="ctrlon in harmonydevice.controlGroup" label="{{ctrlon.name}}">
<option ng-repeat="funcon in ctrlon.function">{{funcon.name}}</option>
</optgroup >
</select>
</td>
<td>
<select name="device-ctrloff" id="device-ctrloff" ng-model="devicectrloff">
<optgroup ng-repeat="ctrloff in harmonydevice.controlGroup" label="{{ctrloff.name}}">
<option ng-repeat="funcoff in ctrloff.function">{{funcoff.name}}</option>
</optgroup >
</select>
</td>
<td>
<button class="btn btn-success" type="submit"
ng-click="buildButtonUrls(harmonydevice, devicectrlon, devicectrloff)">Generate
Button URLs</button>
</td>
</tr>
</table>
</li>
</ul>
</div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading">
<h2 class="panel-title">Add a Harmony Button</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
</label>
<div class="col-xs-8 col-sm-7">
<input type="text" class="form-control" id="device-name"
ng-model="device.name" placeholder="Device Name">
</div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Button</button>
</div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
URL </label>
<div class="col-xs-8 col-sm-7">
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label"
for="device-off-url">Off URL </label>
<div class="col-xs-8 col-sm-7">
<textarea rows="3" class="form-control" id="device-off-url"
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
</div>
</div>
</div>
</form>
</li>
</ul>
</div>

View File

@@ -0,0 +1,64 @@
<ul class="nav nav-pills" role="tablist">
<li role="presentation" class="active"><a href="#">Configuration</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
<div ng-controller="ErrorsController">
<div ng-if="bridge.error"
class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert"
aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h2 ng-show='bridge.error != ""'>ERROR</h2>
<div ng-show='bridge.error != ""'>{{bridge.error}}</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">Current devices</h2>
</div>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<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('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>
</tr>
</thead>
<tr ng-repeat="device in bridge.devices | orderBy:predicate:reverse">
<td>{{device.id}}</td>
<td>{{device.name}}</td>
<td>{{device.deviceType}}</td>
<td>
<button class="btn btn-info" type="submit"
ng-click="testUrl(device, 'on')">Test ON</button>
<button class="btn btn-info" type="submit"
ng-click="testUrl(device, 'off')">Test OFF</button>
<button class="btn btn-warning" type="submit"
ng-click="editDevice(device)">Edit</button>
<button class="btn btn-danger" type="submit"
ng-click="deleteDevice(device)">Delete</button>
</td>
</tr>
</table>
</div>
<div class="panel panel-default bridgeServer">
<div class="panel-heading">
<a href="#/show"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></a>
<h1 class="panel-title">Bridge settings</h1>
</div>
</div>

View File

@@ -2,6 +2,8 @@
<li role="presentation"><a href="#">Configuration</a></li>
<li role="presentation" class="active"><a href="#/veradevices">Vera Devices</a></li>
<li role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>

View File

@@ -2,6 +2,8 @@
<li role="presentation"><a href="#">Configuration</a></li>
<li role="presentation"><a href="#/veradevices">Vera Devices</a></li>
<li role="presentation" class="active"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
@@ -12,7 +14,7 @@
<ul class="list-group">
<li class="list-group-item">
<p class="text-muted">You can select a Vera scene and generate
the add device box selections automatically.</p>
the add scene box selections automatically.</p>
<table class="table table-bordered table-striped table-hover">
<thead>

View File

@@ -0,0 +1,3 @@
version=${project.version}
groupId=${project.groupId}
artifactId=${project.artifactId}