Compare commits

...

58 Commits

Author SHA1 Message Date
Admin
09787fe08d Fixed undefined name error in nest setup and celsius conversion for the
nest thermostat.
2016-01-22 12:20:24 -06:00
Admin
37d346f558 Finished Nest implementation and default ip selection to not be
127.0.0.1

Fixes #12
Fixes #20
2016-01-21 16:44:24 -06:00
Admin
ac59398aa0 Testing nest functionality 2016-01-20 16:46:21 -06:00
Admin
87073435fc Updated code for IP address selection on startup fi none given. 2016-01-13 14:54:35 -06:00
Admin
8687f3482a Continuation of nest implementation 2016-01-12 16:34:05 -06:00
Admin
32a5f26ddd Added new object 2016-01-11 16:45:49 -06:00
Admin
c28f07d628 Continuation of nest implementation. 2016-01-11 16:45:02 -06:00
Admin
d3cc961dfb Initial updates for nest control 2016-01-08 16:14:42 -06:00
Admin
1b3d826f28 Fixed Readme format errors 2015-12-29 10:18:51 -06:00
Admin
c8f4d89a45 Update Readme for clarity on upnp.config.address usage. Also, updated
the 'ask alexa' section with more details.
2015-12-29 10:14:26 -06:00
Admin
2b335d6b9b Updated upnp listener logging. Fixed issue with HUE emulation for
configuration. HUE mobile app now connects.
2015-12-17 16:13:46 -06:00
Admin
b27bb5eef8 Updating hue emulation repsonses using hue android app. fixed issues in
returning the hue config and for requesting the mac address.
2015-12-16 16:41:08 -06:00
Admin
3c54ccd56d Finished implementation of generic UDP handling. This will support the
LimitlessLED Bridge. Updated Readme for UDP and more on configuration
setup. Fixed issue with HueMulator handliong due to devices without
mapTypes.

Fixes #6.
2015-12-07 16:34:37 -06:00
Admin
cf772334c4 Start adding UDP handling. 2015-12-04 16:21:11 -06:00
Admin
5a843f7569 Fixed handling when no harmony hub is given. dev.mode was updated as
well.

This closes #14.
2015-12-03 14:36:23 -06:00
Admin
2a52783bb1 Final release of multiple harmony hubs and fixes for GUI.
This closes #5 and #13
2015-12-02 15:42:09 -06:00
Admin
195f1854ec Remove Current Activity object as it is not needed. 2015-12-02 15:35:35 -06:00
Admin
2e5596a6e4 Pre-release of multiple Harmony Hub Support 2015-12-02 15:34:30 -06:00
Admin
9fc13c6c45 Conitinuation of multiple harmony hub implementation. 2015-12-01 16:40:29 -06:00
Admin
4b4d4e36c7 Update filtering capability. Still errors in html/angular. 2015-11-30 16:43:14 -06:00
Admin
1e7bdc560b Start adding components to handle multiple harmony hubs. 2015-11-24 14:27:21 -06:00
Admin
aff0f8d64c Removed the doling out of hue devices on a get call. 2015-11-23 15:17:17 -06:00
Admin
7a812d6e6b Updated VeraInfo to use default http client. Finished work on doling out
devices in the resource manager. Caveat, this did not work well with the
echo. Next version will roll this back.
2015-11-23 15:14:38 -06:00
Admin
26f2105801 First try at doling out lights when requested. 2015-11-20 16:40:43 -06:00
Admin
feef345a3b Update for start of virtualize multiple respnses 2015-11-19 16:43:30 -06:00
Admin
314ae58ebd This closes #8, closes #9, closes #10 and closes #11.
Finished device, scene and activity tracking, updated upnp handling,
updated HUE API config handling and test on and off calls.
2015-11-18 16:31:11 -06:00
Admin
d8b6232ac1 First draft and start of tracking for scenes, devices, activities.
Implemented Harmony activity list so far.
2015-11-17 16:40:24 -06:00
Admin
41e22ee64d Refactor TestUrl call in apps.js. Updated UI to only have test buttons
after a device is added.
2015-11-16 16:38:29 -06:00
Admin
be2fbcd4cb Update script for test on and off. Incorrect body sent for off commands
error return was interpretted incorrectly.
2015-11-16 14:53:42 -06:00
Admin
14e7f37522 Updated HUE API for generic config requests. Updated config parameters
to be more real.
2015-11-13 16:17:49 -06:00
Admin
e3f5946c9d Updated handling to be CORS compatible. 2015-11-13 11:40:13 -06:00
Admin
12eab16f21 updated Readme and enhance emulator logging code. 2015-11-13 11:26:19 -06:00
Admin
3df68047a9 Updated handling of dimming/brightness request. This could be buggy if
the echo changed to not send an on command when changing dimming. The
HUE state change API is not consistent for brightness handling.
2015-11-13 10:57:18 -06:00
Admin
0dd652f82a Updated device return for HUE emulation based on new information of LUX
bulb handling in the API from the Philips dev info.
2015-11-12 16:31:01 -06:00
Admin
53af1a4dfd Final tweaks to the order of sections in the readme 2015-11-11 15:24:17 -06:00
Admin
816a0025b1 Finished updating upnp for the readme 2015-11-11 15:16:42 -06:00
Admin
405562809a finished adding harmony items to readme 2015-11-11 14:39:13 -06:00
Admin
4c87c6fce8 Update readme some more. 2015-11-11 13:29:06 -06:00
Admin
2fd0f7748b Some more readme updates. 2015-11-11 13:19:50 -06:00
Admin
ed5f3b4b3c More readme updates. 2015-11-11 12:43:38 -06:00
Admin
aad09b7527 More updates to readme 2015-11-11 11:28:41 -06:00
Admin
0ae66da085 Continuation of readme updating. 2015-11-11 11:26:42 -06:00
Admin
d344b764da Updated HUE REst descriptions. 2015-11-10 16:48:30 -06:00
Admin
c85b67fb9f Next update for HUE API description 2015-11-10 16:25:16 -06:00
Admin
a23d662444 Adding supported HUE API calls descriptions. 2015-11-10 13:57:01 -06:00
Admin
4b98f799c2 Updated vera returns for scened and devices. 2015-11-10 11:51:21 -06:00
Admin
acba2b5cae Another config rest api clarification. 2015-11-10 11:45:40 -06:00
Admin
4dc818296a Updated Configuration REST API commands. 2015-11-10 11:40:41 -06:00
Admin
40123ed858 Updating document for REST usage. 2015-11-09 16:47:38 -06:00
Admin
718ba5a5c2 Updated build in pom.xml to include classes that were removed for
minimize. This required a dummy clas setup as well in the Harmony
Server.
2015-11-04 11:00:46 -06:00
Admin
2e6944d840 Missed new java class in release. 2015-11-03 15:26:56 -06:00
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
42 changed files with 3312 additions and 560 deletions

692
README.md

File diff suppressed because one or more lines are too long

104
pom.xml
View File

@@ -5,11 +5,11 @@
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>0.4.10</version>
<version>1.3.1</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 or Nest, using lightweight frameworks</description>
<properties>
<java.version>1.8</java.version>
@@ -17,17 +17,39 @@
<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.github.bwssytems</groupId>
<artifactId>nest-controller</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.2</version>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
@@ -58,9 +80,35 @@
<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>
</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>
@@ -83,24 +131,42 @@
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/*.txt</exclude>
<exclude>META-INF/maven/**</exclude>
<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>*:*</artifact>
</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>xpp3:xpp3</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.igniterealtime.smack:*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>com.github.bwssytems:harmony-java-client</artifact>
<includes>
<include>**</include>
</includes>
</filter>
</filters>
<transformers>
<transformer

View File

@@ -1,14 +1,23 @@
package com.bwssystems.HABridge;
import java.util.List;
public class BridgeSettings {
private String upnpconfigaddress;
private String serverport;
private String upnpresponseport;
private String upnpdevicedb;
private String veraaddress;
private IpList harmonyaddress;
private String harmonyuser;
private String harmonypwd;
private Integer upnpresponsedevices;
private boolean upnpstrict;
private boolean traceupnp;
private boolean vtwocompatibility;
private boolean devmode;
private String nestuser;
private String nestpwd;
private boolean nestconfigured;
public String getUpnpConfigAddress() {
return upnpconfigaddress;
@@ -40,7 +49,30 @@ public class BridgeSettings {
public void setVeraAddress(String veraAddress) {
this.veraaddress = veraAddress;
}
public IpList getHarmonyAddress() {
return harmonyaddress;
}
public void setHarmonyAddress(IpList 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 Integer getUpnpResponseDevices() {
return upnpresponsedevices;
}
public void setUpnpResponseDevices(Integer upnpresponsedevices) {
this.upnpresponsedevices = upnpresponsedevices;
}
public boolean isUpnpStrict() {
return upnpstrict;
}
@@ -53,16 +85,49 @@ 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 String getNestuser() {
return nestuser;
}
public void setNestuser(String nestuser) {
this.nestuser = nestuser;
}
public String getNestpwd() {
return nestpwd;
}
public void setNestpwd(String nestpwd) {
this.nestpwd = nestpwd;
}
public boolean isNestConfigured() {
return nestconfigured;
}
public void setNestConfigured(boolean isNestConfigured) {
this.nestconfigured = isNestConfigured;
}
public Boolean isValidVera() {
if(this.veraaddress.contains(Configuration.DEFAULT_VERA_ADDRESS))
if(this.veraaddress.contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidHarmony() {
List<NamedIP> devicesList = this.harmonyaddress.getDevices();
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
if(this.harmonypwd == null || this.harmonypwd == "")
return false;
if(this.harmonyuser == null || this.harmonyuser == "")
return false;
return true;
}
public Boolean isValidNest() {
if(this.nestpwd == null || this.nestpwd == "")
return false;
if(this.nestuser == null || this.nestuser == "")
return false;
return true;
}

View File

@@ -3,6 +3,12 @@ package com.bwssystems.HABridge;
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 UPNP_RESPONSE_DEVICES = "30";
public final static String DEFAULT_ADDRESS = "1.1.1.1";
public final static String LOOP_BACK_ADDRESS = "127.0.0.1";
public final static String LOOP_BACK_INTERFACE = "lo";
public final static String DEFAULT_HARMONY_ADDRESS_LIST = "{devices:[{name:default,ip:1.1.1.1}]}";
public final static String DEFAULT_USER = "";
public final static String DEFAULT_PWD = "";
public final static String DFAULT_WEB_PORT = "8080";
}

View File

@@ -3,8 +3,11 @@ package com.bwssystems.HABridge;
import static spark.Spark.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import org.apache.http.conn.util.InetAddressUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -12,6 +15,9 @@ 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.NestBridge.NestHome;
import com.bwssystems.harmony.HarmonyHome;
import com.google.gson.Gson;
public class HABridge {
@@ -33,43 +39,93 @@ public class HABridge {
public static void main(String[] args) {
Logger log = LoggerFactory.getLogger(HABridge.class);
DeviceResource theResources;
HarmonyHome harmonyHome;
NestHome nestHome;
HueMulator theHueMulator;
UpnpSettingsResource theSettingResponder;
UpnpListener theUpnpListener;
InetAddress address;
String addressString;
InetAddress address = null;
String addressString = null;
BridgeSettings bridgeSettings;
Version theVersion;
theVersion = new Version();
log.info("HA Bridge (v0.4.10) starting setup....");
//get ip address for upnp requests
try {
address = InetAddress.getLocalHost();
addressString = address.getHostAddress();
} catch (UnknownHostException e) {
log.error("Cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
return;
}
log.info("HA Bridge (v" + theVersion.getVersion() + ") starting setup....");
bridgeSettings = new BridgeSettings();
bridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address", addressString));
bridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DFAULT_WEB_PORT));
bridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address", Configuration.DEFAULT_ADDRESS));
if(bridgeSettings.getUpnpConfigAddress().equalsIgnoreCase(Configuration.DEFAULT_ADDRESS)) {
try {
log.info("Getting an IP address for this host....");
Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();
while (ifs.hasMoreElements() && addressString == null) {
NetworkInterface xface = ifs.nextElement();
Enumeration<InetAddress> addrs = xface.getInetAddresses();
String name = xface.getName();
int IPsPerNic = 0;
while (addrs.hasMoreElements() && IPsPerNic == 0) {
address = addrs.nextElement();
if (InetAddressUtils.isIPv4Address(address.getHostAddress())) {
log.debug(name + " ... has IPV4 addr " + address);
if(!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE)|| !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS)) {
IPsPerNic++;
addressString = address.getHostAddress();
log.info("Adding " + addressString + " from interface " + name + " as our default upnp config address.");
}
}
}
}
} catch (SocketException e) {
log.error("Cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
return;
}
bridgeSettings.setUpnpConfigAddress(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.setVeraAddress(System.getProperty("vera.address", Configuration.DEFAULT_ADDRESS));
IpList theHarmonyList;
try {
theHarmonyList = new Gson().fromJson(System.getProperty("harmony.address", Configuration.DEFAULT_HARMONY_ADDRESS_LIST), IpList.class);
} catch (Exception e) {
try {
theHarmonyList = new Gson().fromJson("{devices:[{name:default,ip:" + System.getProperty("harmony.address", Configuration.DEFAULT_ADDRESS) + "}]}", IpList.class);
} catch (Exception et) {
log.error("Cannot parse harmony.address, Exiting with message: " + e.getMessage(), e);
return;
}
}
bridgeSettings.setHarmonyAddress(theHarmonyList);
bridgeSettings.setHarmonyUser(System.getProperty("harmony.user", Configuration.DEFAULT_USER));
bridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd", Configuration.DEFAULT_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")));
bridgeSettings.setUpnpResponseDevices(Integer.parseInt(System.getProperty("upnp.response.devices", Configuration.UPNP_RESPONSE_DEVICES)));
bridgeSettings.setNestuser(System.getProperty("nest.user", Configuration.DEFAULT_USER));
bridgeSettings.setNestpwd(System.getProperty("nest.pwd", Configuration.DEFAULT_PWD));
// 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");
//setup the harmony connection if available
harmonyHome = new HarmonyHome(bridgeSettings);
//setup the nest connection if available
nestHome = new NestHome(bridgeSettings);
// setup the class to handle the resource setup rest api
theResources = new DeviceResource(bridgeSettings);
theResources = new DeviceResource(bridgeSettings, theVersion, harmonyHome, nestHome);
// setup the class to handle the hue emulator rest api
theHueMulator = new HueMulator(theResources.getDeviceRepository());
theHueMulator = new HueMulator(bridgeSettings, theResources.getDeviceRepository(), harmonyHome, nestHome);
theHueMulator.setupServer();
// setup the class to handle the upnp response rest api
theSettingResponder = new UpnpSettingsResource(bridgeSettings);

View File

@@ -0,0 +1,16 @@
package com.bwssystems.HABridge;
import java.util.List;
public class IpList {
private List<NamedIP> devices;
public List<NamedIP> getDevices() {
return devices;
}
public void setDevices(List<NamedIP> devices) {
this.devices = devices;
}
}

View File

@@ -0,0 +1,25 @@
package com.bwssystems.HABridge;
public class NamedIP {
private String name;
private String ip;
private String port;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
}

View File

@@ -0,0 +1,50 @@
package com.bwssystems.HABridge;
import java.io.InputStream;
import java.util.Properties;
public final class Version {
private String version;
private String groupId;
private String artifactId;
private Properties prop;
public Version()
{
InputStream resourceAsStream =
(InputStream) this.getClass().getResourceAsStream(
"/version.properties"
);
this.prop = new Properties();
try
{
this.prop.load( resourceAsStream );
this.version = this.prop.getProperty("version");
this.groupId = this.prop.getProperty("groupId");
this.artifactId = this.prop.getProperty("artifactId");
}
catch (Exception e)
{
this.version = "0.0.0";
this.groupId = "no group";
this.artifactId = "no artifact";
}
}
public String getVersion() {
return version;
}
public String getGroupId() {
return groupId;
}
public String getArtifactId() {
return artifactId;
}
}

View File

@@ -1,8 +1,5 @@
package com.bwssystems.HABridge.api.hue;
import java.util.HashMap;
import java.util.Map;
/**
* Created by arm on 4/14/15.
*/
@@ -14,7 +11,6 @@ public class DeviceResponse {
private String manufacturername;
private String uniqueid;
private String swversion;
private Map<String, String> pointsymbol;
public DeviceState getState() {
return state;
@@ -72,27 +68,6 @@ public class DeviceResponse {
this.swversion = swversion;
}
public Map<String, String> getPointsymbol() {
if(pointsymbol == null)
{
pointsymbol = new HashMap<>();
pointsymbol.put("1", "none");
pointsymbol.put("2", "none");
pointsymbol.put("3", "none");
pointsymbol.put("4", "none");
pointsymbol.put("5", "none");
pointsymbol.put("6", "none");
pointsymbol.put("7", "none");
pointsymbol.put("8", "none");
}
return pointsymbol;
}
public void setPointsymbol(Map<String, String> pointsymbol) {
this.pointsymbol = pointsymbol;
}
public static DeviceResponse createResponse(String name, String id){
DeviceState deviceState = new DeviceState();
DeviceResponse response = new DeviceResponse();
@@ -102,14 +77,13 @@ public class DeviceResponse {
deviceState.setEffect("none");
deviceState.setAlert("none");
deviceState.setBri(254);
deviceState.setSat(254);
response.setName(name);
response.setUniqueid(id);
response.setManufacturername("Philips");
response.setType("Dimmable light");
response.setModelid("LWB004");
response.setSwversion("65003148");
response.setSwversion("66012040");
return response;
}

View File

@@ -1,6 +1,5 @@
package com.bwssystems.HABridge.api.hue;
import java.util.List;
/**
* Created by arm on 4/14/15.
@@ -8,14 +7,9 @@ import java.util.List;
public class DeviceState {
private boolean on;
private int bri = 255;
private int hue;
private int sat;
private String effect;
private int ct;
private String alert;
private String colormode;
private boolean reachable;
private List<Double> xy;
public boolean isOn() {
return on;
@@ -33,22 +27,6 @@ public class DeviceState {
this.bri = bri;
}
public int getHue() {
return hue;
}
public void setHue(int hue) {
this.hue = hue;
}
public int getSat() {
return sat;
}
public void setSat(int sat) {
this.sat = sat;
}
public String getEffect() {
return effect;
}
@@ -57,14 +35,6 @@ public class DeviceState {
this.effect = effect;
}
public int getCt() {
return ct;
}
public void setCt(int ct) {
this.ct = ct;
}
public String getAlert() {
return alert;
}
@@ -73,14 +43,6 @@ public class DeviceState {
this.alert = alert;
}
public String getColormode() {
return colormode;
}
public void setColormode(String colormode) {
this.colormode = colormode;
}
public boolean isReachable() {
return reachable;
}
@@ -89,14 +51,6 @@ public class DeviceState {
this.reachable = reachable;
}
public List<Double> getXy() {
return xy;
}
public void setXy(List<Double> xy) {
this.xy = xy;
}
@Override
public String toString() {
return "DeviceState{" +

View File

@@ -12,11 +12,17 @@ public class HueApiResponse {
private Map<String, DeviceResponse> lights;
private Map<String, String> scenes;
private Map<String, String> groups;
private Map<String, String> schedules;
private Map<String, String> sensors;
private Map<String, String> rules;
private HueConfig config;
public HueApiResponse(String name, String ipaddress, String username, String userid) {
public HueApiResponse(String name, String ipaddress, String devicetype, String userid) {
super();
this.setConfig(HueConfig.createConfig(name, ipaddress, username, userid));
this.setConfig(HueConfig.createConfig(name, ipaddress, devicetype, userid));
this.setRules(new HashMap<>());
this.setSensors(new HashMap<>());
this.setSchedules(new HashMap<>());
this.setGroups(new HashMap<>());
this.setScenes(new HashMap<>());
}
@@ -45,6 +51,30 @@ public class HueApiResponse {
this.groups = groups;
}
public Map<String, String> getSchedules() {
return schedules;
}
public void setSchedules(Map<String, String> schedules) {
this.schedules = schedules;
}
public Map<String, String> getSensors() {
return sensors;
}
public void setSensors(Map<String, String> sensors) {
this.sensors = sensors;
}
public Map<String, String> getRules() {
return rules;
}
public void setRules(Map<String, String> rules) {
this.rules = rules;
}
public HueConfig getConfig() {
return config;
}

View File

@@ -1,7 +1,14 @@
package com.bwssystems.HABridge.api.hue;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
public class HueConfig
{
@@ -26,10 +33,13 @@ public class HueConfig
public static HueConfig createConfig(String name, String ipaddress, String devicetype, String userid) {
HueConfig aConfig = new HueConfig();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC"));
aConfig.setMac(HueConfig.getMacAddress(ipaddress));
aConfig.setApiversion("1.4.0");
aConfig.setPortalservices(false);
aConfig.setGateway("192.168.1.1");
aConfig.setMac("00:00:88:00:bb:ee");
aConfig.setGateway(ipaddress);
aConfig.setSwversion("01005215");
aConfig.setLinkbutton(false);
aConfig.setIpaddress(ipaddress);
@@ -38,20 +48,50 @@ public class HueConfig
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.setUtc(dateFormatGmt.format(new Date()));
aConfig.setProxyaddress("none");
aConfig.setLocaltime(dateFormat.format(new Date()));
aConfig.setTimezone(TimeZone.getDefault().getID());
aConfig.setZigbeechannel("6");
Map<String, WhitelistEntry> awhitelist = new HashMap<>();
awhitelist.put(userid, WhitelistEntry.createEntry(devicetype));
aConfig.setWhitelist(awhitelist);
return aConfig;
}
private static String getMacAddress(String addr)
{
InetAddress ip;
StringBuilder sb = new StringBuilder();
try {
ip = InetAddress.getByName(addr);
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
byte[] mac = network.getHardwareAddress();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? ":" : ""));
}
} catch (UnknownHostException e) {
sb.append("00:00:88:00:bb:ee");
} catch (SocketException e){
sb.append("00:00:88:00:bb:ee");
} catch (Exception e){
sb.append("00:00:88:00:bb:ee");
}
return sb.toString();
}
public Boolean getPortalservices() {
return portalservices;
}

View File

@@ -5,7 +5,10 @@ package com.bwssystems.HABridge.dao;
public class DeviceDescriptor{
private String id;
private String name;
private String mapId;
private String mapType;
private String deviceType;
private String targetDevice;
private String offUrl;
private String onUrl;
private String httpVerb;
@@ -21,7 +24,23 @@ public class DeviceDescriptor{
this.name = name;
}
public String getDeviceType() {
public String getMapId() {
return mapId;
}
public void setMapId(String mapId) {
this.mapId = mapId;
}
public String getMapType() {
return mapType;
}
public void setMapType(String mapType) {
this.mapType = mapType;
}
public String getDeviceType() {
return deviceType;
}
@@ -29,7 +48,15 @@ public class DeviceDescriptor{
this.deviceType = deviceType;
}
public String getOffUrl() {
public String getTargetDevice() {
return targetDevice;
}
public void setTargetDevice(String targetDevice) {
this.targetDevice = targetDevice;
}
public String getOffUrl() {
return offUrl;
}

View File

@@ -178,9 +178,18 @@ public class DeviceRepository {
} else if (name.equals("name")) {
deviceEntry.setName(reader.nextString());
log.debug("Read a Device - device json name: " + deviceEntry.getName());
} else if (name.equals("mapType")) {
deviceEntry.setMapType(reader.nextString());
log.debug("Read a Device - device json name: " + deviceEntry.getMapType());
} else if (name.equals("mapId")) {
deviceEntry.setMapId(reader.nextString());
log.debug("Read a Device - device json name: " + deviceEntry.getMapId());
} else if (name.equals("deviceType")) {
deviceEntry.setDeviceType(reader.nextString());
log.debug("Read a Device - device json type:" + deviceEntry.getDeviceType());
} else if (name.equals("targetDevice")) {
deviceEntry.setTargetDevice(reader.nextString());
log.debug("Read a Device - device json type:" + deviceEntry.getTargetDevice());
} else if (name.equals("offUrl")) {
deviceEntry.setOffUrl(reader.nextString());
log.debug("Read a Device - device json off URL:" + deviceEntry.getOffUrl());

View File

@@ -1,6 +1,7 @@
package com.bwssystems.HABridge.devicemanagmeent;
import static spark.Spark.get;
import static spark.Spark.options;
import static spark.Spark.post;
import static spark.Spark.put;
import static spark.Spark.delete;
@@ -16,8 +17,11 @@ 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.NestBridge.NestHome;
import com.bwssystems.harmony.HarmonyHome;
import com.bwssystems.luupRequests.Sdata;
import com.bwssystems.vera.VeraInfo;
import com.google.gson.Gson;
@@ -31,12 +35,24 @@ public class DeviceResource {
private DeviceRepository deviceRepository;
private VeraInfo veraInfo;
private Version version;
private HarmonyHome myHarmonyHome;
private NestHome nestHome;
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
public DeviceResource(BridgeSettings theSettings) {
super();
deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
veraInfo = new VeraInfo(theSettings.getVeraAddress(), theSettings.isValidVera());
public DeviceResource(BridgeSettings theSettings, Version theVersion, HarmonyHome theHarmonyHome, NestHome aNestHome) {
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
this.veraInfo = new VeraInfo(theSettings.getVeraAddress(), theSettings.isValidVera());
if(theSettings.isValidHarmony())
this.myHarmonyHome = theHarmonyHome;
else
this.myHarmonyHome = null;
if(theSettings.isValidNest())
this.nestHome = aNestHome;
else
this.nestHome = null;
this.version = theVersion;
setupEndpoints();
}
@@ -46,6 +62,15 @@ public class DeviceResource {
private void setupEndpoints() {
log.info("HABridge device management service started.... ");
// http://ip_address:port/api/devices CORS request
options(API_CONTEXT, "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post(API_CONTEXT, "application/json", (request, response) -> {
log.debug("Create a Device - request body: " + request.body());
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
@@ -61,11 +86,21 @@ public class DeviceResource {
deviceRepository.save(device);
log.debug("Created a Device: " + request.body());
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.status(HttpStatus.SC_CREATED);
return device;
}, new JsonTransformer());
// http://ip_address:port/api/devices/:id CORS request
options(API_CONTEXT + "/:id", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
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);
@@ -81,6 +116,9 @@ public class DeviceResource {
deviceEntry.setName(device.getName());
if (device.getDeviceType() != null)
deviceEntry.setDeviceType(device.getDeviceType());
deviceEntry.setMapId(device.getMapId());
deviceEntry.setMapType(device.getMapType());
deviceEntry.setTargetDevice(device.getTargetDevice());
deviceEntry.setOnUrl(device.getOnUrl());
deviceEntry.setOffUrl(device.getOffUrl());
deviceEntry.setHttpVerb(device.getHttpVerb());
@@ -115,8 +153,9 @@ public class DeviceResource {
}, new JsonTransformer());
delete (API_CONTEXT + "/:id", "application/json", (request, response) -> {
log.debug("Delete a device");
DeviceDescriptor deleted = deviceRepository.findOne(request.params(":id"));
String anId = request.params(":id");
log.debug("Delete a device: " + anId);
DeviceDescriptor deleted = deviceRepository.findOne(anId);
if(deleted == null)
response.status(HttpStatus.SC_NOT_FOUND);
else
@@ -127,6 +166,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 +195,44 @@ public class DeviceResource {
return sData.getScenes();
}, new JsonTransformer());
get (API_CONTEXT + "/harmony/activities", "application/json", (request, response) -> {
log.debug("Get harmony activities");
if(myHarmonyHome == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return myHarmonyHome.getActivities();
}, new JsonTransformer());
get (API_CONTEXT + "/harmony/show", "application/json", (request, response) -> {
log.debug("Get harmony current activity");
if(myHarmonyHome == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return myHarmonyHome.getCurrentActivities();
}, new JsonTransformer());
get (API_CONTEXT + "/harmony/devices", "application/json", (request, response) -> {
log.debug("Get harmony devices");
if(myHarmonyHome == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return myHarmonyHome.getDevices();
}, new JsonTransformer());
get (API_CONTEXT + "/nest/items", "application/json", (request, response) -> {
log.debug("Get nest items");
if(nestHome == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return nestHome.getItems();
}, new JsonTransformer());
}
}

View File

@@ -1,11 +1,19 @@
package com.bwssystems.HABridge.hue;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.JsonTransformer;
import com.bwssystems.HABridge.api.UserCreateRequest;
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.NestBridge.NestInstruction;
import com.bwssystems.NestBridge.NestHome;
import com.bwssystems.harmony.ButtonPress;
import com.bwssystems.harmony.HarmonyHandler;
import com.bwssystems.harmony.HarmonyHome;
import com.bwssystems.harmony.RunActivity;
import com.bwssystems.nest.controller.Nest;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
@@ -13,6 +21,7 @@ import com.google.gson.Gson;
import net.java.dev.eval.Expression;
import static spark.Spark.get;
import static spark.Spark.options;
import static spark.Spark.post;
import static spark.Spark.put;
@@ -33,10 +42,15 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.DatatypeConverter;
/**
* Based on Armzilla's HueMulator - a Philips Hue emulator using sparkjava rest server
*/
@@ -51,23 +65,38 @@ public class HueMulator {
private static final String HUE_CONTEXT = "/api";
private DeviceRepository repository;
private HarmonyHome myHarmonyHome;
private Nest theNest;
private HttpClient httpClient;
private ObjectMapper mapper;
private BridgeSettings bridgeSettings;
private byte[] sendData;
public HueMulator(DeviceRepository aDeviceRepository){
public HueMulator(BridgeSettings theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome, NestHome aNestHome){
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;
if(theBridgeSettings.isValidHarmony())
this.myHarmonyHome = theHarmonyHome;
else
this.myHarmonyHome = null;
if(theBridgeSettings.isValidNest())
this.theNest = aNestHome.getTheNest();
else
this.theNest = null;
bridgeSettings = theBridgeSettings;
}
// This function sets up the sparkjava rest calls for the hue api
public void setupServer() {
log.info("Hue emulator service started....");
// http://ip_address:port/api/{userId}/lights returns json objects of all lights configured
// http://ip_address:port/api/{userId}/lights returns json objects of all lights configured
get(HUE_CONTEXT + "/:userid/lights", "application/json", (request, response) -> {
String userId = request.params(":userid");
if(bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue lights list requested: " + userId + " from " + request.ip());
log.debug("hue lights list requested: " + userId + " from " + request.ip());
List<DeviceDescriptor> deviceList = repository.findAll();
Map<String, DeviceResponse> deviceResponseMap = new HashMap<>();
@@ -80,12 +109,23 @@ public class HueMulator {
return deviceResponseMap;
} , 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 CORS request
options(HUE_CONTEXT, "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/api with body of user request returns json object for a success of user add
post(HUE_CONTEXT, "application/json", (request, response) -> {
UserCreateRequest aNewUser = null;
String newUser = null;
String aDeviceType = null;
if(bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue api/ user create requested: " + request.body() + " from " + request.ip());
log.debug("hue api user create requested: " + request.body() + " from " + request.ip());
if(request.body() != null && !request.body().isEmpty()) {
@@ -98,20 +138,33 @@ public class HueMulator {
if(aDeviceType == null)
aDeviceType = "<not given>";
if(bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
log.debug("hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json; charset=utf-8");
response.status(HttpStatus.SC_OK);
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
} );
// http://ip_address:port/api/* CORS request
options(HUE_CONTEXT + "/*", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/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(bridgeSettings.isTraceupnp())
log.info("Traceupnp: 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);
@@ -125,12 +178,42 @@ public class HueMulator {
aDeviceType = "<not given>";
log.debug("HH trace: hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json; charset=utf-8");
response.status(HttpStatus.SC_OK);
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
} );
// http://ip_address:port/api/{userId} returns json objects for the full state
// http://ip_address:port/api/config returns json objects for the config when no user is given
get(HUE_CONTEXT + "/config", "application/json", (request, response) -> {
if(bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue api/config config requested: <no_user> from " + request.ip());
log.debug("hue api config requested: <no_user> from " + request.ip());
HueApiResponse apiResponse = new HueApiResponse("Philips hue", bridgeSettings.getUpnpConfigAddress(), "My App", "none");
response.type("application/json; charset=utf-8");
response.status(HttpStatus.SC_OK);
// String responseString = null;
// responseString = "[{\"swversion\":\"" + apiResponse.getConfig().getSwversion() + "\",\"apiversion\":\"" + apiResponse.getConfig().getApiversion() + "\",\"name\":\"" + apiResponse.getConfig().getName() + "\",\"mac\":\"" + apiResponse.getConfig().getMac() + "\"}]";
// return responseString;
return apiResponse.getConfig();
}, new JsonTransformer());
// http://ip_address:port/api/{userId}/config returns json objects for the config
get(HUE_CONTEXT + "/:userid/config", "application/json", (request, response) -> {
String userId = request.params(":userid");
if(bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue api/:userid/config config requested: " + userId + " from " + request.ip());
log.debug("hue api config requested: " + userId + " from " + request.ip());
HueApiResponse apiResponse = new HueApiResponse("Philips hue", bridgeSettings.getUpnpConfigAddress(), "My App", userId);
response.type("application/json; charset=utf-8");
response.status(HttpStatus.SC_OK);
return apiResponse.getConfig();
}, new JsonTransformer());
// http://ip_address:port/api/{userId} returns json objects for the full state
get(HUE_CONTEXT + "/:userid", "application/json", (request, response) -> {
String userId = request.params(":userid");
log.debug("hue api full state requested: " + userId + " from " + request.ip());
@@ -146,7 +229,7 @@ public class HueMulator {
deviceList.put(descriptor.getId(), deviceResponse);
}
);
HueApiResponse apiResponse = new HueApiResponse("Philips hue", request.ip(), "My App", userId);
HueApiResponse apiResponse = new HueApiResponse("Philips hue", bridgeSettings.getUpnpConfigAddress(), "My App", userId);
apiResponse.setLights(deviceList);
response.type("application/json; charset=utf-8");
@@ -154,7 +237,7 @@ public class HueMulator {
return apiResponse;
}, new JsonTransformer());
// http://ip_address:port/api/{userId}/lights/{lightId} returns json object for a given light
// http://ip_address:port/api/{userId}/lights/{lightId} returns json object for a given light
get(HUE_CONTEXT + "/:userid/lights/:id", "application/json", (request, response) -> {
String userId = request.params(":userid");
String lightId = request.params(":id");
@@ -173,7 +256,16 @@ public class HueMulator {
return lightResponse;
}, new JsonTransformer());
// http://ip_address:port/api/{userId}/lights/{lightId}/state uses json object to set the lights state
// http://ip_address:port/api/:userid/lights/:id/state CORS request
options(HUE_CONTEXT + "/:userid/lights/:id/state", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/api/{userId}/lights/{lightId}/state uses json object to set the lights state
put(HUE_CONTEXT + "/:userid/lights/:id/state", "application/json", (request, response) -> {
/**
* strangely enough the Echo sends a content type of application/x-www-form-urlencoded even though
@@ -181,50 +273,161 @@ public class HueMulator {
*/
String userId = request.params(":userid");
String lightId = request.params(":id");
log.debug("hue state change requested: " + userId + " from " + request.ip() + " body: " + request.body());
String responseString = null;
String url = null;
DeviceState state = null;
log.debug("hue state change requested: " + userId + " from " + request.ip() + " body: " + request.body());
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json; charset=utf-8");
response.status(HttpStatus.SC_OK);
try {
state = mapper.readValue(request.body(), DeviceState.class);
} catch (IOException e) {
log.error("Object mapper barfed on input of body.", e);
response.status(HttpStatus.SC_BAD_REQUEST);
return null;
log.warn("Object mapper barfed on input of body.", e);
responseString = "[{\"error\":{\"type\": 2, \"address\": \"/lights/" + lightId + ",\"description\": \"Object mapper barfed on input of body.\"}}]";
return responseString;
}
DeviceDescriptor device = repository.findOne(lightId);
if (device == null) {
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());
return null;
log.warn("Could not find device: " + lightId + " for hue state change request: " + userId + " from " + request.ip() + " body: " + request.body());
responseString = "[{\"error\":{\"type\": 3, \"address\": \"/lights/" + lightId + ",\"description\": \"Could not find device\", \"resource\": \"/lights/" + lightId + "\"}}]";
return responseString;
}
String responseString;
String url;
if (state.isOn()) {
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":true}}]";
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":true}}";
url = device.getOnUrl();
} else {
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":false}}]";
} else if (request.body().contains("false")) {
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(request.body().contains("bri"))
{
if(url == null)
{
url = device.getOnUrl();
responseString = "[";
}
else
responseString = responseString + ",";
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/bri\":" + 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;
responseString = responseString + "]";
if(device.getDeviceType().toLowerCase().contains("activity") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyActivity")))
{
log.debug("executing HUE api request to change activity to Harmony: " + url);
RunActivity anActivity = new Gson().fromJson(url, RunActivity.class);
HarmonyHandler myHarmony = myHarmonyHome.getHarmonyHandler(device.getTargetDevice());
if(myHarmony == null)
{
log.warn("Should not get here, no harmony hub available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"Should not get here, no harmony hub available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
else
myHarmony.startActivity(anActivity);
}
else if(device.getDeviceType().toLowerCase().contains("button") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyButton")))
{
log.debug("executing HUE api request to button press to Harmony: " + url);
ButtonPress aDeviceButton = new Gson().fromJson(url, ButtonPress.class);
HarmonyHandler myHarmony = myHarmonyHome.getHarmonyHandler(device.getTargetDevice());
if(myHarmony == null)
{
log.warn("Should not get here, no harmony hub available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"Should not get here, no harmony hub available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
else
myHarmony.pressButton(aDeviceButton);
}
else if(device.getDeviceType().toLowerCase().contains("home") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("nestHomeAway")))
{
log.debug("executing HUE api request to set away for nest home: " + url);
NestInstruction homeAway = new Gson().fromJson(url, NestInstruction.class);
if(theNest == null)
{
log.warn("Should not get here, no Nest available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"Should not get here, no Nest available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
else
theNest.getHome(homeAway.getName()).setAway(homeAway.getAway());
}
else if(device.getDeviceType().toLowerCase().contains("thermo") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("nestThermoSet")))
{
log.debug("executing HUE api request to set thermostat for nest: " + url);
NestInstruction thermoSetting = new Gson().fromJson(url, NestInstruction.class);
if(theNest == null)
{
log.warn("Should not get here, no Nest available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"Should not get here, no Nest available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
else {
if(thermoSetting.getControl().equalsIgnoreCase("temp")) {
if(request.body().contains("bri")) {
thermoSetting.setTemp(String.valueOf(Math.round((Integer.parseInt(replaceIntensityValue(thermoSetting.getTemp(), state.getBri())) - 32)/1.8)));
log.debug("Setting thermostat: " + thermoSetting.getName() + " to " + thermoSetting.getTemp() + "C");
theNest.getThermostat(thermoSetting.getName()).setTargetTemperature(Float.parseFloat(thermoSetting.getTemp()));
}
}
else if (thermoSetting.getControl().contains("range") ||thermoSetting.getControl().contains("heat") ||thermoSetting.getControl().contains("cool") ||thermoSetting.getControl().contains("off")) {
log.debug("Setting thermostat target type: " + thermoSetting.getName() + " to " + thermoSetting.getControl());
theNest.getThermostat(thermoSetting.getName()).setTargetType(thermoSetting.getControl());
}
else if(thermoSetting.getControl().contains("fan")) {
log.debug("Setting thermostat fan mode: " + thermoSetting.getName() + " to " + thermoSetting.getControl().substring(4));
theNest.getThermostat(thermoSetting.getName()).setFanMode(thermoSetting.getControl().substring(4));
}
else {
log.warn("no valid Nest control info: " + thermoSetting.getControl());
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"no valid Nest control info\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
}
}
else if(url.startsWith("udp://"))
{
log.debug("executing HUE api request to UDP: " + url);
try {
String intermediate = url.substring(6);
String ipAddr = intermediate.substring(0, intermediate.indexOf(':'));
String port = intermediate.substring(intermediate.indexOf(':') + 1, intermediate.indexOf('/'));
String theBody = intermediate.substring(intermediate.indexOf('/')+1);
DatagramSocket responseSocket = new DatagramSocket(Integer.parseInt(port));
if(theBody.startsWith("0x")) {
sendData = DatatypeConverter.parseHexBinary(theBody.substring(2));
}
else
sendData = theBody.getBytes();
InetAddress IPAddress = InetAddress.getByName(ipAddr);
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, Integer.parseInt(port));
responseSocket.send(sendPacket);
responseSocket.close();
} catch (IOException e) {
log.warn("Could not send UDP Datagram packet for request.", e);
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
}
else
{
log.debug("executing HUE api request to Http " + (device.getHttpVerb() == null?"GET":device.getHttpVerb()) + ": " + 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)) {
log.warn("Error on calling url to change device state: " + url);
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"Error on calling url to change device state\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
}
response.type("application/json; charset=utf-8");
response.status(HttpStatus.SC_OK);
return responseString;
});
}
@@ -260,8 +463,9 @@ public class HueMulator {
Integer endResult = Math.round(result.floatValue());
request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE, endResult.toString());
} catch (Exception e) {
log.error("Could not execute Math: " + mathDescriptor, e);
} }
log.warn("Could not execute Math: " + mathDescriptor, e);
}
}
return request;
}
@@ -288,12 +492,12 @@ public class HueMulator {
try {
HttpResponse response = httpClient.execute(request);
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
log.debug("Execute on URL responded: " + response.getStatusLine().getStatusCode());
log.debug((httpVerb == null?"GET":httpVerb) + " execute on URL responded: " + response.getStatusLine().getStatusCode());
if(response.getStatusLine().getStatusCode() == 200){
return true;
}
} catch (IOException e) {
log.error("Error calling out to HA gateway", e);
log.warn("Error calling out to HA gateway", e);
}
return false;
}

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(){
@@ -79,14 +76,7 @@ public class UpnpListener {
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
upnpMulticastSocket.receive(packet);
String packetString = new String(packet.getData());
if(packetString != null && packetString.contains("M-SEARCH")) {
if(traceupnp)
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
else
log.debug("Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
}
if(isSSDPDiscovery(packetString)){
if(isSSDPDiscovery(packet)){
sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort());
}
}
@@ -100,31 +90,40 @@ public class UpnpListener {
}
/**
* very naive ssdp discovery packet detection
* @param body
* @return
* ssdp discovery packet detection
*/
protected boolean isSSDPDiscovery(String 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
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")))
protected boolean isSSDPDiscovery(DatagramPacket packet){
//Only respond to discover request for strict upnp form
String packetString = new String(packet.getData());
if(packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1") && packetString.contains("\"ssdp:discover\"")){
log.debug("isSSDPDiscovery Found message to be an M-SEARCH message.");
log.debug("isSSDPDiscovery Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
if(strict && (packetString.contains("ST: urn:schemas-upnp-org:device:basic:1") || packetString.contains("ST: upnp:rootdevice") || packetString.contains("ST: ssdp:all")))
{
if(traceupnp)
log.info("Traceupnp: isSSDPDiscovery found message to be valid under strict rules - strict: " + strict + ", vTwo.Compatibility: " + vTwoCompatibility);
if(traceupnp) {
log.info("Traceupnp: isSSDPDiscovery found message to be an M-SEARCH message.");
log.info("Traceupnp: isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
}
log.debug("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);
if(traceupnp) {
log.info("Traceupnp: isSSDPDiscovery found message to be an M-SEARCH message.");
log.info("Traceupnp: isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
}
log.debug("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);
else {
// log.debug("isSSDPDiscovery found message to not be valid - strict: " + strict);
// log.debug("SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
}
return false;
}
@@ -135,20 +134,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);
if(traceupnp)
log.info("Traceupnp: sendUpnpResponse: " + discoveryResponse);
else
@@ -156,8 +144,4 @@ public class UpnpListener {
DatagramPacket response = new DatagramPacket(discoveryResponse.getBytes(), discoveryResponse.length(), requester, sourcePort);
socket.send(response);
}
protected String getRandomUUIDString(){
return "88f6698f-2c83-4393-bd03-cd54a9f8595"; // https://xkcd.com/221/
}
}

View File

@@ -40,56 +40,20 @@ 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) {
public UpnpSettingsResource(BridgeSettings theBridgeSettings) {
super();
this.theSettings = theSettings;
this.theSettings = new BridgeSettings();
this.theSettings.setDevMode(theBridgeSettings.isDevMode());
this.theSettings.setHarmonyAddress(theBridgeSettings.getHarmonyAddress());
this.theSettings.setServerPort(theBridgeSettings.getServerPort());
this.theSettings.setTraceupnp(theBridgeSettings.isTraceupnp());
this.theSettings.setUpnpConfigAddress(theBridgeSettings.getUpnpConfigAddress());
this.theSettings.setUpnpDeviceDb(theBridgeSettings.getUpnpDeviceDb());
this.theSettings.setUpnpResponseDevices(theBridgeSettings.getUpnpResponseDevices());
this.theSettings.setUpnpResponsePort(theBridgeSettings.getUpnpResponsePort());
this.theSettings.setUpnpStrict(theBridgeSettings.isUpnpStrict());
this.theSettings.setVeraAddress(theBridgeSettings.getVeraAddress());
this.theSettings.setNestConfigured(theBridgeSettings.isValidNest());
}
public void setupServer() {
@@ -102,10 +66,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,101 @@
package com.bwssystems.NestBridge;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.nest.controller.Home;
import com.bwssystems.nest.controller.Nest;
import com.bwssystems.nest.controller.NestSession;
import com.bwssystems.nest.controller.Thermostat;
import com.bwssystems.nest.protocol.error.LoginException;
import com.bwssystems.nest.protocol.status.WhereDetail;
import com.bwssystems.nest.protocol.status.WhereItem;
public class NestHome {
private static final Logger log = LoggerFactory.getLogger(NestHome.class);
private NestSession theSession;
private Nest theNest;
private ArrayList<NestItem> nestItems;
public NestHome(BridgeSettings bridgeSettings) {
theSession = null;
theNest = null;
nestItems = null;
if(!bridgeSettings.isValidNest()) {
log.info("not a valid nest");
return;
}
try {
theSession = new NestSession(bridgeSettings.getNestuser(), bridgeSettings.getNestpwd());
theNest = new Nest(theSession);
} catch (LoginException e) {
log.error("Caught Login Exception, exiting....");
theSession = null;
}
}
public List<NestItem> getItems() {
if(theNest == null)
return null;
if(nestItems == null) {
nestItems = new ArrayList<NestItem>();
Set<String> homeNames = theNest.getHomeNames();
Home aHome = null;
NestItem anItem = null;
for(String name : homeNames) {
aHome = theNest.getHome(name);
anItem = new NestItem();
anItem.setId(name);
anItem.setName(aHome.getDetail().getName());
anItem.setType("Home");
anItem.setLocation(aHome.getDetail().getLocation());
nestItems.add(anItem);
}
Thermostat thermo = null;
Set<String> thermoNames = theNest.getThermostatNames();
for(String name : thermoNames) {
thermo = theNest.getThermostat(name);
anItem = new NestItem();
anItem.setId(name);
anItem.setType("Thermostat");
String where = null;
String homeName= null;
Boolean found = false;
for(String aHomeName : homeNames) {
WhereDetail aDetail = theNest.getWhere(aHomeName);
ListIterator<WhereItem> anIterator = aDetail.getWheres().listIterator();
while(anIterator.hasNext()) {
WhereItem aWhereItem = (WhereItem) anIterator.next();
if(aWhereItem.getWhereId().equals(thermo.getDeviceDetail().getWhereId())) {
where = aWhereItem.getName();
homeName = theNest.getHome(aHomeName).getDetail().getName();
found = true;
break;
}
}
if(found)
break;
}
anItem.setName(where + "(" + name.substring(name.length() - 4) + ")");
anItem.setLocation(where + " - " + homeName);
nestItems.add(anItem);
}
}
return nestItems;
}
public Nest getTheNest() {
return theNest;
}
}

View File

@@ -0,0 +1,33 @@
package com.bwssystems.NestBridge;
public class NestInstruction {
private String name;
private Boolean away;
private String control;
private String temp;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getAway() {
return away;
}
public void setAway(Boolean away) {
this.away = away;
}
public String getControl() {
return control;
}
public void setControl(String control) {
this.control = control;
}
public String getTemp() {
return temp;
}
public void setTemp(String temp) {
this.temp = temp;
}
}

View File

@@ -0,0 +1,32 @@
package com.bwssystems.NestBridge;
public class NestItem {
private String name;
private String id;
private String type;
private String location;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String anid) {
id = anid;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}

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,21 @@
package com.bwssystems.harmony;
import net.whistlingfish.harmony.config.Activity;
public class HarmonyActivity {
private String hub;
private Activity activity;
public String getHub() {
return hub;
}
public void setHub(String hub) {
this.hub = hub;
}
public Activity getActivity() {
return activity;
}
public void setActivity(Activity activity) {
this.activity = activity;
}
}

View File

@@ -0,0 +1,20 @@
package com.bwssystems.harmony;
import net.whistlingfish.harmony.config.Device;
public class HarmonyDevice {
private Device device;
private String hub;
public Device getDevice() {
return device;
}
public void setDevice(Device device) {
this.device = device;
}
public String getHub() {
return hub;
}
public void setHub(String hub) {
this.hub = hub;
}
}

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,115 @@
package com.bwssystems.harmony;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.IpList;
import com.bwssystems.HABridge.NamedIP;
import net.whistlingfish.harmony.config.Activity;
import net.whistlingfish.harmony.config.Device;
public class HarmonyHome {
private static final Logger log = LoggerFactory.getLogger(HarmonyHome.class);
private Map<String, HarmonyServer> hubs;
public HarmonyHome(BridgeSettings bridgeSettings) {
super();
hubs = new HashMap<String, HarmonyServer>();
if(!bridgeSettings.isValidHarmony() && !bridgeSettings.isDevMode())
return;
if(bridgeSettings.isDevMode()) {
NamedIP devModeIp = new NamedIP();
devModeIp.setIp("10.10.10.10");
devModeIp.setName("devMode");
List<NamedIP> theList = new ArrayList<NamedIP>();
theList.add(devModeIp);
IpList thedevList = new IpList();
thedevList.setDevices(theList);
bridgeSettings.setHarmonyAddress(thedevList);
}
Iterator<NamedIP> theList = bridgeSettings.getHarmonyAddress().getDevices().iterator();
while(theList.hasNext()) {
NamedIP aHub = theList.next();
try {
hubs.put(aHub.getName(), HarmonyServer.setup(bridgeSettings, aHub));
} catch (Exception e) {
log.error("Cannot get harmony client (" + aHub.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
return;
}
}
}
public HarmonyHandler getHarmonyHandler(String aName) {
HarmonyHandler aHandler = null;
if(aName == null || aName.equals("")) {
aName = "default";
}
if(hubs.get(aName) == null) {
Set<String> keys = hubs.keySet();
if(!keys.isEmpty()) {
aHandler = hubs.get(keys.toArray()[0]).getMyHarmony();
}
else
aHandler = null;
}
else
aHandler = hubs.get(aName).getMyHarmony();
return aHandler;
}
public List<HarmonyActivity> getActivities() {
Iterator<String> keys = hubs.keySet().iterator();
ArrayList<HarmonyActivity> activityList = new ArrayList<HarmonyActivity>();
while(keys.hasNext()) {
String key = keys.next();
Iterator<Activity> activities = hubs.get(key).getMyHarmony().getActivities().iterator();
while(activities.hasNext()) {
HarmonyActivity anActivity = new HarmonyActivity();
anActivity.setActivity(activities.next());
anActivity.setHub(key);
activityList.add(anActivity);
}
}
return activityList;
}
public List<HarmonyActivity> getCurrentActivities() {
Iterator<String> keys = hubs.keySet().iterator();
ArrayList<HarmonyActivity> activityList = new ArrayList<HarmonyActivity>();
while(keys.hasNext()) {
String key = keys.next();
Iterator<Activity> activities = hubs.get(key).getMyHarmony().getActivities().iterator();
while(activities.hasNext()) {
HarmonyActivity anActivity = new HarmonyActivity();
anActivity.setActivity(activities.next());
anActivity.setHub(key);
activityList.add(anActivity);
}
}
return activityList;
}
public List<HarmonyDevice> getDevices() {
Iterator<String> keys = hubs.keySet().iterator();
ArrayList<HarmonyDevice> deviceList = new ArrayList<HarmonyDevice>();
while(keys.hasNext()) {
String key = keys.next();
Iterator<Device> devices = hubs.get(key).getMyHarmony().getDevices().iterator();
while(devices.hasNext()) {
HarmonyDevice aDevice = new HarmonyDevice();
aDevice.setDevice(devices.next());
aDevice.setHub(key);
deviceList.add(aDevice);
}
}
return deviceList;
}
}

View File

@@ -0,0 +1,84 @@
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.bwssystems.HABridge.NamedIP;
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;
import net.whistlingfish.harmony.protocol.OAReplyProvider;
public class HarmonyServer {
@Inject
private HarmonyClient harmonyClient;
private HarmonyHandler myHarmony;
private DevModeResponse devResponse;
private OAReplyProvider dummyProvider;
private NamedIP myNameAndIP;
private Logger log = LoggerFactory.getLogger(HarmonyServer.class);
public HarmonyServer(NamedIP theHarmonyAddress) {
super();
myHarmony = null;
dummyProvider = null;
myNameAndIP = theHarmonyAddress;
}
public static HarmonyServer setup(BridgeSettings bridgeSettings, NamedIP theHarmonyAddress) throws Exception {
if(!bridgeSettings.isValidHarmony() && !bridgeSettings.isDevMode()) {
return new HarmonyServer(theHarmonyAddress);
}
Injector injector = null;
if(!bridgeSettings.isDevMode())
injector = Guice.createInjector(new HarmonyClientModule());
HarmonyServer mainObject = new HarmonyServer(theHarmonyAddress);
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(dummyProvider != null)
log.debug("something is very wrong as dummyProvider is not null...");
if(mySettings.isDevMode())
modeString = " (development mode)";
else 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(myNameAndIP.getIp(), 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;
}
}

View File

@@ -30,7 +30,7 @@ public class VeraInfo {
public VeraInfo(String addressString, Boolean isValidVera) {
super();
httpClient = HttpClients.createMinimal();
httpClient = HttpClients.createDefault();
veraAddressString = addressString;
validVera = isValidVera;
}

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>
@@ -29,6 +29,7 @@
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="http://echo.amazon.com/#cards" target="_blank">My Echo</a></li>
<li><a href="https://github.com/bwssytems/ha-bridge/blob/master/README.md" target="_blank">Help</a></li>
<li class="dropdown">
<a id="dropdownMenu1" href="" class="dropdown-toggle"
data-toggle="dropdown" role="button" aria-haspopup="true"
@@ -36,7 +37,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.10</a></li>
<li><a href="">HA Bridge Version {{bridge.habridgeversion}}</a></li>
</ul>
</li>
</ul>

View File

@@ -18,6 +18,15 @@ 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'
}).when('/nest', {
templateUrl: 'views/nestactions.html',
controller: 'AddingController'
}).otherwise({
templateUrl: 'views/configuration.html',
controller: 'ViewingController'
@@ -27,6 +36,9 @@ app.config(function ($routeProvider) {
app.run( function (bridgeService) {
bridgeService.loadBridgeSettings();
bridgeService.updateShowVera();
bridgeService.updateShowHarmony();
bridgeService.updateShowNest();
bridgeService.getHABridgeVersion();
});
app.factory('BridgeSettings', function() {
@@ -37,9 +49,11 @@ app.factory('BridgeSettings', function() {
BridgeSettings.upnpdevicedb = "";
BridgeSettings.upnpresponseport = "";
BridgeSettings.veraaddress = "";
BridgeSettings.harmonyaddress = "";
BridgeSettings.upnpstrict = "";
BridgeSettings.traceupnp = "";
BridgeSettings.vtwocompatibility = "";
BridgeSettings.devmode = "";
BridgeSettings.nestconfigured = "";
BridgeSettings.setupnpconfigaddress = function(aconfigaddress){
BridgeSettings.upnpconfigaddress = aconfigaddress;
@@ -60,14 +74,21 @@ 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;
};
BridgeSettings.setnestconfigured = function(anestconfigured){
BridgeSettings.nestconfigured = anestconfigured;
};
return BridgeSettings;
@@ -76,7 +97,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, showNest: false, habridgeversion: ""};
this.viewDevices = function () {
this.state.error = "";
@@ -88,14 +109,63 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
if (error.data) {
self.state.error = error.data.message;
} else {
self.state.error = "If you're not seeing any devices, you may be running into problems with CORS. " +
"You can work around this by running a fresh launch of Chrome with the --disable-web-security flag.";
self.state.error = "Some error occurred.";
}
console.log(error);
}
);
};
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.aContainsB = function (a, b) {
return a.indexOf(b) >= 0;
}
this.updateShowVera = function () {
if(this.aContainsB(self.BridgeSettings.veraaddress, "1.1.1.1") || self.BridgeSettings.veraaddress == "" || self.BridgeSettings.veraaddress == null)
this.state.showVera = false;
else
this.state.showVera = true;
return;
}
this.updateShowNest = function () {
if(self.BridgeSettings.nestconfigured == true)
this.state.showNest = true;
else
this.state.showNest = false;
return;
}
this.updateShowHarmony = function () {
if(self.BridgeSettings.harmonyaddress.devices) {
if(this.aContainsB(self.BridgeSettings.harmonyaddress.devices[0].ip, "1.1.1.1") || self.BridgeSettings.harmonyaddress == "" || self.BridgeSettings.harmonyaddress == null)
this.state.showHarmony = false;
else
this.state.showHarmony = true;
}
else
this.state.showHarmony = false;
return;
}
this.loadBridgeSettings = function () {
this.state.error = "";
return $http.get(this.state.upnpbase).then(
@@ -105,13 +175,11 @@ 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);
if(self.BridgeSettings.veraaddress == "1.1.1.1" || self.BridgeSettings.veraaddress == "")
self.state.showVera = false;
else
self.state.showVera = true;
self.BridgeSettings.setdevmode(response.data.devmode);
self.BridgeSettings.setnestconfigured(response.data.nestconfigured);
},
function (error) {
if (error.data) {
@@ -124,17 +192,28 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
);
};
this.updateShowVera = function () {
if(self.BridgeSettings.veraaddress == "1.1.1.1" || self.BridgeSettings.veraaddress == "")
this.state.showVera = false;
else
this.state.showVera = true;
return;
}
this.viewNestItems = function () {
this.state.error = "";
if(!this.state.showNest)
return;
this.state.error = "";
return $http.get(this.state.base + "/nest/items").then(
function (response) {
self.state.nestitems = response.data;
},
function (error) {
if (error.data) {
$window.alert("Get Nest Items Error: " + error.data.message);
} else {
$window.alert("Get Nest Items Error: unknown");
}
}
);
};
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(
@@ -150,10 +229,10 @@ 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) {
@@ -168,21 +247,78 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
}
);
};
this.addDevice = function (id, name, type, onUrl, offUrl, httpVerb, contentType, contentBody, contentBodyOff) {
this.viewHarmonyActivities = function () {
this.state.error = "";
if (id) {
var putUrl = this.state.base + "/" + id;
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.findDeviceByMapId = function(id, target, type) {
for(var i = 0; i < this.state.devices.length; i++) {
if(this.state.devices[i].mapId == id && this.state.devices[i].mapType == type && this.state.devices[i].targetDevice == target)
return true;
}
return false;
};
this.findNestItemByMapId = function(id, type) {
for(var i = 0; i < this.state.devices.length; i++) {
if(this.state.devices[i].mapId == id && this.aContainsB(this.state.devices[i].mapType, type))
return true;
}
return false;
};
this.addDevice = function (device) {
this.state.error = "";
if(device.httpVerb != null && device.httpVerb != "")
device.deviceType = "custom";
if (device.id) {
var putUrl = this.state.base + "/" + device.id;
return $http.put(putUrl, {
id: id,
name: name,
deviceType: type,
onUrl: onUrl,
offUrl: offUrl,
httpVerb: httpVerb,
contentType: contentType,
contentBody: contentBody,
contentBodyOff: contentBodyOff
id: device.id,
name: device.name,
mapId: device.mapId,
mapType: device.mapType,
deviceType: device.deviceType,
targetDevice: device.targetDevice,
onUrl: device.onUrl,
offUrl: device.offUrl,
httpVerb: device.httpVerb,
contentType: device.contentType,
contentBody: device.contentBody,
contentBodyOff: device.contentBodyOff
}).then(
function (response) {
self.viewDevices();
@@ -195,17 +331,22 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
}
);
} else {
if(type == null || type == "")
type = "switch";
if(device.deviceType == null || device.deviceType == "")
device.deviceType = "custom";
if(device.httpVerb != null && device.httpVerb != "")
device.deviceType = "custom";
return $http.post(this.state.base, {
name: name,
deviceType: type,
onUrl: onUrl,
offUrl: offUrl,
httpVerb: httpVerb,
contentType: contentType,
contentBody: contentBody,
contentBodyOff: contentBodyOff
name: device.name,
mapId: device.mapId,
mapType: device.mapType,
deviceType: device.deviceType,
targetDevice: device.targetDevice,
onUrl: device.onUrl,
offUrl: device.offUrl,
httpVerb: device.httpVerb,
contentType: device.contentType,
contentBody: device.contentBody,
contentBodyOff: device.contentBodyOff
}).then(
function (response) {
self.viewDevices();
@@ -235,8 +376,40 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
);
};
this.editDevice = function (id, name, onUrl, offUrl, httpVerb, contentType, contentBody, contentBodyOff) {
self.state.device = {id: id, name: name, onUrl: onUrl, offUrl: offUrl, httpVerb: httpVerb, contentType: contentType, contentBody: contentBody, contentBodyOff: contentBodyOff};
this.deleteDeviceByMapId = function (id, type) {
for(var i = 0; i < this.state.devices.length; i++) {
if(this.state.devices[i].mapId == id && this.aContainsB(this.state.devices[i].mapType, type))
return self.deleteDevice(this.state.devices[i].id);
}
};
this.editDevice = function (device) {
self.state.device = device;
};
this.testUrl = function (device, type) {
if(type == "on") {
$http.put(this.state.huebase + "/test/lights/" + device.id + "/state", "{\"on\":true}").then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.statusText + ", with status: " + error.status + ", Pleae look in your console log.");
}
);
return;
}
else {
$http.put(this.state.huebase + "/test/lights/" + device.id + "/state", "{\"on\":false}").then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.statusText + ", with status: " + error.status + ", Pleae look in your console log.");
}
);
return;
}
};
});
@@ -246,6 +419,10 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
bridgeService.viewDevices();
$scope.bridge = bridgeService.state;
bridgeService.updateShowVera();
bridgeService.updateShowHarmony();
bridgeService.updateShowNest();
$scope.visible = false;
$scope.imgUrl = "glyphicon glyphicon-plus";
$scope.predicate = '';
$scope.reverse = true;
$scope.order = function(predicate) {
@@ -256,72 +433,52 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
bridgeService.deleteDevice(device.id);
};
$scope.testUrl = function (device, type) {
if(type == "on") {
if(device.httpVerb == "PUT")
$http.put(device.onUrl, device.contentBody).then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
else if(device.httpVerb == "POST")
$http.post(device.onUrl, device.contentBody).then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
else
window.open(device.onUrl, "_blank");
}
else {
if(device.httpVerb == "PUT")
$http.put(device.offUrl, device.contentBodyOff).then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
else if(device.httpVerb == "POST")
$http.post(device.offUrl, device.contentBody).then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
else
window.open(device.offUrl, "_blank");
}
bridgeService.testUrl(device, type);
};
$scope.setBridgeUrl = function (url) {
bridgeService.state.base = url;
bridgeService.viewDevices();
};
$scope.goBridgeUrl = function (url) {
window.open(url, "_blank");
};
$scope.editDevice = function (device) {
bridgeService.editDevice(device.id, device.name, device.onUrl, device.offUrl, device.httpVerb, device.contentType, device.contentBody, device.contentBodyOff);
bridgeService.editDevice(device);
$location.path('/editdevice');
};
$scope.toggle = function () {
$scope.visible = !$scope.visible;
if($scope.visible)
$scope.imgUrl = "glyphicon glyphicon-minus";
else
$scope.imgUrl = "glyphicon glyphicon-plus";
};
});
app.controller('AddingController', function ($scope, $location, $http, bridgeService, BridgeSettings) {
$scope.device = {id: "", name: "", deviceType: "switch", onUrl: "", offUrl: ""};
$scope.device = {id: "", name: "", deviceType: "custom", onUrl: "", offUrl: ""};
$scope.vera = {base: "", port: "3480", id: ""};
$scope.vera.base = "http://" + BridgeSettings.veraaddress;
bridgeService.device = $scope.device;
bridgeService.viewVeraDevices();
bridgeService.viewVeraScenes();
bridgeService.viewHarmonyActivities();
bridgeService.viewHarmonyDevices();
bridgeService.viewNestItems();
$scope.bridge = bridgeService.state;
bridgeService.updateShowVera();
bridgeService.updateShowHarmony();
bridgeService.updateShowNest();
$scope.device = bridgeService.state.device;
$scope.activitiesVisible = false;
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
$scope.buttonsVisible = false;
$scope.imgActivitiesUrl = "glyphicon glyphicon-plus";
$scope.devicesVisible = false;
$scope.imgDevicesUrl = "glyphicon glyphicon-plus";
$scope.scenesVisible = false;
$scope.imgScenesUrl = "glyphicon glyphicon-plus";
$scope.predicate = '';
$scope.reverse = true;
$scope.device_dim_control = "";
@@ -334,6 +491,9 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
if ($scope.vera.base.indexOf("http") < 0) {
$scope.vera.base = "http://" + $scope.vera.base;
}
$scope.device.deviceType = "switch";
$scope.device.mapType = "veraDevice";
$scope.device.mapId = $scope.vera.id;
if(dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0)
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&DeviceNum="
@@ -354,6 +514,8 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
$scope.vera.base = "http://" + $scope.vera.base;
}
$scope.device.deviceType = "scene";
$scope.device.mapType = "veraScene";
$scope.device.mapId = $scope.vera.id;
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum="
+ $scope.vera.id;
@@ -368,6 +530,8 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}
$scope.device.deviceType = "switch";
$scope.device.name = veradevice.name;
$scope.device.mapType = "veraDevice";
$scope.device.mapId = veradevice.id;
if(dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0)
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&DeviceNum="
@@ -389,6 +553,8 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}
$scope.device.deviceType = "scene";
$scope.device.name = verascene.name;
$scope.device.mapType = "veraScene";
$scope.device.mapId = verascene.id;
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum="
+ verascene.id;
@@ -397,60 +563,103 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
+ verascene.id;
};
$scope.testUrl = function (url) {
if(type == "on") {
if(device.httpVerb == "PUT")
$http.put(device.onUrl, device.contentBody).then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
else if(device.httpVerb == "POST")
$http.post(device.onUrl, device.contentBody).then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
else
window.open(device.onUrl, "_blank");
}
else {
if(device.httpVerb == "PUT")
$http.put(device.offUrl, device.contentBodyOff).then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
else if(device.httpVerb == "POST")
$http.post(device.offUrl, device.contentBody).then(
function (response) {
$window.alert("Request Exceuted: " + response.statusText);
},
function (error) {
$window.alert("Request Error: " + error.data.message);
}
);
else
window.open(device.offUrl, "_blank");
}
$scope.buildActivityUrls = function (harmonyactivity) {
$scope.device.deviceType = "activity";
$scope.device.targetDevice = harmonyactivity.hub;
$scope.device.name = harmonyactivity.activity.label;
$scope.device.mapType = "harmonyActivity";
$scope.device.mapId = harmonyactivity.activity.id;
$scope.device.onUrl = "{\"name\":\"" + harmonyactivity.activity.id + "\"}";
$scope.device.offUrl = "{\"name\":\"-1\"}";
};
$scope.buildButtonUrls = function (harmonydevice, onbutton, offbutton) {
$scope.device.deviceType = "button";
$scope.device.targetDevice = harmonydevice.hub;
$scope.device.name = harmonydevice.device.label;
$scope.device.mapType = "harmonyButton";
$scope.device.mapId = harmonydevice.device.id + "-" + onbutton + "-" + offbutton;
$scope.device.onUrl = "{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + onbutton + "\"}";
$scope.device.offUrl = "{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + offbutton + "\"}";
};
$scope.buildNestHomeUrls = function (nestitem) {
$scope.device.deviceType = "home";
$scope.device.name = nestitem.name;
$scope.device.mapType = "nestHomeAway";
$scope.device.mapId = nestitem.id;
$scope.device.onUrl = "{\"name\":\"" + nestitem.id + "\",\"away\":false,\"control\":\"status\"}";
$scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"away\":true,\"control\":\"status\"}";
};
$scope.buildNestTempUrls = function (nestitem) {
$scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Temperature";
$scope.device.mapType = "nestThermoSet";
$scope.device.mapId = nestitem.id + "-SetTemp";
$scope.device.onUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"temp\",\"temp\":\"${intensity.percent}\"}";
$scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"temp\",\"temp\":\"${intensity.percent}\"}";
};
$scope.buildNestHeatUrls = function (nestitem) {
$scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Heat";
$scope.device.mapType = "nestThermoSet";
$scope.device.mapId = nestitem.id + "-SetHeat";
$scope.device.onUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"heat\"}";
$scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"off\"}";
};
$scope.buildNestCoolUrls = function (nestitem) {
$scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Cool";
$scope.device.mapType = "nestThermoSet";
$scope.device.mapId = nestitem.id + "-SetCool";
$scope.device.onUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"cool\"}";
$scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"off\"}";
};
$scope.buildNestRangeUrls = function (nestitem) {
$scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Range";
$scope.device.mapType = "nestThermoSet";
$scope.device.mapId = nestitem.id + "-SetRange";
$scope.device.onUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"range\"}";
$scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"off\"}";
};
$scope.buildNestOffUrls = function (nestitem) {
$scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Thermostat";
$scope.device.mapType = "nestThermoSet";
$scope.device.mapId = nestitem.id + "-TurnOff";
$scope.device.onUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"range\"}";
$scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"off\"}";
};
$scope.buildNestFanUrls = function (nestitem) {
$scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Fan";
$scope.device.mapType = "nestThermoSet";
$scope.device.mapId = nestitem.id + "-SetFan";
$scope.device.onUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"fan-on\"}";
$scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"fan-auto\"}";
};
$scope.testUrl = function (device, type) {
bridgeService.testUrl(device, type);
};
$scope.addDevice = function () {
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, $scope.device.contentBodyOff).then(
bridgeService.addDevice($scope.device).then(
function () {
$scope.device.id = "";
$scope.device.mapType = null;
$scope.device.mapId = null;
$scope.device.name = "";
$scope.device.onUrl = "";
$scope.device.deviceType = "switch";
$scope.device.deviceType = "custom";
$scope.device.targetDevice = null;
$scope.device.offUrl = "";
$scope.device.httpVerb = null;
$scope.device.contentType = null;
@@ -462,9 +671,176 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}
);
}
$scope.toggleActivities = function () {
$scope.activitiesVisible = !$scope.activitiesVisible;
if($scope.activitiesVisible)
$scope.imgActivitiesUrl = "glyphicon glyphicon-minus";
else
$scope.imgActivitiesUrl = "glyphicon glyphicon-plus";
};
$scope.toggleButtons = function () {
$scope.buttonsVisible = !$scope.buttonsVisible;
if($scope.buttonsVisible)
$scope.imgButtonsUrl = "glyphicon glyphicon-minus";
else
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
};
$scope.toggleDevices = function () {
$scope.devicesVisible = !$scope.devicesVisible;
if($scope.devicesVisible)
$scope.imgDevicesUrl = "glyphicon glyphicon-minus";
else
$scope.imgDevicesUrl = "glyphicon glyphicon-plus";
};
$scope.toggleScenes = function () {
$scope.scenesVisible = !$scope.scenesVisible;
if($scope.scenesVisible)
$scope.imgScenesUrl = "glyphicon glyphicon-minus";
else
$scope.imgScenesUrl = "glyphicon glyphicon-plus";
};
$scope.deleteDeviceByMapId = function (id, mapType) {
bridgeService.deleteDeviceByMapId(id, mapType);
};
});
app.filter('availableHarmonyActivityId', function(bridgeService) {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(!bridgeService.findDeviceByMapId(input[i].activity.id, input[i].hub, "harmonyActivity")){
out.push(input[i]);
}
}
return out;
}
});
app.filter('unavailableHarmonyActivityId', function(bridgeService) {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(bridgeService.findDeviceByMapId(input[i].activity.id, input[i].hub, "harmonyActivity")){
out.push(input[i]);
}
}
return out;
}
});
app.filter('availableVeraDeviceId', function(bridgeService) {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(!bridgeService.findDeviceByMapId(input[i].id, null, "veraDevice")){
out.push(input[i]);
}
}
return out;
}
});
app.filter('unavailableVeraDeviceId', function(bridgeService) {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(bridgeService.findDeviceByMapId(input[i].id, null, "veraDevice")){
out.push(input[i]);
}
}
return out;
}
});
app.filter('availableVeraSceneId', function(bridgeService) {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(!bridgeService.findDeviceByMapId(input[i].id, null, "veraScene")){
out.push(input[i]);
}
}
return out;
}
});
app.filter('unavailableVeraSceneId', function(bridgeService) {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(bridgeService.findDeviceByMapId(input[i].id, null, "veraScene")){
out.push(input[i]);
}
}
return out;
}
});
app.filter('availableNestItemId', function(bridgeService) {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(!bridgeService.findNestItemByMapId(input[i].id, "nestHomeAway")){
out.push(input[i]);
}
}
return out;
}
});
app.filter('unavailableNestItemId', function(bridgeService) {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType != null && bridgeService.aContainsB(input[i].mapType, "nest")){
out.push(input[i]);
}
}
return out;
}
});
app.filter('configuredButtons', function() {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType == "harmonyButton"){
out.push(input[i]);
}
}
return out;
}
});
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,12 @@
<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 ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</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"
@@ -98,6 +38,9 @@
<th>
<a href="" ng-click="order('deviceType')">Type</a>
<span class="sortorder" ng-show="predicate === 'deviceType'" ng-class="{reverse:reverse}"></span></th>
<th>
<a href="" ng-click="order('targetDevice')">Target</a>
<span class="sortorder" ng-show="predicate === 'targetDevice'" ng-class="{reverse:reverse}"></span></th>
<th>Actions</th>
</tr>
</thead>
@@ -105,6 +48,7 @@
<td>{{device.id}}</td>
<td>{{device.name}}</td>
<td>{{device.deviceType}}</td>
<td>{{device.targetDevice}}</td>
<td>
<button class="btn btn-info" type="submit"
ng-click="testUrl(device, 'on')">Test ON</button>
@@ -117,4 +61,76 @@
</td>
</tr>
</table>
</div>
</div>
<div class="panel panel-default bridgeServer">
<div class="panel-heading">
<h1 class="panel-title">Bridge settings <a ng-click="toggle()"><span class={{imgUrl}} aria-hidden="true"></a></h1>
</div>
<div ng-if="visible" class="animate-if" class="panel-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-2 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="goBridgeUrl(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>
<tr>
<td>nest.configured</td>
<td>{{BridgeSettings.nestconfigured}}</td>
</tr>
</table>
</div>
</div>

View File

@@ -2,14 +2,19 @@
<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 ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</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>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading">
<h2 class="panel-title">Add a new device</h2>
<h2 class="panel-title">Edit a device</h2>
</div>
<p class="text-muted">This screen allows the modification of many fields the bridge uses. Please use care when
updating these fields as you may break the settings used by the bridge to call a specific end point device.</p>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()">
@@ -22,7 +27,43 @@
ng-model="device.name" placeholder="Device Name">
</div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Update Device</button>
Update Bridge Device</button>
</div>
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-target">Target
</label>
<div class="col-xs-8 col-sm-7">
<input type="text" class="form-control" id="device-target"
ng-model="device.targetDevice" placeholder="default">
</div>
</div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-map-type">Map Type
</label>
<div class="col-xs-8 col-sm-7">
<select name="device-map-type" id="device-map-type" ng-model="device.mapType">
<option value="">---Please select---</option> <!-- not selected / blank option -->
<option value="veraDevice">Vera Device</option>
<option value="veraScene">Vera Scene</option>
<option value="harmonyActivity">Harmony Activity</option>
<option value="harmonyButton">Harmony Button</option>
<option value="nestHomeAway">Nest Home Status</option>
<option value="nestThermoSet">Nest Thermostat</option>
</select>
</div>
</div>
</div>
<div ng-if="device.mapType" class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-map-id">Map ID
</label>
<div class="col-xs-8 col-sm-7">
<input type="text" class="form-control" id="device-map-id"
ng-model="device.mapId" placeholder="1111">
</div>
</div>
<div class="form-group">
<div class="row">
@@ -33,9 +74,6 @@
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
<div class="clearfix visible-xs"></div>
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
ng-click="testUrl(device, 'on')">Test</button>
</div>
</div>
<div class="form-group">
@@ -47,9 +85,6 @@
<textarea rows="3" class="form-control" id="device-off-url"
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
</div>
<div class="clearfix visible-xs"></div>
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
ng-click="testUrl(device, 'off')">Test</button>
</div>
</div>
<div class="form-group">
@@ -67,7 +102,7 @@
</div>
</div>
</div>
<div class="form-group">
<div ng-if="device.httpVerb" class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-content-type">Content Type
</label>
@@ -92,7 +127,7 @@
</div>
</div>
</div>
<div class="form-group">
<div ng-if="device.httpVerb" class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label"
for="device-content-body">Content Body On</label>
@@ -104,7 +139,7 @@
<div class="clearfix visible-xs"></div>
</div>
</div>
<div class="form-group">
<div ng-if="device.httpVerb" class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label"
for="device-content-body-off">Content Body Off</label>

View File

@@ -2,10 +2,13 @@
<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 ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</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>
@@ -71,6 +74,8 @@
<div class="panel-heading">
<h2 class="panel-title">Add a new device</h2>
</div>
<p class="text-muted">This area allows you to create any http or udp call to an endpoint. You can use the default GET or select
the http verb type below and configure a payload for either on or off methods. Currently, https is not supported.</p>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()">
@@ -84,7 +89,7 @@
ng-model="device.name" placeholder="Device Name">
</div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Device</button>
Add Bridge Device</button>
</div>
</div>
<div class="form-group">
@@ -96,9 +101,6 @@
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
<div class="clearfix visible-xs"></div>
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
ng-click="testUrl(device, 'on')">Test</button>
</div>
</div>
<div class="form-group">
@@ -110,9 +112,6 @@
<textarea rows="3" class="form-control" id="device-off-url"
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
</div>
<div class="clearfix visible-xs"></div>
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
ng-click="testUrl(device, 'off')">Test</button>
</div>
</div>
<div class="form-group">
@@ -130,7 +129,7 @@
</div>
</div>
</div>
<div class="form-group">
<div ng-if="device.httpVerb" class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-content-type">Content Type
</label>
@@ -155,7 +154,7 @@
</div>
</div>
</div>
<div class="form-group">
<div ng-if="device.httpVerb" class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label"
for="device-content-body">Content Body On</label>
@@ -167,7 +166,7 @@
<div class="clearfix visible-xs"></div>
</div>
</div>
<div class="form-group">
<div ng-if="device.httpVerb" class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label"
for="device-content-body-off">Content Body Off</label>

View File

@@ -0,0 +1,129 @@
<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 ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</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">For any Harmony Activity, use the action buttons to generate the device addition information below automatically.
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
The 'Already Configured Activities' list below will show what is already setup for your Harmony Hubs.</p>
<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>
<a href="" ng-click="order('hub')">Hub</a>
<span class="sortorder" ng-show="predicate === 'hub'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | availableHarmonyActivityId | orderBy:predicate:reverse">
<td>{{harmonyactivity.activity.label}}</td>
<td>{{harmonyactivity.activity.id}}</td>
<td>{{harmonyactivity.hub}}</td>
<td>
<button class="btn btn-success" type="submit"
ng-click="buildActivityUrls(harmonyactivity)">Generate
Activity URLs</button>
</td>
</tr>
</table>
</li>
</ul>
<div class="panel-heading">
<h2 class="panel-title">Already Configured Activities <a ng-click="toggleActivities()"><span class={{imgActivitiesUrl}} aria-hidden="true"></a></h2>
</div>
<ul ng-if="activitiesVisible" class="list-group">
<li class="list-group-item">
<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>
<a href="" ng-click="order('hub')">Hub</a>
<span class="sortorder" ng-show="predicate === 'hub'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | unavailableHarmonyActivityId | orderBy:predicate:reverse">
<td>{{harmonyactivity.activity.label}}</td>
<td>{{harmonyactivity.activity.id}}</td>
<td>{{harmonyactivity.hub}}</td>
<td><button class="btn btn-danger" type="submit"
ng-click="deleteDeviceByMapId(harmonyactivity.activity.id, 'harmonyActivity')">Delete</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 Bridge Device for 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 Bridge Device</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,152 @@
<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 ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</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">For any Harmony Device and Buttons, use the action buttons to generate the device addition information below automatically.
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
The 'Already Configured Harmony Buttons' list below will show what is already setup for your Harmony Hubs.</p>
<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>
<a href="" ng-click="order('hub')">Hub</a>
<span class="sortorder" ng-show="predicate === 'hub'" 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.device.label}}</td>
<td>{{harmonydevice.device.id}}</td>
<td>{{harmonydevice.hub}}</td>
<td>
<select name="device-ctrlon" id="device-ctrlon" ng-model="devicectrlon">
<optgroup ng-repeat="ctrlon in harmonydevice.device.controlGroup" label="{{ctrlon.name}}">
<option ng-repeat="funcon in ctrlon.function">{{funcon.name}}</option>
</optgroup >
</select>
</td>
<td>
<select name="device-ctrloff" id="device-ctrloff" ng-model="devicectrloff">
<optgroup ng-repeat="ctrloff in harmonydevice.device.controlGroup" label="{{ctrloff.name}}">
<option ng-repeat="funcoff in ctrloff.function">{{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 class="panel-heading">
<h2 class="panel-title">Already Configured Harmony Buttons <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}} aria-hidden="true"></span></a></h2>
</div>
<ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<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('id')">Device Id</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('targetDevice')">Hub</a>
<span class="sortorder" ng-show="predicate === 'targetDevice'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('mapId')">Harmony Device-Button On-Button Off</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="device in bridge.devices | configuredButtons | orderBy:predicate:reverse">
<td>{{device.name}}</td>
<td>{{device.id}}</td>
<td>{{device.targetDevice}}</td>
<td>{{device.mapId}}</td>
<td>
<button class="btn btn-danger" type="submit"
ng-click="deleteDeviceByMapId(device.mapId, device.mapType)">Delete</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 Bridge Device for Harmony Buttons</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 Bridge Device</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,150 @@
<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 ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li role="presentation" class="active"><a href="#/nest">Nest</a></li>
<li 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">Nest Items List</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
<p class="text-muted">For any Nest Item, use the action buttons to generate the device addition information below automatically.
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
The 'Already Configured Nest Items' list below will show what is already setup for your Nest.</p>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<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('type')">Type</a>
<span class="sortorder" ng-show="predicate === 'type'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('location')">Location</a>
<span class="sortorder" ng-show="predicate === 'location'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="nestitem in bridge.nestitems | availableNestItemId | orderBy:predicate:reverse">
<td>{{nestitem.name}}</td>
<td>{{nestitem.type}}</td>
<td>{{nestitem.location}}</td>
<td>
<ul class="list-group">
<li ng-if="nestitem.type ==='Home' " class="list-group-item">
<button class="btn btn-success" type="submit"
ng-click="buildNestHomeUrls(nestitem)">Home/Away</button>
</li>
<li ng-if="nestitem.type ==='Thermostat' " class="list-group-item">
<p>
<button class="btn btn-success" type="submit"
ng-click="buildNestTempUrls(nestitem)">Temp</button>
<button class="btn btn-success" type="submit"
ng-click="buildNestHeatUrls(nestitem)">Heat</button>
<button class="btn btn-success" type="submit"
ng-click="buildNestCoolUrls(nestitem)">Cool</button>
</p>
<p>
<button class="btn btn-success" type="submit"
ng-click="buildNestRangeUrls(nestitem)">Range</button>
<button class="btn btn-success" type="submit"
ng-click="buildNestOffUrls(nestitem)">Off</button>
<button class="btn btn-success" type="submit"
ng-click="buildNestFanUrls(nestitem)">Fan</button>
</p>
</li>
</ul>
</td>
</tr>
</table>
</li>
</ul>
<div class="panel-heading">
<h2 class="panel-title">Already Configured Nest Items <a ng-click="toggleActivities()"><span class={{imgActivitiesUrl}} aria-hidden="true"></a></h2>
</div>
<ul ng-if="activitiesVisible" class="list-group">
<li class="list-group-item">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<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('id')">Id</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('mapId')">mapId</a>
<span class="sortorder" ng-show="predicate === 'mapId'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="device in bridge.devices | unavailableNestItemId | orderBy:predicate:reverse">
<td>{{device.name}}</td>
<td>{{device.id}}</td>
<td>{{device.mapId}}</td>
<td><button class="btn btn-danger" type="submit"
ng-click="deleteDeviceByMapId(device.mapId, 'nest')">Delete</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 Bridge Device for a Nest Item</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 Bridge Device</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

@@ -2,6 +2,9 @@
<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 ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
@@ -11,8 +14,9 @@
</div>
<ul class="list-group">
<li class="list-group-item">
<p class="text-muted">You can select a Vera device and generate
the add device box selections automatically.</p><p>Also, use this select menu for which type of dim
<p class="text-muted">For any Vera Device, use the action buttons to generate the device addition information below automatically.
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
The 'Already Configured Vera Devices' list below will show what is already setup for your Vera.</p><p>Also, use this select menu for which type of dim
control you would like to be generated:
<select name="device-dim-control" id="device-dim-control" ng-model="device_dim_control">
<option value="">none</option>
@@ -43,7 +47,7 @@
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="veradevice in bridge.veradevices | orderBy:predicate:reverse">
<tr ng-repeat="veradevice in bridge.veradevices | availableVeraDeviceId | orderBy:predicate:reverse">
<td>{{veradevice.name}}</td>
<td>{{veradevice.id}}</td>
<td>{{veradevice.category}}</td>
@@ -57,10 +61,50 @@
</table>
</li>
</ul>
<div class="panel-heading">
<h2 class="panel-title">Already Configured Vera Devices <a ng-click="toggleDevices()"><span class={{imgDevicesUrl}} aria-hidden="true"></a></h2>
</div>
<ul ng-if="devicesVisible" class="list-group">
<li class="list-group-item">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<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('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>
</tr>
</thead>
<tr ng-repeat="veradevice in bridge.veradevices | unavailableVeraDeviceId | orderBy:predicate:reverse">
<td>{{veradevice.name}}</td>
<td>{{veradevice.id}}</td>
<td>{{veradevice.category}}</td>
<td>{{veradevice.room}}</td>
<td>
<button class="btn btn-danger" type="submit"
ng-click="deleteDeviceByMapId(veradevice.id, 'veraDevice')">Delete</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 Vera device</h2>
<h2 class="panel-title">Add Bridge Device for a Vera Device</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
@@ -74,7 +118,7 @@
ng-model="device.name" placeholder="Device Name">
</div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Device</button>
Add Bridge Device</button>
</div>
<div class="form-group">
<div class="row">
@@ -85,10 +129,6 @@
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
<div class="clearfix visible-xs"></div>
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
ng-click="testUrl(device, 'on')">Test</button>
</div>
</div>
<div class="form-group">
<div class="row">
@@ -99,10 +139,6 @@
<textarea rows="3" class="form-control" id="device-off-url"
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
</div>
<div class="clearfix visible-xs"></div>
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
ng-click="testUrl(device, 'off')">Test</button>
</div>
</div>
</form>
</li>

View File

@@ -2,6 +2,9 @@
<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 ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
@@ -11,8 +14,9 @@
</div>
<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>
<p class="text-muted">For any Vera Scene, use the action buttons to generate the device addition information below automatically.
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
The 'Already Configured Vera Scenes' list below will show what is already setup for your Vera.</p>
<table class="table table-bordered table-striped table-hover">
<thead>
@@ -32,7 +36,7 @@
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="verascene in bridge.verascenes | orderBy:predicate:reverse">
<tr ng-repeat="verascene in bridge.verascenes | availableVeraSceneId | orderBy:predicate:reverse">
<td>{{verascene.name}}</td>
<td>{{verascene.id}}</td>
<td>{{verascene.room}}</td>
@@ -45,10 +49,45 @@
</table>
</li>
</ul>
<div class="panel-heading">
<h2 class="panel-title">Already Configured Vera Scenes <a ng-click="toggleScenes()"><span class={{imgScenesUrl}} aria-hidden="true"></a></h2>
</div>
<ul ng-if="scenesVisible" class="list-group">
<li class="list-group-item">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<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('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>
</tr>
</thead>
<tr ng-repeat="verascene in bridge.verascenes | unavailableVeraSceneId | orderBy:predicate:reverse">
<td>{{verascene.name}}</td>
<td>{{verascene.id}}</td>
<td>{{verascene.room}}</td>
<td>
<button class="btn btn-danger" type="submit"
ng-click="deleteDeviceByMapId(verascene.id, 'veraScene')">Delete</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 Vera scene</h2>
<h2 class="panel-title">Add a Bridge Device for a Vera scene</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
@@ -62,7 +101,7 @@
ng-model="device.name" placeholder="Device Name">
</div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Scene</button>
Add Bridge Device</button>
</div>
<div class="form-group">
<div class="row">
@@ -73,10 +112,6 @@
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
<div class="clearfix visible-xs"></div>
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
ng-click="testUrl(device, 'on')">Test</button>
</div>
</div>
<div class="form-group">
<div class="row">
@@ -87,10 +122,6 @@
<textarea rows="3" class="form-control" id="device-off-url"
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
</div>
<div class="clearfix visible-xs"></div>
<button class="col-xs-4 col-sm-2 btn btn-success" type="button"
ng-click="testUrl(device, 'off')">Test</button>
</div>
</div>
</form>
</li>

View File

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