mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-18 16:17:30 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
838b86a266 | ||
|
|
ed8fc95782 | ||
|
|
f6cb41b880 | ||
|
|
a578aa9fd8 | ||
|
|
7b97bd75ae | ||
|
|
4de14217b4 | ||
|
|
c7cf48bb6b | ||
|
|
54e9303708 | ||
|
|
6b4344bbe8 | ||
|
|
745986f08f | ||
|
|
5e59b33ed7 | ||
|
|
adc34ddaa6 | ||
|
|
2f456aa0d0 | ||
|
|
201aaa8bca | ||
|
|
6e7b48aa5b | ||
|
|
afd1af4094 | ||
|
|
61156e9820 | ||
|
|
6116d37675 | ||
|
|
0305646b4f | ||
|
|
dd0032a567 | ||
|
|
3016712ad8 | ||
|
|
6b3ae1b971 | ||
|
|
71258c7e52 | ||
|
|
0ac8061118 | ||
|
|
c679548bbd | ||
|
|
16a248ba8e | ||
|
|
0ce23c0f00 | ||
|
|
babf81ea31 |
26
README.md
26
README.md
@@ -33,23 +33,23 @@ ATTENTION: This requires JDK 1.8 to run
|
||||
ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below.
|
||||
|
||||
```
|
||||
java -jar ha-bridge-4.1.1.jar
|
||||
java -jar ha-bridge-4.2.1.jar
|
||||
```
|
||||
### Automation on Linux systems
|
||||
To have this configured and running automatically there are a few resources to use. One is using Docker and a docker container has been built for this and can be gotten here: https://github.com/aptalca/docker-ha-bridge
|
||||
|
||||
Create the directory and make sure that ha-bridge-4.1.1.jar is in your /home/pi/habridge directory.
|
||||
Create the directory and make sure that ha-bridge-4.2.1.jar is in your /home/pi/habridge directory.
|
||||
```
|
||||
pi@raspberrypi:~ $ mkdir habridge
|
||||
pi@raspberrypi:~ $ cd habridge
|
||||
|
||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.1.1/ha-bridge-4.1.1.jar
|
||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.2.1/ha-bridge-4.2.1.jar
|
||||
```
|
||||
Create the directory and make sure that ha-bridge-4.1.1.jar is in your /home/pi/habridge directory.
|
||||
Create the directory and make sure that ha-bridge-4.2.1.jar is in your /home/pi/habridge directory.
|
||||
```
|
||||
pi@raspberrypi:~ $ mkdir habridge
|
||||
pi@raspberrypi:~ $ cd habridge
|
||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.1.1/ha-bridge-4.1.1.jar
|
||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.2.1/ha-bridge-4.2.1.jar
|
||||
```
|
||||
#### System Control Setup on a pi (preferred)
|
||||
For next gen Linux systems (this includes the Raspberry Pi), here is a systemctl unit file that you can install. Here is a link on how to do this: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
|
||||
@@ -69,7 +69,7 @@ After=network.target
|
||||
[Service]
|
||||
Type=simple
|
||||
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.1.1.jar
|
||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.2.1.jar
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -104,7 +104,7 @@ Then cut and past this, modify any locations that are not correct
|
||||
```
|
||||
cd /home/pi/habridge
|
||||
rm /home/pi/habridge/habridge-log.txt
|
||||
nohup java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.1.1.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
|
||||
nohup java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.2.1.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
|
||||
|
||||
chmod 777 /home/pi/habridge/habridge-log.txt
|
||||
```
|
||||
@@ -354,7 +354,7 @@ Headers can be added as well using a Json construct [{"name":"header type name",
|
||||
|
||||
Another option that is detected by the bridge is to use UDP or TCP direct calls such as `udp://<ip_address>:<port>/<your stuff here>` to send a UDP request. TCP calls are handled the same way as `tcp://<ip_address>:<port>/<your stuff here>`. If your data for the UDP or TCP request is formatted as "0x00F009B9" lexical hex format, the bridge will convert the data into a binary stream to send.
|
||||
|
||||
You can also use the value replacement constructs within these statements. Such as using the expressions "${time.format(Java time format string)}" for inserting a date/time stamp, ${intensity.percent} for 0-100 or ${intensity.byte} for 0-255 for straight pass through of the value or items that require special calculated values using ${intensity.math()} i.e. "${intensity.math(X/4)}". See Value Passing Controls Below.
|
||||
You can also use the value replacement constructs within these statements. Such as using the expressions "${time.format(Java time format string)}" for inserting a date/time stamp, ${intensity.percent} for 0-100 or ${intensity.decimal_percent} for 0.00-1.00 or ${intensity.byte} for 0-255 for straight pass through of the value or items that require special calculated values using ${intensity.math()} i.e. "${intensity.math(X/4)}". See Value Passing Controls Below.
|
||||
Examples:
|
||||
```
|
||||
|
||||
@@ -408,7 +408,7 @@ OR
|
||||
|
||||
```
|
||||
#### Value Passing Controls
|
||||
There are multiple replacement constructs available to be put into any of the calls except Harmony items, Net Items and HAL items. These constructs are: "${time.format(Java time format string)}", "${intensity.percent}", "${intensity.byte}" and "${intensity.math(using X in your calc)}".
|
||||
There are multiple replacement constructs available to be put into any of the calls except Harmony items, Net Items and HAL items. These constructs are: "${time.format(Java time format string)}", "${intensity.percent}", "${intensity.decimal_percent}", "${intensity.byte}" and "${intensity.math(using X in your calc)}".
|
||||
You can control items that require special calculated values using ${intensity.math(<your expression using "X" as the value to operate on>)} i.e. "${intensity.math(X/4)}".
|
||||
For the items that want to have a date time put into the message, utilize ${time.format(yyyy-MM-ddTHH:mm:ssXXX)} where "yyyy-MM-ddTHH:mm:ssXXX" can be any format from the Java SimpleDateFormat documented here: https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html
|
||||
e.g.
|
||||
@@ -472,8 +472,8 @@ Dim a light | "Dim the <light name>"
|
||||
Brighten a light | "Brighten the <light name>"
|
||||
Set a light brightness to a certain percentage | "Set <light name> to 50%"
|
||||
Dim/Brighten lights by a certain percentage | "Dim/Brighten <light name> by 50%"
|
||||
Turn on/off all lights in room | “Turn on/off lights in <room name>"
|
||||
Turn on/off all lights | “Turn on/off all of the lightsâ€<EFBFBD>
|
||||
Turn on/off all lights in room | "Turn on/off lights in <room name>"
|
||||
Turn on/off all lights | "Turn on/off all of the lights"
|
||||
|
||||
To see what Home thinks you said, you can ask "Hey Google, What did I say?" or check the history in the app.
|
||||
|
||||
@@ -518,7 +518,7 @@ contentBodyOff | string | This is the content body that you would like to send w
|
||||
}
|
||||
```
|
||||
#### Dimming Control Example
|
||||
Dimming is also supported by using the expressions ${intensity.percent} for 0-100 or ${intensity.byte} for 0-255 for straight pass through of the value.
|
||||
Dimming is also supported by using the expressions ${intensity.percent} for 0-100 or ${intensity.decimal_percent} for 0.00-1.00 or ${intensity.byte} for 0-255 for straight pass through of the value.
|
||||
e.g.
|
||||
```
|
||||
{
|
||||
@@ -692,7 +692,7 @@ This call returns a null json "{}".
|
||||
### Get HA Bridge Version
|
||||
Get current version of the HA bridge software.
|
||||
```
|
||||
GET http://host:port/api/devices/habridge/version
|
||||
GET http://host:port/system/habridge/version
|
||||
```
|
||||
#### Response
|
||||
Name | Type | Description
|
||||
|
||||
14
pom.xml
14
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.bwssystems.HABridge</groupId>
|
||||
<artifactId>ha-bridge</artifactId>
|
||||
<version>4.1.2</version>
|
||||
<version>4.2.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HA Bridge</name>
|
||||
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>nest-controller</artifactId>
|
||||
<version>1.0.13</version>
|
||||
<version>1.0.14</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@@ -121,6 +121,16 @@
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>lifx-sdk-java</artifactId>
|
||||
<version>2.1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -148,6 +148,7 @@ public class BridgeSettings extends BackupHandler {
|
||||
theBridgeSettings.setMqttconfigured(theBridgeSettings.isValidMQTT());
|
||||
theBridgeSettings.setHassconfigured(theBridgeSettings.isValidHass());
|
||||
theBridgeSettings.setDomoticzconfigured(theBridgeSettings.isValidDomoticz());
|
||||
// Lifx is either configured or not, so it does not need an update.
|
||||
if(serverPortOverride != null)
|
||||
theBridgeSettings.setServerPort(serverPortOverride);
|
||||
if(serverIpOverride != null)
|
||||
|
||||
@@ -40,6 +40,7 @@ public class BridgeSettingsDescriptor {
|
||||
private String hubversion;
|
||||
private IpList domoticzaddress;
|
||||
private boolean domoticzconfigured;
|
||||
private boolean lifxconfigured;
|
||||
|
||||
public BridgeSettingsDescriptor() {
|
||||
super();
|
||||
@@ -263,6 +264,12 @@ public class BridgeSettingsDescriptor {
|
||||
public void setDomoticzconfigured(boolean domoticzconfigured) {
|
||||
this.domoticzconfigured = domoticzconfigured;
|
||||
}
|
||||
public boolean isLifxconfigured() {
|
||||
return lifxconfigured;
|
||||
}
|
||||
public void setLifxconfigured(boolean lifxconfigured) {
|
||||
this.lifxconfigured = lifxconfigured;
|
||||
}
|
||||
public Boolean isValidVera() {
|
||||
if(this.getVeraAddress() == null || this.getVeraAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
@@ -328,4 +335,7 @@ public class BridgeSettingsDescriptor {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidLifx() {
|
||||
return this.isLifxconfigured();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ public class DeviceMapTypes {
|
||||
public final static String[] UDP_DEVICE_COMPAT = { "UDP", "UDP Device"};
|
||||
public final static String[] HTTP_DEVICE = { "httpDevice", "HTTP Device"};
|
||||
public final static String[] DOMOTICZ_DEVICE = { "domoticzDevice", "Domoticz Device"};
|
||||
public final static String[] LIFX_DEVICE = { "lifxDevice", "LIFX Device"};
|
||||
|
||||
public final static int typeIndex = 0;
|
||||
public final static int displayIndex = 1;
|
||||
@@ -46,6 +47,7 @@ public class DeviceMapTypes {
|
||||
deviceMapTypes.add(HASS_DEVICE);
|
||||
deviceMapTypes.add(HTTP_DEVICE);
|
||||
deviceMapTypes.add(HUE_DEVICE);
|
||||
deviceMapTypes.add(LIFX_DEVICE);
|
||||
deviceMapTypes.add(MQTT_MESSAGE);
|
||||
deviceMapTypes.add(NEST_HOMEAWAY);
|
||||
deviceMapTypes.add(NEST_THERMO_SET);
|
||||
|
||||
@@ -87,6 +87,7 @@ public class HABridge {
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
homeManager.closeHomes();
|
||||
udpSender.closeResponseSocket();
|
||||
udpSender = null;
|
||||
}
|
||||
bridgeSettings.getBridgeControl().setReinit(false);
|
||||
stop();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -12,6 +13,7 @@ import com.bwssystems.HABridge.plugins.harmony.HarmonyHome;
|
||||
import com.bwssystems.HABridge.plugins.hass.HassHome;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHome;
|
||||
import com.bwssystems.HABridge.plugins.hue.HueHome;
|
||||
import com.bwssystems.HABridge.plugins.lifx.LifxHome;
|
||||
import com.bwssystems.HABridge.plugins.mqtt.MQTTHome;
|
||||
import com.bwssystems.HABridge.plugins.tcp.TCPHome;
|
||||
import com.bwssystems.HABridge.plugins.udp.UDPHome;
|
||||
@@ -70,7 +72,6 @@ public class HomeManager {
|
||||
homeList.put(DeviceMapTypes.CUSTOM_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the tcp handler Home
|
||||
aHome = new TCPHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.TCP_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
@@ -79,13 +80,18 @@ public class HomeManager {
|
||||
aHome = new UDPHome(bridgeSettings, aUdpDatagramSender);
|
||||
homeList.put(DeviceMapTypes.UDP_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.UDP_DEVICE_COMPAT[DeviceMapTypes.typeIndex], aHome);
|
||||
|
||||
// Setup Vera Home if available
|
||||
aHome = new VeraHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the HomeAssistant configuration if available
|
||||
//setup the Domoticz configuration if available
|
||||
aHome = new DomoticzHome(bridgeSettings);
|
||||
homeList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
resourceList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
//setup the Lifx configuration if available
|
||||
aHome = new LifxHome(bridgeSettings);
|
||||
resourceList.put(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
homeList.put(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex], aHome);
|
||||
}
|
||||
|
||||
public Home findHome(String type) {
|
||||
@@ -96,6 +102,11 @@ public class HomeManager {
|
||||
}
|
||||
|
||||
public void closeHomes() {
|
||||
|
||||
Collection<Home> theHomes = homeList.values();
|
||||
for(Home aHome : theHomes) {
|
||||
aHome.closeHome();
|
||||
}
|
||||
homeList.clear();
|
||||
homeList = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,9 @@ public class DeviceDescriptor{
|
||||
@SerializedName("noState")
|
||||
@Expose
|
||||
private boolean noState;
|
||||
@SerializedName("requesterAddress")
|
||||
@Expose
|
||||
private String requesterAddress;
|
||||
|
||||
private DeviceState deviceState;
|
||||
|
||||
@@ -219,6 +222,14 @@ public class DeviceDescriptor{
|
||||
this.noState = noState;
|
||||
}
|
||||
|
||||
public String getRequesterAddress() {
|
||||
return requesterAddress;
|
||||
}
|
||||
|
||||
public void setRequesterAddress(String requesterAddress) {
|
||||
this.requesterAddress = requesterAddress;
|
||||
}
|
||||
|
||||
public boolean containsType(String aType) {
|
||||
if(aType == null)
|
||||
return false;
|
||||
|
||||
@@ -83,6 +83,33 @@ public class DeviceRepository extends BackupHandler {
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<DeviceDescriptor> findAllByRequester(String anAddress) {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
List<DeviceDescriptor> theReturnList = new ArrayList<DeviceDescriptor>();
|
||||
Iterator<DeviceDescriptor> anIterator = list.iterator();
|
||||
DeviceDescriptor theDevice;
|
||||
String theRequesterAddress;
|
||||
|
||||
HashMap<String,String > addressMap;
|
||||
while (anIterator.hasNext()) {
|
||||
theDevice = anIterator.next();
|
||||
theRequesterAddress = theDevice.getRequesterAddress();
|
||||
addressMap = new HashMap<String, String>();
|
||||
if(theRequesterAddress != null) {
|
||||
if (theRequesterAddress.contains(",")) {
|
||||
String[] theArray = theRequesterAddress.split(",");
|
||||
for (String v : theArray) {
|
||||
addressMap.put(v.trim(), v.trim());
|
||||
}
|
||||
} else
|
||||
addressMap.put(theRequesterAddress, theRequesterAddress);
|
||||
}
|
||||
if (theRequesterAddress == null || theRequesterAddress.length() == 0 || addressMap.containsKey(anAddress))
|
||||
theReturnList.add(theDevice);
|
||||
}
|
||||
return theReturnList;
|
||||
}
|
||||
|
||||
public DeviceDescriptor findOne(String id) {
|
||||
return devices.get(id);
|
||||
}
|
||||
|
||||
@@ -18,12 +18,15 @@ import org.slf4j.LoggerFactory;
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.DeviceMapTypes;
|
||||
import com.bwssystems.HABridge.HomeManager;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.BackupFilename;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.dao.DeviceRepository;
|
||||
import com.bwssystems.HABridge.dao.ErrorMessage;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
spark core server for bridge configuration
|
||||
@@ -33,11 +36,13 @@ public class DeviceResource {
|
||||
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
|
||||
private DeviceRepository deviceRepository;
|
||||
private HomeManager homeManager;
|
||||
private Gson aGsonHandler;
|
||||
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
|
||||
|
||||
public DeviceResource(BridgeSettingsDescriptor theSettings, HomeManager aHomeManager) {
|
||||
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
|
||||
homeManager = aHomeManager;
|
||||
aGsonHandler = new GsonBuilder().create();
|
||||
setupEndpoints();
|
||||
}
|
||||
|
||||
@@ -65,14 +70,44 @@ public class DeviceResource {
|
||||
else {
|
||||
devices = new Gson().fromJson("[" + request.body() + "]", DeviceDescriptor[].class);
|
||||
}
|
||||
CallItem[] callItems = null;
|
||||
String errorMessage = null;
|
||||
for(int i = 0; i < devices.length; i++) {
|
||||
if(devices[i].getContentBody() != null ) {
|
||||
if (devices[i].getContentType() == null || devices[i].getHttpVerb() == null || !supportedVerbs.contains(devices[i].getHttpVerb().toLowerCase())) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
log.debug("Bad http verb in create a Device(s): " + request.body());
|
||||
return new ErrorMessage("Bad http verb in create a Device(s): " + request.body() + " ");
|
||||
errorMessage = "Bad http verb in create device(s) for name: " + devices[i].getName() + " with verb: " + devices[i].getHttpVerb();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
try {
|
||||
if(devices[i].getOnUrl() != null && !devices[i].getOnUrl().isEmpty())
|
||||
callItems = aGsonHandler.fromJson(devices[i].getOnUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "Bad on URL JSON in create device(s) for name: " + devices[i].getName() + " with on URL: " + devices[i].getOnUrl();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
try {
|
||||
if(devices[i].getDimUrl() != null && !devices[i].getDimUrl().isEmpty())
|
||||
callItems = aGsonHandler.fromJson(devices[i].getDimUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "Bad dim URL JSON in create device(s) for name: " + devices[i].getName() + " with dim URL: " + devices[i].getDimUrl();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
try {
|
||||
if(devices[i].getOffUrl() != null && !devices[i].getOffUrl().isEmpty())
|
||||
callItems = aGsonHandler.fromJson(devices[i].getOffUrl(), CallItem[].class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
errorMessage = "Bad off URL JSON in create device(s) for name: " + devices[i].getName() + " with off URL: " + devices[i].getOffUrl();
|
||||
log.debug(errorMessage);
|
||||
return new ErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
deviceRepository.save(devices);
|
||||
@@ -219,6 +254,12 @@ public class DeviceResource {
|
||||
return homeManager.findResource(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/lifx/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get LIFX devices");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return homeManager.findResource(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex]);
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/map/types", "application/json", (request, response) -> {
|
||||
log.debug("Get map types");
|
||||
return new DeviceMapTypes().getDeviceMapTypes();
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.lang3.Conversion;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.java.dev.eval.Expression;
|
||||
|
||||
public class BrightnessDecode {
|
||||
private static final Logger log = LoggerFactory.getLogger(BrightnessDecode.class);
|
||||
private static final String INTENSITY_PERCENT = "${intensity.percent}";
|
||||
private static final String INTENSITY_DECIMAL_PERCENT = "${intensity.decimal_percent}";
|
||||
private static final String INTENSITY_BYTE = "${intensity.byte}";
|
||||
private static final String INTENSITY_MATH = "${intensity.math(";
|
||||
private static final String INTENSITY_MATH_VALUE = "X";
|
||||
@@ -49,9 +47,7 @@ public class BrightnessDecode {
|
||||
}
|
||||
if (request.contains(INTENSITY_BYTE)) {
|
||||
if (isHex) {
|
||||
BigInteger bigInt = BigInteger.valueOf(intensity);
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||
String hexValue = convertToHex(intensity);
|
||||
request = request.replace(INTENSITY_BYTE, hexValue);
|
||||
} else {
|
||||
String intensityByte = String.valueOf(intensity);
|
||||
@@ -60,14 +56,17 @@ public class BrightnessDecode {
|
||||
} else if (request.contains(INTENSITY_PERCENT)) {
|
||||
int percentBrightness = (int) Math.round(intensity / 255.0 * 100);
|
||||
if (isHex) {
|
||||
BigInteger bigInt = BigInteger.valueOf(percentBrightness);
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||
String hexValue = convertToHex(percentBrightness);
|
||||
request = request.replace(INTENSITY_PERCENT, hexValue);
|
||||
} else {
|
||||
String intensityPercent = String.valueOf(percentBrightness);
|
||||
request = request.replace(INTENSITY_PERCENT, intensityPercent);
|
||||
}
|
||||
} else if (request.contains(INTENSITY_DECIMAL_PERCENT)) {
|
||||
float decimalBrightness = (float) (intensity / 255.0);
|
||||
|
||||
String intensityPercent = String.format("%1.2f", decimalBrightness);
|
||||
request = request.replace(INTENSITY_DECIMAL_PERCENT, intensityPercent);
|
||||
} else if (request.contains(INTENSITY_MATH)) {
|
||||
Map<String, BigDecimal> variables = new HashMap<String, BigDecimal>();
|
||||
String mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(),
|
||||
@@ -81,9 +80,7 @@ public class BrightnessDecode {
|
||||
BigDecimal result = exp.eval(variables);
|
||||
Integer endResult = Math.round(result.floatValue());
|
||||
if (isHex) {
|
||||
BigInteger bigInt = BigInteger.valueOf(endResult);
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||
String hexValue = convertToHex(endResult);
|
||||
request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE, hexValue);
|
||||
} else {
|
||||
request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE,
|
||||
@@ -100,4 +97,15 @@ public class BrightnessDecode {
|
||||
public static String calculateReplaceIntensityValue(String request, int theIntensity, Integer targetBri, Integer targetBriInc, boolean isHex) {
|
||||
return replaceIntensityValue(request, calculateIntensity(theIntensity, targetBri, targetBriInc), isHex);
|
||||
}
|
||||
}
|
||||
|
||||
// Apache Commons Conversion utils likes little endian too much
|
||||
private static String convertToHex(int theValue) {
|
||||
String destHex = "00";
|
||||
String hexValue = Conversion.intToHex(theValue, 0, destHex, 0, 2);
|
||||
byte[] theBytes = hexValue.getBytes();
|
||||
byte[] newBytes = new byte[2];
|
||||
newBytes[0] = theBytes[1];
|
||||
newBytes[1] = theBytes[0];
|
||||
return new String(newBytes);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import com.bwssystems.HABridge.api.hue.HuePublicConfig;
|
||||
import com.bwssystems.HABridge.api.hue.StateChangeBody;
|
||||
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
|
||||
import com.bwssystems.HABridge.dao.*;
|
||||
import com.bwssystems.HABridge.plugins.hue.HueDeviceIdentifier;
|
||||
import com.bwssystems.HABridge.plugins.hue.HueHome;
|
||||
import com.bwssystems.HABridge.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
@@ -669,7 +668,8 @@ public class HueMulator {
|
||||
log.debug("hue lights list requested: " + userId + " from " + requestIp);
|
||||
theErrors = validateWhitelistUser(userId, false);
|
||||
if (theErrors == null) {
|
||||
List<DeviceDescriptor> deviceList = repository.findActive();
|
||||
List<DeviceDescriptor> deviceList = repository.findAllByRequester(requestIp);
|
||||
// List<DeviceDescriptor> deviceList = repository.findActive();
|
||||
deviceResponseMap = new HashMap<String, DeviceResponse>();
|
||||
for (DeviceDescriptor device : deviceList) {
|
||||
DeviceResponse deviceResponse = null;
|
||||
@@ -945,8 +945,8 @@ public class HueMulator {
|
||||
}
|
||||
|
||||
for (int i = 0; callItems != null && i < callItems.length; i++) {
|
||||
if(!filterByRequester(callItems[i].getFilterIPs(), ipAddress)) {
|
||||
log.debug("filter for requester address not present in list: " + callItems[i].getFilterIPs() + " with request ip of: " + ipAddress);
|
||||
if(!filterByRequester(device.getRequesterAddress(), ipAddress) || !filterByRequester(callItems[i].getFilterIPs(), ipAddress)) {
|
||||
log.warn("filter for requester address not present in: (device)" + device.getRequesterAddress() + " OR then (item)" + callItems[i].getFilterIPs() + " with request ip of: " + ipAddress);
|
||||
continue;
|
||||
}
|
||||
if (callItems[i].getCount() != null && callItems[i].getCount() > 0)
|
||||
|
||||
@@ -94,10 +94,10 @@ public class NestHome implements com.bwssystems.HABridge.Home {
|
||||
public void closeHome() {
|
||||
if(theSession != null) {
|
||||
theNest.endNestSession();
|
||||
theNest = null;
|
||||
theSession = null;
|
||||
nestItems = null;
|
||||
}
|
||||
theNest = null;
|
||||
theSession = null;
|
||||
nestItems = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,11 +3,12 @@ package com.bwssystems.HABridge.plugins.domoticz;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.Base64;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.NameValue;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
@@ -16,32 +17,33 @@ public class DomoticzHandler {
|
||||
private static final String GET_REQUEST = "/json.htm?type=";
|
||||
private static final String DEVICES_TYPE = "devices";
|
||||
private static final String SCENES_TYPE = "scenes";
|
||||
private static final String FILTER_USED = "&used=";
|
||||
private HTTPHandler httpClient;
|
||||
private static final String FILTER_USED = "&used=true";
|
||||
private NamedIP domoticzAddress;
|
||||
|
||||
public DomoticzHandler(NamedIP addressName) {
|
||||
super();
|
||||
httpClient = new HTTPHandler();
|
||||
domoticzAddress = addressName;
|
||||
}
|
||||
|
||||
public List<DomoticzDevice> getDevices() {
|
||||
return getDomoticzDevices(GET_REQUEST, DEVICES_TYPE, FILTER_USED);
|
||||
public List<DomoticzDevice> getDevices(HTTPHandler httpClient) {
|
||||
return getDomoticzDevices(GET_REQUEST, DEVICES_TYPE, FILTER_USED, httpClient);
|
||||
}
|
||||
|
||||
public List<DomoticzDevice> getScenes() {
|
||||
return getDomoticzDevices(GET_REQUEST, SCENES_TYPE, null);
|
||||
public List<DomoticzDevice> getScenes(HTTPHandler httpClient) {
|
||||
return getDomoticzDevices(GET_REQUEST, SCENES_TYPE, null, httpClient);
|
||||
}
|
||||
|
||||
private List<DomoticzDevice> getDomoticzDevices(String rootRequest, String type, String postpend) {
|
||||
private List<DomoticzDevice> getDomoticzDevices(String rootRequest, String type, String postpend, HTTPHandler httpClient) {
|
||||
Devices theDomoticzApiResponse = null;
|
||||
List<DomoticzDevice> deviceList = null;
|
||||
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
theUrl = "http://" + domoticzAddress.getIp() + ":" + domoticzAddress.getPort() + rootRequest + type;
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, null);
|
||||
if(postpend != null && !postpend.isEmpty())
|
||||
theUrl = buildUrl(rootRequest + type + postpend);
|
||||
else
|
||||
theUrl = buildUrl(rootRequest + type);
|
||||
theData = httpClient.doHttpRequest(theUrl, null, null, null, buildHeaders());
|
||||
if(theData != null) {
|
||||
log.debug("GET " + type + " DomoticzApiResponse - data: " + theData);
|
||||
theDomoticzApiResponse = new Gson().fromJson(theData, Devices.class);
|
||||
@@ -70,6 +72,44 @@ public class DomoticzHandler {
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
public String buildUrl(String thePayload) {
|
||||
String newUrl = null;
|
||||
|
||||
if(thePayload != null && !thePayload.isEmpty()) {
|
||||
if(domoticzAddress.getSecure() != null && domoticzAddress.getSecure())
|
||||
newUrl = "https://";
|
||||
else
|
||||
newUrl = "http://";
|
||||
|
||||
newUrl = newUrl + domoticzAddress.getIp();
|
||||
|
||||
if(domoticzAddress.getPort() != null && !domoticzAddress.getPort().isEmpty())
|
||||
newUrl = newUrl + ":" + domoticzAddress.getPort();
|
||||
|
||||
if(thePayload.startsWith("/"))
|
||||
newUrl = newUrl + thePayload;
|
||||
else
|
||||
newUrl = newUrl + "/" + thePayload;
|
||||
}
|
||||
|
||||
return newUrl;
|
||||
}
|
||||
|
||||
public NameValue[] buildHeaders() {
|
||||
NameValue[] headers = null;
|
||||
|
||||
if(domoticzAddress.getUsername() != null && !domoticzAddress.getUsername().isEmpty()
|
||||
&& domoticzAddress.getPassword() != null && !domoticzAddress.getPassword().isEmpty()) {
|
||||
NameValue theAuth = new NameValue();
|
||||
theAuth.setName("Authorization");
|
||||
String encoding = Base64.getEncoder().encodeToString((domoticzAddress.getUsername() + ":" + domoticzAddress.getPassword()).getBytes());
|
||||
theAuth.setValue("Basic " + encoding);
|
||||
headers = new NameValue[1];
|
||||
headers[0] = theAuth;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
public NamedIP getDomoticzAddress() {
|
||||
return domoticzAddress;
|
||||
}
|
||||
|
||||
@@ -13,13 +13,19 @@ import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.api.hue.HueError;
|
||||
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class DomoticzHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(DomoticzHome.class);
|
||||
private Map<String, DomoticzHandler> domoticzs;
|
||||
private Boolean validDomoticz;
|
||||
private HTTPHandler httpClient;
|
||||
|
||||
public DomoticzHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
@@ -36,14 +42,14 @@ public class DomoticzHome implements Home {
|
||||
List<DomoticzDevice> deviceList = new ArrayList<DomoticzDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = domoticzs.get(key).getDevices();
|
||||
theResponse = domoticzs.get(key).getDevices(httpClient);
|
||||
if(theResponse != null)
|
||||
addDomoticzDevices(deviceList, theResponse, key);
|
||||
else {
|
||||
log.warn("Cannot get lights for Domoticz with name: " + key + ", skipping this Domoticz.");
|
||||
continue;
|
||||
}
|
||||
theResponse = domoticzs.get(key).getScenes();
|
||||
theResponse = domoticzs.get(key).getScenes(httpClient);
|
||||
if(theResponse != null)
|
||||
addDomoticzDevices(deviceList, theResponse, key);
|
||||
else
|
||||
@@ -66,8 +72,54 @@ public class DomoticzHome implements Home {
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
// Not a device handler
|
||||
return null;
|
||||
Devices theDomoticzApiResponse = null;
|
||||
String responseString = null;
|
||||
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && (theUrl.startsWith("http://") || theUrl.startsWith("https://"))) {
|
||||
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
DomoticzHandler theHandler = findHandlerByAddress(hostAddr);
|
||||
if(theHandler != null){
|
||||
String theData;
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
theData = httpClient.doHttpRequest(theHandler.buildUrl(anUrl), null, null, null, theHandler.buildHeaders());
|
||||
try {
|
||||
theDomoticzApiResponse = new Gson().fromJson(theData, Devices.class);
|
||||
if(theDomoticzApiResponse.getStatus().equals("OK"))
|
||||
responseString = null;
|
||||
else {
|
||||
log.warn("Call failed for Domoticz " + theHandler.getDomoticzAddress().getName() + " with status " + theDomoticzApiResponse.getStatus() + " for item " + theDomoticzApiResponse.getTitle());
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Cannot interrpret result from call for Domoticz " + theHandler.getDomoticzAddress().getName() + " as response is not parsable.");
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
} else {
|
||||
log.warn("Domoticz Call could not complete, no address found: " + theUrl);
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
} else {
|
||||
log.warn("Domoticz Call to be presented as http(s)://<ip_address>(:<port>)/payload, format of request unknown: " + theUrl);
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,6 +128,7 @@ public class DomoticzHome implements Home {
|
||||
log.info("Domoticz Home created." + (validDomoticz ? "" : " No Domoticz devices configured."));
|
||||
if(!validDomoticz)
|
||||
return null;
|
||||
httpClient = new HTTPHandler();
|
||||
domoticzs = new HashMap<String, DomoticzHandler>();
|
||||
Iterator<NamedIP> theList = bridgeSettings.getDomoticzaddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
@@ -90,9 +143,26 @@ public class DomoticzHome implements Home {
|
||||
return this;
|
||||
}
|
||||
|
||||
private DomoticzHandler findHandlerByAddress(String hostAddress) {
|
||||
DomoticzHandler aHandler = null;
|
||||
boolean found = false;
|
||||
Iterator<String> keys = domoticzs.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
aHandler = domoticzs.get(key);
|
||||
if(aHandler != null && aHandler.getDomoticzAddress().getIp().equals(hostAddress)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
aHandler = null;
|
||||
return aHandler;
|
||||
}
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// noop
|
||||
if(httpClient != null)
|
||||
httpClient.closeHandler();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,4 +185,11 @@ public class HalInfo {
|
||||
this.halAddress = halAddress;
|
||||
}
|
||||
|
||||
public void closeInfo() {
|
||||
if(httpClient != null)
|
||||
httpClient.closeHandler();
|
||||
httpClient = null;
|
||||
halAddress = null;
|
||||
theToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,10 +151,14 @@ public class HassHome implements Home {
|
||||
public void closeHome() {
|
||||
if(!validHass)
|
||||
return;
|
||||
if(hassMap == null)
|
||||
return;
|
||||
Iterator<String> keys = hassMap.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
hassMap.get(key).closeClient();
|
||||
}
|
||||
|
||||
hassMap = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,12 @@ public class HomeAssistant {
|
||||
aUrl = "https";
|
||||
else
|
||||
aUrl = "http";
|
||||
aUrl = aUrl + "://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/services/" + aCommand.getEntityId().substring(0, aCommand.getEntityId().indexOf("."));
|
||||
String domain = aCommand.getEntityId().substring(0, aCommand.getEntityId().indexOf("."));
|
||||
aUrl = aUrl + "://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/services/";
|
||||
if(domain.equals("group"))
|
||||
aUrl = aUrl + "homeassistant";
|
||||
else
|
||||
aUrl = aUrl + domain;
|
||||
String aBody = "{\"entity_id\":\"" + aCommand.getEntityId() + "\"";
|
||||
NameValue[] headers = null;
|
||||
if(hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
|
||||
@@ -62,6 +67,7 @@ public class HomeAssistant {
|
||||
aUrl = aUrl + "/turn_off";
|
||||
aBody = aBody + "}";
|
||||
}
|
||||
log.debug("Calling HomeAssistant with url: " + aUrl);
|
||||
String theData = anHttpHandler.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "application/json", aBody, headers);
|
||||
log.debug("call Command return is: <" + theData + ">");
|
||||
return true;
|
||||
@@ -104,5 +110,6 @@ public class HomeAssistant {
|
||||
|
||||
protected void closeClient() {
|
||||
anHttpHandler.closeHandler();
|
||||
anHttpHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,27 +4,18 @@ import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.CookieSpecs;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.ssl.SSLContexts;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -33,23 +24,13 @@ import com.bwssystems.HABridge.api.NameValue;
|
||||
|
||||
public class HTTPHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(HTTPHandler.class);
|
||||
private HttpClient httpClient;
|
||||
// private CloseableHttpClient httpclientSSL;
|
||||
// private SSLContext sslcontext;
|
||||
// private SSLConnectionSocketFactory sslsf;
|
||||
// private RequestConfig globalConfig;
|
||||
private CloseableHttpClient httpClient;
|
||||
private RequestConfig globalConfig;
|
||||
|
||||
|
||||
public HTTPHandler() {
|
||||
httpClient = HttpClients.createDefault();
|
||||
// Removed Specific SSL as Apache HttpClient automatically uses SSL if the URI starts with https://
|
||||
// Trust own CA and all self-signed certs
|
||||
// sslcontext = SSLContexts.createDefault();
|
||||
// Allow TLSv1 protocol only
|
||||
// sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLS" }, null,
|
||||
// SSLConnectionSocketFactory.getDefaultHostnameVerifier());
|
||||
// globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
|
||||
// httpclientSSL = HttpClients.custom().setSSLSocketFactory(sslsf).setDefaultRequestConfig(globalConfig).build();
|
||||
globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
|
||||
httpClient = HttpClients.custom().setDefaultRequestConfig(globalConfig).build();
|
||||
}
|
||||
|
||||
|
||||
@@ -100,33 +81,56 @@ public class HTTPHandler {
|
||||
request.setHeader(headers[i].getName(), headers[i].getValue());
|
||||
}
|
||||
}
|
||||
HttpResponse response;
|
||||
try {
|
||||
HttpResponse response;
|
||||
// Removed Specific SSL as Apache HttpClient automatically uses SSL if the URI starts with https://
|
||||
// if (url.startsWith("xyzhttps"))
|
||||
// response = httpclientSSL.execute(request);
|
||||
// else
|
||||
for(int retryCount = 0; retryCount < 2; retryCount++) {
|
||||
response = httpClient.execute(request);
|
||||
log.debug((httpVerb == null ? "GET" : httpVerb) + " execute on URL responded: "
|
||||
+ response.getStatusLine().getStatusCode());
|
||||
if (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300) {
|
||||
if (response.getEntity() != null) {
|
||||
try {
|
||||
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); // read
|
||||
// content
|
||||
// for
|
||||
// data
|
||||
EntityUtils.consume(response.getEntity()); // close out
|
||||
// inputstream
|
||||
// ignore
|
||||
// content
|
||||
} catch (Exception e) {
|
||||
log.debug("Error ocurred in handling response entity after successful call, still responding success. "
|
||||
+ e.getMessage(), e);
|
||||
log.debug((httpVerb == null ? "GET" : httpVerb) + " execute (" + retryCount + ") on URL responded: "
|
||||
+ response.getStatusLine().getStatusCode());
|
||||
if (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300) {
|
||||
if (response.getEntity() != null) {
|
||||
try {
|
||||
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); // read
|
||||
// content
|
||||
// for
|
||||
// data
|
||||
EntityUtils.consume(response.getEntity()); // close out
|
||||
// inputstream
|
||||
// ignore
|
||||
// content
|
||||
} catch (Exception e) {
|
||||
log.debug("Error ocurred in handling response entity after successful call, still responding success. "
|
||||
+ e.getMessage(), e);
|
||||
}
|
||||
log.debug("Successfull response - The http response is <<<" + theContent + ">>>");
|
||||
}
|
||||
retryCount = 2;
|
||||
} else {
|
||||
log.warn("HTTP response code was not an expected successful response of between 200 - 299, the code was: " + response.getStatusLine());
|
||||
try {
|
||||
String someContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); // read
|
||||
// content
|
||||
// for
|
||||
// data
|
||||
EntityUtils.consume(response.getEntity()); // close out
|
||||
// inputstream
|
||||
// ignore
|
||||
// content
|
||||
log.debug("Unsuccessfull response - The http response is <<<" + someContent + ">>>");
|
||||
} catch (Exception e) {
|
||||
//noop
|
||||
}
|
||||
if (response.getStatusLine().getStatusCode() == 504) {
|
||||
log.warn("HTTP response code was 504, retrying...");
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e1) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
else
|
||||
retryCount = 2;
|
||||
}
|
||||
if (theContent == null)
|
||||
theContent = "";
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Error calling out to HA gateway: IOException in log", e);
|
||||
@@ -134,23 +138,22 @@ public class HTTPHandler {
|
||||
return theContent;
|
||||
}
|
||||
|
||||
public HttpClient getHttpClient() {
|
||||
// public HttpClient getHttpClient() {
|
||||
// return httpClient;
|
||||
// }
|
||||
|
||||
|
||||
public CloseableHttpClient getHttpClient() {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
|
||||
// public CloseableHttpClient getHttpclientSSL() {
|
||||
// return httpclientSSL;
|
||||
// }
|
||||
|
||||
|
||||
public void closeHandler() {
|
||||
try {
|
||||
httpClient.close();
|
||||
} catch (IOException e) {
|
||||
// noop
|
||||
}
|
||||
httpClient = null;
|
||||
// try {
|
||||
// httpclientSSL.close();
|
||||
// } catch (IOException e) {
|
||||
// // noop
|
||||
// }
|
||||
// httpclientSSL = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,23 +29,25 @@ public class HTTPHome implements Home {
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String responseString = null;
|
||||
|
||||
//Backwards Compatibility Items
|
||||
if(anItem.getHttpVerb() == null || anItem.getHttpVerb().isEmpty())
|
||||
{
|
||||
if(device.getHttpVerb() != null && !device.getHttpVerb().isEmpty())
|
||||
anItem.setHttpVerb(device.getHttpVerb());
|
||||
}
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && (theUrl.startsWith("http://") || theUrl.startsWith("https://"))) {
|
||||
//Backwards Compatibility Items
|
||||
if(anItem.getHttpVerb() == null || anItem.getHttpVerb().isEmpty())
|
||||
{
|
||||
if(device.getHttpVerb() != null && !device.getHttpVerb().isEmpty())
|
||||
anItem.setHttpVerb(device.getHttpVerb());
|
||||
}
|
||||
|
||||
if(anItem.getHttpHeaders() == null || anItem.getHttpHeaders().isEmpty()) {
|
||||
if(device.getHeaders() != null && !device.getHeaders().isEmpty() )
|
||||
anItem.setHttpHeaders(device.getHeaders());
|
||||
}
|
||||
|
||||
log.debug("executing HUE api request to Http "
|
||||
+ (anItem.getHttpVerb() == null ? "GET" : anItem.getHttpVerb()) + ": "
|
||||
+ anItem.getItem().getAsString());
|
||||
|
||||
if(anItem.getHttpHeaders() == null || anItem.getHttpHeaders().isEmpty()) {
|
||||
if(device.getHeaders() != null && !device.getHeaders().isEmpty() )
|
||||
anItem.setHttpHeaders(device.getHeaders());
|
||||
}
|
||||
|
||||
log.debug("executing HUE api request to Http "
|
||||
+ (anItem.getHttpVerb() == null ? "GET" : anItem.getHttpVerb()) + ": "
|
||||
+ anItem.getItem().getAsString());
|
||||
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(anItem.getItem().getAsString(),
|
||||
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrl,
|
||||
intensity, targetBri, targetBriInc, false);
|
||||
|
||||
anUrl = TimeDecode.replaceTimeValue(anUrl);
|
||||
@@ -63,6 +65,13 @@ public class HTTPHome implements Home {
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
} else {
|
||||
log.warn("HTTP Call to be presented as http(s)://<ip_address>(:<port>)/payload, format of request unknown: " + theUrl);
|
||||
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
|
||||
"Error on calling url to change device state", "/lights/"
|
||||
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
|
||||
return responseString;
|
||||
}
|
||||
|
||||
@@ -81,7 +90,9 @@ public class HTTPHome implements Home {
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
anHttpHandler.closeHandler();
|
||||
if(anHttpHandler != null)
|
||||
anHttpHandler.closeHandler();
|
||||
anHttpHandler = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -124,10 +124,13 @@ public class HueHome implements Home {
|
||||
public void closeHome() {
|
||||
if(!validHue)
|
||||
return;
|
||||
if(hues == null)
|
||||
return;
|
||||
Iterator<String> keys = hues.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
hues.get(key).closeHue();;
|
||||
}
|
||||
hues = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.bwssystems.HABridge.plugins.lifx;
|
||||
|
||||
import com.github.besherman.lifx.LFXGroup;
|
||||
import com.github.besherman.lifx.LFXLight;
|
||||
|
||||
public class LifxDevice {
|
||||
private Object lifxObject;
|
||||
private String type;
|
||||
public final static String LIGHT_TYPE = "Light";
|
||||
public final static String GROUP_TYPE = "Group";
|
||||
|
||||
public LifxDevice(Object lifxObject, String type) {
|
||||
super();
|
||||
this.lifxObject = lifxObject;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public LifxEntry toEntry() {
|
||||
LifxEntry anEntry = null;
|
||||
if(type.equals(LIGHT_TYPE)) {
|
||||
anEntry = new LifxEntry();
|
||||
anEntry.setId(((LFXLight)lifxObject).getID());
|
||||
anEntry.setName(((LFXLight)lifxObject).getLabel());
|
||||
anEntry.setType(LIGHT_TYPE);
|
||||
}
|
||||
if(type.equals(GROUP_TYPE)) {
|
||||
anEntry = new LifxEntry();
|
||||
anEntry.setId("na");
|
||||
anEntry.setName(((LFXGroup)lifxObject).getLabel());
|
||||
anEntry.setType(GROUP_TYPE);
|
||||
}
|
||||
return anEntry;
|
||||
}
|
||||
public Object getLifxObject() {
|
||||
return lifxObject;
|
||||
}
|
||||
|
||||
public void setLifxObject(Object lifxObject) {
|
||||
this.lifxObject = lifxObject;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.bwssystems.HABridge.plugins.lifx;
|
||||
|
||||
public class LifxEntry {
|
||||
private String name;
|
||||
private String id;
|
||||
private String type;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
257
src/main/java/com/bwssystems/HABridge/plugins/lifx/LifxHome.java
Normal file
257
src/main/java/com/bwssystems/HABridge/plugins/lifx/LifxHome.java
Normal file
@@ -0,0 +1,257 @@
|
||||
package com.bwssystems.HABridge.plugins.lifx;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.Home;
|
||||
import com.bwssystems.HABridge.api.CallItem;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.hue.BrightnessDecode;
|
||||
import com.bwssystems.HABridge.hue.MultiCommandUtil;
|
||||
import com.github.besherman.lifx.LFXClient;
|
||||
import com.github.besherman.lifx.LFXGroup;
|
||||
import com.github.besherman.lifx.LFXGroupCollection;
|
||||
import com.github.besherman.lifx.LFXGroupCollectionListener;
|
||||
import com.github.besherman.lifx.LFXLight;
|
||||
import com.github.besherman.lifx.LFXLightCollection;
|
||||
import com.github.besherman.lifx.LFXLightCollectionListener;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
public class LifxHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(LifxHome.class);
|
||||
private static final float DIM_DIVISOR = (float)254.00;
|
||||
private Map<String, LifxDevice> lifxMap;
|
||||
private LFXClient client;
|
||||
private Boolean validLifx;
|
||||
private Gson aGsonHandler;
|
||||
|
||||
public LifxHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
createHome(bridgeSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
lifxMap = null;
|
||||
aGsonHandler = null;
|
||||
validLifx = bridgeSettings.isValidLifx();
|
||||
log.info("LifxDevice Home created." + (validLifx ? "" : " No LifxDevices configured."));
|
||||
if(validLifx) {
|
||||
try {
|
||||
log.info("Open Lifx client....");
|
||||
InetAddress configuredAddress = InetAddress.getByName(bridgeSettings.getUpnpConfigAddress());
|
||||
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(configuredAddress);
|
||||
InetAddress bcastInetAddr = null;
|
||||
if (networkInterface != null) {
|
||||
for (InterfaceAddress ifaceAddr : networkInterface.getInterfaceAddresses()) {
|
||||
InetAddress addr = ifaceAddr.getAddress();
|
||||
if (addr instanceof Inet4Address) {
|
||||
bcastInetAddr = ifaceAddr.getBroadcast();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bcastInetAddr != null) {
|
||||
lifxMap = new HashMap<String, LifxDevice>();
|
||||
log.info("Opening LFX Client with broadcast address: " + bcastInetAddr.getHostAddress());
|
||||
client = new LFXClient(bcastInetAddr.getHostAddress());
|
||||
client.getLights().addLightCollectionListener(new MyLightListener(lifxMap));
|
||||
client.getGroups().addGroupCollectionListener(new MyGroupListener(lifxMap));
|
||||
client.open(false);
|
||||
aGsonHandler =
|
||||
new GsonBuilder()
|
||||
.create();
|
||||
} else {
|
||||
log.warn("Could not open LIFX, no bcast addr available, check your upnp config address.");
|
||||
client = null;
|
||||
validLifx = false;
|
||||
return this;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not open LIFX, with IO Exception", e);
|
||||
client = null;
|
||||
validLifx = false;
|
||||
return this;
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Could not open LIFX, with Interruprted Exception", e);
|
||||
client = null;
|
||||
validLifx = false;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LifxDevice getLifxDevice(String aName) {
|
||||
if(!validLifx)
|
||||
return null;
|
||||
LifxDevice aLifxDevice = null;
|
||||
if(aName == null || aName.equals("")) {
|
||||
log.debug("Cannot get LifxDevice for name as it is empty.");
|
||||
}
|
||||
else {
|
||||
aLifxDevice = lifxMap.get(aName);
|
||||
log.debug("Retrieved a LifxDevice for name: " + aName);
|
||||
}
|
||||
return aLifxDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItems(String type) {
|
||||
log.debug("consolidating devices for lifx");
|
||||
if(!validLifx)
|
||||
return null;
|
||||
LifxEntry theResponse = null;
|
||||
Iterator<String> keys = lifxMap.keySet().iterator();
|
||||
List<LifxEntry> deviceList = new ArrayList<LifxEntry>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = lifxMap.get(key).toEntry();
|
||||
if(theResponse != null)
|
||||
deviceList.add(theResponse);
|
||||
else {
|
||||
log.warn("Cannot get LifxDevice with name: " + key + ", skipping this Lifx.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
private Boolean addLifxLights(LFXLightCollection theDeviceList) {
|
||||
if(!validLifx)
|
||||
return false;
|
||||
Iterator<LFXLight> devices = theDeviceList.iterator();;
|
||||
while(devices.hasNext()) {
|
||||
LFXLight theDevice = devices.next();
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(theDevice, LifxDevice.LIGHT_TYPE);
|
||||
lifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Boolean addLifxGroups(LFXGroupCollection theDeviceList) {
|
||||
if(!validLifx)
|
||||
return false;
|
||||
Iterator<LFXGroup> devices = theDeviceList.iterator();;
|
||||
while(devices.hasNext()) {
|
||||
LFXGroup theDevice = devices.next();
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(theDevice, LifxDevice.GROUP_TYPE);
|
||||
lifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
String theReturn = null;
|
||||
float aBriValue;
|
||||
float theValue;
|
||||
log.debug("executing HUE api request to send message to LifxDevice: " + anItem.getItem().toString());
|
||||
if(!validLifx) {
|
||||
log.warn("Should not get here, no LifxDevice clients configured");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no LifxDevices configured\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
|
||||
} else {
|
||||
LifxEntry lifxCommand = null;
|
||||
if(anItem.getItem().isJsonObject())
|
||||
lifxCommand = aGsonHandler.fromJson(anItem.getItem(), LifxEntry.class);
|
||||
else
|
||||
lifxCommand = aGsonHandler.fromJson(anItem.getItem().getAsString(), LifxEntry.class);
|
||||
LifxDevice theDevice = getLifxDevice(lifxCommand.getName());
|
||||
if (theDevice == null) {
|
||||
log.warn("Should not get here, no LifxDevices available");
|
||||
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||
+ "\",\"description\": \"Should not get here, no Lifx clients available\", \"parameter\": \"/lights/"
|
||||
+ lightId + "state\"}}]";
|
||||
} else {
|
||||
log.debug("calling LifxDevice: " + lifxCommand.getName());
|
||||
if(theDevice.getType().equals(LifxDevice.LIGHT_TYPE)) {
|
||||
LFXLight theLight = (LFXLight)theDevice.getLifxObject();
|
||||
if(body.contains("true"))
|
||||
theLight.setPower(true);
|
||||
if(body.contains("false"))
|
||||
theLight.setPower(false);
|
||||
if(targetBri != null || targetBriInc != null) {
|
||||
aBriValue = (float)BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc);
|
||||
theValue = aBriValue/DIM_DIVISOR;
|
||||
if(theValue > (float)1.0)
|
||||
theValue = (float)0.99;
|
||||
theLight.setBrightness(theValue);
|
||||
}
|
||||
} else if (theDevice.getType().equals(LifxDevice.GROUP_TYPE)) {
|
||||
LFXGroup theGroup = (LFXGroup)theDevice.getLifxObject();
|
||||
if(body.contains("true"))
|
||||
theGroup.setPower(true);
|
||||
if(body.contains("false"))
|
||||
theGroup.setPower(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return theReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
if(!validLifx)
|
||||
return;
|
||||
client.close();
|
||||
}
|
||||
private static class MyLightListener implements LFXLightCollectionListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(MyLightListener.class);
|
||||
private Map<String, LifxDevice> aLifxMap;
|
||||
public MyLightListener(Map<String, LifxDevice> theMap) {
|
||||
aLifxMap = theMap;
|
||||
}
|
||||
@Override
|
||||
public void lightAdded(LFXLight light) {
|
||||
log.debug("Light added, label: " + light.getLabel() + " and id: " + light.getID());
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(light, LifxDevice.LIGHT_TYPE);
|
||||
aLifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lightRemoved(LFXLight light) {
|
||||
log.debug("Light removed, label: " + light.getLabel() + " and id: " + light.getID());
|
||||
aLifxMap.remove(light.getLabel());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
private static class MyGroupListener implements LFXGroupCollectionListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(MyLightListener.class);
|
||||
private Map<String, LifxDevice> aLifxMap;
|
||||
public MyGroupListener(Map<String, LifxDevice> theMap) {
|
||||
aLifxMap = theMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void groupAdded(LFXGroup group) {
|
||||
log.debug("Group: " + group.getLabel() + " added: " + group.size());
|
||||
LifxDevice aNewLifxDevice = new LifxDevice(group, LifxDevice.GROUP_TYPE);
|
||||
aLifxMap.put(aNewLifxDevice.toEntry().getName(), aNewLifxDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void groupRemoved(LFXGroup group) {
|
||||
log.debug("Group: " + group.getLabel() + " removed");
|
||||
aLifxMap.remove(group.getLabel());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bwssystems.HABridge.plugins.mqtt;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.eclipse.paho.client.mqttv3.MqttClient;
|
||||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
@@ -47,7 +48,7 @@ public class MQTTHandler {
|
||||
}
|
||||
|
||||
public void publishMessage(String topic, String content) {
|
||||
MqttMessage message = new MqttMessage(content.getBytes());
|
||||
MqttMessage message = new MqttMessage(StringEscapeUtils.unescapeJava(content).getBytes());
|
||||
message.setQos(qos);
|
||||
try {
|
||||
myClient.publish(topic, message);
|
||||
@@ -68,5 +69,6 @@ public class MQTTHandler {
|
||||
} catch (MqttException e) {
|
||||
log.warn("Could not disconnect MQTT client for name: " + myConfig.getName() + " and ip: " + myConfig.getIp());
|
||||
}
|
||||
myClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public class MQTTHome implements Home {
|
||||
handlers.get(key).shutdown();
|
||||
}
|
||||
}
|
||||
handlers = null;
|
||||
}
|
||||
|
||||
public MQTTHandler getMQTTHandler(String aName) {
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package com.bwssystems.HABridge.plugins.tcp;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -21,6 +26,7 @@ import com.bwssystems.HABridge.hue.TimeDecode;
|
||||
public class TCPHome implements Home {
|
||||
private static final Logger log = LoggerFactory.getLogger(TCPHome.class);
|
||||
private byte[] sendData;
|
||||
private Map<String, Socket> theSockets;
|
||||
|
||||
|
||||
public TCPHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
@@ -31,47 +37,68 @@ public class TCPHome implements Home {
|
||||
@Override
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
Socket dataSendSocket = null;
|
||||
log.debug("executing HUE api request to TCP: " + anItem.getItem().getAsString());
|
||||
String intermediate = anItem.getItem().getAsString().substring(anItem.getItem().getAsString().indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
InetAddress IPAddress = null;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && theUrl.startsWith("tcp://")) {
|
||||
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
InetAddress IPAddress = null;
|
||||
dataSendSocket = theSockets.get(hostPortion);
|
||||
if(dataSendSocket == null) {
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
try {
|
||||
IPAddress = InetAddress.getByName(hostAddr);
|
||||
} catch (UnknownHostException e) {
|
||||
// noop
|
||||
}
|
||||
|
||||
try {
|
||||
dataSendSocket = new Socket(IPAddress, Integer.parseInt(port));
|
||||
theSockets.put(hostPortion, dataSendSocket);
|
||||
} catch (Exception e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
|
||||
if (theUrlBody.startsWith("0x")) {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
} else {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, false);
|
||||
theUrlBody = StringEscapeUtils.unescapeJava(theUrlBody);
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
try {
|
||||
DataOutputStream outToClient = new DataOutputStream(dataSendSocket.getOutputStream());
|
||||
outToClient.write(sendData);
|
||||
outToClient.flush();
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not send data to TCP socket <<<" + e.getMessage() + ">>>, closing socket: " + theUrl);
|
||||
try {
|
||||
dataSendSocket.close();
|
||||
} catch (IOException e1) {
|
||||
// noop
|
||||
}
|
||||
theSockets.remove(hostPortion);
|
||||
}
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
try {
|
||||
IPAddress = InetAddress.getByName(hostAddr);
|
||||
} catch (UnknownHostException e) {
|
||||
// noop
|
||||
}
|
||||
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
|
||||
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
|
||||
if (theUrlBody.startsWith("0x")) {
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
} else {
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
|
||||
try {
|
||||
Socket dataSendSocket = new Socket(IPAddress, Integer.parseInt(port));
|
||||
DataOutputStream outToClient = new DataOutputStream(dataSendSocket.getOutputStream());
|
||||
outToClient.write(sendData);
|
||||
outToClient.flush();
|
||||
dataSendSocket.close();
|
||||
} catch (Exception e) {
|
||||
// noop
|
||||
}
|
||||
log.warn("Tcp Call to be presented as tcp://<ip_address>:<port>/payload, format of request unknown: " + theUrl);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
log.info("TCP Home created.");
|
||||
theSockets = new HashMap<String, Socket>();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -83,8 +110,18 @@ public class TCPHome implements Home {
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// noop
|
||||
|
||||
log.debug("Shutting down TCP sockets.");
|
||||
if(theSockets != null && !theSockets.isEmpty()) {
|
||||
Iterator<String> keys = theSockets.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
try {
|
||||
theSockets.get(key).close();
|
||||
} catch (IOException e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.net.UnknownHostException;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -33,38 +34,45 @@ public class UDPHome implements Home {
|
||||
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
|
||||
Integer targetBri,Integer targetBriInc, DeviceDescriptor device, String body) {
|
||||
log.debug("executing HUE api request to UDP: " + anItem.getItem().getAsString());
|
||||
String intermediate = anItem.getItem().getAsString().substring(anItem.getItem().getAsString().indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
InetAddress IPAddress = null;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
String theUrl = anItem.getItem().getAsString();
|
||||
if(theUrl != null && !theUrl.isEmpty () && theUrl.startsWith("udp://")) {
|
||||
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
|
||||
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
|
||||
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
|
||||
String hostAddr = null;
|
||||
String port = null;
|
||||
InetAddress IPAddress = null;
|
||||
if (hostPortion.contains(":")) {
|
||||
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
|
||||
port = hostPortion.substring(intermediate.indexOf(':') + 1);
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
try {
|
||||
IPAddress = InetAddress.getByName(hostAddr);
|
||||
} catch (UnknownHostException e) {
|
||||
log.warn("Udp Call, unknown host, continuing...");
|
||||
return null;
|
||||
}
|
||||
|
||||
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
|
||||
if (theUrlBody.startsWith("0x")) {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
} else {
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, false);
|
||||
theUrlBody = StringEscapeUtils.unescapeJava(theUrlBody);
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
try {
|
||||
theUDPDatagramSender.sendUDPResponse(sendData, IPAddress, Integer.parseInt(port));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("Udp Call, Number format exception on port, continuing...");
|
||||
} catch (IOException e) {
|
||||
log.warn("IO exception on udp call, continuing...");
|
||||
}
|
||||
} else
|
||||
hostAddr = hostPortion;
|
||||
try {
|
||||
IPAddress = InetAddress.getByName(hostAddr);
|
||||
} catch (UnknownHostException e) {
|
||||
log.warn("Udp Call, unknown host, continuing...");
|
||||
return null;
|
||||
}
|
||||
log.warn("Udp Call to be presented as udp://<ip_address>:<port>/payload, format of request unknown: " + theUrl);
|
||||
|
||||
theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true);
|
||||
theUrlBody = TimeDecode.replaceTimeValue(theUrlBody);
|
||||
if (theUrlBody.startsWith("0x")) {
|
||||
sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2));
|
||||
} else {
|
||||
sendData = theUrlBody.getBytes();
|
||||
}
|
||||
try {
|
||||
theUDPDatagramSender.sendUDPResponse(sendData, IPAddress, Integer.parseInt(port));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("Udp Call, Number format exception on port, continuing...");
|
||||
} catch (IOException e) {
|
||||
log.warn("IO exception on udp call, continuing...");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,6 @@ public class VeraHome implements Home {
|
||||
|
||||
@Override
|
||||
public void closeHome() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
veras = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@ app.config (function ($locationProvider, $routeProvider) {
|
||||
}).when ('/domoticzdevices', {
|
||||
templateUrl: 'views/domoticzdevice.html',
|
||||
controller: 'DomoticzController'
|
||||
}).when ('/lifxdevices', {
|
||||
templateUrl: 'views/lifxdevice.html',
|
||||
controller: 'LifxController'
|
||||
}).otherwise ({
|
||||
templateUrl: 'views/configuration.html',
|
||||
controller: 'ViewingController'
|
||||
@@ -71,7 +74,7 @@ String.prototype.replaceAll = function (search, replace)
|
||||
|
||||
app.service ('bridgeService', function ($http, $window, ngToast) {
|
||||
var self = this;
|
||||
this.state = {base: window.location.origin + "/api/devices", bridgelocation: window.location.origin, systemsbase: window.location.origin + "/system", huebase: window.location.origin + "/api", configs: [], backups: [], devices: [], device: {}, mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], mapTypes: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, showHal: false, showMqtt: false, showHass: false, showDomoticz: 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: {}, mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], mapTypes: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, showHal: false, showMqtt: false, showHass: false, showDomoticz: false, showLifx: false, habridgeversion: ""};
|
||||
|
||||
this.displayWarn = function(errorTitle, error) {
|
||||
var toastContent = errorTitle;
|
||||
@@ -257,6 +260,11 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateShowLifx = function () {
|
||||
this.state.showLifx = self.state.settings.lifxconfigured;
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadBridgeSettings = function () {
|
||||
return $http.get(this.state.systemsbase + "/settings").then(
|
||||
function (response) {
|
||||
@@ -269,6 +277,7 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
|
||||
self.updateShowMqtt();
|
||||
self.updateShowHass();
|
||||
self.updateShowDomoticz();
|
||||
self.updateShowLifx();
|
||||
},
|
||||
function (error) {
|
||||
self.displayWarn("Load Bridge Settings Error: ", error);
|
||||
@@ -450,6 +459,19 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
|
||||
);
|
||||
};
|
||||
|
||||
this.viewLifxDevices = function () {
|
||||
if (!this.state.showLifx)
|
||||
return;
|
||||
return $http.get(this.state.base + "/lifx/devices").then(
|
||||
function (response) {
|
||||
self.state.lifxdevices = response.data;
|
||||
},
|
||||
function (error) {
|
||||
self.displayWarn("Get Lifx Devices Error: ", error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
this.formatCallItem = function (currentItem) {
|
||||
if(!currentItem.startsWith("{\"item") && !currentItem.startsWith("[{\"item")) {
|
||||
if (currentItem.startsWith("[") || currentItem.startsWith("{"))
|
||||
@@ -1145,7 +1167,7 @@ app.controller('ValueDialogCtrl', function ($scope, bridgeService, ngDialog) {
|
||||
$scope.slider = {
|
||||
value: 100,
|
||||
options: {
|
||||
floor: 0,
|
||||
floor: 1,
|
||||
ceil: 100,
|
||||
showSelectionBar: true
|
||||
}
|
||||
@@ -1154,8 +1176,8 @@ app.controller('ValueDialogCtrl', function ($scope, bridgeService, ngDialog) {
|
||||
$scope.valueType = "percentage";
|
||||
$scope.changeScale = function () {
|
||||
if($scope.valueType === "raw") {
|
||||
$scope.slider.options.ceil = 255;
|
||||
$scope.slider.value = 255;
|
||||
$scope.slider.options.ceil = 254;
|
||||
$scope.slider.value = 254;
|
||||
}
|
||||
else {
|
||||
$scope.slider.options.ceil = 100;
|
||||
@@ -1164,7 +1186,7 @@ app.controller('ValueDialogCtrl', function ($scope, bridgeService, ngDialog) {
|
||||
};
|
||||
$scope.setValue = function () {
|
||||
ngDialog.close('ngdialog1');
|
||||
var theValue = 0;
|
||||
var theValue = 1;
|
||||
if($scope.valueType === "percentage")
|
||||
theValue = Math.round(($scope.slider.value * .01) * 255);
|
||||
else
|
||||
@@ -1205,7 +1227,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
|
||||
$scope.device = bridgeService.state.device;
|
||||
};
|
||||
|
||||
$scope.buildDeviceUrls = function (veradevice, dim_control) {
|
||||
$scope.buildDeviceUrls = function (veradevice, dim_control, buildonly) {
|
||||
if(dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0) {
|
||||
dimpayload = "http://" + veradevice.veraaddress + ":" + $scope.vera.port
|
||||
+ "/data_request?id=action&output_format=json&DeviceNum="
|
||||
@@ -1226,8 +1248,10 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
|
||||
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, false, veradevice.id, veradevice.name, veradevice.veraname, "switch", "veraDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.buildSceneUrls = function (verascene) {
|
||||
@@ -1246,10 +1270,11 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
|
||||
|
||||
$scope.bulkAddDevices = function(dim_control) {
|
||||
var devicesList = [];
|
||||
$scope.clearDevice();
|
||||
for(var i = 0; i < $scope.bulk.devices.length; i++) {
|
||||
for(var x = 0; x < bridgeService.state.veradevices.length; x++) {
|
||||
if(bridgeService.state.veradevices[x].id === $scope.bulk.devices[i]) {
|
||||
$scope.buildDeviceUrls(bridgeService.state.veradevices[x],dim_control);
|
||||
$scope.buildDeviceUrls(bridgeService.state.veradevices[x],dim_control,true);
|
||||
devicesList[i] = {
|
||||
name: $scope.device.name,
|
||||
mapId: $scope.device.mapId,
|
||||
@@ -1266,6 +1291,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
|
||||
contentBodyDim: $scope.device.contentBodyDim,
|
||||
contentBodyOff: $scope.device.contentBodyOff
|
||||
};
|
||||
$scope.clearDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1309,7 +1335,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
|
||||
else {
|
||||
$scope.selectAll = true;
|
||||
for(var x = 0; x < bridgeService.state.veradevices.length; x++) {
|
||||
if($scope.bulk.devices.indexOf(bridgeService.state.veradevices[x]) < 0 && !bridgeService.findDeviceByMapId(bridgeService.state.veradevices[x].id, bridgeService.state.veradevices[x].veraname, "veraDevice"))
|
||||
if($scope.bulk.devices.indexOf(bridgeService.state.veradevices[x]) < 0)
|
||||
$scope.bulk.devices.push(bridgeService.state.veradevices[x].id);
|
||||
}
|
||||
}
|
||||
@@ -1519,16 +1545,19 @@ app.controller('HueController', function ($scope, $location, $http, bridgeServic
|
||||
offpayload = "{\"ipAddress\":\"" + huedevice.hueaddress + "\",\"deviceId\":\"" + huedevice.huedeviceid +"\",\"hueName\":\"" + huedevice.huename + "\"}";
|
||||
bridgeService.buildUrls(onpayload, null, offpayload, true, huedevice.device.uniqueid, huedevice.device.name, huedevice.huename, "passthru", "hueDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.bulkAddDevices = function() {
|
||||
var devicesList = [];
|
||||
$scope.clearDevice();
|
||||
for(var i = 0; i < $scope.bulk.devices.length; i++) {
|
||||
for(var x = 0; x < bridgeService.state.huedevices.length; x++) {
|
||||
if(bridgeService.state.huedevices[x].device.uniqueid === $scope.bulk.devices[i]) {
|
||||
$scope.buildDeviceUrls(bridgeService.state.huedevices[x]);
|
||||
$scope.buildDeviceUrls(bridgeService.state.huedevices[x],true);
|
||||
devicesList[i] = {
|
||||
name: $scope.device.name,
|
||||
mapId: $scope.device.mapId,
|
||||
@@ -1545,6 +1574,7 @@ app.controller('HueController', function ($scope, $location, $http, bridgeServic
|
||||
contentBodyDim: $scope.device.contentBodyDim,
|
||||
contentBodyOff: $scope.device.contentBodyOff
|
||||
};
|
||||
$scope.clearDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1632,7 +1662,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
||||
$scope.device = bridgeService.state.device;
|
||||
};
|
||||
|
||||
$scope.buildDeviceUrls = function (haldevice, dim_control) {
|
||||
$scope.buildDeviceUrls = function (haldevice, dim_control, buildonly) {
|
||||
var preOnCmd = "";
|
||||
var preDimCmd = "";
|
||||
var preOffCmd = "";
|
||||
@@ -1687,11 +1717,13 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
||||
+ postCmd;
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname, haldevice.haldevicename, haldevice.halname, aDeviceType, "halDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.buildButtonUrls = function (haldevice, onbutton, offbutton) {
|
||||
$scope.buildButtonUrls = function (haldevice, onbutton, offbutton, buildonly) {
|
||||
var actionOn = angular.fromJson(onbutton);
|
||||
var actionOff = angular.fromJson(offbutton);
|
||||
onpayload = "http://" + haldevice.haladdress + "/IrService!IrCmd=Set!IrDevice=" + haldevice.haldevicename.replaceAll(" ", "%20") + "!IrButton=" + actionOn.DeviceName.replaceAll(" ", "%20") + "?Token=" + $scope.bridge.settings.haltoken;
|
||||
@@ -1699,20 +1731,24 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
||||
|
||||
bridgeService.buildUrls(onpayload, null, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-" + actionOn.DeviceName, haldevice.haldevicename, haldevice.halname, "button", "halButton", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.buildHALHomeUrls = function (haldevice) {
|
||||
$scope.buildHALHomeUrls = function (haldevice, buildonly) {
|
||||
onpayload = "http://" + haldevice.haladdress + "/ModeService!ModeCmd=Set!ModeName=Home?Token=" + $scope.bridge.settings.haltoken;
|
||||
offpayload = "http://" + haldevice.haladdress + "/ModeService!ModeCmd=Set!ModeName=Away?Token=" + $scope.bridge.settings.haltoken;
|
||||
bridgeService.buildUrls(onpayload, null, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-HomeAway", haldevice.haldevicename, haldevice.halname, "home", "halHome", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.buildHALHeatUrls = function (haldevice) {
|
||||
$scope.buildHALHeatUrls = function (haldevice, buildonly) {
|
||||
onpayload = "http://" + haldevice.haladdress
|
||||
+ "/HVACService!HVACCmd=Set!HVACName="
|
||||
+ haldevice.haldevicename.replaceAll(" ", "%20")
|
||||
@@ -1730,11 +1766,13 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
||||
+ $scope.bridge.settings.haltoken;
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-SetHeat", haldevice.haldevicename + " Heat", haldevice.halname, "thermo", "halThermoSet", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.buildHALCoolUrls = function (haldevice) {
|
||||
$scope.buildHALCoolUrls = function (haldevice, buildonly) {
|
||||
onpayload = "http://" + haldevice.haladdress
|
||||
+ "/HVACService!HVACCmd=Set!HVACName="
|
||||
+ haldevice.haldevicename.replaceAll(" ", "%20")
|
||||
@@ -1752,11 +1790,13 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
||||
+ $scope.bridge.settings.haltoken;
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-SetCool", haldevice.haldevicename + " Cool", haldevice.halname, "thermo", "halThermoSet", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.buildHALAutoUrls = function (haldevice) {
|
||||
$scope.buildHALAutoUrls = function (haldevice, buildonly) {
|
||||
onpayload = "http://" + haldevice.haladdress
|
||||
+ "/HVACService!HVACCmd=Set!HVACName="
|
||||
+ haldevice.haldevicename.replaceAll(" ", "%20")
|
||||
@@ -1768,11 +1808,13 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
||||
+ "!HVACMode=Off?Token="
|
||||
bridgeService.buildUrls(onpayload, null, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-SetAuto", haldevice.haldevicename + " Auto", haldevice.halname, "thermo", "halThermoSet", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.buildHALOffUrls = function (haldevice) {
|
||||
$scope.buildHALOffUrls = function (haldevice, buildonly) {
|
||||
onpayload = "http://" + haldevice.haladdress
|
||||
+ "/HVACService!HVACCmd=Set!HVACName="
|
||||
+ haldevice.haldevicename.replaceAll(" ", "%20")
|
||||
@@ -1785,11 +1827,13 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
||||
$scope.device.offUrl = "http://" + haldevice.haladdress
|
||||
bridgeService.buildUrls(onpayload, null, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-TurnOff", haldevice.haldevicename + " Thermostat", haldevice.halname, "thermo", "halThermoSet", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.buildHALFanUrls = function (haldevice) {
|
||||
$scope.buildHALFanUrls = function (haldevice, buildonly) {
|
||||
onpayload = "http://" + haldevice.haladdress
|
||||
+ "/HVACService!HVACCmd=Set!HVACName="
|
||||
+ haldevice.haldevicename.replaceAll(" ", "%20")
|
||||
@@ -1802,21 +1846,24 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
||||
+ $scope.bridge.settings.haltoken;
|
||||
bridgeService.buildUrls(onpayload, null, offpayload, false, haldevice.haldevicename + "-" + haldevice.halname + "-SetFan", haldevice.haldevicename + " Fan", haldevice.halname, "thermo", "halThermoSet", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.bulkAddDevices = function(dim_control) {
|
||||
var devicesList = [];
|
||||
$scope.clearDevice();
|
||||
for(var i = 0; i < $scope.bulk.devices.length; i++) {
|
||||
for(var x = 0; x < bridgeService.state.haldevices.length; x++) {
|
||||
if(bridgeService.state.haldevices[x].haldevicename === $scope.bulk.devices[i]) {
|
||||
if(bridgeService.state.haldevices[x].haldevicetype === "HVAC")
|
||||
$scope.buildHALAutoUrls(bridgeService.state.haldevices[x]);
|
||||
$scope.buildHALAutoUrls(bridgeService.state.haldevices[x], true);
|
||||
else if(bridgeService.state.haldevices[x].haldevicetype === "HOME")
|
||||
$scope.buildHALHomeUrls(bridgeService.state.haldevices[x]);
|
||||
$scope.buildHALHomeUrls(bridgeService.state.haldevices[x], true);
|
||||
else
|
||||
$scope.buildDeviceUrls(bridgeService.state.haldevices[x],dim_control);
|
||||
$scope.buildDeviceUrls(bridgeService.state.haldevices[x],dim_control, true);
|
||||
devicesList[i] = {
|
||||
name: $scope.device.name,
|
||||
mapId: $scope.device.mapId,
|
||||
@@ -1833,6 +1880,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
||||
contentBodyDim: $scope.device.contentBodyDim,
|
||||
contentBodyOff: $scope.device.contentBodyOff
|
||||
};
|
||||
$scope.clearDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1875,7 +1923,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
||||
else {
|
||||
$scope.selectAll = true;
|
||||
for(var x = 0; x < bridgeService.state.haldevices.length; x++) {
|
||||
if($scope.bulk.devices.indexOf(bridgeService.state.haldevices[x]) < 0 && !bridgeService.findDeviceByMapId(bridgeService.state.haldevices[x].haldevicename + "-" + bridgeService.state.haldevices[x].halname, bridgeService.state.haldevices[x].halname, "halDevice"))
|
||||
if($scope.bulk.devices.indexOf(bridgeService.state.haldevices[x]) < 0)
|
||||
$scope.bulk.devices.push(bridgeService.state.haldevices[x].haldevicename);
|
||||
}
|
||||
}
|
||||
@@ -1964,7 +2012,7 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
|
||||
$scope.device = bridgeService.state.device;
|
||||
};
|
||||
|
||||
$scope.buildDeviceUrls = function (hassdevice, dim_control) {
|
||||
$scope.buildDeviceUrls = function (hassdevice, dim_control, buildonly) {
|
||||
onpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
if((dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0))
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\",\"bri\":\"" + dim_control + "\"}";
|
||||
@@ -1974,89 +2022,19 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
|
||||
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
};
|
||||
|
||||
$scope.buildHassHeatUrls = function (hassdevice) {
|
||||
onpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
if((dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0))
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\",\"bri\":\"" + dim_control + "\"}";
|
||||
else
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
offpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"off\"}";
|
||||
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
};
|
||||
|
||||
$scope.buildHassCoolUrls = function (hassdevice) {
|
||||
onpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
if((dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0))
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\",\"bri\":\"" + dim_control + "\"}";
|
||||
else
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
offpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"off\"}";
|
||||
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
};
|
||||
|
||||
$scope.buildHassAutoUrls = function (hassdevice) {
|
||||
onpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
if((dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0))
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\",\"bri\":\"" + dim_control + "\"}";
|
||||
else
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
offpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"off\"}";
|
||||
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
};
|
||||
|
||||
$scope.buildHassOffUrls = function (hassdevice) {
|
||||
onpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
if((dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0))
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\",\"bri\":\"" + dim_control + "\"}";
|
||||
else
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
offpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"off\"}";
|
||||
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
};
|
||||
|
||||
$scope.buildHassFanUrls = function (hassdevice) {
|
||||
onpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
if((dim_control.indexOf("byte") >= 0 || dim_control.indexOf("percent") >= 0 || dim_control.indexOf("math") >= 0))
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\",\"bri\":\"" + dim_control + "\"}";
|
||||
else
|
||||
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
|
||||
offpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"off\"}";
|
||||
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.bulkAddDevices = function(dim_control) {
|
||||
var devicesList = [];
|
||||
$scope.clearDevice();
|
||||
for(var i = 0; i < $scope.bulk.devices.length; i++) {
|
||||
for(var x = 0; x < bridgeService.state.hassdevices.length; x++) {
|
||||
if(bridgeService.state.hassdevices[x].deviceName === $scope.bulk.devices[i]) {
|
||||
if(bridgeService.state.hassdevices[x].domain === "climate")
|
||||
$scope.buildHassAutoUrls(bridgeService.state.hassdevices[x]);
|
||||
else
|
||||
$scope.buildDeviceUrls(bridgeService.state.hassdevices[x],dim_control);
|
||||
if(bridgeService.state.hassdevices[x].deviceState.entity_id === $scope.bulk.devices[i] && bridgeService.state.hassdevices[x].domain !== "sensor" && bridgeService.state.hassdevices[x].domain !== "sun") {
|
||||
$scope.buildDeviceUrls(bridgeService.state.hassdevices[x],dim_control,true);
|
||||
devicesList[i] = {
|
||||
name: $scope.device.name,
|
||||
mapId: $scope.device.mapId,
|
||||
@@ -2073,6 +2051,7 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
|
||||
contentBodyDim: $scope.device.contentBodyDim,
|
||||
contentBodyOff: $scope.device.contentBodyOff
|
||||
};
|
||||
$scope.clearDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2080,7 +2059,7 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
|
||||
function () {
|
||||
$scope.clearDevice();
|
||||
bridgeService.viewDevices();
|
||||
bridgeService.viewhassdevices();
|
||||
bridgeService.viewHassDevices();
|
||||
},
|
||||
function (error) {
|
||||
bridgeService.displayWarn("Error adding Hass devices in bulk.", error)
|
||||
@@ -2115,8 +2094,8 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
|
||||
else {
|
||||
$scope.selectAll = true;
|
||||
for(var x = 0; x < bridgeService.state.hassdevices.length; x++) {
|
||||
if($scope.bulk.devices.indexOf(bridgeService.state.hassdevices[x]) < 0 && !bridgeService.findDeviceByMapId(bridgeService.state.hassdevices[x].hassdevicename + "-" + bridgeService.state.hassdevices[x].halname, bridgeService.state.hassdevices[x].halname, "hassdevice"))
|
||||
$scope.bulk.devices.push(bridgeService.state.hassdevices[x].hassdevicename);
|
||||
if($scope.bulk.devices.indexOf(bridgeService.state.hassdevices[x].deviceState.entity_id) < 0 && bridgeService.state.hassdevices[x].domain !== "sensor" && bridgeService.state.hassdevices[x].domain !== "sun")
|
||||
$scope.bulk.devices.push(bridgeService.state.hassdevices[x].deviceState.entity_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -2159,7 +2138,7 @@ app.controller('DomoticzController', function ($scope, $location, $http, bridgeS
|
||||
$scope.device = bridgeService.state.device;
|
||||
};
|
||||
|
||||
$scope.buildDeviceUrls = function (domoticzdevice, dim_control) {
|
||||
$scope.buildDeviceUrls = function (domoticzdevice, dim_control, buildonly) {
|
||||
var preCmd = "";
|
||||
var postOnCmd = "";
|
||||
var postDimCmd = "";
|
||||
@@ -2198,16 +2177,19 @@ app.controller('DomoticzController', function ($scope, $location, $http, bridgeS
|
||||
+ postOffCmd;
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, false, domoticzdevice.devicename + "-" + domoticzdevice.domoticzname, domoticzdevice.devicename, domoticzdevice.domoticzname, aDeviceType, "domoticzDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.bulkAddDevices = function(dim_control) {
|
||||
var devicesList = [];
|
||||
$scope.clearDevice();
|
||||
for(var i = 0; i < $scope.bulk.devices.length; i++) {
|
||||
for(var x = 0; x < bridgeService.state.domoticzdevices.length; x++) {
|
||||
if(bridgeService.state.domoticzdevices[x].devicename === $scope.bulk.devices[i]) {
|
||||
$scope.buildDeviceUrls(bridgeService.state.domoticzdevices[x],dim_control);
|
||||
$scope.buildDeviceUrls(bridgeService.state.domoticzdevices[x],dim_control,true);
|
||||
devicesList[i] = {
|
||||
name: $scope.device.name,
|
||||
mapId: $scope.device.mapId,
|
||||
@@ -2224,6 +2206,7 @@ app.controller('DomoticzController', function ($scope, $location, $http, bridgeS
|
||||
contentBodyDim: $scope.device.contentBodyDim,
|
||||
contentBodyOff: $scope.device.contentBodyOff
|
||||
};
|
||||
$scope.clearDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2234,7 +2217,7 @@ app.controller('DomoticzController', function ($scope, $location, $http, bridgeS
|
||||
bridgeService.viewHalDevices();
|
||||
},
|
||||
function (error) {
|
||||
bridgeService.displayWarn("Error adding HAL devices in bulk.", error)
|
||||
bridgeService.displayWarn("Error adding Domoticz devices in bulk.", error)
|
||||
}
|
||||
);
|
||||
$scope.bulk = { devices: [] };
|
||||
@@ -2266,8 +2249,130 @@ app.controller('DomoticzController', function ($scope, $location, $http, bridgeS
|
||||
else {
|
||||
$scope.selectAll = true;
|
||||
for(var x = 0; x < bridgeService.state.haldevices.length; x++) {
|
||||
if($scope.bulk.devices.indexOf(bridgeService.state.haldevices[x]) < 0 && !bridgeService.findDeviceByMapId(bridgeService.state.haldevices[x].haldevicename + "-" + bridgeService.state.haldevices[x].halname, bridgeService.state.haldevices[x].halname, "halDevice"))
|
||||
$scope.bulk.devices.push(bridgeService.state.haldevices[x].haldevicename);
|
||||
if($scope.bulk.devices.indexOf(bridgeService.state.domoticzdevices[x]) < 0)
|
||||
$scope.bulk.devices.push(bridgeService.state.domoticzdevices[x].devicename);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.toggleButtons = function () {
|
||||
$scope.buttonsVisible = !$scope.buttonsVisible;
|
||||
if($scope.buttonsVisible)
|
||||
$scope.imgButtonsUrl = "glyphicon glyphicon-minus";
|
||||
else
|
||||
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
|
||||
};
|
||||
|
||||
$scope.deleteDevice = function (device) {
|
||||
$scope.bridge.device = device;
|
||||
ngDialog.open({
|
||||
template: 'deleteDialog',
|
||||
controller: 'DeleteDialogCtrl',
|
||||
className: 'ngdialog-theme-default'
|
||||
});
|
||||
};
|
||||
|
||||
$scope.editDevice = function (device) {
|
||||
bridgeService.editDevice(device);
|
||||
$location.path('/editdevice');
|
||||
};
|
||||
});
|
||||
|
||||
app.controller('LifxController', function ($scope, $location, $http, bridgeService, ngDialog) {
|
||||
$scope.bridge = bridgeService.state;
|
||||
$scope.device = bridgeService.state.device;
|
||||
$scope.device_dim_control = "";
|
||||
$scope.bulk = { devices: [] };
|
||||
$scope.selectAll = false;
|
||||
bridgeService.viewLifxDevices();
|
||||
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
|
||||
$scope.buttonsVisible = false;
|
||||
|
||||
$scope.clearDevice = function () {
|
||||
bridgeService.clearDevice();
|
||||
$scope.device = bridgeService.state.device;
|
||||
};
|
||||
|
||||
$scope.buildDeviceUrls = function (lifxdevice, dim_control, buildonly) {
|
||||
dimpayload = angular.toJson(lifxdevice);
|
||||
onpayload = angular.toJson(lifxdevice);
|
||||
offpayload = angular.toJson(lifxdevice);
|
||||
bridgeService.buildUrls(onpayload, dimpayload, offpayload, true, lifxdevice.name, lifxdevice.name, lifxdevice.name, null, "lifxDevice", null, null);
|
||||
$scope.device = bridgeService.state.device;
|
||||
if (!buildonly) {
|
||||
bridgeService.editNewDevice($scope.device);
|
||||
$location.path('/editdevice');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.bulkAddDevices = function(dim_control) {
|
||||
var devicesList = [];
|
||||
$scope.clearDevice();
|
||||
for(var i = 0; i < $scope.bulk.devices.length; i++) {
|
||||
for(var x = 0; x < bridgeService.state.lifxdevices.length; x++) {
|
||||
if(bridgeService.state.lifxdevices[x].devicename === $scope.bulk.devices[i]) {
|
||||
$scope.buildDeviceUrls(bridgeService.state.lifxdevices[x],dim_control,true);
|
||||
devicesList[i] = {
|
||||
name: $scope.device.name,
|
||||
mapId: $scope.device.mapId,
|
||||
mapType: $scope.device.mapType,
|
||||
deviceType: $scope.device.deviceType,
|
||||
targetDevice: $scope.device.targetDevice,
|
||||
onUrl: $scope.device.onUrl,
|
||||
dimUrl: $scope.device.dimUrl,
|
||||
offUrl: $scope.device.offUrl,
|
||||
headers: $scope.device.headers,
|
||||
httpVerb: $scope.device.httpVerb,
|
||||
contentType: $scope.device.contentType,
|
||||
contentBody: $scope.device.contentBody,
|
||||
contentBodyDim: $scope.device.contentBodyDim,
|
||||
contentBodyOff: $scope.device.contentBodyOff
|
||||
};
|
||||
$scope.clearDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
bridgeService.bulkAddDevice(devicesList).then(
|
||||
function () {
|
||||
$scope.clearDevice();
|
||||
bridgeService.viewDevices();
|
||||
bridgeService.viewHalDevices();
|
||||
},
|
||||
function (error) {
|
||||
bridgeService.displayWarn("Error adding LIFX devices in bulk.", error)
|
||||
}
|
||||
);
|
||||
$scope.bulk = { devices: [] };
|
||||
$scope.selectAll = false;
|
||||
};
|
||||
|
||||
$scope.toggleSelection = function toggleSelection(deviceId) {
|
||||
var idx = $scope.bulk.devices.indexOf(deviceId);
|
||||
|
||||
// is currently selected
|
||||
if (idx > -1) {
|
||||
$scope.bulk.devices.splice(idx, 1);
|
||||
if($scope.bulk.devices.length === 0 && $scope.selectAll)
|
||||
$scope.selectAll = false;
|
||||
}
|
||||
|
||||
// is newly selected
|
||||
else {
|
||||
$scope.bulk.devices.push(deviceId);
|
||||
$scope.selectAll = true;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.toggleSelectAll = function toggleSelectAll() {
|
||||
if($scope.selectAll) {
|
||||
$scope.selectAll = false;
|
||||
$scope.bulk = { devices: [] };
|
||||
}
|
||||
else {
|
||||
$scope.selectAll = true;
|
||||
for(var x = 0; x < bridgeService.state.haldevices.length; x++) {
|
||||
if($scope.bulk.devices.indexOf(bridgeService.state.lifxdevices[x]) < 0)
|
||||
$scope.bulk.devices.push(bridgeService.state.lifxdevices[x].devicename);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -2342,8 +2447,11 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi
|
||||
bridgeService.displayWarn("Error adding device. Name has not been changed from original.", null);
|
||||
return;
|
||||
}
|
||||
if (copy)
|
||||
if (copy) {
|
||||
$scope.device.id = null;
|
||||
$scope.device.uniqueid = null;
|
||||
$scope.device.mapId = $scope.device.mapId + "-copy";
|
||||
}
|
||||
if($scope.mapTypeSelected !== undefined && $scope.mapTypeSelected !== null)
|
||||
$scope.device.mapType = $scope.mapTypeSelected[0];
|
||||
else
|
||||
@@ -2566,6 +2674,20 @@ app.filter('configuredDomoticzItems', function (bridgeService) {
|
||||
}
|
||||
});
|
||||
|
||||
app.filter('configuredLifxItems', function (bridgeService) {
|
||||
return function(input) {
|
||||
var out = [];
|
||||
if(input === undefined || input === null || input.length === undefined)
|
||||
return out;
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
if (bridgeService.deviceContainsType(input[i], "lifx")) {
|
||||
out.push(input[i]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
});
|
||||
|
||||
app.controller('VersionController', function ($scope, bridgeService) {
|
||||
$scope.bridge = bridgeService.state;
|
||||
});
|
||||
@@ -20,6 +20,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -35,17 +36,18 @@
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Domoticz Devices' list below will show what
|
||||
is already setup for your Domoticz.</p>
|
||||
<p>
|
||||
<p class="text-muted">
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.decimal_percent}">Decimal Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on and off or dim and
|
||||
off if selected with the name of the device from the Domoticz.</p>
|
||||
@@ -75,7 +77,7 @@
|
||||
<td>{{domoticzdevice.domoticzname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(domoticzdevice, device_dim_control)">Build Item</button>
|
||||
ng-click="buildDeviceUrls(domoticzdevice, device_dim_control,false)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<li ng-if="bridge.showHass" role="presentation"><a
|
||||
href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -32,7 +33,7 @@
|
||||
fields the bridge uses. Please use care when updating these fields as
|
||||
you may break the settings used by the bridge to call a specific end
|
||||
point device.</p>
|
||||
<p class="text-muted">This area allows you to create any http or
|
||||
<p class="text-muted">This area allows you to create any http, tcp or
|
||||
udp call to an endpoint. You can use the default GET or select the
|
||||
http verb type below and configure a payload for either on, dim or
|
||||
off methods. For Execution of a
|
||||
@@ -40,10 +41,10 @@
|
||||
can use Json notation of array with [{"item":"the
|
||||
payload"},{"item":"another payload"}] to
|
||||
execute multiple entries. Adding the value replacements
|
||||
(${intensity..byte},${intensity.percent},${intensity.math(X*1)}) will
|
||||
(${intensity.byte},${intensity.percent},${intensity.decimal_percent},${intensity.math(X*1)}) will
|
||||
also work. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command.</p>
|
||||
<p>When copying, update the name and select the "Add Bridge
|
||||
<p class="text-muted">When copying, update the name and select the "Add Bridge
|
||||
Device" Button.</p>
|
||||
|
||||
<form name="form">
|
||||
@@ -64,17 +65,22 @@
|
||||
ng-model="device.name" placeholder="Device Name"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Inactive</td>
|
||||
<td><label>Inactive</label></td>
|
||||
<td><input type="checkbox"
|
||||
ng-model="device.inactive" ng-true-value=true
|
||||
ng-false-value=false> {{device.inactive}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No State (Do not update state for device)</td>
|
||||
<td><label>No State (Do not update state for device)</label></td>
|
||||
<td><input type="checkbox"
|
||||
ng-model="device.noState" ng-true-value=true
|
||||
ng-false-value=false> {{device.noState}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Filter Address (comma separated list)</label></td>
|
||||
<td><input type="text" class="form-control" id="device-requester-addr"
|
||||
ng-model="device.requesterAddress" placeholder="Only use if you want to restrict this device to a specific caller(s)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Target</label></td>
|
||||
|
||||
@@ -129,6 +135,7 @@
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<th>Type</th>
|
||||
<th>Target Item</th>
|
||||
<th>Delay</th>
|
||||
@@ -139,19 +146,21 @@
|
||||
<th>Http Headers</th>
|
||||
<th>Content Type</th>
|
||||
<th>Manage</th>
|
||||
</div>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="onItem in onDevices">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="onItem.type"></select></td>
|
||||
<td><textarea rows="1" cols="20" class="form-control"
|
||||
<td><textarea rows="1" class="form-control"
|
||||
id="item-target" ng-model="onItem.item" placeholder="The Call"></textarea></td>
|
||||
<td><textarea rows="1" cols="4" class="form-control"
|
||||
<td><textarea rows="1" class="form-control"
|
||||
id="item-delay" ng-model="onItem.delay" placeholder="millis"></textarea></td>
|
||||
<td><textarea rows="1" cols="2" class="form-control"
|
||||
<td><textarea rows="1" class="form-control"
|
||||
id="item-count" ng-model="onItem.count" placeholder="number"></textarea></td>
|
||||
<td><textarea rows="1" cols="16" class="form-control"
|
||||
<td><textarea rows="1" class="form-control"
|
||||
id="item-filterIPs" ng-model="onItem.filterIPs"
|
||||
placeholder="restrict IPs"></textarea></td>
|
||||
<td><select name="item-http-verb" id="item-http-verb"
|
||||
@@ -162,7 +171,7 @@
|
||||
<option value="PUT">PUT</option>
|
||||
<option value="POST">POST</option>
|
||||
</select></td>
|
||||
<td><textarea rows="1" cols="16" class="form-control"
|
||||
<td><textarea rows="1" class="form-control"
|
||||
id="item-httpBody" ng-model="onItem.httpBody"
|
||||
placeholder="body args"></textarea></td>
|
||||
<td><textarea rows="1" cols="16" class="form-control"
|
||||
@@ -188,8 +197,10 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="removeItemOn(onItem)">Del</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="newOnItem.type"></select></td>
|
||||
@@ -239,6 +250,7 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-success" type="submit"
|
||||
ng-click="addItemOn(newOnItem)">Add</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table></td>
|
||||
@@ -250,6 +262,7 @@
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<th>Type</th>
|
||||
<th>Target Item</th>
|
||||
<th>Delay</th>
|
||||
@@ -260,9 +273,11 @@
|
||||
<th>Http Headers</th>
|
||||
<th>Content Type</th>
|
||||
<th>Manage</th>
|
||||
</div>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="dimItem in dimDevices">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="dimItem.type"></select></td>
|
||||
@@ -310,8 +325,10 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="removeItemDim(dimItem)">Del</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="newDimItem.type"></select></td>
|
||||
@@ -361,6 +378,7 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-success" type="submit"
|
||||
ng-click="addItemDim(newDimItem)">Add</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table></td>
|
||||
@@ -372,6 +390,7 @@
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<th>Type</th>
|
||||
<th>Target Item</th>
|
||||
<th>Delay</th>
|
||||
@@ -382,9 +401,11 @@
|
||||
<th>Http Headers</th>
|
||||
<th>Content Type</th>
|
||||
<th>Manage</th>
|
||||
</div>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="offItem in offDevices">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="offItem.type"></select></td>
|
||||
@@ -432,8 +453,10 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="removeItemOff(offItem)">Del</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
<tr>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<td><select
|
||||
ng-options="mapType as mapType[1] for mapType in bridge.mapTypes track by mapType[0]"
|
||||
ng-model="newOffItem.type"></select></td>
|
||||
@@ -483,6 +506,7 @@
|
||||
</select></td>
|
||||
<td><button class="btn btn-success" type="submit"
|
||||
ng-click="addItemOff(newOffItem)">Add</button></td>
|
||||
</div>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table></td>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -34,17 +35,18 @@
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured HAL Devices' list below will show what
|
||||
is already setup for your HAL.</p>
|
||||
<p>
|
||||
<p class="text-muted">
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.decimal_percent}">Decimal Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on and off or dim and
|
||||
off if selected with the name of the device from the HAL.</p>
|
||||
@@ -88,27 +90,27 @@
|
||||
</td>
|
||||
<td>
|
||||
<button ng-if="haldevice.haldevicetype != 'Home' && haldevice.haldevicetype != 'HVAC' && haldevice.haldevicetype != 'IrData'" class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(haldevice, device_dim_control)">Build Item</button>
|
||||
ng-click="buildDeviceUrls(haldevice, device_dim_control, false)">Build Item</button>
|
||||
<button ng-if="haldevice.haldevicetype == 'Home'" class="btn btn-success" type="submit"
|
||||
ng-click="buildHALHomeUrls(haldevice)">Build Home/Away</button>
|
||||
ng-click="buildHALHomeUrls(haldevice, false)">Build Home/Away</button>
|
||||
<button ng-if="haldevice.haldevicetype == 'IrData'" class="btn btn-success" type="submit"
|
||||
ng-click="buildButtonUrls(haldevice, button_on, button_off)">Build
|
||||
ng-click="buildButtonUrls(haldevice, button_on, button_off, false)">Build
|
||||
A Button</button>
|
||||
<ul ng-if="haldevice.haldevicetype == 'HVAC'" class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALHeatUrls(haldevice)">Heat</button>
|
||||
ng-click="buildHALHeatUrls(haldevice, false)">Heat</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALCoolUrls(haldevice)">Cool</button>
|
||||
ng-click="buildHALCoolUrls(haldevice, false)">Cool</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALAutoUrls(haldevice)">Auto</button>
|
||||
ng-click="buildHALAutoUrls(haldevice, false)">Auto</button>
|
||||
</p>
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALOffUrls(haldevice)">Off</button>
|
||||
ng-click="buildHALOffUrls(haldevice, false)">Off</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildHALFanUrls(haldevice)">Fan</button>
|
||||
ng-click="buildHALFanUrls(haldevice, false)">Fan</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a
|
||||
href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation" class="active"><a href="#!/haldevices">HAL
|
||||
<li ng-if="bridge.showHal" role="presentation"><a href="#!/haldevices">HAL
|
||||
Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -35,17 +36,18 @@
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured HomeAssistant Devices' list below will show what
|
||||
is already setup for your HomeAssitant.</p>
|
||||
<p>
|
||||
<p class="text-muted">
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated, BUT for Home Assistant, the selection should be Pass-thru: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.decimal_percent}">Decimal Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on and off or dim and
|
||||
off if selected with the name of the device from the HomeAssitant.</p>
|
||||
@@ -72,26 +74,8 @@
|
||||
{{hassdevice.deviceState.entity_id}}</td>
|
||||
<td>{{hassdevice.hassname}}</td>
|
||||
<td>
|
||||
<button ng-if="hassdevice.domain != 'climate' && hassdevice.domain != 'sensor' && hassdevice.domain != 'sun'" class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(hassdevice, device_dim_control)">Build Item</button>
|
||||
<ul ng-if="hassdevice.domain == 'climate'" class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildhassHeatUrls(hassdevice)">Heat</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildhassCoolUrls(hassdevice)">Cool</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildhassAutoUrls(hassdevice)">Auto</button>
|
||||
</p>
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildhassOffUrls(hassdevice)">Off</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildhassFanUrls(hassdevice)">Fan</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<button ng-if="hassdevice.domain != 'sensor' && hassdevice.domain != 'sun'" class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(hassdevice, device_dim_control, false)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -35,9 +36,9 @@
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Hue Devices' list below will show what
|
||||
is already setup for your Hue.</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items, then click bulk add below. Your items
|
||||
will be added with on and off or dim and off if selected with the
|
||||
will be added with on, off and dim with the
|
||||
name of the device from the Hue.</p>
|
||||
<scrollable-table watch="bridge.huedevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
@@ -64,7 +65,7 @@
|
||||
<td>{{huedevice.huename}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(huedevice)">Build Item</button>
|
||||
ng-click="buildDeviceUrls(huedevice, false)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
122
src/main/resources/public/views/lifxdevice.html
Normal file
122
src/main/resources/public/views/lifxdevice.html
Normal file
@@ -0,0 +1,122 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#!/logs">Logs</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#!/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#!/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#!/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#!/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
|
||||
<li ng-if="bridge.showHue" role="presentation"><a href="#!/huedevices">Hue Devices</a></li>
|
||||
<li ng-if="bridge.showHal" role="presentation"><a href="#!/haldevices">HAL Devices</a></li>
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">LIFX Devices</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-muted">For any LIFX Device, use the build action buttons
|
||||
to generate the item addition information into the ha-bridge device and this will put you into the edit screen. Then
|
||||
you can modify the name to anything you want that will be the keyword
|
||||
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
|
||||
action button to add another item for a multi-command. After you are
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured LIFX Devices' list below will show what
|
||||
is already setup for your LIFX.</p>
|
||||
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on, off and dim
|
||||
with the name of the device from the LIFX device.</p>
|
||||
<scrollable-table watch="bridge.lifxdevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">
|
||||
<span><input type="checkbox" name="selectAll"
|
||||
value="{{selectAll}}"
|
||||
ng-checked="selectAll"
|
||||
ng-click="toggleSelectAll()"> Name</span></th>
|
||||
<th sortable-header col="id">ID</th>
|
||||
<th sortable-header col="type">Type</th>
|
||||
<th>Build Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="lifxdev in bridge.lifxdevices">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]"
|
||||
value="{{lifxdev.name}}"
|
||||
ng-checked="bulk.devices.indexOf(lifxdev.name) > -1"
|
||||
ng-click="toggleSelection(lifxdev.name)">{{lifxdev.name}}</td>
|
||||
<td>{{lifxdev.id}}</td>
|
||||
<td>{{lifxdev.type}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(lifxdev, false)">Build
|
||||
Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkAddDevices(device_dim_control)">Bulk Add
|
||||
({{bulk.devices.length}})</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
Already Configured LIFX Devices <a ng-click="toggleButtons()"><span
|
||||
class={{imgButtonsUrl}} aria-hidden="true"></span></a>
|
||||
</h2>
|
||||
</div>
|
||||
<div ng-if="buttonsVisible" class="panel-body">
|
||||
<scrollable-table watch="bridge.lifxdevices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th sortable-header col="name">Name</th>
|
||||
<th sortable-header col="targetDevice">Light Name</th>
|
||||
<th>Map Id</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="device in bridge.devices | configuredLifxItems | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</scrollable-table>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/ng-template" id="deleteMapandIdDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Device Map and Id?</h2>
|
||||
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
|
||||
<p>Are you Sure?</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
|
||||
</div>
|
||||
</script>
|
||||
@@ -18,6 +18,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<li role="presentation" class="active"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -375,6 +376,12 @@
|
||||
ng-model="bridge.settings.farenheit" ng-true-value=true
|
||||
ng-false-value=false> {{bridge.settings.farenheit}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>LIFX Support</td>
|
||||
<td><input type="checkbox"
|
||||
ng-model="bridge.settings.lifxconfigured" ng-true-value=true
|
||||
ng-false-value=false> {{bridge.settings.lifxconfigured}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Emulate Hue Hub Version</td>
|
||||
<td><input id="bridge-settings-hubversion" class="form-control"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -35,17 +36,18 @@
|
||||
done in the edit tab, click the 'Add Bridge Device' to finish that selection
|
||||
setup. The 'Already Configured Vera Devices' list below will show
|
||||
what is already setup for your Vera.</p>
|
||||
<p>
|
||||
<p class="text-muted">
|
||||
Also, use this select menu for which type of dim control you would
|
||||
like to be generated: <select name="device-dim-control"
|
||||
id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
<option value="${intensity.byte}">Pass-thru Value</option>
|
||||
<option value="${intensity.percent}">Percentage</option>
|
||||
<option value="${intensity.decimal_percent}">Decimal Percentage</option>
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition
|
||||
<p class="text-muted">Use the check boxes by the names to use the bulk addition
|
||||
feature. Select your items and dim control type if wanted, then click
|
||||
bulk add below. Your items will be added with on and off or dim and
|
||||
off if selected with the name of the device from the Vera.</p>
|
||||
@@ -79,7 +81,7 @@
|
||||
<td>{{veradevice.veraname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(veradevice, device_dim_control)">Build Item</button>
|
||||
ng-click="buildDeviceUrls(veradevice, device_dim_control, false)">Build Item</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
|
||||
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
|
||||
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
|
||||
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
|
||||
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user