Compare commits

..

8 Commits

Author SHA1 Message Date
BWS Systems
fcb31b8f76 Update for HomeGenie type handling 2019-06-26 14:05:36 -05:00
BWS Systems
0205633684 Update version to next release as the tag is on the wrong branch 2019-06-25 09:49:55 -05:00
BWS Systems
7b48590807 updated upnp notify to add root and basic schemas 2019-06-25 09:27:10 -05:00
BWS Systems
5f7cd70710 Added back to top button on long scroll 2019-06-17 13:04:07 -05:00
BWS Systems
46ad4489ad Finish HomeAssistant auth changes and implement no scroll for pages 2019-06-14 10:32:15 -05:00
BWS Systems
bfd1b94473 Start adding new Bearer Token for Home Assistant 2019-06-13 15:51:49 -05:00
BWS Systems
7d920d3885 Update pom.xml for build instructions for java 1.8 and raspberry pi 2019-06-13 10:11:24 -05:00
BWS Systems
2dbf4c96c4 Finished config up/down load impl and Finished startup action implementation 2019-06-12 16:05:29 -05:00
17 changed files with 1182 additions and 691 deletions

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>5.2.next_e</version> <version>5.3.0RC6</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>HA Bridge</name> <name>HA Bridge</name>
@@ -167,6 +167,7 @@
<configuration> <configuration>
<rules> <rules>
<requireMavenVersion> <requireMavenVersion>
<!-- Change this to Version 3.3 for Java 1.8 and Raspberry PI compilation -->
<version>3.6</version> <version>3.6</version>
</requireMavenVersion> </requireMavenVersion>
</rules> </rules>
@@ -178,7 +179,11 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<!-- Comment this release line out for Java 1.8 and Raspberry PI compilation -->
<release>11</release> <release>11</release>
<!-- Uncomment the next two lines for Java 1.8 and Raspberry PI compilation -->
<!-- <source>1.8</source> -->
<!-- <target>1.8</target> -->
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>

View File

@@ -437,6 +437,41 @@ public class SystemControl {
return stop(); return stop();
}); });
// http://ip_address:port/system/devices/backup/download CORS request
options(SYSTEM_CONTEXT + "/backup/download", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (SYSTEM_CONTEXT + "/backup/download", "application/json", (request, response) -> {
log.debug("Create download: {}", request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
String backupContent = bridgeSettings.downloadBackup(aFilename.getFilename());
return backupContent;
}, new JsonTransformer());
// http://ip_address:port/system/devices/backup/upload CORS request
options(SYSTEM_CONTEXT + "/backup/upload/:filename", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (SYSTEM_CONTEXT + "/backup/upload/:filename", "application/json", (request, response) -> {
log.debug("Create upload: {} - {}", request.params(":filename"), request.body());
String theSuccess = bridgeSettings.uploadBackup(request.params(":filename"), request.body());
if(theSuccess.contains("Error:"))
response.status(HttpStatus.SC_METHOD_FAILURE);
else
response.status(HttpStatus.SC_OK);
return theSuccess;
}, new JsonTransformer());
// http://ip_address:port/system/backup/available returns a list of config backup filenames // http://ip_address:port/system/backup/available returns a list of config backup filenames
get (SYSTEM_CONTEXT + "/backup/available", (request, response) -> { get (SYSTEM_CONTEXT + "/backup/available", (request, response) -> {
log.debug("Get backup filenames"); log.debug("Get backup filenames");

View File

@@ -89,6 +89,9 @@ public class DeviceDescriptor{
@SerializedName("lockDeviceId") @SerializedName("lockDeviceId")
@Expose @Expose
private boolean lockDeviceId; private boolean lockDeviceId;
@SerializedName("startupActions")
@Expose
private String startupActions;
public String getName() { public String getName() {
return name; return name;
@@ -344,4 +347,12 @@ public class DeviceDescriptor{
public void setLockDeviceId(boolean lockDeviceId) { public void setLockDeviceId(boolean lockDeviceId) {
this.lockDeviceId = lockDeviceId; this.lockDeviceId = lockDeviceId;
} }
public String getStartupActions() {
return startupActions;
}
public void setStartupActions(String startupActions) {
this.startupActions = startupActions;
}
} }

View File

@@ -202,15 +202,33 @@ public class DeviceRepository extends BackupHandler {
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values()); List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
Iterator<DeviceDescriptor> deviceIterator = list.iterator(); Iterator<DeviceDescriptor> deviceIterator = list.iterator();
Map<String, DeviceDescriptor> newdevices = new HashMap<String, DeviceDescriptor>(); Map<String, DeviceDescriptor> newdevices = new HashMap<String, DeviceDescriptor>();
List<String> lockedIds = new ArrayList<String>();
nextId = seedId;
String hexValue; String hexValue;
Integer newValue; Integer newValue;
DeviceDescriptor theDevice; DeviceDescriptor theDevice;
log.debug("Renumber devices with seed: {}", seedId); boolean findNext = true;
nextId = seedId;
while(deviceIterator.hasNext()) {
theDevice = deviceIterator.next();
if(theDevice.isLockDeviceId()) {
lockedIds.add(theDevice.getId());
}
}
log.debug("Renumber devices starting with: {}", nextId);
deviceIterator = list.iterator();
while (deviceIterator.hasNext()) { while (deviceIterator.hasNext()) {
theDevice = deviceIterator.next(); theDevice = deviceIterator.next();
if (!theDevice.isLockDeviceId()) { if (!theDevice.isLockDeviceId()) {
findNext = true;
while(findNext) {
if(lockedIds.contains(String.valueOf(nextId))) {
nextId++;
} else {
findNext = false;
}
}
theDevice.setId(String.valueOf(nextId)); theDevice.setId(String.valueOf(nextId));
newValue = nextId % 256; newValue = nextId % 256;
if (newValue <= 0) if (newValue <= 0)

View File

@@ -41,6 +41,7 @@ import org.slf4j.LoggerFactory;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.awt.Color;
import java.util.Arrays; import java.util.Arrays;
/** /**
@@ -77,6 +78,7 @@ public class HueMulator {
// 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("Hue emulator service started....");
startupDeviceCall();
before(HUE_CONTEXT + "/*", (request, response) -> { before(HUE_CONTEXT + "/*", (request, response) -> {
// This currently causes an error with Spark replies // This currently causes an error with Spark replies
// String path = request.pathInfo(); // String path = request.pathInfo();
@@ -1671,4 +1673,50 @@ public class HueMulator {
return responseString; return responseString;
} }
private void startupDeviceCall() {
String aUserId = bridgeSettingMaster.getBridgeSecurity().createWhitelistUser("test_ha_bridge");
List<DeviceDescriptor> deviceList = repository.findAll();
String aChangeBody;
String[] components;
boolean comma = false;
for (DeviceDescriptor aDevice : deviceList) {
if(aDevice.getStartupActions() != null && !aDevice.getStartupActions().isEmpty()) {
log.info("Startup call for {} with startupActions {}", aDevice.getName(), aDevice.getStartupActions());
aChangeBody = "{";
components = aDevice.getStartupActions().split(":");
if(components.length > 0 && components[0] != null && components[0].length() > 0) {
if(components[0].equals("On")) {
aChangeBody = aChangeBody + "\"on\":true";
}
else {
aChangeBody = aChangeBody + "\"on\":false";
}
comma = true;
}
if(components.length > 1 && components[1] != null && components[1].length() > 0 && !(components.length > 2 && components[2] != null && components[2].length() > 0)) {
if(comma)
aChangeBody = aChangeBody + ",";
aChangeBody = aChangeBody + "\"bri\":" + components[1];
comma = true;
}
if(components.length > 2 && components[2] != null && components[2].length() > 0) {
if(comma)
aChangeBody = aChangeBody + ",";
String theRGB = components[2].substring(components[2].indexOf('(') + 1, components[2].indexOf(')'));
String[] RGB = theRGB.split(",");
float[] hsb = new float[3];
Color.RGBtoHSB(Integer.parseInt(RGB[0]), Integer.parseInt(RGB[1]), Integer.parseInt(RGB[2]), hsb);
float hue = hsb[0] * (float) 360.0;
float sat = hsb[1] * (float) 100.0;
float bright = hsb[2] * (float) 100.0;
aChangeBody = String.format("%s\"hue\":%.2f,\"sat\":%.2f,\"bri\":%d", aChangeBody, hue, sat, Math.round(bright));
}
aChangeBody = aChangeBody + "}";
log.info("Startup call to set state for {} with body {}", aDevice.getName(), aChangeBody);
changeState(aUserId, aDevice.getId(), aChangeBody, "localhost", true);
}
}
}
} }

View File

@@ -0,0 +1,13 @@
package com.bwssystems.HABridge.plugins.hass;
public class HassAuth {
boolean legacyauth;
public boolean isLegacyauth() {
return legacyauth;
}
public void setLegacyauth(boolean legacyauth) {
this.legacyauth = legacyauth;
}
}

View File

@@ -19,11 +19,16 @@ public class HomeAssistant {
private static final Logger log = LoggerFactory.getLogger(HomeAssistant.class); private static final Logger log = LoggerFactory.getLogger(HomeAssistant.class);
private NamedIP hassAddress; private NamedIP hassAddress;
private HTTPHandler anHttpHandler; private HTTPHandler anHttpHandler;
private HassAuth theAuthType;
private NameValue[] headers;
public HomeAssistant(NamedIP addressName) { public HomeAssistant(NamedIP addressName) {
super(); super();
anHttpHandler = HTTPHome.getHandler(); anHttpHandler = HTTPHome.getHandler();
hassAddress = addressName; hassAddress = addressName;
theAuthType = null;
headers = null;
isLegacyAuth();
} }
public NamedIP getHassAddress() { public NamedIP getHassAddress() {
@@ -35,8 +40,8 @@ public class HomeAssistant {
} }
public Boolean callCommand(HassCommand aCommand) { public Boolean callCommand(HassCommand aCommand) {
log.debug("calling HomeAssistant: " + aCommand.getHassName() + " - " log.debug("calling HomeAssistant: " + aCommand.getHassName() + " - " + aCommand.getEntityId() + " - "
+ aCommand.getEntityId() + " - " + aCommand.getState() + " - " + aCommand.getBri()); + aCommand.getState() + " - " + aCommand.getBri());
String aUrl = null; String aUrl = null;
String domain = aCommand.getEntityId().substring(0, aCommand.getEntityId().indexOf(".")); String domain = aCommand.getEntityId().substring(0, aCommand.getEntityId().indexOf("."));
aUrl = hassAddress.getHttpPreamble() + "/api/services/"; aUrl = hassAddress.getHttpPreamble() + "/api/services/";
@@ -45,22 +50,14 @@ public class HomeAssistant {
else else
aUrl = aUrl + domain; aUrl = aUrl + domain;
String aBody = "{\"entity_id\":\"" + aCommand.getEntityId() + "\""; String aBody = "{\"entity_id\":\"" + aCommand.getEntityId() + "\"";
NameValue[] headers = null; headers = getAuthHeader();
if(hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
NameValue password = new NameValue();
password.setName("x-ha-access");
password.setValue(hassAddress.getPassword());
headers = new NameValue[1];
headers[0] = password;
}
if (aCommand.getState().equalsIgnoreCase("on")) { if (aCommand.getState().equalsIgnoreCase("on")) {
aUrl = aUrl + "/turn_on"; aUrl = aUrl + "/turn_on";
if (aCommand.getBri() != null) if (aCommand.getBri() != null)
aBody = aBody + ",\"brightness\":" + aCommand.getBri() + "}"; aBody = aBody + ",\"brightness\":" + aCommand.getBri() + "}";
else else
aBody = aBody + "}"; aBody = aBody + "}";
} } else {
else {
aUrl = aUrl + "/turn_off"; aUrl = aUrl + "/turn_off";
aBody = aBody + "}"; aBody = aBody + "}";
} }
@@ -75,14 +72,7 @@ public class HomeAssistant {
State[] theHassStates; State[] theHassStates;
String theUrl = null; String theUrl = null;
String theData; String theData;
NameValue[] headers = null; headers = getAuthHeader();
if(hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
NameValue password = new NameValue();
password.setName("x-ha-access");
password.setValue(hassAddress.getPassword());
headers = new NameValue[1];
headers[0] = password;
}
if (hassAddress.getSecure() != null && hassAddress.getSecure()) if (hassAddress.getSecure() != null && hassAddress.getSecure())
theUrl = "https"; theUrl = "https";
else else
@@ -93,20 +83,50 @@ public class HomeAssistant {
log.debug("GET Hass States - data: " + theData); log.debug("GET Hass States - data: " + theData);
theHassStates = new Gson().fromJson(theData, State[].class); theHassStates = new Gson().fromJson(theData, State[].class);
if (theHassStates == null) { if (theHassStates == null) {
log.warn("Cannot get an devices for HomeAssistant " + hassAddress.getName() + " as response is not parsable."); log.warn("Cannot get an devices for HomeAssistant " + hassAddress.getName()
} + " as response is not parsable.");
else { } else {
theDeviceStates = new ArrayList<State>(Arrays.asList(theHassStates)); theDeviceStates = new ArrayList<State>(Arrays.asList(theHassStates));
} }
} } else
else
log.warn("Cannot get an devices for HomeAssistant " + hassAddress.getName() + " http call failed."); log.warn("Cannot get an devices for HomeAssistant " + hassAddress.getName() + " http call failed.");
return theDeviceStates; return theDeviceStates;
} }
protected void closeClient() { protected void closeClient() {
anHttpHandler.closeHandler(); anHttpHandler.closeHandler();
anHttpHandler = null; anHttpHandler = null;
} }
private boolean isLegacyAuth() {
if (theAuthType == null) {
try {
theAuthType = new Gson().fromJson(hassAddress.getExtensions(), HassAuth.class);
} catch (Exception e) {
log.warn("Could not read hass auth - continuing with defaults.");
theAuthType = new HassAuth();
theAuthType.setLegacyauth(false);
}
}
return theAuthType.isLegacyauth();
}
private NameValue[] getAuthHeader() {
if (headers == null) {
if (hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
headers = new NameValue[1];
headers[0] = new NameValue();
if (isLegacyAuth()) {
headers[0].setName("x-ha-access");
headers[0].setValue(hassAddress.getPassword());
} else {
headers[0].setName("Authorization");
headers[0].setValue("Bearer " + hassAddress.getPassword());
}
}
} else if(hassAddress.getPassword() == null || hassAddress.getPassword().isEmpty()) {
headers = null;
}
return headers;
}
} }

View File

@@ -61,12 +61,12 @@ public class HomeGenieInstance {
if (hgModules != null && hgModules.length > 0) { if (hgModules != null && hgModules.length > 0) {
deviceList = new ArrayList<Module>(); deviceList = new ArrayList<Module>();
for (int i = 0; i < hgModules.length; i++) { for (int i = 0; i < hgModules.length; i++) {
if (hgModules[i].isSwitch() || hgModules[i].isDimmer()) if (hgModules[i].isModuleValid(homegenieIP.getExtensions()))
deviceList.add(hgModules[i]); deviceList.add(hgModules[i]);
} }
} }
} catch (Exception e) { } catch (Exception e) {
log.warn("Cannot get an devices for Homegenie {} Gson Parse Error.", homegenieIP.getName()); log.warn("Cannot get devices for Homegenie {} Gson Parse Error.", homegenieIP.getName(), e);
} }
} }
return deviceList; return deviceList;

View File

@@ -1,11 +1,20 @@
package com.bwssystems.HABridge.plugins.homegenie; package com.bwssystems.HABridge.plugins.homegenie;
import java.util.List;
// import java.util.List; // import java.util.List;
import com.google.gson.annotations.Expose; import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class Module { public class Module {
private static final Logger log = LoggerFactory.getLogger(HomeGenieInstance.class);
@SerializedName("Name") @SerializedName("Name")
@Expose @Expose
@@ -23,9 +32,9 @@ public class Module {
@Expose @Expose
private String address; private String address;
/* /*
@SerializedName("Properties") * @SerializedName("Properties")
@Expose *
private List<Property> properties = null; * @Expose private List<Property> properties = null;
*/ */
@SerializedName("RoutingNode") @SerializedName("RoutingNode")
@Expose @Expose
@@ -70,14 +79,12 @@ public class Module {
public void setAddress(String address) { public void setAddress(String address) {
this.address = address; this.address = address;
} }
/*
public List<Property> getProperties() {
return properties;
}
public void setProperties(List<Property> properties) { /*
this.properties = properties; * public List<Property> getProperties() { return properties; }
} *
* public void setProperties(List<Property> properties) { this.properties =
* properties; }
*/ */
public String getRoutingNode() { public String getRoutingNode() {
return routingNode; return routingNode;
@@ -95,10 +102,48 @@ public class Module {
return isDeviceType("Dimmer"); return isDeviceType("Dimmer");
} }
public boolean isLight() {
return isDeviceType("Light");
}
private boolean isDeviceType(String theType) { private boolean isDeviceType(String theType) {
if (deviceType.equals(theType)) { if (deviceType.equals(theType)) {
return true; return true;
} }
return false; return false;
} }
public boolean isModuleValid(JsonObject theExtensions) {
ModuleTypes moduleTypes = null;
if (this.name == null || this.name.trim().isEmpty())
return false;
if (isSwitch())
return true;
if (isDimmer())
return true;
if (isLight())
return true;
if (theExtensions != null && theExtensions.isJsonObject() && theExtensions.get("moduleTypes").isJsonArray()) {
try {
moduleTypes = new Gson().fromJson(theExtensions, ModuleTypes.class);
} catch (Exception e) {
log.warn("Could not parse extensions for {} with {}", this.name, theExtensions);
return false;
}
if (moduleTypes == null)
return false;
for (ModuleType moduleType : moduleTypes.getModuleTypes()) {
if (isDeviceType(moduleType.getModuleType()))
return true;
}
}
return false;
}
} }

View File

@@ -0,0 +1,20 @@
package com.bwssystems.HABridge.plugins.homegenie;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class ModuleType {
@SerializedName("moduleType")
@Expose
private String moduleType;
public String getModuleType() {
return moduleType;
}
public void setModuleType(String moduleType) {
this.moduleType = moduleType;
}
}

View File

@@ -0,0 +1,21 @@
package com.bwssystems.HABridge.plugins.homegenie;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class ModuleTypes {
@SerializedName("moduleTypes")
@Expose
private List<ModuleType> moduleTypes = null;
public List<ModuleType> getModuleTypes() {
return moduleTypes;
}
public void setModuleTypes(List<ModuleType> moduleTypes) {
this.moduleTypes = moduleTypes;
}
}

View File

@@ -53,6 +53,16 @@ public class UpnpListener {
+ HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n" + "NT: uuid:" + HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n" + "NT: uuid:"
+ HueConstants.UUID_PREFIX + "%s\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n"; + HueConstants.UUID_PREFIX + "%s\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate2 = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n"
+ "NT: upnp:rootdevice\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String notifyTemplate3 = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n"
+ "NT: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl, public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl,
UDPDatagramSender aUdpDatagramSender) throws IOException { UDPDatagramSender aUdpDatagramSender) throws IOException {
super(); super();
@@ -242,9 +252,9 @@ public class UpnpListener {
discoveryResponse = String.format(responseTemplateOriginal, Configuration.UPNP_MULTICAST_ADDRESS, discoveryResponse = String.format(responseTemplateOriginal, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID); Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) { if (traceupnp) {
log.info("Traceupnp: send upnp discovery template Original with response address: " + httpLocationAddress + ":" log.info("Traceupnp: send upnp discovery template Original with response address: "
+ httpServerPort + " to address: " + requester + ":" + sourcePort); + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
} else }
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " with discovery responseTemplateOriginal is <<<" + discoveryResponse + ">>>"); + " with discovery responseTemplateOriginal is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
@@ -252,9 +262,9 @@ public class UpnpListener {
discoveryResponse = String.format(responseTemplateOriginal, Configuration.UPNP_MULTICAST_ADDRESS, discoveryResponse = String.format(responseTemplateOriginal, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID); Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) { if (traceupnp) {
log.info("Traceupnp: send upnp discovery template Original with response address: " + httpLocationAddress + ":" log.info("Traceupnp: send upnp discovery template Original with response address: "
+ httpServerPort + " to address: " + requester + ":" + sourcePort); + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
} else }
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " with discovery responseTemplateOriginal is <<<" + discoveryResponse + ">>>"); + " with discovery responseTemplateOriginal is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
@@ -268,7 +278,7 @@ public class UpnpListener {
if (traceupnp) { if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 1 with response address: " + httpLocationAddress + ":" log.info("Traceupnp: send upnp discovery template 1 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort); + httpServerPort + " to address: " + requester + ":" + sourcePort);
} else }
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " with discovery responseTemplate1 is <<<" + discoveryResponse + ">>>"); + " with discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
@@ -284,7 +294,7 @@ public class UpnpListener {
if (traceupnp) { if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":" log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort); + httpServerPort + " to address: " + requester + ":" + sourcePort);
} else }
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>"); + " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
@@ -299,7 +309,7 @@ public class UpnpListener {
if (traceupnp) { if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":" log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort); + httpServerPort + " to address: " + requester + ":" + sourcePort);
} else }
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>"); + " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
@@ -316,17 +326,50 @@ public class UpnpListener {
protected void sendUpnpNotify(InetAddress aSocketAddress) { protected void sendUpnpNotify(InetAddress aSocketAddress) {
String notifyData = null; String notifyData = null;
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
notifyData = String.format(notifyTemplate, Configuration.UPNP_MULTICAST_ADDRESS, notifyData = String.format(notifyTemplate, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID); Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
log.debug("sendUpnpNotify notifyTemplate is <<<" + notifyData + ">>>"); sendNotifyDatagram(notifyData, aSocketAddress, "notifyTemplate1");
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
notifyData = String.format(notifyTemplate2, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID);
sendNotifyDatagram(notifyData, aSocketAddress, "notifyTemplate2");
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
notifyData = String.format(notifyTemplate3, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID);
sendNotifyDatagram(notifyData, aSocketAddress, "notifyTemplate3");
}
public void sendNotifyDatagram(String notifyData, InetAddress aSocketAddress, String templateNumber) {
if (traceupnp) {
log.info("Traceupnp: sendUpnpNotify {}", templateNumber);
}
log.debug("sendUpnpNotify {} is <<<{}>>>", templateNumber, notifyData);
DatagramPacket notifyPacket = new DatagramPacket(notifyData.getBytes(), notifyData.length(), aSocketAddress, DatagramPacket notifyPacket = new DatagramPacket(notifyData.getBytes(), notifyData.length(), aSocketAddress,
Configuration.UPNP_DISCOVERY_PORT); Configuration.UPNP_DISCOVERY_PORT);
try { try {
upnpMulticastSocket.send(notifyPacket); upnpMulticastSocket.send(notifyPacket);
} catch (IOException e1) { } catch (IOException e1) {
log.warn("UpnpListener encountered an error sending upnp notify packet. IP: " log.warn("UpnpListener encountered an error sending upnp {}. IP: {} with message: {}", templateNumber,
+ notifyPacket.getAddress().getHostAddress() + " with message: " + e1.getMessage()); notifyPacket.getAddress().getHostAddress(), e1.getMessage());
log.debug("UpnpListener send upnp notify exception: ", e1); log.debug("UpnpListener send {} exception: ", templateNumber, e1);
} }
} }

View File

@@ -29,7 +29,9 @@
.scrollArea { .scrollArea {
height: 100%; height: 100%;
/* THis makes the table scroll - disabled as a feature for now
max-height: 800px; max-height: 800px;
*/
overflow-x: auto; overflow-x: auto;
overflow-y: auto; overflow-y: auto;
border: 1px solid #d5d5d5; border: 1px solid #d5d5d5;

View File

@@ -17,6 +17,23 @@
<link href="css/strength-meter.min.css" rel="stylesheet"> <link href="css/strength-meter.min.css" rel="stylesheet">
<link href="css/colorpicker.min.css" rel="stylesheet"> <link href="css/colorpicker.min.css" rel="stylesheet">
<style id="compiled-css" type="text/css">
.goToTop
{
position: fixed;
width: 100px;
height: 40px;
bottom: 0;
right: 0;
z-index: 100000;
cursor: pointer;
margin: 10px;
-moz-opacity: 0.60;
opacity: .60;
filter: alpha(opacity=60);
}
</style>
<!--[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>
<script type="text/javascript" src="js/respond.min.js"></script> <script type="text/javascript" src="js/respond.min.js"></script>
@@ -75,6 +92,10 @@
</div> </div>
<span class="goToTop">
<button type="button" class="btn btn-light"><i class="glyphicon glyphicon-chevron-up"></i> Back to Top</button>
</span>
<!-- <input type="button" class="goToTop" value="Back to Top" style="display:none;background-color:blue" /> -->
<script src="js/jquery-1.11.3.min.js"></script> <script src="js/jquery-1.11.3.min.js"></script>
<script src="js/angular.min.js"></script> <script src="js/angular.min.js"></script>
@@ -93,5 +114,35 @@
<script src="js/ng-file-upload-shim.min.js"></script> <script src="js/ng-file-upload-shim.min.js"></script>
<script src="js/ng-file-upload.min.js"></script> <script src="js/ng-file-upload.min.js"></script>
<script src="scripts/app.js"></script> <script src="scripts/app.js"></script>
<script type="text/javascript">
$(window).load(function(){
$(window).scroll(function () {
if ($(this).scrollTop() > 100) {
$('.goToTop').fadeIn();
} else {
$('.goToTop').fadeOut();
}
});
$('.goToTop').click(function () {
$("html, body").animate({ scrollTop: 0 }, 1000);
return false;
});
});
</script>
<script>
// tell the embed parent frame the height of the content
if (window.parent && window.parent.parent){
window.parent.parent.postMessage(["resultsFrame", {
height: document.body.getBoundingClientRect().height,
slug: "fvdrw83s"
}], "*")
}
// always overwrite window.name, in case users try to set it manually
window.name = "result"
</script>
</body> </body>
</html> </html>

View File

@@ -1279,7 +1279,7 @@ app.service('bridgeService', function ($rootScope, $http, $base64, $location, ng
); );
}; };
this.downloadBackup = function (afilename) { this.downloadDeviceBackup = function (afilename) {
return $http.put(this.state.base + "/backup/download", { return $http.put(this.state.base + "/backup/download", {
filename: afilename filename: afilename
}).then( }).then(
@@ -1459,6 +1459,54 @@ app.service('bridgeService', function ($rootScope, $http, $base64, $location, ng
); );
}; };
this.downloadConfigBackup = function (afilename) {
return $http.put(this.state.systemsbase + "/backup/download", {
filename: afilename
}).then(
function (response) {
self.state.backupContent = response.data;
var blob = new Blob([self.state.backupContent], {
type: 'text/plain'
});
var downloadLink = angular.element('<a></a>');
downloadLink.attr('href', window.URL.createObjectURL(blob));
downloadLink.attr('download', afilename);
downloadLink[0].click();
},
function (error) {
if (error.status === 401)
$rootScope.$broadcast('securityReinit', 'done');
else
self.displayWarn("Download Backup Config File Error:", error);
}
);
};
this.uploadConfigFile = function (filename, file) {
file.upload = Upload.http({
url: this.state.systemsbase + "/backup/upload/" + filename,
method: 'PUT',
headers: {
'Content-Type': file.type
},
data: file
});
file.upload.then(function (response) {
file.result = response.data;
self.viewConfigs();
}, function (response) {
if (response.status === 401)
$rootScope.$broadcast('securityReinit', 'done');
else if (response.status > 0)
self.displayWarn('Upload Backup Config File Error:' + response.status + ': ' + response.data);
});
file.upload.progress(function (evt) {
file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
});
}
this.deleteDevice = function (id) { this.deleteDevice = function (id) {
return $http.delete(this.state.base + "/" + id).then( return $http.delete(this.state.base + "/" + id).then(
function (response) { function (response) {
@@ -1827,7 +1875,7 @@ app.controller('SystemController', function ($scope, $location, bridgeService, n
} }
} }
}; };
$scope.addHasstoSettings = function (newhassname, newhassip, newhassport, newhasspassword, newhasssecure) { $scope.addHasstoSettings = function (newhassname, newhassip, newhassport, newhasspassword, newhasssecure, newhassauth) {
if ($scope.bridge.settings.hassaddress === undefined || $scope.bridge.settings.hassaddress === null) { if ($scope.bridge.settings.hassaddress === undefined || $scope.bridge.settings.hassaddress === null) {
$scope.bridge.settings.hassaddress = { $scope.bridge.settings.hassaddress = {
devices: [] devices: []
@@ -1838,7 +1886,8 @@ app.controller('SystemController', function ($scope, $location, bridgeService, n
ip: newhassip, ip: newhassip,
port: newhassport, port: newhassport,
password: newhasspassword, password: newhasspassword,
secure: newhasssecure secure: newhasssecure,
extensions: newhassauth
}; };
$scope.bridge.settings.hassaddress.devices.push(newhass); $scope.bridge.settings.hassaddress.devices.push(newhass);
$scope.newhassname = null; $scope.newhassname = null;
@@ -2022,12 +2071,39 @@ app.controller('SystemController', function ($scope, $location, bridgeService, n
} }
}; };
$scope.addHomeGenietoSettings = function (newhomegeniename, newhomegenieip, newhomegenieport, newhomegenieusername, newhomegeniepassword, newhomegeniewebhook, newhomegeniesecure) { $scope.addHomeGenietoSettings = function (newhomegeniename, newhomegenieip, newhomegenieport, newhomegenieusername, newhomegeniepassword, newhomegeniewebhook, newhomegeniesecure, newhomegenieothertypes) {
if ($scope.bridge.settings.homegenieaddress === undefined || $scope.bridge.settings.homegenieaddress === null) { if ($scope.bridge.settings.homegenieaddress === undefined || $scope.bridge.settings.homegenieaddress === null) {
$scope.bridge.settings.homegenieaddress = { $scope.bridge.settings.homegenieaddress = {
devices: [] devices: []
}; };
} }
var othertypes = [];
othertypes = newhomegenieothertypes.split(",");
var theModuleTypes = [];
var count = 0;
if (othertypes.length > 0) {
for (var i = 0; i < othertypes.length; i++) {
var aType = othertypes[i].trim();
if (aType.length > 0) {
var moduleType = {
moduleType: aType
};
theModuleTypes.push(moduleType);
count++;
}
}
}
var theExtension;
if (count == 0) {
theExtension = undefined;
} else {
theExtension = {
moduleTypes: theModuleTypes
}
}
var newhomegenie = { var newhomegenie = {
name: newhomegeniename, name: newhomegeniename,
ip: newhomegenieip, ip: newhomegenieip,
@@ -2035,16 +2111,61 @@ app.controller('SystemController', function ($scope, $location, bridgeService, n
username: newhomegenieusername, username: newhomegenieusername,
password: newhomegeniepassword, password: newhomegeniepassword,
secure: newhomegeniesecure, secure: newhomegeniesecure,
webhook: newhomegeniewebhook webhook: newhomegeniewebhook,
extensions: theExtension
}; };
$scope.bridge.settings.homegenieaddress.devices.push(newhomegenie); $scope.bridge.settings.homegenieaddress.devices.push(newhomegenie);
$scope.newhomegeniename = null; $scope.newhomegeniename = null;
$scope.newhomegenieip = null; $scope.newhomegenieip = null;
$scope.newhomegenieport = "8080"; $scope.newhomegenieport = null;
$scope.newhomegenieusername = null; $scope.newhomegenieusername = null;
$scope.newhomegeniepassword = null; $scope.newhomegeniepassword = null;
$scope.newhomegeniewebhook = null; $scope.newhomegeniewebhook = null;
$scope.newhomegenieextensions = null;
}; };
$scope.updateModuleTypes = function (theIndex, homegenieExtensions) {
var othertypes = [];
if(homegenieExtensions != undefined)
othertypes = homegenieExtensions.split(",");
var theModuleTypes = [];
var count = 0;
if (othertypes.length > 0) {
for (var x = 0; x < othertypes.length; x++) {
var aType = othertypes[x].trim();
if (aType.length > 0) {
var moduleType = {
moduleType: aType
};
theModuleTypes.push(moduleType);
count++;
}
}
}
var theExtension;
if (count == 0) {
theExtension = undefined;
} else {
theExtension = {
moduleTypes: theModuleTypes
}
}
$scope.bridge.settings.homegenieaddress.devices[theIndex].extensions = theExtension;
};
$scope.convertModuleTypes = function (theIndex) {
var displayExtension = "";
if ($scope.bridge.settings.homegenieaddress.devices[theIndex].extensions != undefined && $scope.bridge.settings.homegenieaddress.devices[theIndex].extensions.moduleTypes != undefined) {
for (var i = 0; i < $scope.bridge.settings.homegenieaddress.devices[theIndex].extensions.moduleTypes.length; i++) {
displayExtension = displayExtension + $scope.bridge.settings.homegenieaddress.devices[theIndex].extensions.moduleTypes[i].moduleType;
}
}
return displayExtension;
};
$scope.removeHomeGenietoSettings = function (homegeniename, homegenieip) { $scope.removeHomeGenietoSettings = function (homegeniename, homegenieip) {
for (var i = $scope.bridge.settings.homegenieaddress.devices.length - 1; i >= 0; i--) { for (var i = $scope.bridge.settings.homegenieaddress.devices.length - 1; i >= 0; i--) {
if ($scope.bridge.settings.homegenieaddress.devices[i].name === homegeniename && $scope.bridge.settings.homegenieaddress.devices[i].ip === homegenieip) { if ($scope.bridge.settings.homegenieaddress.devices[i].name === homegeniename && $scope.bridge.settings.homegenieaddress.devices[i].ip === homegenieip) {
@@ -2074,6 +2195,17 @@ app.controller('SystemController', function ($scope, $location, bridgeService, n
$scope.deleteSettingsBackup = function (backupname) { $scope.deleteSettingsBackup = function (backupname) {
bridgeService.deleteSettingsBackup(backupname); bridgeService.deleteSettingsBackup(backupname);
}; };
$scope.downloadBackup = function (backupname) {
bridgeService.downloadConfigBackup(backupname);
};
$scope.uploadConfigFile = function (aFilename, aConfigFile) {
if (aConfigFile != null) {
bridgeService.uploadConfigFile(aFilename, aConfigFile);
}
};
$scope.toggle = function () { $scope.toggle = function () {
$scope.visible = !$scope.visible; $scope.visible = !$scope.visible;
if ($scope.visible) if ($scope.visible)
@@ -2349,12 +2481,41 @@ app.controller('ViewingController', function ($scope, $location, bridgeService,
bridgeService.editDevice(device); bridgeService.editDevice(device);
$location.path('/editdevice'); $location.path('/editdevice');
}; };
$scope.setStartupAction = function (device) {
$scope.bridge.device = device;
ngDialog.open({
template: 'startupActionDialog',
controller: 'StartupActionDialogCtrl',
className: 'ngdialog-theme-default'
});
};
$scope.renumberDevices = function () { $scope.renumberDevices = function () {
bridgeService.renumberDevices(); bridgeService.renumberDevices();
}; };
$scope.pushLinkButton = function () { $scope.pushLinkButton = function () {
bridgeService.pushLinkButton(); bridgeService.pushLinkButton();
}; };
$scope.toggleLock = function (device) {
if (device.lockDeviceId) {
device.lockDeviceId = false;
} else {
device.lockDeviceId = true;
}
console.log("toggle lock device called: " + device.name);
bridgeService.addDevice(device).then(
function () {
bridgeService.state.queueDevId = device.id;
console.log("Device updated for Q Id <<" + bridgeService.state.queueDevId + ">>");
$location.path('/');
},
function (error) {
bridgeService.displayWarn("Error updating lock id for device....", error);
}
);
};
$scope.manageLinksButton = function () { $scope.manageLinksButton = function () {
ngDialog.open({ ngDialog.open({
template: 'views/managelinksdialog.html', template: 'views/managelinksdialog.html',
@@ -2372,7 +2533,7 @@ app.controller('ViewingController', function ($scope, $location, bridgeService,
bridgeService.deleteBackup(backupname); bridgeService.deleteBackup(backupname);
}; };
$scope.downloadBackup = function (backupname) { $scope.downloadBackup = function (backupname) {
bridgeService.downloadBackup(backupname); bridgeService.downloadDeviceBackup(backupname);
}; };
$scope.uploadDeviceFile = function (aFilename, aDeviceFile) { $scope.uploadDeviceFile = function (aFilename, aDeviceFile) {
@@ -2408,23 +2569,23 @@ app.controller('ViewingController', function ($scope, $location, bridgeService,
}); });
app.controller('ValueDialogCtrl', function ($scope, bridgeService, ngDialog) { app.controller('ValueDialogCtrl', function ($scope, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.valueType = "percentage";
$scope.slider = { $scope.slider = {
value: 100, value: Math.round($scope.bridge.device.deviceState.bri / 2.55),
options: { options: {
floor: 1, floor: 1,
ceil: 100, ceil: 100,
showSelectionBar: true showSelectionBar: true
} }
}; };
$scope.bridge = bridgeService.state;
$scope.valueType = "percentage";
$scope.changeScale = function () { $scope.changeScale = function () {
if ($scope.valueType === "raw") { if ($scope.valueType === "raw") {
$scope.slider.options.ceil = 254; $scope.slider.options.ceil = 254;
$scope.slider.value = 254; $scope.slider.value = $scope.bridge.device.deviceState.bri;
} else { } else {
$scope.slider.options.ceil = 100; $scope.slider.options.ceil = 100;
$scope.slider.value = 100; $scope.slider.value = Math.round($scope.bridge.device.deviceState.bri / 2.55);
} }
}; };
$scope.setValue = function () { $scope.setValue = function () {
@@ -2473,6 +2634,57 @@ app.controller('DeleteDialogCtrl', function ($scope, bridgeService, ngDialog) {
}; };
}); });
app.controller('StartupActionDialogCtrl', function ($scope, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = $scope.bridge.device;
$scope.setDim = false;
var components = [];
if ($scope.device.startupActions != undefined) {
components = $scope.device.startupActions.split(":");
if (components[1] != undefined && components[1].length > 0)
$scope.setDim = true;
} else {
components = "::".split(":");
}
$scope.slider = {
value: parseInt(components[1]),
options: {
floor: 1,
ceil: 254,
showSelectionBar: true
}
};
$scope.rgbPicker = {
color: components[2]
};
$scope.theState = components[0];
$scope.startupActionSave = function (device) {
console.log("Startup action set for device called: " + device.name);
ngDialog.close('ngdialog1');
var theValue = 1;
if ($scope.setDim) {
theValue = $scope.theState + ":" + $scope.slider.value + ":" + $scope.rgbPicker.color;
} else {
theValue = $scope.theState + "::" + $scope.rgbPicker.color;
}
if (theValue == "::")
theValue = "";
device.startupActions = theValue;
bridgeService.addDevice(device).then(
function () {
bridgeService.state.queueDevId = device.id;
console.log("Device updated for Q Id <<" + bridgeService.state.queueDevId + ">>");
},
function (error) {
bridgeService.displayWarn("Error updating lock id for device....", error);
}
);
};
});
app.controller('VeraController', function ($scope, $location, bridgeService, ngDialog) { app.controller('VeraController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state; $scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device; $scope.device = bridgeService.state.device;

View File

@@ -68,9 +68,9 @@
<th sortable-header col="id" comparator-fn="comparatorUniqueId">ID</th> <th sortable-header col="id" comparator-fn="comparatorUniqueId">ID</th>
<th sortable-header col="name">Name</th> <th sortable-header col="name">Name</th>
<th sortable-header col="description">Description</th> <th sortable-header col="description">Description</th>
<th sortable-header col="devicestate">Device State</th>
<th sortable-header col="deviceType">Type</th> <th sortable-header col="deviceType">Type</th>
<th sortable-header col="targetDevice">Target</th> <th sortable-header col="targetDevice">Target</th>
<th sortable-header col="startupAction">Startup Action</th>
<th sortable-header col="inactive">Inactive</th> <th sortable-header col="inactive">Inactive</th>
<th sortable-header col="noState">No State</th> <th sortable-header col="noState">No State</th>
<th>Actions</th> <th>Actions</th>
@@ -79,14 +79,17 @@
<tr ng-repeat="device in bridge.devices | filterDevicesByRequester:bridge.state.filterDevicesByIpAddress:bridge.state.filterDevicesOnlyFiltered:bridge.state.filterDeviceType" <tr ng-repeat="device in bridge.devices | filterDevicesByRequester:bridge.state.filterDevicesByIpAddress:bridge.state.filterDevicesOnlyFiltered:bridge.state.filterDeviceType"
row-id="{{device.id}}" ng-class="{info: bridge.viewDevId == device.id}"> row-id="{{device.id}}" ng-class="{info: bridge.viewDevId == device.id}">
<td>{{$index+1}}</td> <td>{{$index+1}}</td>
<td>{{device.id}}</td> <td title="Locked: {{device.lockDeviceId}} - Click to Toggle" ng-click="toggleLock(device)">
<td>{{device.name}}</td> <b ng-if="device.lockDeviceId">{{device.id}}</b>
<td class="cr">{{device.description}}</td> <p ng-if="!device.lockDeviceId">{{device.id}}</p>
<td class="cr">
on={{device.deviceState.on}},bri={{device.deviceState.bri}},hue={{device.deviceState.hue}},sat={{device.deviceState.sat}},effect={{device.deviceState.effect}},ct={{device.deviceState.ct}},alert={{device.deviceState.alert}},colormode={{device.deviceState.colormode}},reachable={{device.deviceState.reachable}},XYList={{device.deviceState.xy}}
</td> </td>
<td
title="on={{device.deviceState.on}},bri={{device.deviceState.bri}},hue={{device.deviceState.hue}},sat={{device.deviceState.sat}},effect={{device.deviceState.effect}},ct={{device.deviceState.ct}},alert={{device.deviceState.alert}},colormode={{device.deviceState.colormode}},reachable={{device.deviceState.reachable}},XYList={{device.deviceState.xy}}">
{{device.name}}</td>
<td class="cr">{{device.description}}</td>
<td>{{device.deviceType}}</td> <td>{{device.deviceType}}</td>
<td>{{device.targetDevice}}</td> <td>{{device.targetDevice}}</td>
<td class="cr" title="Click to set actions for device on ha-bridge startup." ng-click="setStartupAction(device)">{{device.startupActions}}</td>
<td>{{device.inactive}}</td> <td>{{device.inactive}}</td>
<td>{{device.noState}}</td> <td>{{device.noState}}</td>
<td> <td>
@@ -142,8 +145,8 @@
<button ng-disabled="!myForm.$valid" type="submit" class="btn btn-primary" <button ng-disabled="!myForm.$valid" type="submit" class="btn btn-primary"
ng-click="uploadDeviceFile(deviceFile.name, deviceFile)">Upload</button> ng-click="uploadDeviceFile(deviceFile.name, deviceFile)">Upload</button>
<span class="progress" ng-show="deviceFile.progress >= 0"> <span class="progress" ng-show="deviceFile.progress >= 0">
<div style="width:{{deviceFile.progress}}%" ng-bind="deviceFile.progress + '%'" <div style="width:{{deviceFile.progress}}%" ng-bind="deviceFile.progress + '%'" class="ng-binding">
class="ng-binding"></div> </div>
</span> </span>
<span ng-show="deviceFile.result">Upload Successful</span> <span ng-show="deviceFile.result">Upload Successful</span>
</div> </div>
@@ -202,3 +205,43 @@
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteDevice(device)">Delete</button> <button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteDevice(device)">Delete</button>
</div> </div>
</script> </script>
<script type="text/ng-template" id="startupActionDialog">
<div class="ngdialog-message">
<h2>Select Actions for startup on {{device.name}}</h2>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Setting</th>
<th>Value</th>
</tr>
</thead>
<tr>
<td>State</td>
<td>
<select name="the-state" id="the-state" ng-model="theState">
<option value="">---None---</option>
<!-- not selected / blank option -->
<option value="On">On</option>
<option value="Off">Off</option>
</select>
</td>
</tr>
<tr>
<td>Dim</td>
<td>
<input type="checkbox"
ng-model="setDim" ng-true-value=true
ng-false-value=false>
<rzslider rz-slider-model="slider.value" rz-slider-options="slider.options"></rzslider>
</td>
</tr>
<tr>
<td>Color RGB</td>
<td><input colorpicker="rgb" ng-model="rgbPicker.color" type="text"></td>
</tr>
</table>
</div>
<div class="ngdialog-buttons mt">
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="startupActionSave(device)">Save</button>
</div>
</script>

File diff suppressed because it is too large Load Diff