Compare commits

..

11 Commits

Author SHA1 Message Date
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
23 changed files with 770 additions and 92 deletions

File diff suppressed because one or more lines are too long

14
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>com.bwssystems.HABridge</groupId> <groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId> <artifactId>ha-bridge</artifactId>
<version>1.2.2</version> <version>1.3.0</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>HA Bridge</name> <name>HA Bridge</name>
@@ -30,6 +30,11 @@
<artifactId>harmony-java-client</artifactId> <artifactId>harmony-java-client</artifactId>
<version>1.0.8</version> <version>1.0.8</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.bwssytems</groupId>
<artifactId>nest-controller</artifactId>
<version>1.0.1</version>
</dependency>
<dependency> <dependency>
<groupId>com.sparkjava</groupId> <groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId> <artifactId>spark-core</artifactId>
@@ -38,8 +43,13 @@
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
<version>4.3.6</version> <version>4.5.1</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.4</version>
</dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId> <artifactId>slf4j-simple</artifactId>

View File

@@ -15,6 +15,9 @@ public class BridgeSettings {
private boolean upnpstrict; private boolean upnpstrict;
private boolean traceupnp; private boolean traceupnp;
private boolean devmode; private boolean devmode;
private String nestuser;
private String nestpwd;
private boolean nestconfigured;
public String getUpnpConfigAddress() { public String getUpnpConfigAddress() {
return upnpconfigaddress; return upnpconfigaddress;
@@ -88,14 +91,32 @@ public class BridgeSettings {
public void setDevMode(boolean devmode) { public void setDevMode(boolean devmode) {
this.devmode = 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() { public Boolean isValidVera() {
if(this.veraaddress.contains(Configuration.DEFAULT_VERA_ADDRESS)) if(this.veraaddress.contains(Configuration.DEFAULT_ADDRESS))
return false; return false;
return true; return true;
} }
public Boolean isValidHarmony() { public Boolean isValidHarmony() {
List<NamedIP> devicesList = this.harmonyaddress.getDevices(); List<NamedIP> devicesList = this.harmonyaddress.getDevices();
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_HARMONY_ADDRESS)) if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false; return false;
if(this.harmonypwd == null || this.harmonypwd == "") if(this.harmonypwd == null || this.harmonypwd == "")
return false; return false;
@@ -103,4 +124,11 @@ public class BridgeSettings {
return false; return false;
return true; 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

@@ -4,10 +4,11 @@ public class Configuration {
public final static String DEVICE_DB_DIRECTORY = "data/device.db"; public final static String DEVICE_DB_DIRECTORY = "data/device.db";
public final static String UPNP_RESPONSE_PORT = "50000"; public final static String UPNP_RESPONSE_PORT = "50000";
public final static String UPNP_RESPONSE_DEVICES = "30"; public final static String UPNP_RESPONSE_DEVICES = "30";
public final static String DEFAULT_VERA_ADDRESS = "1.1.1.1"; public final static String DEFAULT_ADDRESS = "1.1.1.1";
public final static String DEFAULT_HARMONY_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_HARMONY_ADDRESS_LIST = "{devices:[{name:default,ip:1.1.1.1}]}";
public final static String DEFAULT_HARMONY_USER = ""; public final static String DEFAULT_USER = "";
public final static String DEFAULT_HARMONY_PWD = ""; public final static String DEFAULT_PWD = "";
public final static String DFAULT_WEB_PORT = "8080"; public final static String DFAULT_WEB_PORT = "8080";
} }

View File

@@ -3,8 +3,11 @@ package com.bwssystems.HABridge;
import static spark.Spark.*; import static spark.Spark.*;
import java.net.InetAddress; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -12,6 +15,7 @@ import com.bwssystems.HABridge.devicemanagmeent.*;
import com.bwssystems.HABridge.hue.HueMulator; import com.bwssystems.HABridge.hue.HueMulator;
import com.bwssystems.HABridge.upnp.UpnpListener; import com.bwssystems.HABridge.upnp.UpnpListener;
import com.bwssystems.HABridge.upnp.UpnpSettingsResource; import com.bwssystems.HABridge.upnp.UpnpSettingsResource;
import com.bwssystems.NestBridge.NestHome;
import com.bwssystems.harmony.HarmonyHome; import com.bwssystems.harmony.HarmonyHome;
import com.google.gson.Gson; import com.google.gson.Gson;
@@ -36,51 +40,77 @@ public class HABridge {
Logger log = LoggerFactory.getLogger(HABridge.class); Logger log = LoggerFactory.getLogger(HABridge.class);
DeviceResource theResources; DeviceResource theResources;
HarmonyHome harmonyHome; HarmonyHome harmonyHome;
NestHome nestHome;
HueMulator theHueMulator; HueMulator theHueMulator;
UpnpSettingsResource theSettingResponder; UpnpSettingsResource theSettingResponder;
UpnpListener theUpnpListener; UpnpListener theUpnpListener;
InetAddress address; InetAddress address = null;
String addressString; String addressString = null;
BridgeSettings bridgeSettings; BridgeSettings bridgeSettings;
Version theVersion; Version theVersion;
theVersion = new Version(); theVersion = new Version();
log.info("HA Bridge (v" + theVersion.getVersion() + ") starting setup...."); log.info("HA Bridge (v" + theVersion.getVersion() + ") 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;
}
bridgeSettings = new BridgeSettings(); bridgeSettings = new BridgeSettings();
bridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DFAULT_WEB_PORT)); bridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DFAULT_WEB_PORT));
bridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address", addressString)); 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.setUpnpDeviceDb(System.getProperty("upnp.device.db", Configuration.DEVICE_DB_DIRECTORY));
bridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT)); 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; IpList theHarmonyList;
try { try {
theHarmonyList = new Gson().fromJson(System.getProperty("harmony.address", Configuration.DEFAULT_HARMONY_ADDRESS_LIST), IpList.class); theHarmonyList = new Gson().fromJson(System.getProperty("harmony.address", Configuration.DEFAULT_HARMONY_ADDRESS_LIST), IpList.class);
} catch (Exception e) { } catch (Exception e) {
try { try {
theHarmonyList = new Gson().fromJson("{devices:[{name:default,ip:" + System.getProperty("harmony.address", Configuration.DEFAULT_HARMONY_ADDRESS) + "}]}", IpList.class); theHarmonyList = new Gson().fromJson("{devices:[{name:default,ip:" + System.getProperty("harmony.address", Configuration.DEFAULT_ADDRESS) + "}]}", IpList.class);
} catch (Exception et) { } catch (Exception et) {
log.error("Cannot parse harmony.address, Exiting with message: " + e.getMessage(), e); log.error("Cannot parse harmony.address, Exiting with message: " + e.getMessage(), e);
return; return;
} }
} }
bridgeSettings.setHarmonyAddress(theHarmonyList); bridgeSettings.setHarmonyAddress(theHarmonyList);
bridgeSettings.setHarmonyUser(System.getProperty("harmony.user", Configuration.DEFAULT_HARMONY_USER)); bridgeSettings.setHarmonyUser(System.getProperty("harmony.user", Configuration.DEFAULT_USER));
bridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd", Configuration.DEFAULT_HARMONY_PWD)); bridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd", Configuration.DEFAULT_PWD));
bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true"))); bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
bridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false"))); bridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
bridgeSettings.setDevMode(Boolean.parseBoolean(System.getProperty("dev.mode", "false"))); bridgeSettings.setDevMode(Boolean.parseBoolean(System.getProperty("dev.mode", "false")));
bridgeSettings.setUpnpResponseDevices(Integer.parseInt(System.getProperty("upnp.response.devices", Configuration.UPNP_RESPONSE_DEVICES))); 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 // sparkjava config directive to set ip address for the web server to listen on
// ipAddress("0.0.0.0"); // not used // ipAddress("0.0.0.0"); // not used
@@ -90,10 +120,12 @@ public class HABridge {
staticFileLocation("/public"); staticFileLocation("/public");
//setup the harmony connection if available //setup the harmony connection if available
harmonyHome = new HarmonyHome(bridgeSettings); harmonyHome = new HarmonyHome(bridgeSettings);
//setup the nest connection if available
nestHome = new NestHome(bridgeSettings);
// setup the class to handle the resource setup rest api // setup the class to handle the resource setup rest api
theResources = new DeviceResource(bridgeSettings, theVersion, harmonyHome); theResources = new DeviceResource(bridgeSettings, theVersion, harmonyHome, nestHome);
// setup the class to handle the hue emulator rest api // setup the class to handle the hue emulator rest api
theHueMulator = new HueMulator(bridgeSettings, theResources.getDeviceRepository(), harmonyHome); theHueMulator = new HueMulator(bridgeSettings, theResources.getDeviceRepository(), harmonyHome, nestHome);
theHueMulator.setupServer(); theHueMulator.setupServer();
// setup the class to handle the upnp response rest api // setup the class to handle the upnp response rest api
theSettingResponder = new UpnpSettingsResource(bridgeSettings); theSettingResponder = new UpnpSettingsResource(bridgeSettings);

View File

@@ -12,11 +12,17 @@ public class HueApiResponse {
private Map<String, DeviceResponse> lights; private Map<String, DeviceResponse> lights;
private Map<String, String> scenes; private Map<String, String> scenes;
private Map<String, String> groups; private Map<String, String> groups;
private Map<String, String> schedules;
private Map<String, String> sensors;
private Map<String, String> rules;
private HueConfig config; private HueConfig config;
public HueApiResponse(String name, String ipaddress, String devicetype, String userid) { public HueApiResponse(String name, String ipaddress, String devicetype, String userid) {
super(); super();
this.setConfig(HueConfig.createConfig(name, ipaddress, devicetype, 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.setGroups(new HashMap<>());
this.setScenes(new HashMap<>()); this.setScenes(new HashMap<>());
} }
@@ -45,6 +51,30 @@ public class HueApiResponse {
this.groups = groups; 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() { public HueConfig getConfig() {
return config; return config;
} }

View File

@@ -84,6 +84,10 @@ public class HueConfig
sb.append("00:00:88:00:bb:ee"); sb.append("00:00:88:00:bb:ee");
} catch (Exception e){
sb.append("00:00:88:00:bb:ee");
} }
return sb.toString(); return sb.toString();

View File

@@ -20,6 +20,7 @@ import com.bwssystems.HABridge.JsonTransformer;
import com.bwssystems.HABridge.Version; import com.bwssystems.HABridge.Version;
import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.dao.DeviceRepository; import com.bwssystems.HABridge.dao.DeviceRepository;
import com.bwssystems.NestBridge.NestHome;
import com.bwssystems.harmony.HarmonyHome; import com.bwssystems.harmony.HarmonyHome;
import com.bwssystems.luupRequests.Sdata; import com.bwssystems.luupRequests.Sdata;
import com.bwssystems.vera.VeraInfo; import com.bwssystems.vera.VeraInfo;
@@ -36,15 +37,21 @@ public class DeviceResource {
private VeraInfo veraInfo; private VeraInfo veraInfo;
private Version version; private Version version;
private HarmonyHome myHarmonyHome; private HarmonyHome myHarmonyHome;
private NestHome nestHome;
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post")); private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
public DeviceResource(BridgeSettings theSettings, Version theVersion, HarmonyHome theHarmonyHome) { public DeviceResource(BridgeSettings theSettings, Version theVersion, HarmonyHome theHarmonyHome, NestHome aNestHome) {
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb()); this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
this.veraInfo = new VeraInfo(theSettings.getVeraAddress(), theSettings.isValidVera()); this.veraInfo = new VeraInfo(theSettings.getVeraAddress(), theSettings.isValidVera());
if(theSettings.isValidHarmony()) if(theSettings.isValidHarmony())
this.myHarmonyHome = theHarmonyHome; this.myHarmonyHome = theHarmonyHome;
else else
this.myHarmonyHome = null; this.myHarmonyHome = null;
if(theSettings.isValidNest())
this.nestHome = aNestHome;
else
this.nestHome = null;
this.version = theVersion; this.version = theVersion;
setupEndpoints(); setupEndpoints();
} }
@@ -218,5 +225,14 @@ public class DeviceResource {
return myHarmonyHome.getDevices(); return myHarmonyHome.getDevices();
}, new JsonTransformer()); }, 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

@@ -7,10 +7,13 @@ import com.bwssystems.HABridge.api.hue.DeviceResponse;
import com.bwssystems.HABridge.api.hue.DeviceState; import com.bwssystems.HABridge.api.hue.DeviceState;
import com.bwssystems.HABridge.api.hue.HueApiResponse; import com.bwssystems.HABridge.api.hue.HueApiResponse;
import com.bwssystems.HABridge.dao.*; import com.bwssystems.HABridge.dao.*;
import com.bwssystems.NestBridge.NestInstruction;
import com.bwssystems.NestBridge.NestHome;
import com.bwssystems.harmony.ButtonPress; import com.bwssystems.harmony.ButtonPress;
import com.bwssystems.harmony.HarmonyHandler; import com.bwssystems.harmony.HarmonyHandler;
import com.bwssystems.harmony.HarmonyHome; import com.bwssystems.harmony.HarmonyHome;
import com.bwssystems.harmony.RunActivity; import com.bwssystems.harmony.RunActivity;
import com.bwssystems.nest.controller.Nest;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.google.gson.Gson;
@@ -63,13 +66,14 @@ public class HueMulator {
private DeviceRepository repository; private DeviceRepository repository;
private HarmonyHome myHarmonyHome; private HarmonyHome myHarmonyHome;
private Nest theNest;
private HttpClient httpClient; private HttpClient httpClient;
private ObjectMapper mapper; private ObjectMapper mapper;
private BridgeSettings bridgeSettings; private BridgeSettings bridgeSettings;
private byte[] sendData; private byte[] sendData;
public HueMulator(BridgeSettings theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome){ public HueMulator(BridgeSettings theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome, NestHome aNestHome){
httpClient = HttpClients.createDefault(); httpClient = HttpClients.createDefault();
mapper = new ObjectMapper(); //armzilla: work around Echo incorrect content type and breaking mapping. Map manually mapper = new ObjectMapper(); //armzilla: work around Echo incorrect content type and breaking mapping. Map manually
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@@ -78,6 +82,10 @@ public class HueMulator {
this.myHarmonyHome = theHarmonyHome; this.myHarmonyHome = theHarmonyHome;
else else
this.myHarmonyHome = null; this.myHarmonyHome = null;
if(theBridgeSettings.isValidNest())
this.theNest = aNestHome.getTheNest();
else
this.theNest = null;
bridgeSettings = theBridgeSettings; bridgeSettings = theBridgeSettings;
} }
@@ -117,7 +125,7 @@ public class HueMulator {
String aDeviceType = null; String aDeviceType = null;
if(bridgeSettings.isTraceupnp()) if(bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue api user create requested: " + request.body() + " from " + request.ip()); 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()); log.debug("hue api user create requested: " + request.body() + " from " + request.ip());
if(request.body() != null && !request.body().isEmpty()) { if(request.body() != null && !request.body().isEmpty()) {
@@ -155,7 +163,8 @@ public class HueMulator {
String newUser = null; String newUser = null;
String aDeviceType = 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()) { if(request.body() != null && !request.body().isEmpty()) {
aNewUser = new Gson().fromJson(request.body(), UserCreateRequest.class); aNewUser = new Gson().fromJson(request.body(), UserCreateRequest.class);
@@ -177,22 +186,26 @@ public class HueMulator {
// http://ip_address:port/api/config returns json objects for the config when no user is given // 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) -> { get(HUE_CONTEXT + "/config", "application/json", (request, response) -> {
String userId = request.params(":userid"); if(bridgeSettings.isTraceupnp())
log.debug("hue api config requested: " + userId + " from " + request.ip()); log.info("Traceupnp: hue api/config config requested: <no_user> from " + request.ip());
HueApiResponse apiResponse = new HueApiResponse("Philips hue", request.ip(), "My App", userId); 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.type("application/json; charset=utf-8");
response.status(HttpStatus.SC_OK); response.status(HttpStatus.SC_OK);
String responseString = null; // String responseString = null;
responseString = "[{\"swversion\":\"" + apiResponse.getConfig().getSwversion() + "\",\"apiversion\":\"" + apiResponse.getConfig().getApiversion() + "\",\"name\":\"" + apiResponse.getConfig().getName() + "\",\"mac\":\"" + apiResponse.getConfig().getMac() + "\"}]"; // responseString = "[{\"swversion\":\"" + apiResponse.getConfig().getSwversion() + "\",\"apiversion\":\"" + apiResponse.getConfig().getApiversion() + "\",\"name\":\"" + apiResponse.getConfig().getName() + "\",\"mac\":\"" + apiResponse.getConfig().getMac() + "\"}]";
return responseString; // return responseString;
}); return apiResponse.getConfig();
}, new JsonTransformer());
// http://ip_address:port/api/{userId}/config returns json objects for the config // http://ip_address:port/api/{userId}/config returns json objects for the config
get(HUE_CONTEXT + "/:userid/config", "application/json", (request, response) -> { get(HUE_CONTEXT + "/:userid/config", "application/json", (request, response) -> {
String userId = request.params(":userid"); 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()); log.debug("hue api config requested: " + userId + " from " + request.ip());
HueApiResponse apiResponse = new HueApiResponse("Philips hue", request.ip(), "My App", userId); HueApiResponse apiResponse = new HueApiResponse("Philips hue", bridgeSettings.getUpnpConfigAddress(), "My App", userId);
response.type("application/json; charset=utf-8"); response.type("application/json; charset=utf-8");
response.status(HttpStatus.SC_OK); response.status(HttpStatus.SC_OK);
@@ -216,7 +229,7 @@ public class HueMulator {
deviceList.put(descriptor.getId(), deviceResponse); 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); apiResponse.setLights(deviceList);
response.type("application/json; charset=utf-8"); response.type("application/json; charset=utf-8");
@@ -332,6 +345,39 @@ public class HueMulator {
else else
myHarmony.pressButton(aDeviceButton); myHarmony.pressButton(aDeviceButton);
} }
else if(device.getDeviceType().toLowerCase().contains("home") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("nestHomeAway")))
{
log.debug("executing 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 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(replaceIntensityValue(thermoSetting.getTemp(), state.getBri()));
theNest.getThermostat(thermoSetting.getName()).setTargetTemperature(Float.parseFloat(thermoSetting.getTemp()));
}
}
else if (!thermoSetting.getControl().equalsIgnoreCase("status")) {
theNest.getThermostat(thermoSetting.getName()).setTargetType(thermoSetting.getControl());
}
}
}
else if(url.startsWith("udp://")) else if(url.startsWith("udp://"))
{ {
try { try {
@@ -407,7 +453,8 @@ public class HueMulator {
request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE, endResult.toString()); request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE, endResult.toString());
} catch (Exception e) { } catch (Exception e) {
log.warn("Could not execute Math: " + mathDescriptor, e); log.warn("Could not execute Math: " + mathDescriptor, e);
} } }
}
return request; return request;
} }

View File

@@ -96,30 +96,34 @@ public class UpnpListener {
//Only respond to discover request for strict upnp form //Only respond to discover request for strict upnp form
String packetString = new String(packet.getData()); String packetString = new String(packet.getData());
if(packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1") && packetString.contains("\"ssdp:discover\"")){ if(packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1") && packetString.contains("\"ssdp:discover\"")){
if(traceupnp) { log.debug("isSSDPDiscovery Found message to be an M-SEARCH message.");
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString); log.debug("isSSDPDiscovery Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
log.info("Traceupnp: isSSDPDiscovery found message to be an M-SEARCH message.");
}
else {
log.debug("Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
log.debug("Found message to be an M-SEARCH message.");
}
if(strict && (packetString.contains("ST: urn:schemas-upnp-org:device:basic:1") || packetString.contains("ST: upnp:rootdevice") || packetString.contains("ST: ssdp:all"))) if(strict && (packetString.contains("ST: urn:schemas-upnp-org:device:basic:1") || packetString.contains("ST: upnp:rootdevice") || packetString.contains("ST: ssdp:all")))
{ {
if(traceupnp) 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: 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; return true;
} }
else if (!strict) else if (!strict)
{ {
if(traceupnp) 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: 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; return true;
} }
} }
if(traceupnp) else {
log.info("Traceupnp: isSSDPDiscovery found message to not be valid - strict: " + strict); // 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; return false;
} }

View File

@@ -53,6 +53,7 @@ public class UpnpSettingsResource {
this.theSettings.setUpnpResponsePort(theBridgeSettings.getUpnpResponsePort()); this.theSettings.setUpnpResponsePort(theBridgeSettings.getUpnpResponsePort());
this.theSettings.setUpnpStrict(theBridgeSettings.isUpnpStrict()); this.theSettings.setUpnpStrict(theBridgeSettings.isUpnpStrict());
this.theSettings.setVeraAddress(theBridgeSettings.getVeraAddress()); this.theSettings.setVeraAddress(theBridgeSettings.getVeraAddress());
this.theSettings.setNestConfigured(theBridgeSettings.isValidNest());
} }
public void setupServer() { public void setupServer() {

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

@@ -24,6 +24,9 @@ app.config(function ($routeProvider) {
}).when('/harmonyactivities', { }).when('/harmonyactivities', {
templateUrl: 'views/harmonyactivity.html', templateUrl: 'views/harmonyactivity.html',
controller: 'AddingController' controller: 'AddingController'
}).when('/nest', {
templateUrl: 'views/nestactions.html',
controller: 'AddingController'
}).otherwise({ }).otherwise({
templateUrl: 'views/configuration.html', templateUrl: 'views/configuration.html',
controller: 'ViewingController' controller: 'ViewingController'
@@ -34,6 +37,7 @@ app.run( function (bridgeService) {
bridgeService.loadBridgeSettings(); bridgeService.loadBridgeSettings();
bridgeService.updateShowVera(); bridgeService.updateShowVera();
bridgeService.updateShowHarmony(); bridgeService.updateShowHarmony();
bridgeService.updateShowNest();
bridgeService.getHABridgeVersion(); bridgeService.getHABridgeVersion();
}); });
@@ -49,6 +53,7 @@ app.factory('BridgeSettings', function() {
BridgeSettings.upnpstrict = ""; BridgeSettings.upnpstrict = "";
BridgeSettings.traceupnp = ""; BridgeSettings.traceupnp = "";
BridgeSettings.devmode = ""; BridgeSettings.devmode = "";
BridgeSettings.nestconfigured = "";
BridgeSettings.setupnpconfigaddress = function(aconfigaddress){ BridgeSettings.setupnpconfigaddress = function(aconfigaddress){
BridgeSettings.upnpconfigaddress = aconfigaddress; BridgeSettings.upnpconfigaddress = aconfigaddress;
@@ -82,13 +87,17 @@ app.factory('BridgeSettings', function() {
BridgeSettings.devmode = adevmode; BridgeSettings.devmode = adevmode;
}; };
BridgeSettings.setnestconfigured = function(anestconfigured){
BridgeSettings.nestconfigured = anestconfigured;
};
return BridgeSettings; return BridgeSettings;
}); });
app.service('bridgeService', function ($http, $window, BridgeSettings) { app.service('bridgeService', function ($http, $window, BridgeSettings) {
var self = this; var self = this;
self.BridgeSettings = BridgeSettings; self.BridgeSettings = BridgeSettings;
this.state = {base: window.location.origin + "/api/devices", upnpbase: window.location.origin + "/upnp/settings", huebase: window.location.origin + "/api", devices: [], device: [], error: "", showVera: false, showHarmony: false, habridgeversion: ""}; this.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.viewDevices = function () {
this.state.error = ""; this.state.error = "";
@@ -136,6 +145,14 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
return; return;
} }
this.updateShowNest = function () {
if(self.BridgeSettings.nestconfigured == true)
this.state.showNest = true;
else
this.state.showNest = false;
return;
}
this.updateShowHarmony = function () { this.updateShowHarmony = function () {
if(self.BridgeSettings.harmonyaddress.devices) { 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) if(this.aContainsB(self.BridgeSettings.harmonyaddress.devices[0].ip, "1.1.1.1") || self.BridgeSettings.harmonyaddress == "" || self.BridgeSettings.harmonyaddress == null)
@@ -162,6 +179,7 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
self.BridgeSettings.settraceupnp(response.data.traceupnp); self.BridgeSettings.settraceupnp(response.data.traceupnp);
self.BridgeSettings.setupnpstrict(response.data.upnpstrict); self.BridgeSettings.setupnpstrict(response.data.upnpstrict);
self.BridgeSettings.setdevmode(response.data.devmode); self.BridgeSettings.setdevmode(response.data.devmode);
self.BridgeSettings.setnestconfigured(response.data.nestconfigured);
}, },
function (error) { function (error) {
if (error.data) { if (error.data) {
@@ -174,6 +192,25 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
); );
}; };
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.viewVeraDevices = function () {
this.state.error = ""; this.state.error = "";
if(!this.state.showVera) if(!this.state.showVera)
@@ -255,6 +292,14 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
return false; 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.addDevice = function (device) {
this.state.error = ""; this.state.error = "";
if(device.httpVerb != null && device.httpVerb != "") if(device.httpVerb != null && device.httpVerb != "")
@@ -333,7 +378,7 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
this.deleteDeviceByMapId = function (id, type) { this.deleteDeviceByMapId = function (id, type) {
for(var i = 0; i < this.state.devices.length; i++) { for(var i = 0; i < this.state.devices.length; i++) {
if(this.state.devices[i].mapId == id && this.state.devices[i].mapType == type) if(this.state.devices[i].mapId == id && this.aContainsB(this.state.devices[i].mapType, type))
return self.deleteDevice(this.state.devices[i].id); return self.deleteDevice(this.state.devices[i].id);
} }
}; };
@@ -375,6 +420,7 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
$scope.bridge = bridgeService.state; $scope.bridge = bridgeService.state;
bridgeService.updateShowVera(); bridgeService.updateShowVera();
bridgeService.updateShowHarmony(); bridgeService.updateShowHarmony();
bridgeService.updateShowNest();
$scope.visible = false; $scope.visible = false;
$scope.imgUrl = "glyphicon glyphicon-plus"; $scope.imgUrl = "glyphicon glyphicon-plus";
$scope.predicate = ''; $scope.predicate = '';
@@ -419,9 +465,11 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
bridgeService.viewVeraScenes(); bridgeService.viewVeraScenes();
bridgeService.viewHarmonyActivities(); bridgeService.viewHarmonyActivities();
bridgeService.viewHarmonyDevices(); bridgeService.viewHarmonyDevices();
bridgeService.viewNestItems();
$scope.bridge = bridgeService.state; $scope.bridge = bridgeService.state;
bridgeService.updateShowVera(); bridgeService.updateShowVera();
bridgeService.updateShowHarmony(); bridgeService.updateShowHarmony();
bridgeService.updateShowNest();
$scope.device = bridgeService.state.device; $scope.device = bridgeService.state.device;
$scope.activitiesVisible = false; $scope.activitiesVisible = false;
$scope.imgButtonsUrl = "glyphicon glyphicon-plus"; $scope.imgButtonsUrl = "glyphicon glyphicon-plus";
@@ -535,6 +583,69 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
$scope.device.offUrl = "{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + offbutton + "\"}"; $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) { $scope.testUrl = function (device, type) {
bridgeService.testUrl(device, type); bridgeService.testUrl(device, type);
}; };
@@ -683,6 +794,34 @@ return function(input) {
} }
}); });
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() { app.filter('configuredButtons', function() {
return function(input) { return function(input) {
var out = []; var out = [];

View File

@@ -4,6 +4,7 @@
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</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="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</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"><a href="#/editor">Manual Add</a></li>
</ul> </ul>
@@ -126,6 +127,10 @@
<td>dev.mode</td> <td>dev.mode</td>
<td>{{BridgeSettings.devmode}}</td> <td>{{BridgeSettings.devmode}}</td>
</tr> </tr>
<tr>
<td>nest.configured</td>
<td>{{BridgeSettings.nestconfigured}}</td>
</tr>
</table> </table>
</div> </div>
</div> </div>

View File

@@ -4,6 +4,7 @@
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</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="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</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"><a href="#/editor">Manual Add</a></li>
<li role="presentation" class="active"><a href="#/editdevice">Edit Device</a></li> <li role="presentation" class="active"><a href="#/editdevice">Edit Device</a></li>
</ul> </ul>
@@ -12,6 +13,8 @@
<div class="panel-heading"> <div class="panel-heading">
<h2 class="panel-title">Edit a device</h2> <h2 class="panel-title">Edit a device</h2>
</div> </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"> <ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()"> <form class="form-horizontal" ng-submit="addDevice()">
@@ -24,7 +27,7 @@
ng-model="device.name" placeholder="Device Name"> ng-model="device.name" placeholder="Device Name">
</div> </div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary"> <button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Update Device</button> Update Bridge Device</button>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-target">Target <label class="col-xs-12 col-sm-2 control-label" for="device-target">Target
@@ -47,6 +50,8 @@
<option value="veraScene">Vera Scene</option> <option value="veraScene">Vera Scene</option>
<option value="harmonyActivity">Harmony Activity</option> <option value="harmonyActivity">Harmony Activity</option>
<option value="harmonyButton">Harmony Button</option> <option value="harmonyButton">Harmony Button</option>
<option value="nestHomeAway">Nest Home Status</option>
<option value="nestThermoSet">Nest Thermostat</option>
</select> </select>
</div> </div>
</div> </div>

View File

@@ -4,6 +4,7 @@
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</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="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</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> <li role="presentation" class="active"><a href="#/editor">Manual Add</a></li>
</ul> </ul>
@@ -73,6 +74,8 @@
<div class="panel-heading"> <div class="panel-heading">
<h2 class="panel-title">Add a new device</h2> <h2 class="panel-title">Add a new device</h2>
</div> </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"> <ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()"> <form class="form-horizontal" ng-submit="addDevice()">
@@ -86,7 +89,7 @@
ng-model="device.name" placeholder="Device Name"> ng-model="device.name" placeholder="Device Name">
</div> </div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary"> <button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Device</button> Add Bridge Device</button>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@@ -4,6 +4,7 @@
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</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" class="active"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li role="presentation"><a href="#/harmonydevices">Harmony Devices</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> <li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul> </ul>
@@ -13,8 +14,9 @@
</div> </div>
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<p class="text-muted">You can select a Harmony Activity and generate <p class="text-muted">For any Harmony Activity, use the action buttons to generate the device addition information below automatically.
the add activity box selections automatically.</p> 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"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
@@ -83,7 +85,7 @@
</div> </div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error"> <div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading"> <div class="panel-heading">
<h2 class="panel-title">Add a Harmony Activity</h2> <h2 class="panel-title">Add a Bridge Device for a Harmony Activity</h2>
</div> </div>
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
@@ -97,7 +99,7 @@
ng-model="device.name" placeholder="Device Name"> ng-model="device.name" placeholder="Device Name">
</div> </div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary"> <button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Activity</button> Add Bridge Device</button>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="row"> <div class="row">

View File

@@ -4,6 +4,7 @@
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</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"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li role="presentation" class="active"><a href="#/harmonydevices">Harmony Devices</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> <li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul> </ul>
@@ -13,8 +14,9 @@
</div> </div>
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<p class="text-muted">You can select a Harmony Device and Button and generate <p class="text-muted">For any Harmony Device and Buttons, use the action buttons to generate the device addition information below automatically.
the add button box selections automatically.</p> 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"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
@@ -106,7 +108,7 @@
</div> </div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error"> <div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading"> <div class="panel-heading">
<h2 class="panel-title">Add a Harmony Button</h2> <h2 class="panel-title">Add a Bridge Device for Harmony Buttons</h2>
</div> </div>
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
@@ -120,7 +122,7 @@
ng-model="device.name" placeholder="Device Name"> ng-model="device.name" placeholder="Device Name">
</div> </div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary"> <button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Button</button> Add Bridge Device</button>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="row"> <div class="row">

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

@@ -4,6 +4,7 @@
<li role="presentation"><a href="#/verascenes">Vera Scenes</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="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</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"><a href="#/editor">Manual Add</a></li>
</ul> </ul>
@@ -13,8 +14,9 @@
</div> </div>
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<p class="text-muted">You can select a Vera device and generate <p class="text-muted">For any Vera Device, use the action buttons to generate the device addition information below automatically.
the add device box selections automatically.</p><p>Also, use this select menu for which type of dim 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: control you would like to be generated:
<select name="device-dim-control" id="device-dim-control" ng-model="device_dim_control"> <select name="device-dim-control" id="device-dim-control" ng-model="device_dim_control">
<option value="">none</option> <option value="">none</option>
@@ -102,7 +104,7 @@
</div> </div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error"> <div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading"> <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> </div>
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
@@ -116,7 +118,7 @@
ng-model="device.name" placeholder="Device Name"> ng-model="device.name" placeholder="Device Name">
</div> </div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary"> <button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Device</button> Add Bridge Device</button>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="row"> <div class="row">

View File

@@ -4,6 +4,7 @@
<li role="presentation" class="active"><a href="#/verascenes">Vera Scenes</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="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</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"><a href="#/editor">Manual Add</a></li>
</ul> </ul>
@@ -13,8 +14,9 @@
</div> </div>
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<p class="text-muted">You can select a Vera scene and generate <p class="text-muted">For any Vera Scene, use the action buttons to generate the device addition information below automatically.
the add scene box selections automatically.</p> 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"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
@@ -85,7 +87,7 @@
</div> </div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error"> <div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading"> <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> </div>
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
@@ -99,7 +101,7 @@
ng-model="device.name" placeholder="Device Name"> ng-model="device.name" placeholder="Device Name">
</div> </div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary"> <button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Scene</button> Add Bridge Device</button>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="row"> <div class="row">