mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-18 08:13:23 +00:00
Merge pull request #189 from bwssytems/postv3.1fixes
Fixes #129 Fixes #161 Fixes #166 Fixes #168 Fixes #172 Fixes #173 Fixes #174 Fixes #181
This commit is contained in:
42
README.md
42
README.md
@@ -21,7 +21,7 @@ Then locate the jar and start the server with:
|
|||||||
ATTENTION: This requires JDK 1.8 to run
|
ATTENTION: This requires JDK 1.8 to run
|
||||||
|
|
||||||
```
|
```
|
||||||
java -jar ha-bridge-3.1.0.jar
|
java -jar ha-bridge-3.2.0.jar
|
||||||
```
|
```
|
||||||
### Automation on Linux systems
|
### 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
|
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
|
||||||
@@ -38,7 +38,7 @@ After=network.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/amazon-echo/data/habridge.config /home/pi/amazon-echo/ha-bridge-3.1.0.jar
|
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/amazon-echo/data/habridge.config /home/pi/amazon-echo/ha-bridge-3.2.0.jar
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
@@ -46,11 +46,11 @@ WantedBy=multi-user.target
|
|||||||
|
|
||||||
Basic script setup to run the bridge on a pi.
|
Basic script setup to run the bridge on a pi.
|
||||||
|
|
||||||
Create the directory and make sure that ha-bridge-3.1.0.jar is in your /home/pi/habridge directory.
|
Create the directory and make sure that ha-bridge-3.2.0.jar is in your /home/pi/habridge directory.
|
||||||
```
|
```
|
||||||
pi@raspberrypi:~ $ mkdir habridge
|
pi@raspberrypi:~ $ mkdir habridge
|
||||||
pi@raspberrypi:~ $ cd habridge
|
pi@raspberrypi:~ $ cd habridge
|
||||||
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v3.1.0/ha-bridge-3.1.0.jar
|
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v3.2.0/ha-bridge-3.2.0.jar
|
||||||
```
|
```
|
||||||
Edit the shell script for starting:
|
Edit the shell script for starting:
|
||||||
```
|
```
|
||||||
@@ -60,7 +60,7 @@ Then cut and past this, modify any locations that are not correct
|
|||||||
```
|
```
|
||||||
cd /home/pi/habridge
|
cd /home/pi/habridge
|
||||||
rm /home/pi/habridge/habridge-log.txt
|
rm /home/pi/habridge/habridge-log.txt
|
||||||
nohup java -jar /home/pi/habridge/ha-bridge-3.1.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
|
nohup java -jar /home/pi/habridge/ha-bridge-3.2.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
|
||||||
chmod 777 /home/pi/habridge/habridge-log.txt
|
chmod 777 /home/pi/habridge/habridge-log.txt
|
||||||
```
|
```
|
||||||
Exit and save the file with ctrl-X and follow the prompts and then execute on the command line:
|
Exit and save the file with ctrl-X and follow the prompts and then execute on the command line:
|
||||||
@@ -83,14 +83,14 @@ The default location for the configuration file to contain the settings for the
|
|||||||
java -jar -Dconfig.file=/home/me/data/myhabridge.config ha-bridge-W.X.Y.jar
|
java -jar -Dconfig.file=/home/me/data/myhabridge.config ha-bridge-W.X.Y.jar
|
||||||
```
|
```
|
||||||
### -Dserver.port=`<port number>`
|
### -Dserver.port=`<port number>`
|
||||||
The default port number for the bridge is 8080. To override what the default or what is in the configuration file for this parameter, specify -Dserver.port=`<port number>` explicitly. This is especially helpful if you are running the ha-bridge for the first time and have another application on port 8080. The command line example:
|
The default port number for the bridge is 80. To override what the default or what is in the configuration file for this parameter, specify -Dserver.port=`<port number>` explicitly. This is especially helpful if you are running the ha-bridge for the first time and have another application on port 80. The command line example:
|
||||||
```
|
```
|
||||||
java -jar -Dserver.port=80 ha-bridge-W.X.Y.jar
|
java -jar -Dserver.port=80 ha-bridge-W.X.Y.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
## HA Bridge Usage and Configuration
|
## HA Bridge Usage and Configuration
|
||||||
This section will cover the basics of configuration and where this configuration can be done. This requires that you have started your bridge process and then have pointed your
|
This section will cover the basics of configuration and where this configuration can be done. This requires that you have started your bridge process and then have pointed your
|
||||||
favorite web interface by going to the http://<my ip address>:<port> or http://localhost:<port> with port you have assigned. The default quick link is http://localhost:8080 for yoru reference.
|
favorite web interface by going to the http://<my ip address>:<port> or http://localhost:<port> with port you have assigned. The default quick link is http://localhost for yoru reference.
|
||||||
### The Bridge Devices Tab
|
### The Bridge Devices Tab
|
||||||
This screen allows you to see your devices you have configured for the ha-bridge to present to a controller, such as an Amazon Echo/Dot. It gives you a count of devices as there have been reports that the Echo only supports a limited number, but has been growing as of late, YMMV. You can test each device from this page as this calls the ha-bridge just as a controller would, i.e. the Echo. This is useful to make sure your configuration for each device is correct and for trouble shooting. You can also manages your devices as well by editing and making a new device copy as well as deleting it.
|
This screen allows you to see your devices you have configured for the ha-bridge to present to a controller, such as an Amazon Echo/Dot. It gives you a count of devices as there have been reports that the Echo only supports a limited number, but has been growing as of late, YMMV. You can test each device from this page as this calls the ha-bridge just as a controller would, i.e. the Echo. This is useful to make sure your configuration for each device is correct and for trouble shooting. You can also manages your devices as well by editing and making a new device copy as well as deleting it.
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ The default location for the db to contain the devices as they are added is "dat
|
|||||||
#### UPNP IP Address
|
#### UPNP IP Address
|
||||||
The server defaults to the first available address on the host if this is not given. This default may NOT be the correct IP that is your public IP for your host on the network. It is best to set this parameter to not have discovery issues. Replace this value with the server ipv4 address you would like to use as the address that any upnp device will call after discovery.
|
The server defaults to the first available address on the host if this is not given. This default may NOT be the correct IP that is your public IP for your host on the network. It is best to set this parameter to not have discovery issues. Replace this value with the server ipv4 address you would like to use as the address that any upnp device will call after discovery.
|
||||||
#### Web Server Port
|
#### Web Server Port
|
||||||
The server defaults to running on port 8080. To override what the default is, specify a different number. ATTENTION: If you want to use any of the apps made for the Hue to control this bridge, you should set this port to 80.
|
The server defaults to running on port 80. To override what the default is, specify a different number. ATTENTION: If you want to use any of the apps made for the Hue to control this bridge, you should keep this port set to 80.
|
||||||
#### UPNP Response Port
|
#### UPNP Response Port
|
||||||
The upnp response port that will be used. The default is 50000.
|
The upnp response port that will be used. The default is 50000.
|
||||||
#### Vera Names and IP Addresses
|
#### Vera Names and IP Addresses
|
||||||
@@ -282,7 +282,7 @@ These calls can be accomplished with a REST tool using the following URLs and HT
|
|||||||
### Add a device
|
### Add a device
|
||||||
Add a new device to the HA Bridge configuration. There is a basic examples and then three alternate examples for the add. Please note that dimming is supported as well as custom value based on the dimming number given from the echo. This is under the Dimming and Value example.
|
Add a new device to the HA Bridge configuration. There is a basic examples and then three alternate examples for the add. Please note that dimming is supported as well as custom value based on the dimming number given from the echo. This is under the Dimming and Value example.
|
||||||
```
|
```
|
||||||
POST http://host:8080/api/devices
|
POST http://host/api/devices
|
||||||
```
|
```
|
||||||
#### Body Arguments
|
#### Body Arguments
|
||||||
Name | Type | Description | Required
|
Name | Type | Description | Required
|
||||||
@@ -938,16 +938,16 @@ If this criteria is met, the following response is provided to the calling appli
|
|||||||
HTTP/1.1 200 OK\r\n
|
HTTP/1.1 200 OK\r\n
|
||||||
CACHE-CONTROL: max-age=86400\r\n
|
CACHE-CONTROL: max-age=86400\r\n
|
||||||
EXT:\r\n
|
EXT:\r\n
|
||||||
LOCATION: http://192.168.1.1:8080/description.xml\r\n
|
LOCATION: http://192.168.1.1:80/description.xml\r\n
|
||||||
SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1\r\n
|
SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.10.0\r\n
|
||||||
ST: urn:schemas-upnp-org:device:basic:1\r\n
|
ST: urn:schemas-upnp-org:device:basic:1\r\n
|
||||||
"USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n
|
"USN: uuid:2f402f80-da50-11e1-9b23-001788102201::urn:schemas-upnp-org:device:basic:1\r\n\r\n
|
||||||
```
|
```
|
||||||
### UPNP description service
|
### UPNP description service
|
||||||
The bridge provides the description service which is used by the calling app to interogate access details after it has decided the upnp multicast response is the correct device.
|
The bridge provides the description service which is used by the calling app to interogate access details after it has decided the upnp multicast response is the correct device.
|
||||||
#### Get Description
|
#### Get Description
|
||||||
```
|
```
|
||||||
GET http://host:8080/description.xml
|
GET http://host:80/description.xml
|
||||||
```
|
```
|
||||||
#### Response
|
#### Response
|
||||||
```
|
```
|
||||||
@@ -957,18 +957,18 @@ GET http://host:8080/description.xml
|
|||||||
<major>1</major>\n
|
<major>1</major>\n
|
||||||
<minor>0</minor>\n
|
<minor>0</minor>\n
|
||||||
</specVersion>\n
|
</specVersion>\n
|
||||||
<URLBase>http://192.168.1.1:8080/</URLBase>\n
|
<URLBase>http://192.168.1.1:80/</URLBase>\n
|
||||||
<device>\n
|
<device>\n
|
||||||
<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\n
|
<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\n
|
||||||
<friendlyName>HA-Bridge (192.168.1.1)</friendlyName>\n
|
<friendlyName>Philips hue (192.168.1.1)</friendlyName>\n
|
||||||
<manufacturer>Royal Philips Electronics</manufacturer>\n
|
<manufacturer>Royal Philips Electronics</manufacturer>\n
|
||||||
<manufacturerURL>http://www.bwssystems.com</manufacturerURL>\n
|
<manufacturerURL>http://www.philips.com</manufacturerURL>\n
|
||||||
<modelDescription>Hue Emulator for HA bridge</modelDescription>\n
|
<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>\n"
|
||||||
<modelName>Philips hue bridge 2012</modelName>\n
|
<modelName>Philips hue bridge 2015</modelName>\n
|
||||||
<modelNumber>929000226503</modelNumber>\n
|
<modelNumber>BSB002</modelNumber>\n
|
||||||
<modelURL>http://www.bwssystems.com/apps.html</modelURL>\n
|
<modelURL>http://www.meethue.com</modelURL>\n
|
||||||
<serialNumber>0017880ae670</serialNumber>\n
|
<serialNumber>0017880ae670</serialNumber>\n
|
||||||
<UDN>uuid:88f6698f-2c83-4393-bd03-cd54a9f8595</UDN>\n
|
<UDN>uuid:2f402f80-da50-11e1-9b23-001788102201</UDN>\n
|
||||||
<serviceList>\n
|
<serviceList>\n
|
||||||
<service>\n
|
<service>\n
|
||||||
<serviceType>(null)</serviceType>\n
|
<serviceType>(null)</serviceType>\n
|
||||||
|
|||||||
4
pom.xml
4
pom.xml
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>com.bwssystems.HABridge</groupId>
|
<groupId>com.bwssystems.HABridge</groupId>
|
||||||
<artifactId>ha-bridge</artifactId>
|
<artifactId>ha-bridge</artifactId>
|
||||||
<version>3.1.0</version>
|
<version>3.2.0</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>HA Bridge</name>
|
<name>HA Bridge</name>
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.bwssytems</groupId>
|
<groupId>com.github.bwssytems</groupId>
|
||||||
<artifactId>nest-controller</artifactId>
|
<artifactId>nest-controller</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.9</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ public class BridgeSettings extends BackupHandler {
|
|||||||
return theBridgeSettings;
|
return theBridgeSettings;
|
||||||
}
|
}
|
||||||
public void buildSettings() {
|
public void buildSettings() {
|
||||||
InetAddress address = null;
|
|
||||||
String addressString = null;
|
String addressString = null;
|
||||||
String theVeraAddress = null;
|
String theVeraAddress = null;
|
||||||
String theHarmonyAddress = null;
|
String theHarmonyAddress = null;
|
||||||
@@ -109,34 +108,18 @@ public class BridgeSettings extends BackupHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().equals("")) {
|
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().equals("")) {
|
||||||
try {
|
addressString = checkIpAddress(null, true);
|
||||||
log.info("Getting an IP address for this host....");
|
if(addressString != null) {
|
||||||
Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();
|
theBridgeSettings.setUpnpConfigAddress(addressString);
|
||||||
|
log.info("Adding " + addressString + " as our default upnp config address.");
|
||||||
while (ifs.hasMoreElements() && addressString == null) {
|
}
|
||||||
NetworkInterface xface = ifs.nextElement();
|
else
|
||||||
Enumeration<InetAddress> addrs = xface.getInetAddresses();
|
log.error("Cannot get ip address of this host.");
|
||||||
String name = xface.getName();
|
}
|
||||||
int IPsPerNic = 0;
|
else {
|
||||||
|
addressString = checkIpAddress(theBridgeSettings.getUpnpConfigAddress(), false);
|
||||||
while (addrs.hasMoreElements() && IPsPerNic == 0) {
|
if(addressString == null)
|
||||||
address = addrs.nextElement();
|
log.warn("The upnp config address, " + theBridgeSettings.getUpnpConfigAddress() + ", does not match any known IP's on this host.");
|
||||||
if (InetAddressUtils.isIPv4Address(address.getHostAddress())) {
|
|
||||||
log.debug(name + " ... has IPV4 addr " + address);
|
|
||||||
if(!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE)|| !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS)) {
|
|
||||||
IPsPerNic++;
|
|
||||||
addressString = address.getHostAddress();
|
|
||||||
log.info("Adding " + addressString + " from interface " + name + " as our default upnp config address.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SocketException e) {
|
|
||||||
log.error("Cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
theBridgeSettings.setUpnpConfigAddress(addressString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(theBridgeSettings.getUpnpResponsePort() == null)
|
if(theBridgeSettings.getUpnpResponsePort() == null)
|
||||||
@@ -258,4 +241,39 @@ public class BridgeSettings extends BackupHandler {
|
|||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String checkIpAddress(String ipAddress, boolean checkForLocalhost) {
|
||||||
|
Enumeration<NetworkInterface> ifs = null;
|
||||||
|
try {
|
||||||
|
ifs = NetworkInterface.getNetworkInterfaces();
|
||||||
|
} catch(SocketException e) {
|
||||||
|
log.error("checkIpAddress cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String addressString = null;
|
||||||
|
InetAddress address = null;
|
||||||
|
while (ifs.hasMoreElements() && addressString == null) {
|
||||||
|
NetworkInterface xface = ifs.nextElement();
|
||||||
|
Enumeration<InetAddress> addrs = xface.getInetAddresses();
|
||||||
|
String name = xface.getName();
|
||||||
|
int IPsPerNic = 0;
|
||||||
|
|
||||||
|
while (addrs.hasMoreElements() && IPsPerNic == 0) {
|
||||||
|
address = addrs.nextElement();
|
||||||
|
if (InetAddressUtils.isIPv4Address(address.getHostAddress())) {
|
||||||
|
log.debug(name + " ... has IPV4 addr " + address);
|
||||||
|
if(checkForLocalhost && (!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE) || !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS))) {
|
||||||
|
IPsPerNic++;
|
||||||
|
addressString = address.getHostAddress();
|
||||||
|
log.debug("checkIpAddress found " + addressString + " from interface " + name);
|
||||||
|
}
|
||||||
|
else if(ipAddress != null && ipAddress.equalsIgnoreCase(address.getHostAddress())){
|
||||||
|
addressString = ipAddress;
|
||||||
|
IPsPerNic++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addressString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ public class Configuration {
|
|||||||
public final static String DEFAULT_ADDRESS = "1.1.1.1";
|
public final static String DEFAULT_ADDRESS = "1.1.1.1";
|
||||||
public final static String LOOP_BACK_ADDRESS = "127.0.0.1";
|
public final static String LOOP_BACK_ADDRESS = "127.0.0.1";
|
||||||
public final static String LOOP_BACK_INTERFACE = "lo";
|
public final static String LOOP_BACK_INTERFACE = "lo";
|
||||||
public final static String DEFAULT_WEB_PORT = "8080";
|
public final static String DEFAULT_WEB_PORT = "80";
|
||||||
public final static String DEFAULT_BUTTON_SLEEP = "100";
|
public final static String DEFAULT_BUTTON_SLEEP = "100";
|
||||||
public static final int UPNP_DISCOVERY_PORT = 1900;
|
public static final int UPNP_DISCOVERY_PORT = 1900;
|
||||||
public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250";
|
public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250";
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import com.bwssystems.NestBridge.NestHome;
|
|||||||
import com.bwssystems.hal.HalHome;
|
import com.bwssystems.hal.HalHome;
|
||||||
import com.bwssystems.harmony.HarmonyHome;
|
import com.bwssystems.harmony.HarmonyHome;
|
||||||
import com.bwssystems.hue.HueHome;
|
import com.bwssystems.hue.HueHome;
|
||||||
|
import com.bwssystems.util.UDPDatagramSender;
|
||||||
|
|
||||||
public class HABridge {
|
public class HABridge {
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ public class HABridge {
|
|||||||
HueHome hueHome;
|
HueHome hueHome;
|
||||||
HalHome halHome;
|
HalHome halHome;
|
||||||
HueMulator theHueMulator;
|
HueMulator theHueMulator;
|
||||||
|
UDPDatagramSender udpSender;
|
||||||
UpnpSettingsResource theSettingResponder;
|
UpnpSettingsResource theSettingResponder;
|
||||||
UpnpListener theUpnpListener;
|
UpnpListener theUpnpListener;
|
||||||
SystemControl theSystem;
|
SystemControl theSystem;
|
||||||
@@ -72,29 +74,37 @@ public class HABridge {
|
|||||||
halHome = new HalHome(bridgeSettings.getBridgeSettingsDescriptor());
|
halHome = new HalHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||||
// setup the class to handle the resource setup rest api
|
// setup the class to handle the resource setup rest api
|
||||||
theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), harmonyHome, nestHome, hueHome, halHome);
|
theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), harmonyHome, nestHome, hueHome, halHome);
|
||||||
// setup the class to handle the hue emulator rest api
|
|
||||||
theHueMulator = new HueMulator(bridgeSettings.getBridgeSettingsDescriptor(), theResources.getDeviceRepository(), harmonyHome, nestHome, hueHome);
|
|
||||||
theHueMulator.setupServer();
|
|
||||||
// setup the class to handle the upnp response rest api
|
// setup the class to handle the upnp response rest api
|
||||||
theSettingResponder = new UpnpSettingsResource(bridgeSettings.getBridgeSettingsDescriptor());
|
theSettingResponder = new UpnpSettingsResource(bridgeSettings.getBridgeSettingsDescriptor());
|
||||||
theSettingResponder.setupServer();
|
theSettingResponder.setupServer();
|
||||||
// wait for the sparkjava initialization of the rest api classes to be complete
|
// setup the UDP Datagram socket to be used by the HueMulator and the upnpListener
|
||||||
awaitInitialization();
|
udpSender = UDPDatagramSender.createUDPDatagramSender(bridgeSettings.getBridgeSettingsDescriptor().getUpnpResponsePort());
|
||||||
|
if(udpSender == null) {
|
||||||
// start the upnp ssdp discovery listener
|
|
||||||
theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl());
|
|
||||||
if(theUpnpListener.startListening())
|
|
||||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") reinitialization requessted....");
|
|
||||||
else
|
|
||||||
bridgeSettings.getBridgeControl().setStop(true);
|
bridgeSettings.getBridgeControl().setStop(true);
|
||||||
if(bridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged())
|
}
|
||||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
else {
|
||||||
|
// setup the class to handle the hue emulator rest api
|
||||||
|
theHueMulator = new HueMulator(bridgeSettings.getBridgeSettingsDescriptor(), theResources.getDeviceRepository(), harmonyHome, nestHome, hueHome, udpSender);
|
||||||
|
theHueMulator.setupServer();
|
||||||
|
// wait for the sparkjava initialization of the rest api classes to be complete
|
||||||
|
awaitInitialization();
|
||||||
|
|
||||||
|
// start the upnp ssdp discovery listener
|
||||||
|
theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl(), udpSender);
|
||||||
|
if(theUpnpListener.startListening())
|
||||||
|
log.info("HA Bridge (v" + theVersion.getVersion() + ") reinitialization requessted....");
|
||||||
|
else
|
||||||
|
bridgeSettings.getBridgeControl().setStop(true);
|
||||||
|
if(bridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged())
|
||||||
|
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||||
|
}
|
||||||
bridgeSettings.getBridgeControl().setReinit(false);
|
bridgeSettings.getBridgeControl().setReinit(false);
|
||||||
stop();
|
stop();
|
||||||
nestHome.closeTheNest();
|
nestHome.closeTheNest();
|
||||||
nestHome = null;
|
nestHome = null;
|
||||||
harmonyHome.shutdownHarmonyHubs();
|
harmonyHome.shutdownHarmonyHubs();
|
||||||
harmonyHome = null;
|
harmonyHome = null;
|
||||||
|
udpSender.closeResponseSocket();
|
||||||
}
|
}
|
||||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting....");
|
log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting....");
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public class DeviceResponse {
|
|||||||
response.setState(device.getDeviceState());
|
response.setState(device.getDeviceState());
|
||||||
|
|
||||||
response.setName(device.getName());
|
response.setName(device.getName());
|
||||||
response.setUniqueid(device.getId());
|
response.setUniqueid(device.getUniqueid());
|
||||||
response.setManufacturername("Philips");
|
response.setManufacturername("Philips");
|
||||||
response.setType("Dimmable light");
|
response.setType("Dimmable light");
|
||||||
response.setModelid("LWB004");
|
response.setModelid("LWB004");
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class DeviceState {
|
|||||||
private String colormode;
|
private String colormode;
|
||||||
private boolean reachable;
|
private boolean reachable;
|
||||||
private List<Double> xy;
|
private List<Double> xy;
|
||||||
private int transitiontime;
|
// private int transitiontime;
|
||||||
|
|
||||||
public boolean isOn() {
|
public boolean isOn() {
|
||||||
return on;
|
return on;
|
||||||
@@ -98,13 +98,13 @@ public class DeviceState {
|
|||||||
public void setXy(List<Double> xy) {
|
public void setXy(List<Double> xy) {
|
||||||
this.xy = xy;
|
this.xy = xy;
|
||||||
}
|
}
|
||||||
public int getTransitiontime() {
|
// public int getTransitiontime() {
|
||||||
return transitiontime;
|
// return transitiontime;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void setTransitiontime(int transitiontime) {
|
// public void setTransitiontime(int transitiontime) {
|
||||||
this.transitiontime = transitiontime;
|
// this.transitiontime = transitiontime;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public static DeviceState createDeviceState() {
|
public static DeviceState createDeviceState() {
|
||||||
DeviceState newDeviceState = new DeviceState();
|
DeviceState newDeviceState = new DeviceState();
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package com.bwssystems.HABridge.api.hue;
|
package com.bwssystems.HABridge.api.hue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||||
|
import com.bwssystems.HABridge.dao.DeviceRepository;
|
||||||
|
|
||||||
public class GroupResponse {
|
public class GroupResponse {
|
||||||
private DeviceState action;
|
private DeviceState action;
|
||||||
private String[] lights;
|
private String[] lights;
|
||||||
@@ -23,11 +28,17 @@ public class GroupResponse {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GroupResponse createGroupResponse(String[] theLights) {
|
public static GroupResponse createGroupResponse(List<DeviceDescriptor> deviceList) {
|
||||||
|
String[] theList = new String[deviceList.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (DeviceDescriptor device : deviceList) {
|
||||||
|
theList[i] = device.getId();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
GroupResponse theResponse = new GroupResponse();
|
GroupResponse theResponse = new GroupResponse();
|
||||||
theResponse.setAction(DeviceState.createDeviceState());
|
theResponse.setAction(DeviceState.createDeviceState());
|
||||||
theResponse.setName("Lightset 0");
|
theResponse.setName("Lightset 0");
|
||||||
theResponse.setLights(theLights);
|
theResponse.setLights(theList);
|
||||||
return theResponse;
|
return theResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ public class HueConfig
|
|||||||
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||||
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC"));
|
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
aConfig.setMac(HueConfig.getMacAddress(ipaddress));
|
aConfig.setMac(HueConfig.getMacAddress(ipaddress));
|
||||||
aConfig.setApiversion("1.10.0");
|
aConfig.setApiversion("1.15.0");
|
||||||
aConfig.setPortalservices(false);
|
aConfig.setPortalservices(false);
|
||||||
aConfig.setGateway(ipaddress);
|
aConfig.setGateway(ipaddress);
|
||||||
aConfig.setSwversion("01028090");
|
aConfig.setSwversion("01035934");
|
||||||
aConfig.setLinkbutton(false);
|
aConfig.setLinkbutton(true);
|
||||||
aConfig.setIpaddress(ipaddress);
|
aConfig.setIpaddress(ipaddress);
|
||||||
aConfig.setProxyport(0);
|
aConfig.setProxyport(0);
|
||||||
aConfig.setSwupdate(Swupdate.createSwupdate());
|
aConfig.setSwupdate(Swupdate.createSwupdate());
|
||||||
@@ -56,7 +56,7 @@ public class HueConfig
|
|||||||
aConfig.setLocaltime(dateFormat.format(new Date()));
|
aConfig.setLocaltime(dateFormat.format(new Date()));
|
||||||
aConfig.setTimezone(TimeZone.getDefault().getID());
|
aConfig.setTimezone(TimeZone.getDefault().getID());
|
||||||
aConfig.setZigbeechannel("6");
|
aConfig.setZigbeechannel("6");
|
||||||
aConfig.setBridgeid(HuePublicConfig.getBridgeIdFromMac(aConfig.getMac(), ipaddress));
|
aConfig.setBridgeid(HuePublicConfig.createConfig(name, ipaddress).getHueBridgeIdFromMac());
|
||||||
aConfig.setModelid("BSB002");
|
aConfig.setModelid("BSB002");
|
||||||
aConfig.setFactorynew(false);
|
aConfig.setFactorynew(false);
|
||||||
aConfig.setReplacesbridgeid(null);
|
aConfig.setReplacesbridgeid(null);
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
package com.bwssystems.HABridge.api.hue;
|
package com.bwssystems.HABridge.api.hue;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import javax.xml.bind.DatatypeConverter;
|
|
||||||
|
|
||||||
public class HuePublicConfig
|
public class HuePublicConfig
|
||||||
{
|
{
|
||||||
@@ -23,10 +21,10 @@ public class HuePublicConfig
|
|||||||
public static HuePublicConfig createConfig(String name, String ipaddress) {
|
public static HuePublicConfig createConfig(String name, String ipaddress) {
|
||||||
HuePublicConfig aConfig = new HuePublicConfig();
|
HuePublicConfig aConfig = new HuePublicConfig();
|
||||||
aConfig.setMac(HuePublicConfig.getMacAddress(ipaddress));
|
aConfig.setMac(HuePublicConfig.getMacAddress(ipaddress));
|
||||||
aConfig.setApiversion("1.10.0");
|
aConfig.setApiversion("1.15.0");
|
||||||
aConfig.setSwversion("01028090");
|
aConfig.setSwversion("01035934");
|
||||||
aConfig.setName(name);
|
aConfig.setName(name);
|
||||||
aConfig.setBridgeid(HuePublicConfig.getBridgeIdFromMac(aConfig.getMac(), ipaddress));
|
aConfig.setBridgeid(aConfig.getHueBridgeIdFromMac());
|
||||||
aConfig.setModelid("BSB002");
|
aConfig.setModelid("BSB002");
|
||||||
aConfig.setFactorynew(false);
|
aConfig.setFactorynew(false);
|
||||||
aConfig.setReplacesbridgeid(null);
|
aConfig.setReplacesbridgeid(null);
|
||||||
@@ -67,23 +65,22 @@ public class HuePublicConfig
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static String getBridgeIdFromMac(String macAddr, String ipAddr)
|
public String getSNUUIDFromMac()
|
||||||
{
|
{
|
||||||
StringTokenizer st = new StringTokenizer(macAddr, ":");
|
StringTokenizer st = new StringTokenizer(this.getMac(), ":");
|
||||||
String bridgeId = "";
|
String bridgeUUID = "";
|
||||||
String port = null;
|
|
||||||
while(st.hasMoreTokens()) {
|
while(st.hasMoreTokens()) {
|
||||||
bridgeId = bridgeId + st.nextToken();
|
bridgeUUID = bridgeUUID + st.nextToken();
|
||||||
}
|
}
|
||||||
if(ipAddr.contains(":")) {
|
bridgeUUID = bridgeUUID.toLowerCase();
|
||||||
port = ipAddr.substring(ipAddr.indexOf(":"));
|
return bridgeUUID.toLowerCase();
|
||||||
BigInteger bigInt = BigInteger.valueOf(Integer.getInteger(port).intValue());
|
}
|
||||||
byte[] theBytes = bigInt.toByteArray();
|
|
||||||
bridgeId = bridgeId + DatatypeConverter.printHexBinary(theBytes);
|
protected String getHueBridgeIdFromMac()
|
||||||
}
|
{
|
||||||
else
|
String cleanMac = this.getSNUUIDFromMac();
|
||||||
bridgeId = bridgeId + "0800";
|
String bridgeId = cleanMac.substring(0, 6) + "FFFE" + cleanMac.substring(6);
|
||||||
return bridgeId;
|
return bridgeId.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMac() {
|
public String getMac() {
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ public class DeviceDescriptor{
|
|||||||
@SerializedName("id")
|
@SerializedName("id")
|
||||||
@Expose
|
@Expose
|
||||||
private String id;
|
private String id;
|
||||||
|
@SerializedName("uniqueid")
|
||||||
|
@Expose
|
||||||
|
private String uniqueid;
|
||||||
@SerializedName("name")
|
@SerializedName("name")
|
||||||
@Expose
|
@Expose
|
||||||
private String name;
|
private String name;
|
||||||
@@ -50,6 +53,9 @@ public class DeviceDescriptor{
|
|||||||
@SerializedName("contentBodyOff")
|
@SerializedName("contentBodyOff")
|
||||||
@Expose
|
@Expose
|
||||||
private String contentBodyOff;
|
private String contentBodyOff;
|
||||||
|
@SerializedName("contentBodyDim")
|
||||||
|
@Expose
|
||||||
|
private String contentBodyDim;
|
||||||
|
|
||||||
private DeviceState deviceState;
|
private DeviceState deviceState;
|
||||||
|
|
||||||
@@ -125,6 +131,14 @@ public class DeviceDescriptor{
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUniqueid() {
|
||||||
|
return uniqueid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUniqueid(String uniqueid) {
|
||||||
|
this.uniqueid = uniqueid;
|
||||||
|
}
|
||||||
|
|
||||||
public String getHeaders() {
|
public String getHeaders() {
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
@@ -165,6 +179,14 @@ public class DeviceDescriptor{
|
|||||||
this.contentBodyOff = contentBodyOff;
|
this.contentBodyOff = contentBodyOff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getContentBodyDim() {
|
||||||
|
return contentBodyDim;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentBodyDim(String contentBodyDim) {
|
||||||
|
this.contentBodyDim = contentBodyDim;
|
||||||
|
}
|
||||||
|
|
||||||
public DeviceState getDeviceState() {
|
public DeviceState getDeviceState() {
|
||||||
if(deviceState == null)
|
if(deviceState == null)
|
||||||
deviceState = DeviceState.createDeviceState();
|
deviceState = DeviceState.createDeviceState();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.bwssystems.HABridge.dao;
|
|||||||
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.FileSystems;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -9,8 +10,11 @@ import java.nio.file.Paths;
|
|||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -29,7 +33,7 @@ public class DeviceRepository extends BackupHandler {
|
|||||||
private Map<String, DeviceDescriptor> devices;
|
private Map<String, DeviceDescriptor> devices;
|
||||||
private Path repositoryPath;
|
private Path repositoryPath;
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
final private Random random = new Random();
|
private Integer maxId;
|
||||||
private Logger log = LoggerFactory.getLogger(DeviceRepository.class);
|
private Logger log = LoggerFactory.getLogger(DeviceRepository.class);
|
||||||
|
|
||||||
public DeviceRepository(String deviceDb) {
|
public DeviceRepository(String deviceDb) {
|
||||||
@@ -41,6 +45,7 @@ public class DeviceRepository extends BackupHandler {
|
|||||||
repositoryPath = null;
|
repositoryPath = null;
|
||||||
repositoryPath = Paths.get(deviceDb);
|
repositoryPath = Paths.get(deviceDb);
|
||||||
setupParams(repositoryPath, ".bk", "device.db-");
|
setupParams(repositoryPath, ".bk", "device.db-");
|
||||||
|
maxId = 1;
|
||||||
_loadRepository(repositoryPath);
|
_loadRepository(repositoryPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +62,9 @@ public class DeviceRepository extends BackupHandler {
|
|||||||
DeviceDescriptor list[] = gson.fromJson(jsonContent, DeviceDescriptor[].class);
|
DeviceDescriptor list[] = gson.fromJson(jsonContent, DeviceDescriptor[].class);
|
||||||
for(int i = 0; i < list.length; i++) {
|
for(int i = 0; i < list.length; i++) {
|
||||||
put(list[i].getId(), list[i]);
|
put(list[i].getId(), list[i]);
|
||||||
|
if(Integer.decode(list[i].getId()) > maxId) {
|
||||||
|
maxId = Integer.decode(list[i].getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,8 +92,17 @@ public class DeviceRepository extends BackupHandler {
|
|||||||
for(int i = 0; i < descriptors.length; i++) {
|
for(int i = 0; i < descriptors.length; i++) {
|
||||||
if(descriptors[i].getId() != null && descriptors[i].getId().length() > 0)
|
if(descriptors[i].getId() != null && descriptors[i].getId().length() > 0)
|
||||||
devices.remove(descriptors[i].getId());
|
devices.remove(descriptors[i].getId());
|
||||||
else
|
else {
|
||||||
descriptors[i].setId(String.valueOf(random.nextInt(Integer.MAX_VALUE)));
|
descriptors[i].setId(String.valueOf(maxId));
|
||||||
|
maxId++;
|
||||||
|
}
|
||||||
|
if(descriptors[i].getUniqueid() == null || descriptors[i].getUniqueid().length() == 0) {
|
||||||
|
BigInteger bigInt = BigInteger.valueOf(Integer.decode(descriptors[i].getId()));
|
||||||
|
byte[] theBytes = bigInt.toByteArray();
|
||||||
|
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||||
|
|
||||||
|
descriptors[i].setUniqueid("00:17:88:5E:D3:" + hexValue + "-" + hexValue);
|
||||||
|
}
|
||||||
put(descriptors[i].getId(), descriptors[i]);
|
put(descriptors[i].getId(), descriptors[i]);
|
||||||
theNames = theNames + " " + descriptors[i].getName() + ", ";
|
theNames = theNames + " " + descriptors[i].getName() + ", ";
|
||||||
}
|
}
|
||||||
@@ -94,6 +111,28 @@ public class DeviceRepository extends BackupHandler {
|
|||||||
log.debug("Save device(s): " + theNames);
|
log.debug("Save device(s): " + theNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void renumber() {
|
||||||
|
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||||
|
Iterator<DeviceDescriptor> deviceIterator = list.iterator();
|
||||||
|
Map<String, DeviceDescriptor> newdevices = new HashMap<String, DeviceDescriptor>();;
|
||||||
|
maxId = 1;
|
||||||
|
log.debug("Renumber devices.");
|
||||||
|
while(deviceIterator.hasNext()) {
|
||||||
|
DeviceDescriptor theDevice = deviceIterator.next();
|
||||||
|
theDevice.setId(String.valueOf(maxId));
|
||||||
|
BigInteger bigInt = BigInteger.valueOf(maxId);
|
||||||
|
byte[] theBytes = bigInt.toByteArray();
|
||||||
|
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||||
|
|
||||||
|
theDevice.setUniqueid("00:17:88:5E:D3:" + hexValue + "-" + hexValue);
|
||||||
|
newdevices.put(theDevice.getId(), theDevice);
|
||||||
|
maxId++;
|
||||||
|
}
|
||||||
|
devices = newdevices;
|
||||||
|
String jsonValue = gson.toJson(findAll());
|
||||||
|
repositoryWriter(jsonValue, repositoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
public String delete(DeviceDescriptor aDescriptor) {
|
public String delete(DeviceDescriptor aDescriptor) {
|
||||||
if (aDescriptor != null) {
|
if (aDescriptor != null) {
|
||||||
devices.remove(aDescriptor.getId());
|
devices.remove(aDescriptor.getId());
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import static spark.Spark.delete;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -280,6 +281,21 @@ public class DeviceResource {
|
|||||||
return halHome.getDevices();
|
return halHome.getDevices();
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
|
|
||||||
|
// http://ip_address:port/api/devices/exec/renumber CORS request
|
||||||
|
options(API_CONTEXT + "/exec/renumber", "application/json", (request, response) -> {
|
||||||
|
response.status(HttpStatus.SC_OK);
|
||||||
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
|
response.header("Access-Control-Allow-Methods", "POST");
|
||||||
|
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||||
|
response.header("Content-Type", "text/html; charset=utf-8");
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
post (API_CONTEXT + "/exec/renumber", "application/json", (request, response) -> {
|
||||||
|
log.debug("Renumber devices.");
|
||||||
|
deviceRepository.renumber();
|
||||||
|
return null;
|
||||||
|
}, new JsonTransformer());
|
||||||
|
|
||||||
get (API_CONTEXT + "/backup/available", "application/json", (request, response) -> {
|
get (API_CONTEXT + "/backup/available", "application/json", (request, response) -> {
|
||||||
log.debug("Get backup filenames");
|
log.debug("Get backup filenames");
|
||||||
response.status(HttpStatus.SC_OK);
|
response.status(HttpStatus.SC_OK);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.bwssystems.hue.HueHome;
|
|||||||
import com.bwssystems.hue.HueUtil;
|
import com.bwssystems.hue.HueUtil;
|
||||||
import com.bwssystems.nest.controller.Nest;
|
import com.bwssystems.nest.controller.Nest;
|
||||||
import com.bwssystems.util.JsonTransformer;
|
import com.bwssystems.util.JsonTransformer;
|
||||||
|
import com.bwssystems.util.UDPDatagramSender;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
import net.java.dev.eval.Expression;
|
import net.java.dev.eval.Expression;
|
||||||
@@ -60,8 +61,6 @@ import java.io.DataOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.DatagramPacket;
|
|
||||||
import java.net.DatagramSocket;
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@@ -99,12 +98,13 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
private SSLConnectionSocketFactory sslsf;
|
private SSLConnectionSocketFactory sslsf;
|
||||||
private RequestConfig globalConfig;
|
private RequestConfig globalConfig;
|
||||||
private BridgeSettingsDescriptor bridgeSettings;
|
private BridgeSettingsDescriptor bridgeSettings;
|
||||||
|
private UDPDatagramSender theUDPDatagramSender;
|
||||||
private byte[] sendData;
|
private byte[] sendData;
|
||||||
private String hueUser;
|
private String hueUser;
|
||||||
private String errorString;
|
private String errorString;
|
||||||
|
|
||||||
|
|
||||||
public HueMulator(BridgeSettingsDescriptor theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome, NestHome aNestHome, HueHome aHueHome){
|
public HueMulator(BridgeSettingsDescriptor theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome, NestHome aNestHome, HueHome aHueHome, UDPDatagramSender aUdpDatagramSender) {
|
||||||
httpClient = HttpClients.createDefault();
|
httpClient = HttpClients.createDefault();
|
||||||
// Trust own CA and all self-signed certs
|
// Trust own CA and all self-signed certs
|
||||||
sslcontext = SSLContexts.createDefault();
|
sslcontext = SSLContexts.createDefault();
|
||||||
@@ -136,6 +136,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
else
|
else
|
||||||
this.myHueHome = null;
|
this.myHueHome = null;
|
||||||
bridgeSettings = theBridgeSettings;
|
bridgeSettings = theBridgeSettings;
|
||||||
|
theUDPDatagramSender = aUdpDatagramSender;
|
||||||
hueUser = null;
|
hueUser = null;
|
||||||
errorString = null;
|
errorString = null;
|
||||||
}
|
}
|
||||||
@@ -173,14 +174,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(groupId.equalsIgnoreCase("0")) {
|
if(groupId.equalsIgnoreCase("0")) {
|
||||||
List<DeviceDescriptor> deviceList = repository.findAll();
|
GroupResponse theResponse = GroupResponse.createGroupResponse(repository.findAll());
|
||||||
String[] theList = new String[deviceList.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (DeviceDescriptor device : deviceList) {
|
|
||||||
theList[i] = device.getId();
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
GroupResponse theResponse = GroupResponse.createGroupResponse(theList);
|
|
||||||
return new Gson().toJson(theResponse, GroupResponse.class);
|
return new Gson().toJson(theResponse, GroupResponse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +261,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
if(bridgeSettings.isTraceupnp())
|
if(bridgeSettings.isTraceupnp())
|
||||||
log.info("Traceupnp: hue lights list requested: " + userId + " from " + request.ip());
|
log.info("Traceupnp: hue lights list requested: " + userId + " from " + request.ip());
|
||||||
log.debug("hue lights list requested: " + userId + " from " + request.ip());
|
log.debug("hue lights list requested: " + userId + " from " + request.ip());
|
||||||
response.type("application/json; charset=utf-8");
|
response.type("application/json");
|
||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.status(HttpStatus.SC_OK);
|
response.status(HttpStatus.SC_OK);
|
||||||
if(validateWhitelistUser(userId, false) == null) {
|
if(validateWhitelistUser(userId, false) == null) {
|
||||||
@@ -280,7 +274,41 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
List<DeviceDescriptor> deviceList = repository.findAll();
|
List<DeviceDescriptor> deviceList = repository.findAll();
|
||||||
Map<String, DeviceResponse> deviceResponseMap = new HashMap<>();
|
Map<String, DeviceResponse> deviceResponseMap = new HashMap<>();
|
||||||
for (DeviceDescriptor device : deviceList) {
|
for (DeviceDescriptor device : deviceList) {
|
||||||
DeviceResponse deviceResponse = DeviceResponse.createResponse(device);
|
DeviceResponse deviceResponse = null;
|
||||||
|
String responseString;
|
||||||
|
if((device.getMapType() != null && device.getMapType().equalsIgnoreCase("hueDevice"))) {
|
||||||
|
HueDeviceIdentifier deviceId = new Gson().fromJson(device.getOnUrl(), HueDeviceIdentifier.class);
|
||||||
|
if(myHueHome.getTheHUERegisteredUser() == null) {
|
||||||
|
hueUser = HueUtil.registerWithHue(httpClient, deviceId.getIpAddress(), device.getName(), myHueHome.getTheHUERegisteredUser(), this);
|
||||||
|
if(hueUser == null) {
|
||||||
|
return errorString;
|
||||||
|
}
|
||||||
|
myHueHome.setTheHUERegisteredUser(hueUser);
|
||||||
|
}
|
||||||
|
// make call
|
||||||
|
responseString = doHttpRequest("http://"+deviceId.getIpAddress()+"/api/"+myHueHome.getTheHUERegisteredUser()+"/lights/"+deviceId.getDeviceId(), HttpGet.METHOD_NAME, device.getContentType(), null, null);
|
||||||
|
if (responseString == null) {
|
||||||
|
log.warn("Error on calling hue device to get state: " + device.getName());
|
||||||
|
deviceResponse = DeviceResponse.createResponse(device);
|
||||||
|
}
|
||||||
|
else if(responseString.contains("[{\"error\":") && responseString.contains("unauthorized user")) {
|
||||||
|
myHueHome.setTheHUERegisteredUser(null);
|
||||||
|
hueUser = HueUtil.registerWithHue(httpClient, deviceId.getIpAddress(), device.getName(), myHueHome.getTheHUERegisteredUser(), this);
|
||||||
|
if(hueUser == null) {
|
||||||
|
return errorString;
|
||||||
|
}
|
||||||
|
myHueHome.setTheHUERegisteredUser(hueUser);
|
||||||
|
deviceResponse = DeviceResponse.createResponse(device);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
deviceResponse = new Gson().fromJson(responseString, DeviceResponse.class);
|
||||||
|
if(deviceResponse == null)
|
||||||
|
deviceResponse = DeviceResponse.createResponse(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
deviceResponse = DeviceResponse.createResponse(device);
|
||||||
deviceResponseMap.put(device.getId(), deviceResponse);
|
deviceResponseMap.put(device.getId(), deviceResponse);
|
||||||
}
|
}
|
||||||
return deviceResponseMap;
|
return deviceResponseMap;
|
||||||
@@ -292,7 +320,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||||
response.header("Content-Type", "text/html; charset=utf-8");
|
response.header("Content-Type", "text/html");
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
// http://ip_address:port/api with body of user request returns json object for a success of user add
|
// http://ip_address:port/api with body of user request returns json object for a success of user add
|
||||||
@@ -321,7 +349,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
log.debug("hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
|
log.debug("hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
|
||||||
|
|
||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.type("application/json; charset=utf-8");
|
response.type("application/json");
|
||||||
response.status(HttpStatus.SC_OK);
|
response.status(HttpStatus.SC_OK);
|
||||||
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
|
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
|
||||||
} );
|
} );
|
||||||
@@ -332,7 +360,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||||
response.header("Content-Type", "text/html; charset=utf-8");
|
response.header("Content-Type", "text/html");
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
// http://ip_address:port/api/* with body of user request returns json object for a success of user add - This method is for Harmony Hub
|
// http://ip_address:port/api/* with body of user request returns json object for a success of user add - This method is for Harmony Hub
|
||||||
@@ -356,7 +384,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
aDeviceType = "<not given>";
|
aDeviceType = "<not given>";
|
||||||
log.debug("HH trace: hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
|
log.debug("HH trace: hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
|
||||||
|
|
||||||
response.type("application/json; charset=utf-8");
|
response.type("application/json");
|
||||||
response.status(HttpStatus.SC_OK);
|
response.status(HttpStatus.SC_OK);
|
||||||
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
|
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
|
||||||
} );
|
} );
|
||||||
@@ -368,7 +396,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
log.debug("hue api public config requested, from " + request.ip());
|
log.debug("hue api public config requested, from " + request.ip());
|
||||||
HuePublicConfig apiResponse = HuePublicConfig.createConfig("Philips hue", bridgeSettings.getUpnpConfigAddress());
|
HuePublicConfig apiResponse = HuePublicConfig.createConfig("Philips hue", bridgeSettings.getUpnpConfigAddress());
|
||||||
|
|
||||||
response.type("application/json; charset=utf-8");
|
response.type("application/json");
|
||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.status(HttpStatus.SC_OK);
|
response.status(HttpStatus.SC_OK);
|
||||||
return apiResponse;
|
return apiResponse;
|
||||||
@@ -377,7 +405,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
// http://ip_address:port/api/{userId}/config returns json objects for the config
|
// http://ip_address:port/api/{userId}/config returns json objects for the config
|
||||||
get(HUE_CONTEXT + "/:userid/config", "application/json", (request, response) -> {
|
get(HUE_CONTEXT + "/:userid/config", "application/json", (request, response) -> {
|
||||||
String userId = request.params(":userid");
|
String userId = request.params(":userid");
|
||||||
response.type("application/json; charset=utf-8");
|
response.type("application/json");
|
||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.status(HttpStatus.SC_OK);
|
response.status(HttpStatus.SC_OK);
|
||||||
if(bridgeSettings.isTraceupnp())
|
if(bridgeSettings.isTraceupnp())
|
||||||
@@ -399,7 +427,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
get(HUE_CONTEXT + "/:userid", "application/json", (request, response) -> {
|
get(HUE_CONTEXT + "/:userid", "application/json", (request, response) -> {
|
||||||
String userId = request.params(":userid");
|
String userId = request.params(":userid");
|
||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.type("application/json; charset=utf-8");
|
response.type("application/json");
|
||||||
response.status(HttpStatus.SC_OK);
|
response.status(HttpStatus.SC_OK);
|
||||||
log.debug("hue api full state requested: " + userId + " from " + request.ip());
|
log.debug("hue api full state requested: " + userId + " from " + request.ip());
|
||||||
if(validateWhitelistUser(userId, false) == null) {
|
if(validateWhitelistUser(userId, false) == null) {
|
||||||
@@ -409,9 +437,9 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
return theErrorResp.getTheErrors();
|
return theErrorResp.getTheErrors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<DeviceDescriptor> descriptorList = repository.findAll();
|
||||||
HueApiResponse apiResponse = new HueApiResponse("Philips hue", bridgeSettings.getUpnpConfigAddress(), bridgeSettings.getWhitelist());
|
HueApiResponse apiResponse = new HueApiResponse("Philips hue", bridgeSettings.getUpnpConfigAddress(), bridgeSettings.getWhitelist());
|
||||||
Map<String, DeviceResponse> deviceList = new HashMap<>();
|
Map<String, DeviceResponse> deviceList = new HashMap<>();
|
||||||
List<DeviceDescriptor> descriptorList = repository.findAll();
|
|
||||||
if (descriptorList != null) {
|
if (descriptorList != null) {
|
||||||
descriptorList.forEach(descriptor -> {
|
descriptorList.forEach(descriptor -> {
|
||||||
DeviceResponse deviceResponse = DeviceResponse.createResponse(descriptor);
|
DeviceResponse deviceResponse = DeviceResponse.createResponse(descriptor);
|
||||||
@@ -429,7 +457,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
String userId = request.params(":userid");
|
String userId = request.params(":userid");
|
||||||
String lightId = request.params(":id");
|
String lightId = request.params(":id");
|
||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.type("application/json; charset=utf-8");
|
response.type("application/json");
|
||||||
response.status(HttpStatus.SC_OK);
|
response.status(HttpStatus.SC_OK);
|
||||||
log.debug("hue light requested: " + lightId + " for user: " + userId + " from " + request.ip());
|
log.debug("hue light requested: " + lightId + " for user: " + userId + " from " + request.ip());
|
||||||
if(validateWhitelistUser(userId, false) == null) {
|
if(validateWhitelistUser(userId, false) == null) {
|
||||||
@@ -448,7 +476,41 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
} else {
|
} else {
|
||||||
log.debug("found device named: " + device.getName());
|
log.debug("found device named: " + device.getName());
|
||||||
}
|
}
|
||||||
DeviceResponse lightResponse = DeviceResponse.createResponse(device);
|
DeviceResponse lightResponse = null;
|
||||||
|
String responseString;
|
||||||
|
if((device.getMapType() != null && device.getMapType().equalsIgnoreCase("hueDevice"))) {
|
||||||
|
HueDeviceIdentifier deviceId = new Gson().fromJson(device.getOnUrl(), HueDeviceIdentifier.class);
|
||||||
|
if(myHueHome.getTheHUERegisteredUser() == null) {
|
||||||
|
hueUser = HueUtil.registerWithHue(httpClient, deviceId.getIpAddress(), device.getName(), myHueHome.getTheHUERegisteredUser(), this);
|
||||||
|
if(hueUser == null) {
|
||||||
|
return errorString;
|
||||||
|
}
|
||||||
|
myHueHome.setTheHUERegisteredUser(hueUser);
|
||||||
|
}
|
||||||
|
// make call
|
||||||
|
responseString = doHttpRequest("http://"+deviceId.getIpAddress()+"/api/"+myHueHome.getTheHUERegisteredUser()+"/lights/"+deviceId.getDeviceId(), HttpGet.METHOD_NAME, device.getContentType(), null, null);
|
||||||
|
if (responseString == null) {
|
||||||
|
log.warn("Error on calling hue device to get state: " + device.getName());
|
||||||
|
lightResponse = DeviceResponse.createResponse(device);
|
||||||
|
}
|
||||||
|
else if(responseString.contains("[{\"error\":") && responseString.contains("unauthorized user")) {
|
||||||
|
myHueHome.setTheHUERegisteredUser(null);
|
||||||
|
hueUser = HueUtil.registerWithHue(httpClient, deviceId.getIpAddress(), device.getName(), myHueHome.getTheHUERegisteredUser(), this);
|
||||||
|
if(hueUser == null) {
|
||||||
|
return errorString;
|
||||||
|
}
|
||||||
|
myHueHome.setTheHUERegisteredUser(hueUser);
|
||||||
|
lightResponse = DeviceResponse.createResponse(device);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lightResponse = new Gson().fromJson(responseString, DeviceResponse.class);
|
||||||
|
if(lightResponse == null)
|
||||||
|
lightResponse = DeviceResponse.createResponse(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lightResponse = DeviceResponse.createResponse(device);
|
||||||
|
|
||||||
return lightResponse;
|
return lightResponse;
|
||||||
}, new JsonTransformer());
|
}, new JsonTransformer());
|
||||||
@@ -459,7 +521,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||||
response.header("Content-Type", "text/html; charset=utf-8");
|
response.header("Content-Type", "text/html");
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
// http://ip_address:port/api/{userId}/lights/{lightId}/bridgeupdatestate uses json object to update the internal bridge lights state.
|
// http://ip_address:port/api/{userId}/lights/{lightId}/bridgeupdatestate uses json object to update the internal bridge lights state.
|
||||||
@@ -474,7 +536,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
boolean stateHasBriInc = false;
|
boolean stateHasBriInc = false;
|
||||||
log.debug("Update state requested: " + userId + " from " + request.ip() + " body: " + request.body());
|
log.debug("Update state requested: " + userId + " from " + request.ip() + " body: " + request.body());
|
||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.type("application/json; charset=utf-8");
|
response.type("application/json");
|
||||||
response.status(HttpStatus.SC_OK);
|
response.status(HttpStatus.SC_OK);
|
||||||
if(validateWhitelistUser(userId, false) == null) {
|
if(validateWhitelistUser(userId, false) == null) {
|
||||||
log.debug("Valudate user, No User supplied");
|
log.debug("Valudate user, No User supplied");
|
||||||
@@ -490,8 +552,12 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
return responseString;
|
return responseString;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.body().contains("\"bri\""))
|
if (request.body().contains("\"bri\"")) {
|
||||||
stateHasBri = true;
|
if(theStateChanges.isOn() && theStateChanges.getBri() == 0)
|
||||||
|
stateHasBri = false;
|
||||||
|
else
|
||||||
|
stateHasBri = true;
|
||||||
|
}
|
||||||
if (request.body().contains("\"bri_inc\""))
|
if (request.body().contains("\"bri_inc\""))
|
||||||
stateHasBriInc = true;
|
stateHasBriInc = true;
|
||||||
|
|
||||||
@@ -539,7 +605,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||||
response.header("Content-Type", "text/html; charset=utf-8");
|
response.header("Content-Type", "text/html");
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
// http://ip_address:port/api/{userId}/lights/{lightId}/state uses json object to set the lights state
|
// http://ip_address:port/api/{userId}/lights/{lightId}/state uses json object to set the lights state
|
||||||
@@ -559,7 +625,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
boolean stateHasBriInc = false;
|
boolean stateHasBriInc = false;
|
||||||
log.debug("hue state change requested: " + userId + " from " + request.ip() + " body: " + request.body());
|
log.debug("hue state change requested: " + userId + " from " + request.ip() + " body: " + request.body());
|
||||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||||
response.type("application/json; charset=utf-8");
|
response.type("application/json");
|
||||||
response.status(HttpStatus.SC_OK);
|
response.status(HttpStatus.SC_OK);
|
||||||
if(validateWhitelistUser(userId, false) == null) {
|
if(validateWhitelistUser(userId, false) == null) {
|
||||||
log.debug("Valudate user, No User supplied");
|
log.debug("Valudate user, No User supplied");
|
||||||
@@ -576,8 +642,12 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
return responseString;
|
return responseString;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.body().contains("\"bri\""))
|
if (request.body().contains("\"bri\"")) {
|
||||||
stateHasBri = true;
|
if(theStateChanges.isOn() && theStateChanges.getBri() == 0)
|
||||||
|
stateHasBri = false;
|
||||||
|
else
|
||||||
|
stateHasBri = true;
|
||||||
|
}
|
||||||
if (request.body().contains("\"bri_inc\""))
|
if (request.body().contains("\"bri_inc\""))
|
||||||
stateHasBriInc = true;
|
stateHasBriInc = true;
|
||||||
|
|
||||||
@@ -836,10 +906,7 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
}
|
}
|
||||||
if(callItems[i].getItem().contains("udp://")) {
|
if(callItems[i].getItem().contains("udp://")) {
|
||||||
log.debug("executing HUE api request to UDP: " + callItems[i].getItem());
|
log.debug("executing HUE api request to UDP: " + callItems[i].getItem());
|
||||||
DatagramSocket responseSocket = new DatagramSocket(Integer.parseInt(port));
|
theUDPDatagramSender.sendUDPResponse(new String(sendData), IPAddress, Integer.parseInt(port));
|
||||||
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, Integer.parseInt(port));
|
|
||||||
responseSocket.send(sendPacket);
|
|
||||||
responseSocket.close();
|
|
||||||
}
|
}
|
||||||
else if(callItems[i].getItem().contains("tcp://"))
|
else if(callItems[i].getItem().contains("tcp://"))
|
||||||
{
|
{
|
||||||
@@ -974,33 +1041,39 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
protected String doHttpRequest(String url, String httpVerb, String contentType, String body, NameValue[] headers) {
|
protected String doHttpRequest(String url, String httpVerb, String contentType, String body, NameValue[] headers) {
|
||||||
HttpUriRequest request = null;
|
HttpUriRequest request = null;
|
||||||
String theContent = null;
|
String theContent = null;
|
||||||
|
ContentType parsedContentType = null;
|
||||||
|
StringEntity requestBody = null;
|
||||||
|
if(contentType != null && contentType.length() > 0) {
|
||||||
|
parsedContentType = ContentType.parse(contentType);
|
||||||
|
if(body != null && body.length() > 0)
|
||||||
|
requestBody = new StringEntity(body, parsedContentType);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(HttpGet.METHOD_NAME.equalsIgnoreCase(httpVerb) || httpVerb == null) {
|
if(HttpGet.METHOD_NAME.equalsIgnoreCase(httpVerb) || httpVerb == null) {
|
||||||
request = new HttpGet(url);
|
request = new HttpGet(url);
|
||||||
}else if(HttpPost.METHOD_NAME.equalsIgnoreCase(httpVerb)){
|
}else if(HttpPost.METHOD_NAME.equalsIgnoreCase(httpVerb)){
|
||||||
HttpPost postRequest = new HttpPost(url);
|
HttpPost postRequest = new HttpPost(url);
|
||||||
ContentType parsedContentType = ContentType.parse(contentType);
|
if(requestBody != null)
|
||||||
StringEntity requestBody = new StringEntity(body, parsedContentType);
|
postRequest.setEntity(requestBody);
|
||||||
postRequest.setEntity(requestBody);
|
|
||||||
request = postRequest;
|
request = postRequest;
|
||||||
}else if(HttpPut.METHOD_NAME.equalsIgnoreCase(httpVerb)){
|
}else if(HttpPut.METHOD_NAME.equalsIgnoreCase(httpVerb)){
|
||||||
HttpPut putRequest = new HttpPut(url);
|
HttpPut putRequest = new HttpPut(url);
|
||||||
ContentType parsedContentType = ContentType.parse(contentType);
|
if(requestBody != null)
|
||||||
StringEntity requestBody = new StringEntity(body, parsedContentType);
|
putRequest.setEntity(requestBody);
|
||||||
putRequest.setEntity(requestBody);
|
|
||||||
request = putRequest;
|
request = putRequest;
|
||||||
}
|
}
|
||||||
} catch(IllegalArgumentException e) {
|
} catch(IllegalArgumentException e) {
|
||||||
log.warn("Error calling out to HA gateway: IllegalArgumentException in log", e);
|
log.warn("Error creating outbound http request: IllegalArgumentException in log", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
log.debug("Making outbound call in doHttpRequest: " + request);
|
log.debug("Making outbound call in doHttpRequest: " + request);
|
||||||
|
if(headers != null && headers.length > 0) {
|
||||||
|
for(int i = 0; i < headers.length; i++) {
|
||||||
|
request.setHeader(headers[i].getName(), headers[i].getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if(headers != null && headers.length > 0) {
|
|
||||||
for(int i = 0; i < headers.length; i++) {
|
|
||||||
request.setHeader(headers[i].getName(), headers[i].getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HttpResponse response;
|
HttpResponse response;
|
||||||
if(url.startsWith("https"))
|
if(url.startsWith("https"))
|
||||||
response = httpclientSSL.execute(request);
|
response = httpclientSSL.execute(request);
|
||||||
@@ -1025,18 +1098,29 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
return theContent;
|
return theContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String doExecRequest(String anItem, int intensity, String lightId) {
|
private String doExecRequest(String anItem, int intensity, String lightId) {
|
||||||
log.debug("Executing request: " + anItem);
|
log.debug("Executing request: " + anItem);
|
||||||
String responseString = null;
|
String responseString = null;
|
||||||
try {
|
if(anItem != null && !anItem.equalsIgnoreCase("")) {
|
||||||
Process p = Runtime.getRuntime().exec(replaceIntensityValue(anItem, intensity, false));
|
try {
|
||||||
log.debug("Process running: " + p.isAlive());
|
Process p = Runtime.getRuntime().exec(replaceIntensityValue(anItem, intensity, false));
|
||||||
} catch (IOException e) {
|
log.debug("Process running: " + p.isAlive());
|
||||||
log.warn("Could not execute request: " + anItem, e);
|
} catch (IOException e) {
|
||||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
log.warn("Could not execute request: " + anItem, e);
|
||||||
}
|
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||||
return responseString;
|
+ "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId
|
||||||
}
|
+ "state\"}}]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.warn("Could not execute request. Request is empty.");
|
||||||
|
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
|
||||||
|
+ "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId
|
||||||
|
+ "state\"}}]";
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseString;
|
||||||
|
}
|
||||||
|
|
||||||
private String formatSuccessHueResponse(StateChangeBody state, String body, String lightId, DeviceState deviceState) {
|
private String formatSuccessHueResponse(StateChangeBody state, String body, String lightId, DeviceState deviceState) {
|
||||||
|
|
||||||
@@ -1169,8 +1253,8 @@ public class HueMulator implements HueErrorStringSet {
|
|||||||
if(notFirstChange)
|
if(notFirstChange)
|
||||||
responseString = responseString + ",";
|
responseString = responseString + ",";
|
||||||
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/transitiontime\":" + state.getTransitiontime() + "}}";
|
responseString = responseString + "{\"success\":{\"/lights/" + lightId + "/state/transitiontime\":" + state.getTransitiontime() + "}}";
|
||||||
if(deviceState != null)
|
// if(deviceState != null)
|
||||||
deviceState.setTransitiontime(state.getTransitiontime());
|
// deviceState.setTransitiontime(state.getTransitiontime());
|
||||||
notFirstChange = true;
|
notFirstChange = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
import com.bwssystems.HABridge.BridgeControlDescriptor;
|
import com.bwssystems.HABridge.BridgeControlDescriptor;
|
||||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||||
import com.bwssystems.HABridge.Configuration;
|
import com.bwssystems.HABridge.Configuration;
|
||||||
|
import com.bwssystems.HABridge.api.hue.HuePublicConfig;
|
||||||
|
import com.bwssystems.util.UDPDatagramSender;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
@@ -16,69 +18,56 @@ import org.apache.http.conn.util.*;
|
|||||||
|
|
||||||
public class UpnpListener {
|
public class UpnpListener {
|
||||||
private Logger log = LoggerFactory.getLogger(UpnpListener.class);
|
private Logger log = LoggerFactory.getLogger(UpnpListener.class);
|
||||||
private int upnpResponsePort;
|
private UDPDatagramSender theUDPDatagramSender;
|
||||||
private int httpServerPort;
|
private int httpServerPort;
|
||||||
private String responseAddress;
|
private String responseAddress;
|
||||||
private boolean strict;
|
private boolean strict;
|
||||||
private boolean traceupnp;
|
private boolean traceupnp;
|
||||||
private BridgeControlDescriptor bridgeControl;
|
private BridgeControlDescriptor bridgeControl;
|
||||||
private boolean discoveryTemplateLatest;
|
private String responseTemplate1 = "HTTP/1.1 200 OK\r\n" +
|
||||||
private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" +
|
"HOST: %s:%s\r\n" +
|
||||||
"CACHE-CONTROL: max-age=86400\r\n" +
|
"CACHE-CONTROL: max-age=100\r\n" +
|
||||||
"EXT:\r\n" +
|
"EXT:\r\n" +
|
||||||
"LOCATION: http://%s:%s/description.xml\r\n" +
|
"LOCATION: http://%s:%s/description.xml\r\n" +
|
||||||
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.10.0\r\n" +
|
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0\r\n" +
|
||||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
|
"hue-bridgeid: %s\r\n" +
|
||||||
"USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n";
|
"ST: upnp:rootdevice\r\n" +
|
||||||
private String discoveryTemplateOld = "HTTP/1.1 200 OK\r\n" +
|
"USN: uuid:2f402f80-da50-11e1-9b23-%s::upnp:rootdevice\r\n\r\n";
|
||||||
"CACHE-CONTROL: max-age=86400\r\n" +
|
private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" +
|
||||||
|
"HOST: %s:%s\r\n" +
|
||||||
|
"CACHE-CONTROL: max-age=100\r\n" +
|
||||||
"EXT:\r\n" +
|
"EXT:\r\n" +
|
||||||
"LOCATION: http://%s:%s/description.xml\r\n" +
|
"LOCATION: http://%s:%s/description.xml\r\n" +
|
||||||
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1\r\n" +
|
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0\r\n" +
|
||||||
|
"hue-bridgeid: %s\r\n" +
|
||||||
|
"ST: uuid:2f402f80-da50-11e1-9b23-%s\r\n" +
|
||||||
|
"USN: uuid:2f402f80-da50-11e1-9b23-%s\r\n\r\n";
|
||||||
|
private String responseTemplate3 = "HTTP/1.1 200 OK\r\n" +
|
||||||
|
"HOST: %s:%s\r\n" +
|
||||||
|
"CACHE-CONTROL: max-age=100\r\n" +
|
||||||
|
"EXT:\r\n" +
|
||||||
|
"LOCATION: http://%s:%s/description.xml\r\n" +
|
||||||
|
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0\r\n" +
|
||||||
|
"hue-bridgeid: %s\r\n" +
|
||||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
|
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
|
||||||
"USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n";
|
"USN: uuid:2f402f80-da50-11e1-9b23-%s\r\n\r\n";
|
||||||
|
|
||||||
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl) {
|
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl, UDPDatagramSender aUdpDatagramSender) {
|
||||||
super();
|
super();
|
||||||
upnpResponsePort = theSettings.getUpnpResponsePort();
|
theUDPDatagramSender = aUdpDatagramSender;
|
||||||
httpServerPort = Integer.valueOf(theSettings.getServerPort());
|
httpServerPort = Integer.valueOf(theSettings.getServerPort());
|
||||||
responseAddress = theSettings.getUpnpConfigAddress();
|
responseAddress = theSettings.getUpnpConfigAddress();
|
||||||
strict = theSettings.isUpnpStrict();
|
strict = theSettings.isUpnpStrict();
|
||||||
traceupnp = theSettings.isTraceupnp();
|
traceupnp = theSettings.isTraceupnp();
|
||||||
bridgeControl = theControl;
|
bridgeControl = theControl;
|
||||||
discoveryTemplateLatest = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
public boolean startListening(){
|
public boolean startListening(){
|
||||||
log.info("UPNP Discovery Listener starting....");
|
log.info("UPNP Discovery Listener starting....");
|
||||||
DatagramSocket responseSocket = null;
|
|
||||||
MulticastSocket upnpMulticastSocket = null;
|
MulticastSocket upnpMulticastSocket = null;
|
||||||
Enumeration<NetworkInterface> ifs = null;
|
Enumeration<NetworkInterface> ifs = null;
|
||||||
|
|
||||||
boolean portLoopControl = true;
|
|
||||||
int retryCount = 0;
|
|
||||||
while(portLoopControl) {
|
|
||||||
try {
|
|
||||||
responseSocket = new DatagramSocket(upnpResponsePort);
|
|
||||||
if(retryCount > 0)
|
|
||||||
log.info("Upnp Response Port issue, found open port: " + upnpResponsePort);
|
|
||||||
portLoopControl = false;
|
|
||||||
} catch(SocketException e) {
|
|
||||||
if(retryCount == 0)
|
|
||||||
log.warn("Upnp Response Port is in use, starting loop to find open port for 20 tries - configured port is: " + upnpResponsePort);
|
|
||||||
if(retryCount >= 20) {
|
|
||||||
portLoopControl = false;
|
|
||||||
log.error("Upnp Response Port issue, could not find open port - last port tried: " + upnpResponsePort + " with message: " + e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(portLoopControl) {
|
|
||||||
retryCount++;
|
|
||||||
upnpResponsePort++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
|
upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
|
||||||
} catch(IOException e){
|
} catch(IOException e){
|
||||||
@@ -135,10 +124,14 @@ public class UpnpListener {
|
|||||||
upnpMulticastSocket.receive(packet);
|
upnpMulticastSocket.receive(packet);
|
||||||
if (isSSDPDiscovery(packet)) {
|
if (isSSDPDiscovery(packet)) {
|
||||||
try {
|
try {
|
||||||
sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort());
|
sendUpnpResponse(packet.getAddress(), packet.getPort());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("UpnpListener encountered an error sending upnp response packet. Shutting down", e);
|
if(e.getMessage().equalsIgnoreCase("Host is down"))
|
||||||
error = true;
|
log.warn("UpnpListener encountered an error sending upnp response packet as requesting host is now not available. IP: " + packet.getAddress().getHostAddress());
|
||||||
|
else {
|
||||||
|
log.error("UpnpListener encountered an error sending upnp response packet. Shutting down", e);
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -155,7 +148,6 @@ public class UpnpListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
upnpMulticastSocket.close();
|
upnpMulticastSocket.close();
|
||||||
responseSocket.close();
|
|
||||||
if (bridgeControl.isReinit())
|
if (bridgeControl.isReinit())
|
||||||
log.info("UPNP Discovery Listener - ended, restart found");
|
log.info("UPNP Discovery Listener - ended, restart found");
|
||||||
if (bridgeControl.isStop())
|
if (bridgeControl.isStop())
|
||||||
@@ -184,7 +176,8 @@ public class UpnpListener {
|
|||||||
log.info("Traceupnp: isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
log.info("Traceupnp: isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
||||||
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||||
}
|
}
|
||||||
log.debug("isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
else
|
||||||
|
log.debug("isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (!strict)
|
else if (!strict)
|
||||||
@@ -194,7 +187,8 @@ public class UpnpListener {
|
|||||||
log.info("Traceupnp: isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
log.info("Traceupnp: isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
||||||
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||||
}
|
}
|
||||||
log.debug("isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
else
|
||||||
|
log.debug("isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,17 +199,35 @@ public class UpnpListener {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void sendUpnpResponse(DatagramSocket socket, InetAddress requester, int sourcePort) throws IOException {
|
protected void sendUpnpResponse(InetAddress requester, int sourcePort) throws IOException {
|
||||||
String discoveryResponse = null;
|
String discoveryResponse = null;
|
||||||
if(discoveryTemplateLatest)
|
String bridgeId = null;
|
||||||
discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort);
|
String bridgeSNUUID = null;
|
||||||
|
HuePublicConfig aHueConfig = HuePublicConfig.createConfig("temp", responseAddress);
|
||||||
|
bridgeId = aHueConfig.getBridgeid();
|
||||||
|
bridgeSNUUID = aHueConfig.getSNUUIDFromMac();
|
||||||
|
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID);
|
||||||
|
if(traceupnp) {
|
||||||
|
log.info("Traceupnp: sendUpnpResponse discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
discoveryResponse = String.format(discoveryTemplateOld, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort);
|
log.debug("sendUpnpResponse discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
|
||||||
if(traceupnp)
|
theUDPDatagramSender.sendUDPResponse(discoveryResponse, requester, sourcePort);
|
||||||
log.info("Traceupnp: sendUpnpResponse discovery template with address: " + responseAddress + " and port: " + httpServerPort);
|
|
||||||
|
discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
|
||||||
|
if(traceupnp) {
|
||||||
|
log.info("Traceupnp: sendUpnpResponse discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
log.debug("sendUpnpResponse discovery template with address: " + responseAddress + " and port: " + httpServerPort);
|
log.debug("sendUpnpResponse discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
|
||||||
DatagramPacket response = new DatagramPacket(discoveryResponse.getBytes(), discoveryResponse.length(), requester, sourcePort);
|
theUDPDatagramSender.sendUDPResponse(discoveryResponse, requester, sourcePort);
|
||||||
socket.send(response);
|
|
||||||
|
discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID);
|
||||||
|
if(traceupnp) {
|
||||||
|
log.info("Traceupnp: sendUpnpResponse discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log.debug("sendUpnpResponse discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
|
||||||
|
theUDPDatagramSender.sendUDPResponse(discoveryResponse, requester, sourcePort);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||||
|
import com.bwssystems.HABridge.api.hue.HuePublicConfig;
|
||||||
|
|
||||||
import static spark.Spark.get;
|
import static spark.Spark.get;
|
||||||
|
|
||||||
@@ -31,8 +32,8 @@ public class UpnpSettingsResource {
|
|||||||
+ "<modelName>Philips hue bridge 2015</modelName>\n"
|
+ "<modelName>Philips hue bridge 2015</modelName>\n"
|
||||||
+ "<modelNumber>BSB002</modelNumber>\n"
|
+ "<modelNumber>BSB002</modelNumber>\n"
|
||||||
+ "<modelURL>http://www.meethue.com</modelURL>\n"
|
+ "<modelURL>http://www.meethue.com</modelURL>\n"
|
||||||
+ "<serialNumber>0017880ae670</serialNumber>\n"
|
+ "<serialNumber>%s</serialNumber>\n"
|
||||||
+ "<UDN>uuid:2f402f80-da50-11e1-9b23-001788102201</UDN>\n"
|
+ "<UDN>uuid:2f402f80-da50-11e1-9b23-%s</UDN>\n"
|
||||||
+ "<serviceList>\n"
|
+ "<serviceList>\n"
|
||||||
+ "<service>\n"
|
+ "<service>\n"
|
||||||
+ "<serviceType>(null)</serviceType>\n"
|
+ "<serviceType>(null)</serviceType>\n"
|
||||||
@@ -77,7 +78,8 @@ public class UpnpSettingsResource {
|
|||||||
log.debug("upnp device settings requested: " + " from " + request.ip() + ":" + request.port());
|
log.debug("upnp device settings requested: " + " from " + request.ip() + ":" + request.port());
|
||||||
String portNumber = Integer.toString(request.port());
|
String portNumber = Integer.toString(request.port());
|
||||||
String filledTemplate = null;
|
String filledTemplate = null;
|
||||||
filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress());
|
String bridgeIdMac = HuePublicConfig.createConfig("temp", theSettings.getUpnpConfigAddress()).getSNUUIDFromMac();
|
||||||
|
filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress(), bridgeIdMac, bridgeIdMac);
|
||||||
if(theSettings.isTraceupnp())
|
if(theSettings.isTraceupnp())
|
||||||
log.info("Traceupnp: upnp device settings template filled with address: " + theSettings.getUpnpConfigAddress() + " and port: " + portNumber);
|
log.info("Traceupnp: upnp device settings template filled with address: " + theSettings.getUpnpConfigAddress() + " and port: " + portNumber);
|
||||||
else
|
else
|
||||||
|
|||||||
72
src/main/java/com/bwssystems/util/UDPDatagramSender.java
Normal file
72
src/main/java/com/bwssystems/util/UDPDatagramSender.java
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package com.bwssystems.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.SocketException;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class UDPDatagramSender {
|
||||||
|
private Logger log = LoggerFactory.getLogger(UDPDatagramSender.class);
|
||||||
|
private DatagramSocket responseSocket = null;
|
||||||
|
private int udpResponsePort;
|
||||||
|
|
||||||
|
public UDPDatagramSender() {
|
||||||
|
super();
|
||||||
|
udpResponsePort = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UDPDatagramSender createUDPDatagramSender(int udpResponsePort) {
|
||||||
|
UDPDatagramSender aDatagramSender = new UDPDatagramSender();
|
||||||
|
if(aDatagramSender.initializeSocket(udpResponsePort))
|
||||||
|
return aDatagramSender;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean initializeSocket(int port) {
|
||||||
|
log.info("Initializing UDP response Seocket...");
|
||||||
|
udpResponsePort = port;
|
||||||
|
boolean portLoopControl = true;
|
||||||
|
int retryCount = 0;
|
||||||
|
while(portLoopControl) {
|
||||||
|
try {
|
||||||
|
responseSocket = new DatagramSocket(udpResponsePort);
|
||||||
|
portLoopControl = false;
|
||||||
|
} catch(SocketException e) {
|
||||||
|
if(retryCount == 0)
|
||||||
|
log.warn("UDP Response Port is in use, starting loop to find open port for 20 tries - configured port is: " + udpResponsePort);
|
||||||
|
if(retryCount >= 20) {
|
||||||
|
portLoopControl = false;
|
||||||
|
log.error("UDP Response Port issue, could not find open port - last port tried: " + udpResponsePort + " with message: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(portLoopControl) {
|
||||||
|
retryCount++;
|
||||||
|
udpResponsePort++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("UDP response Seocket initialized to: " + udpResponsePort);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUdpResponsePort() {
|
||||||
|
return udpResponsePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeResponseSocket() {
|
||||||
|
responseSocket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendUDPResponse(String udpResponse, InetAddress requester, int sourcePort) throws IOException {
|
||||||
|
log.debug("Sending response string: <<<" + udpResponse + ">>>");
|
||||||
|
if(responseSocket == null)
|
||||||
|
throw new IOException("Socket not initialized");
|
||||||
|
DatagramPacket response = new DatagramPacket(udpResponse.getBytes(), udpResponse.length(), requester, sourcePort);
|
||||||
|
responseSocket.send(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -115,12 +115,24 @@ app.service('bridgeService', function ($http, $window, ngToast) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.renumberDevices = function () {
|
||||||
|
return $http.post(this.state.base + "/exec/renumber").then(
|
||||||
|
function (response) {
|
||||||
|
self.viewDevices();
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
self.displayError("Cannot renumber devices from habridge: ", error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
this.clearDevice = function () {
|
this.clearDevice = function () {
|
||||||
if(self.state.device == null)
|
if(self.state.device == null)
|
||||||
self.state.device = [];
|
self.state.device = [];
|
||||||
self.state.device.id = "";
|
self.state.device.id = "";
|
||||||
self.state.device.mapType = null;
|
self.state.device.mapType = null;
|
||||||
self.state.device.mapId = null;
|
self.state.device.mapId = null;
|
||||||
|
self.state.device.uniqueid = null;
|
||||||
self.state.device.name = "";
|
self.state.device.name = "";
|
||||||
self.state.device.onUrl = "";
|
self.state.device.onUrl = "";
|
||||||
self.state.device.dimUrl = "";
|
self.state.device.dimUrl = "";
|
||||||
@@ -131,6 +143,7 @@ app.service('bridgeService', function ($http, $window, ngToast) {
|
|||||||
self.state.device.httpVerb = null;
|
self.state.device.httpVerb = null;
|
||||||
self.state.device.contentType = null;
|
self.state.device.contentType = null;
|
||||||
self.state.device.contentBody = null;
|
self.state.device.contentBody = null;
|
||||||
|
self.state.device.contentBodyDim = null;
|
||||||
self.state.device.contentBodyOff = null;
|
self.state.device.contentBodyOff = null;
|
||||||
self.state.olddevicename = "";
|
self.state.olddevicename = "";
|
||||||
};
|
};
|
||||||
@@ -784,6 +797,9 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
|
|||||||
bridgeService.editDevice(device);
|
bridgeService.editDevice(device);
|
||||||
$location.path('/editdevice');
|
$location.path('/editdevice');
|
||||||
};
|
};
|
||||||
|
$scope.renumberDevices = function() {
|
||||||
|
bridgeService.renumberDevices();
|
||||||
|
};
|
||||||
$scope.backupDeviceDb = function (optionalbackupname) {
|
$scope.backupDeviceDb = function (optionalbackupname) {
|
||||||
bridgeService.backupDeviceDb(optionalbackupname);
|
bridgeService.backupDeviceDb(optionalbackupname);
|
||||||
};
|
};
|
||||||
@@ -976,6 +992,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
|
|||||||
httpVerb: $scope.device.httpVerb,
|
httpVerb: $scope.device.httpVerb,
|
||||||
contentType: $scope.device.contentType,
|
contentType: $scope.device.contentType,
|
||||||
contentBody: $scope.device.contentBody,
|
contentBody: $scope.device.contentBody,
|
||||||
|
contentBodyDim: $scope.device.contentBodyDim,
|
||||||
contentBodyOff: $scope.device.contentBodyOff
|
contentBodyOff: $scope.device.contentBodyOff
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1309,6 +1326,7 @@ app.controller('HueController', function ($scope, $location, $http, bridgeServic
|
|||||||
httpVerb: $scope.device.httpVerb,
|
httpVerb: $scope.device.httpVerb,
|
||||||
contentType: $scope.device.contentType,
|
contentType: $scope.device.contentType,
|
||||||
contentBody: $scope.device.contentBody,
|
contentBody: $scope.device.contentBody,
|
||||||
|
contentBodyDim: $scope.device.contentBodyDim,
|
||||||
contentBodyOff: $scope.device.contentBodyOff
|
contentBodyOff: $scope.device.contentBodyOff
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1627,6 +1645,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
|
|||||||
httpVerb: $scope.device.httpVerb,
|
httpVerb: $scope.device.httpVerb,
|
||||||
contentType: $scope.device.contentType,
|
contentType: $scope.device.contentType,
|
||||||
contentBody: $scope.device.contentBody,
|
contentBody: $scope.device.contentBody,
|
||||||
|
contentBodyDim: $scope.device.contentBodyDim,
|
||||||
contentBodyOff: $scope.device.contentBodyOff
|
contentBodyOff: $scope.device.contentBodyOff
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,11 @@
|
|||||||
<h2 class="panel-title">Current devices
|
<h2 class="panel-title">Current devices
|
||||||
({{bridge.devices.length}})</h2>
|
({{bridge.devices.length}})</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<form name="form">
|
||||||
|
<p>
|
||||||
|
<button class="btn btn-primary" type="submit" ng-click="renumberDevices()">Renumber Devices</button>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
<scrollable-table watch="bridge.devices">
|
<scrollable-table watch="bridge.devices">
|
||||||
<table class="table table-bordered table-striped table-hover">
|
<table class="table table-bordered table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@@ -107,6 +107,14 @@
|
|||||||
Clear Device</button>
|
Clear Device</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-xs-12 col-sm-2 control-label" for="device-unique-id">Unique Id (used for Hue responses) </label>
|
||||||
|
|
||||||
|
<div class="col-xs-8 col-sm-7">
|
||||||
|
<input type="text" class="form-control" id="device-unique-id"
|
||||||
|
ng-model="device.uniqueid" placeholder="AA:BB:CC:DD:EE:FF-XX" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div ng-if="device.mapType" class="form-group">
|
<div ng-if="device.mapType" class="form-group">
|
||||||
<label class="col-xs-12 col-sm-2 control-label" for="device-map-id">Map
|
<label class="col-xs-12 col-sm-2 control-label" for="device-map-id">Map
|
||||||
ID </label>
|
ID </label>
|
||||||
@@ -218,6 +226,19 @@
|
|||||||
<div class="clearfix visible-xs"></div>
|
<div class="clearfix visible-xs"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div ng-if="device.httpVerb" class="form-group">
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-xs-12 col-sm-2 control-label"
|
||||||
|
for="device-content-body-dim">Content Body Dim</label>
|
||||||
|
|
||||||
|
<div class="col-xs-8 col-sm-7">
|
||||||
|
<textarea rows="3" class="form-control" id="device-content-body-dim"
|
||||||
|
ng-model="device.contentBodyDim"
|
||||||
|
placeholder="Content Body Dim for specific GET/PUT/POST type"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div ng-if="device.httpVerb" class="form-group">
|
<div ng-if="device.httpVerb" class="form-group">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label"
|
<label class="col-xs-12 col-sm-2 control-label"
|
||||||
|
|||||||
@@ -233,6 +233,19 @@
|
|||||||
<div class="clearfix visible-xs"></div>
|
<div class="clearfix visible-xs"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div ng-if="device.httpVerb" class="form-group">
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-xs-12 col-sm-2 control-label"
|
||||||
|
for="device-content-body-dim">Content Body Dim</label>
|
||||||
|
|
||||||
|
<div class="col-xs-8 col-sm-7">
|
||||||
|
<textarea rows="3" class="form-control" id="device-content-body-dim"
|
||||||
|
ng-model="device.contentBodyDim"
|
||||||
|
placeholder="Content Body Dim for specific GET/PUT/POST type"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix visible-xs"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div ng-if="device.httpVerb" class="form-group">
|
<div ng-if="device.httpVerb" class="form-group">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label class="col-xs-12 col-sm-2 control-label"
|
<label class="col-xs-12 col-sm-2 control-label"
|
||||||
|
|||||||
Reference in New Issue
Block a user