From 45e2b63f981caa2303b00e32ef8027e08fdf2619 Mon Sep 17 00:00:00 2001 From: BWS Systems Date: Tue, 21 May 2019 16:44:38 -0500 Subject: [PATCH] Updating color handling --- README.md | 17 +- pom.xml | 255 +++++++------ .../HABridge/dao/DeviceRepository.java | 6 +- .../bwssystems/HABridge/hue/ColorDecode.java | 347 +++++++++++------- .../bwssystems/HABridge/hue/HueMulator.java | 8 +- 5 files changed, 370 insertions(+), 263 deletions(-) diff --git a/README.md b/README.md index 5eee244..e9301de 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,6 @@ This bridge was built to help put the Internet of Things together. ## Build To customize and build it yourself, build a new jar with maven: -ATTENTION: This requires JDK 1.8 to build - ``` mvn install ``` @@ -58,30 +56,23 @@ Otherwise, downloads are available at https://github.com/bwssytems/ha-bridge/rel ## Run Then locate the jar and start the server with: -ATTENTION: This requires JDK 1.8 to run - ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below. ``` -java -jar ha-bridge-5.2.2.jar -``` - -ATTENTION: If running Java9, you will need to add the xml bind module -``` -java -jar --add-modules java.xml.bind ha-bridge-5.2.2.jar +java -jar ha-bridge-5.3.0.jar ``` ## Manual installation of ha-bridge and setup of systemd service Next gen Linux systems (this includes the Raspberry Pi), use systemd to run and manage services. Here is a link on how to use systemd: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units -Create the directory and make sure that ha-bridge-5.2.2.jar is in your /home/pi/ha-bridge directory. +Create the directory and make sure that ha-bridge-5.3.0.jar is in your /home/pi/ha-bridge directory. ``` pi@raspberrypi:~ $ mkdir ha-bridge pi@raspberrypi:~ $ cd ha-bridge -pi@raspberrypi:~/ha-bridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v5.2.2/ha-bridge-5.2.2.jar +pi@raspberrypi:~/ha-bridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v5.3.0/ha-bridge-5.3.0.jar ``` Create the ha-bridge.service unit file: @@ -100,7 +91,7 @@ After=network.target Type=simple WorkingDirectory=/home/pi/ha-bridge -ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/ha-bridge/data/habridge.config /home/pi/ha-bridge/ha-bridge-5.2.2.jar +ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/ha-bridge/data/habridge.config /home/pi/ha-bridge/ha-bridge-5.3.0.jar [Install] WantedBy=multi-user.target diff --git a/pom.xml b/pom.xml index 50e8f8f..f63ebbf 100644 --- a/pom.xml +++ b/pom.xml @@ -1,105 +1,102 @@ - + 4.0.0 com.bwssystems.HABridge ha-bridge - 5.2.2 + 5.2.next_a jar HA Bridge Emulates a Philips Hue bridge to allow the Amazon Echo to hook up to other HA systems, i.e. Vera or Harmony Hub or Nest, using lightweight frameworks - 1.8 - 1.8 - 1.8 - UTF-8 + UTF-8 - jitpack.io - https://jitpack.io + jitpack.io + https://jitpack.io + + + Eclipse Paho Repo + https://repo.eclipse.org/content/repositories/paho-releases/ - - Eclipse Paho Repo - https://repo.eclipse.org/content/repositories/paho-releases/ - - com.github.bwssytems - harmony-java-client - master-SNAPSHOT - - - org.slf4j - slf4j-simple - - - org.slf4j - log4j-over-slf4j - - + com.github.bwssytems + harmony-java-client + master-SNAPSHOT + + + org.slf4j + slf4j-simple + + + org.slf4j + log4j-over-slf4j + + - - com.github.bwssytems - nest-controller - 1.0.14 - - - org.slf4j - slf4j-simple - - - org.slf4j - log4j-over-slf4j - - - - com.sparkjava - spark-core - 2.7.2 - - - slf4j-simple - org.slf4j - - + com.github.bwssytems + nest-controller + 1.0.14 + + + org.slf4j + slf4j-simple + + + org.slf4j + log4j-over-slf4j + + + + + com.sparkjava + spark-core + 2.7.2 + + + slf4j-simple + org.slf4j + + org.apache.httpcomponents httpclient 4.5.1 - - org.apache.httpcomponents - httpcore - 4.4.4 - - - org.slf4j - slf4j-api - 1.7.24 - + + org.apache.httpcomponents + httpcore + 4.4.4 + + + org.slf4j + slf4j-api + 1.7.24 + ch.qos.logback logback-classic 1.2.1 - - com.google.code.gson - gson - 2.6.2 - - net.java.dev.eval - eval - 0.5 + com.google.code.gson + gson + 2.6.2 + + + net.java.dev.eval + eval + 0.5 com.google.inject @@ -107,68 +104,95 @@ 4.1.0 - org.igniterealtime.smack - smack-core - 4.2.0 - - - org.eclipse.paho - org.eclipse.paho.client.mqttv3 - 1.2.0 - - - junit - junit - 4.12 - test + org.igniterealtime.smack + smack-core + 4.2.0 - com.github.bwssytems - lifx-sdk-java - 2.1.6 + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.0 - com.github.mob41 - broadlink-java-api - master-SNAPSHOT + junit + junit + 4.12 + test - org.apache.commons - commons-lang3 - 3.5 + com.github.bwssytems + lifx-sdk-java + 2.1.6 + + + com.github.mob41 + broadlink-java-api + master-SNAPSHOT + + + org.apache.commons + commons-lang3 + 3.5 - - - src/main/resources - - version.properties - - true - - - src/main/resources - - version.properties - - false - - + + + src/main/resources + + version.properties + + true + + + src/main/resources + + version.properties + + false + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M2 + + + enforce-maven + + enforce + + + + + 3.6 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 11 + + - org.apache.maven.plugins - maven-surefire-plugin - 2.22.1 - - true - + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M3 + + true + org.apache.maven.plugins maven-shade-plugin - 2.4.3 + 3.2.1 package @@ -230,8 +254,7 @@ - + com.bwssystems.HABridge.HABridge diff --git a/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java b/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java index b8b5651..aafc0df 100644 --- a/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java +++ b/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java @@ -18,6 +18,7 @@ import org.slf4j.LoggerFactory; import com.bwssystems.HABridge.DeviceMapTypes; import com.bwssystems.HABridge.api.CallItem; import com.bwssystems.HABridge.api.hue.DeviceResponse; +import com.bwssystems.HABridge.api.hue.DeviceState; import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.plugins.hue.HueHome; import com.bwssystems.HABridge.util.BackupHandler; @@ -67,7 +68,10 @@ public class DeviceRepository extends BackupHandler { { DeviceDescriptor list[] = gson.fromJson(jsonContent, DeviceDescriptor[].class); for(int i = 0; i < list.length; i++) { - list[i].setDeviceState(null); + if(list[i].getColorUrl() == null || list[i].getColorUrl().isEmpty()) + list[i].setDeviceState(DeviceState.createDeviceState(false)); + else + list[i].setDeviceState(DeviceState.createDeviceState(true)); put(list[i].getId(), list[i]); if(Integer.decode(list[i].getId()) > nextId) { nextId = Integer.decode(list[i].getId()); diff --git a/src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java b/src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java index ad9a3af..89b97f9 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java +++ b/src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java @@ -4,8 +4,7 @@ import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.awt.Color; - +import java.awt.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,91 +23,174 @@ public class ColorDecode { private static final String COLOR_HSL = "${color.hsl}"; private static final Pattern COLOR_MILIGHT = Pattern.compile("\\$\\{color.milight\\:([01234])\\}"); + public static List convertHSLtoRGB(int hue, int sat, int brightness) { + List rgb; + float decimalBrightness = (float) 0.0; + float var_1 = (float) 0.0; + float var_2 = (float) 0.0; + float h = (float) 0.0; + float h2 = (float) 0.0; + float s = (float) 0.0; + double r = 0.0; + double g = 0.0; + double b = 0.0; + + if(brightness > 0) + decimalBrightness = (float) (brightness / 255.0); + + if(hue > 0) { + h = (float)(hue / 65535.0); + h2 = h + (float)0.5; + if(h2 > 1) { + h2 = h2 * (float) -1.0; + } + } + if(sat > 0) { + s = (float)(sat / 254.0); + } + + if (s == 0) + { + r = decimalBrightness * 255; + g = decimalBrightness * 255; + b = decimalBrightness * 255; + } + else + { + if (decimalBrightness < 0.5) + { + var_2 = decimalBrightness * (1 + s); + } + else + { + var_2 = (decimalBrightness + s) - (s * decimalBrightness); + }; + + var_1 = 2 * decimalBrightness - var_2; + r = 255 * hue_2_rgb(var_1,var_2,h2 + (1 / 3)); + g = 255 * hue_2_rgb(var_1,var_2,h2); + b = 255 * hue_2_rgb(var_1,var_2,h2 - (1 / 3)); + }; + + rgb = new ArrayList(); + rgb.add((int) Math.round(r * 255)); + rgb.add((int) Math.round(g * 255)); + rgb.add((int) Math.round(b * 255)); + + return rgb; + } + + public static float hue_2_rgb(float v1, float v2, float vh) { + if (vh < 0) + { + vh += 1; + }; + + if (vh > 1) + { + vh -= 1; + }; + + if ((6 * vh) < 1) + { + return (v1 + (v2 - v1) * 6 * vh); + }; + + if ((2 * vh) < 1) + { + return (v2); + }; + + if ((3 * vh) < 2) + { + return (v1 + (v2 - v1) * ((2 / 3 - vh) * 6)); + }; + + return (v1); + } + public static List convertCIEtoRGB(List xy, int brightness) { List rgb; double x = xy.get(0); // the given x value double y = xy.get(1); // the given y value double z = 1.0 - x - y; - double Y = (double)brightness/(double)254.00; // The given brightness value + double Y = (double) brightness / (double) 254.00; // The given brightness value double X = (Y / y) * x; double Z = (Y / y) * z; - double r = X * 1.656492 - Y * 0.354851 - Z * 0.255038; + double r = X * 1.656492 - Y * 0.354851 - Z * 0.255038; double g = -X * 0.707196 + Y * 1.655397 + Z * 0.036152; - double b = X * 0.051713 - Y * 0.121364 + Z * 1.011530; + double b = X * 0.051713 - Y * 0.121364 + Z * 1.011530; if (r > b && r > g && r > 1.0) { g = g / r; b = b / r; r = 1.0; - } - else if (g > b && g > r && g > 1.0) { + } else if (g > b && g > r && g > 1.0) { r = r / g; b = b / g; g = 1.0; - } - else if (b > r && b > g && b > 1.0) { + } else if (b > r && b > g && b > 1.0) { r = r / b; g = g / b; b = 1.0; } - r = r <= 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055; g = g <= 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055; b = b <= 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055; - - if (r > b && r > g) { - // red is biggest - if (r > 1.0) { - g = g / r; - b = b / r; - r = 1.0; - } - } - else if (g > b && g > r) { - // green is biggest - if (g > 1.0) { - r = r / g; - b = b / g; - g = 1.0; - } - } - else if (b > r && b > g) { - // blue is biggest - if (b > 1.0) { - r = r / b; - g = g / b; - b = 1.0; - } - } - if(r < 0.0) - r = 0; - if(g < 0.0) - g = 0; - if(b < 0.0) - b = 0; - rgb = new ArrayList(); - rgb.add((int)Math.round(r * 255)); - rgb.add((int)Math.round(g * 255)); - rgb.add((int)Math.round(b * 255)); - log.debug("Color change with XY: " + x + " " + y + " Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + rgb.get(2)); + if (r > b && r > g) { + // red is biggest + if (r > 1.0) { + g = g / r; + b = b / r; + r = 1.0; + } + } else if (g > b && g > r) { + // green is biggest + if (g > 1.0) { + r = r / g; + b = b / g; + g = 1.0; + } + } else if (b > r && b > g) { + // blue is biggest + if (b > 1.0) { + r = r / b; + g = g / b; + b = 1.0; + } + } + if (r < 0.0) + r = 0; + if (g < 0.0) + g = 0; + if (b < 0.0) + b = 0; + + rgb = new ArrayList(); + rgb.add((int) Math.round(r * 255)); + rgb.add((int) Math.round(g * 255)); + rgb.add((int) Math.round(b * 255)); + log.debug("Color change with XY: " + x + " " + y + " Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + + " " + rgb.get(2)); return rgb; } - // took that approximation from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ + // took that approximation from + // http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ public static List convertCTtoRGB(Integer ct) { - double temperature = 1000000.0 / (double)ct; + double temperature = 1000000.0 / (double) ct; temperature /= 100; - double r,g,b; + double r, g, b; if (temperature <= 66) { r = 255; g = temperature; - g = 99.4708025861 * Math.log(g) - 161.1195681661; + g = 99.4708025861 * Math.log(g) - 161.1195681661; } else { r = temperature - 60; r = 329.698727446 * (Math.pow(r, -0.1332047592)); @@ -126,25 +208,26 @@ public class ColorDecode { b = 138.5177312231 * Math.log(b) - 305.0447927307; } } - r = assureBounds(r); - g = assureBounds(g); - b = assureBounds(b); - List rgb = new ArrayList(); - rgb.add((int)Math.round(r)); - rgb.add((int)Math.round(g)); - rgb.add((int)Math.round(b)); - log.debug("Color change with CT: " + ct + ". Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + rgb.get(2)); - return rgb; + r = assureBounds(r); + g = assureBounds(g); + b = assureBounds(b); + List rgb = new ArrayList(); + rgb.add((int) Math.round(r)); + rgb.add((int) Math.round(g)); + rgb.add((int) Math.round(b)); + log.debug("Color change with CT: " + ct + ". Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + + rgb.get(2)); + return rgb; } private static double assureBounds(double value) { if (value < 0.0) { - value = 0; - } - if (value > 255.0) { - value = 255; - } - return value; + value = 0; + } + if (value > 255.0) { + value = 255; + } + return value; } @SuppressWarnings("unchecked") @@ -159,25 +242,28 @@ public class ColorDecode { ColorData.ColorMode colorMode = colorData.getColorMode(); List rgb = null; if (colorMode == ColorData.ColorMode.XY) { - rgb = convertCIEtoRGB((List)colorData.getData(), setIntensity); + rgb = convertCIEtoRGB((List) colorData.getData(), setIntensity); } else if (colorMode == ColorData.ColorMode.CT) { - rgb = convertCTtoRGB((Integer)colorData.getData()); + rgb = convertCTtoRGB((Integer) colorData.getData()); } - - while(notDone) { + + while (notDone) { notDone = false; if (request.contains(COLOR_R)) { - request = request.replace(COLOR_R, isHex ? String.format("%02X", rgb.get(0)) : String.valueOf(rgb.get(0))); + request = request.replace(COLOR_R, + isHex ? String.format("%02X", rgb.get(0)) : String.valueOf(rgb.get(0))); notDone = true; } if (request.contains(COLOR_G)) { - request = request.replace(COLOR_G, isHex ? String.format("%02X", rgb.get(1)) : String.valueOf(rgb.get(1))); + request = request.replace(COLOR_G, + isHex ? String.format("%02X", rgb.get(1)) : String.valueOf(rgb.get(1))); notDone = true; } if (request.contains(COLOR_B)) { - request = request.replace(COLOR_B, isHex ? String.format("%02X", rgb.get(2)) : String.valueOf(rgb.get(2))); + request = request.replace(COLOR_B, + isHex ? String.format("%02X", rgb.get(2)) : String.valueOf(rgb.get(2))); notDone = true; } @@ -197,75 +283,78 @@ public class ColorDecode { } if (request.contains(COLOR_RGBX)) { - request = request.replace(COLOR_RGBX, String.format("%02X%02X%02X", rgb.get(0), rgb.get(1), rgb.get(2))); + request = request.replace(COLOR_RGBX, + String.format("%02X%02X%02X", rgb.get(0), rgb.get(1), rgb.get(2))); notDone = true; - } - - if (request.contains(COLOR_HSL)) { - float[] hsb = new float[3]; - Color.RGBtoHSB(rgb.get(0),rgb.get(1),rgb.get(2),hsb); - float hue = hsb[0] * (float) 360.0; - float sat = hsb[1] * (float) 100.0; - float bright = hsb[2] * (float) 100.0; - request = request.replace(COLOR_HSL, String.format("%f,%f,%f", hue, sat, bright)); + } + + if (request.contains(COLOR_HSL)) { + float[] hsb = new float[3]; + Color.RGBtoHSB(rgb.get(0), rgb.get(1), rgb.get(2), hsb); + float hue = hsb[0] * (float) 360.0; + float sat = hsb[1] * (float) 100.0; + float bright = hsb[2] * (float) 100.0; + request = request.replace(COLOR_HSL, String.format("%f,%f,%f", hue, sat, bright)); notDone = true; - } - + } + Matcher m = COLOR_MILIGHT.matcher(request); while (m.find()) { - int group = Integer.parseInt(m.group(1)); - request = m.replaceFirst(getMilightV5FromRgb(rgb, group)); - m.reset(request); + int group = Integer.parseInt(m.group(1)); + request = m.replaceFirst(getMilightV5FromRgb(rgb, group)); + m.reset(request); } - + log.debug("Request <<" + request + ">>, not done: " + notDone); } return request; } private static String getMilightV5FromRgb(List rgb, int group) { - double r = (double)rgb.get(0); - double g = (double)rgb.get(1); - double b = (double)rgb.get(2); - if (r > 245 && g > 245 && b > 245) { // it's white - String retVal = ""; - if (group == 0) { - retVal += "C2"; - } else if (group == 1) { - retVal += "C5"; - } else if (group == 2) { - retVal += "C7"; - } else if (group == 3) { - retVal += "C9"; - } else if (group == 4) { - retVal += "CB"; - } - log.debug("Convert RGB to Milight. Result: WHITE. RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + rgb.get(2)); - return retVal + "0055"; - } else { // normal color - r /= (double)0xFF; - g /= (double)0xFF; - b /= (double)0xFF; - double max = Math.max(Math.max(r, g), b), min = Math.min(Math.min(r, g), b); - double h = 0; - double d = max - min; + double r = (double) rgb.get(0); + double g = (double) rgb.get(1); + double b = (double) rgb.get(2); + if (r > 245 && g > 245 && b > 245) { // it's white + String retVal = ""; + if (group == 0) { + retVal += "C2"; + } else if (group == 1) { + retVal += "C5"; + } else if (group == 2) { + retVal += "C7"; + } else if (group == 3) { + retVal += "C9"; + } else if (group == 4) { + retVal += "CB"; + } + log.debug("Convert RGB to Milight. Result: WHITE. RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + + rgb.get(2)); + return retVal + "0055"; + } else { // normal color + r /= (double) 0xFF; + g /= (double) 0xFF; + b /= (double) 0xFF; + double max = Math.max(Math.max(r, g), b), min = Math.min(Math.min(r, g), b); + double h = 0; + double d = max - min; - if (max == min) { - h = 0; - } else { - if (max == r) { - h = ((g - b) / d + (g < b ? 6 : 0)); - } else if (max == g) { - h = ((b - r) / d + 2); - } else if (max == b){ - h = ((r - g) / d + 4); - } - h = Math.round(h * 60); - } - int milight = (int)((256 + 176 - Math.floor(h / 360.0 * 255.0)) % 256); - log.debug("Convert RGB to Milight. Result: " + milight + " RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + rgb.get(2)); - return "40" + String.format("%02X", milight) + "55"; - } + if (max == min) { + h = 0; + } else { + if (max == r) { + h = ((g - b) / d + (g < b ? 6 : 0)); + } else if (max == g) { + h = ((b - r) / d + 2); + } else if (max == b) { + h = ((r - g) / d + 4); + } + h = Math.round(h * 60); + } + int milight = (int) ((256 + 176 - Math.floor(h / 360.0 * 255.0)) % 256); + log.debug("Convert RGB to Milight. Result: " + milight + " RGB Values: " + rgb.get(0) + " " + rgb.get(1) + + " " + rgb.get(2)); + return "40" + String.format("%02X", milight) + "55"; + } } @SuppressWarnings("unchecked") diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index 75c5302..80174b1 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -600,8 +600,8 @@ public class HueMulator { if((deviceState != null) && deviceState.isOn() && deviceState.getBri() <= 0) deviceState.setBri(254); - if((deviceState != null) && !deviceState.isOn() && (targetBri != null || targetBriInc != null)) - deviceState.setOn(true); +// if((deviceState != null) && !deviceState.isOn() && (targetBri != null || targetBriInc != null)) +// deviceState.setOn(true); responseString = responseString + "]"; @@ -1186,11 +1186,11 @@ public class HueMulator { isOnRequest = true; } - if(!device.isOnFirstDim() && device.isOnWhenDimPresent() && isDimRequest) { + if(!device.isOnFirstDim() && device.isOnWhenDimPresent() && isDimRequest && !isOnRequest) { isOnRequest = true; theStateChanges.setOn(true); } else if(!device.isOnFirstDim() && !device.isOnWhenDimPresent() && isDimRequest) { - isOnRequest = false; + // isOnRequest = false; } if(device.isOnFirstDim() && isDimRequest && !device.getDeviceState().isOn()) {