mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-16 18:24:36 +00:00
Amazon Echo support for groups
So the groups are now somewhat usable with an Amazon Echo. This is a bit of a workaround: The group gets presented to the Echo as another fake light. For that to work you must manually enable this feature for every room by adding "exposeAsLight":"192.168.0.30" to the room in group.db (restart ha-bridge afterwards). Use the ip-address of your echo. No need to do that for other devices, because these can handle rooms directly. The fake light for the group will only be shown/usable to the specified ip-addresses.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.dao.GroupDescriptor;
|
||||
|
||||
/**
|
||||
* Created by arm on 4/14/15.
|
||||
@@ -129,4 +130,24 @@ public class DeviceResponse {
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public static DeviceResponse createResponseForVirtualLight(GroupDescriptor group){
|
||||
DeviceResponse response = new DeviceResponse();
|
||||
response.setState(group.getAction());
|
||||
|
||||
response.setName(group.getName());
|
||||
response.setUniqueid("00:17:88:5E:D3:FF-" + String.format("%02X", Integer.parseInt(group.getId())));
|
||||
response.setManufacturername("Philips");
|
||||
response.setType("Extended color light");
|
||||
response.setModelid("LCT010");
|
||||
response.setSwversion("1.15.2_r19181");
|
||||
response.setSwconfigid("F921C859");
|
||||
response.setProductid("Philips-LCT010-1-A19ECLv4");
|
||||
|
||||
response.setLuminaireuniqueid(null);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -70,6 +70,9 @@ public class GroupResponse {
|
||||
Boolean any_on = false;
|
||||
int i = 0;
|
||||
for (Map.Entry<String, DeviceResponse> device : deviceList.entrySet()) {
|
||||
if (Integer.parseInt(device.getKey()) >= 10000) { // don't show fake lights for other groups
|
||||
continue;
|
||||
}
|
||||
theList[i] = device.getKey();
|
||||
Boolean is_on = device.getValue().getState().isOn();
|
||||
if (is_on)
|
||||
|
||||
@@ -124,10 +124,14 @@ public class DeviceRepository extends BackupHandler {
|
||||
}
|
||||
|
||||
public Map<String, DeviceResponse> findAllByGroupWithState(String[] lightsInGroup, String anAddress, HueHome myHueHome, Gson aGsonBuilder) {
|
||||
return findAllByGroupWithState(lightsInGroup, anAddress, myHueHome, aGsonBuilder, false);
|
||||
}
|
||||
|
||||
public Map<String, DeviceResponse> findAllByGroupWithState(String[] lightsInGroup, String anAddress, HueHome myHueHome, Gson aGsonBuilder, boolean ignoreAddress) {
|
||||
Map<String, DeviceResponse> deviceResponseMap = new HashMap<String, DeviceResponse>();
|
||||
Map<String, DeviceDescriptor> lights = new HashMap<String, DeviceDescriptor>(devices);
|
||||
lights.keySet().retainAll(Arrays.asList(lightsInGroup));
|
||||
for (DeviceDescriptor light : findAllByRequester(anAddress, lights.values())) {
|
||||
for (DeviceDescriptor light : (ignoreAddress ? lights.values() : findAllByRequester(anAddress, lights.values()))) {
|
||||
DeviceResponse deviceResponse = null;
|
||||
if(!light.isInactive()) {
|
||||
if (light.containsType(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
|
||||
|
||||
@@ -34,15 +34,16 @@ public class GroupDescriptor{
|
||||
@SerializedName("comments")
|
||||
@Expose
|
||||
private String comments;
|
||||
@SerializedName("action")
|
||||
@Expose
|
||||
|
||||
private DeviceState action;
|
||||
@SerializedName("groupState")
|
||||
@Expose
|
||||
private GroupState groupState;
|
||||
|
||||
@SerializedName("lights")
|
||||
@Expose
|
||||
private String[] lights;
|
||||
@SerializedName("exposeAsLight")
|
||||
@Expose
|
||||
private String exposeAsLight;
|
||||
|
||||
|
||||
public String getName() {
|
||||
@@ -136,4 +137,12 @@ public class GroupDescriptor{
|
||||
public void setLights(String[] lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
|
||||
public void setExposeAsLight(String exposeFor) {
|
||||
this.exposeAsLight = exposeFor;
|
||||
}
|
||||
|
||||
public String getExposeAsLight() {
|
||||
return exposeAsLight;
|
||||
}
|
||||
}
|
||||
@@ -115,6 +115,17 @@ public class GroupRepository extends BackupHandler {
|
||||
return theReturnList;
|
||||
}
|
||||
|
||||
public List<GroupDescriptor> findVirtualLights(String anAddress) {
|
||||
List<GroupDescriptor> list = new ArrayList<GroupDescriptor>();
|
||||
for (GroupDescriptor group : groups.values()) {
|
||||
String expose = group.getExposeAsLight();
|
||||
if (expose != null && expose.contains(anAddress)) {
|
||||
list.add(group);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public GroupDescriptor findOne(String id) {
|
||||
return groups.get(id);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ public class HueMulator {
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.type("application/json");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return changeGroupState(request.params(":userid"), request.params(":groupid"), request.body(), request.ip());
|
||||
return changeGroupState(request.params(":userid"), request.params(":groupid"), request.body(), request.ip(), false);
|
||||
});
|
||||
// http://ip_address:port/api/{userId}/scenes returns json objects of
|
||||
// all scenes configured
|
||||
@@ -440,7 +440,7 @@ public class HueMulator {
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.type("application/json");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return changeState(request.params(":userid"), request.params(":id"), request.body(), request.ip());
|
||||
return changeState(request.params(":userid"), request.params(":id"), request.body(), request.ip(), false);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -890,6 +890,13 @@ public class HueMulator {
|
||||
deviceResponseMap.put(device.getId(), deviceResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// handle groups which shall be exposed as fake lights to selected devices like amazon echos
|
||||
List<GroupDescriptor> groups = groupRepository.findVirtualLights(requestIp);
|
||||
for (GroupDescriptor group : groups) {
|
||||
deviceResponseMap.put(String.valueOf(Integer.parseInt(group.getId()) + 10000),
|
||||
DeviceResponse.createResponseForVirtualLight(group));
|
||||
}
|
||||
}
|
||||
|
||||
if (theErrors != null)
|
||||
@@ -991,6 +998,11 @@ public class HueMulator {
|
||||
if (theErrors != null)
|
||||
return theErrors;
|
||||
|
||||
if (Integer.parseInt(lightId) >= 10000) {
|
||||
GroupDescriptor group = groupRepository.findOne(String.valueOf(Integer.parseInt(lightId) - 10000));
|
||||
return DeviceResponse.createResponseForVirtualLight(group);
|
||||
}
|
||||
|
||||
DeviceDescriptor device = repository.findOne(lightId);
|
||||
if (device == null) {
|
||||
// response.status(HttpStatus.SC_NOT_FOUND);
|
||||
@@ -1069,7 +1081,10 @@ public class HueMulator {
|
||||
return responseString;
|
||||
}
|
||||
|
||||
private String changeState(String userId, String lightId, String body, String ipAddress) {
|
||||
private String changeState(String userId, String lightId, String body, String ipAddress, boolean ignoreRequester) {
|
||||
if (Integer.parseInt(lightId) >= 10000) {
|
||||
return changeGroupState(userId, String.valueOf(Integer.parseInt(lightId) - 10000), body, ipAddress, true);
|
||||
}
|
||||
String responseString = null;
|
||||
String url = null;
|
||||
StateChangeBody theStateChanges = null;
|
||||
@@ -1161,10 +1176,13 @@ public class HueMulator {
|
||||
}
|
||||
|
||||
for (int i = 0; callItems != null && i < callItems.length; i++) {
|
||||
if(!filterByRequester(device.getRequesterAddress(), ipAddress) || !filterByRequester(callItems[i].getFilterIPs(), ipAddress)) {
|
||||
log.warn("filter for requester address not present in: (device)" + device.getRequesterAddress() + " OR then (item)" + callItems[i].getFilterIPs() + " with request ip of: " + ipAddress);
|
||||
continue;
|
||||
if (!ignoreRequester) {
|
||||
if(!filterByRequester(device.getRequesterAddress(), ipAddress) || !filterByRequester(callItems[i].getFilterIPs(), ipAddress)) {
|
||||
log.warn("filter for requester address not present in: (device)" + device.getRequesterAddress() + " OR then (item)" + callItems[i].getFilterIPs() + " with request ip of: " + ipAddress);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (callItems[i].getCount() != null && callItems[i].getCount() > 0)
|
||||
aMultiUtil.setSetCount(callItems[i].getCount());
|
||||
else
|
||||
@@ -1239,7 +1257,7 @@ public class HueMulator {
|
||||
}
|
||||
|
||||
|
||||
private Object changeGroupState(String userId, String groupId, String body, String ipAddress) {
|
||||
private String changeGroupState(String userId, String groupId, String body, String ipAddress, boolean fakeLightResponse) {
|
||||
log.debug("PUT action to group " + groupId + " from " + ipAddress + " user " + userId + " with body " + body);
|
||||
HueError[] theErrors = null;
|
||||
theErrors = bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
|
||||
@@ -1247,16 +1265,24 @@ public class HueMulator {
|
||||
if(bridgeSettingMaster.getBridgeSecurity().isSettingsChanged())
|
||||
bridgeSettingMaster.updateConfigFile();
|
||||
|
||||
GroupDescriptor group = null;
|
||||
Integer targetBriInc = null;
|
||||
Integer targetBri = null;
|
||||
DeviceState state = null;
|
||||
Map<String, DeviceResponse> lights = null;
|
||||
if (groupId.equalsIgnoreCase("0")) {
|
||||
lights = (Map<String, DeviceResponse>)lightsListHandler(userId, ipAddress);
|
||||
} else {
|
||||
GroupDescriptor group = groupRepository.findOne(groupId);
|
||||
group = groupRepository.findOne(groupId);
|
||||
if (group == null || group.isInactive()) {
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("3", "/groups/" + groupId,
|
||||
"resource, /groups/" + groupId + ", not available", null, null, null).getTheErrors(), HueError[].class);
|
||||
} else {
|
||||
lights = repository.findAllByGroupWithState(group.getLights(), ipAddress, myHueHome, aGsonHandler);
|
||||
if (fakeLightResponse) {
|
||||
lights = repository.findAllByGroupWithState(group.getLights(), ipAddress, myHueHome, aGsonHandler, true);
|
||||
} else {
|
||||
lights = repository.findAllByGroupWithState(group.getLights(), ipAddress, myHueHome, aGsonHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1272,6 +1298,23 @@ public class HueMulator {
|
||||
return aGsonHandler.toJson(HueErrorResponse.createResponse("2", "/groups/" + groupId + "/action",
|
||||
"Could not parse state change body.", null, null, null).getTheErrors(), HueError[].class);
|
||||
}
|
||||
|
||||
if (group != null) {
|
||||
if (body.contains("\"bri_inc\"")) {
|
||||
targetBriInc = new Integer(theStateChanges.getBri_inc());
|
||||
}
|
||||
else if (body.contains("\"bri\"")) {
|
||||
targetBri = new Integer(theStateChanges.getBri());
|
||||
}
|
||||
|
||||
state = group.getAction();
|
||||
if (state == null) {
|
||||
state = DeviceState.createDeviceState();
|
||||
group.setAction(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean turnOn = false;
|
||||
boolean turnOff = false;
|
||||
if (!(body.contains("\"bri_inc\"") || body.contains("\"bri\""))) {
|
||||
@@ -1284,23 +1327,38 @@ public class HueMulator {
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, DeviceResponse> light : lights.entrySet()) {
|
||||
log.debug("Processing light" + light.getKey() + ": " + turnOn + " " + turnOff + " " + light.getValue().getState().isOn());
|
||||
// ignore on/off for devices that are already on/off
|
||||
if (turnOff && !light.getValue().getState().isOn())
|
||||
continue;
|
||||
if (turnOn && light.getValue().getState().isOn())
|
||||
continue;
|
||||
changeState(userId, light.getKey(), body, ipAddress);
|
||||
changeState(userId, light.getKey(), body, ipAddress, fakeLightResponse);
|
||||
}
|
||||
// construct success response: one success message per changed property, but not per light
|
||||
if (group != null) { // if not group 0
|
||||
String response = formatSuccessHueResponse(theStateChanges, body, String.valueOf(Integer.parseInt(groupId) + 10000),
|
||||
state, targetBri, targetBriInc, true);
|
||||
group.setAction(state);
|
||||
if (fakeLightResponse) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
String successString = "[";
|
||||
for (String pairStr : body.replaceAll("[{|}]", "").split(",\\s*\"")) {
|
||||
String[] pair = pairStr.split(":");
|
||||
successString += "{\"success\":{ \"address\": \"/groups/" + groupId + "/action/" + pair[0].replaceAll("\"", "").trim() + "\", \"value\": " + pair[1].trim() + "}},";
|
||||
if (fakeLightResponse) {
|
||||
successString += "{\"success\":{ \"/lights/" + String.valueOf(Integer.parseInt(groupId) + 10000) + "/state/" + pair[0].replaceAll("\"", "").trim() + "\": " + pair[1].trim() + "}},";
|
||||
} else {
|
||||
successString += "{\"success\":{ \"address\": \"/groups/" + groupId + "/action/" + pair[0].replaceAll("\"", "").trim() + "\", \"value\": " + pair[1].trim() + "}},";
|
||||
}
|
||||
|
||||
}
|
||||
return (successString.length() == 1) ? "[]" : successString.substring(0, successString.length()-1) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
return theErrors;
|
||||
return aGsonHandler.toJson(theErrors);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user