Compare commits

..

12 Commits

Author SHA1 Message Date
Admin
9c3d95f177 Finished implementing and testing state change and bug fixes from
testing

Fixes #44
Fixes #60
Fixes #61
Fixes #63
Fixes #66
Fixes #69
2016-03-21 16:37:13 -05:00
Admin
926a7f50dc Finished adding dim versus on, utf-8 and track state and return on api
calls.
2016-03-18 16:38:18 -05:00
Admin
ad820a68c9 Updated C/F setting. Started to implement UTF-8 2016-03-16 17:13:03 -05:00
Admin
73f0f766f7 Added more logging and updated nest controller version to fix issues
with range temp setting in nest.
2016-03-09 16:45:21 -06:00
Admin
113ce0ca59 Updated logging with new nest module. 2016-03-08 16:40:32 -06:00
bwssystems
a5860417c1 Update null numberoflogmessages 2016-02-27 12:56:42 -06:00
bwssystems
48af3d84a2 Finished configurable logging view and control.
Fixes #51
Fixes #52
2016-02-27 12:48:19 -06:00
Admin
8ce0483e54 Adding log level control 2016-02-26 16:38:41 -06:00
Admin
1a2024d92b Tables in all views updated to scrollable/sortable. Refactored
controllers for views. debugging logging message return for messages
with invalid text for json parsing.
2016-02-25 16:42:35 -06:00
Admin
8198919a27 playing with scrollable table for log 2016-02-24 16:46:52 -06:00
Admin
614734a2aa Adding logs window. 2016-02-23 16:52:00 -06:00
Admin
58fccb1fa7 fixed about issue. Looking into log view and changed log impl to logback
within slf4j.
2016-02-22 16:38:57 -06:00
41 changed files with 1695 additions and 536 deletions

View File

@@ -531,6 +531,30 @@ A response to a successful PUT request contains confirmation of the arguments pa
{"success":{"/lights/1/state/on":true}}, {"success":{"/lights/1/state/on":true}},
] ]
``` ```
### Update bridge internal light state
Allows the user to set the internal state of the light on and off, modify the brightness. This is not a HUE API call and is special to the bridge as it keeps track of the state changes to the light from the api. It is intended to allow you to sync the bridge state with your HA system state.
```
PUT http://host:port/api/<username>/lights/<id>/bridgeupdatestate
```
#### Body arguments
Name | Type | Description
-----|-------|-------------
on | bool | On/Off state of the light. On=true, Off=false. Optional
bri | uint8 | The brightness value to set the light to. Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. e.g. "brightness": 60 will set the light to a specific brightness. Optional
```
{
"on": true,
"bri": 200
}
```
#### Response
A response to a successful PUT request contains confirmation of the arguments passed in. Note: If the new value is too large to return in the response due to internal memory constraints then a value of "Updated." is returned.
```
[
{"success":{"/lights/1/state/bri":200}},
{"success":{"/lights/1/state/on":true}},
]
```
### Create user ### Create user
Emulates creating a new user. The link button state on the HA Bridge is always on for the purpose of responding to this request. No actual user is saved as this is for compatibility. Emulates creating a new user. The link button state on the HA Bridge is always on for the purpose of responding to this request. No actual user is saved as this is for compatibility.
``` ```

40
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>com.bwssystems.HABridge</groupId> <groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId> <artifactId>ha-bridge</artifactId>
<version>1.4.0</version> <version>1.4.2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>HA Bridge</name> <name>HA Bridge</name>
@@ -29,16 +29,42 @@
<groupId>com.github.bwssytems</groupId> <groupId>com.github.bwssytems</groupId>
<artifactId>harmony-java-client</artifactId> <artifactId>harmony-java-client</artifactId>
<version>1.0.8</version> <version>1.0.8</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.bwssytems</groupId> <groupId>com.github.bwssytems</groupId>
<artifactId>nest-controller</artifactId> <artifactId>nest-controller</artifactId>
<version>1.0.4</version> <version>1.0.8</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.sparkjava</groupId> <groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId> <artifactId>spark-core</artifactId>
<version>2.3</version> <version>2.3</version>
<exclusions>
<exclusion>
<artifactId>slf4j-simple</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
@@ -52,13 +78,13 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId> <artifactId>slf4j-api</artifactId>
<version>1.7.5</version> <version>1.7.5</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>log4j-over-slf4j</artifactId> <artifactId>logback-classic</artifactId>
<version>1.7.5</version> <version>1.1.5</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
@@ -138,7 +164,7 @@
<artifact>*:*</artifact> <artifact>*:*</artifact>
</filter> </filter>
<filter> <filter>
<artifact>org.slf4j:*</artifact> <artifact>org.slf4j:slf4j-api</artifact>
<includes> <includes>
<include>**</include> <include>**</include>
</includes> </includes>

View File

@@ -15,6 +15,8 @@ import org.apache.http.conn.util.InetAddressUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.bwssystems.util.BackupHandler;
import com.bwssystems.util.JsonTransformer;
import com.google.gson.Gson; import com.google.gson.Gson;
public class BridgeSettings extends BackupHandler { public class BridgeSettings extends BackupHandler {
@@ -38,12 +40,14 @@ public class BridgeSettings extends BackupHandler {
String addressString = null; String addressString = null;
String theVeraAddress = null; String theVeraAddress = null;
String theHarmonyAddress = null; String theHarmonyAddress = null;
String configFileProperty = System.getProperty("config.file"); String configFileProperty = System.getProperty("config.file");
if(configFileProperty == null) { if(configFileProperty == null) {
Path filePath = Paths.get(Configuration.CONFIG_FILE); Path filePath = Paths.get(Configuration.CONFIG_FILE);
if(Files.exists(filePath) && Files.isReadable(filePath)) if(Files.exists(filePath) && Files.isReadable(filePath))
configFileProperty = Configuration.CONFIG_FILE; configFileProperty = Configuration.CONFIG_FILE;
} }
if(configFileProperty != null) if(configFileProperty != null)
{ {
log.info("reading from config file: " + configFileProperty); log.info("reading from config file: " + configFileProperty);
@@ -53,6 +57,8 @@ public class BridgeSettings extends BackupHandler {
else else
{ {
log.info("reading from system properties"); log.info("reading from system properties");
theBridgeSettings.setNumberoflogmessages(Configuration.NUMBER_OF_LOG_MESSAGES);
theBridgeSettings.setFarenheit(true);
theBridgeSettings.setConfigfile(Configuration.CONFIG_FILE); theBridgeSettings.setConfigfile(Configuration.CONFIG_FILE);
theBridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DEFAULT_WEB_PORT)); theBridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DEFAULT_WEB_PORT));
theBridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address")); theBridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address"));
@@ -139,6 +145,9 @@ public class BridgeSettings extends BackupHandler {
if(theBridgeSettings.getUpnpDeviceDb() == null) if(theBridgeSettings.getUpnpDeviceDb() == null)
theBridgeSettings.setUpnpDeviceDb(Configuration.DEVICE_DB_DIRECTORY); theBridgeSettings.setUpnpDeviceDb(Configuration.DEVICE_DB_DIRECTORY);
if(theBridgeSettings.getNumberoflogmessages() == null)
theBridgeSettings.setNumberoflogmessages(Configuration.NUMBER_OF_LOG_MESSAGES);
if(theBridgeSettings.getButtonsleep() <= 0) if(theBridgeSettings.getButtonsleep() <= 0)
theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP)); theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP));
@@ -176,6 +185,8 @@ public class BridgeSettings extends BackupHandler {
theBridgeSettings.setVeraconfigured(aBridgeSettings.isValidVera()); theBridgeSettings.setVeraconfigured(aBridgeSettings.isValidVera());
theBridgeSettings.setHarmonyconfigured(aBridgeSettings.isValidHarmony()); theBridgeSettings.setHarmonyconfigured(aBridgeSettings.isValidHarmony());
theBridgeSettings.setNestConfigured(aBridgeSettings.isValidNest()); theBridgeSettings.setNestConfigured(aBridgeSettings.isValidNest());
theBridgeSettings.setNumberoflogmessages(aBridgeSettings.getNumberoflogmessages());
theBridgeSettings.setFarenheit(aBridgeSettings.isFarenheit());
} }
public void save(BridgeSettingsDescriptor newBridgeSettings) { public void save(BridgeSettingsDescriptor newBridgeSettings) {

View File

@@ -19,7 +19,9 @@ public class BridgeSettingsDescriptor {
private boolean veraconfigured; private boolean veraconfigured;
private boolean harmonyconfigured; private boolean harmonyconfigured;
private boolean nestconfigured; private boolean nestconfigured;
private boolean farenheit;
private String configfile; private String configfile;
private Integer numberoflogmessages;
public BridgeSettingsDescriptor() { public BridgeSettingsDescriptor() {
super(); super();
@@ -28,6 +30,7 @@ public class BridgeSettingsDescriptor {
this.nestconfigured = false; this.nestconfigured = false;
this.veraconfigured = false; this.veraconfigured = false;
this.harmonyconfigured = false; this.harmonyconfigured = false;
this.farenheit = true;
} }
public String getUpnpConfigAddress() { public String getUpnpConfigAddress() {
return upnpconfigaddress; return upnpconfigaddress;
@@ -137,8 +140,20 @@ public class BridgeSettingsDescriptor {
public void setConfigfile(String configfile) { public void setConfigfile(String configfile) {
this.configfile = configfile; this.configfile = configfile;
} }
public Integer getNumberoflogmessages() {
return numberoflogmessages;
}
public void setNumberoflogmessages(Integer numberoflogmessages) {
this.numberoflogmessages = numberoflogmessages;
}
public boolean isFarenheit() {
return farenheit;
}
public void setFarenheit(boolean farenheit) {
this.farenheit = farenheit;
}
public Boolean isValidVera() { public Boolean isValidVera() {
if(this.getVeraAddress() == null) if(this.getVeraAddress() == null || this.getVeraAddress().getDevices().size() <= 0)
return false; return false;
List<NamedIP> devicesList = this.getVeraAddress().getDevices(); List<NamedIP> devicesList = this.getVeraAddress().getDevices();
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS)) if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))

View File

@@ -11,4 +11,5 @@ public class Configuration {
public static final int UPNP_DISCOVERY_PORT = 1900; public static final int UPNP_DISCOVERY_PORT = 1900;
public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250"; public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250";
public static final String CONFIG_FILE = "data/habridge.config"; public static final String CONFIG_FILE = "data/habridge.config";
public static final int NUMBER_OF_LOG_MESSAGES = 512;
} }

View File

@@ -9,28 +9,52 @@ import java.io.IOException;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.MulticastSocket; import java.net.MulticastSocket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.dao.BackupFilename; import com.bwssystems.HABridge.dao.BackupFilename;
import com.bwssystems.logservices.LoggerInfo;
import com.bwssystems.logservices.LoggingForm;
import com.bwssystems.logservices.LoggingManager;
import com.bwssystems.util.JsonFreeTextStringFormatter;
import com.bwssystems.util.JsonTransformer;
import com.google.gson.Gson; import com.google.gson.Gson;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.read.CyclicBufferAppender;
public class SystemControl { public class SystemControl {
private static final Logger log = LoggerFactory.getLogger(SystemControl.class); private static final Logger log = LoggerFactory.getLogger(SystemControl.class);
public static final String CYCLIC_BUFFER_APPENDER_NAME = "CYCLIC";
private LoggerContext lc;
private static final String SYSTEM_CONTEXT = "/system"; private static final String SYSTEM_CONTEXT = "/system";
private BridgeSettings bridgeSettings; private BridgeSettings bridgeSettings;
private Version version; private Version version;
private CyclicBufferAppender<ILoggingEvent> cyclicBufferAppender;
private DateFormat dateFormat;
private LoggingManager theLogServiceMgr;
public SystemControl(BridgeSettings theBridgeSettings, Version theVersion) { public SystemControl(BridgeSettings theBridgeSettings, Version theVersion) {
this.bridgeSettings = theBridgeSettings; this.bridgeSettings = theBridgeSettings;
this.version = theVersion; this.version = theVersion;
this.lc = (LoggerContext) LoggerFactory.getILoggerFactory();
this.dateFormat = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss.SSS");
reacquireCBA();
theLogServiceMgr = new LoggingManager();
theLogServiceMgr.init();
} }
// This function sets up the sparkjava rest calls for the hue api // This function sets up the sparkjava rest calls for the hue api
public void setupServer() { public void setupServer() {
log.info("Hue emulator service started...."); log.info("System control service started....");
// http://ip_address:port/system/habridge/version gets the version of this bridge instance // http://ip_address:port/system/habridge/version gets the version of this bridge instance
get (SYSTEM_CONTEXT + "/habridge/version", "application/json", (request, response) -> { get (SYSTEM_CONTEXT + "/habridge/version", "application/json", (request, response) -> {
log.debug("Get HA Bridge version: v" + version.getVersion()); log.debug("Get HA Bridge version: v" + version.getVersion());
@@ -38,7 +62,68 @@ public class SystemControl {
return "{\"version\":\"" + version.getVersion() + "\"}"; return "{\"version\":\"" + version.getVersion() + "\"}";
}); });
// http://ip_address:port/system/settings which returns the bridge configuration settings // http://ip_address:port/system/logmsgs gets the log messages for the bridge
get (SYSTEM_CONTEXT + "/logmsgs", "application/json", (request, response) -> {
log.debug("Get logmsgs.");
response.status(HttpStatus.SC_OK);
String logMsgs;
int count = -1;
if(cyclicBufferAppender == null)
reacquireCBA();
if (cyclicBufferAppender != null) {
count = cyclicBufferAppender.getLength();
}
logMsgs = "[";
if (count == -1) {
logMsgs = logMsgs + "{\"message\":\"Failed to locate CyclicBuffer\"}";
} else if (count == 0) {
logMsgs = logMsgs + "{\"message\":\"No logging events to display\"}";
} else {
LoggingEvent le;
for (int i = 0; i < count; i++) {
le = (LoggingEvent) cyclicBufferAppender.get(i);
logMsgs = logMsgs + ( i > 0?",{":"{") + "\"time\":\"" + dateFormat.format(le.getTimeStamp()) + "\",\"level\":\"" + le.getLevel().levelStr + "\",\"component\":\"" + le.getLoggerName() + "\",\"message\":\"" + JsonFreeTextStringFormatter.forJSON(le.getFormattedMessage()) + "\"}";
}
}
logMsgs = logMsgs + "]";
response.status(200);
return logMsgs;
});
// http://ip_address:port/system/logmgmt/loggers gets the logger info for the bridge
get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", "application/json", (request, response) -> {
log.debug("Get loggers info with showAll argument: " + request.params(":all"));
Boolean showAll = false;
if(request.params(":all").equals("true"))
showAll = true;
theLogServiceMgr.setShowAll(showAll);
theLogServiceMgr.init();
response.status(200);
return theLogServiceMgr.getConfiguredLoggers();
}, new JsonTransformer());
// http://ip_address:port/system/logmgmt/update CORS request
options(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/logmgmt/update which changes logging parameters for the process
put(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
log.debug("update loggers: " + request.body());
response.status(200);
LoggerInfo updateLoggers[];
updateLoggers = new Gson().fromJson(request.body(), LoggerInfo[].class);
LoggingForm theModel = theLogServiceMgr.getModel();
theModel.setUpdatedLoggers(Arrays.asList(updateLoggers));
theLogServiceMgr.updateLogLevels();
return theLogServiceMgr.getConfiguredLoggers();
}, new JsonTransformer());
// http://ip_address:port/system/settings which returns the bridge configuration settings
get(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> { get(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
log.debug("bridge settings requested from " + request.ip()); log.debug("bridge settings requested from " + request.ip());
@@ -155,10 +240,16 @@ public class SystemControl {
} }
else else
log.warn("No filename given for restore backup."); log.warn("No filename given for restore backup.");
return null; return bridgeSettings.getBridgeSettingsDescriptor();
}, new JsonTransformer()); }, new JsonTransformer());
} }
void reacquireCBA() {
cyclicBufferAppender = (CyclicBufferAppender<ILoggingEvent>) lc.getLogger(
Logger.ROOT_LOGGER_NAME).getAppender(CYCLIC_BUFFER_APPENDER_NAME);
cyclicBufferAppender.setMaxSize(bridgeSettings.getBridgeSettingsDescriptor().getNumberoflogmessages());
}
protected void pingListener() { protected void pingListener() {
try { try {
byte[] buf = new byte[256]; byte[] buf = new byte[256];

View File

@@ -1,5 +1,7 @@
package com.bwssystems.HABridge.api.hue; package com.bwssystems.HABridge.api.hue;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
/** /**
* Created by arm on 4/14/15. * Created by arm on 4/14/15.
*/ */
@@ -68,18 +70,18 @@ public class DeviceResponse {
this.swversion = swversion; this.swversion = swversion;
} }
public static DeviceResponse createResponse(String name, String id){ public static DeviceResponse createResponse(DeviceDescriptor device){
DeviceState deviceState = new DeviceState(); DeviceState deviceState = new DeviceState();
DeviceResponse response = new DeviceResponse(); DeviceResponse response = new DeviceResponse();
response.setState(deviceState); response.setState(deviceState);
deviceState.setOn(false); deviceState.setOn(device.getDeviceState());
deviceState.setReachable(true); deviceState.setReachable(true);
deviceState.setEffect("none"); deviceState.setEffect("none");
deviceState.setAlert("none"); deviceState.setAlert("none");
deviceState.setBri(254); deviceState.setBri(device.getDeviceSetValue());
response.setName(name); response.setName(device.getName());
response.setUniqueid(id); response.setUniqueid(device.getId());
response.setManufacturername("Philips"); response.setManufacturername("Philips");
response.setType("Dimmable light"); response.setType("Dimmable light");
response.setModelid("LWB004"); response.setModelid("LWB004");

View File

@@ -6,7 +6,7 @@ package com.bwssystems.HABridge.api.hue;
*/ */
public class DeviceState { public class DeviceState {
private boolean on; private boolean on;
private int bri = 255; private int bri = 0;
private String effect; private String effect;
private String alert; private String alert;
private boolean reachable; private boolean reachable;

View File

@@ -1,21 +1,55 @@
package com.bwssystems.HABridge.dao; package com.bwssystems.HABridge.dao;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/* /*
* Object to handle the device configuration * Object to handle the device configuration
*/ */
public class DeviceDescriptor{ public class DeviceDescriptor{
@SerializedName("id")
@Expose
private String id; private String id;
@SerializedName("name")
@Expose
private String name; private String name;
@SerializedName("mapId")
@Expose
private String mapId; private String mapId;
@SerializedName("mapType")
@Expose
private String mapType; private String mapType;
@SerializedName("deviceType")
@Expose
private String deviceType; private String deviceType;
@SerializedName("targetDevice")
@Expose
private String targetDevice; private String targetDevice;
@SerializedName("offUrl")
@Expose
private String offUrl; private String offUrl;
@SerializedName("dimUrl")
@Expose
private String dimUrl;
@SerializedName("onUrl")
@Expose
private String onUrl; private String onUrl;
@SerializedName("httpVerb")
@Expose
private String httpVerb; private String httpVerb;
@SerializedName("contentType")
@Expose
private String contentType; private String contentType;
@SerializedName("contentBody")
@Expose
private String contentBody; private String contentBody;
@SerializedName("contentBodyOff")
@Expose
private String contentBodyOff; private String contentBodyOff;
private boolean deviceState;
private int deviceSetValue;
public String getName() { public String getName() {
return name; return name;
} }
@@ -64,6 +98,14 @@ public class DeviceDescriptor{
this.offUrl = offUrl; this.offUrl = offUrl;
} }
public String getDimUrl() {
return dimUrl;
}
public void setDimUrl(String dimUrl) {
this.dimUrl = dimUrl;
}
public String getOnUrl() { public String getOnUrl() {
return onUrl; return onUrl;
} }
@@ -112,5 +154,21 @@ public class DeviceDescriptor{
this.contentBodyOff = contentBodyOff; this.contentBodyOff = contentBodyOff;
} }
public boolean getDeviceState() {
return deviceState;
}
public void setDeviceState(boolean deviceState) {
this.deviceState = deviceState;
}
public int getDeviceSetValue() {
return deviceSetValue;
}
public void setDeviceSetValue(int deviceSetValue) {
this.deviceSetValue = deviceSetValue;
}
} }

View File

@@ -2,7 +2,6 @@ package com.bwssystems.HABridge.dao;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@@ -15,25 +14,30 @@ import java.util.Random;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BackupHandler;
import com.bwssystems.HABridge.JsonTransformer;
import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.google.gson.stream.JsonReader; import com.bwssystems.util.BackupHandler;
import com.bwssystems.util.JsonTransformer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.List; import java.util.List;
import java.util.ListIterator;
/* /*
* This is an in memory list to manage the configured devices and saves the list as a JSON string to a file for later * This is an in memory list to manage the configured devices and saves the list as a JSON string to a file for later
* loading. * loading.
*/ */
public class DeviceRepository extends BackupHandler { public class DeviceRepository extends BackupHandler {
Map<String, DeviceDescriptor> devices; private Map<String, DeviceDescriptor> devices;
Path repositoryPath; private Path repositoryPath;
final Random random = new Random(); private Gson gson;
final Logger log = LoggerFactory.getLogger(DeviceRepository.class); final private Random random = new Random();
private Logger log = LoggerFactory.getLogger(DeviceRepository.class);
public DeviceRepository(String deviceDb) { public DeviceRepository(String deviceDb) {
super(); super();
gson =
new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
repositoryPath = null; repositoryPath = null;
repositoryPath = Paths.get(deviceDb); repositoryPath = Paths.get(deviceDb);
setupParams(repositoryPath, ".bk", "device.db-"); setupParams(repositoryPath, ".bk", "device.db-");
@@ -50,15 +54,11 @@ public class DeviceRepository extends BackupHandler {
if(jsonContent != null) if(jsonContent != null)
{ {
List<DeviceDescriptor> list = readJsonStream(jsonContent); DeviceDescriptor list[] = gson.fromJson(jsonContent, DeviceDescriptor[].class);
ListIterator<DeviceDescriptor> theIterator = list.listIterator(); for(int i = 0; i < list.length; i++) {
DeviceDescriptor theDevice = null; put(list[i].getId(), list[i]);
while (theIterator.hasNext()) {
theDevice = theIterator.next();
put(theDevice.getId(), theDevice);
} }
} }
} }
public List<DeviceDescriptor> findAll() { public List<DeviceDescriptor> findAll() {
@@ -82,15 +82,14 @@ public class DeviceRepository extends BackupHandler {
public void save(DeviceDescriptor[] descriptors) { public void save(DeviceDescriptor[] descriptors) {
String theNames = ""; String theNames = "";
for(int i = 0; i < descriptors.length; i++) { for(int i = 0; i < descriptors.length; i++) {
if(descriptors[i].getId() != null) if(descriptors[i].getId() != null && descriptors[i].getId().length() > 0)
devices.remove(descriptors[i].getId()); devices.remove(descriptors[i].getId());
else else
descriptors[i].setId(String.valueOf(random.nextInt(Integer.MAX_VALUE))); descriptors[i].setId(String.valueOf(random.nextInt(Integer.MAX_VALUE)));
put(descriptors[i].getId(), descriptors[i]); put(descriptors[i].getId(), descriptors[i]);
theNames = theNames + " " + descriptors[i].getName() + ", "; theNames = theNames + " " + descriptors[i].getName() + ", ";
} }
JsonTransformer aRenderer = new JsonTransformer(); String jsonValue = gson.toJson(findAll());
String jsonValue = aRenderer.render(findAll());
repositoryWriter(jsonValue, repositoryPath); repositoryWriter(jsonValue, repositoryPath);
log.debug("Save device(s): " + theNames); log.debug("Save device(s): " + theNames);
} }
@@ -153,82 +152,4 @@ public class DeviceRepository extends BackupHandler {
return content; return content;
} }
private List<DeviceDescriptor> readJsonStream(String context) {
JsonReader reader = new JsonReader(new StringReader(context));
List<DeviceDescriptor> theDescriptors = null;
try {
theDescriptors = readDescriptorArray(reader);
} catch (IOException e) {
log.error("Error reading json array: " + context + " message: " + e.getMessage(), e);
} finally {
try {
reader.close();
} catch (IOException e) {
log.error("Error closing json reader: " + context + " message: " + e.getMessage(), e);
}
}
return theDescriptors;
}
public List<DeviceDescriptor> readDescriptorArray(JsonReader reader) throws IOException {
List<DeviceDescriptor> descriptors = new ArrayList<DeviceDescriptor>();
reader.beginArray();
while (reader.hasNext()) {
descriptors.add(readDescriptor(reader));
}
reader.endArray();
return descriptors;
}
public DeviceDescriptor readDescriptor(JsonReader reader) throws IOException {
DeviceDescriptor deviceEntry = new DeviceDescriptor();
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("id")) {
deviceEntry.setId(reader.nextString());
log.debug("Read a Device - device json id: " + deviceEntry.getId());
} else if (name.equals("name")) {
deviceEntry.setName(reader.nextString());
log.debug("Read a Device - device json name: " + deviceEntry.getName());
} else if (name.equals("mapType")) {
deviceEntry.setMapType(reader.nextString());
log.debug("Read a Device - device json name: " + deviceEntry.getMapType());
} else if (name.equals("mapId")) {
deviceEntry.setMapId(reader.nextString());
log.debug("Read a Device - device json name: " + deviceEntry.getMapId());
} else if (name.equals("deviceType")) {
deviceEntry.setDeviceType(reader.nextString());
log.debug("Read a Device - device json type:" + deviceEntry.getDeviceType());
} else if (name.equals("targetDevice")) {
deviceEntry.setTargetDevice(reader.nextString());
log.debug("Read a Device - device json type:" + deviceEntry.getTargetDevice());
} else if (name.equals("offUrl")) {
deviceEntry.setOffUrl(reader.nextString());
log.debug("Read a Device - device json off URL:" + deviceEntry.getOffUrl());
} else if (name.equals("onUrl")) {
deviceEntry.setOnUrl(reader.nextString());
log.debug("Read a Device - device json on URL:" + deviceEntry.getOnUrl());
} else if (name.equals("httpVerb")) {
deviceEntry.setHttpVerb(reader.nextString());
log.debug("Read a Device - device json httpVerb:" + deviceEntry.getHttpVerb());
} else if (name.equals("contentType")) {
deviceEntry.setContentType(reader.nextString());
log.debug("Read a Device - device json contentType:" + deviceEntry.getContentType());
} else if (name.equals("contentBody")) {
deviceEntry.setContentBody(reader.nextString());
log.debug("Read a Device - device json contentBody:" + deviceEntry.getContentBody());
} else if (name.equals("contentBodyOff")) {
deviceEntry.setContentBodyOff(reader.nextString());
log.debug("Read a Device - device json contentBodyOff:" + deviceEntry.getContentBodyOff());
} else {
reader.skipValue();
}
}
reader.endObject();
return deviceEntry;
}
} }

View File

@@ -16,7 +16,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor; import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.JsonTransformer;
import com.bwssystems.HABridge.dao.BackupFilename; import com.bwssystems.HABridge.dao.BackupFilename;
import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.dao.DeviceRepository; import com.bwssystems.HABridge.dao.DeviceRepository;
@@ -25,6 +24,7 @@ import com.bwssystems.NestBridge.NestHome;
import com.bwssystems.harmony.HarmonyHome; import com.bwssystems.harmony.HarmonyHome;
import com.bwssystems.luupRequests.Device; import com.bwssystems.luupRequests.Device;
import com.bwssystems.luupRequests.Scene; import com.bwssystems.luupRequests.Scene;
import com.bwssystems.util.JsonTransformer;
import com.bwssystems.vera.VeraHome; import com.bwssystems.vera.VeraHome;
import com.google.gson.Gson; import com.google.gson.Gson;
@@ -34,7 +34,6 @@ import com.google.gson.Gson;
public class DeviceResource { public class DeviceResource {
private static final String API_CONTEXT = "/api/devices"; private static final String API_CONTEXT = "/api/devices";
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class); private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
private DeviceRepository deviceRepository; private DeviceRepository deviceRepository;
private VeraHome veraHome; private VeraHome veraHome;
private HarmonyHome myHarmonyHome; private HarmonyHome myHarmonyHome;
@@ -58,7 +57,6 @@ public class DeviceResource {
this.nestHome = aNestHome; this.nestHome = aNestHome;
else else
this.nestHome = null; this.nestHome = null;
setupEndpoints(); setupEndpoints();
} }
@@ -117,35 +115,24 @@ public class DeviceResource {
put (API_CONTEXT + "/:id", "application/json", (request, response) -> { put (API_CONTEXT + "/:id", "application/json", (request, response) -> {
log.debug("Edit a Device - request body: " + request.body()); log.debug("Edit a Device - request body: " + request.body());
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class); DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
DeviceDescriptor deviceEntry = deviceRepository.findOne(request.params(":id")); if(deviceRepository.findOne(request.params(":id")) == null){
if(deviceEntry == null){ log.debug("Could not save an edited device, Device Id not found: " + request.params(":id"));
log.debug("Could not save an edited Device Id: " + request.params(":id"));
response.status(HttpStatus.SC_BAD_REQUEST); response.status(HttpStatus.SC_BAD_REQUEST);
return new ErrorMessage("Could not save an edited Device Id: " + request.params(":id") + " "); return new ErrorMessage("Could not save an edited device, Device Id not found: " + request.params(":id") + " ");
} }
else else
{ {
log.debug("Saving an edited Device: " + deviceEntry.getName()); log.debug("Saving an edited Device: " + device.getName());
deviceEntry.setName(device.getName());
if (device.getDeviceType() != null) if (device.getDeviceType() != null)
deviceEntry.setDeviceType(device.getDeviceType()); device.setDeviceType(device.getDeviceType());
deviceEntry.setMapId(device.getMapId());
deviceEntry.setMapType(device.getMapType());
deviceEntry.setTargetDevice(device.getTargetDevice());
deviceEntry.setOnUrl(device.getOnUrl());
deviceEntry.setOffUrl(device.getOffUrl());
deviceEntry.setHttpVerb(device.getHttpVerb());
deviceEntry.setContentType(device.getContentType());
deviceEntry.setContentBody(device.getContentBody());
deviceEntry.setContentBodyOff(device.getContentBodyOff());
DeviceDescriptor[] theDevices = new DeviceDescriptor[1]; DeviceDescriptor[] theDevices = new DeviceDescriptor[1];
theDevices[0] = deviceEntry; theDevices[0] = device;
deviceRepository.save(theDevices); deviceRepository.save(theDevices);
response.status(HttpStatus.SC_OK); response.status(HttpStatus.SC_OK);
} }
return deviceEntry; return device;
}, new JsonTransformer()); }, new JsonTransformer());
get (API_CONTEXT, "application/json", (request, response) -> { get (API_CONTEXT, "application/json", (request, response) -> {

View File

@@ -1,7 +1,6 @@
package com.bwssystems.HABridge.hue; package com.bwssystems.HABridge.hue;
import com.bwssystems.HABridge.BridgeSettingsDescriptor; import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.JsonTransformer;
import com.bwssystems.HABridge.api.UserCreateRequest; import com.bwssystems.HABridge.api.UserCreateRequest;
import com.bwssystems.HABridge.api.hue.DeviceResponse; import com.bwssystems.HABridge.api.hue.DeviceResponse;
import com.bwssystems.HABridge.api.hue.DeviceState; import com.bwssystems.HABridge.api.hue.DeviceState;
@@ -14,6 +13,7 @@ import com.bwssystems.harmony.HarmonyHandler;
import com.bwssystems.harmony.HarmonyHome; import com.bwssystems.harmony.HarmonyHome;
import com.bwssystems.harmony.RunActivity; import com.bwssystems.harmony.RunActivity;
import com.bwssystems.nest.controller.Nest; import com.bwssystems.nest.controller.Nest;
import com.bwssystems.util.JsonTransformer;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.google.gson.Gson;
@@ -101,7 +101,7 @@ public class HueMulator {
List<DeviceDescriptor> deviceList = repository.findAll(); List<DeviceDescriptor> deviceList = repository.findAll();
Map<String, DeviceResponse> deviceResponseMap = new HashMap<>(); Map<String, DeviceResponse> deviceResponseMap = new HashMap<>();
for (DeviceDescriptor device : deviceList) { for (DeviceDescriptor device : deviceList) {
DeviceResponse deviceResponse = DeviceResponse.createResponse(device.getName(), device.getId()); DeviceResponse deviceResponse = DeviceResponse.createResponse(device);
deviceResponseMap.put(device.getId(), deviceResponse); deviceResponseMap.put(device.getId(), deviceResponse);
} }
response.type("application/json; charset=utf-8"); response.type("application/json; charset=utf-8");
@@ -225,7 +225,7 @@ public class HueMulator {
Map<String, DeviceResponse> deviceList = new HashMap<>(); Map<String, DeviceResponse> deviceList = new HashMap<>();
descriptorList.forEach(descriptor -> { descriptorList.forEach(descriptor -> {
DeviceResponse deviceResponse = DeviceResponse.createResponse(descriptor.getName(), descriptor.getId()); DeviceResponse deviceResponse = DeviceResponse.createResponse(descriptor);
deviceList.put(descriptor.getId(), deviceResponse); deviceList.put(descriptor.getId(), deviceResponse);
} }
); );
@@ -249,13 +249,68 @@ public class HueMulator {
} else { } else {
log.debug("found device named: " + device.getName()); log.debug("found device named: " + device.getName());
} }
DeviceResponse lightResponse = DeviceResponse.createResponse(device.getName(), device.getId()); DeviceResponse lightResponse = DeviceResponse.createResponse(device);
response.type("application/json; charset=utf-8"); response.type("application/json; charset=utf-8");
response.status(HttpStatus.SC_OK); response.status(HttpStatus.SC_OK);
return lightResponse; return lightResponse;
}, new JsonTransformer()); }, new JsonTransformer());
// http://ip_address:port/api/:userid/lights/:id/bridgeupdatestate CORS request
options(HUE_CONTEXT + "/:userid/lights/:id/bridgeupdatestate", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/api/{userId}/lights/{lightId}/bridgeupdatestate uses json object to update the internal bridge lights state.
// THIS IS NOT A HUE API CALL... It is for state management if so desired.
put(HUE_CONTEXT + "/:userid/lights/:id/bridgeupdatestate", "application/json", (request, response) -> {
String userId = request.params(":userid");
String lightId = request.params(":id");
String responseString = null;
DeviceState state = null;
log.debug("Update state requested: " + userId + " from " + request.ip() + " body: " + request.body());
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.type("application/json; charset=utf-8");
response.status(HttpStatus.SC_OK);
try {
state = mapper.readValue(request.body(), DeviceState.class);
} catch (IOException e) {
log.warn("Object mapper barfed on input of body.", e);
responseString = "[{\"error\":{\"type\": 2, \"address\": \"/lights/" + lightId + "\",\"description\": \"Object mapper barfed on input of body.\"}}]";
return responseString;
}
DeviceDescriptor device = repository.findOne(lightId);
if (device == null) {
log.warn("Could not find device: " + lightId + " for hue state change request: " + userId + " from " + request.ip() + " body: " + request.body());
responseString = "[{\"error\":{\"type\": 3, \"address\": \"/lights/" + lightId + "\",\"description\": \"Could not find device\", \"resource\": \"/lights/" + lightId + "\"}}]";
return responseString;
}
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":";
if(request.body().contains("bri"))
{
responseString = responseString + "true}},{\"success\":{\"/lights/" + lightId + "/state/bri\":" + state.getBri() + "}}]";
}
else
{
if (state.isOn()) {
responseString = responseString + "true}}]";
state.setBri(255);
} else if (request.body().contains("false")) {
responseString = responseString + "false}}]";
state.setBri(0);
}
}
device.setDeviceSetValue(state.getBri());
device.setDeviceState(state.isOn());
return responseString;
});
// http://ip_address:port/api/:userid/lights/:id/state CORS request // http://ip_address:port/api/:userid/lights/:id/state CORS request
options(HUE_CONTEXT + "/:userid/lights/:id/state", "application/json", (request, response) -> { options(HUE_CONTEXT + "/:userid/lights/:id/state", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK); response.status(HttpStatus.SC_OK);
@@ -296,28 +351,34 @@ public class HueMulator {
return responseString; return responseString;
} }
if (state.isOn()) { responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":";
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":true}}";
url = device.getOnUrl();
} else if (request.body().contains("false")) {
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":false}}";
url = device.getOffUrl();
}
if(request.body().contains("bri")) if(request.body().contains("bri"))
{ {
if(url == null) url = device.getDimUrl();
{
url = device.getOnUrl();
responseString = "[";
}
else
responseString = responseString + ",";
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/bri\":" + state.getBri() + "}}]"; if(url == null || url.length() == 0)
url = device.getOnUrl();
responseString = responseString + "true}},{\"success\":{\"/lights/" + lightId + "/state/bri\":" + state.getBri() + "}}]";
} }
else else
responseString = responseString + "]"; {
if (state.isOn()) {
responseString = responseString + "true}}]";
url = device.getOnUrl();
state.setBri(255);
} else if (request.body().contains("false")) {
responseString = responseString + "false}}]";
url = device.getOffUrl();
state.setBri(0);
}
}
if (url == null) {
log.warn("Could not find url: " + lightId + " for hue state change request: " + userId + " from " + request.ip() + " body: " + request.body());
responseString = "[{\"error\":{\"type\": 3, \"address\": \"/lights/" + lightId + "\",\"description\": \"Could not find url\", \"resource\": \"/lights/" + lightId + "\"}}]";
return responseString;
}
if(device.getDeviceType().toLowerCase().contains("activity") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyActivity"))) if(device.getDeviceType().toLowerCase().contains("activity") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyActivity")))
{ {
@@ -337,7 +398,6 @@ public class HueMulator {
else { else {
log.warn("Should not get here, no harmony configured"); log.warn("Should not get here, no harmony configured");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Should not get here, no harmony configured\", \"parameter\": \"/lights/" + lightId + "state\"}}]"; responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Should not get here, no harmony configured\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
} }
} }
else if(device.getDeviceType().toLowerCase().contains("button") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyButton"))) else if(device.getDeviceType().toLowerCase().contains("button") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyButton")))
@@ -395,7 +455,10 @@ public class HueMulator {
NestInstruction thermoSetting = new Gson().fromJson(url, NestInstruction.class); NestInstruction thermoSetting = new Gson().fromJson(url, NestInstruction.class);
if(thermoSetting.getControl().equalsIgnoreCase("temp")) { if(thermoSetting.getControl().equalsIgnoreCase("temp")) {
if(request.body().contains("bri")) { if(request.body().contains("bri")) {
if(bridgeSettings.isFarenheit())
thermoSetting.setTemp(String.valueOf((Double.parseDouble(replaceIntensityValue(thermoSetting.getTemp(), state.getBri())) - 32.0)/1.8)); thermoSetting.setTemp(String.valueOf((Double.parseDouble(replaceIntensityValue(thermoSetting.getTemp(), state.getBri())) - 32.0)/1.8));
else
thermoSetting.setTemp(String.valueOf(Double.parseDouble(replaceIntensityValue(thermoSetting.getTemp(), state.getBri()))));
log.debug("Setting thermostat: " + thermoSetting.getName() + " to " + thermoSetting.getTemp() + "C"); log.debug("Setting thermostat: " + thermoSetting.getName() + " to " + thermoSetting.getTemp() + "C");
theNest.getThermostat(thermoSetting.getName()).setTargetTemperature(Float.parseFloat(thermoSetting.getTemp())); theNest.getThermostat(thermoSetting.getName()).setTargetTemperature(Float.parseFloat(thermoSetting.getTemp()));
} }
@@ -454,6 +517,10 @@ public class HueMulator {
} }
} }
if(!responseString.contains("[{\"error\":")) {
device.setDeviceSetValue(state.getBri());
device.setDeviceState(state.isOn());
}
return responseString; return responseString;
}); });
} }

View File

@@ -104,7 +104,7 @@ public class UpnpListener {
*/ */
protected boolean isSSDPDiscovery(DatagramPacket packet){ protected boolean isSSDPDiscovery(DatagramPacket packet){
//Only respond to discover request for strict upnp form //Only respond to discover request for strict upnp form
String packetString = new String(packet.getData()); String packetString = new String(packet.getData(), 0, packet.getLength());
if(packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1") && packetString.contains("\"ssdp:discover\"")){ if(packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1") && packetString.contains("\"ssdp:discover\"")){
log.debug("isSSDPDiscovery Found message to be an M-SEARCH message."); log.debug("isSSDPDiscovery Found message to be an M-SEARCH message.");
log.debug("isSSDPDiscovery Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString); log.debug("isSSDPDiscovery Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
@@ -148,9 +148,9 @@ public class UpnpListener {
String discoveryResponse = null; String discoveryResponse = null;
discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort); discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort);
if(traceupnp) if(traceupnp)
log.info("Traceupnp: sendUpnpResponse: " + discoveryResponse); log.info("Traceupnp: sendUpnpResponse discovery template with address: " + responseAddress + " and port: " + httpServerPort);
else else
log.debug("sendUpnpResponse: " + discoveryResponse); log.debug("sendUpnpResponse discovery template with address: " + responseAddress + " and port: " + httpServerPort);
DatagramPacket response = new DatagramPacket(discoveryResponse.getBytes(), discoveryResponse.length(), requester, sourcePort); DatagramPacket response = new DatagramPacket(discoveryResponse.getBytes(), discoveryResponse.length(), requester, sourcePort);
socket.send(response); socket.send(response);
} }

View File

@@ -54,9 +54,9 @@ public class UpnpSettingsResource {
String filledTemplate = null; String filledTemplate = null;
filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress()); filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress());
if(theSettings.isTraceupnp()) if(theSettings.isTraceupnp())
log.info("Traceupnp: upnp device settings response: " + filledTemplate); log.info("Traceupnp: upnp device settings template filled with address: " + theSettings.getUpnpConfigAddress() + " and port: " + portNumber);
else else
log.debug("upnp device settings response: " + filledTemplate); log.debug("Traceupnp: upnp device settings template filled with address: " + theSettings.getUpnpConfigAddress() + " and port: " + portNumber);
// response.header("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"); // response.header("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
// response.header("Pragma", "no-cache"); // response.header("Pragma", "no-cache");
// response.header("Expires", "Mon, 1 Aug 2011 09:00:00 GMT"); // response.header("Expires", "Mon, 1 Aug 2011 09:00:00 GMT");

View File

@@ -1,5 +1,7 @@
package com.bwssystems.NestBridge; package com.bwssystems.NestBridge;
import java.io.UnsupportedEncodingException;
public class NestItem { public class NestItem {
private String name; private String name;
private String id; private String id;
@@ -9,7 +11,14 @@ public class NestItem {
return name; return name;
} }
public void setName(String name) { public void setName(String name) {
this.name = name; byte ptext[];
String theLabel = new String(name);
try {
ptext = theLabel.getBytes("ISO-8859-1");
this.name = new String(ptext, "UTF-8");
} catch (UnsupportedEncodingException e) {
this.name = theLabel;
}
} }
public String getId() { public String getId() {
return id; return id;

View File

@@ -1,5 +1,7 @@
package com.bwssystems.harmony; package com.bwssystems.harmony;
import java.io.UnsupportedEncodingException;
import net.whistlingfish.harmony.config.Activity; import net.whistlingfish.harmony.config.Activity;
public class HarmonyActivity { public class HarmonyActivity {
@@ -15,6 +17,14 @@ public class HarmonyActivity {
return activity; return activity;
} }
public void setActivity(Activity activity) { public void setActivity(Activity activity) {
byte ptext[];
String theLabel = activity.getLabel();
try {
ptext = theLabel.getBytes("ISO-8859-1");
activity.setLabel(new String(ptext, "UTF-8"));
} catch (UnsupportedEncodingException e) {
activity.setLabel(theLabel);
}
this.activity = activity; this.activity = activity;
} }

View File

@@ -1,5 +1,7 @@
package com.bwssystems.harmony; package com.bwssystems.harmony;
import java.io.UnsupportedEncodingException;
import net.whistlingfish.harmony.config.Device; import net.whistlingfish.harmony.config.Device;
public class HarmonyDevice { public class HarmonyDevice {
@@ -9,6 +11,14 @@ public class HarmonyDevice {
return device; return device;
} }
public void setDevice(Device device) { public void setDevice(Device device) {
byte ptext[];
String theLabel = device.getLabel();
try {
ptext = theLabel.getBytes("ISO-8859-1");
device.setLabel(new String(ptext, "UTF-8"));
} catch (UnsupportedEncodingException e) {
device.setLabel(theLabel);
}
this.device = device; this.device = device;
} }
public String getHub() { public String getHub() {

View File

@@ -0,0 +1,50 @@
/*
* http://www.jrecruiter.org
*
* Disclaimer of Warranty.
*
* Unless required by applicable law or agreed to in writing, Licensor provides
* the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
* including, without limitation, any warranties or conditions of TITLE,
* NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
* solely responsible for determining the appropriateness of using or
* redistributing the Work and assume any risks associated with Your exercise of
* permissions under this License.
*
*/
package com.bwssystems.logservices;
import java.util.Date;
/**
* Log file information.
*
*/
public class LogFileInfo {
private String fileName;
private Long fileSize;
private Date fileLastChanged;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Long getFileSize() {
return fileSize;
}
public void setFileSize(Long fileSize) {
this.fileSize = fileSize;
}
public Date getFileLastChanged() {
return fileLastChanged;
}
public void setFileLastChanged(Date fileLastChanged) {
this.fileLastChanged = fileLastChanged;
}
}

View File

@@ -0,0 +1,51 @@
package com.bwssystems.logservices;
import com.bwssystems.logservices.LoggingUtil.LogLevels;
/**
* Logger information.
*
*
*/
public class LoggerInfo {
private String loggerName;
private LogLevels logLevel;
private LogLevels newLogLevel;
//~~~~~Constructors~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
*
*/
public LoggerInfo() {
super();
}
public LoggerInfo(String loggerName, int logLevelAsInt) {
super();
this.loggerName = loggerName;
this.logLevel = LogLevels.getLogLevelFromId(logLevelAsInt);
}
//~~~~~Getters and Setters~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public String getLoggerName() {
return loggerName;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
public LogLevels getLogLevel() {
return logLevel;
}
public void setLogLevel(LogLevels logLevel) {
this.logLevel = logLevel;
}
public LogLevels getNewLogLevel() {
return newLogLevel;
}
public void setNewLogLevel(LogLevels newLogLevel) {
this.newLogLevel = newLogLevel;
}
}

View File

@@ -0,0 +1,58 @@
package com.bwssystems.logservices;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* Form for the Logging Action.
*/
public class LoggingForm implements Serializable {
/** serialVersionUID. */
private static final long serialVersionUID = 5970927715241338665L;
/** List of Loggers, simplified for display purposes */
private List<LoggerInfo> updatedLoggers = new ArrayList<LoggerInfo>();
/** The user can enter a new logger to be configured */
private LoggerInfo newLogger;
/** Used for requesting a logfile download */
private String fileName;
//~~~~~Constructors~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public LoggingForm() {
super();
}
//~~~~~Getters and Setters~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public List<LoggerInfo> getUpdatedLoggers() {
return updatedLoggers;
}
public void setUpdatedLoggers(List<LoggerInfo> updatedLoggers) {
this.updatedLoggers = updatedLoggers;
}
public LoggerInfo getNewLogger() {
return newLogger;
}
public void setNewLogger(LoggerInfo newLogger) {
this.newLogger = newLogger;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}

View File

@@ -0,0 +1,163 @@
package com.bwssystems.logservices;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import com.bwssystems.logservices.LoggingUtil;
import com.bwssystems.logservices.LoggingUtil.LogLevels;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
/**
* Show log files and allow to set log levels for the configured loggers (logback)
* to be changed dynamically at runtime.
*/
public class LoggingManager {
/** Show all loggers or only the configured loggers */
private boolean showAll = false;
/** List of log files and associated information */
private List<LogFileInfo> logFileInfos = new ArrayList<LogFileInfo>();
/** List of Loggers, simplified for display purposes */
private List<LoggerInfo> configuredLoggers = new ArrayList<LoggerInfo>();
/** Used to stream a logfile back to the client */
private transient InputStream fileToDownLoad;
private LoggingForm model = new LoggingForm();
//~~~~~Reference Data~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/** Defines a collection of log levels. Unfortunately logback does not use
* enums to define its log levels. */
private Set<LogLevels> logLevels = EnumSet.allOf(LogLevels.class);
//~~~~~Prepare data~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public void init() {
loadLoggers();
loadLogFiles();
}
//~~~~~Helper Methods~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private void loadLoggers() {
this.configuredLoggers.clear();
final List<Logger> loggers = LoggingUtil.getLoggers(this.showAll);
for (Logger logger : loggers) {
this.configuredLoggers.add(new LoggerInfo(logger.getName(), logger.getEffectiveLevel().levelInt));
}
}
private void loadLogFiles() {
this.logFileInfos = LoggingUtil.getLogFileInfos();
}
/** Updates loglevels for loggers */
public String updateLogLevels() {
if (this.model.getUpdatedLoggers() != null && !this.model.getUpdatedLoggers().isEmpty()) {
for (LoggerInfo loggerInfo : this.model.getUpdatedLoggers()) {
if (loggerInfo != null && loggerInfo.getNewLogLevel() != null) {
LoggingUtil.getLogger(loggerInfo.getLoggerName())
.setLevel(Level.toLevel(loggerInfo.getNewLogLevel().getLogLevel()));
}
}
//Need to refresh the loggers
loadLoggers();
}
return "successRedirect";
}
/** Adds a new logger at runtime. */
public String addNewLogger() {
if (this.model.getNewLogger() != null
&& this.model.getNewLogger().getLoggerName() != null
&& this.model.getNewLogger().getNewLogLevel() != null) {
final Logger newLogger = LoggingUtil.getLogger(this.model.getNewLogger().getLoggerName());
newLogger.setLevel(Level.toLevel(this.model.getNewLogger().getNewLogLevel().getLogLevel()));
//Need to refresh the loggers
loadLoggers();
}
return "successRedirect";
}
/**
* Retrieve the requested log file.
*
* @return
* @throws Exception
*/
public String download() throws Exception {
if (this.model.getFileName() == null) {
throw new IllegalArgumentException("FileName must not be null.");
}
final File logFile = LoggingUtil.getLogFile(this.model.getFileName());
if (logFile != null) {
this.fileToDownLoad = new FileInputStream(logFile);
}
return "download";
}
//~~~~~Getters and Setters~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public List<LogFileInfo> getLogFileInfos() {
return logFileInfos;
}
public InputStream getFileToDownLoad() {
return fileToDownLoad;
}
public List<LoggerInfo> getConfiguredLoggers() {
return configuredLoggers;
}
public boolean isShowAll() {
return showAll;
}
public void setShowAll(boolean showAll) {
this.showAll = showAll;
}
public Set<LogLevels> getLogLevels() {
return logLevels;
}
public LoggingForm getModel() {
return model;
}
public void setModel(LoggingForm model) {
this.model = model;
}
}

View File

@@ -0,0 +1,198 @@
package com.bwssystems.logservices;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import com.bwssystems.logservices.LogFileInfo;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.FileAppender;
/**
* Contains utility methods to interact with the logback during runtime.
*/
public class LoggingUtil {
/**
* re-defines the logback logging levels as a Java enumeration. This is quite
* helpful if you need to render the various log-levels as select-box. I wish
* logback @see {@link ch.qos.logback.classic.Level} would not use static variables
* but use enums instead.
*/
public enum LogLevels {
OFF(Integer.MAX_VALUE, "Off"),
ERROR_INT(40000, "Error"),
WARN_INT(30000, "Warn"),
INFO_INT(20000, "Info"),
DEBUG_INT(10000, "Debug"),
TRACE(5000, "Trace"),
ALL(Integer.MIN_VALUE, "All");
private int logLevel;
private String logLevelName;
LogLevels(final int logLevel, final String logLevelName) {
this.logLevel = logLevel;
this.logLevelName = logLevelName;
}
public int getLogLevel() {
return this.logLevel;
}
public String getLogLevelName() {
return this.logLevelName;
}
public static LogLevels getLogLevelFromId(final int logLevelAsInt) {
for (LogLevels logLevel : LogLevels.values()) {
if (logLevelAsInt == logLevel.logLevel) {
return logLevel;
}
}
throw new IllegalStateException("Loglevel " + logLevelAsInt + " does not exist.");
}
@Override
public String toString() {
return this.logLevelName;
}
}
/**
* Retrieve all configured logback loggers.
*
* @param showAll If set return ALL loggers, not only the configured ones.
* @return List of Loggers
*/
public static List<ch.qos.logback.classic.Logger> getLoggers(final boolean showAll) {
final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
final List<ch.qos.logback.classic.Logger> loggers = new ArrayList<ch.qos.logback.classic.Logger>();
for (ch.qos.logback.classic.Logger log : lc.getLoggerList()) {
if(showAll == false) {
if(log.getLevel() != null || LoggingUtil.hasAppenders(log)) {
loggers.add(log);
}
} else {
loggers.add(log);
}
}
return loggers;
}
/**
* Get a single logger.
*
* @return Logger
*/
public static ch.qos.logback.classic.Logger getLogger(final String loggerName) {
final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
return lc.getLogger(loggerName);
}
/**
* Test whether the provided logger has appenders.
*
* @param logger The logger to test
* @return true if the logger has appenders.
*/
public static boolean hasAppenders(ch.qos.logback.classic.Logger logger) {
Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders();
return it.hasNext();
}
/**
* Get the logfile information for the roor logger.
*
* @return List of LogFileInfo obejcts
*/
public static List<LogFileInfo> getLogFileInfos() {
final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
final List<LogFileInfo> logFileInfos = new ArrayList<LogFileInfo>();
final Logger logger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
final Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders();
while (it.hasNext()) {
final Appender<ILoggingEvent> appender = it.next();
if (appender instanceof FileAppender) {
final FileAppender<ILoggingEvent> fileAppender = (FileAppender<ILoggingEvent>) appender;
final File logFile = new File(fileAppender.getFile());
final LogFileInfo logFileInfo = new LogFileInfo();
logFileInfo.setFileName(logFile.getName());
logFileInfo.setFileLastChanged(new Date(logFile.lastModified()));
logFileInfo.setFileSize(logFile.length());
logFileInfos.add(logFileInfo);
}
}
return logFileInfos;
}
/**
* Get the log file.
*
* @param logFileName The name of the log file
* @return The actual file
*/
public static File getLogFile(final String logFileName) {
if (logFileName == null) {
throw new IllegalArgumentException("logFileName cannot be null.");
}
final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
final Logger logger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
final Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders();
while (it.hasNext()) {
final Appender<ILoggingEvent> appender = it.next();
if (appender instanceof FileAppender) {
final FileAppender<ILoggingEvent> fileAppender = (FileAppender<ILoggingEvent>) appender;
final File logFile = new File(fileAppender.getFile());
if (logFile.getName().equalsIgnoreCase(logFileName)) {
return logFile;
}
}
}
return null;
}
}

View File

@@ -1,4 +1,4 @@
package com.bwssystems.HABridge; package com.bwssystems.util;
import java.io.IOException; import java.io.IOException;
import java.nio.file.DirectoryStream; import java.nio.file.DirectoryStream;

View File

@@ -0,0 +1,49 @@
package com.bwssystems.util;
import java.text.StringCharacterIterator;
public final class JsonFreeTextStringFormatter {
private JsonFreeTextStringFormatter(){
//empty - prevent construction
}
public static String forJSON(String aText){
final StringBuilder result = new StringBuilder();
StringCharacterIterator iterator = new StringCharacterIterator(aText);
char character = iterator.current();
while (character != StringCharacterIterator.DONE){
if( character == '\"' ){
result.append("\\\"");
}
else if(character == '\\'){
result.append("\\\\");
}
else if(character == '/'){
result.append("\\/");
}
else if(character == '\b'){
result.append("\\b");
}
else if(character == '\f'){
result.append("\\f");
}
else if(character == '\n'){
result.append("\\n");
}
else if(character == '\r'){
result.append("\\r");
}
else if(character == '\t'){
result.append("\\t");
}
else {
//the char is not a special one
//add it to the result as is
result.append(character);
}
character = iterator.next();
}
return result.toString();
}
}

View File

@@ -1,4 +1,4 @@
package com.bwssystems.HABridge; package com.bwssystems.util;
import com.google.gson.Gson; import com.google.gson.Gson;
import spark.ResponseTransformer; import spark.ResponseTransformer;

View File

@@ -1,6 +1,7 @@
package com.bwssystems.vera; package com.bwssystems.vera;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap; import java.util.HashMap;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Map; import java.util.Map;
@@ -101,7 +102,7 @@ public class VeraInfo {
HttpResponse response = httpClient.execute(httpGet); HttpResponse response = httpClient.execute(httpGet);
log.debug("GET on URL responded: " + response.getStatusLine().getStatusCode()); log.debug("GET on URL responded: " + response.getStatusLine().getStatusCode());
if(response.getStatusLine().getStatusCode() == 200){ if(response.getStatusLine().getStatusCode() == 200){
theContent = EntityUtils.toString(response.getEntity()); //read content for data theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); //read content for data
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
} }
} catch (IOException e) { } catch (IOException e) {

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<appender name="CYCLIC" class="ch.qos.logback.core.read.CyclicBufferAppender">
<MaxSize>512</MaxSize>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="CYCLIC" />
</root>
</configuration>

View File

@@ -0,0 +1,127 @@
.scrollableContainer {
height: 800px;
position: relative;
padding-top: 35px;
overflow: hidden;
}
.scrollableContainer .headerSpacer {
border: 1px solid #d5d5d5;
border-bottom-color: #bbb;
position: absolute;
height: 36px;
top: 0;
right: 0;
left: 0;
}
.scrollableContainer th .orderWrapper {
position: absolute;
top: 0;
right: 2px;
cursor: pointer;
}
.scrollableContainer th .orderWrapper .order {
font-size: 8pt;
color: #BDBDBD;
}
.scrollableContainer th .orderWrapper .active {
color: #464646;
}
.scrollArea {
height: 100%;
overflow-x: auto;
overflow-y: auto;
border: 1px solid #d5d5d5;
/* the implementation of this is still quite buggy; specifically, it doesn't like the
absolutely positioned headers within
-webkit-overflow-scrolling: touch; */
}
.scrollArea table {
overflow-x: hidden;
overflow-y: auto;
margin-bottom: 0;
width: 100%;
border: none;
/*border-collapse: separate;*/
}
.scrollArea table th {
padding: 0 !important;
border: none !important;
min-width: 60px;
}
.scrollArea table .th-inner {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
position: absolute;
top: 0;
height: 36px;
line-height: 36px;
}
.scrollArea table th .box {
padding: 0 8px;
padding-right: 11px; /* order icon width*/
border-left: 1px solid #ddd;
}
/* to hack fix firefox border issue */
@-moz-document url-prefix() {
.scrollArea table th .box{
border-right: 1px solid #ddd;
border-left: none;
}
}
.scrollArea table .th-inner .ng-scope {
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
.scrollArea table tr th:first-child th .box {
border-left: none;
}
.scrollArea table .th-inner.condensed {
padding: 0 3px;
}
.scrollArea table tbody tr td:first-child {
border-left: none;
}
.scrollArea table tbody tr td:last-child {
border-right: none;
}
.scrollArea table tbody tr:first-child td {
border-top: none;
}
.scrollArea table tbody tr:last-child td {
border-bottom: 2px solid #ddd;
}
.scrollArea table tbody tr td {
border-bottom: 1px solid #ddd;
overflow: hidden;
text-overflow: ellipsis;
}
.scrollableContainer .scaler {
position: absolute;
top: 0px;
width: 2px;
height: 100%;
background-color: #CFCFCF;
}
.scrollableContainer th .resize-rod {
position: absolute;
top: 0;
right: 0;
cursor: col-resize;
width: 4px;
height: 100%;
}
.scrollable-resizing .scrollableContainer {
cursor: col-resize;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}

View File

@@ -12,6 +12,7 @@
<link href="css/rzslider.min.css" rel="stylesheet"> <link href="css/rzslider.min.css" rel="stylesheet">
<link href="css/ngDialog.min.css" rel="stylesheet"> <link href="css/ngDialog.min.css" rel="stylesheet">
<link href="css/ngDialog-theme-default.min.css" rel="stylesheet"> <link href="css/ngDialog-theme-default.min.css" rel="stylesheet">
<link href="css/scrollable-table.css" rel="stylesheet">
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script type="text/javascript" src="js/html5shiv.min.js"></script> <script type="text/javascript" src="js/html5shiv.min.js"></script>
@@ -59,10 +60,11 @@
<script src="js/angular.min.js"></script> <script src="js/angular.min.js"></script>
<script src="js/angular-route.min.js"></script> <script src="js/angular-route.min.js"></script>
<script src="js/angular-sanitize.min.js"></script> <script src="js/angular-sanitize.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/ngToast.min.js"></script> <script src="js/ngToast.min.js"></script>
<script src="js/rzslider.min.js"></script> <script src="js/rzslider.min.js"></script>
<script src="js/rzslider.min.js"></script>
<script src="js/ngDialog.min.js"></script> <script src="js/ngDialog.min.js"></script>
<script src="js/angular-scrollable-table.min.js"></script>
<script src="scripts/app.js"></script> <script src="scripts/app.js"></script>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
var app = angular.module('habridge', ['ngRoute','ngToast','rzModule','ngDialog']); var app = angular.module('habridge', ['ngRoute','ngToast','rzModule','ngDialog','scrollable-table']);
app.config(function ($routeProvider) { app.config(function ($routeProvider) {
$routeProvider.when('/#', { $routeProvider.when('/#', {
@@ -7,27 +7,30 @@ app.config(function ($routeProvider) {
}).when('/system', { }).when('/system', {
templateUrl: 'views/system.html', templateUrl: 'views/system.html',
controller: 'SystemController' controller: 'SystemController'
}).when('/logs', {
templateUrl: 'views/logs.html',
controller: 'LogsController'
}).when('/editor', { }).when('/editor', {
templateUrl: 'views/editor.html', templateUrl: 'views/editor.html',
controller: 'AddingController' controller: 'EditController'
}).when('/editdevice', { }).when('/editdevice', {
templateUrl: 'views/editdevice.html', templateUrl: 'views/editdevice.html',
controller: 'AddingController' controller: 'EditController'
}).when('/veradevices', { }).when('/veradevices', {
templateUrl: 'views/veradevice.html', templateUrl: 'views/veradevice.html',
controller: 'AddingController' controller: 'VeraController'
}).when('/verascenes', { }).when('/verascenes', {
templateUrl: 'views/verascene.html', templateUrl: 'views/verascene.html',
controller: 'AddingController' controller: 'VeraController'
}).when('/harmonydevices', { }).when('/harmonydevices', {
templateUrl: 'views/harmonydevice.html', templateUrl: 'views/harmonydevice.html',
controller: 'AddingController' controller: 'HarmonyController'
}).when('/harmonyactivities', { }).when('/harmonyactivities', {
templateUrl: 'views/harmonyactivity.html', templateUrl: 'views/harmonyactivity.html',
controller: 'AddingController' controller: 'HarmonyController'
}).when('/nest', { }).when('/nest', {
templateUrl: 'views/nestactions.html', templateUrl: 'views/nestactions.html',
controller: 'AddingController' controller: 'NestController'
}).otherwise({ }).otherwise({
templateUrl: 'views/configuration.html', templateUrl: 'views/configuration.html',
controller: 'ViewingController' controller: 'ViewingController'
@@ -41,30 +44,28 @@ app.run( function (bridgeService) {
app.service('bridgeService', function ($http, $window, ngToast) { app.service('bridgeService', function ($http, $window, ngToast) {
var self = this; var self = this;
this.state = {base: window.location.origin + "/api/devices", bridgelocation: window.location.origin, systemsbase: window.location.origin + "/system", huebase: window.location.origin + "/api", configs: [], backups: [], devices: [], device: [], type: "", settings: [], myToastMsg: [], olddevicename: "", isInControl: false, showVera: false, showHarmony: false, showNest: false, habridgeversion: ""}; this.state = {base: window.location.origin + "/api/devices", bridgelocation: window.location.origin, systemsbase: window.location.origin + "/system", huebase: window.location.origin + "/api", configs: [], backups: [], devices: [], device: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, habridgeversion: ""};
this.displayWarn = function(errorTitle, error) { this.displayWarn = function(errorTitle, error) {
if(error == null || typeof(error) != 'undefined') { var toastContent = errorTitle;
error = {status: 200, statusText: "OK", data: []}; if(error != null && typeof(error) != 'undefined')
error.data = {message: "success"}; toastContent = toastContent + " " + error.data.message + " with status: " + error.statusText + " - " + error.status;
}
ngToast.create({ ngToast.create({
className: "warning", className: "warning",
dismissButton: true, dismissButton: true,
dismissOnTimeout: false, dismissOnTimeout: false,
content: errorTitle + error.data.message + " with status: " + error.statusText + " - " + error.status}); content: toastContent});
}; };
this.displayError = function(errorTitle, error) { this.displayError = function(errorTitle, error) {
if(error == null || typeof(error) != 'undefined') { var toastContent = errorTitle;
error = {status: 200, statusText: "OK", data: []}; if(error != null && typeof(error) != 'undefined')
error.data = {message: "success"}; toastContent = toastContent + " " + error.data.message + " with status: " + error.statusText + " - " + error.status;
}
ngToast.create({ ngToast.create({
className: "danger", className: "danger",
dismissButton: true, dismissButton: true,
dismissOnTimeout: false, dismissOnTimeout: false,
content: errorTitle + error.data.message + " with status: " + error.statusText + " - " + error.status}); content: toastContent});
}; };
this.displayErrorMessage = function(errorTitle, errorMessage) { this.displayErrorMessage = function(errorTitle, errorMessage) {
@@ -92,6 +93,23 @@ app.service('bridgeService', function ($http, $window, ngToast) {
); );
}; };
this.clearDevice = function () {
self.state.device.id = "";
self.state.device.mapType = null;
self.state.device.mapId = null;
self.state.device.name = "";
self.state.device.onUrl = "";
self.state.device.dimUrl = "";
self.state.device.deviceType = "custom";
self.state.device.targetDevice = null;
self.state.device.offUrl = "";
self.state.device.httpVerb = null;
self.state.device.contentType = null;
self.state.device.contentBody = null;
self.state.device.contentBodyOff = null;
self.state.olddevicename = "";
};
this.getHABridgeVersion = function () { this.getHABridgeVersion = function () {
return $http.get(this.state.systemsbase + "/habridge/version").then( return $http.get(this.state.systemsbase + "/habridge/version").then(
function (response) { function (response) {
@@ -158,6 +176,28 @@ app.service('bridgeService', function ($http, $window, ngToast) {
); );
}; };
this.viewLogs = function () {
return $http.get(this.state.systemsbase + "/logmsgs").then(
function (response) {
self.state.logMsgs = response.data;
},
function (error) {
self.displayWarn("Get log messages Error: ", error);
}
);
};
this.viewLoggerInfo = function () {
return $http.get(this.state.systemsbase + "/logmgmt/loggers/" + self.state.logShowAll).then(
function (response) {
self.state.loggerInfo = response.data;
},
function (error) {
self.displayWarn("Get logger info Error: ", error);
}
);
};
this.viewNestItems = function () { this.viewNestItems = function () {
if(!this.state.showNest) if(!this.state.showNest)
return; return;
@@ -223,6 +263,19 @@ app.service('bridgeService', function ($http, $window, ngToast) {
); );
}; };
this.updateLogLevels = function(logComponents) {
return $http.put(this.state.systemsbase + "/logmgmt/update", logComponents ).then(
function (response) {
self.state.loggerInfo = response.data;
self.displaySuccess("Updated " + logComponents.length + " loggers for log levels.")
},
function (error) {
self.displayWarn("Update Log components Error: ", error);
}
);
};
this.findDeviceByMapId = function(id, target, type) { this.findDeviceByMapId = function(id, target, type) {
for(var i = 0; i < this.state.devices.length; i++) { for(var i = 0; i < this.state.devices.length; i++) {
if(this.state.devices[i].mapId == id && this.state.devices[i].mapType == type && this.state.devices[i].targetDevice == target) if(this.state.devices[i].mapId == id && this.state.devices[i].mapType == type && this.state.devices[i].targetDevice == target)
@@ -249,27 +302,16 @@ app.service('bridgeService', function ($http, $window, ngToast) {
); );
}; };
this.addDevice = function (device) { this.addDevice = function (aDevice) {
var device = {};
angular.extend(device, aDevice );
if(device.httpVerb != null && device.httpVerb != "") if(device.httpVerb != null && device.httpVerb != "")
device.deviceType = "custom"; device.deviceType = "custom";
if(device.targetDevice == null || device.targetDevice == "") if(device.targetDevice == null || device.targetDevice == "")
device.targetDevice = "Encapsulated"; device.targetDevice = "Encapsulated";
if (device.id) { if (device.id) {
var putUrl = this.state.base + "/" + device.id; var putUrl = this.state.base + "/" + device.id;
return $http.put(putUrl, { return $http.put(putUrl, device).then(
id: device.id,
name: device.name,
mapId: device.mapId,
mapType: device.mapType,
deviceType: device.deviceType,
targetDevice: device.targetDevice,
onUrl: device.onUrl,
offUrl: device.offUrl,
httpVerb: device.httpVerb,
contentType: device.contentType,
contentBody: device.contentBody,
contentBodyOff: device.contentBodyOff
}).then(
function (response) { function (response) {
}, },
function (error) { function (error) {
@@ -279,19 +321,7 @@ app.service('bridgeService', function ($http, $window, ngToast) {
} else { } else {
if(device.deviceType == null || device.deviceType == "") if(device.deviceType == null || device.deviceType == "")
device.deviceType = "custom"; device.deviceType = "custom";
return $http.post(this.state.base, { return $http.post(this.state.base, device).then(
name: device.name,
mapId: device.mapId,
mapType: device.mapType,
deviceType: device.deviceType,
targetDevice: device.targetDevice,
onUrl: device.onUrl,
offUrl: device.offUrl,
httpVerb: device.httpVerb,
contentType: device.contentType,
contentBody: device.contentBody,
contentBodyOff: device.contentBodyOff
}).then(
function (response) { function (response) {
}, },
function (error) { function (error) {
@@ -420,6 +450,7 @@ app.service('bridgeService', function ($http, $window, ngToast) {
filename: afilename filename: afilename
}).then( }).then(
function (response) { function (response) {
self.state.settings = response.data;
self.viewConfigs(); self.viewConfigs();
self.viewDevices(); self.viewDevices();
}, },
@@ -448,7 +479,7 @@ app.service('bridgeService', function ($http, $window, ngToast) {
self.viewDevices(); self.viewDevices();
}, },
function (error) { function (error) {
nself.displayWarn("Delete Device Error: ", error); self.displayWarn("Delete Device Error: ", error);
} }
); );
}; };
@@ -469,11 +500,11 @@ app.service('bridgeService', function ($http, $window, ngToast) {
var msgDescription = "unknown"; var msgDescription = "unknown";
var testUrl = this.state.huebase + "/test/lights/" + device.id + "/state"; var testUrl = this.state.huebase + "/test/lights/" + device.id + "/state";
var testBody = "{\"on\":"; var testBody = "{\"on\":";
if(type == "on") { if(type == "off") {
testBody = testBody + "true"; testBody = testBody + "false";
} }
else { else {
testBody = testBody + "false"; testBody = testBody + "true";
} }
if(value) { if(value) {
testBody = testBody + ",\"bri\":" + value; testBody = testBody + ",\"bri\":" + value;
@@ -507,8 +538,6 @@ app.controller('SystemController', function ($scope, $location, $http, $window,
$scope.isInControl = false; $scope.isInControl = false;
$scope.visible = false; $scope.visible = false;
$scope.imgUrl = "glyphicon glyphicon-plus"; $scope.imgUrl = "glyphicon glyphicon-plus";
$scope.visibleBk = false;
$scope.imgBkUrl = "glyphicon glyphicon-plus";
$scope.addVeratoSettings = function (newveraname, newveraip) { $scope.addVeratoSettings = function (newveraname, newveraip) {
if($scope.bridge.settings.veraaddress == null) { if($scope.bridge.settings.veraaddress == null) {
$scope.bridge.settings.veraaddress = { devices: [] }; $scope.bridge.settings.veraaddress = { devices: [] };
@@ -575,12 +604,47 @@ app.controller('SystemController', function ($scope, $location, $http, $window,
else else
$scope.imgUrl = "glyphicon glyphicon-plus"; $scope.imgUrl = "glyphicon glyphicon-plus";
}; };
$scope.toggleBk = function () { });
$scope.visibleBk = !$scope.visibleBk;
if($scope.visibleBk) app.controller('LogsController', function ($scope, $location, $http, $window, bridgeService) {
$scope.imgBkUrl = "glyphicon glyphicon-minus"; bridgeService.viewLogs();
$scope.bridge = bridgeService.state;
$scope.levels = ["INFO_INT", "WARN_INT", "DEBUG_INT", "TRACE_INT"];
$scope.updateComponents = [];
$scope.visible = false;
$scope.imgUrl = "glyphicon glyphicon-plus";
$scope.updateLogs = function() {
bridgeService.viewLogs();
};
$scope.toggle = function () {
$scope.visible = !$scope.visible;
if($scope.visible) {
$scope.imgUrl = "glyphicon glyphicon-minus";
bridgeService.viewLoggerInfo();
}
else else
$scope.imgBkUrl = "glyphicon glyphicon-plus"; $scope.imgUrl = "glyphicon glyphicon-plus";
};
$scope.addToUpdate = function (logInfo) {
var idx = $scope.updateComponents.indexOf(logInfo);
// is currently selected
if (idx > -1) {
$scope.updateComponents.splice(idx, 1);
}
// is newly selected
else {
$scope.updateComponents.push(logInfo);
}
};
$scope.updateLoggers = function () {
bridgeService.updateLogLevels($scope.updateComponents);
};
$scope.reloadLoggers = function () {
bridgeService.viewLoggerInfo();
}; };
}); });
@@ -594,20 +658,17 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
$scope.imgUrl = "glyphicon glyphicon-plus"; $scope.imgUrl = "glyphicon glyphicon-plus";
$scope.visibleBk = false; $scope.visibleBk = false;
$scope.imgBkUrl = "glyphicon glyphicon-plus"; $scope.imgBkUrl = "glyphicon glyphicon-plus";
$scope.predicate = '';
$scope.reverse = true;
$scope.order = function(predicate) {
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
$scope.predicate = predicate;
};
$scope.testUrl = function (device, type) { $scope.testUrl = function (device, type) {
var dialogNeeded = false; var dialogNeeded = false;
if((type == "on" && (bridgeService.aContainsB(device.onUrl, "${intensity..byte}") || if((type == "on" && (bridgeService.aContainsB(device.onUrl, "${intensity.byte}") ||
bridgeService.aContainsB(device.onUrl, "${intensity.percent}") || bridgeService.aContainsB(device.onUrl, "${intensity.percent}") ||
bridgeService.aContainsB(device.onUrl, "${intensity.math("))) || bridgeService.aContainsB(device.onUrl, "${intensity.math("))) ||
(type == "off" && (bridgeService.aContainsB(device.offUrl, "${intensity..byte}") || (type == "off" && (bridgeService.aContainsB(device.offUrl, "${intensity.byte}") ||
bridgeService.aContainsB(device.offUrl, "${intensity.percent}") || bridgeService.aContainsB(device.offUrl, "${intensity.percent}") ||
bridgeService.aContainsB(device.offUrl, "${intensity.math(")))) { bridgeService.aContainsB(device.offUrl, "${intensity.math("))) ||
(type == "dim" && (bridgeService.aContainsB(device.dimUrl, "${intensity.byte}") ||
bridgeService.aContainsB(device.dimUrl, "${intensity.percent}") ||
bridgeService.aContainsB(device.dimUrl, "${intensity.math(")))) {
$scope.bridge.device = device; $scope.bridge.device = device;
$scope.bridge.type = type; $scope.bridge.type = type;
ngDialog.open({ ngDialog.open({
@@ -685,7 +746,7 @@ app.controller('ValueDialogCtrl', function ($scope, bridgeService, ngDialog) {
}; };
}); });
app.controller('AddingController', function ($scope, $location, $http, bridgeService) { app.controller('VeraController', function ($scope, $location, $http, bridgeService) {
$scope.bridge = bridgeService.state; $scope.bridge = bridgeService.state;
$scope.device = $scope.bridge.device; $scope.device = $scope.bridge.device;
$scope.device_dim_control = ""; $scope.device_dim_control = "";
@@ -697,87 +758,30 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
$scope.vera = {base: "http://", port: "3480", id: ""}; $scope.vera = {base: "http://", port: "3480", id: ""};
bridgeService.viewVeraDevices(); bridgeService.viewVeraDevices();
bridgeService.viewVeraScenes(); bridgeService.viewVeraScenes();
bridgeService.viewHarmonyActivities();
bridgeService.viewHarmonyDevices();
bridgeService.viewNestItems();
$scope.imgButtonsUrl = "glyphicon glyphicon-plus"; $scope.imgButtonsUrl = "glyphicon glyphicon-plus";
$scope.buttonsVisible = false; $scope.buttonsVisible = false;
$scope.predicate = '';
$scope.reverse = true;
$scope.order = function(predicate) {
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
$scope.predicate = predicate;
};
$scope.clearDevice = function () { $scope.clearDevice = function () {
$scope.device.id = ""; bridgeService.clearDevice();
$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.bridge.olddevicename = "";
};
$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 = "Encapsulated";
$scope.device.mapType = "veraDevice";
$scope.device.mapId = $scope.vera.id;
if(dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0)
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&DeviceNum="
+ $scope.vera.id
+ "&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget="
+ dim_control;
else
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum="
+ $scope.vera.id;
$scope.device.offUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum="
+ $scope.vera.id;
};
$scope.buildUrlsUsingScene = function () {
if ($scope.vera.base.indexOf("http") < 0) {
$scope.vera.base = "http://" + $scope.vera.base;
}
$scope.device.deviceType = "scene";
$scope.device.targetDevice = "Encapsulated";
$scope.device.mapType = "veraScene";
$scope.device.mapId = $scope.vera.id;
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum="
+ $scope.vera.id;
$scope.device.offUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum="
+ $scope.vera.id;
}; };
$scope.buildDeviceUrls = function (veradevice, dim_control) { $scope.buildDeviceUrls = function (veradevice, dim_control) {
bridgeService.clearDevice();
$scope.device.deviceType = "switch"; $scope.device.deviceType = "switch";
$scope.device.name = veradevice.name; $scope.device.name = veradevice.name;
$scope.device.targetDevice = veradevice.veraname; $scope.device.targetDevice = veradevice.veraname;
$scope.device.mapType = "veraDevice"; $scope.device.mapType = "veraDevice";
$scope.device.mapId = veradevice.id; $scope.device.mapId = veradevice.id;
if(dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0) if(dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0)
$scope.device.onUrl = "http://" + veradevice.veraaddress + ":" + $scope.vera.port $scope.device.dimUrl = "http://" + veradevice.veraaddress + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&DeviceNum=" + "/data_request?id=action&output_format=json&DeviceNum="
+ veradevice.id + veradevice.id
+ "&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=" + "&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget="
+ dim_control; + dim_control;
else else
$scope.device.dimUrl = "http://" + veradevice.veraaddress + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum="
+ veradevice.id;
$scope.device.onUrl = "http://" + veradevice.veraaddress + ":" + $scope.vera.port $scope.device.onUrl = "http://" + veradevice.veraaddress + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum=" + "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum="
+ veradevice.id; + veradevice.id;
@@ -787,6 +791,7 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}; };
$scope.buildSceneUrls = function (verascene) { $scope.buildSceneUrls = function (verascene) {
bridgeService.clearDevice();
$scope.device.deviceType = "scene"; $scope.device.deviceType = "scene";
$scope.device.name = verascene.name; $scope.device.name = verascene.name;
$scope.device.targetDevice = verascene.veraname; $scope.device.targetDevice = verascene.veraname;
@@ -800,7 +805,98 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
+ verascene.id; + verascene.id;
}; };
$scope.addDevice = function () {
if($scope.device.name == "" && $scope.device.onUrl == "")
return;
bridgeService.addDevice($scope.device).then(
function () {
$scope.clearDevice();
bridgeService.viewDevices();
bridgeService.viewVeraDevices();
bridgeService.viewVeraScenes();
},
function (error) {
bridgeService.displayWarn("Error adding device: " + $scope.device.name, error)
}
);
};
$scope.bulkAddDevices = function(dim_control) {
var devicesList = [];
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);
devicesList[i] = {
name: $scope.device.name,
mapId: $scope.device.mapId,
mapType: $scope.device.mapType,
deviceType: $scope.device.deviceType,
targetDevice: $scope.device.targetDevice,
onUrl: $scope.device.onUrl,
offUrl: $scope.device.offUrl,
httpVerb: $scope.device.httpVerb,
contentType: $scope.device.contentType,
contentBody: $scope.device.contentBody,
contentBodyOff: $scope.device.contentBodyOff
};
}
}
}
bridgeService.bulkAddDevice(devicesList);
$scope.clearDevice();
bridgeService.viewDevices();
bridgeService.viewVeraDevices();
bridgeService.viewVeraScenes();
$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)
$scope.imgButtonsUrl = "glyphicon glyphicon-minus";
else
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
};
$scope.deleteDeviceByMapId = function (id, mapType) {
bridgeService.deleteDeviceByMapId(id, mapType);
bridgeService.viewDevices();
bridgeService.viewVeraDevices();
bridgeService.viewVeraScenes();
};
});
app.controller('HarmonyController', function ($scope, $location, $http, bridgeService) {
$scope.bridge = bridgeService.state;
$scope.device = $scope.bridge.device;
bridgeService.viewHarmonyActivities();
bridgeService.viewHarmonyDevices();
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
$scope.buttonsVisible = false;
$scope.clearDevice = function () {
bridgeService.clearDevice();
};
$scope.buildActivityUrls = function (harmonyactivity) { $scope.buildActivityUrls = function (harmonyactivity) {
bridgeService.clearDevice();
$scope.device.deviceType = "activity"; $scope.device.deviceType = "activity";
$scope.device.targetDevice = harmonyactivity.hub; $scope.device.targetDevice = harmonyactivity.hub;
$scope.device.name = harmonyactivity.activity.label; $scope.device.name = harmonyactivity.activity.label;
@@ -811,6 +907,7 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}; };
$scope.buildButtonUrls = function (harmonydevice, onbutton, offbutton) { $scope.buildButtonUrls = function (harmonydevice, onbutton, offbutton) {
bridgeService.clearDevice();
var currentOn = $scope.device.onUrl; var currentOn = $scope.device.onUrl;
var currentOff = $scope.device.offUrl; var currentOff = $scope.device.offUrl;
var actionOn = angular.fromJson(onbutton); var actionOn = angular.fromJson(onbutton);
@@ -830,7 +927,52 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
} }
}; };
$scope.addDevice = function () {
if($scope.device.name == "" && $scope.device.onUrl == "")
return;
bridgeService.addDevice($scope.device).then(
function () {
$scope.clearDevice();
bridgeService.viewDevices();
bridgeService.viewHarmonyActivities();
bridgeService.viewHarmonyDevices();
},
function (error) {
}
);
};
$scope.toggleButtons = function () {
$scope.buttonsVisible = !$scope.buttonsVisible;
if($scope.buttonsVisible)
$scope.imgButtonsUrl = "glyphicon glyphicon-minus";
else
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
};
$scope.deleteDeviceByMapId = function (id, mapType) {
bridgeService.deleteDeviceByMapId(id, mapType);
bridgeService.viewDevices();
bridgeService.viewHarmonyActivities();
bridgeService.viewHarmonyDevices();
};
});
app.controller('NestController', function ($scope, $location, $http, bridgeService) {
$scope.bridge = bridgeService.state;
$scope.device = $scope.bridge.device;
bridgeService.viewNestItems();
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
$scope.buttonsVisible = false;
$scope.clearDevice = function () {
bridgeService.clearDevice();
};
$scope.buildNestHomeUrls = function (nestitem) { $scope.buildNestHomeUrls = function (nestitem) {
bridgeService.clearDevice();
$scope.device.deviceType = "home"; $scope.device.deviceType = "home";
$scope.device.name = nestitem.name; $scope.device.name = nestitem.name;
$scope.device.targetDevice = nestitem.name; $scope.device.targetDevice = nestitem.name;
@@ -841,16 +983,18 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}; };
$scope.buildNestTempUrls = function (nestitem) { $scope.buildNestTempUrls = function (nestitem) {
bridgeService.clearDevice();
$scope.device.deviceType = "thermo"; $scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Temperature"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Temperature";
$scope.device.targetDevice = nestitem.location; $scope.device.targetDevice = nestitem.location;
$scope.device.mapType = "nestThermoSet"; $scope.device.mapType = "nestThermoSet";
$scope.device.mapId = nestitem.id + "-SetTemp"; $scope.device.mapId = nestitem.id + "-SetTemp";
$scope.device.onUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"temp\",\"temp\":\"${intensity.percent}\"}"; $scope.device.dimUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"temp\",\"temp\":\"${intensity.percent}\"}";
$scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"temp\",\"temp\":\"${intensity.percent}\"}"; $scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"off\"}";
}; };
$scope.buildNestHeatUrls = function (nestitem) { $scope.buildNestHeatUrls = function (nestitem) {
bridgeService.clearDevice();
$scope.device.deviceType = "thermo"; $scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Heat"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Heat";
$scope.device.targetDevice = nestitem.location; $scope.device.targetDevice = nestitem.location;
@@ -861,6 +1005,7 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}; };
$scope.buildNestCoolUrls = function (nestitem) { $scope.buildNestCoolUrls = function (nestitem) {
bridgeService.clearDevice();
$scope.device.deviceType = "thermo"; $scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Cool"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Cool";
$scope.device.targetDevice = nestitem.location; $scope.device.targetDevice = nestitem.location;
@@ -871,6 +1016,7 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}; };
$scope.buildNestRangeUrls = function (nestitem) { $scope.buildNestRangeUrls = function (nestitem) {
bridgeService.clearDevice();
$scope.device.deviceType = "thermo"; $scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Range"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Range";
$scope.device.targetDevice = nestitem.location; $scope.device.targetDevice = nestitem.location;
@@ -881,6 +1027,7 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}; };
$scope.buildNestOffUrls = function (nestitem) { $scope.buildNestOffUrls = function (nestitem) {
bridgeService.clearDevice();
$scope.device.deviceType = "thermo"; $scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Thermostat"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Thermostat";
$scope.device.targetDevice = nestitem.location; $scope.device.targetDevice = nestitem.location;
@@ -891,6 +1038,7 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}; };
$scope.buildNestFanUrls = function (nestitem) { $scope.buildNestFanUrls = function (nestitem) {
bridgeService.clearDevice();
$scope.device.deviceType = "thermo"; $scope.device.deviceType = "thermo";
$scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Fan"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Fan";
$scope.device.targetDevice = nestitem.location; $scope.device.targetDevice = nestitem.location;
@@ -900,8 +1048,90 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
$scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"fan-auto\"}"; $scope.device.offUrl = "{\"name\":\"" + nestitem.id + "\",\"control\":\"fan-auto\"}";
}; };
$scope.testUrl = function (device, type) { $scope.addDevice = function () {
bridgeService.testUrl(device, type); if($scope.device.name == "" && $scope.device.onUrl == "")
return;
bridgeService.addDevice($scope.device).then(
function () {
$scope.clearDevice();
bridgeService.viewDevices();
bridgeService.viewNestItems();
},
function (error) {
}
);
};
$scope.toggleButtons = function () {
$scope.buttonsVisible = !$scope.buttonsVisible;
if($scope.buttonsVisible)
$scope.imgButtonsUrl = "glyphicon glyphicon-minus";
else
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
};
$scope.deleteDeviceByMapId = function (id, mapType) {
bridgeService.deleteDeviceByMapId(id, mapType);
bridgeService.viewDevices();
bridgeService.viewNestItems();
};
});
app.controller('EditController', function ($scope, $location, $http, bridgeService) {
$scope.bridge = bridgeService.state;
$scope.device = $scope.bridge.device;
$scope.device_dim_control = "";
$scope.bulk = { devices: [] };
var veraList = angular.fromJson($scope.bridge.settings.veraaddress);
if(veraList != null && veraList.devices.length > 0)
$scope.vera = {base: "http://" + veraList.devices[0].ip, port: "3480", id: ""};
else
$scope.vera = {base: "http://", port: "3480", id: ""};
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
$scope.buttonsVisible = false;
$scope.clearDevice = function () {
bridgeService.clearDevice();
};
$scope.buildUrlsUsingDevice = function (dim_control) {
bridgeService.clearDevice();
$scope.device.deviceType = "switch";
$scope.device.targetDevice = "Encapsulated";
$scope.device.mapType = "veraDevice";
$scope.device.mapId = $scope.vera.id;
if(dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0)
$scope.device.dimUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&DeviceNum="
+ $scope.vera.id
+ "&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget="
+ dim_control;
else
$scope.device.dimUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum="
+ $scope.vera.id;
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum="
+ $scope.vera.id;
$scope.device.offUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum="
+ $scope.vera.id;
};
$scope.buildUrlsUsingScene = function () {
bridgeService.clearDevice();
$scope.device.deviceType = "scene";
$scope.device.targetDevice = "Encapsulated";
$scope.device.mapType = "veraScene";
$scope.device.mapId = $scope.vera.id;
$scope.device.onUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum="
+ $scope.vera.id;
$scope.device.offUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum="
+ $scope.vera.id;
}; };
$scope.addDevice = function () { $scope.addDevice = function () {
@@ -938,59 +1168,6 @@ app.controller('AddingController', function ($scope, $location, $http, bridgeSer
}; };
$scope.bulkAddDevices = function(dim_control) {
var devicesList = [];
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);
devicesList[i] = {
name: $scope.device.name,
mapId: $scope.device.mapId,
mapType: $scope.device.mapType,
deviceType: $scope.device.deviceType,
targetDevice: $scope.device.targetDevice,
onUrl: $scope.device.onUrl,
offUrl: $scope.device.offUrl,
httpVerb: $scope.device.httpVerb,
contentType: $scope.device.contentType,
contentBody: $scope.device.contentBody,
contentBodyOff: $scope.device.contentBodyOff
};
}
}
}
bridgeService.bulkAddDevice(devicesList);
$scope.clearDevice();
$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)
$scope.imgButtonsUrl = "glyphicon glyphicon-minus";
else
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
};
$scope.deleteDeviceByMapId = function (id, mapType) {
bridgeService.deleteDeviceByMapId(id, mapType);
};
}); });
app.filter('availableHarmonyActivityId', function(bridgeService) { app.filter('availableHarmonyActivityId', function(bridgeService) {

View File

@@ -1,6 +1,7 @@
<ul class="nav nav-pills" role="tablist"> <ul class="nav nav-pills" role="tablist">
<li role="presentation" class="active"><a href="#">Bridge Devices</a></li> <li role="presentation" class="active"><a href="#">Bridge Devices</a></li>
<li role="presentation"><a href="#/system">Bridge Control</a></li> <li role="presentation"><a href="#/system">Bridge Control</a></li>
<li role="presentation"><a href="#/logs">Logs</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="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li> <li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li> <li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
@@ -13,26 +14,19 @@
<div class="panel-heading"> <div class="panel-heading">
<h2 class="panel-title">Current devices ({{bridge.devices.length}}) </h2> <h2 class="panel-title">Current devices ({{bridge.devices.length}}) </h2>
</div> </div>
<scrollable-table watch="bridge.devices">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="id">ID</th>
<a href="" ng-click="order('id')">ID</a> <th sortable-header col="name">Name</th>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span></th> <th sortable-header col="deviceType">Type</th>
<th> <th sortable-header col="targetDevice">Target</th>
<a href="" ng-click="order('name')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span></th>
<th>
<a href="" ng-click="order('deviceType')">Type</a>
<span class="sortorder" ng-show="predicate === 'deviceType'" ng-class="{reverse:reverse}"></span></th>
<th>
<a href="" ng-click="order('targetDevice')">Target</a>
<span class="sortorder" ng-show="predicate === 'targetDevice'" ng-class="{reverse:reverse}"></span></th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tr ng-repeat="device in bridge.devices | orderBy:predicate:reverse"> <tr ng-repeat="device in bridge.devices">
<td>{{$index+1}}</td> <td>{{$index+1}}</td>
<td>{{device.id}}</td> <td>{{device.id}}</td>
<td>{{device.name}}</td> <td>{{device.name}}</td>
@@ -42,6 +36,8 @@
<p> <p>
<button class="btn btn-info" type="submit" <button class="btn btn-info" type="submit"
ng-click="testUrl(device, 'on')">Test ON</button> ng-click="testUrl(device, 'on')">Test ON</button>
<button class="btn btn-info" type="submit"
ng-click="testUrl(device, 'dim')">Test Dim</button>
<button class="btn btn-info" type="submit" <button class="btn btn-info" type="submit"
ng-click="testUrl(device, 'off')">Test OFF</button> ng-click="testUrl(device, 'off')">Test OFF</button>
<button class="btn btn-warning" type="submit" <button class="btn btn-warning" type="submit"
@@ -52,6 +48,7 @@
</td> </td>
</tr> </tr>
</table> </table>
</scrollable-table>
</div> </div>
<div class="panel panel-default backup"> <div class="panel panel-default backup">
<div class="panel-heading"> <div class="panel-heading">

View File

@@ -1,6 +1,7 @@
<ul class="nav nav-pills" role="tablist"> <ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Bridge Devices</a></li> <li role="presentation"><a href="#">Bridge Devices</a></li>
<li role="presentation"><a href="#/system">Bridge Control</a></li> <li role="presentation"><a href="#/system">Bridge Control</a></li>
<li role="presentation"><a href="#/logs">Logs</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="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li> <li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li> <li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
@@ -82,6 +83,17 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-dim-url">Dim
URL </label>
<div class="col-xs-8 col-sm-7">
<textarea rows="3" class="form-control" id="device-dim-url"
ng-model="device.dimUrl" placeholder="URL to dim device"></textarea>
</div>
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="row"> <div class="row">
<label class="col-xs-12 col-sm-2 control-label" <label class="col-xs-12 col-sm-2 control-label"

View File

@@ -1,6 +1,7 @@
<ul class="nav nav-pills" role="tablist"> <ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Bridge Devices</a></li> <li role="presentation"><a href="#">Bridge Devices</a></li>
<li role="presentation"><a href="#/system">Bridge Control</a></li> <li role="presentation"><a href="#/system">Bridge Control</a></li>
<li role="presentation"><a href="#/logs">Logs</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="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li> <li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li> <li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
@@ -106,6 +107,17 @@
Clear Device</button> Clear Device</button>
</div> </div>
</div> </div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-dim-url">Dim
URL </label>
<div class="col-xs-8 col-sm-7">
<textarea rows="3" class="form-control" id="device-dim-url"
ng-model="device.dimUrl" placeholder="URL to dim device"></textarea>
</div>
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="row"> <div class="row">
<label class="col-xs-12 col-sm-2 control-label" <label class="col-xs-12 col-sm-2 control-label"

View File

@@ -1,6 +1,7 @@
<ul class="nav nav-pills" role="tablist"> <ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Bridge Devices</a></li> <li role="presentation"><a href="#">Bridge Devices</a></li>
<li role="presentation"><a href="#/system">Bridge Control</a></li> <li role="presentation"><a href="#/system">Bridge Control</a></li>
<li role="presentation"><a href="#/logs">Logs</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="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li> <li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li role="presentation" class="active"><a href="#/harmonyactivities">Harmony Activities</a></li> <li role="presentation" class="active"><a href="#/harmonyactivities">Harmony Activities</a></li>
@@ -19,26 +20,18 @@
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. 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> The 'Already Configured Activities' list below will show what is already setup for your Harmony Hubs.</p>
<scrollable-table watch="bridge.harmonyactivities">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="name">Name</th>
<a href="" ng-click="order('label')">Name</a> <th sortable-header col="id">Id</th>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> <th sortable-header col="hub">Hub</th>
</th>
<th>
<a href="" ng-click="order('id')">Id</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('hub')">Hub</a>
<span class="sortorder" ng-show="predicate === 'hub'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | availableHarmonyActivityId | orderBy:predicate:reverse"> <tr ng-repeat="harmonyactivity in bridge.harmonyactivities | availableHarmonyActivityId">
<td>{{$index+1}}</td> <td>{{$index+1}}</td>
<td>{{harmonyactivity.activity.label}}</td> <td>{{harmonyactivity.activity.label}}</td>
<td>{{harmonyactivity.activity.id}}</td> <td>{{harmonyactivity.activity.id}}</td>
@@ -50,6 +43,7 @@
</td> </td>
</tr> </tr>
</table> </table>
</scrollable-table>
</li> </li>
</ul> </ul>
<div class="panel-heading"> <div class="panel-heading">
@@ -57,26 +51,18 @@
</div> </div>
<ul ng-if="buttonsVisible" class="list-group"> <ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<scrollable-table watch="bridge.harmonyactivities">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="name">Name</th>
<a href="" ng-click="order('label')">Name</a> <th sortable-header col="id">Id</th>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> <th sortable-header col="hub">Hub</th>
</th>
<th>
<a href="" ng-click="order('id')">Id</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('hub')">Hub</a>
<span class="sortorder" ng-show="predicate === 'hub'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | unavailableHarmonyActivityId | orderBy:predicate:reverse"> <tr ng-repeat="harmonyactivity in bridge.harmonyactivities | unavailableHarmonyActivityId">
<td>{{$index+1}}</td> <td>{{$index+1}}</td>
<td>{{harmonyactivity.activity.label}}</td> <td>{{harmonyactivity.activity.label}}</td>
<td>{{harmonyactivity.activity.id}}</td> <td>{{harmonyactivity.activity.id}}</td>
@@ -85,6 +71,7 @@
ng-click="deleteDeviceByMapId(harmonyactivity.activity.id, 'harmonyActivity')">Delete</button></td> ng-click="deleteDeviceByMapId(harmonyactivity.activity.id, 'harmonyActivity')">Delete</button></td>
</tr> </tr>
</table> </table>
</scrollable-table>
</li> </li>
</ul> </ul>
</div> </div>

View File

@@ -1,6 +1,7 @@
<ul class="nav nav-pills" role="tablist"> <ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Bridge Devices</a></li> <li role="presentation"><a href="#">Bridge Devices</a></li>
<li role="presentation"><a href="#/system">Bridge Control</a></li> <li role="presentation"><a href="#/system">Bridge Control</a></li>
<li role="presentation"><a href="#/logs">Logs</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="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li> <li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li> <li role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
@@ -20,28 +21,20 @@
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. 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> The 'Already Configured Harmony Buttons' list below will show what is already setup for your Harmony Hubs.</p>
<scrollable-table watch="bridge.harmonydevices">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="name">Name</th>
<a href="" ng-click="order('label')">Name</a> <th sortable-header col="id">Id</th>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> <th sortable-header col="hub">Hub</th>
</th>
<th>
<a href="" ng-click="order('id')">Id</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('hub')">Hub</a>
<span class="sortorder" ng-show="predicate === 'hub'" ng-class="{reverse:reverse}"></span>
</th>
<th>On Button</th> <th>On Button</th>
<th>Off Button</th> <th>Off Button</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tr ng-repeat="harmonydevice in bridge.harmonydevices | orderBy:predicate:reverse"> <tr ng-repeat="harmonydevice in bridge.harmonydevices">
<td>{{$index+1}}</td> <td>{{$index+1}}</td>
<td>{{harmonydevice.device.label}}</td> <td>{{harmonydevice.device.label}}</td>
<td>{{harmonydevice.device.id}}</td> <td>{{harmonydevice.device.id}}</td>
@@ -66,6 +59,7 @@
</td> </td>
</tr> </tr>
</table> </table>
</scrollable-table>
</li> </li>
</ul> </ul>
<div class="panel-heading"> <div class="panel-heading">
@@ -73,26 +67,15 @@
</div> </div>
<ul ng-if="buttonsVisible" class="list-group"> <ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<scrollable-table watch="bridge.harmonydevices">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="name">Name</th>
<a href="" ng-click="order('name')">Name</a> <th sortable-header col="id">Device Id</th>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> <th sortable-header col="targetDevice">Hub</th>
</th> <th>Harmony Device-Button On-Button Off</th>
<th>
<a href="" ng-click="order('id')">Device Id</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('targetDevice')">Hub</a>
<span class="sortorder" ng-show="predicate === 'targetDevice'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('mapId')">Harmony Device-Button On-Button Off</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
@@ -108,6 +91,7 @@
</td> </td>
</tr> </tr>
</table> </table>
</scrollable-table>
</li> </li>
</ul> </ul>
</div> </div>

View File

@@ -0,0 +1,75 @@
<ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Bridge Devices</a></li>
<li role="presentation"><a href="#/system">Bridge Control</a></li>
<li role="presentation" class="active"><a href="#/logs">Logs</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
<div class="panel panel-default bridgeServer">
<div class="panel-heading">
<h1 class="panel-title">Log Messages</h1>
</div>
<div class="panel-body">
<p>
<button class="btn btn-primary" type="submit"
ng-click="updateLogs()">Update Log</button>
</p>
<scrollable-table watch="bridge.logMsgs">
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th sortable-header col="time">Time</th>
<th sortable-header col="level">Level</th>
<th sortable-header col="message">Message</th>
<th sortable-header col="component">Component</th>
</tr>
</thead>
<tr ng-repeat="logMessage in bridge.logMsgs">
<td>{{logMessage.time}}</td>
<td>{{logMessage.level}}</td>
<td>{{logMessage.message}}</td>
<td>{{logMessage.component}}</td>
</tr>
</table>
</scrollable-table>
</div>
</div>
<div class="panel panel-default logconfig">
<div class="panel-heading">
<h1 class="panel-title">Logging Configuration <a ng-click="toggle()"><span class={{imgUrl}} aria-hidden="true"></a></h1>
</div>
<div ng-if="visible" class="animate-if" class="panel-body">
<p>
<button class="btn btn-primary" type="submit"
ng-click="updateLoggers()">Update Log Levels</button>
Show All Loggers <input type="checkbox" ng-model="bridge.logShowAll"
ng-change="reloadLoggers()" ng-true-value=true ng-false-value=false> {{bridge.logShowAll}}
</p>
<scrollable-table watch="bridge.loggerInfo">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th sortable-header col="logLevel">Log Level</th>
<th sortable-header col="loggerName">Component</th>
<th>New Log Level</th>
</tr>
</thead>
<tr ng-repeat="logInfo in bridge.loggerInfo">
<td>{{logInfo.logLevel.substr(0,logInfo.logLevel.indexOf("_"))}}</td>
<td>{{logInfo.loggerName}}</td>
<td>
<select name="new-log-level" id="new-log-level" ng-change="addToUpdate(logInfo)" ng-model="logInfo.newLogLevel">
<option ng-repeat="alevel in levels" value="{{alevel}}">{{alevel.substr(0,alevel.indexOf("_"))}}</option>
</select>
</td>
</tr>
</table>
</scrollable-table>
</div>
</div>

View File

@@ -1,6 +1,7 @@
<ul class="nav nav-pills" role="tablist"> <ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Bridge Devices</a></li> <li role="presentation"><a href="#">Bridge Devices</a></li>
<li role="presentation"><a href="#/system">Bridge Control</a></li> <li role="presentation"><a href="#/system">Bridge Control</a></li>
<li role="presentation"><a href="#/logs">Logs</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="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li> <li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li> <li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
@@ -19,22 +20,14 @@
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. 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> The 'Already Configured Nest Items' list below will show what is already setup for your Nest.</p>
<scrollable-table watch="bridge.nestitems">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="name">Name</th>
<a href="" ng-click="order('name')">Name</a> <th sortable-header col="type">Type</th>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> <th sortable-header col="location">Location</th>
</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> <th>Actions</th>
</tr> </tr>
</thead> </thead>
@@ -71,6 +64,7 @@
</td> </td>
</tr> </tr>
</table> </table>
</scrollable-table>
</li> </li>
</ul> </ul>
<div class="panel-heading"> <div class="panel-heading">
@@ -78,22 +72,14 @@
</div> </div>
<ul ng-if="buttonsVisible" class="list-group"> <ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<scrollable-table watch="bridge.nestitems">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="name">Name</th>
<a href="" ng-click="order('name')">Name</a> <th sortable-header col="id">Device Id</th>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> <th>mapId</th>
</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> <th>Actions</th>
</tr> </tr>
</thead> </thead>
@@ -106,6 +92,7 @@
ng-click="deleteDeviceByMapId(device.mapId, 'nest')">Delete</button></td> ng-click="deleteDeviceByMapId(device.mapId, 'nest')">Delete</button></td>
</tr> </tr>
</table> </table>
</scrollable-table>
</li> </li>
</ul> </ul>
</div> </div>

View File

@@ -1,6 +1,7 @@
<ul class="nav nav-pills" role="tablist"> <ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Bridge Devices</a></li> <li role="presentation"><a href="#">Bridge Devices</a></li>
<li role="presentation" class="active"><a href="#/system">Bridge Control</a></li> <li role="presentation" class="active"><a href="#/system">Bridge Control</a></li>
<li role="presentation"><a href="#/logs">Logs</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="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li> <li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li> <li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
@@ -157,6 +158,11 @@
<td><input id="bridge-settings-nestpwd" class="form-control" type="password" <td><input id="bridge-settings-nestpwd" class="form-control" type="password"
ng-model="bridge.settings.nestpwd" placeholder="thepassword"></td> ng-model="bridge.settings.nestpwd" placeholder="thepassword"></td>
</tr> </tr>
<tr>
<td>Log Messages to Buffer</td>
<td><input id="bridge-settings-numberoflogmessages" class="form-control" type="number"
ng-model="bridge.settings.numberoflogmessages" min="100" max="65535"></td>
</tr>
<tr> <tr>
<td>UPNP Strict Handling</td> <td>UPNP Strict Handling</td>
<td><input type="checkbox" ng-model="bridge.settings.upnpstrict" <td><input type="checkbox" ng-model="bridge.settings.upnpstrict"
@@ -167,15 +173,20 @@
<td><input type="checkbox" ng-model="bridge.settings.traceupnp" <td><input type="checkbox" ng-model="bridge.settings.traceupnp"
ng-true-value=true ng-false-value=false> {{bridge.settings.traceupnp}}</td> ng-true-value=true ng-false-value=false> {{bridge.settings.traceupnp}}</td>
</tr> </tr>
<tr>
<td>Nest Temp Farenheit</td>
<td><input type="checkbox" ng-model="bridge.settings.farenheit"
ng-true-value=true ng-false-value=false> {{bridge.settings.farenheit}}</td>
</tr>
</table> </table>
</form> </form>
</div> </div>
</div> </div>
<div class="panel panel-default backup"> <div class="panel panel-default backup">
<div class="panel-heading"> <div class="panel-heading">
<h1 class="panel-title">Bridge Settings Backup <a ng-click="toggleBk()"><span class={{imgBkUrl}} aria-hidden="true"></a></h1> <h1 class="panel-title">Bridge Settings Backup <a ng-click="toggle()"><span class={{imgUrl}} aria-hidden="true"></a></h1>
</div> </div>
<div ng-if="visibleBk" class="animate-if" class="panel-body"> <div ng-if="visible" class="animate-if" class="panel-body">
<form class="form-horizontal"> <form class="form-horizontal">
<div class="form-group"> <div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="backup-name">Backup Settings File Name</label> <label class="col-xs-12 col-sm-2 control-label" for="backup-name">Backup Settings File Name</label>

View File

@@ -1,6 +1,7 @@
<ul class="nav nav-pills" role="tablist"> <ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Bridge Devices</a></li> <li role="presentation"><a href="#">Bridge Devices</a></li>
<li role="presentation"><a href="#/system">Bridge Control</a></li> <li role="presentation"><a href="#/system">Bridge Control</a></li>
<li role="presentation"><a href="#/logs">Logs</a></li>
<li role="presentation" class="active"><a href="#/veradevices">Vera Devices</a></li> <li role="presentation" class="active"><a href="#/veradevices">Vera Devices</a></li>
<li role="presentation"><a href="#/verascenes">Vera Scenes</a></li> <li role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li> <li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
@@ -30,34 +31,20 @@
<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. <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. 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> </p>
<scrollable-table watch="bridge.veradevices">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="name">Name</th>
<a href="" ng-click="order('name')">Name</a> <th sortable-header col="id">Id</th>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> <th sortable-header col="category">Category</th>
</th> <th sortable-header col="room">Room</th>
<th> <th sortable-header col="veraname">Vera</th>
<a href="" ng-click="order('id')">Id</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('category')">Category</a>
<span class="sortorder" ng-show="predicate === 'category'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('room')">Room</a>
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('vera')">Vera</a>
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tr ng-repeat="veradevice in bridge.veradevices | availableVeraDeviceId | orderBy:predicate:reverse"> <tr ng-repeat="veradevice in bridge.veradevices | availableVeraDeviceId">
<td>{{$index+1}}</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><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.id}}</td>
@@ -71,6 +58,7 @@
</td> </td>
</tr> </tr>
</table> </table>
</scrollable-table>
<p> <p>
<button class="btn btn-success" type="submit" <button class="btn btn-success" type="submit"
ng-click="bulkAddDevices(device_dim_control)">Bulk Add ({{bulk.devices.length}})</button> ng-click="bulkAddDevices(device_dim_control)">Bulk Add ({{bulk.devices.length}})</button>
@@ -82,34 +70,20 @@
</div> </div>
<ul ng-if="buttonsVisible" class="list-group"> <ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<scrollable-table watch="bridge.veradevices">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="name">Name</th>
<a href="" ng-click="order('name')">Name</a> <th sortable-header col="id">Id</th>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> <th sortable-header col="category">Category</th>
</th> <th sortable-header col="room">Room</th>
<th> <th sortable-header col="veraname">Vera</th>
<a href="" ng-click="order('id')">Id</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('category')">Category</a>
<span class="sortorder" ng-show="predicate === 'category'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('room')">Room</a>
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('vera')">Vera</a>
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tr ng-repeat="veradevice in bridge.veradevices | unavailableVeraDeviceId | orderBy:predicate:reverse"> <tr ng-repeat="veradevice in bridge.veradevices | unavailableVeraDeviceId">
<td>{{$index+1}}</td> <td>{{$index+1}}</td>
<td>{{veradevice.name}}</td> <td>{{veradevice.name}}</td>
<td>{{veradevice.id}}</td> <td>{{veradevice.id}}</td>
@@ -122,6 +96,7 @@
</td> </td>
</tr> </tr>
</table> </table>
</scrollable-table>
</li> </li>
</ul> </ul>
</div> </div>
@@ -155,6 +130,17 @@
<button class="btn btn-danger" ng-click="clearDevice()"> <button class="btn btn-danger" ng-click="clearDevice()">
Clear Device</button> Clear Device</button>
</div> </div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-dim-url">Dim
URL </label>
<div class="col-xs-8 col-sm-7">
<textarea rows="3" class="form-control" id="device-dim-url"
ng-model="device.dimUrl" placeholder="URL to dim device"></textarea>
</div>
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="row"> <div class="row">
<label class="col-xs-12 col-sm-2 control-label" <label class="col-xs-12 col-sm-2 control-label"

View File

@@ -1,6 +1,7 @@
<ul class="nav nav-pills" role="tablist"> <ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Bridge Devices</a></li> <li role="presentation"><a href="#">Bridge Devices</a></li>
<li role="presentation"><a href="#/system">Bridge Control</a></li> <li role="presentation"><a href="#/system">Bridge Control</a></li>
<li role="presentation"><a href="#/logs">Logs</a></li>
<li role="presentation"><a href="#/veradevices">Vera Devices</a></li> <li role="presentation"><a href="#/veradevices">Vera Devices</a></li>
<li role="presentation" class="active"><a href="#/verascenes">Vera Scenes</a></li> <li role="presentation" class="active"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li> <li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
@@ -19,30 +20,19 @@
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. 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> The 'Already Configured Vera Scenes' list below will show what is already setup for your Vera.</p>
<scrollable-table watch="bridge.verascenes">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="name">Name</th>
<a href="" ng-click="order('name')">Name</a> <th sortable-header col="id">Id</th>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> <th sortable-header col="room">Room</th>
</th> <th sortable-header col="veraname">Vera</th>
<th>
<a href="" ng-click="order('id')">Id</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('room')">Room</a>
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('vera')">Vera</a>
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tr ng-repeat="verascene in bridge.verascenes | availableVeraSceneId | orderBy:predicate:reverse"> <tr ng-repeat="verascene in bridge.verascenes | availableVeraSceneId">
<td>{{$index+1}}</td> <td>{{$index+1}}</td>
<td>{{verascene.name}}</td> <td>{{verascene.name}}</td>
<td>{{verascene.id}}</td> <td>{{verascene.id}}</td>
@@ -55,6 +45,7 @@
</td> </td>
</tr> </tr>
</table> </table>
</scrollable-table>
</li> </li>
</ul> </ul>
<div class="panel-heading"> <div class="panel-heading">
@@ -62,30 +53,19 @@
</div> </div>
<ul ng-if="buttonsVisible" class="list-group"> <ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<scrollable-table watch="bridge.verascenes">
<table class="table table-bordered table-striped table-hover"> <table class="table table-bordered table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Row</th> <th>Row</th>
<th> <th sortable-header col="name">Name</th>
<a href="" ng-click="order('name')">Name</a> <th sortable-header col="id">Id</th>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> <th sortable-header col="room">Room</th>
</th> <th sortable-header col="veraname">Vera</th>
<th>
<a href="" ng-click="order('id')">Id</a>
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('room')">Room</a>
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('vera')">Vera</a>
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tr ng-repeat="verascene in bridge.verascenes | unavailableVeraSceneId | orderBy:predicate:reverse"> <tr ng-repeat="verascene in bridge.verascenes | unavailableVeraSceneId">
<td>{{$index+1}}</td> <td>{{$index+1}}</td>
<td>{{verascene.name}}</td> <td>{{verascene.name}}</td>
<td>{{verascene.id}}</td> <td>{{verascene.id}}</td>
@@ -97,6 +77,7 @@
</td> </td>
</tr> </tr>
</table> </table>
</scrollable-table>
</li> </li>
</ul> </ul>
</div> </div>