Updtated bridge to be able to emulate the armzilla upnp responses.

Adding optional field edits - in progress.
This commit is contained in:
Admin
2015-09-02 13:06:59 -05:00
parent 392a46c3d8
commit 68de92bb74
9 changed files with 167 additions and 47 deletions

View File

@@ -1,42 +1,40 @@
# ha-bridge
Emulates philips hue api to other home automation gateways. The Amazon echo now supports wemo and philips hue.
Build
-----
To customize and build it yourself, build a new jar with maven:
Emulates philips hue api to other home automation gateways. The Amazon echo now supports wemo and philips hue.
## Build
To customize and build it yourself, build a new jar with maven:
```
mvn install
```
Otherwise go to http://www.bwssystems.com/apps.html to download the latest jar file.
Run
----
Then locate the jar and start the server with:
Otherwise go to http://www.bwssystems.com/apps.html to download the latest jar file.
## Run
Then locate the jar and start the server with:
```
java -jar -Dvera.address=192.168.X.Y ha-bridge-0.X.Y.jar
```
``'
## Available Arguments
### -Dvera.address=<ip address>
The argument for the vera address should be given as it the system does not have a way to find the address. Supply -Dvera.address=X.Y.Z.A on the command line to provide it.
### -Dupnp.config.address=<ip address>
The server defaults to the first available address on the host. Replace the -Dupnp.config.address=<ip address> value with the server ipv4 address you would like to use.
### -Dserver.port=<port>
The server defaults to running on port 8080. If you're already running a server (like openHAB) on 8080, -Dserver.port=<port> on the command line.
### -Dupnp.devices.db=<filepath>
The default location for the db to contain the devices as they are added is "data/devices.db". If you would like a different filename or directory, specify -Dupnp.devices.db=<directory>/<filename> or <filename> if it is the same directory.
The default upnp response port will be 50000 otherwise it can be set with -Dupnp.response.port=<port>.
Upnp has been very closed on this platform to try and respond as a hue and there is now a setting to control if it is more open or strict, Add -Dupnp.strict=<true|false> to your command line to have the emulator respond to what it thinks is an echo to a hue or any other device. The default is upnp.strict=true.
Then configure by going to the url for the host you are running on or localhost:
### -Dupnp.resonse.port=<port>
The upnp response port that will be used. The default is 50000.
### -Dupnp.strict=<true|false>
Upnp has been very closed on this platform to try and respond as a hue and there is now a setting to control if it is more open or strict, Add -Dupnp.strict=<true|false> to your command line to have the emulator respond to what it thinks is an echo to a hue or any other device. The default is upnp.strict=true.
### -Dtrace.upnp=<true|false>
Turn on tracing for upnp discovery messages. The default is false.
### -Dvtwo.compatibility=<true|false>
Turns on compatibility for upnp detection and response as it was in the original version of amazon-echo-ha-bridge. The default is true.
## Web Config
Configure by going to the url for the host you are running on or localhost with port you have assigned:
```
http://192.168.1.240:8080
http://<ip address>:<port>
```
Command line configure
----
Register a device via REST by by using a tool and the url, for example:
## Command line configure
Register a device via REST by by using a tool and the url, for example:
```
POST http://host:8080/api/devices
{
@@ -46,9 +44,8 @@ POST http://host:8080/api/devices
"offUrl" : "http://192.168.1.201:3480/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0&DeviceNum=41"
}
```
Dimming
----
Dimming is also supported by using the expessions ${intensity.percent} or ${intensity.byte} for 0-100 and 0-255 respectively.
## Dimming
Dimming is also supported by using the expessions ${intensity.percent} or ${intensity.byte} for 0-100 and 0-255 respectively.
e.g.
```
{
@@ -60,14 +57,13 @@ e.g.
```
See the echo's documentation for the dimming phrase.
POST/PUT support
-----
## POST/PUT support
added optional fields
* contentType (currently un-validated)
* httpVerb (POST/PUT/GET only supported)
* contentBody your post/put body here
This will allow control of any other application that may need mroe then GET.
This will allow control of any other application that may need more then GET.
e.g:
```
{
@@ -80,7 +76,7 @@ e.g:
"contentBody" : "{\"fooBar\":\"baz\"}"
}
```
Anything that takes an action as a result of an HTTP request will probably work - like putting Vera in and out of night mode:
Anything that takes an action as a result of an HTTP request will probably work - like putting Vera in and out of night mode:
```
{
"name": "night mode",
@@ -89,10 +85,9 @@ Anything that takes an action as a result of an HTTP request will probably work
"onUrl": "http://192.168.1.201:3480/data_request?id=lu_action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=SetHouseMode&Mode=3"
}
```
Ask Alexa
----
After this Tell Alexa: "Alexa, discover my devices"
## Ask Alexa
After this Tell Alexa: "Alexa, discover my devices"
Then you can say "Alexa, Turn on the office light" or whatever name you have given your configured devices.
Then you can say "Alexa, Turn on the office light" or whatever name you have given your configured devices.
To view or remove devices that Alexa knows about, you can use the mobile app Menu / Settings / Connected Home
To view or remove devices that Alexa knows about, you can use the mobile app Menu / Settings / Connected Home

View File

@@ -5,7 +5,7 @@
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>0.4.0</version>
<version>0.4.1</version>
<packaging>jar</packaging>
<name>HA Bridge</name>

View File

@@ -7,6 +7,8 @@ public class BridgeSettings {
private String upnpdevicedb;
private String veraaddress;
private boolean upnpStrict;
private boolean traceupnp;
private boolean vtwocompatibility;
public String getUpnpConfigAddress() {
return upnpconfigaddress;
@@ -45,4 +47,18 @@ public class BridgeSettings {
public void setUpnpStrict(boolean upnpStrict) {
this.upnpStrict = upnpStrict;
}
public boolean isTraceupnp() {
return traceupnp;
}
public void setTraceupnp(boolean traceupnp) {
this.traceupnp = traceupnp;
}
public boolean isVtwocompatibility() {
return vtwocompatibility;
}
public void setVtwocompatibility(boolean vtwocompatibility) {
this.vtwocompatibility = vtwocompatibility;
}
}

View File

@@ -55,6 +55,8 @@ public class HABridge {
bridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", "50000"));
bridgeSettings.setVeraAddress(System.getProperty("vera.address", "192.168.1.100"));
bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("vtwo.compatibility", "true")));
// sparkjava config directive to set ip address for the web server to listen on
// ipAddress("0.0.0.0"); // not used

View File

@@ -61,7 +61,7 @@ public class DeviceResource {
deviceRepository.save(device);
log.debug("Created a Device: " + request.body());
response.status(HttpStatus.SC_OK);
response.status(HttpStatus.SC_CREATED);
}
return device;
}, new JsonTransformer());

View File

@@ -24,6 +24,10 @@ public class UpnpListener {
private String responseAddress;
private boolean strict;
private boolean traceupnp;
private boolean vTwoCompatibility;
public UpnpListener(BridgeSettings theSettings) {
super();
@@ -31,6 +35,8 @@ public class UpnpListener {
httpServerPort = Integer.valueOf(theSettings.getServerPort());
responseAddress = theSettings.getUpnpConfigAddress();
strict = theSettings.isUpnpStrict();
traceupnp = theSettings.isTraceupnp();
vTwoCompatibility = theSettings.isVtwocompatibility();
}
public void startListening(){
@@ -66,8 +72,12 @@ public class UpnpListener {
DatagramPacket packet = new DatagramPacket(buf, buf.length);
upnpMulticastSocket.receive(packet);
String packetString = new String(packet.getData());
if(packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1"))
log.debug("Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + " body : " + packetString);
if(packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1")) {
if(traceupnp)
log.info("Trace SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + " body : " + packetString);
else
log.debug("Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + " body : " + packetString);
}
if(isSSDPDiscovery(packetString)){
sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort());
}
@@ -92,7 +102,7 @@ public class UpnpListener {
if(body != null && body.startsWith("M-SEARCH * HTTP/1.1") && body.contains("MAN: \"ssdp:discover\"")){
if(strict && body.contains("ST: urn:schemas-upnp-org:device:basic:1"))
return true;
else if (!strict)
else if (!strict || vTwoCompatibility)
return true;
}
return false;
@@ -105,8 +115,20 @@ public class UpnpListener {
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.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";
String discoveryTemplateVTwo = "HTTP/1.1 200 OK\r\n" +
"CACHE-CONTROL: max-age=86400\r\n" +
"EXT:\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" +
"01-NLS: %s\r\n" +
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
"USN: uuid:Socket-1_0-221438K0100073::urn:Belkin:device:**\r\n\r\n";
protected void sendUpnpResponse(DatagramSocket socket, InetAddress requester, int sourcePort) throws IOException {
String discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort, getRandomUUIDString());
String discoveryResponse = null;
if(vTwoCompatibility)
discoveryResponse = String.format(discoveryTemplateVTwo, responseAddress, httpServerPort, getRandomUUIDString());
else
discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort, getRandomUUIDString());
log.debug("sndUpnpResponse: " + discoveryResponse);
DatagramPacket response = new DatagramPacket(discoveryResponse.getBytes(), discoveryResponse.length(), requester, sourcePort);
socket.send(response);

View File

@@ -38,6 +38,53 @@ public class UpnpSettingsResource {
+ "<depth>24</depth>\n" + "<url>hue_logo_3.png</url>\n" + "</icon>\n" + "</iconList>\n" + "</device>\n"
+ "</root>\n";
private String hueTemplateVTwo = "<?xml version=\"1.0\"?>\n" +
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n" +
"<specVersion>\n" +
"<major>1</major>\n" +
"<minor>0</minor>\n" +
"</specVersion>\n" +
"<URLBase>http://%s:%s/</URLBase>\n" + //hostname string
"<device>\n" +
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\n" +
"<friendlyName>Amazon-Echo-HA-Bridge (%s)</friendlyName>\n" +
"<manufacturer>Royal Philips Electronics</manufacturer>\n" +
"<manufacturerURL>http://www.bwssystems.com</manufacturerURL>\n" +
"<modelDescription>Hue Emulator for Amazon Echo bridge</modelDescription>\n" +
"<modelName>Philips hue bridge 2012</modelName>\n" +
"<modelNumber>929000226503</modelNumber>\n" +
"<modelURL>http://www.bwssystems.com/apps.html</modelURL>\n" +
"<serialNumber>01189998819991197253</serialNumber>\n" +
"<UDN>uuid:88f6698f-2c83-4393-bd03-cd54a9f8595</UDN>\n" +
"<serviceList>\n" +
"<service>\n" +
"<serviceType>(null)</serviceType>\n" +
"<serviceId>(null)</serviceId>\n" +
"<controlURL>(null)</controlURL>\n" +
"<eventSubURL>(null)</eventSubURL>\n" +
"<SCPDURL>(null)</SCPDURL>\n" +
"</service>\n" +
"</serviceList>\n" +
"<presentationURL>index.html</presentationURL>\n" +
"<iconList>\n" +
"<icon>\n" +
"<mimetype>image/png</mimetype>\n" +
"<height>48</height>\n" +
"<width>48</width>\n" +
"<depth>24</depth>\n" +
"<url>hue_logo_0.png</url>\n" +
"</icon>\n" +
"<icon>\n" +
"<mimetype>image/png</mimetype>\n" +
"<height>120</height>\n" +
"<width>120</width>\n" +
"<depth>24</depth>\n" +
"<url>hue_logo_3.png</url>\n" +
"</icon>\n" +
"</iconList>\n" +
"</device>\n" +
"</root>\n";
public UpnpSettingsResource(BridgeSettings theSettings) {
super();
setupListener(theSettings);
@@ -49,7 +96,11 @@ public class UpnpSettingsResource {
get("/description.xml", "application/xml; charset=utf-8", (request, response) -> {
log.debug("upnp device settings requested: " + request.params(":id") + " from " + request.ip());
String portNumber = Integer.toString(request.port());
String filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress());
String filledTemplate = null;
if(theSettings.isVtwocompatibility())
filledTemplate = String.format(hueTemplateVTwo, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress());
else
filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress());
log.debug("upnp device settings response: " + filledTemplate);
// response.header("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
// response.header("Pragma", "no-cache");

View File

@@ -36,7 +36,7 @@
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li><a href="http://www.bwssystems.com" target="_blank">Developed by BWS Systems</a></li>
<li><a href="http://www.amazon.com/echo" target="_blank">Amazon Echo</a></li>
<li><a href="#">HA Bridge Version 0.4.0</a></li>
<li><a href="#">HA Bridge Version 0.4.1</a></li>
</ul>
</li>
</ul>

View File

@@ -103,6 +103,40 @@
ng-click="testUrl(device.offUrl)">Test</button>
</div>
</div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-http-verb">Http Verb
</label>
<div class="col-xs-8 col-sm-7">
<input type="text" class="form-control" id="device-http-verb"
ng-model="device.httpVerb" placeholder="Http Verb, i.e. GET/PUT/POST">
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-content-type">Name
</label>
<div class="col-xs-8 col-sm-7">
<input type="text" class="form-control" id="device-content-type"
ng-model="device.contentType" placeholder="Content type, i.e. application/json">
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label"
for="device-content-body">Content Body </label>
<div class="col-xs-8 col-sm-7">
<textarea rows="3" class="form-control" id="device-content-body"
ng-model="device.contentBody" placeholder="Content Body for specific GET/PUT/POST type"></textarea>
</div>
<div class="clearfix visible-xs"></div>
</div>
</div>
</form>
</li>
</ul>