Compare commits

..

15 Commits

Author SHA1 Message Date
Admin
f9f5a3a878 Updated code in the html and javascript to select the coorect name for
the button press command. Fixed the calculation of celsius conversion
for setting nest temperatures.

Fixes #33
Fixes #34
2016-01-29 13:49:34 -06:00
Admin
2565183ee9 Finished implementation for multiple vera support. Tweaks to the UI.
Fixes #19
Fixes #21
Fixes #23
Fixes #26
Fixes #28
Fixes #30
Fixes #31
2016-01-28 16:39:20 -06:00
Admin
a6bb1ae3aa Fixed the harmony button to be a label and not the name. Implementation
of multi batch add completed.
2016-01-27 16:54:23 -06:00
Admin
4bc91be88b Added backup and restore for device db functionality. Testing multi
button press for harmony.
2016-01-26 16:47:16 -06:00
Admin
315fd31270 implemented multi button build for devices. more testing needed. fixed
tab display on first view.
2016-01-25 16:35:22 -06:00
Admin
09787fe08d Fixed undefined name error in nest setup and celsius conversion for the
nest thermostat.
2016-01-22 12:20:24 -06:00
Admin
37d346f558 Finished Nest implementation and default ip selection to not be
127.0.0.1

Fixes #12
Fixes #20
2016-01-21 16:44:24 -06:00
Admin
ac59398aa0 Testing nest functionality 2016-01-20 16:46:21 -06:00
Admin
87073435fc Updated code for IP address selection on startup fi none given. 2016-01-13 14:54:35 -06:00
Admin
8687f3482a Continuation of nest implementation 2016-01-12 16:34:05 -06:00
Admin
32a5f26ddd Added new object 2016-01-11 16:45:49 -06:00
Admin
c28f07d628 Continuation of nest implementation. 2016-01-11 16:45:02 -06:00
Admin
d3cc961dfb Initial updates for nest control 2016-01-08 16:14:42 -06:00
Admin
1b3d826f28 Fixed Readme format errors 2015-12-29 10:18:51 -06:00
Admin
c8f4d89a45 Update Readme for clarity on upnp.config.address usage. Also, updated
the 'ask alexa' section with more details.
2015-12-29 10:14:26 -06:00
26 changed files with 1329 additions and 270 deletions

File diff suppressed because one or more lines are too long

16
pom.xml
View File

@@ -5,11 +5,11 @@
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>1.2.3</version>
<version>1.3.6</version>
<packaging>jar</packaging>
<name>HA Bridge</name>
<description>Emulates a Philips Hue bridge to allow the Amazon Echo to hook up to other HA systems, i.e. Vera or Harmony Hub, using lightweight frameworks</description>
<description>Emulates a Philips Hue bridge to allow the Amazon Echo to hook up to other HA systems, i.e. Vera or Harmony Hub or Nest, using lightweight frameworks</description>
<properties>
<java.version>1.8</java.version>
@@ -30,6 +30,11 @@
<artifactId>harmony-java-client</artifactId>
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>com.github.bwssytems</groupId>
<artifactId>nest-controller</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
@@ -38,8 +43,13 @@
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>

View File

@@ -7,7 +7,7 @@ public class BridgeSettings {
private String serverport;
private String upnpresponseport;
private String upnpdevicedb;
private String veraaddress;
private IpList veraaddress;
private IpList harmonyaddress;
private String harmonyuser;
private String harmonypwd;
@@ -15,6 +15,9 @@ public class BridgeSettings {
private boolean upnpstrict;
private boolean traceupnp;
private boolean devmode;
private String nestuser;
private String nestpwd;
private boolean nestconfigured;
public String getUpnpConfigAddress() {
return upnpconfigaddress;
@@ -40,10 +43,10 @@ public class BridgeSettings {
public void setUpnpDeviceDb(String upnpDeviceDb) {
this.upnpdevicedb = upnpDeviceDb;
}
public String getVeraAddress() {
public IpList getVeraAddress() {
return veraaddress;
}
public void setVeraAddress(String veraAddress) {
public void setVeraAddress(IpList veraAddress) {
this.veraaddress = veraAddress;
}
public IpList getHarmonyAddress() {
@@ -88,14 +91,33 @@ public class BridgeSettings {
public void setDevMode(boolean devmode) {
this.devmode = devmode;
}
public String getNestuser() {
return nestuser;
}
public void setNestuser(String nestuser) {
this.nestuser = nestuser;
}
public String getNestpwd() {
return nestpwd;
}
public void setNestpwd(String nestpwd) {
this.nestpwd = nestpwd;
}
public boolean isNestConfigured() {
return nestconfigured;
}
public void setNestConfigured(boolean isNestConfigured) {
this.nestconfigured = isNestConfigured;
}
public Boolean isValidVera() {
if(this.veraaddress.contains(Configuration.DEFAULT_VERA_ADDRESS))
List<NamedIP> devicesList = this.veraaddress.getDevices();
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidHarmony() {
List<NamedIP> devicesList = this.harmonyaddress.getDevices();
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_HARMONY_ADDRESS))
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
if(this.harmonypwd == null || this.harmonypwd == "")
return false;
@@ -103,4 +125,11 @@ public class BridgeSettings {
return false;
return true;
}
public Boolean isValidNest() {
if(this.nestpwd == null || this.nestpwd == "")
return false;
if(this.nestuser == null || this.nestuser == "")
return false;
return true;
}
}

View File

@@ -4,10 +4,11 @@ public class Configuration {
public final static String DEVICE_DB_DIRECTORY = "data/device.db";
public final static String UPNP_RESPONSE_PORT = "50000";
public final static String UPNP_RESPONSE_DEVICES = "30";
public final static String DEFAULT_VERA_ADDRESS = "1.1.1.1";
public final static String DEFAULT_HARMONY_ADDRESS = "1.1.1.1";
public final static String DEFAULT_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_USER = "";
public final static String DEFAULT_HARMONY_PWD = "";
public final static String DEFAULT_USER = "";
public final static String DEFAULT_PWD = "";
public final static String DFAULT_WEB_PORT = "8080";
}

View File

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

View File

@@ -0,0 +1,13 @@
package com.bwssystems.HABridge.dao;
public class BackupFilename {
private String filename;
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
}

View File

@@ -3,12 +3,17 @@ package com.bwssystems.HABridge.dao;
import java.io.IOException;
import java.io.StringReader;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
@@ -33,8 +38,16 @@ public class DeviceRepository {
public DeviceRepository(String deviceDb) {
super();
repositoryPath = Paths.get(deviceDb);
String jsonContent = repositoryReader(repositoryPath);
_loadRepository(deviceDb);
}
private void _loadRepository(String aFilePath){
repositoryPath = Paths.get(aFilePath);
_loadRepository(repositoryPath);
}
private void _loadRepository(Path aPath){
String jsonContent = repositoryReader(aPath);
devices = new HashMap<String, DeviceDescriptor>();
if(jsonContent != null)
@@ -47,7 +60,8 @@ public class DeviceRepository {
put(theDevice.getId(), theDevice);
}
}
}
}
public List<DeviceDescriptor> findAll() {
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
@@ -79,6 +93,66 @@ public class DeviceRepository {
log.debug("Save device: " + aDescriptor.getName());
}
public String backup(String aFilename) {
if(aFilename == null || aFilename.equalsIgnoreCase("")) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
aFilename = "devicedb-" + dateFormat.format(Calendar.getInstance().getTime()) + ".bk";
}
else
aFilename = aFilename + ".bk";
try {
Files.copy(repositoryPath, FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename), StandardCopyOption.COPY_ATTRIBUTES);
} catch (IOException e) {
log.error("Could not backup to file: " + aFilename + " message: " + e.getMessage(), e);
}
log.debug("Backup repository: " + aFilename);
return aFilename;
}
public String deleteBackup(String aFilename) {
log.debug("Delete backup repository: " + aFilename);
try {
Files.delete(FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename));
} catch (IOException e) {
log.error("Could not delete file: " + aFilename + " message: " + e.getMessage(), e);
}
return aFilename;
}
public String restoreBackup(String aFilename) {
log.debug("Restore backup repository: " + aFilename);
try {
Path target = null;
if(Files.exists(repositoryPath)) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
target = FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), "devicedb-" + dateFormat.format(Calendar.getInstance().getTime()) + ".bk");
Files.move(repositoryPath, target);
}
Files.copy(FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename), repositoryPath, StandardCopyOption.COPY_ATTRIBUTES);
} catch (IOException e) {
log.error("Error restoring the file: " + aFilename + " message: " + e.getMessage(), e);
return null;
}
_loadRepository(repositoryPath);
return aFilename;
}
public List<String> getBackups() {
List<String> theFilenames = new ArrayList<String>();
Path dir = repositoryPath.getParent();
try (DirectoryStream<Path> stream =
Files.newDirectoryStream(dir, "*.{bk}")) {
for (Path entry: stream) {
theFilenames.add(entry.getFileName().toString());
}
} catch (IOException x) {
// IOException can never be thrown by the iteration.
// In this snippet, it can // only be thrown by newDirectoryStream.
log.error("Issue getting direcotyr listing for backups - " + x.getMessage());
}
return theFilenames;
}
public String delete(DeviceDescriptor aDescriptor) {
if (aDescriptor != null) {
devices.remove(aDescriptor.getId());

View File

@@ -18,10 +18,13 @@ import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.JsonTransformer;
import com.bwssystems.HABridge.Version;
import com.bwssystems.HABridge.dao.BackupFilename;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.dao.DeviceRepository;
import com.bwssystems.NestBridge.NestHome;
import com.bwssystems.harmony.HarmonyHome;
import com.bwssystems.luupRequests.Sdata;
import com.bwssystems.vera.VeraHome;
import com.bwssystems.vera.VeraInfo;
import com.google.gson.Gson;
@@ -33,18 +36,30 @@ public class DeviceResource {
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
private DeviceRepository deviceRepository;
private VeraInfo veraInfo;
private VeraHome veraHome;
private Version version;
private HarmonyHome myHarmonyHome;
private NestHome nestHome;
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
public DeviceResource(BridgeSettings theSettings, Version theVersion, HarmonyHome theHarmonyHome) {
public DeviceResource(BridgeSettings theSettings, Version theVersion, HarmonyHome theHarmonyHome, NestHome aNestHome) {
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
this.veraInfo = new VeraInfo(theSettings.getVeraAddress(), theSettings.isValidVera());
if(theSettings.isValidVera())
this.veraHome = new VeraHome(theSettings);
else
this.veraHome = null;
if(theSettings.isValidHarmony())
this.myHarmonyHome = theHarmonyHome;
else
this.myHarmonyHome = null;
if(theSettings.isValidNest())
this.nestHome = aNestHome;
else
this.nestHome = null;
this.version = theVersion;
setupEndpoints();
}
@@ -167,25 +182,23 @@ public class DeviceResource {
get (API_CONTEXT + "/vera/devices", "application/json", (request, response) -> {
log.debug("Get vera devices");
Sdata sData = veraInfo.getSdata();
if(sData == null){
if(veraHome == null){
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return sData.getDevices();
return veraHome.getDevices();
}, new JsonTransformer());
get (API_CONTEXT + "/vera/scenes", "application/json", (request, response) -> {
log.debug("Get vera scenes");
Sdata sData = veraInfo.getSdata();
if(sData == null){
if(veraHome == null){
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return sData.getScenes();
return veraHome.getScenes();
}, new JsonTransformer());
get (API_CONTEXT + "/harmony/activities", "application/json", (request, response) -> {
@@ -218,5 +231,75 @@ public class DeviceResource {
return myHarmonyHome.getDevices();
}, new JsonTransformer());
get (API_CONTEXT + "/nest/items", "application/json", (request, response) -> {
log.debug("Get nest items");
if(nestHome == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return nestHome.getItems();
}, new JsonTransformer());
get (API_CONTEXT + "/backup/available", "application/json", (request, response) -> {
log.debug("Get backup filenames");
response.status(HttpStatus.SC_OK);
return deviceRepository.getBackups();
}, new JsonTransformer());
// http://ip_address:port/api/devices/backup/create CORS request
options(API_CONTEXT + "/backup/create", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (API_CONTEXT + "/backup/create", "application/json", (request, response) -> {
log.debug("Create backup: " + request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
BackupFilename returnFilename = new BackupFilename();
returnFilename.setFilename(deviceRepository.backup(aFilename.getFilename()));
return returnFilename;
}, new JsonTransformer());
// http://ip_address:port/api/devices/backup/delete CORS request
options(API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "POST");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post (API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
log.debug("Delete backup: " + request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
if(aFilename != null)
deviceRepository.deleteBackup(aFilename.getFilename());
else
log.warn("No filename given for delete backup.");
return null;
}, new JsonTransformer());
// http://ip_address:port/api/devices/backup/restore CORS request
options(API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "POST");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post (API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
log.debug("Restore backup: " + request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
if(aFilename != null)
deviceRepository.restoreBackup(aFilename.getFilename());
else
log.warn("No filename given for restore backup.");
return null;
}, 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.HueApiResponse;
import com.bwssystems.HABridge.dao.*;
import com.bwssystems.NestBridge.NestInstruction;
import com.bwssystems.NestBridge.NestHome;
import com.bwssystems.harmony.ButtonPress;
import com.bwssystems.harmony.HarmonyHandler;
import com.bwssystems.harmony.HarmonyHome;
import com.bwssystems.harmony.RunActivity;
import com.bwssystems.nest.controller.Nest;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
@@ -63,13 +66,14 @@ public class HueMulator {
private DeviceRepository repository;
private HarmonyHome myHarmonyHome;
private Nest theNest;
private HttpClient httpClient;
private ObjectMapper mapper;
private BridgeSettings bridgeSettings;
private byte[] sendData;
public HueMulator(BridgeSettings theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome){
public HueMulator(BridgeSettings theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome, NestHome aNestHome){
httpClient = HttpClients.createDefault();
mapper = new ObjectMapper(); //armzilla: work around Echo incorrect content type and breaking mapping. Map manually
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@@ -78,6 +82,10 @@ public class HueMulator {
this.myHarmonyHome = theHarmonyHome;
else
this.myHarmonyHome = null;
if(theBridgeSettings.isValidNest())
this.theNest = aNestHome.getTheNest();
else
this.theNest = null;
bridgeSettings = theBridgeSettings;
}
@@ -313,7 +321,7 @@ public class HueMulator {
if(device.getDeviceType().toLowerCase().contains("activity") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyActivity")))
{
log.debug("executing activity to Harmony: " + url);
log.debug("executing HUE api request to change activity to Harmony: " + url);
RunActivity anActivity = new Gson().fromJson(url, RunActivity.class);
HarmonyHandler myHarmony = myHarmonyHome.getHarmonyHandler(device.getTargetDevice());
if(myHarmony == null)
@@ -326,19 +334,72 @@ public class HueMulator {
}
else if(device.getDeviceType().toLowerCase().contains("button") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyButton")))
{
log.debug("executing button press to Harmony: " + url);
ButtonPress aDeviceButton = new Gson().fromJson(url, ButtonPress.class);
log.debug("executing HUE api request to button press(es) to Harmony: " + url);
if(url.substring(0, 1).equalsIgnoreCase("{")) {
url = "[" + url +"]";
}
ButtonPress[] deviceButtons = new Gson().fromJson(url, ButtonPress[].class);
HarmonyHandler myHarmony = myHarmonyHome.getHarmonyHandler(device.getTargetDevice());
if(myHarmony == null)
{
log.warn("Should not get here, no harmony hub available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"Should not get here, no harmony hub available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
else {
for(int i = 0; i < deviceButtons.length; i++) {
if( i > 0)
Thread.sleep(100);
log.debug("pressing button: " + deviceButtons[i].getDevice() + " - " + deviceButtons[i].getButton() + " - iteration: " + String.valueOf(i));
myHarmony.pressButton(deviceButtons[i]);
}
}
}
else if(device.getDeviceType().toLowerCase().contains("home") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("nestHomeAway")))
{
log.debug("executing HUE api request to set away for nest home: " + url);
NestInstruction homeAway = new Gson().fromJson(url, NestInstruction.class);
if(theNest == null)
{
log.warn("Should not get here, no Nest available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"Should not get here, no Nest available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
else
myHarmony.pressButton(aDeviceButton);
theNest.getHome(homeAway.getName()).setAway(homeAway.getAway());
}
else if(device.getDeviceType().toLowerCase().contains("thermo") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("nestThermoSet")))
{
log.debug("executing HUE api request to set thermostat for nest: " + url);
NestInstruction thermoSetting = new Gson().fromJson(url, NestInstruction.class);
if(theNest == null)
{
log.warn("Should not get here, no Nest available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"Should not get here, no Nest available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
else {
if(thermoSetting.getControl().equalsIgnoreCase("temp")) {
if(request.body().contains("bri")) {
thermoSetting.setTemp(String.valueOf((Double.parseDouble(replaceIntensityValue(thermoSetting.getTemp(), state.getBri())) - 32.0)/1.8));
log.debug("Setting thermostat: " + thermoSetting.getName() + " to " + thermoSetting.getTemp() + "C");
theNest.getThermostat(thermoSetting.getName()).setTargetTemperature(Float.parseFloat(thermoSetting.getTemp()));
}
}
else if (thermoSetting.getControl().contains("range") ||thermoSetting.getControl().contains("heat") ||thermoSetting.getControl().contains("cool") ||thermoSetting.getControl().contains("off")) {
log.debug("Setting thermostat target type: " + thermoSetting.getName() + " to " + thermoSetting.getControl());
theNest.getThermostat(thermoSetting.getName()).setTargetType(thermoSetting.getControl());
}
else if(thermoSetting.getControl().contains("fan")) {
log.debug("Setting thermostat fan mode: " + thermoSetting.getName() + " to " + thermoSetting.getControl().substring(4));
theNest.getThermostat(thermoSetting.getName()).setFanMode(thermoSetting.getControl().substring(4));
}
else {
log.warn("no valid Nest control info: " + thermoSetting.getControl());
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + ",\"description\": \"no valid Nest control info\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
}
}
else if(url.startsWith("udp://"))
{
log.debug("executing HUE api request to UDP: " + url);
try {
String intermediate = url.substring(6);
String ipAddr = intermediate.substring(0, intermediate.indexOf(':'));
@@ -361,7 +422,7 @@ public class HueMulator {
}
else
{
log.debug("executing activity to Http " + (device.getHttpVerb() == null?"GET":device.getHttpVerb()) + ": " + url);
log.debug("executing HUE api request to Http " + (device.getHttpVerb() == null?"GET":device.getHttpVerb()) + ": " + url);
// quick template
String body;
url = replaceIntensityValue(url, state.getBri());
@@ -412,7 +473,8 @@ public class HueMulator {
request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE, endResult.toString());
} catch (Exception e) {
log.warn("Could not execute Math: " + mathDescriptor, e);
} }
}
}
return request;
}

View File

@@ -53,6 +53,7 @@ public class UpnpSettingsResource {
this.theSettings.setUpnpResponsePort(theBridgeSettings.getUpnpResponsePort());
this.theSettings.setUpnpStrict(theBridgeSettings.isUpnpStrict());
this.theSettings.setVeraAddress(theBridgeSettings.getVeraAddress());
this.theSettings.setNestConfigured(theBridgeSettings.isValidNest());
}
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

@@ -13,6 +13,8 @@ public class Device {
private String level;
private String state;
private String comment;
private String veraname;
private String veraaddress;
public String getName() {
return name;
}
@@ -79,5 +81,17 @@ public class Device {
public void setComment(String comment) {
this.comment = comment;
}
public String getVeraname() {
return veraname;
}
public void setVeraname(String veraname) {
this.veraname = veraname;
}
public String getVeraaddress() {
return veraaddress;
}
public void setVeraaddress(String veraaddress) {
this.veraaddress = veraaddress;
}
}

View File

@@ -5,6 +5,8 @@ public class Scene {
private String name;
private String id;
private String room;
private String veraname;
private String veraaddress;
public String getActive() {
return active;
}
@@ -29,5 +31,17 @@ public class Scene {
public void setRoom(String room) {
this.room = room;
}
public String getVeraname() {
return veraname;
}
public void setVeraname(String veraname) {
this.veraname = veraname;
}
public String getVeraaddress() {
return veraaddress;
}
public void setVeraaddress(String veraaddress) {
this.veraaddress = veraaddress;
}
}

View File

@@ -0,0 +1,58 @@
package com.bwssystems.vera;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.luupRequests.Device;
import com.bwssystems.luupRequests.Scene;
public class VeraHome {
private static final Logger log = LoggerFactory.getLogger(VeraHome.class);
private Map<String, VeraInfo> veras;
public VeraHome(BridgeSettings bridgeSettings) {
veras = new HashMap<String, VeraInfo>();
if(!bridgeSettings.isValidVera())
return;
Iterator<NamedIP> theList = bridgeSettings.getVeraAddress().getDevices().iterator();
while(theList.hasNext()) {
NamedIP aVera = theList.next();
veras.put(aVera.getName(), new VeraInfo(aVera, bridgeSettings.isValidVera()));
}
}
public List<Device> getDevices() {
log.debug("consolidating devices for veras");
Iterator<String> keys = veras.keySet().iterator();
ArrayList<Device> deviceList = new ArrayList<Device>();
while(keys.hasNext()) {
String key = keys.next();
Iterator<Device> devices = veras.get(key).getSdata().getDevices().iterator();
while(devices.hasNext()) {
deviceList.add(devices.next());
}
}
return deviceList;
}
public List<Scene> getScenes() {
log.debug("consolidating scenes for veras");
Iterator<String> keys = veras.keySet().iterator();
ArrayList<Scene> sceneList = new ArrayList<Scene>();
while(keys.hasNext()) {
String key = keys.next();
Iterator<Scene> scenes = veras.get(key).getSdata().getScenes().iterator();
while(scenes.hasNext()) {
sceneList.add(scenes.next());
}
}
return sceneList;
}
}

View File

@@ -13,6 +13,7 @@ import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.luupRequests.Categorie;
import com.bwssystems.luupRequests.Device;
import com.bwssystems.luupRequests.Room;
@@ -25,13 +26,13 @@ public class VeraInfo {
private static final Logger log = LoggerFactory.getLogger(VeraInfo.class);
private HttpClient httpClient;
private static final String SDATA_REQUEST = ":3480/data_request?id=sdata&output_format=json";
private String veraAddressString;
private NamedIP veraAddress;
private Boolean validVera;
public VeraInfo(String addressString, Boolean isValidVera) {
public VeraInfo(NamedIP addressName, Boolean isValidVera) {
super();
httpClient = HttpClients.createDefault();
veraAddressString = addressString;
veraAddress = addressName;
validVera = isValidVera;
}
@@ -39,7 +40,7 @@ public class VeraInfo {
if(!validVera)
return new Sdata();
String theUrl = "http://" + veraAddressString + SDATA_REQUEST;
String theUrl = "http://" + veraAddress.getIp() + SDATA_REQUEST;
String theData;
theData = doHttpGETRequest(theUrl);
@@ -71,6 +72,8 @@ public class VeraInfo {
theDevice.setCategory(categoryMap.get(theDevice.getCategory()).getName());
else
theDevice.setCategory("<unknown>");
theDevice.setVeraaddress(veraAddress.getIp());
theDevice.setVeraname(veraAddress.getName());
}
ListIterator<Scene> theSecneIter = theSdata.getScenes().listIterator();
@@ -81,6 +84,8 @@ public class VeraInfo {
theScene.setRoom(roomMap.get(theScene.getRoom()).getName());
else
theScene.setRoom("no room");
theScene.setVeraaddress(veraAddress.getIp());
theScene.setVeraname(veraAddress.getName());
}
}

View File

@@ -1,6 +1,4 @@
var app = angular.module('habridge', [
'ngRoute'
]);
var app = angular.module('habridge', ['ngRoute']);
app.config(function ($routeProvider) {
$routeProvider.when('/#', {
@@ -24,6 +22,9 @@ app.config(function ($routeProvider) {
}).when('/harmonyactivities', {
templateUrl: 'views/harmonyactivity.html',
controller: 'AddingController'
}).when('/nest', {
templateUrl: 'views/nestactions.html',
controller: 'AddingController'
}).otherwise({
templateUrl: 'views/configuration.html',
controller: 'ViewingController'
@@ -32,63 +33,12 @@ app.config(function ($routeProvider) {
app.run( function (bridgeService) {
bridgeService.loadBridgeSettings();
bridgeService.updateShowVera();
bridgeService.updateShowHarmony();
bridgeService.getHABridgeVersion();
});
app.factory('BridgeSettings', function() {
var BridgeSettings = {};
BridgeSettings.upnpconfigaddress = "";
BridgeSettings.serverport = "";
BridgeSettings.upnpdevicedb = "";
BridgeSettings.upnpresponseport = "";
BridgeSettings.veraaddress = "";
BridgeSettings.harmonyaddress = "";
BridgeSettings.upnpstrict = "";
BridgeSettings.traceupnp = "";
BridgeSettings.devmode = "";
BridgeSettings.setupnpconfigaddress = function(aconfigaddress){
BridgeSettings.upnpconfigaddress = aconfigaddress;
};
BridgeSettings.setserverport = function(aserverport){
BridgeSettings.serverport = aserverport;
};
BridgeSettings.setupnpdevicedb = function(aupnpdevicedb){
BridgeSettings.upnpdevicedb = aupnpdevicedb;
};
BridgeSettings.setupnpresponseport = function(aupnpresponseport){
BridgeSettings.upnpresponseport = aupnpresponseport;
};
BridgeSettings.setveraaddress = function(averaaddress){
BridgeSettings.veraaddress = averaaddress;
};
BridgeSettings.setharmonyaddress = function(aharmonyaddress){
BridgeSettings.harmonyaddress = aharmonyaddress;
};
BridgeSettings.setupnpstrict = function(aupnpstrict){
BridgeSettings.upnpstrict = aupnpstrict;
};
BridgeSettings.settraceupnp = function(atraceupnp){
BridgeSettings.traceupnp = atraceupnp;
};
BridgeSettings.setdevmode = function(adevmode){
BridgeSettings.devmode = adevmode;
};
return BridgeSettings;
});
app.service('bridgeService', function ($http, $window, BridgeSettings) {
app.service('bridgeService', function ($http, $window) {
var self = this;
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", backups: [], devices: [], device: [], settings: [], error: "", showVera: false, showHarmony: false, showNest: false, habridgeversion: ""};
this.viewDevices = function () {
this.state.error = "";
@@ -129,16 +79,24 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
}
this.updateShowVera = function () {
if(this.aContainsB(self.BridgeSettings.veraaddress, "1.1.1.1") || self.BridgeSettings.veraaddress == "" || self.BridgeSettings.veraaddress == null)
if(this.aContainsB(self.state.settings.veraaddress.devices[0].ip, "1.1.1.1") || self.state.settings.veraaddress == "" || self.state.settings.veraaddress == null)
this.state.showVera = false;
else
this.state.showVera = true;
return;
}
this.updateShowNest = function () {
if(self.state.settings.nestconfigured == true)
this.state.showNest = true;
else
this.state.showNest = false;
return;
}
this.updateShowHarmony = function () {
if(self.BridgeSettings.harmonyaddress.devices) {
if(this.aContainsB(self.BridgeSettings.harmonyaddress.devices[0].ip, "1.1.1.1") || self.BridgeSettings.harmonyaddress == "" || self.BridgeSettings.harmonyaddress == null)
if(self.state.settings.harmonyaddress.devices) {
if(this.aContainsB(self.state.settings.harmonyaddress.devices[0].ip, "1.1.1.1") || self.state.settings.harmonyaddress == "" || self.state.settings.harmonyaddress == null)
this.state.showHarmony = false;
else
this.state.showHarmony = true;
@@ -153,15 +111,10 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
this.state.error = "";
return $http.get(this.state.upnpbase).then(
function (response) {
self.BridgeSettings.setupnpconfigaddress(response.data.upnpconfigaddress);
self.BridgeSettings.setserverport(response.data.serverport);
self.BridgeSettings.setupnpdevicedb(response.data.upnpdevicedb);
self.BridgeSettings.setupnpresponseport(response.data.upnpresponseport);
self.BridgeSettings.setveraaddress(response.data.veraaddress);
self.BridgeSettings.setharmonyaddress(response.data.harmonyaddress);
self.BridgeSettings.settraceupnp(response.data.traceupnp);
self.BridgeSettings.setupnpstrict(response.data.upnpstrict);
self.BridgeSettings.setdevmode(response.data.devmode);
self.state.settings = response.data;
self.updateShowVera();
self.updateShowHarmony();
self.updateShowNest();
},
function (error) {
if (error.data) {
@@ -174,11 +127,44 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
);
};
this.viewBackups = function () {
this.state.error = "";
return $http.get(this.state.base + "/backup/available").then(
function (response) {
self.state.backups = response.data;
},
function (error) {
if (error.data) {
$window.alert("Get Backups Error: " + error.data.message);
} else {
$window.alert("Get Backups Error: unknown");
}
}
);
};
this.viewNestItems = function () {
this.state.error = "";
if(!this.state.showNest)
return;
return $http.get(this.state.base + "/nest/items").then(
function (response) {
self.state.nestitems = response.data;
},
function (error) {
if (error.data) {
$window.alert("Get Nest Items Error: " + error.data.message);
} else {
$window.alert("Get Nest Items Error: unknown");
}
}
);
};
this.viewVeraDevices = function () {
this.state.error = "";
if(!this.state.showVera)
return;
this.state.error = "";
return $http.get(this.state.base + "/vera/devices").then(
function (response) {
self.state.veradevices = response.data;
@@ -255,10 +241,20 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
return false;
};
this.findNestItemByMapId = function(id, type) {
for(var i = 0; i < this.state.devices.length; i++) {
if(this.state.devices[i].mapId == id && this.aContainsB(this.state.devices[i].mapType, type))
return true;
}
return false;
};
this.addDevice = function (device) {
this.state.error = "";
if(device.httpVerb != null && device.httpVerb != "")
device.deviceType = "custom";
if(device.targetDevice == null || device.targetDevice == "")
device.targetDevice = "Encapsulated";
if (device.id) {
var putUrl = this.state.base + "/" + device.id;
return $http.put(putUrl, {
@@ -288,8 +284,6 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
} else {
if(device.deviceType == null || device.deviceType == "")
device.deviceType = "custom";
if(device.httpVerb != null && device.httpVerb != "")
device.deviceType = "custom";
return $http.post(this.state.base, {
name: device.name,
mapId: device.mapId,
@@ -316,6 +310,58 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
}
};
this.backupDeviceDb = function (afilename) {
this.state.error = "";
return $http.put(this.state.base + "/backup/create", {
filename: afilename
}).then(
function (response) {
self.viewBackups();
},
function (error) {
if (error.data) {
self.state.error = error.data.message;
}
$window.alert("Backup Device Db Error: unknown");
}
);
};
this.restoreBackup = function (afilename) {
this.state.error = "";
return $http.post(this.state.base + "/backup/restore", {
filename: afilename
}).then(
function (response) {
self.viewBackups();
self.viewDevices();
},
function (error) {
if (error.data) {
self.state.error = error.data.message;
}
$window.alert("Backup Db Restore Error: unknown");
}
);
};
this.deleteBackup = function (afilename) {
this.state.error = "";
return $http.post(this.state.base + "/backup/delete", {
filename: afilename
}).then(
function (response) {
self.viewBackups();
},
function (error) {
if (error.data) {
self.state.error = error.data.message;
}
$window.alert("Backup Db Frlryr Error: unknown");
}
);
};
this.deleteDevice = function (id) {
this.state.error = "";
return $http.delete(this.state.base + "/" + id).then(
@@ -333,7 +379,7 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
this.deleteDeviceByMapId = function (id, type) {
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);
}
};
@@ -368,15 +414,16 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
};
});
app.controller('ViewingController', function ($scope, $location, $http, $window, bridgeService, BridgeSettings) {
app.controller('ViewingController', function ($scope, $location, $http, $window, bridgeService) {
$scope.BridgeSettings = bridgeService.BridgeSettings;
bridgeService.viewDevices();
bridgeService.viewBackups();
$scope.bridge = bridgeService.state;
bridgeService.updateShowVera();
bridgeService.updateShowHarmony();
$scope.optionalbackupname = "";
$scope.visible = false;
$scope.imgUrl = "glyphicon glyphicon-plus";
$scope.visibleBk = false;
$scope.imgBkUrl = "glyphicon glyphicon-plus";
$scope.predicate = '';
$scope.reverse = true;
$scope.order = function(predicate) {
@@ -400,6 +447,15 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
bridgeService.editDevice(device);
$location.path('/editdevice');
};
$scope.backupDeviceDb = function (optionalbackupname) {
bridgeService.backupDeviceDb(optionalbackupname);
};
$scope.restoreBackup = function (backupname) {
bridgeService.restoreBackup(backupname);
};
$scope.deleteBackup = function (backupname) {
bridgeService.deleteBackup(backupname);
};
$scope.toggle = function () {
$scope.visible = !$scope.visible;
if($scope.visible)
@@ -407,43 +463,57 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
else
$scope.imgUrl = "glyphicon glyphicon-plus";
};
$scope.toggleBk = function () {
$scope.visibleBk = !$scope.visibleBk;
if($scope.visibleBk)
$scope.imgBkUrl = "glyphicon glyphicon-minus";
else
$scope.imgBkUrl = "glyphicon glyphicon-plus";
};
});
app.controller('AddingController', function ($scope, $location, $http, bridgeService, BridgeSettings) {
$scope.device = {id: "", name: "", deviceType: "custom", onUrl: "", offUrl: ""};
$scope.vera = {base: "", port: "3480", id: ""};
$scope.vera.base = "http://" + BridgeSettings.veraaddress;
bridgeService.device = $scope.device;
app.controller('AddingController', function ($scope, $location, $http, bridgeService) {
$scope.bridge = bridgeService.state;
$scope.device = $scope.bridge.device;
$scope.device_dim_control = "";
$scope.bulk = { devices: [] };
$scope.vera = {base: "http://" + $scope.bridge.settings.veraaddress, port: "3480", id: ""};
bridgeService.viewVeraDevices();
bridgeService.viewVeraScenes();
bridgeService.viewHarmonyActivities();
bridgeService.viewHarmonyDevices();
$scope.bridge = bridgeService.state;
bridgeService.updateShowVera();
bridgeService.updateShowHarmony();
$scope.device = bridgeService.state.device;
$scope.activitiesVisible = false;
bridgeService.viewNestItems();
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
$scope.buttonsVisible = false;
$scope.imgActivitiesUrl = "glyphicon glyphicon-plus";
$scope.devicesVisible = false;
$scope.imgDevicesUrl = "glyphicon glyphicon-plus";
$scope.scenesVisible = false;
$scope.imgScenesUrl = "glyphicon glyphicon-plus";
$scope.predicate = '';
$scope.reverse = true;
$scope.device_dim_control = "";
$scope.order = function(predicate) {
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
$scope.predicate = predicate;
};
$scope.buildUrlsUsingDevice = function (dim_control) {
$scope.clearDevice = function () {
$scope.device.id = "";
$scope.device.mapType = null;
$scope.device.mapId = null;
$scope.device.name = "";
$scope.device.onUrl = "";
$scope.device.deviceType = "custom";
$scope.device.targetDevice = null;
$scope.device.offUrl = "";
$scope.device.httpVerb = null;
$scope.device.contentType = null;
$scope.device.contentBody = null;
$scope.device.contentBodyOff = null;
};
$scope.buildUrlsUsingDevice = function (dim_control) {
if ($scope.vera.base.indexOf("http") < 0) {
$scope.vera.base = "http://" + $scope.vera.base;
}
$scope.device.deviceType = "switch";
$scope.device.targetDevice = $scope.bridge.settings.veraaddress;
$scope.device.mapType = "veraDevice";
$scope.device.mapId = $scope.vera.id;
if(dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0)
@@ -466,6 +536,7 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
$scope.vera.base = "http://" + $scope.vera.base;
}
$scope.device.deviceType = "scene";
$scope.device.targetDevice = $scope.bridge.settings.veraaddress;
$scope.device.mapType = "veraScene";
$scope.device.mapId = $scope.vera.id;
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
@@ -482,6 +553,7 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}
$scope.device.deviceType = "switch";
$scope.device.name = veradevice.name;
$scope.device.targetDevice = veradevice.veraname;
$scope.device.mapType = "veraDevice";
$scope.device.mapId = veradevice.id;
if(dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0)
@@ -505,6 +577,7 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}
$scope.device.deviceType = "scene";
$scope.device.name = verascene.name;
$scope.device.targetDevice = verascene.veraname;
$scope.device.mapType = "veraScene";
$scope.device.mapId = verascene.id;
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
@@ -526,49 +599,138 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
};
$scope.buildButtonUrls = function (harmonydevice, onbutton, offbutton) {
$scope.device.deviceType = "button";
$scope.device.targetDevice = harmonydevice.hub;
$scope.device.name = harmonydevice.device.label;
$scope.device.mapType = "harmonyButton";
$scope.device.mapId = harmonydevice.device.id + "-" + onbutton + "-" + offbutton;
$scope.device.onUrl = "{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + onbutton + "\"}";
$scope.device.offUrl = "{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + offbutton + "\"}";
var currentOn = $scope.device.onUrl;
var currentOff = $scope.device.offUrl;
var actionOn = angular.fromJson(onbutton);
var actionOff = angular.fromJson(offbutton);
if( $scope.device.mapType == "harmonyButton") {
$scope.device.onUrl = currentOn.substr(0, currentOn.indexOf("]")) + ",{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + actionOn.command + "\"}]";
$scope.device.offUrl = currentOff.substr(0, currentOff.indexOf("]")) + ",{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + actionOff.command + "\"}]";
}
else if ($scope.device.mapType == null || $scope.device.mapType == "") {
$scope.device.deviceType = "button";
$scope.device.targetDevice = harmonydevice.hub;
$scope.device.name = harmonydevice.device.label;
$scope.device.mapType = "harmonyButton";
$scope.device.mapId = harmonydevice.device.id + "-" + actionOn.command + "-" + actionOff.command;
$scope.device.onUrl = "[{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + actionOn.command + "\"}]";
$scope.device.offUrl = "[{\"device\":\"" + harmonydevice.device.id + "\",\"button\":\"" + actionOff.command + "\"}]";
}
};
$scope.buildNestHomeUrls = function (nestitem) {
$scope.device.deviceType = "home";
$scope.device.name = nestitem.name;
$scope.device.targetDevice = 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.targetDevice = nestitem.location;
$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.targetDevice = nestitem.location;
$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.targetDevice = nestitem.location;
$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.targetDevice = nestitem.location;
$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.targetDevice = nestitem.location;
$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.targetDevice = nestitem.location;
$scope.device.mapType = "nestThermoSet";
$scope.device.mapId = nestitem.id + "-SetFan";
$scope.device.onUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"fan-on\"}";
$scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"fan-auto\"}";
};
$scope.testUrl = function (device, type) {
bridgeService.testUrl(device, type);
};
$scope.addDevice = function () {
if($scope.device.name == "" && $scope.device.onUrl == "")
return;
bridgeService.addDevice($scope.device).then(
function () {
$scope.device.id = "";
$scope.device.mapType = null;
$scope.device.mapId = null;
$scope.device.name = "";
$scope.device.onUrl = "";
$scope.device.deviceType = "custom";
$scope.device.targetDevice = null;
$scope.device.offUrl = "";
$scope.device.httpVerb = null;
$scope.device.contentType = null;
$scope.device.contentBody = null;
$scope.device.contentBodyOff = null;
$location.path('/#');
$scope.clearDevice();
},
function (error) {
}
);
}
$scope.toggleActivities = function () {
$scope.activitiesVisible = !$scope.activitiesVisible;
if($scope.activitiesVisible)
$scope.imgActivitiesUrl = "glyphicon glyphicon-minus";
else
$scope.imgActivitiesUrl = "glyphicon glyphicon-plus";
};
$scope.bulkAddDevices = function(dim_control) {
for(var i = 0; i < $scope.bulk.devices.length; i++) {
for(var x = 0; x < bridgeService.state.veradevices.length; x++) {
if(bridgeService.state.veradevices[x].id == $scope.bulk.devices[i]) {
$scope.buildDeviceUrls(bridgeService.state.veradevices[x],dim_control);
$scope.addDevice();
}
}
}
$scope.bulk = { devices: [] };
};
$scope.toggleSelection = function toggleSelection(deviceId) {
var idx = $scope.bulk.devices.indexOf(deviceId);
// is currently selected
if (idx > -1) {
$scope.bulk.devices.splice(idx, 1);
}
// is newly selected
else {
$scope.bulk.devices.push(deviceId);
}
};
$scope.toggleButtons = function () {
$scope.buttonsVisible = !$scope.buttonsVisible;
if($scope.buttonsVisible)
@@ -576,23 +738,7 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
else
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
};
$scope.toggleDevices = function () {
$scope.devicesVisible = !$scope.devicesVisible;
if($scope.devicesVisible)
$scope.imgDevicesUrl = "glyphicon glyphicon-minus";
else
$scope.imgDevicesUrl = "glyphicon glyphicon-plus";
};
$scope.toggleScenes = function () {
$scope.scenesVisible = !$scope.scenesVisible;
if($scope.scenesVisible)
$scope.imgScenesUrl = "glyphicon glyphicon-minus";
else
$scope.imgScenesUrl = "glyphicon glyphicon-plus";
};
$scope.deleteDeviceByMapId = function (id, mapType) {
bridgeService.deleteDeviceByMapId(id, mapType);
};
@@ -633,7 +779,7 @@ app.filter('availableVeraDeviceId', function(bridgeService) {
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(!bridgeService.findDeviceByMapId(input[i].id, null, "veraDevice")){
if(!bridgeService.findDeviceByMapId(input[i].id, input[i].veraname, "veraDevice")){
out.push(input[i]);
}
}
@@ -647,7 +793,7 @@ return function(input) {
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(bridgeService.findDeviceByMapId(input[i].id, null, "veraDevice")){
if(bridgeService.findDeviceByMapId(input[i].id, input[i].veraname, "veraDevice")){
out.push(input[i]);
}
}
@@ -661,7 +807,7 @@ app.filter('availableVeraSceneId', function(bridgeService) {
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(!bridgeService.findDeviceByMapId(input[i].id, null, "veraScene")){
if(!bridgeService.findDeviceByMapId(input[i].id, input[i].veraname, "veraScene")){
out.push(input[i]);
}
}
@@ -675,7 +821,35 @@ return function(input) {
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(bridgeService.findDeviceByMapId(input[i].id, null, "veraScene")){
if(bridgeService.findDeviceByMapId(input[i].id,input[i].veraname, "veraScene")){
out.push(input[i]);
}
}
return out;
}
});
app.filter('availableNestItemId', function(bridgeService) {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(!bridgeService.findNestItemByMapId(input[i].id, "nestHomeAway")){
out.push(input[i]);
}
}
return out;
}
});
app.filter('unavailableNestItemId', function(bridgeService) {
return function(input) {
var out = [];
if(input == null)
return out;
for (var i = 0; i < input.length; i++) {
if(input[i].mapType != null && bridgeService.aContainsB(input[i].mapType, "nest")){
out.push(input[i]);
}
}

View File

@@ -4,6 +4,7 @@
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
@@ -23,11 +24,12 @@
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">Current devices</h2>
<h2 class="panel-title">Current devices ({{bridge.devices.length}}) </h2>
</div>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th>
<a href="" ng-click="order('id')">ID</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span></th>
@@ -44,6 +46,7 @@
</tr>
</thead>
<tr ng-repeat="device in bridge.devices | orderBy:predicate:reverse">
<td>{{$index+1}}</td>
<td>{{device.id}}</td>
<td>{{device.name}}</td>
<td>{{device.deviceType}}</td>
@@ -64,7 +67,7 @@
<div class="panel panel-default bridgeServer">
<div class="panel-heading">
<h1 class="panel-title">Bridge settings <a ng-click="toggle()"><span class={{imgUrl}} aria-hidden="true"></a></h1>
<h1 class="panel-title">Bridge Settings <a ng-click="toggle()"><span class={{imgUrl}} aria-hidden="true"></a></h1>
</div>
<div ng-if="visible" class="animate-if" class="panel-body">
@@ -92,40 +95,81 @@
</thead>
<tr>
<td>upnp.config.address</td>
<td>{{BridgeSettings.upnpconfigaddress}}</td>
<td>{{bridge.settings.upnpconfigaddress}}</td>
</tr>
<tr>
<td>server.port</td>
<td>{{BridgeSettings.serverport}}</td>
<td>{{bridge.settings.serverport}}</td>
</tr>
<tr>
<td>upnp.devices.db</td>
<td>{{BridgeSettings.upnpdevicedb}}</td>
<td>{{bridge.settings.upnpdevicedb}}</td>
</tr>
<tr>
<td>upnp.response.port</td>
<td>{{BridgeSettings.upnpresponseport}}</td>
<td>{{bridge.settings.upnpresponseport}}</td>
</tr>
<tr>
<td>vera.address</td>
<td>{{BridgeSettings.veraaddress}}</td>
<td>{{bridge.settings.veraaddress}}</td>
</tr>
<tr>
<td>harmony.address</td>
<td>{{BridgeSettings.harmonyaddress}}</td>
<td>{{bridge.settings.harmonyaddress}}</td>
</tr>
<tr>
<td>upnp.strict</td>
<td>{{BridgeSettings.upnpstrict}}</td>
<td>{{bridge.settings.upnpstrict}}</td>
</tr>
<tr>
<td>trace.upnp</td>
<td>{{BridgeSettings.traceupnp}}</td>
<td>{{bridge.settings.traceupnp}}</td>
</tr>
<tr>
<td>dev.mode</td>
<td>{{BridgeSettings.devmode}}</td>
<td>{{bridge.settings.devmode}}</td>
</tr>
<tr>
<td>nest.configured</td>
<td>{{bridge.settings.nestconfigured}}</td>
</tr>
</table>
</div>
</div>
<div class="panel panel-default backup">
<div class="panel-heading">
<h1 class="panel-title">Bridge Device DB Backup <a ng-click="toggleBk()"><span class={{imgBkUrl}} aria-hidden="true"></a></h1>
</div>
<div ng-if="visibleBk" class="animate-if" class="panel-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="backup-name">Backup File Name</label>
<div class="col-xs-8 col-sm-7">
<input id="backup-name" class="form-control" type="text"
ng-model="optionalbackupname" placeholder="Optional">
</div>
<button type="submit" class="btn btn-primary"
ng-click="backupDeviceDb(optionalbackupname)">Backup Device DB</button>
</div>
</form>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Filename</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="backup in bridge.backups">
<td>{{backup}}</td>
<td>
<button class="btn btn-danger" type="submit"
ng-click="restoreBackup(backup)">Restore</button>
<button class="btn btn-warning" type="submit"
ng-click="deleteBackup(backup)">Delete</button>
</td>
</tr>
</table>
</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.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
<li role="presentation" class="active"><a href="#/editdevice">Edit Device</a></li>
</ul>
@@ -12,9 +13,11 @@
<div class="panel-heading">
<h2 class="panel-title">Edit a device</h2>
</div>
<p class="text-muted">This screen allows the modification of many fields the bridge uses. Please use care when
updating these fields as you may break the settings used by the bridge to call a specific end point device.</p>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
</label>
@@ -23,8 +26,8 @@
<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">
Update Device</button>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
Update Bridge Device</button>
</div>
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-target">Target
@@ -34,6 +37,8 @@
<input type="text" class="form-control" id="device-target"
ng-model="device.targetDevice" placeholder="default">
</div>
<button class="btn btn-danger" ng-click="clearDevice()">
Clear Device</button>
</div>
<div class="form-group">
<div class="row">
@@ -47,6 +52,8 @@
<option value="veraScene">Vera Scene</option>
<option value="harmonyActivity">Harmony Activity</option>
<option value="harmonyButton">Harmony Button</option>
<option value="nestHomeAway">Nest Home Status</option>
<option value="nestThermoSet">Nest Thermostat</option>
</select>
</div>
</div>

View File

@@ -4,6 +4,7 @@
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation" class="active"><a href="#/editor">Manual Add</a></li>
</ul>
@@ -73,9 +74,11 @@
<div class="panel-heading">
<h2 class="panel-title">Add a new device</h2>
</div>
<p class="text-muted">This area allows you to create any http or udp call to an endpoint. You can use the default GET or select
the http verb type below and configure a payload for either on or off methods. Currently, https is not supported.</p>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()">
<form class="form-horizontal">
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
@@ -85,8 +88,8 @@
<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 Device</button>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
Add Bridge Device</button>
</div>
</div>
<div class="form-group">
@@ -98,6 +101,8 @@
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
<button class="btn btn-danger" ng-click="clearDevice()">
Clear Device</button>
</div>
</div>
<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 role="presentation" class="active"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
@@ -13,12 +14,14 @@
</div>
<ul class="list-group">
<li class="list-group-item">
<p class="text-muted">You can select a Harmony Activity and generate
the add activity box selections automatically.</p>
<p class="text-muted">For any Harmony Activity, use the action buttons to generate the device addition information below automatically.
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
The 'Already Configured Activities' list below will show what is already setup for your Harmony Hubs.</p>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th>
<a href="" ng-click="order('label')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
@@ -35,6 +38,7 @@
</tr>
</thead>
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | availableHarmonyActivityId | orderBy:predicate:reverse">
<td>{{$index+1}}</td>
<td>{{harmonyactivity.activity.label}}</td>
<td>{{harmonyactivity.activity.id}}</td>
<td>{{harmonyactivity.hub}}</td>
@@ -48,13 +52,14 @@
</li>
</ul>
<div class="panel-heading">
<h2 class="panel-title">Already Configured Activities <a ng-click="toggleActivities()"><span class={{imgActivitiesUrl}} aria-hidden="true"></a></h2>
<h2 class="panel-title">Already Configured Activities <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}} aria-hidden="true"></span></a></h2>
</div>
<ul ng-if="activitiesVisible" class="list-group">
<ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th>
<a href="" ng-click="order('label')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
@@ -71,6 +76,7 @@
</tr>
</thead>
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | unavailableHarmonyActivityId | orderBy:predicate:reverse">
<td>{{$index+1}}</td>
<td>{{harmonyactivity.activity.label}}</td>
<td>{{harmonyactivity.activity.id}}</td>
<td>{{harmonyactivity.hub}}</td>
@@ -83,11 +89,11 @@
</div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<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>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
</label>
@@ -96,8 +102,8 @@
<input type="text" class="form-control" id="device-name"
ng-model="device.name" placeholder="Device Name">
</div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Activity</button>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
Add Bridge Device</button>
</div>
<div class="form-group">
<div class="row">
@@ -108,6 +114,8 @@
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
<button class="btn btn-danger" ng-click="clearDevice()">
Clear Device</button>
</div>
</div>
<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 role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li role="presentation" class="active"><a href="#/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
@@ -13,12 +14,15 @@
</div>
<ul class="list-group">
<li class="list-group-item">
<p class="text-muted">You can select a Harmony Device and Button and generate
the add button box selections automatically.</p>
<p class="text-muted">For any Harmony Device and Buttons, use the build button to generate the configuration for this bridge device. You can add button presses by
selecting yoru devic and buttons and then selecting the Build Button. This will allow multiple button presses to be built for a given device.
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
The 'Already Configured Harmony Buttons' list below will show what is already setup for your Harmony Hubs.</p>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th>
<a href="" ng-click="order('label')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
@@ -37,27 +41,27 @@
</tr>
</thead>
<tr ng-repeat="harmonydevice in bridge.harmonydevices | orderBy:predicate:reverse">
<td>{{$index+1}}</td>
<td>{{harmonydevice.device.label}}</td>
<td>{{harmonydevice.device.id}}</td>
<td>{{harmonydevice.hub}}</td>
<td>
<select name="device-ctrlon" id="device-ctrlon" ng-model="devicectrlon">
<optgroup ng-repeat="ctrlon in harmonydevice.device.controlGroup" label="{{ctrlon.name}}">
<option ng-repeat="funcon in ctrlon.function">{{funcon.name}}</option>
<option ng-repeat="funcon in ctrlon.function" value="{{funcon.action}}">{{funcon.label}}</option>
</optgroup >
</select>
</td>
<td>
<select name="device-ctrloff" id="device-ctrloff" ng-model="devicectrloff">
<optgroup ng-repeat="ctrloff in harmonydevice.device.controlGroup" label="{{ctrloff.name}}">
<option ng-repeat="funcoff in ctrloff.function">{{funcoff.name}}</option>
<option ng-repeat="funcoff in ctrloff.function" value="{{funcoff.action}}">{{funcoff.label}}</option>
</optgroup >
</select>
</td>
<td>
<button class="btn btn-success" type="submit"
ng-click="buildButtonUrls(harmonydevice, devicectrlon, devicectrloff)">Generate
Button URLs</button>
ng-click="buildButtonUrls(harmonydevice, devicectrlon, devicectrloff)">Build A Button</button>
</td>
</tr>
</table>
@@ -71,6 +75,7 @@
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th>
<a href="" ng-click="order('name')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
@@ -91,6 +96,7 @@
</tr>
</thead>
<tr ng-repeat="device in bridge.devices | configuredButtons | orderBy:predicate:reverse">
<td>{{$index+1}}</td>
<td>{{device.name}}</td>
<td>{{device.id}}</td>
<td>{{device.targetDevice}}</td>
@@ -106,11 +112,11 @@
</div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<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>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
</label>
@@ -119,8 +125,8 @@
<input type="text" class="form-control" id="device-name"
ng-model="device.name" placeholder="Device Name">
</div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
Add Button</button>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
Add Bridge Device</button>
</div>
<div class="form-group">
<div class="row">
@@ -131,6 +137,8 @@
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
<button class="btn btn-danger" ng-click="clearDevice()">
Clear Device</button>
</div>
</div>
<div class="form-group">

View File

@@ -0,0 +1,156 @@
<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>Row</th>
<th>
<a href="" ng-click="order('name')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('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>{{$index+1}}</td>
<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="toggleButtons()"><span class={{imgButtonsUrl}} aria-hidden="true"></span></a></h2>
</div>
<ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th>
<a href="" ng-click="order('name')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('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>{{$index+1}}</td>
<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">
<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" ng-click="addDevice()">
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>
<button class="btn btn-danger" ng-click="clearDevice()">
Clear Device</button>
</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,17 +4,20 @@
<li role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading">
<h2 class="panel-title">Vera Device List</h2>
<h2 class="panel-title">Vera Device List ({{bridge.veradevices.length}})</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
<p class="text-muted">You can select a Vera device and generate
the add device box selections automatically.</p><p>Also, use this select menu for which type of dim
<p class="text-muted">For any Vera Device, use the action buttons to generate the device addition information below automatically.
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
The 'Already Configured Vera Devices' list below will show what is already setup for your Vera.</p>
<p>Also, use this select menu for which type of dim
control you would like to be generated:
<select name="device-dim-control" id="device-dim-control" ng-model="device_dim_control">
<option value="">none</option>
@@ -23,9 +26,13 @@
<option value="${intensity.math(X*1)}">Custom Math</option>
</select>
</p>
<p>Use the check boxes by the names to use the bulk addition feature. Select your items and dim control type if wanted, then click bulk add below.
Your items will be added with on and off or dim and off if selected with the name of the device from the Vera.
</p>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th>
<a href="" ng-click="order('name')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
@@ -41,15 +48,21 @@
<th>
<a href="" ng-click="order('room')">Room</a>
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('vera')">Vera</a>
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="veradevice in bridge.veradevices | availableVeraDeviceId | orderBy:predicate:reverse">
<td>{{veradevice.name}}</td>
<td>{{$index+1}}</td>
<td><input type="checkbox" name="bulk.devices[]" value="{{veradevice.id}}" ng-checked="bulk.devices.indexOf(veradevice.id) > -1" ng-click="toggleSelection(veradevice.id)"> {{veradevice.name}}</td>
<td>{{veradevice.id}}</td>
<td>{{veradevice.category}}</td>
<td>{{veradevice.room}}</td>
<td>{{veradevice.veraname}}</td>
<td>
<button class="btn btn-success" type="submit"
ng-click="buildDeviceUrls(veradevice, device_dim_control)">Generate
@@ -57,16 +70,21 @@
</td>
</tr>
</table>
<p>
<button class="btn btn-success" type="submit"
ng-click="bulkAddDevices(device_dim_control)">Bulk Add ({{bulk.devices.length}})</button>
</p>
</li>
</ul>
<div class="panel-heading">
<h2 class="panel-title">Already Configured Vera Devices <a ng-click="toggleDevices()"><span class={{imgDevicesUrl}} aria-hidden="true"></a></h2>
<h2 class="panel-title">Already Configured Vera Devices <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}} aria-hidden="true"></span></a></a></h2>
</div>
<ul ng-if="devicesVisible" class="list-group">
<ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th>
<a href="" ng-click="order('name')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
@@ -82,15 +100,21 @@
<th>
<a href="" ng-click="order('room')">Room</a>
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('vera')">Vera</a>
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="veradevice in bridge.veradevices | unavailableVeraDeviceId | orderBy:predicate:reverse">
<td>{{$index+1}}</td>
<td>{{veradevice.name}}</td>
<td>{{veradevice.id}}</td>
<td>{{veradevice.category}}</td>
<td>{{veradevice.room}}</td>
<td>{{veradevice.veraname}}</td>
<td>
<button class="btn btn-danger" type="submit"
ng-click="deleteDeviceByMapId(veradevice.id, 'veraDevice')">Delete</button>
@@ -102,11 +126,11 @@
</div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading">
<h2 class="panel-title">Add a Vera device</h2>
<h2 class="panel-title">Add Bridge Device for a Vera Device</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
</label>
@@ -115,8 +139,8 @@
<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 Device</button>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
Add Bridge Device</button>
</div>
<div class="form-group">
<div class="row">
@@ -127,6 +151,8 @@
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
<button class="btn btn-danger" ng-click="clearDevice()">
Clear Device</button>
</div>
<div class="form-group">
<div class="row">

View File

@@ -4,6 +4,7 @@
<li role="presentation" class="active"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
@@ -13,12 +14,14 @@
</div>
<ul class="list-group">
<li class="list-group-item">
<p class="text-muted">You can select a Vera scene and generate
the add scene box selections automatically.</p>
<p class="text-muted">For any Vera Scene, use the action buttons to generate the device addition information below automatically.
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
The 'Already Configured Vera Scenes' list below will show what is already setup for your Vera.</p>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th>
<a href="" ng-click="order('name')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
@@ -30,14 +33,20 @@
<th>
<a href="" ng-click="order('room')">Room</a>
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('vera')">Vera</a>
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="verascene in bridge.verascenes | availableVeraSceneId | orderBy:predicate:reverse">
<td>{{$index+1}}</td>
<td>{{verascene.name}}</td>
<td>{{verascene.id}}</td>
<td>{{verascene.room}}</td>
<td>{{verascene.veraname}}</td>
<td>
<button class="btn btn-success" type="submit"
ng-click="buildSceneUrls(verascene)">Generate
@@ -48,13 +57,14 @@
</li>
</ul>
<div class="panel-heading">
<h2 class="panel-title">Already Configured Vera Scenes <a ng-click="toggleScenes()"><span class={{imgScenesUrl}} aria-hidden="true"></a></h2>
<h2 class="panel-title">Already Configured Vera Scenes <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}} aria-hidden="true"></span></a></h2>
</div>
<ul ng-if="scenesVisible" class="list-group">
<ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th>
<a href="" ng-click="order('name')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
@@ -66,14 +76,20 @@
<th>
<a href="" ng-click="order('room')">Room</a>
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('vera')">Vera</a>
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="verascene in bridge.verascenes | unavailableVeraSceneId | orderBy:predicate:reverse">
<td>{{$index+1}}</td>
<td>{{verascene.name}}</td>
<td>{{verascene.id}}</td>
<td>{{verascene.room}}</td>
<td>{{verascene.veraname}}</td>
<td>
<button class="btn btn-danger" type="submit"
ng-click="deleteDeviceByMapId(verascene.id, 'veraScene')">Delete</button>
@@ -85,11 +101,11 @@
</div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading">
<h2 class="panel-title">Add a Vera scene</h2>
<h2 class="panel-title">Add a Bridge Device for a Vera scene</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal" ng-submit="addDevice()">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
</label>
@@ -98,8 +114,8 @@
<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 Scene</button>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
Add Bridge Device</button>
</div>
<div class="form-group">
<div class="row">
@@ -110,6 +126,8 @@
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
<button class="btn btn-danger" ng-click="clearDevice()">
Clear Device</button>
</div>
<div class="form-group">
<div class="row">