Compare commits

..

8 Commits

Author SHA1 Message Date
Admin
3ac83912f3 Fixed success return validation on calls and implemented validation
catching logic for URL issues.

Fixes #73
Fixes #75
2016-03-29 16:24:12 -05:00
Admin
e62fcf7765 Adding URL formatting for various characters and updating test for
success return.
2016-03-28 16:36:21 -05:00
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
23 changed files with 635 additions and 278 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.
``` ```

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.1</version> <version>1.4.3</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>HA Bridge</name> <name>HA Bridge</name>
@@ -43,7 +43,7 @@
<dependency> <dependency>
<groupId>com.github.bwssytems</groupId> <groupId>com.github.bwssytems</groupId>
<artifactId>nest-controller</artifactId> <artifactId>nest-controller</artifactId>
<version>1.0.5</version> <version>1.0.8</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>

View File

@@ -40,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);
@@ -56,6 +58,7 @@ public class BridgeSettings extends BackupHandler {
{ {
log.info("reading from system properties"); log.info("reading from system properties");
theBridgeSettings.setNumberoflogmessages(Configuration.NUMBER_OF_LOG_MESSAGES); 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"));
@@ -142,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));
@@ -179,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,6 +19,7 @@ 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; private Integer numberoflogmessages;
@@ -29,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;
@@ -144,8 +146,14 @@ public class BridgeSettingsDescriptor {
public void setNumberoflogmessages(Integer numberoflogmessages) { public void setNumberoflogmessages(Integer numberoflogmessages) {
this.numberoflogmessages = 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

@@ -21,7 +21,7 @@ import com.bwssystems.HABridge.dao.BackupFilename;
import com.bwssystems.logservices.LoggerInfo; import com.bwssystems.logservices.LoggerInfo;
import com.bwssystems.logservices.LoggingForm; import com.bwssystems.logservices.LoggingForm;
import com.bwssystems.logservices.LoggingManager; import com.bwssystems.logservices.LoggingManager;
import com.bwssystems.util.JsonFreeTextStringFormatter; import com.bwssystems.util.TextStringFormatter;
import com.bwssystems.util.JsonTransformer; import com.bwssystems.util.JsonTransformer;
import com.google.gson.Gson; import com.google.gson.Gson;
@@ -82,7 +82,7 @@ public class SystemControl {
LoggingEvent le; LoggingEvent le;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
le = (LoggingEvent) cyclicBufferAppender.get(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 + ( i > 0?",{":"{") + "\"time\":\"" + dateFormat.format(le.getTimeStamp()) + "\",\"level\":\"" + le.getLevel().levelStr + "\",\"component\":\"" + le.getLoggerName() + "\",\"message\":\"" + TextStringFormatter.forJSON(le.getFormattedMessage()) + "\"}";
} }
} }
logMsgs = logMsgs + "]"; logMsgs = logMsgs + "]";
@@ -92,7 +92,7 @@ public class SystemControl {
// http://ip_address:port/system/logmgmt/loggers gets the logger info for the bridge // http://ip_address:port/system/logmgmt/loggers gets the logger info for the bridge
get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", "application/json", (request, response) -> { get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", "application/json", (request, response) -> {
log.info("Get loggers info with showAll argument: " + request.params(":all")); log.debug("Get loggers info with showAll argument: " + request.params(":all"));
Boolean showAll = false; Boolean showAll = false;
if(request.params(":all").equals("true")) if(request.params(":all").equals("true"))
showAll = true; showAll = true;
@@ -113,6 +113,7 @@ public class SystemControl {
}); });
// http://ip_address:port/system/logmgmt/update which changes logging parameters for the process // http://ip_address:port/system/logmgmt/update which changes logging parameters for the process
put(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> { put(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
log.debug("update loggers: " + request.body());
response.status(200); response.status(200);
LoggerInfo updateLoggers[]; LoggerInfo updateLoggers[];
updateLoggers = new Gson().fromJson(request.body(), LoggerInfo[].class); updateLoggers = new Gson().fromJson(request.body(), LoggerInfo[].class);
@@ -239,7 +240,7 @@ 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());
} }

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{
private String id; @SerializedName("id")
@Expose
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,7 +98,15 @@ public class DeviceDescriptor{
this.offUrl = offUrl; this.offUrl = offUrl;
} }
public String getOnUrl() { public String getDimUrl() {
return dimUrl;
}
public void setDimUrl(String dimUrl) {
this.dimUrl = dimUrl;
}
public String getOnUrl() {
return onUrl; return onUrl;
} }
@@ -111,6 +153,22 @@ public class DeviceDescriptor{
public void setContentBodyOff(String contentBodyOff) { public void setContentBodyOff(String contentBodyOff) {
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;
@@ -18,22 +17,27 @@ import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.util.BackupHandler; import com.bwssystems.util.BackupHandler;
import com.bwssystems.util.JsonTransformer; import com.bwssystems.util.JsonTransformer;
import com.google.gson.stream.JsonReader; 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

@@ -115,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

@@ -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")) {
thermoSetting.setTemp(String.valueOf((Double.parseDouble(replaceIntensityValue(thermoSetting.getTemp(), state.getBri())) - 32.0)/1.8)); if(bridgeSettings.isFarenheit())
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()));
} }
@@ -453,7 +516,11 @@ public class HueMulator {
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling url to change device state\", \"parameter\": \"/lights/" + lightId + "state\"}}]"; responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling url to change device state\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
} }
} }
if(!responseString.contains("[{\"error\":")) {
device.setDeviceSetValue(state.getBri());
device.setDeviceState(state.isOn());
}
return responseString; return responseString;
}); });
} }
@@ -499,31 +566,36 @@ public class HueMulator {
// This function executes the url from the device repository against the vera // This function executes the url from the device repository against the vera
protected boolean doHttpRequest(String url, String httpVerb, String contentType, String body) { protected boolean doHttpRequest(String url, String httpVerb, String contentType, String body) {
HttpUriRequest request = null; HttpUriRequest request = null;
if(HttpGet.METHOD_NAME.equalsIgnoreCase(httpVerb) || httpVerb == null) { try {
request = new HttpGet(url); if(HttpGet.METHOD_NAME.equalsIgnoreCase(httpVerb) || httpVerb == null) {
}else if(HttpPost.METHOD_NAME.equalsIgnoreCase(httpVerb)){ request = new HttpGet(url);
HttpPost postRequest = new HttpPost(url); }else if(HttpPost.METHOD_NAME.equalsIgnoreCase(httpVerb)){
ContentType parsedContentType = ContentType.parse(contentType); HttpPost postRequest = new HttpPost(url);
StringEntity requestBody = new StringEntity(body, parsedContentType); ContentType parsedContentType = ContentType.parse(contentType);
postRequest.setEntity(requestBody); StringEntity requestBody = new StringEntity(body, parsedContentType);
request = postRequest; postRequest.setEntity(requestBody);
}else if(HttpPut.METHOD_NAME.equalsIgnoreCase(httpVerb)){ request = postRequest;
HttpPut putRequest = new HttpPut(url); }else if(HttpPut.METHOD_NAME.equalsIgnoreCase(httpVerb)){
ContentType parsedContentType = ContentType.parse(contentType); HttpPut putRequest = new HttpPut(url);
StringEntity requestBody = new StringEntity(body, parsedContentType); ContentType parsedContentType = ContentType.parse(contentType);
putRequest.setEntity(requestBody); StringEntity requestBody = new StringEntity(body, parsedContentType);
request = putRequest; putRequest.setEntity(requestBody);
request = putRequest;
}
} catch(IllegalArgumentException e) {
log.warn("Error calling out to HA gateway: IllegalArgumentException in log", e);
return false;
} }
log.debug("Making outbound call in doHttpRequest: " + request); log.debug("Making outbound call in doHttpRequest: " + request);
try { try {
HttpResponse response = httpClient.execute(request); HttpResponse response = httpClient.execute(request);
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
log.debug((httpVerb == null?"GET":httpVerb) + " execute on URL responded: " + response.getStatusLine().getStatusCode()); log.debug((httpVerb == null?"GET":httpVerb) + " execute on URL responded: " + response.getStatusLine().getStatusCode());
if(response.getStatusLine().getStatusCode() == 200){ if(response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300){
return true; return true;
} }
} catch (IOException e) { } catch (IOException e) {
log.warn("Error calling out to HA gateway", e); log.warn("Error calling out to HA gateway: IOException in log", e);
} }
return false; return false;
} }

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

@@ -1,49 +0,0 @@
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

@@ -0,0 +1,259 @@
package com.bwssystems.util;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
public final class TextStringFormatter {
private TextStringFormatter() {
// empty - prevent construction
}
/**
Escapes characters for text appearing as data in the
<a href='http://www.json.org/'>Javascript Object Notation</a>
(JSON) data interchange format.
<P>The following commonly used control characters are escaped :
<table border='1' cellpadding='3' cellspacing='0'>
<tr><th> Character </th><th> Escaped As </th></tr>
<tr><td> " </td><td> \" </td></tr>
<tr><td> \ </td><td> \\ </td></tr>
<tr><td> / </td><td> \/ </td></tr>
<tr><td> back space </td><td> \b </td></tr>
<tr><td> form feed </td><td> \f </td></tr>
<tr><td> line feed </td><td> \n </td></tr>
<tr><td> carriage return </td><td> \r </td></tr>
<tr><td> tab </td><td> \t </td></tr>
</table>
<P>See <a href='http://www.ietf.org/rfc/rfc4627.txt'>RFC 4627</a> for more information.
*/
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();
}
/**
Escape characters for text appearing in HTML markup.
<P>This method exists as a defence against Cross Site Scripting (XSS) hacks.
The idea is to neutralize control characters commonly used by scripts, such that
they will not be executed by the browser. This is done by replacing the control
characters with their escaped equivalents.
See {@link hirondelle.web4j.security.SafeText} as well.
<P>The following characters are replaced with corresponding
HTML character entities :
<table border='1' cellpadding='3' cellspacing='0'>
<tr><th> Character </th><th>Replacement</th></tr>
<tr><td> < </td><td> &lt; </td></tr>
<tr><td> > </td><td> &gt; </td></tr>
<tr><td> & </td><td> &amp; </td></tr>
<tr><td> " </td><td> &quot;</td></tr>
<tr><td> \t </td><td> &#009;</td></tr>
<tr><td> ! </td><td> &#033;</td></tr>
<tr><td> # </td><td> &#035;</td></tr>
<tr><td> $ </td><td> &#036;</td></tr>
<tr><td> % </td><td> &#037;</td></tr>
<tr><td> ' </td><td> &#039;</td></tr>
<tr><td> ( </td><td> &#040;</td></tr>
<tr><td> ) </td><td> &#041;</td></tr>
<tr><td> * </td><td> &#042;</td></tr>
<tr><td> + </td><td> &#043; </td></tr>
<tr><td> , </td><td> &#044; </td></tr>
<tr><td> - </td><td> &#045; </td></tr>
<tr><td> . </td><td> &#046; </td></tr>
<tr><td> / </td><td> &#047; </td></tr>
<tr><td> : </td><td> &#058;</td></tr>
<tr><td> ; </td><td> &#059;</td></tr>
<tr><td> = </td><td> &#061;</td></tr>
<tr><td> ? </td><td> &#063;</td></tr>
<tr><td> @ </td><td> &#064;</td></tr>
<tr><td> [ </td><td> &#091;</td></tr>
<tr><td> \ </td><td> &#092;</td></tr>
<tr><td> ] </td><td> &#093;</td></tr>
<tr><td> ^ </td><td> &#094;</td></tr>
<tr><td> _ </td><td> &#095;</td></tr>
<tr><td> ` </td><td> &#096;</td></tr>
<tr><td> { </td><td> &#123;</td></tr>
<tr><td> | </td><td> &#124;</td></tr>
<tr><td> } </td><td> &#125;</td></tr>
<tr><td> ~ </td><td> &#126;</td></tr>
</table>
<P>Note that JSTL's {@code <c:out>} escapes <em>only the first
five</em> of the above characters.
*/
public static String forHTML(String aText) {
final StringBuilder result = new StringBuilder();
final StringCharacterIterator iterator = new StringCharacterIterator(aText);
char character = iterator.current();
while (character != CharacterIterator.DONE) {
if (character == '<') {
result.append("&lt;");
} else if (character == '>') {
result.append("&gt;");
} else if (character == '&') {
result.append("&amp;");
} else if (character == '\"') {
result.append("&quot;");
} else if (character == '\t') {
addCharEntity(9, result);
} else if (character == '!') {
addCharEntity(33, result);
} else if (character == '#') {
addCharEntity(35, result);
} else if (character == '$') {
addCharEntity(36, result);
} else if (character == '%') {
addCharEntity(37, result);
} else if (character == '\'') {
addCharEntity(39, result);
} else if (character == '(') {
addCharEntity(40, result);
} else if (character == ')') {
addCharEntity(41, result);
} else if (character == '*') {
addCharEntity(42, result);
} else if (character == '+') {
addCharEntity(43, result);
} else if (character == ',') {
addCharEntity(44, result);
} else if (character == '-') {
addCharEntity(45, result);
} else if (character == '.') {
addCharEntity(46, result);
} else if (character == '/') {
addCharEntity(47, result);
} else if (character == ':') {
addCharEntity(58, result);
} else if (character == ';') {
addCharEntity(59, result);
} else if (character == '=') {
addCharEntity(61, result);
} else if (character == '?') {
addCharEntity(63, result);
} else if (character == '@') {
addCharEntity(64, result);
} else if (character == '[') {
addCharEntity(91, result);
} else if (character == '\\') {
addCharEntity(92, result);
} else if (character == ']') {
addCharEntity(93, result);
} else if (character == '^') {
addCharEntity(94, result);
} else if (character == '_') {
addCharEntity(95, result);
} else if (character == '`') {
addCharEntity(96, result);
} else if (character == '{') {
addCharEntity(123, result);
} else if (character == '|') {
addCharEntity(124, result);
} else if (character == '}') {
addCharEntity(125, result);
} else if (character == '~') {
addCharEntity(126, result);
} else {
// the char is not a special one
// add it to the result as is
result.append(character);
}
character = iterator.next();
}
return result.toString();
}
/**
* Escape all ampersand characters in a URL.
*
* <P>
* Replaces all <tt>'&'</tt> characters with <tt>'&amp;'</tt>.
*
* <P>
* An ampersand character may appear in the query string of a URL. The
* ampersand character is indeed valid in a URL.
* <em>However, URLs usually appear as an <tt>HREF</tt> attribute, and such
* attributes have the additional constraint that ampersands must be
* escaped.</em>
*
* <P>
* The JSTL <c:url> tag does indeed perform proper URL encoding of query
* parameters. But it does not, in general, produce text which is valid as
* an <tt>HREF</tt> attribute, simply because it does not escape the
* ampersand character. This is a nuisance when multiple query parameters
* appear in the URL, since it requires a little extra work.
*/
public static String forHrefAmpersand(String aURL) {
return aURL.replace("&", "&amp;");
}
public static String forQuerySpace(String aURL) {
return aURL.replace(" ", "\u0020");
}
/**
* Synonym for <tt>URLEncoder.encode(String, "UTF-8")</tt>.
*
* <P>
* Used to ensure that HTTP query strings are in proper form, by escaping
* special characters such as spaces.
*
* <P>
* It is important to note that if a query string appears in an
* <tt>HREF</tt> attribute, then there are two issues - ensuring the query
* string is valid HTTP (it is URL-encoded), and ensuring it is valid HTML
* (ensuring the ampersand is escaped).
*/
public static String forURL(String aURLFragment) {
String result = null;
try {
result = URLEncoder.encode(aURLFragment, "UTF-8");
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException("UTF-8 not supported", ex);
}
return result;
}
private static void addCharEntity(Integer aIdx, StringBuilder aBuilder) {
String padding = "";
if (aIdx <= 9) {
padding = "00";
} else if (aIdx <= 99) {
padding = "0";
} else {
// no prefix
}
String number = padding + aIdx.toString();
aBuilder.append("&#" + number + ";");
}
}

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

@@ -47,27 +47,25 @@ app.service('bridgeService', function ($http, $window, ngToast) {
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.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) {
@@ -101,6 +99,7 @@ app.service('bridgeService', function ($http, $window, ngToast) {
self.state.device.mapId = null; self.state.device.mapId = null;
self.state.device.name = ""; self.state.device.name = "";
self.state.device.onUrl = ""; self.state.device.onUrl = "";
self.state.device.dimUrl = "";
self.state.device.deviceType = "custom"; self.state.device.deviceType = "custom";
self.state.device.targetDevice = null; self.state.device.targetDevice = null;
self.state.device.offUrl = ""; self.state.device.offUrl = "";
@@ -108,7 +107,7 @@ app.service('bridgeService', function ($http, $window, ngToast) {
self.state.device.contentType = null; self.state.device.contentType = null;
self.state.device.contentBody = null; self.state.device.contentBody = null;
self.state.device.contentBodyOff = null; self.state.device.contentBodyOff = null;
self.state.bridge.olddevicename = ""; self.state.olddevicename = "";
}; };
this.getHABridgeVersion = function () { this.getHABridgeVersion = function () {
@@ -303,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) {
@@ -333,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) {
@@ -474,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();
}, },
@@ -523,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;
@@ -683,12 +660,15 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
$scope.imgBkUrl = "glyphicon glyphicon-plus"; $scope.imgBkUrl = "glyphicon glyphicon-plus";
$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({
@@ -786,27 +766,32 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
}; };
$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.onUrl = "http://" + veradevice.veraaddress + ":" + $scope.vera.port $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=" + "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum="
+ veradevice.id; + veradevice.id;
$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="
+ veradevice.id;
$scope.device.offUrl = "http://" + veradevice.veraaddress + ":" + $scope.vera.port $scope.device.offUrl = "http://" + veradevice.veraaddress + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=" + "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum="
+ veradevice.id; + veradevice.id;
}; };
$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;
@@ -831,6 +816,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
bridgeService.viewVeraScenes(); bridgeService.viewVeraScenes();
}, },
function (error) { function (error) {
bridgeService.displayWarn("Error adding device: " + $scope.device.name, error)
} }
); );
@@ -910,6 +896,7 @@ app.controller('HarmonyController', function ($scope, $location, $http, bridgeSe
}; };
$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;
@@ -920,6 +907,7 @@ app.controller('HarmonyController', function ($scope, $location, $http, bridgeSe
}; };
$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);
@@ -984,6 +972,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
}; };
$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;
@@ -994,16 +983,18 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
}; };
$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;
@@ -1014,6 +1005,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
}; };
$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;
@@ -1024,6 +1016,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
}; };
$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;
@@ -1034,6 +1027,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
}; };
$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;
@@ -1044,6 +1038,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
}; };
$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;
@@ -1090,7 +1085,7 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi
$scope.device_dim_control = ""; $scope.device_dim_control = "";
$scope.bulk = { devices: [] }; $scope.bulk = { devices: [] };
var veraList = angular.fromJson($scope.bridge.settings.veraaddress); var veraList = angular.fromJson($scope.bridge.settings.veraaddress);
if(veraList != null) if(veraList != null && veraList.devices.length > 0)
$scope.vera = {base: "http://" + veraList.devices[0].ip, port: "3480", id: ""}; $scope.vera = {base: "http://" + veraList.devices[0].ip, port: "3480", id: ""};
else else
$scope.vera = {base: "http://", port: "3480", id: ""}; $scope.vera = {base: "http://", port: "3480", id: ""};
@@ -1102,32 +1097,31 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi
}; };
$scope.buildUrlsUsingDevice = function (dim_control) { $scope.buildUrlsUsingDevice = function (dim_control) {
if ($scope.vera.base.indexOf("http") < 0) { bridgeService.clearDevice();
$scope.vera.base = "http://" + $scope.vera.base;
}
$scope.device.deviceType = "switch"; $scope.device.deviceType = "switch";
$scope.device.targetDevice = "Encapsulated"; $scope.device.targetDevice = "Encapsulated";
$scope.device.mapType = "veraDevice"; $scope.device.mapType = "veraDevice";
$scope.device.mapId = $scope.vera.id; $scope.device.mapId = $scope.vera.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 = $scope.vera.base + ":" + $scope.vera.port $scope.device.dimUrl = $scope.vera.base + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&DeviceNum=" + "/data_request?id=action&output_format=json&DeviceNum="
+ $scope.vera.id + $scope.vera.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.onUrl = $scope.vera.base + ":" + $scope.vera.port $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=" + "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum="
+ $scope.vera.id; + $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 $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=" + "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum="
+ $scope.vera.id; + $scope.vera.id;
}; };
$scope.buildUrlsUsingScene = function () { $scope.buildUrlsUsingScene = function () {
if ($scope.vera.base.indexOf("http") < 0) { bridgeService.clearDevice();
$scope.vera.base = "http://" + $scope.vera.base;
}
$scope.device.deviceType = "scene"; $scope.device.deviceType = "scene";
$scope.device.targetDevice = "Encapsulated"; $scope.device.targetDevice = "Encapsulated";
$scope.device.mapType = "veraScene"; $scope.device.mapType = "veraScene";

View File

@@ -36,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"

View File

@@ -83,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

@@ -107,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

@@ -173,6 +173,11 @@
<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>

View File

@@ -130,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"