mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-18 08:13:23 +00:00
207 lines
8.7 KiB
Java
207 lines
8.7 KiB
Java
package com.bwssystems.HABridge.hue;
|
|
|
|
import com.bwssystems.HABridge.JsonTransformer;
|
|
import com.bwssystems.HABridge.api.UserCreateRequest;
|
|
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
|
import com.bwssystems.HABridge.api.hue.DeviceState;
|
|
import com.bwssystems.HABridge.api.hue.HueApiResponse;
|
|
import com.bwssystems.HABridge.dao.*;
|
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.google.gson.Gson;
|
|
|
|
import static spark.Spark.get;
|
|
import static spark.Spark.post;
|
|
import static spark.Spark.put;
|
|
|
|
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 java.io.IOException;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Based on Armzilla's HueMulator - a Philips Hue emulator using sparkjava rest server
|
|
*/
|
|
|
|
public class HueMulator {
|
|
private static final Logger log = LoggerFactory.getLogger(HueMulator.class);
|
|
private static final String INTENSITY_PERCENT = "${intensity.percent}";
|
|
private static final String INTENSITY_BYTE = "${intensity.byte}";
|
|
private static final String HUE_CONTEXT = "/api";
|
|
|
|
private DeviceRepository repository;
|
|
private HttpClient httpClient;
|
|
private ObjectMapper mapper;
|
|
|
|
|
|
public HueMulator(DeviceRepository aDeviceRepository){
|
|
httpClient = HttpClients.createMinimal();
|
|
mapper = new ObjectMapper(); //armzilla: work around Echo incorrect content type and breaking mapping. Map manually
|
|
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
|
repository = aDeviceRepository;
|
|
setupEndpoints();
|
|
}
|
|
|
|
// This function sets up the sparkjava rest calls for the hue api
|
|
private void setupEndpoints() {
|
|
// http://ip_address:port/api/{userId}/lights returns json objects of all lights configured
|
|
get(HUE_CONTEXT + "/:userid/lights", "application/json", (request, response) -> {
|
|
String userId = request.params(":userid");
|
|
log.info("hue lights list requested: " + userId + " from " + request.ip());
|
|
List<DeviceDescriptor> deviceList = repository.findAll();
|
|
Map<String, DeviceResponse> deviceResponseMap = new HashMap<>();
|
|
for (DeviceDescriptor device : deviceList) {
|
|
DeviceResponse deviceResponse = DeviceResponse.createResponse(device.getName(), device.getId());
|
|
deviceResponseMap.put(device.getId(), deviceResponse);
|
|
}
|
|
response.status(200);
|
|
return deviceResponseMap;
|
|
} , new JsonTransformer());
|
|
|
|
// http://ip_address:port/api with body of user request returns json object for a success of user add
|
|
post(HUE_CONTEXT, "application/json", (request, response) -> {
|
|
log.debug("hue api user create requested: " + request.body() + " from " + request.ip());
|
|
UserCreateRequest aNewUser = new Gson().fromJson(request.body(), UserCreateRequest.class);
|
|
String newUser = aNewUser.getUsername();
|
|
if(newUser == null)
|
|
newUser = "lightssystem";
|
|
log.debug("hue api user create requested for device type: " + aNewUser.getDevicetype() + " and username: " + newUser);
|
|
|
|
response.status(200);
|
|
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
|
|
} );
|
|
|
|
// http://ip_address:port/api/{userId} returns json objects for the full state
|
|
get(HUE_CONTEXT + "/:userid", "application/json", (request, response) -> {
|
|
String userId = request.params(":userid");
|
|
log.info("hue api full state requested: " + userId + " from " + request.ip());
|
|
List<DeviceDescriptor> descriptorList = repository.findAll();
|
|
if (descriptorList == null) {
|
|
response.status(404);
|
|
return null;
|
|
}
|
|
Map<String, DeviceResponse> deviceList = new HashMap<>();
|
|
|
|
descriptorList.forEach(descriptor -> {
|
|
DeviceResponse deviceResponse = DeviceResponse.createResponse(descriptor.getName(), descriptor.getId());
|
|
deviceList.put(descriptor.getId(), deviceResponse);
|
|
}
|
|
);
|
|
HueApiResponse apiResponse = new HueApiResponse();
|
|
apiResponse.setLights(deviceList);
|
|
|
|
response.status(200);
|
|
return apiResponse;
|
|
}, new JsonTransformer());
|
|
|
|
// http://ip_address:port/api/{userId}/lights/{lightId} returns json object for a given light
|
|
get(HUE_CONTEXT + "/:userid/lights/:id", "application/json", (request, response) -> {
|
|
String userId = request.params(":userid");
|
|
String lightId = request.params(":id");
|
|
log.info("hue light requested: " + lightId + "for user: " + userId + " from " + request.ip());
|
|
DeviceDescriptor device = repository.findOne(lightId);
|
|
if (device == null) {
|
|
response.status(404);
|
|
return null;
|
|
} else {
|
|
log.info("found device named: " + device.getName());
|
|
}
|
|
DeviceResponse lightResponse = DeviceResponse.createResponse(device.getName(), device.getId());
|
|
|
|
response.status(200);
|
|
return lightResponse;
|
|
}, new JsonTransformer());
|
|
|
|
// http://ip_address:port/api/{userId}/lights/{lightId}/state uses json object to set the lights state
|
|
put(HUE_CONTEXT + "/:userid/lights/:id/state", "application/json", (request, response) -> {
|
|
/**
|
|
* strangely enough the Echo sends a content type of application/x-www-form-urlencoded even though
|
|
* it sends a json object
|
|
*/
|
|
String userId = request.params(":userid");
|
|
String lightId = request.params(":id");
|
|
log.info("hue state change requested: " + userId + " from " + request.ip() + " body: " + request.body());
|
|
|
|
DeviceState state = null;
|
|
try {
|
|
state = mapper.readValue(request.body(), DeviceState.class);
|
|
} catch (IOException e) {
|
|
log.error("Object mapper barfed on input of body.", e);
|
|
response.status(400);
|
|
return null;
|
|
}
|
|
|
|
DeviceDescriptor device = repository.findOne(lightId);
|
|
if (device == null) {
|
|
response.status(404);
|
|
log.error("Could not find devcie: " + lightId);
|
|
return null;
|
|
}
|
|
|
|
String responseString;
|
|
String url;
|
|
if (state.isOn()) {
|
|
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":true}}]";
|
|
url = device.getOnUrl();
|
|
} else {
|
|
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":false}}]";
|
|
url = device.getOffUrl();
|
|
}
|
|
|
|
/* light weight templating here, was going to use free marker but it was a bit too
|
|
* heavy for what we were trying to do.
|
|
*
|
|
* currently provides only two variables:
|
|
* intensity.byte : 0-255 brightness. this is raw from the echo
|
|
* intensity.percent : 0-100, adjusted for the vera
|
|
*/
|
|
if(url.contains(INTENSITY_BYTE)){
|
|
String intensityByte = String.valueOf(state.getBri());
|
|
url = url.replace(INTENSITY_BYTE, intensityByte);
|
|
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/bri\":"+ String.valueOf(state.getBri()) + "}}]";
|
|
}else if(url.contains(INTENSITY_PERCENT)){
|
|
int percentBrightness = (int) Math.round(state.getBri()/255.0*100);
|
|
String intensityPercent = String.valueOf(percentBrightness);
|
|
url = url.replace(INTENSITY_PERCENT, intensityPercent);
|
|
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/bri\":"+ String.valueOf(state.getBri()) + "}}]";
|
|
}
|
|
|
|
//make call
|
|
if(!doHttpGETRequest(url)){
|
|
response.status(503);
|
|
log.error("Error on calling url to change device state: " + url);
|
|
return null;
|
|
}
|
|
|
|
response.status(200);
|
|
return responseString;
|
|
});
|
|
}
|
|
|
|
// This function executes the url from the device repository against the vera
|
|
protected boolean doHttpGETRequest(String url) {
|
|
log.info("calling GET on URL: " + url);
|
|
HttpGet httpGet = new HttpGet(url);
|
|
try {
|
|
HttpResponse response = httpClient.execute(httpGet);
|
|
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
|
log.info("GET on URL responded: " + response.getStatusLine().getStatusCode());
|
|
if(response.getStatusLine().getStatusCode() == 200){
|
|
return true;
|
|
}
|
|
} catch (IOException e) {
|
|
log.error("Error calling out to HA gateway", e);
|
|
}
|
|
return false;
|
|
}
|
|
}
|