mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-18 16:17:30 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37d346f558 | ||
|
|
ac59398aa0 | ||
|
|
87073435fc | ||
|
|
8687f3482a | ||
|
|
32a5f26ddd | ||
|
|
c28f07d628 | ||
|
|
d3cc961dfb | ||
|
|
1b3d826f28 | ||
|
|
c8f4d89a45 | ||
|
|
2b335d6b9b | ||
|
|
b27bb5eef8 |
14
pom.xml
14
pom.xml
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>com.bwssystems.HABridge</groupId>
|
<groupId>com.bwssystems.HABridge</groupId>
|
||||||
<artifactId>ha-bridge</artifactId>
|
<artifactId>ha-bridge</artifactId>
|
||||||
<version>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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
101
src/main/java/com/bwssystems/NestBridge/NestHome.java
Normal file
101
src/main/java/com/bwssystems/NestBridge/NestHome.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
33
src/main/java/com/bwssystems/NestBridge/NestInstruction.java
Normal file
33
src/main/java/com/bwssystems/NestBridge/NestInstruction.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/main/java/com/bwssystems/NestBridge/NestItem.java
Normal file
32
src/main/java/com/bwssystems/NestBridge/NestItem.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 = [];
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
150
src/main/resources/public/views/nestactions.html
Normal file
150
src/main/resources/public/views/nestactions.html
Normal 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>
|
||||||
@@ -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">
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
Reference in New Issue
Block a user