Compare commits

...

24 Commits

Author SHA1 Message Date
BWS Systems
df67980bd6 Fix compile error in bridghtnessdecode class. 2018-11-15 09:46:56 -06:00
BWS Systems
b6b78c4849 Update v to v5.2.2 2018-11-14 11:29:45 -06:00
BWS Systems
b1d1f2ac46 changes for dfim at 1percent and domoticz https usage 2018-11-14 11:25:54 -06:00
BWS Systems
750056df06 add new device param for on with dim and changed behavior for onFirstDim 2018-11-13 14:09:53 -06:00
BWS Systems
3f13e957ad update handling for upnp response with ip path 2018-11-12 16:25:15 -06:00
BWS Systems
afc254720c Update for parse default route 2018-11-06 16:11:42 -06:00
BWS Systems
943e4420e6 update .gitignore 2018-11-06 15:52:34 -06:00
BWS Systems
c9e6cd079f Update Harmonhy connection handling on disconnect. Add Route Parse 2018-11-06 15:46:45 -06:00
bsamuels
faae6aa31f Updating upnp address corrections 2018-08-07 16:20:48 -05:00
BWS Systems
c25f08f142 Merge pull request #927 from nel-stefan/master
Added quotes around strings in json response for huemulator
2018-08-07 13:42:23 -05:00
audiofreak9
d6ad9d288e Updated README Alexa device list
Updated README Alexa device list to include all names of physical Amazon devices for clarity.
2018-07-10 17:01:42 -04:00
BWS Systems
3400c4d43a Fix habridge directory to be ha-bridge
Fixes #947
2018-04-10 13:15:30 -05:00
BWS Systems
ddcbea001c add dash in path 2018-03-28 08:39:27 -05:00
Stefan Marchal
9a35e47c27 Added quotes around strings in json response for huemulator
Bumped version number
2018-03-19 22:44:53 +01:00
BWS Systems
199fcce549 Merge pull request #883 from escalate/master
Update README.md with better instructions for running created by @escalate
2018-03-16 09:17:38 -05:00
BWS Systems
bfeb382d1f Merge branch 'master' into master 2018-03-16 09:16:49 -05:00
bsamuels
3a93d9e98f Updated version for release and linked in the mob41 master branch for
broadlink api
2018-03-16 08:31:45 -05:00
bsamuels
3b22e3f711 Start bug fixing, update FHEM bulk add issue 2018-03-09 09:18:23 -06:00
Felix Boerner
dc28eb2984 docs: refine Docker container usage instructions 2018-01-29 08:15:34 +01:00
Felix Boerner
b8acb4a52c feat: add minus in ha-bridge names for consistency 2018-01-27 13:07:37 +01:00
Felix Boerner
21fdaf4545 docs: remove trailing whitespaces 2018-01-26 13:42:49 +01:00
Felix Boerner
db192df2c6 docs: add section how to run docker container 2018-01-26 13:42:00 +01:00
Felix Boerner
4a24d263c1 docs: rewrite manual installation section 2018-01-26 13:02:59 +01:00
Felix Boerner
3394559539 docs: remove basic script setup section
It is not best practice to run an application this way.
2018-01-26 10:54:31 +01:00
18 changed files with 864 additions and 130 deletions

4
.gitignore vendored
View File

@@ -15,3 +15,7 @@ data
sftp-config\.json
/bin/
.vscode/launch.json
.vscode/launch.test.json
.vscode/settings.json
.vscode/tasks.json

112
README.md
View File

@@ -29,7 +29,7 @@ A Custom implementation path looks like this:
```
**SECURITY RISK: If you are unsure on how this software operates and what it exposes to your network, please make sure you understand that it can allow root access to your system. It is best practice to not open this to the Internet through your router as there are no security protocols in place to protect the system. The License agreement states specifically that you use this at your own risk.**
**ATTENTION: This requires a physical Amazon Echo, Dot or Tap and does not work with prototype devices built using the Alexa Voice Service e.g. Amazon's Alexa AVS Sample App and Sam Machin's AlexaPi. The AVS version does not have any capability for Hue Bridge discovery!**
**ATTENTION: This requires a physical Amazon Echo, Echo Plus, Spot, Tap or Show and does not work with prototype devices built using the Alexa Voice Service e.g. Amazon's Alexa AVS Sample App and Sam Machin's AlexaPi. The AVS version does not have any capability for Hue Bridge discovery!**
**NOTE: This software does require the user to have knowledge on how processes run on Linux or Windows with java. Also, an understanding of networking basics will help as well. This system receives upnp udp multicast packets from devices to be found, so that is something to understand. Please make sure you have all your devices use static IP addresses from your router. Most all questions have been answered already. PLEASE USE GOOGLE TO FIND YOUR ANSWERS!**
@@ -63,31 +63,31 @@ 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-5.2.0.jar
java -jar ha-bridge-5.2.2.jar
```
ATTENTION: If running Java9, you will need to add the xml bind module
```
java -jar --add-modules java.xml.bind ha-bridge-5.2.0.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-5.2.0.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/v5.2.0/ha-bridge-5.2.0.jar
java -jar --add-modules java.xml.bind ha-bridge-5.2.2.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
## Manual installation of ha-bridge and setup of systemd service
Next gen Linux systems (this includes the Raspberry Pi), use systemd to run and manage services.
Here is a link on how to use systemd: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
Start here to create the habridge.service unit file:
Create the directory and make sure that ha-bridge-5.2.2.jar is in your /home/pi/ha-bridge directory.
```
pi@raspberrypi:~ $ mkdir ha-bridge
pi@raspberrypi:~ $ cd ha-bridge
pi@raspberrypi:~/ha-bridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v5.2.2/ha-bridge-5.2.2.jar
```
Create the ha-bridge.service unit file:
```
pi@raspberrypi:~ $ cd /etc/systemd/system
pi@raspberrypi:~ $ sudo nano habridge.service
pi@raspberrypi:~ $ sudo nano ha-bridge.service
```
Copy the text below into the editor nano.
```
@@ -98,58 +98,92 @@ After=network.target
[Service]
Type=simple
WorkingDirectory=/home/pi/habridge
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-5.2.0.jar
WorkingDirectory=/home/pi/ha-bridge
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/ha-bridge/data/habridge.config /home/pi/ha-bridge/ha-bridge-5.2.2.jar
[Install]
WantedBy=multi-user.target
```
Save the file in the editor by hitting CTL-X and then saying Y to update and save.
Reload the system control config:
```
pi@raspberrypi:~ $ sudo systemctl daemon-reload
```
To start the bridge:
```
pi@raspberrypi:~ $ sudo systemctl start habridge.service
pi@raspberrypi:~ $ sudo systemctl start ha-bridge.service
```
To start the service at boot, use the `enable` command:
```
pi@raspberrypi:~ $ sudo systemctl enable habridge.service
pi@raspberrypi:~ $ sudo systemctl enable ha-bridge.service
```
To look at the log, the output goes into the system log at `/var/log/syslog':
```
pi@raspberrypi:~ $ tail -f /var/log/syslog
```
#### Basic script setup to run the bridge on a pi.
*NOTE ON RC.LOCAL*: Due to the way network subsystem is brought up on the pi, it uses the new systemctl to start services. The old style runlevel setup, which rc.local is part of does not get the benefit of knowing if the network has been fully realized. Starting ha-bridge from rc.local on next gen systems will cause unexpected results and issues with discovering registered devices.
### ha-bridge inside Docker container
Docker start offering official support for Raspbian operating system since autumn 2016.
Running services inside containers became to be a good alternative to normal installation method described before.
Edit the shell script for starting:
Install Docker Community Edition (CE) on Raspberry Pi
```
pi@raspberrypi:~/habridge $ nano starthabridge.sh
pi@raspberrypi:~ $ curl -fsSL get.docker.com -o get-docker.sh
pi@raspberrypi:~ $ sudo sh get-docker.sh
```
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-5.2.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
chmod 777 /home/pi/habridge/habridge-log.txt
For every architecture there is a specialized ha-bridge Docker image available.
Please use the suitable image from the following list.
* Generic x86 / x86_64 system: [aptalca/home-automation-bridge](https://hub.docker.com/r/aptalca/home-automation-bridge)
* Raspberry Pi 1 (ARM): [habridge/ha-bridge-raspberry-pi](https://hub.docker.com/r/habridge/ha-bridge-raspberry-pi)
* Raspberry Pi 2 (ARM): [habridge/ha-bridge-raspberry-pi2](https://hub.docker.com/r/habridge/ha-bridge-raspberry-pi2)
* Raspberry Pi 3 (ARM): [habridge/ha-bridge-raspberrypi3](https://hub.docker.com/r/habridge/ha-bridge-raspberrypi3)
The following example explains how to run the latest version of ha-bridge Docker container on Raspberry Pi 3.
```
Exit and save the file with ctrl-X and follow the prompts and then execute on the command line:
pi@raspberrypi:~ $ docker pull habridge/ha-bridge-raspberrypi3
pi@raspberrypi:~ $ docker run \
--name ha-bridge \
--rm \
--init \
--detach \
--net=host \
--volume=$PWD:/ha-bridge/data \
--volume=/etc/localtime:/etc/localtime:ro \
--volume=/etc/timezone:/etc/timezone:ro \
habridge/ha-bridge-raspberrypi3
```
pi@raspberrypi:~/habridge $ chmod u+x starthabridge.sh
To set additional arguments for ha-bridge just write them as arguments for docker run command.
```
Then execute the script:
pi@raspberrypi:~ $ docker run \
--name ha-bridge \
--rm \
--init \
--detach \
--net=host \
--volume=$PWD:/ha-bridge/data \
--volume=/etc/localtime:/etc/localtime:ro \
--volume=/etc/timezone:/etc/timezone:ro \
habridge/ha-bridge-raspberry-pi3 \
-Dserver.port=8080 \
-Dsecurity.key=secret
```
pi@raspberrypi:~/habridge $ ./starthabridge.sh
To halt the ha-bridge Docker container use the `stop` command:
```
You should now be running the bridge. Check for errors:
```
pi@raspberrypi:~/habridge $ tail -f habridge-log.txt
pi@raspberrypi:~ $ docker stop ha-bridge
```
If you like to automate the deployment just use the Ansible role on https://github.com/escalate/ansible-ha-bridge-docker.
## Run ha-bridge alongside web server already on port 80
These examples will help you proxy your current webserver requests to the ha-bridge running on a different port, such as 8080.
@@ -287,7 +321,7 @@ Provide IP Addresses of your HAL Systems that you want to utilize with the bridg
#### MQTT Client IDs and IP Addresses
Provide Client ID and IP Addresses and ports of your MQTT Brokers that you want to utilize with the bridge. Also, you can provide the username and password if you have secured your MQTT broker which is optional. When these Client ID and IP's are given, the bridge will be able to publish MQTT messages by the call it receives and send it to the target MQTT Broker you configure. The MQTT Messages Tab will become available to help you build messages.
#### Home Assistant Names and IP Addresses
Provide IP Addresses and ports of your Home Assistant that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Home Assistant and device/scene you configure.
<Provide IP Addresses and ports of your Home Assistant that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Home Assistant and device/scene you configure.
#### HomeWizard Gateways Names and IP Addresses
Provide IP Addresses of your HomeWizard Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target HomeWizard and device/scene you configure.
#### Domoticz Names and IP Addresses

19
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>5.2.0</version>
<version>5.2.2RC2</version>
<packaging>jar</packaging>
<name>HA Bridge</name>
@@ -63,7 +63,7 @@
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.7.1</version>
<version>2.7.2</version>
<exclusions>
<exclusion>
<artifactId>slf4j-simple</artifactId>
@@ -114,12 +114,13 @@
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.1.0</version>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.bwssytems</groupId>
@@ -127,9 +128,9 @@
<version>2.1.6</version>
</dependency>
<dependency>
<groupId>com.github.bwssytems</groupId>
<groupId>com.github.mob41</groupId>
<artifactId>broadlink-java-api</artifactId>
<version>1.0.0</version>
<version>master-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
@@ -156,6 +157,14 @@
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>

View File

@@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.util.BackupHandler;
import com.bwssystems.HABridge.util.JsonTransformer;
import com.bwssystems.HABridge.util.ParseRoute;
import com.google.gson.Gson;
public class BridgeSettings extends BackupHandler {
@@ -166,8 +167,9 @@ public class BridgeSettings extends BackupHandler {
theBridgeSettings.setNestpwd(System.getProperty("nest.pwd"));
}
ParseRoute aDefaultRoute = ParseRoute.getInstance();
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().trim().equals("") || theBridgeSettings.getUpnpConfigAddress().trim().equals("0.0.0.0")) {
addressString = checkIpAddress(null, true);
addressString = aDefaultRoute.getLocalIPAddress();
if(addressString != null) {
theBridgeSettings.setUpnpConfigAddress(addressString);
log.info("Adding " + addressString + " as our default upnp config address.");
@@ -177,8 +179,10 @@ public class BridgeSettings extends BackupHandler {
}
else {
addressString = checkIpAddress(theBridgeSettings.getUpnpConfigAddress(), false);
if(addressString == null)
log.warn("The upnp config address, " + theBridgeSettings.getUpnpConfigAddress() + ", does not match any known IP's on this host.");
if(addressString == null) {
addressString = aDefaultRoute.getLocalIPAddress();
log.warn("The upnp config address, " + theBridgeSettings.getUpnpConfigAddress() + ", does not match any known IP's on this host. Using default address: " + addressString);
}
}
if(theBridgeSettings.getUpnpResponsePort() == null)
@@ -215,8 +219,10 @@ public class BridgeSettings extends BackupHandler {
// Lifx is either configured or not, so it does not need an update.
if(serverPortOverride != null)
theBridgeSettings.setServerPort(serverPortOverride);
if(serverIpOverride != null)
if(serverIpOverride != null) {
theBridgeSettings.setWebaddress(serverIpOverride);
theBridgeSettings.setUpnpConfigAddress(serverIpOverride);
}
if(upnpStrictOverride != null)
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(upnpStrictOverride));
setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-");

View File

@@ -83,6 +83,9 @@ public class DeviceDescriptor{
@SerializedName("onFirstDim")
@Expose
private boolean onFirstDim;
@SerializedName("onWhenDimPresent")
@Expose
private boolean onWhenDimPresent;
public String getName() {
return name;
@@ -286,6 +289,14 @@ public class DeviceDescriptor{
this.onFirstDim = onFirstDim;
}
public boolean isOnWhenDimPresent() {
return onWhenDimPresent;
}
public void setOnWhenDimPresent(boolean onWhenDimPresent) {
this.onWhenDimPresent = onWhenDimPresent;
}
public boolean containsType(String aType) {
if(aType == null)
return false;

View File

@@ -52,11 +52,22 @@ public class BrightnessDecode {
boolean notDone = true;
String replaceValue = null;
String replaceTarget = null;
int percentBrightness = (int) Math.round(intensity / 255.0 * 100);
float decimalBrightness = (float) (intensity / 255.0);
int percentBrightness = 0;
float decimalBrightness = (float) 0.0;
Map<String, BigDecimal> variables = new HashMap<String, BigDecimal>();
String mathDescriptor = null;
if(intensity > 0) {
decimalBrightness = (float) (intensity / 255.0);
if(intensity > 0 && intensity < 5)
percentBrightness = 1;
else
percentBrightness = (int) Math.round(intensity / 255.0 * 100);
} else {
decimalBrightness = (float) 0.0;
percentBrightness = 0;
}
while(notDone) {
notDone = false;
if (request.contains(INTENSITY_BYTE)) {

View File

@@ -570,8 +570,8 @@ public class HueMulator {
if (body.contains("\"effect\"")) {
if (notFirstChange)
responseString = responseString + ",";
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/effect\":"
+ stateChanges.getEffect() + "}}";
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/effect\":\""
+ stateChanges.getEffect() + "\"}}";
if (deviceState != null)
deviceState.setEffect(stateChanges.getEffect());
notFirstChange = true;
@@ -590,8 +590,8 @@ public class HueMulator {
if (body.contains("\"alert\"")) {
if (notFirstChange)
responseString = responseString + ",";
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/alert\":"
+ stateChanges.getAlert() + "}}";
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/alert\":\""
+ stateChanges.getAlert() + "\"}}";
if (deviceState != null)
deviceState.setAlert(stateChanges.getAlert());
notFirstChange = true;
@@ -1186,13 +1186,22 @@ public class HueMulator {
isOnRequest = true;
}
if(!device.isOnFirstDim() && device.isOnWhenDimPresent() && isDimRequest) {
isOnRequest = true;
theStateChanges.setOn(true);
} else if(!device.isOnFirstDim() && !device.isOnWhenDimPresent() && isDimRequest) {
isOnRequest = false;
}
if(device.isOnFirstDim() && isDimRequest && !device.getDeviceState().isOn()) {
isOnRequest = true;
theStateChanges.setOn(true);
isDimRequest = false;
isColorRequest = false;
} else if(device.isOnFirstDim() && isDimRequest && device.getDeviceState().isOn()) {
if(device.getDeviceState().getBri() == theStateChanges.getBri()) {
isOnRequest = true;
theStateChanges.setOn(true);
isDimRequest = false;
isColorRequest = false;
} else {

View File

@@ -9,6 +9,7 @@ import net.whistlingfish.harmony.HarmonyClient;
import net.whistlingfish.harmony.config.Activity;
import net.whistlingfish.harmony.config.Device;
import net.whistlingfish.harmony.config.HarmonyConfig;
import com.bwssystems.HABridge.NamedIP;
public class HarmonyHandler {
private static final Logger log = LoggerFactory.getLogger(HarmonyHandler.class);
@@ -16,8 +17,9 @@ public class HarmonyHandler {
private Boolean noopCalls;
private Boolean devMode;
private DevModeResponse devResponse;
private NamedIP myNameAndIP;
public HarmonyHandler(HarmonyClient theClient, Boolean noopCallsSetting, DevModeResponse devResponseSetting) {
public HarmonyHandler(HarmonyClient theClient, Boolean noopCallsSetting, DevModeResponse devResponseSetting, NamedIP aNameAndIP) {
super();
noopCalls = noopCallsSetting;
devMode = Boolean.TRUE;
@@ -27,6 +29,7 @@ public class HarmonyHandler {
else
devResponse = devResponseSetting;
harmonyClient = theClient;
myNameAndIP = aNameAndIP;
}
public List<Activity> getActivities() {
@@ -34,7 +37,15 @@ public class HarmonyHandler {
if(devMode)
return devResponse.getActivities();
return harmonyClient.getConfig().getActivities();
List<Activity> listOfActivities = null;
try {
listOfActivities = harmonyClient.getConfig().getActivities();
} catch (RuntimeException e) {
handleExceptionError(e);
}
return listOfActivities;
}
public List<Device> getDevices() {
@@ -42,7 +53,14 @@ public class HarmonyHandler {
if(devMode)
return devResponse.getDevices();
return harmonyClient.getConfig().getDevices();
List<Device> listOfDevices = null;
try {
listOfDevices = harmonyClient.getConfig().getDevices();
} catch (RuntimeException e) {
handleExceptionError(e);
}
return listOfDevices;
}
public HarmonyConfig getConfig() {
@@ -50,7 +68,13 @@ public class HarmonyHandler {
if(devMode)
return devResponse.getConfig();
return harmonyClient.getConfig();
HarmonyConfig aConfig = null;
try {
aConfig = harmonyClient.getConfig();
} catch (RuntimeException e) {
handleExceptionError(e);
}
return aConfig;
}
public Activity getCurrentActivity() {
@@ -58,7 +82,13 @@ public class HarmonyHandler {
if(devMode)
return devResponse.getCurrentActivity();
return harmonyClient.getCurrentActivity();
Activity anActivity = null;
try {
anActivity = harmonyClient.getCurrentActivity();
} catch (RuntimeException e) {
handleExceptionError(e);
}
return anActivity;
}
public Boolean startActivity(RunActivity anActivity) {
@@ -83,7 +113,11 @@ public class HarmonyHandler {
} catch (IllegalArgumentException ei) {
log.error("Error in finding activity: " + anActivity.getName() + " for a hub: " + anActivity.getHub());
return false;
} catch (RuntimeException e1) {
handleExceptionError(e1);
}
} catch (RuntimeException e1) {
handleExceptionError(e1);
}
} else {
log.error("Error in finding activity: " + anActivity.getName() + " for a hub: " + anActivity.getHub());
@@ -114,7 +148,11 @@ public class HarmonyHandler {
} catch (IllegalArgumentException ei) {
log.error("Error in finding device: " + aDeviceButton.getDevice() +" and a button: " + aDeviceButton.getButton() + " for a hub: " + aDeviceButton.getHub());
return false;
} catch (RuntimeException e1) {
handleExceptionError(e1);
}
} catch (RuntimeException e1) {
handleExceptionError(e1);
}
} else {
log.error("Error in finding device: " + aDeviceButton.getDevice() +" and a button: " + aDeviceButton.getButton() + " for a hub: " + aDeviceButton.getHub());
@@ -124,6 +162,18 @@ public class HarmonyHandler {
return true;
}
void handleExceptionError(Exception e) {
if(e.getMessage().equalsIgnoreCase("Failed communicating with Harmony Hub")) {
log.warn("Issue in communcicating with Harmony Hub, retrying connect....");
try {
harmonyClient.disconnect();
} catch(Exception e1) {
log.warn("Hub disconnect failed, continuing....");
}
harmonyClient.connect(myNameAndIP.getIp());
}
}
public void shutdown() {
log.debug("Harmony api shutdown requested.");
if(devMode)

View File

@@ -110,7 +110,7 @@ public class HarmonyServer {
});
harmonyClient.connect(myNameAndIP.getIp());
}
myHarmony = new HarmonyHandler(harmonyClient, noopCalls, devResponse);
myHarmony = new HarmonyHandler(harmonyClient, noopCalls, devResponse, myNameAndIP);
}
public HarmonyHandler getMyHarmony() {

View File

@@ -22,7 +22,7 @@ public class UpnpListener {
private Logger log = LoggerFactory.getLogger(UpnpListener.class);
private MulticastSocket upnpMulticastSocket;
private int httpServerPort;
private String responseAddress;
private String upnpConfigIP;
private boolean strict;
private boolean traceupnp;
private boolean useUpnpIface;
@@ -73,16 +73,19 @@ public class UpnpListener {
super();
upnpMulticastSocket = null;
httpServerPort = Integer.valueOf(theSettings.getServerPort());
responseAddress = theSettings.getUpnpConfigAddress();
upnpConfigIP = theSettings.getUpnpConfigAddress();
strict = theSettings.isUpnpStrict();
traceupnp = theSettings.isTraceupnp();
useUpnpIface = theSettings.isUseupnpiface();
theUpnpSendDelay = theSettings.getUpnpsenddelay();
bridgeControl = theControl;
aHueConfig = HuePublicConfig.createConfig("temp", responseAddress, HueConstants.HUB_VERSION, theSettings.getHubmac());
aHueConfig = HuePublicConfig.createConfig("temp", upnpConfigIP, HueConstants.HUB_VERSION, theSettings.getHubmac());
bridgeId = aHueConfig.getBridgeid();
bridgeSNUUID = aHueConfig.getSNUUIDFromMac();
try {
if(useUpnpIface)
upnpMulticastSocket = new MulticastSocket(new InetSocketAddress(upnpConfigIP, Configuration.UPNP_DISCOVERY_PORT));
else
upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
} catch(IOException e){
log.error("Upnp Discovery Port is in use, or restricted by admin (try running with sudo or admin privs): " + Configuration.UPNP_DISCOVERY_PORT + " with message: " + e.getMessage());
@@ -118,7 +121,7 @@ public class UpnpListener {
log.info("Traceupnp: Interface: " + name + " valid usable IP address: " + addr);
IPsPerNic++;
}
else if(addr.getHostAddress().equals(responseAddress)) {
else if(addr.getHostAddress().equals(upnpConfigIP)) {
if(traceupnp)
log.info("Traceupnp: Interface: " + name + " matches upnp config address of IP address: " + addr);
IPsPerNic++;
@@ -157,7 +160,7 @@ public class UpnpListener {
upnpMulticastSocket.receive(packet);
if (isSSDPDiscovery(packet)) {
try {
sendUpnpResponse(packet.getAddress(), packet.getPort());
sendUpnpResponse(packet);
} catch (IOException e) {
log.warn("UpnpListener encountered an error sending upnp response packet. IP: " + packet.getAddress().getHostAddress() + " with message: " + e.getMessage());
log.debug("UpnpListener send upnp exception: ", e);
@@ -230,16 +233,21 @@ public class UpnpListener {
return false;
}
protected void sendUpnpResponse(InetAddress requester, int sourcePort) throws IOException {
protected void sendUpnpResponse(DatagramPacket aPacket) throws IOException {
SocketAddress requesterAddress = aPacket.getSocketAddress();
InetAddress requester = aPacket.getAddress();
int sourcePort = aPacket.getPort();
String discoveryResponse = null;
// refactored suggestion by https://github.com/pvint
String httpLocationAddress = getOutboundAddress(requesterAddress).getHostAddress();
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID);
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if(traceupnp) {
log.info("Traceupnp: send upnp discovery template 1 with response address: " + responseAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
log.info("Traceupnp: send upnp discovery template 1 with response address: " + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort + " with discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
@@ -250,9 +258,9 @@ public class UpnpListener {
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
if(traceupnp) {
log.info("Traceupnp: send upnp discovery template 2 with response address: " + responseAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort + " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
@@ -263,9 +271,9 @@ public class UpnpListener {
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID);
discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if(traceupnp) {
log.info("Traceupnp: send upnp discovery template 3 with response address: " + responseAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort + " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
@@ -282,7 +290,7 @@ public class UpnpListener {
protected void sendUpnpNotify(InetAddress aSocketAddress) {
String notifyData = null;
notifyData = String.format(notifyTemplate, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
notifyData = String.format(notifyTemplate, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
log.debug("sendUpnpNotify notifyTemplate is <<<" + notifyData + ">>>");
DatagramPacket notifyPacket = new DatagramPacket(notifyData.getBytes(), notifyData.length(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
try {
@@ -291,6 +299,20 @@ public class UpnpListener {
log.warn("UpnpListener encountered an error sending upnp notify packet. IP: " + notifyPacket.getAddress().getHostAddress() + " with message: " + e1.getMessage());
log.debug("UpnpListener send upnp notify exception: ", e1);
}
}
// added by https://github.com/pvint
// Ruthlessly stolen from https://stackoverflow.com/questions/22045165/java-datagrampacket-receive-how-to-determine-local-ip-interface
// Try to get a source IP that makes sense for the requestor to contact for use in the LOCATION header in replies
private InetAddress getOutboundAddress(SocketAddress remoteAddress) throws SocketException {
DatagramSocket sock = new DatagramSocket();
// connect is needed to bind the socket and retrieve the local address
// later (it would return 0.0.0.0 otherwise)
sock.connect(remoteAddress);
final InetAddress localAddress = sock.getLocalAddress();
sock.disconnect();
sock.close();
sock = null;
return localAddress;
}
}

View File

@@ -7,9 +7,14 @@ import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.api.hue.HueConstants;
import com.bwssystems.HABridge.api.hue.HuePublicConfig;
import com.bwssystems.HABridge.util.ParseRoute;
import static spark.Spark.get;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
/**
*
*/
@@ -75,12 +80,13 @@ public class UpnpSettingsResource {
String portNumber = Integer.toString(request.port());
String filledTemplate = null;
String bridgeIdMac = HuePublicConfig.createConfig("temp", theSettings.getUpnpConfigAddress(), HueConstants.HUB_VERSION, theSettings.getHubmac()).getSNUUIDFromMac();
filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress(), bridgeIdMac, bridgeIdMac);
String httpLocationAddr = getOutboundAddress(request.ip(), request.port()).getHostAddress();
String bridgeIdMac = HuePublicConfig.createConfig("temp", httpLocationAddr, HueConstants.HUB_VERSION, theSettings.getHubmac()).getSNUUIDFromMac();
filledTemplate = String.format(hueTemplate, httpLocationAddr, portNumber, httpLocationAddr, bridgeIdMac, bridgeIdMac);
if(theSettings.isTraceupnp())
log.info("Traceupnp: request of description.xml from: " + request.ip() + ":" + request.port() + " filled in with address: " + theSettings.getUpnpConfigAddress() + ":" + portNumber);
log.info("Traceupnp: request of description.xml from: " + request.ip() + ":" + request.port() + " filled in with address: " + httpLocationAddr + ":" + portNumber);
else
log.debug("request of description.xml from: " + request.ip() + ":" + request.port() + " filled in with address: " + theSettings.getUpnpConfigAddress() + ":" + portNumber);
log.debug("request of description.xml from: " + request.ip() + ":" + request.port() + " filled in with address: " + httpLocationAddr + ":" + portNumber);
// response.header("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
// response.header("Pragma", "no-cache");
// response.header("Expires", "Mon, 1 Aug 2011 09:00:00 GMT");
@@ -109,4 +115,28 @@ public class UpnpSettingsResource {
return "";
} );
}
// added by https://github.com/pvint
// Ruthlessly stolen from https://stackoverflow.com/questions/22045165/java-datagrampacket-receive-how-to-determine-local-ip-interface
// Try to get a source IP that makes sense for the requester to contact for use in the LOCATION header in replies
private InetAddress getOutboundAddress(String remoteAddress, int remotePort) {
InetAddress localAddress = null;
try {
DatagramSocket sock = new DatagramSocket();
// connect is needed to bind the socket and retrieve the local address
// later (it would return 0.0.0.0 otherwise)
sock.connect(new InetSocketAddress(remoteAddress, remotePort));
localAddress = sock.getLocalAddress();
sock.disconnect();
sock.close();
sock = null;
} catch(Exception e) {
ParseRoute theRoute = ParseRoute.getInstance();
try {
localAddress = InetAddress.getByName(theRoute.getLocalIPAddress());
} catch(Exception e1) {}
log.warn("Error <" + e.getMessage() + "> on determining interface to reply for <" + remoteAddress + ">. Using default route IP Address of " + localAddress.getHostAddress());
}
log.debug("getOutbountAddress returning IP Address of " + localAddress.getHostAddress());
return localAddress;
}
}

View File

@@ -0,0 +1,165 @@
package com.bwssystems.HABridge.util;
import java.net.*;
import java.io.*;
import java.util.*;
/**
* Find out the local IP address and default gateway
* @author Henry Zheng
* @url http://www.ireasoning.com
*/
public class ParseRoute
{
private String _gateway;
private String _ip;
private static ParseRoute _instance;
public static void main(String[] args)
{
try
{
ParseRoute pr = ParseRoute.getInstance();
System.out.println( "Gateway: " + pr.getGateway() );
System.out.println( "IP: " + pr.getLocalIPAddress() );
}
catch(Exception e)
{
System.out.println( e);
e.printStackTrace();
}
}
private ParseRoute ()
{
parse();
}
private static boolean isWindows ()
{
String os = System.getProperty ( "os.name" ).toUpperCase ();
return os.contains( "WINDOWS" ) ;
}
private static boolean isLinux ()
{
String os = System.getProperty ( "os.name" ).toUpperCase ();
return os.contains( "LINUX" ) ;
}
public String getLocalIPAddress()
{
return _ip;
}
public String getGateway()
{
return _gateway;
}
public static ParseRoute getInstance()
{
if(_instance == null)
{
_instance = new ParseRoute();
}
return _instance;
}
private void parse()
{
if(isWindows())
{
parseWindows();
}
else if(isLinux())
{
parseLinux();
}
}
private void parseWindows()
{
try
{
Process pro = Runtime.getRuntime().exec("cmd.exe /c route print");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(pro.getInputStream()));
String line;
while((line = bufferedReader.readLine())!=null)
{
line = line.trim();
String [] tokens = Tokenizer.parse(line, ' ', true , true);// line.split(" ");
if(tokens.length == 5 && tokens[0].equals("0.0.0.0"))
{
_gateway = tokens[2];
_ip = tokens[3];
return;
}
}
//pro.waitFor();
}
catch(IOException e)
{
System.err.println(e);
e.printStackTrace();
}
}
private void parseLinux()
{
BufferedReader reader = null;
try
{
reader = new BufferedReader(new FileReader("/proc/net/route"));
String line;
while((line = reader.readLine())!=null)
{
line = line.trim();
String [] tokens = Tokenizer.parse(line, '\t', true , true);// line.split(" ");
if(tokens.length > 1 && tokens[1].equals("00000000"))
{
String gateway = tokens[2]; //0102A8C0
if(gateway.length() == 8)
{
String[] s4 = new String[4];
s4[3] = String.valueOf(Integer.parseInt(gateway.substring(0, 2), 16));
s4[2] = String.valueOf(Integer.parseInt(gateway.substring(2, 4), 16));
s4[1] = String.valueOf(Integer.parseInt(gateway.substring(4, 6), 16));
s4[0] = String.valueOf(Integer.parseInt(gateway.substring(6, 8), 16));
_gateway = s4[0] + "." + s4[1] + "." + s4[2] + "." + s4[3];
}
String iface = tokens[0];
NetworkInterface nif = NetworkInterface.getByName(iface);
Enumeration<java.net.InetAddress> addrs = nif.getInetAddresses();
while(addrs.hasMoreElements())
{
Object obj = addrs.nextElement();
if(obj instanceof Inet4Address)
{
_ip = obj.toString();
if(_ip.startsWith("/")) _ip = _ip.substring(1);
reader.close();
return;
}
}
reader.close();
return;
}
}
}
catch(IOException e)
{
System.err.println(e);
e.printStackTrace();
}
if(reader != null) {
try {
reader.close();
} catch(Exception e) {
}
}
}
}

View File

@@ -0,0 +1,195 @@
package com.bwssystems.HABridge.util;
import java.io.*;
/**
* This class is an auto-resizable String array. It has similar methods to ArrayList
*
* @author Henry Zheng
* @url http://www.ireasoning.com
*/
public class StringArray implements Serializable
{
public static final long serialVersionUID = 42L;
public static final int DEFAULT_CAPACITY = 10;
protected String[] _strings = null;
protected int _upperBound = 0;
protected int _capacity = DEFAULT_CAPACITY;
protected int _initialSize = _capacity;
protected float _loadFactory = 1.5F;
public StringArray ()
{
this(DEFAULT_CAPACITY);
}
public StringArray( int size)
{
_capacity = size;
_initialSize = size;
_strings = new String[size];
}
public synchronized void ensureCapacity(int capacity)
{
if(_capacity < capacity)
{
_capacity = (_capacity * 3)/2 + 1;
if(_capacity < capacity)
{
_capacity = capacity;
}
String [] oldData = _strings;
_strings = new String[_capacity];
System.arraycopy(oldData, 0, _strings, 0, _upperBound);
}
}
public synchronized void add(String s)
{
if(_upperBound == _capacity )
{
resize((int) (_capacity * _loadFactory));
}
_strings[_upperBound++] = s;
}
public synchronized void add(StringArray sa)
{
for (int i = 0; i < sa.size() ; i++)
{
add(sa.get(i));
}
}
public synchronized String get(int index)
{
return _strings[index];
}
public synchronized void set(int index, String newVal)
{
_strings[index] = newVal;
}
/** Adds all elements in passed string array */
public synchronized void add(String [] strs)
{
for (int i = 0; i < strs.length ; i++)
{
add(strs[i]);
}
}
/** Resets this object. */
public synchronized void clear()
{
_capacity = _initialSize;
_strings = new String[_capacity];
_upperBound = 0;
}
public synchronized String remove(int index)
{
if(index >= _upperBound )
{
throw new IndexOutOfBoundsException();
}
String s = _strings[index];
for (int i = index; i < _upperBound - 1 ; i++)
{
_strings[i] = _strings[i + 1];
}
_upperBound --;
return s;
}
/**
* Removes the first occurance of passed str
* @return the string removed, or null if not found
*/
public synchronized String remove(String str)
{
for (int i = 0; i < _upperBound ; i++)
{
if(_strings[i].equals(str))
{
return remove(i);
}
}
return null;
}
public synchronized int size()
{
return _upperBound;
}
public synchronized boolean isEmpty()
{
return _upperBound == 0;
}
public synchronized String[] toArray()
{
String [] ret = new String[_upperBound];
for (int i = 0; i < _upperBound ; i++)
{
ret[i] = _strings[i];
}
return ret;
}
protected synchronized void resize(int newCapacity)
{
String [] as = new String[newCapacity];
for (int i = 0; i < _strings.length ; i++)
{
as[i] = _strings[i];
}
_strings = as;
_capacity = newCapacity;
}
public String toString()
{
StringBuffer buf = new StringBuffer();
for (int i = 0; i < _upperBound ; i++)
{
buf.append(_strings[i] + "\n");
}
return buf.toString();
}
public static void main(String[] args)
{
StringArray as = new StringArray();
String [] ss = null;
ss = as.toArray();
// System.out.println( "ss len="+ss.length);
// System.out.println( "ss = " + ss);
for (int i = 0; i < 10 ; i++)
{
as.add("" + i);
}
// System.out.println( "size = " + as.size());
ss = as.toArray();
for (int i = 0; i < ss.length ; i++)
{
// System.out.println( ss[i]);
}
// System.out.println( "remove 5th element.");
as.remove(5);
// System.out.println( "size = " + as.size());
ss = as.toArray();
for (int i = 0; i < ss.length ; i++)
{
// System.out.println( ss[i]);
}
}
}//end of class StringArray

View File

@@ -0,0 +1,175 @@
package com.bwssystems.HABridge.util;
/**
* This class perform similar functionality to StringTokenizer class but faster.
* The difference is StringTokenizer doesn't count empty token, but this class does.
*
* @author Henry Zheng
* @url http://www.ireasoning.com
*/
public class Tokenizer
{
private Tokenizer()
{
}
/**
* It's different from the other parse method in that it checks left and right string first, which take higer
* priority than the delimiter. For example, if left and right is ", for string a:b:1"c:d"2:3 ,
* it returns { a, b, 1"c:d"2, 3 }
* @param left the openning tag of higher priority token
* @param right the closing tag of higher priority token
* @trimEachToken if true, each token will be trim by calling String.trim()
*/
public static String[] parse(String text, char delimiter, boolean trimEachToken, String left, String right)
{
if(text == null) return null;
StringArray tokens = new StringArray();
int pos1 = -1;
int pos2 = -1;
int firstPos = -1;
while(true)
{
pos2 = text.indexOf(delimiter, firstPos + 1);
if(pos2 < 0 )
{
String str = text.substring(pos1 + 1);
if(trimEachToken )
{
str = str.trim();
}
tokens.add(str);
break;
}
if(pos2 == pos1 + 1)
{
tokens.add("");
}
else
{
int tagPos1 = text.indexOf(left, firstPos + 1);
if(tagPos1 > 0 && tagPos1 < pos2 )
{
int tagPos2 = text.indexOf(right, tagPos1 + 1);
if(tagPos2 > 0)
{
firstPos = tagPos2;
continue;
}
}
String str = text.substring(pos1 + 1, pos2);
if(trimEachToken )
{
str = str.trim();
}
tokens.add(str);
}
pos1 = pos2;
firstPos = pos1;
}
String[] ret = tokens.toArray();
return ret;
}
/**
* @trimEachToken if true, each token will be trim by calling String.trim()
*/
public static String[] parse(String text, char delimiter, boolean trimEachToken)
{
return parse(text, delimiter, trimEachToken, false);
}
/**
* @trimEachToken if true, each token will be trim by calling String.trim()
*/
public static String[] parse(String text, char delimiter, boolean trimEachToken, boolean ignoreEmptyToken)
{
if(text == null) return null;
StringArray tokens = new StringArray();
int pos1 = -1;
int pos2 = -1;
while(true)
{
pos2 = text.indexOf(delimiter, pos1 + 1);
if(pos2 < 0 )
{
String str = text.substring(pos1 + 1);
if(trimEachToken )
{
str = str.trim();
}
if(ignoreEmptyToken)
{
if(str.length() != 0) tokens.add(str);
}
else
{
tokens.add(str);
}
break;
}
if(pos2 == pos1 + 1)
{
if(!ignoreEmptyToken) { tokens.add(""); }
}
else
{
String str = text.substring(pos1 + 1, pos2);
if(trimEachToken )
{
str = str.trim();
}
if(ignoreEmptyToken)
{
if(str.length() != 0) tokens.add(str);
}
else
{
tokens.add(str);
}
}
pos1 = pos2;
}
String[] ret = tokens.toArray();
return ret;
}
/**
* Does not trim each token.
* @see #parse(String, char, boolean)
*/
public static String[] parse(String text, char delimiter)
{
return parse(text, delimiter, false);
}
// public static void main(String[] args)
// {
// String str = "1,\"2,\"ab\",ttt1,\"3,,a\"222\",4";
// if(args.length > 0)
// {
// str = args[0];
// }
// String [] tokens = Tokenizer.parse(str, ',');
//
//// System.out.println( "Text = (" + str + ")");
// // System.out.println( "------------------------------------------");
// for (int i = 0; i < tokens.length ; i++)
// {
// // System.out.println( "(" + tokens[i] + ")");
// }
// // System.out.println( "------------------------------------------");
// String [] tokens = Tokenizer.parse(str, ',', new String[]{"("}, new String[]{")"});
//
// // System.out.println( "Text = [" + str + "]");
// // System.out.println( "------------------------------------------");
// for (int i = 0; i < tokens.length ; i++)
// {
// // System.out.println( "[" + tokens[i] + "]");
// }
// // System.out.println( "------------------------------------------");
// }
}//end of class Tokenizer

View File

@@ -1684,11 +1684,11 @@ app.controller ('SystemController', function ($scope, $location, bridgeService,
}
}
};
$scope.addDomoticztoSettings = function (newdomoticzname, newdomoticzip, newdomoticzport, newdomoticzusername, newdomoticzpassword) {
$scope.addDomoticztoSettings = function (newdomoticzname, newdomoticzip, newdomoticzport, newdomoticzusername, newdomoticzpassword, newdomoticzsecure) {
if($scope.bridge.settings.domoticzaddress === undefined || $scope.bridge.settings.domoticzaddress === null) {
$scope.bridge.settings.domoticzaddress = { devices: [] };
}
var newdomoticz = {name: newdomoticzname, ip: newdomoticzip, port: newdomoticzport, username: newdomoticzusername, password: newdomoticzpassword }
var newdomoticz = {name: newdomoticzname, ip: newdomoticzip, port: newdomoticzport, username: newdomoticzusername, password: newdomoticzpassword, secure: newdomoticzsecure }
$scope.bridge.settings.domoticzaddress.devices.push(newdomoticz);
$scope.newdomoticzname = null;
$scope.newdomoticzip = null;
@@ -3984,7 +3984,7 @@ app.controller('FhemController', function ($scope, $location, bridgeService, ngD
$scope.clearDevice();
for(var i = 0; i < $scope.bulk.devices.length; i++) {
for(var x = 0; x < bridgeService.state.fhemdevices.length; x++) {
if(bridgeService.state.fhemdevices[x].devicename === $scope.bulk.devices[i]) {
if(bridgeService.state.fhemdevices[x].item.Name === $scope.bulk.devices[i]) {
$scope.buildDeviceUrls(bridgeService.state.fhemdevices[x],dim_control,true);
devicesList[i] = {
name: $scope.device.name,
@@ -4047,7 +4047,7 @@ app.controller('FhemController', function ($scope, $location, bridgeService, ngD
$scope.selectAll = true;
for(var x = 0; x < bridgeService.state.fhemdevices.length; x++) {
if($scope.bulk.devices.indexOf(bridgeService.state.fhemdevices[x]) < 0)
$scope.bulk.devices.push(bridgeService.state.fhemdevices[x].devicename);
$scope.bulk.devices.push(bridgeService.state.fhemdevices[x].item.Name);
}
}
};

View File

@@ -94,6 +94,12 @@
ng-model="device.offState" ng-true-value=true
ng-false-value=false> {{device.offState}}</td>
</tr>
<tr>
<td><label>On when Dim is present (Always uses on with a dim request otherwise it will ignore a given on was well. This is overidden by onFirstDim.)</label></td>
<td><input type="checkbox"
ng-model="device.onWhenDimPresent" ng-true-value=true
ng-false-value=false> {{device.onWhenDimPresent}}</td>
</tr>
<tr>
<td><label>On with First Dim (If the device is not on in the ha-bridge state, it will send on instead of the dim.)</label></td>
<td><input type="checkbox"

View File

@@ -70,9 +70,9 @@
<tr ng-repeat="fhemdevice in bridge.fhemdevices">
<td>{{$index+1}}</td>
<td><input type="checkbox" name="bulk.devices[]"
value="{{fhemdevice.item.name}}"
ng-checked="bulk.devices.indexOf(fhemdevice.item.name) > -1"
ng-click="toggleSelection(fhemdevice.item.name)">
value="{{fhemdevice.item.Name}}"
ng-checked="bulk.devices.indexOf(fhemdevice.item.Name) > -1"
ng-click="toggleSelection(fhemdevice.item.Name)">
{{fhemdevice.item.Name}}</td>
<td>{{fhemdevice.name}}</td>
<td>{{fhemdevice.item.PossibleSets}}</td>

View File

@@ -512,6 +512,7 @@
<th>Port</th>
<th>Username (opt)</th>
<th>Password (opt)</th>
<th>Use SSL</th>
<th>Manage</th>
</tr>
</thead>
@@ -531,6 +532,9 @@
<td><input id="bridge-settings-next-domoticz-password"
class="form-control" type="password" ng-model="domoticz.password"
placeholder="Domoticz password (opt)"></td>
<td><input type="checkbox"
ng-model="domoticz.secure" ng-true-value=true
ng-false-value=false></td>
<td><button class="btn btn-danger" type="submit"
ng-click="removeDomoticztoSettings(domoticz.name, domoticz.ip)">Del</button></td>
</tr>
@@ -550,8 +554,11 @@
<td><input id="bridge-settings-new-domoticz-password"
class="form-control" type="password" ng-model="newdomoticzpassword"
placeholder="Domoticz password (opt)"></td>
<td><input type="checkbox"
ng-model="newdomoticzsecure" ng-true-value=true
ng-false-value=false></td>
<td><button class="btn btn-success" type="submit"
ng-click="addDomoticztoSettings(newdomoticzname, newdomoticzip, newdomoticzport, newdomoticzusername, newdomoticzpassword)">Add</button></td>
ng-click="addDomoticztoSettings(newdomoticzname, newdomoticzip, newdomoticzport, newdomoticzusername, newdomoticzpassword, newdomoticzsecure)">Add</button></td>
</tr>
</table></td>
</tr>