Continue adding HUE pass thru.

This commit is contained in:
Admin
2016-04-15 16:30:34 -05:00
parent 50c9369d71
commit 7a0946e3b7
11 changed files with 438 additions and 8 deletions

View File

@@ -22,6 +22,7 @@ import com.bwssystems.HABridge.dao.DeviceRepository;
import com.bwssystems.HABridge.dao.ErrorMessage; import com.bwssystems.HABridge.dao.ErrorMessage;
import com.bwssystems.NestBridge.NestHome; import com.bwssystems.NestBridge.NestHome;
import com.bwssystems.harmony.HarmonyHome; import com.bwssystems.harmony.HarmonyHome;
import com.bwssystems.hue.HueHome;
import com.bwssystems.luupRequests.Device; import com.bwssystems.luupRequests.Device;
import com.bwssystems.luupRequests.Scene; import com.bwssystems.luupRequests.Scene;
import com.bwssystems.util.JsonTransformer; import com.bwssystems.util.JsonTransformer;
@@ -38,6 +39,7 @@ public class DeviceResource {
private VeraHome veraHome; private VeraHome veraHome;
private HarmonyHome myHarmonyHome; private HarmonyHome myHarmonyHome;
private NestHome nestHome; private NestHome nestHome;
private HueHome hueHome;
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post")); private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
public DeviceResource(BridgeSettingsDescriptor theSettings, HarmonyHome theHarmonyHome, NestHome aNestHome) { public DeviceResource(BridgeSettingsDescriptor theSettings, HarmonyHome theHarmonyHome, NestHome aNestHome) {
@@ -57,6 +59,11 @@ public class DeviceResource {
this.nestHome = aNestHome; this.nestHome = aNestHome;
else else
this.nestHome = null; this.nestHome = null;
if(theSettings.isValidHue())
this.hueHome = new HueHome(theSettings);
else
this.hueHome = null;
setupEndpoints(); setupEndpoints();
} }
@@ -245,6 +252,16 @@ public class DeviceResource {
return nestHome.getItems(); return nestHome.getItems();
}, new JsonTransformer()); }, new JsonTransformer());
get (API_CONTEXT + "/hue/devices", "application/json", (request, response) -> {
log.debug("Get hue items");
if(hueHome == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return new ErrorMessage("A Hue is not available.");
}
response.status(HttpStatus.SC_OK);
return hueHome.getDevices();
}, 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);

View File

@@ -12,6 +12,7 @@ import com.bwssystems.harmony.ButtonPress;
import com.bwssystems.harmony.HarmonyHandler; import com.bwssystems.harmony.HarmonyHandler;
import com.bwssystems.harmony.HarmonyHome; import com.bwssystems.harmony.HarmonyHome;
import com.bwssystems.harmony.RunActivity; import com.bwssystems.harmony.RunActivity;
import com.bwssystems.hue.HueDeviceIdentifier;
import com.bwssystems.nest.controller.Nest; import com.bwssystems.nest.controller.Nest;
import com.bwssystems.util.JsonTransformer; import com.bwssystems.util.JsonTransformer;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -336,6 +337,23 @@ public class HueMulator {
return responseString; return responseString;
} }
responseString = this.formatSuccessHueResponse(state, request.body(), lightId);
if(device.getDeviceType().toLowerCase().contains("hue") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("hueDevice")))
{
url = device.getOnUrl();
HueDeviceIdentifier deviceId = new Gson().fromJson(url, HueDeviceIdentifier.class);
// make call
if (!doHttpRequest("http://"+deviceId.getIpAddress()+"/api/"+userId+"/lights/"+deviceId.getDeviceId(), HttpPut.METHOD_NAME, device.getContentType(), request.body())) {
log.warn("Error on calling url to change device state: " + url);
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling url to change device state\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
}
else
device.setDeviceState(state);
return responseString;
}
if(request.body().contains("bri")) if(request.body().contains("bri"))
{ {
url = device.getDimUrl(); url = device.getDimUrl();
@@ -360,7 +378,6 @@ public class HueMulator {
return responseString; return responseString;
} }
responseString = this.formatSuccessHueResponse(state, request.body(), lightId);
if(device.getDeviceType().toLowerCase().contains("activity") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyActivity"))) if(device.getDeviceType().toLowerCase().contains("activity") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyActivity")))
{ {

View File

@@ -0,0 +1,28 @@
package com.bwssystems.hue;
import com.bwssystems.HABridge.api.hue.DeviceResponse;
public class HueDevice {
private DeviceResponse device;
private String hubaddress;
private String hubname;
public DeviceResponse getDevice() {
return device;
}
public void setDevice(DeviceResponse device) {
this.device = device;
}
public String getHubaddress() {
return hubaddress;
}
public void setHubaddress(String hubaddress) {
this.hubaddress = hubaddress;
}
public String getHubname() {
return hubname;
}
public void setHubname(String hubname) {
this.hubname = hubname;
}
}

View File

@@ -0,0 +1,18 @@
package com.bwssystems.hue;
public class HueDeviceIdentifier {
private String ipAddress;
private String deviceId;
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
}

View File

@@ -0,0 +1,54 @@
package com.bwssystems.hue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.hue.DeviceResponse;
public class HueHome {
private static final Logger log = LoggerFactory.getLogger(HueHome.class);
private Map<String, HueInfo> hues;
public HueHome(BridgeSettingsDescriptor bridgeSettings) {
hues = new HashMap<String, HueInfo>();
if(!bridgeSettings.isValidHue())
return;
Iterator<NamedIP> theList = bridgeSettings.getVeraAddress().getDevices().iterator();
while(theList.hasNext()) {
NamedIP aHue = theList.next();
hues.put(aHue.getName(), new HueInfo(aHue));
}
}
public List<HueDevice> getDevices() {
log.debug("consolidating devices for hues");
Iterator<String> keys = hues.keySet().iterator();
ArrayList<HueDevice> deviceList = new ArrayList<HueDevice>();
while(keys.hasNext()) {
String key = keys.next();
Map<String, DeviceResponse> theDevices = hues.get(key).getHueApiResponse().getLights();
if(theDevices != null) {
Iterator<String> deviceKeys = theDevices.keySet().iterator();
while(deviceKeys.hasNext()) {
HueDevice aNewHueDevice = new HueDevice();
aNewHueDevice.setDevice(theDevices.get(deviceKeys.next()));
aNewHueDevice.setHubaddress("");
deviceList.add(aNewHueDevice);
}
}
else {
deviceList = null;
break;
}
}
return deviceList;
}
}

View File

@@ -0,0 +1,61 @@
package com.bwssystems.hue;
import java.io.IOException;
import java.nio.charset.Charset;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.hue.HueApiResponse;
import com.google.gson.Gson;
public class HueInfo {
private static final Logger log = LoggerFactory.getLogger(HueInfo.class);
private HttpClient httpClient;
private static final String HUE_REQUEST = "/api/habridge/config";
private NamedIP hueAddress;
public HueInfo(NamedIP addressName) {
super();
httpClient = HttpClients.createDefault();
hueAddress = addressName;
}
public HueApiResponse getHueApiResponse() {
HueApiResponse theHueApiResponse = null;
String theUrl = "http://" + hueAddress.getIp() + HUE_REQUEST;
String theData;
theData = doHttpGETRequest(theUrl);
if(theData != null) {
theHueApiResponse = new Gson().fromJson(theData, HueApiResponse.class);
log.debug("GET HueApiResponse - name: " + theHueApiResponse.getConfig().getName() + ", mac addr: " + theHueApiResponse.getConfig().getMac());
}
return theHueApiResponse;
}
// This function executes the url against the vera
protected String doHttpGETRequest(String url) {
String theContent = null;
log.debug("calling GET on URL: " + url);
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse response = httpClient.execute(httpGet);
log.debug("GET on URL responded: " + response.getStatusLine().getStatusCode());
if(response.getStatusLine().getStatusCode() == 200){
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); //read content for data
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
}
} catch (IOException e) {
log.error("doHttpGETRequest: Error calling out to HA gateway: " + e.getMessage());
}
return theContent;
}
}

View File

@@ -26,7 +26,7 @@ public class VeraHome {
Iterator<NamedIP> theList = bridgeSettings.getVeraAddress().getDevices().iterator(); Iterator<NamedIP> theList = bridgeSettings.getVeraAddress().getDevices().iterator();
while(theList.hasNext()) { while(theList.hasNext()) {
NamedIP aVera = theList.next(); NamedIP aVera = theList.next();
veras.put(aVera.getName(), new VeraInfo(aVera, bridgeSettings.isValidVera())); veras.put(aVera.getName(), new VeraInfo(aVera));
} }
} }

View File

@@ -28,19 +28,15 @@ public class VeraInfo {
private HttpClient httpClient; private HttpClient httpClient;
private static final String SDATA_REQUEST = ":3480/data_request?id=sdata&output_format=json"; private static final String SDATA_REQUEST = ":3480/data_request?id=sdata&output_format=json";
private NamedIP veraAddress; private NamedIP veraAddress;
private Boolean validVera;
public VeraInfo(NamedIP addressName, Boolean isValidVera) { public VeraInfo(NamedIP addressName) {
super(); super();
httpClient = HttpClients.createDefault(); httpClient = HttpClients.createDefault();
veraAddress = addressName; veraAddress = addressName;
validVera = isValidVera;
} }
public Sdata getSdata() { public Sdata getSdata() {
Sdata theSdata = null; Sdata theSdata = null;
if(!validVera)
return theSdata;
String theUrl = "http://" + veraAddress.getIp() + SDATA_REQUEST; String theUrl = "http://" + veraAddress.getIp() + SDATA_REQUEST;
String theData; String theData;

View File

@@ -44,7 +44,7 @@ app.run( function (bridgeService) {
app.service('bridgeService', function ($http, $window, ngToast) { app.service('bridgeService', function ($http, $window, ngToast) {
var self = this; var self = this;
this.state = {base: window.location.origin + "/api/devices", bridgelocation: window.location.origin, systemsbase: window.location.origin + "/system", huebase: window.location.origin + "/api", configs: [], backups: [], devices: [], device: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, habridgeversion: ""}; this.state = {base: window.location.origin + "/api/devices", bridgelocation: window.location.origin, systemsbase: window.location.origin + "/system", huebase: window.location.origin + "/api", configs: [], backups: [], devices: [], device: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, habridgeversion: ""};
this.displayWarn = function(errorTitle, error) { this.displayWarn = function(errorTitle, error) {
var toastContent = errorTitle; var toastContent = errorTitle;
@@ -140,6 +140,11 @@ app.service('bridgeService', function ($http, $window, ngToast) {
return; return;
} }
this.updateShowHue = function () {
this.state.showHue = self.state.settings.hueconfigured;
return;
}
this.loadBridgeSettings = function () { this.loadBridgeSettings = function () {
return $http.get(this.state.systemsbase + "/settings").then( return $http.get(this.state.systemsbase + "/settings").then(
function (response) { function (response) {
@@ -147,6 +152,7 @@ app.service('bridgeService', function ($http, $window, ngToast) {
self.updateShowVera(); self.updateShowVera();
self.updateShowHarmony(); self.updateShowHarmony();
self.updateShowNest(); self.updateShowNest();
self.updateShowHue();
}, },
function (error) { function (error) {
self.displayWarn("Load Bridge Settings Error: ", error); self.displayWarn("Load Bridge Settings Error: ", error);
@@ -211,6 +217,19 @@ app.service('bridgeService', function ($http, $window, ngToast) {
); );
}; };
this.viewHueDevices = function () {
if(!this.state.showHue)
return;
return $http.get(this.state.base + "/hue/devices").then(
function (response) {
self.state.huedevices = response.data;
},
function (error) {
self.displayWarn("Get Hue Items Error: ", error);
}
);
};
this.viewVeraDevices = function () { this.viewVeraDevices = function () {
if(!this.state.showVera) if(!this.state.showVera)
return; return;
@@ -1079,6 +1098,105 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
}); });
app.controller('HueController', function ($scope, $location, $http, bridgeService) {
$scope.bridge = bridgeService.state;
$scope.device = $scope.bridge.device;
$scope.bulk = { devices: [] };
bridgeService.viewHueDevices();
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
$scope.buttonsVisible = false;
$scope.clearDevice = function () {
bridgeService.clearDevice();
};
$scope.buildDeviceUrls = function (huedevice) {
bridgeService.clearDevice();
$scope.device.deviceType = "switch";
$scope.device.name = huedevice.name;
$scope.device.targetDevice = huedevice.huename;
$scope.device.mapType = "hueDevice";
$scope.device.mapId = huedevice.id;
$scope.device.onUrl = "http://" + veradevice.veraaddress + ":" + $scope.vera.port
+ "/data_request?id=action&output_format=json&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1&DeviceNum="
+ veradevice.id;
};
$scope.addDevice = function () {
if($scope.device.name == "" && $scope.device.onUrl == "")
return;
bridgeService.addDevice($scope.device).then(
function () {
$scope.clearDevice();
bridgeService.viewDevices();
bridgeService.viewHueDevices();
},
function (error) {
bridgeService.displayWarn("Error adding device: " + $scope.device.name, error)
}
);
};
$scope.bulkAddDevices = function() {
var devicesList = [];
for(var i = 0; i < $scope.bulk.devices.length; i++) {
for(var x = 0; x < bridgeService.state.huedevices.length; x++) {
if(bridgeService.state.huedevices[x].id == $scope.bulk.devices[i]) {
$scope.buildDeviceUrls(bridgeService.state.huedevices[x]);
devicesList[i] = {
name: $scope.device.name,
mapId: $scope.device.mapId,
mapType: $scope.device.mapType,
deviceType: $scope.device.deviceType,
targetDevice: $scope.device.targetDevice,
onUrl: $scope.device.onUrl,
offUrl: $scope.device.offUrl,
httpVerb: $scope.device.httpVerb,
contentType: $scope.device.contentType,
contentBody: $scope.device.contentBody,
contentBodyOff: $scope.device.contentBodyOff
};
}
}
}
bridgeService.bulkAddDevice(devicesList);
$scope.clearDevice();
bridgeService.viewDevices();
bridgeService.viewHueDevices();
$scope.bulk = { devices: [] };
};
$scope.toggleSelection = function toggleSelection(deviceId) {
var idx = $scope.bulk.devices.indexOf(deviceId);
// is currently selected
if (idx > -1) {
$scope.bulk.devices.splice(idx, 1);
}
// is newly selected
else {
$scope.bulk.devices.push(deviceId);
}
};
$scope.toggleButtons = function () {
$scope.buttonsVisible = !$scope.buttonsVisible;
if($scope.buttonsVisible)
$scope.imgButtonsUrl = "glyphicon glyphicon-minus";
else
$scope.imgButtonsUrl = "glyphicon glyphicon-plus";
};
$scope.deleteDeviceByMapId = function (id, mapType) {
bridgeService.deleteDeviceByMapId(id, mapType);
bridgeService.viewDevices();
bridgeService.viewHueDevices();
};
});
app.controller('EditController', function ($scope, $location, $http, bridgeService) { app.controller('EditController', function ($scope, $location, $http, bridgeService) {
$scope.bridge = bridgeService.state; $scope.bridge = bridgeService.state;
$scope.device = $scope.bridge.device; $scope.device = $scope.bridge.device;

View File

@@ -7,6 +7,7 @@
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li> <li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li> <li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li> <li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li ng-if="bridge.showHue" role="presentation"><a href="#/huedevices">Hue Devices</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li> <li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul> </ul>

View File

@@ -0,0 +1,120 @@
<ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#">Bridge Devices</a></li>
<li role="presentation"><a href="#/system">Bridge Control</a></li>
<li role="presentation"><a href="#/logs">Logs</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
<li role="presentation" class="active"><a href="#/huedevices">Hue Devices</a></li>
<li role="presentation"><a href="#/editor">Manual Add</a></li>
</ul>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading">
<h2 class="panel-title">Hue Device List ({{bridge.huedevices.length}})</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
<p class="text-muted">For any Hue Device, use the action buttons to generate the device addition information below automatically.
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
The 'Already Configured Hue Devices' list below will show what is already setup for your Hue.</p>
<p>Use the check boxes by the names to use the bulk addition feature. Select your items, then click bulk add below.
Your items will be added with on and off or dim and off if selected with the name of the device from the Hue.
</p>
<scrollable-table watch="bridge.huedevices">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th sortable-header col="name">Name</th>
<th sortable-header col="id">Id</th>
<th sortable-header col="huename">Hue</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="huedevice in bridge.huedevices | availableHueDeviceId">
<td>{{$index+1}}</td>
<td><input type="checkbox" name="bulk.devices[]" value="{{huedevice.id}}" ng-checked="bulk.devices.indexOf(huedevice.id) > -1" ng-click="toggleSelection(huedevice.id)"> {{huedevice.name}}</td>
<td>{{huedevice.uniqueid}}</td>
<td>{{huedevice.huename}}</td>
<td>
<button class="btn btn-success" type="submit"
ng-click="buildDeviceUrls(huedevice)">Generate
Device URLs</button>
</td>
</tr>
</table>
</scrollable-table>
<p>
<button class="btn btn-success" type="submit"
ng-click="bulkHueDevices()">Bulk Add ({{bulk.devices.length}})</button>
</p>
</li>
</ul>
<div class="panel-heading">
<h2 class="panel-title">Already Configured Hue Devices <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}} aria-hidden="true"></span></a></a></h2>
</div>
<ul ng-if="buttonsVisible" class="list-group">
<li class="list-group-item">
<scrollable-table watch="bridge.huedevices">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th sortable-header col="name">Name</th>
<th sortable-header col="id">Id</th>
<th sortable-header col="huename">hue</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="huedevice in bridge.huedevices | unavailableHueDeviceId">
<td>{{$index+1}}</td>
<td>{{huedevice.name}}</td>
<td>{{huedevice.uniqueid}}</td>
<td>{{huedevice.huename}}</td>
<td>
<button class="btn btn-danger" type="submit"
ng-click="deleteDeviceByMapId(huedevice.uniqueid, 'hueDevice')">Delete</button>
</td>
</tr>
</table>
</scrollable-table>
</li>
</ul>
</div>
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
<div class="panel-heading">
<h2 class="panel-title">Add Bridge Device for a Hue Device</h2>
</div>
<ul class="list-group">
<li class="list-group-item">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
</label>
<div class="col-xs-8 col-sm-7">
<input type="text" class="form-control" id="device-name"
ng-model="device.name" placeholder="Device Name">
</div>
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
Add Bridge Device</button>
</div>
<div class="form-group">
<div class="row">
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
URL </label>
<div class="col-xs-8 col-sm-7">
<textarea rows="3" class="form-control" id="device-on-url"
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
</div>
<button class="btn btn-danger" ng-click="clearDevice()">
Clear Device</button>
</div>
</form>
</li>
</ul>
</div>