Compare commits

...

24 Commits

Author SHA1 Message Date
bwssystems
969ed352f7 Update handling of upnp and updated how unique ids are generated 2020-12-01 19:32:42 -06:00
bwssystems
c98513c365 remove .project file 2020-11-19 12:16:08 -06:00
bwssystems
add9617a07 Fix command line config path issue
When the command line config path is used and the ocnfig file has no entry for configfile a null pointer exception occurrs
2020-11-19 12:09:13 -06:00
BWS Systems
f9e9f16756 Update Readme for RC2 2020-11-09 14:20:53 -05:00
BWS Systems
f15cc0d53a Merge pull request #1238 from bwssytems/dependabot/maven/junit-junit-4.13.1
Bump junit from 4.12 to 4.13.1
2020-11-09 13:08:53 -05:00
BWS Systems
0f791c1e71 Merge pull request #1249 from marcopollacci/dev_branch_5.3.x
Dev branch 5.3.x merge changes
2020-11-09 13:07:29 -05:00
Marco Pollacci
9887042f4d Update DeviceRepository.java 2020-11-06 10:26:00 +01:00
Marco Pollacci
c840f2bc4d Update DeviceResponse.java 2020-11-06 10:07:40 +01:00
dependabot[bot]
9a355b7906 Bump junit from 4.12 to 4.13.1
Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-13 06:42:38 +00:00
BWS Systems
0d568d8d68 Changed version to 5.3.1RC1 2020-03-26 15:31:55 -04:00
BWS Systems
f5e100667e Needed to not fix the multicast socket to a specific ip address
This is when upnp use interface only
2020-03-26 15:25:10 -04:00
BWS Systems
c376253488 Fix getting outbound route from inbound address 2020-03-26 13:34:27 -04:00
BWS Systems
a3fd2ca722 finished color decode fixes and tests, updated deviceId counting, fixing min bright value 2019-10-02 15:21:35 -05:00
BWS Systems
fe4df16e10 Continue test cases 2019-10-01 14:42:20 -05:00
BWS Systems
9399af7ec7 Merge pull request #1138 from jimirocks/proxy
Fix nginx example
2019-09-30 08:29:21 -05:00
Jiří Mikulášek
be72f7e62c Fix nginx example 2019-09-28 00:52:06 +02:00
BWS Systems
768eebfc78 Updated color conversions and tests 2019-09-26 14:12:41 -05:00
BWS Systems
79ce23b80a Continue color validation 2019-09-25 16:38:30 -05:00
BWS Systems
bddc7c1c31 Updated handling for use upnp iface and start color upgrades 2019-09-24 16:07:36 -05:00
BWS Systems
51c6ffc48a Auto stash before rebase of "origin/dev_branch_5.3.x" 2019-09-24 08:40:16 -05:00
BWS Systems
ee4afc00c0 Merge pull request #1121 from gaudryc/master
Fix the bad practice "Comparison of String objects using == or !=".
2019-08-19 08:08:19 -05:00
gaudryc
8b48f23741 Fix the bad practice "Comparison of String objects using == or !=". 2019-08-18 22:33:27 +02:00
BWS Systems
87f79df8b1 Merge pull request #1115 from bwssytems/target_5_2_next
Fixes #670 Execute select Device when bridge loads [Enhancement Request] enhancement
Fixes #877 Color in Hue/Sat enhancement
Fixes #917 lock device ID #s enhancement
Fixes #1032 Support for HomeGenie enhancement
Fixes #1033 Backup & Restore enhancement
Fixes #1060 Voice command colour change bug question
Fixes #1077 several somfy action in a same Row ID doesn't work bug question
Fixes #1084 Add Mozilla IoT gateway Helper enhancement
Fixes #1092 ha-bridge incompatible with Amazon Echo Dot Gen. 3? enhancement question
Fixes #1093 Alexa Issue : ID conflict between HA Bridge an Hue Bridge enhancement question
Fixes #1094 issue with 5.2.2 and Harmony enhancement question
Fixes #1095 issue with 5.2.2 and fhem enhancement question
Fixes #1098 Home Assistant has new authentication methods and Legacy API Passwords will be removed enhancement question
Fixes #1103 Have single scroll bar for Bridge Devices page. enhancement
Fixes #1108 Add mDNS Discovery to ha-bridge
Fixes #1109 Add HTTPS to ha-bridge
2019-07-29 09:24:22 -05:00
BWS Systems
1142704d22 Change version number for release 2019-07-29 09:20:01 -05:00
20 changed files with 1532 additions and 411 deletions

2
.gitignore vendored
View File

@@ -12,6 +12,7 @@ data
/.settings/ /.settings/
/start.bat /start.bat
/.classpath /.classpath
/.project
sftp-config\.json sftp-config\.json
/bin/ /bin/
@@ -23,3 +24,4 @@ sftp-config\.json
/node_modules /node_modules
package-lock.json package-lock.json
.project

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ha-bridge</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -57,20 +57,20 @@ Then locate the jar and start the server with:
ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below. ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below.
``` ```
java -jar ha-bridge-5.3.0.jar java -jar ha-bridge-5.3.1RC3.jar
``` ```
## Manual installation of ha-bridge and setup of systemd service ## 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. 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 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.3.0.jar is in your /home/pi/ha-bridge directory. Create the directory and make sure that ha-bridge-5.3.1RC3.jar is in your /home/pi/ha-bridge directory.
``` ```
pi@raspberrypi:~ $ mkdir ha-bridge pi@raspberrypi:~ $ mkdir ha-bridge
pi@raspberrypi:~ $ cd ha-bridge pi@raspberrypi:~ $ cd ha-bridge
pi@raspberrypi:~/ha-bridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v5.3.0/ha-bridge-5.3.0.jar pi@raspberrypi:~/ha-bridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v5.3.1RC3/ha-bridge-5.3.1RC3.jar
``` ```
Create the ha-bridge.service unit file: Create the ha-bridge.service unit file:
@@ -89,7 +89,7 @@ After=network.target
Type=simple Type=simple
WorkingDirectory=/home/pi/ha-bridge 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.3.0.jar ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/ha-bridge/data/habridge.config /home/pi/ha-bridge/ha-bridge-5.3.1RC3.jar
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
@@ -222,7 +222,7 @@ proxy.server = (
``` ```
### nginx Example ### nginx Example
``` ```
location /api/ { location /api {
proxy_pass http://127.0.0.1:8080/api; proxy_pass http://127.0.0.1:8080/api;
} }
``` ```

12
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>com.bwssystems.HABridge</groupId> <groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId> <artifactId>ha-bridge</artifactId>
<version>5.3.0RC13</version> <version>5.3.1RC3</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>HA Bridge</name> <name>HA Bridge</name>
@@ -111,12 +111,12 @@
<dependency> <dependency>
<groupId>org.eclipse.paho</groupId> <groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId> <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.0</version> <version>1.2.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.12</version> <version>4.13.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -172,7 +172,7 @@
<configuration> <configuration>
<rules> <rules>
<requireMavenVersion> <requireMavenVersion>
<!-- Change this to Version 3.3 for Java 1.8 and Raspberry PI compilation --> <!-- Change this to Version 3.3 for Java 1.8 and Raspberry PI compilation, Java 11 is 3.6 -->
<version>3.6</version> <version>3.6</version>
</requireMavenVersion> </requireMavenVersion>
</rules> </rules>
@@ -184,7 +184,7 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<!-- Comment this release line out for Java 1.8 and Raspberry PI compilation --> <!-- Comment this release line out for Java 1.8 and Raspberry PI compilation -->
<release>11</release> <release>11</release>
<!-- Uncomment the next two lines for Java 1.8 and Raspberry PI compilation --> <!-- Uncomment the next two lines for Java 1.8 and Raspberry PI compilation -->
<!-- <source>1.8</source> --> <!-- <source>1.8</source> -->
@@ -196,7 +196,7 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version> <version>3.0.0-M3</version>
<configuration> <configuration>
<skipTests>true</skipTests> <skipTests>false</skipTests>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>

View File

@@ -250,10 +250,10 @@ public class BridgeSettings extends BackupHandler {
return; return;
try { try {
theBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettingsDescriptor.class); theBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettingsDescriptor.class);
} catch (Exception e) {
log.warn("Issue loading values from file: " + aPath.toUri().toString() + ", Gson convert failed.");
theBridgeSettings = new BridgeSettingsDescriptor();
theBridgeSettings.setConfigfile(aPath.toString()); theBridgeSettings.setConfigfile(aPath.toString());
} catch (Exception e) {
log.warn("Issue loading values from file: " + aPath.toUri().toString() + ", Gson convert failed. Using default settings.");
theBridgeSettings = new BridgeSettingsDescriptor();
} }
} }

View File

@@ -132,6 +132,9 @@ public class BridgeSettingsDescriptor {
@SerializedName("haaddressessecured") @SerializedName("haaddressessecured")
@Expose @Expose
private boolean haaddressessecured; private boolean haaddressessecured;
@SerializedName("upnpadvanced")
@Expose
private boolean upnpadvanced;
// @SerializedName("activeloggers") // @SerializedName("activeloggers")
// @Expose // @Expose
// private List<NameValue> activeloggers; // private List<NameValue> activeloggers;
@@ -192,6 +195,8 @@ public class BridgeSettingsDescriptor {
this.upnporiginal = false; this.upnporiginal = false;
this.seedid = 100; this.seedid = 100;
this.haaddressessecured = false; this.haaddressessecured = false;
this.configfile = Configuration.CONFIG_FILE;
this.upnpadvanced = false;
} }
public String getUpnpConfigAddress() { public String getUpnpConfigAddress() {
@@ -847,4 +852,12 @@ public class BridgeSettingsDescriptor {
public void setHaaddressessecured(boolean haaddressessecured) { public void setHaaddressessecured(boolean haaddressessecured) {
this.haaddressessecured = haaddressessecured; this.haaddressessecured = haaddressessecured;
} }
public boolean isUpnpadvanced() {
return upnpadvanced;
}
public void setUpnpadvanced(boolean upnpadvanced) {
this.upnpadvanced = upnpadvanced;
}
} }

View File

@@ -17,6 +17,7 @@ public class DeviceResponse {
private String swversion; private String swversion;
private String swconfigid; private String swconfigid;
private String productid; private String productid;
private String productname;
public DeviceState getState() { public DeviceState getState() {
return state; return state;
@@ -90,6 +91,14 @@ public class DeviceResponse {
this.productid = productid; this.productid = productid;
} }
public String getProductName() {
return productname;
}
public void setProductName(String productname) {
this.productname = productname;
}
public String getLuminaireuniqueid() { public String getLuminaireuniqueid() {
return luminaireuniqueid; return luminaireuniqueid;
@@ -109,10 +118,11 @@ public class DeviceResponse {
if (device.isColorDevice()) { if (device.isColorDevice()) {
response.setType("Extended color light"); response.setType("Extended color light");
response.setModelid("LCT010"); response.setModelid("LCT015");
response.setSwversion("1.15.2_r19181"); response.setSwversion("1.46.13_r26312");
response.setSwconfigid("F921C859"); response.setSwconfigid("52E3234B");
response.setProductid("Philips-LCT010-1-A19ECLv4"); response.setProductid("Philips-LCT015-1-A19ECLv5");
response.setProductName("Hue color lamp");
} else { } else {
response.setType("Dimmable light"); response.setType("Dimmable light");
response.setModelid("LWB007"); response.setModelid("LWB007");
@@ -129,13 +139,14 @@ public class DeviceResponse {
response.setState(group.getAction()); response.setState(group.getAction());
response.setName(group.getName()); response.setName(group.getName());
response.setUniqueid("00:17:88:5E:D3:FF-" + String.format("%02X", Integer.parseInt(group.getId()))); response.setUniqueid("00:11:22:33:44:55:66:77-" + String.format("%02X", Integer.parseInt(group.getId())));
response.setManufacturername("Philips"); response.setManufacturername("Philips");
response.setType("Extended color light"); response.setType("Extended color light");
response.setModelid("LCT010"); response.setModelid("LCT015");
response.setSwversion("1.15.2_r19181"); response.setSwversion("1.46.13_r26312");
response.setSwconfigid("F921C859"); response.setSwconfigid("52E3234B");
response.setProductid("Philips-LCT010-1-A19ECLv4"); response.setProductid("Philips-LCT015-1-A19ECLv5");
response.setProductName("Hue color lamp");
response.setLuminaireuniqueid(null); response.setLuminaireuniqueid(null);

View File

@@ -2,7 +2,7 @@ package com.bwssystems.HABridge.api.hue;
public class HueConstants { public class HueConstants {
public final static String HUB_VERSION = "9999999999"; public final static String HUB_VERSION = "9999999999";
public final static String API_VERSION = "1.19.0"; public final static String API_VERSION = "1.17.0";
public final static String MODEL_ID = "BSB002"; public final static String MODEL_ID = "BSB002";
public final static String UUID_PREFIX = "2f402f80-da50-11e1-9b23-"; public final static String UUID_PREFIX = "2f402f80-da50-11e1-9b23-";
} }

View File

@@ -30,6 +30,8 @@ import com.google.gson.JsonSyntaxException;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Arrays; import java.util.Arrays;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/* /*
* This is an in memory list to manage the configured devices and saves the list as a JSON string to a file for later * This is an in memory list to manage the configured devices and saves the list as a JSON string to a file for later
@@ -75,6 +77,8 @@ public class DeviceRepository extends BackupHandler {
nextId = Integer.decode(list[i].getId()); nextId = Integer.decode(list[i].getId());
} }
} }
nextId = nextId + 1;
} }
} }
@@ -186,7 +190,7 @@ public class DeviceRepository extends BackupHandler {
nextId++; nextId++;
} }
if (descriptors[i].getUniqueid() == null || descriptors[i].getUniqueid().length() == 0) { if (descriptors[i].getUniqueid() == null || descriptors[i].getUniqueid().length() == 0) {
descriptors[i].setUniqueid("00:17:88:5E:D3:" + hueUniqueId(Integer.valueOf(descriptors[i].getId()))); descriptors[i].setUniqueid(hueUniqueId(Integer.valueOf(descriptors[i].getId())));
} }
put(descriptors[i].getId(), descriptors[i]); put(descriptors[i].getId(), descriptors[i]);
theNames = theNames + " " + descriptors[i].getName() + ", "; theNames = theNames + " " + descriptors[i].getName() + ", ";
@@ -204,11 +208,10 @@ public class DeviceRepository extends BackupHandler {
DeviceDescriptor theDevice; DeviceDescriptor theDevice;
boolean findNext = true; boolean findNext = true;
nextId = seedId; nextId = seedId;
while(deviceIterator.hasNext()) { while (deviceIterator.hasNext()) {
theDevice = deviceIterator.next(); theDevice = deviceIterator.next();
if(theDevice.isLockDeviceId()) { if (theDevice.isLockDeviceId()) {
lockedIds.add(theDevice.getId()); lockedIds.add(theDevice.getId());
} }
} }
@@ -218,15 +221,15 @@ public class DeviceRepository extends BackupHandler {
theDevice = deviceIterator.next(); theDevice = deviceIterator.next();
if (!theDevice.isLockDeviceId()) { if (!theDevice.isLockDeviceId()) {
findNext = true; findNext = true;
while(findNext) { while (findNext) {
if(lockedIds.contains(String.valueOf(nextId))) { if (lockedIds.contains(String.valueOf(nextId))) {
nextId++; nextId++;
} else { } else {
findNext = false; findNext = false;
} }
} }
theDevice.setId(String.valueOf(nextId)); theDevice.setId(String.valueOf(nextId));
theDevice.setUniqueid("00:17:88:5E:D3:" + hueUniqueId(nextId)); theDevice.setUniqueid(hueUniqueId(nextId));
nextId++; nextId++;
} }
newdevices.put(theDevice.getId(), theDevice); newdevices.put(theDevice.getId(), theDevice);
@@ -295,27 +298,51 @@ public class DeviceRepository extends BackupHandler {
} }
private String hueUniqueId(Integer anId) { private String hueUniqueId(Integer anId) {
String theUniqueId; String theUniqueId = null;
Integer newValue; Integer newValue;
String hexValueLeft; String hexValueLeft;
String hexValueRight; String hexValueRight;
newValue = anId % 256; MessageDigest md = null;
if (newValue <= 0)
newValue = 1;
else if (newValue > 255)
newValue = 255;
hexValueLeft = HexLibrary.byteToHex(newValue.byteValue());
newValue = anId / 256;
newValue = newValue % 256;
if (newValue < 0)
newValue = 0;
else if (newValue > 255)
newValue = 255;
hexValueRight = HexLibrary.byteToHex(newValue.byteValue());
theUniqueId = String.format("%s-%s", hexValueLeft, hexValueRight).toUpperCase(); try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
log.warn("Cannot get MD5 utility to hash unique ids.");
}
if(md != null) {
md.update(anId.toString().getBytes());
byte[] digest = md.digest();
theUniqueId = String.format("%s:%s:%s:%s:%s:%s:%s-%s",
HexLibrary.encodeHexString(digest).substring(0, 2),
HexLibrary.encodeHexString(digest).substring(2, 4),
HexLibrary.encodeHexString(digest).substring(4, 6),
HexLibrary.encodeHexString(digest).substring(6, 8),
HexLibrary.encodeHexString(digest).substring(8, 10),
HexLibrary.encodeHexString(digest).substring(10, 12),
HexLibrary.encodeHexString(digest).substring(12, 14),
HexLibrary.encodeHexString(digest).substring(14, 16));
}
if(theUniqueId == null) {
newValue = anId % 256;
if (newValue <= 0)
newValue = 1;
else if (newValue > 255)
newValue = 255;
hexValueLeft = HexLibrary.byteToHex(newValue.byteValue());
newValue = anId / 256;
newValue = newValue % 256;
if (newValue < 0)
newValue = 0;
else if (newValue > 255)
newValue = 255;
hexValueRight = HexLibrary.byteToHex(newValue.byteValue());
theUniqueId = String.format("11:22:33:44:55:66:%s-%s", hexValueLeft, hexValueRight).toUpperCase();
}
return theUniqueId; return theUniqueId;
} }
} }

View File

@@ -26,7 +26,7 @@ public class BrightnessDecode {
if (targetBri != null) { if (targetBri != null) {
setIntensity = targetBri; setIntensity = targetBri;
} else if (targetBriInc != null) { } else if (targetBriInc != null) {
if ((setIntensity + targetBriInc) <= 0) if ((setIntensity + targetBriInc) <= 1)
setIntensity = targetBriInc; setIntensity = targetBriInc;
else if ((setIntensity + targetBriInc) > 254) else if ((setIntensity + targetBriInc) > 254)
setIntensity = targetBriInc; setIntensity = targetBriInc;
@@ -53,7 +53,7 @@ public class BrightnessDecode {
String replaceValue = null; String replaceValue = null;
String replaceTarget = null; String replaceTarget = null;
int percentBrightness = 0; int percentBrightness = 0;
float decimalBrightness = (float) 0.0; float decimalBrightness = (float) 1.0;
Map<String, BigDecimal> variables = new HashMap<String, BigDecimal>(); Map<String, BigDecimal> variables = new HashMap<String, BigDecimal>();
String mathDescriptor = null; String mathDescriptor = null;
@@ -64,8 +64,8 @@ public class BrightnessDecode {
else else
percentBrightness = (int) Math.round(intensity / 255.0 * 100); percentBrightness = (int) Math.round(intensity / 255.0 * 100);
} else { } else {
decimalBrightness = (float) 0.0; decimalBrightness = (float) 1.0;
percentBrightness = 0; percentBrightness = 1;
} }
while(notDone) { while(notDone) {

View File

@@ -0,0 +1,963 @@
package com.bwssystems.HABridge.hue;
/**
* Convert between different color spaces supported.
* RGB -> CMYK -> RGB
* RGB -> YIQ -> RGB
* RGB -> YCbCr -> RGB
* RGB -> YUV -> RGB
* RGB -> RGChromaticity
* RGB -> HSV -> RGB
* RGB -> YCC -> RGB
* RGB -> YCoCg -> RGB
* RGB -> XYZ -> RGB
* RGB -> HunterLAB -> RGB
* RGB -> HLS -> RGB
* RGB -> CIE-LAB -> RGB
* XYZ -> HunterLAB -> XYZ
* XYZ -> CIE-LAB -> XYZ
* @author Diego Catalano
*/
public class ColorConverter {
/**
* Don't let anyone instantiate this class.
*/
private ColorConverter() {}
public static enum YCbCrColorSpace {ITU_BT_601,ITU_BT_709_HDTV};
// XYZ (Tristimulus) Reference values of a perfect reflecting diffuser
//2o Observer (CIE 1931)
// X2, Y2, Z2
public static float[] CIE2_A = {109.850f, 100f, 35.585f}; //Incandescent
public static float[] CIE2_C = {98.074f, 100f, 118.232f};
public static float[] CIE2_D50 = {96.422f, 100f, 82.521f};
public static float[] CIE2_D55 = {95.682f, 100f, 92.149f};
public static float[] CIE2_D65 = {95.047f, 100f, 108.883f}; //Daylight
public static float[] CIE2_D75 = {94.972f, 100f, 122.638f};
public static float[] CIE2_F2 = {99.187f, 100f, 67.395f}; //Fluorescent
public static float[] CIE2_F7 = {95.044f, 100f, 108.755f};
public static float[] CIE2_F11 = {100.966f, 100f, 64.370f};
//10o Observer (CIE 1964)
// X2, Y2, Z2
public static float[] CIE10_A = {111.144f, 100f, 35.200f}; //Incandescent
public static float[] CIE10_C = {97.285f, 100f, 116.145f};
public static float[] CIE10_D50 = {96.720f, 100f, 81.427f};
public static float[] CIE10_D55 = {95.799f, 100f, 90.926f};
public static float[] CIE10_D65 = {94.811f, 100f, 107.304f}; //Daylight
public static float[] CIE10_D75 = {94.416f, 100f, 120.641f};
public static float[] CIE10_F2 = {103.280f, 100f, 69.026f}; //Fluorescent
public static float[] CIE10_F7 = {95.792f, 100f, 107.687f};
public static float[] CIE10_F11 = {103.866f, 100f, 65.627f};
/**
* RFB -> CMYK
* @param red Values in the range [0..255].
* @param green Values in the range [0..255].
* @param blue Values in the range [0..255].
* @return CMYK color space. Normalized.
*/
public static float[] RGBtoCMYK(int red, int green, int blue){
float[] cmyk = new float[4];
float r = red / 255f;
float g = green / 255f;
float b = blue / 255f;
float k = 1.0f - Math.max(r, Math.max(g, b));
float c = (1f-r-k) / (1f-k);
float m = (1f-g-k) / (1f-k);
float y = (1f-b-k) / (1f-k);
cmyk[0] = c;
cmyk[1] = m;
cmyk[2] = y;
cmyk[3] = k;
return cmyk;
}
/**
* CMYK -> RGB
* @param c Cyan.
* @param m Magenta.
* @param y Yellow.
* @param k Black.
* @return RGB color space.
*/
public static int[] CMYKtoRGB(float c, float m, float y, float k){
int[] rgb = new int[3];
rgb[0] = (int)(255 * (1-c) * (1-k));
rgb[1] = (int)(255 * (1-m) * (1-k));
rgb[2] = (int)(255 * (1-y) * (1-k));
return rgb;
}
/**
* RGB -> YUV.
* Y in the range [0..1].
* U in the range [-0.5..0.5].
* V in the range [-0.5..0.5].
* @param red Values in the range [0..255].
* @param green Values in the range [0..255].
* @param blue Values in the range [0..255].
* @return YUV color space.
*/
public static float[] RGBtoYUV(int red, int green, int blue){
float r = (float)red / 255;
float g = (float)green / 255;
float b = (float)blue / 255;
float[] yuv = new float[3];
float y,u,v;
y = (float)(0.299 * r + 0.587 * g + 0.114 * b);
u = (float)(-0.14713 * r - 0.28886 * g + 0.436 * b);
v = (float)(0.615 * r - 0.51499 * g - 0.10001 * b);
yuv[0] = y;
yuv[1] = u;
yuv[2] = v;
return yuv;
}
/**
* YUV -> RGB.
* @param y Luma. In the range [0..1].
* @param u Chrominance. In the range [-0.5..0.5].
* @param v Chrominance. In the range [-0.5..0.5].
* @return RGB color space.
*/
public static int[] YUVtoRGB(float y, float u, float v){
int[] rgb = new int[3];
float r,g,b;
r = (float)((y + 0.000 * u + 1.140 * v) * 255);
g = (float)((y - 0.396 * u - 0.581 * v) * 255);
b = (float)((y + 2.029 * u + 0.000 * v) * 255);
rgb[0] = (int)r;
rgb[1] = (int)g;
rgb[2] = (int)b;
return rgb;
}
/**
* RGB -> YIQ.
* @param red Values in the range [0..255].
* @param green Values in the range [0..255].
* @param blue Values in the range [0..255].
* @return YIQ color space.
*/
public static float[] RGBtoYIQ(int red, int green, int blue){
float[] yiq = new float[3];
float y,i,q;
float r = (float)red / 255;
float g = (float)green / 255;
float b = (float)blue / 255;
y = (float)(0.299 * r + 0.587 * g + 0.114 * b);
i = (float)(0.596 * r - 0.275 * g - 0.322 * b);
q = (float)(0.212 * r - 0.523 * g + 0.311 * b);
yiq[0] = y;
yiq[1] = i;
yiq[2] = q;
return yiq;
}
/**
* YIQ -> RGB.
* @param y Luma. Values in the range [0..1].
* @param i In-phase. Values in the range [-0.5..0.5].
* @param q Quadrature. Values in the range [-0.5..0.5].
* @return RGB color space.
*/
public static int[] YIQtoRGB(double y, double i, double q){
int[] rgb = new int[3];
int r,g,b;
r = (int)((y + 0.956 * i + 0.621 * q) * 255);
g = (int)((y - 0.272 * i - 0.647 * q) * 255);
b = (int)((y - 1.105 * i + 1.702 * q) * 255);
r = Math.max(0,Math.min(255,r));
g = Math.max(0,Math.min(255,g));
b = Math.max(0,Math.min(255,b));
rgb[0] = r;
rgb[1] = g;
rgb[2] = b;
return rgb;
}
public static float[] RGBtoYCbCr(int red, int green, int blue, YCbCrColorSpace colorSpace){
float r = (float)red / 255;
float g = (float)green / 255;
float b = (float)blue / 255;
float[] YCbCr = new float[3];
float y,cb,cr;
if (colorSpace == YCbCrColorSpace.ITU_BT_601) {
y = (float)(0.299 * r + 0.587 * g + 0.114 * b);
cb = (float)(-0.169 * r - 0.331 * g + 0.500 * b);
cr = (float)(0.500 * r - 0.419 * g - 0.081 * b);
}
else{
y = (float)(0.2215 * r + 0.7154 * g + 0.0721 * b);
cb = (float)(-0.1145 * r - 0.3855 * g + 0.5000 * b);
cr = (float)(0.5016 * r - 0.4556 * g - 0.0459 * b);
}
YCbCr[0] = (float)y;
YCbCr[1] = (float)cb;
YCbCr[2] = (float)cr;
return YCbCr;
}
public static int[] YCbCrtoRGB(float y, float cb, float cr, YCbCrColorSpace colorSpace){
int[] rgb = new int[3];
float r,g,b;
if (colorSpace == YCbCrColorSpace.ITU_BT_601) {
r = (float)(y + 0.000 * cb + 1.403 * cr) * 255;
g = (float)(y - 0.344 * cb - 0.714 * cr) * 255;
b = (float)(y + 1.773 * cb + 0.000 * cr) * 255;
}
else{
r = (float)(y + 0.000 * cb + 1.5701 * cr) * 255;
g = (float)(y - 0.1870 * cb - 0.4664 * cr) * 255;
b = (float)(y + 1.8556 * cb + 0.000 * cr) * 255;
}
rgb[0] = (int)r;
rgb[1] = (int)g;
rgb[2] = (int)b;
return rgb;
}
/**
* Rg-Chromaticity space is already known to remove ambiguities due to illumination or surface pose.
* @see Neural Information Processing - Chi Sing Leung. p. 668
* @param red Red coefficient.
* @param green Green coefficient.
* @param blue Blue coefficient.
* @return Normalized RGChromaticity. Range[0..1].
*/
public static double[] RGChromaticity(int red, int green, int blue){
double[] color = new double[5];
double sum = red + green + blue;
//red
color[0] = red / sum;
//green
color[1] = green / sum;
//blue
color[2] = 1 - color[0] - color[1];
double rS = color[0] - 0.333;
double gS = color[1] - 0.333;
//saturation
color[3] = Math.sqrt(rS * rS + gS * gS);
//hue
color[4] = Math.atan(rS / gS);
return color;
}
/**
* RGB -> HSV.
* Adds (hue + 360) % 360 for represent hue in the range [0..359].
* @param red Red coefficient. Values in the range [0..255].
* @param green Green coefficient. Values in the range [0..255].
* @param blue Blue coefficient. Values in the range [0..255].
* @return HSV color space.
*/
public static float[] RGBtoHSV(int red, int green, int blue){
float[] hsv = new float[3];
float r = red / 255f;
float g = green / 255f;
float b = blue / 255f;
float max = Math.max(r, Math.max(g, b));
float min = Math.min(r, Math.min(g, b));
float delta = max - min;
// Hue
if (max == min){
hsv[0] = 0;
}
else if (max == r){
hsv[0] = ((g - b) / delta) * 60f;
}
else if (max == g){
hsv[0] = ((b - r) / delta + 2f) * 60f;
}
else if (max == b){
hsv[0] = ((r - g) / delta + 4f) * 60f;
}
// Saturation
if (delta == 0)
hsv[1] = 0;
else
hsv[1] = delta / max;
//Value
hsv[2] = max;
return hsv;
}
/**
* HSV -> RGB.
* @param hue Hue.
* @param saturation Saturation. In the range[0..1].
* @param value Value. In the range[0..1].
* @return RGB color space. In the range[0..255].
*/
public static int[] HSVtoRGB(float hue, float saturation, float value){
int[] rgb = new int[3];
float hi = (float)Math.floor(hue / 60.0) % 6;
float f = (float)((hue / 60.0) - Math.floor(hue / 60.0));
float p = (float)(value * (1.0 - saturation));
float q = (float)(value * (1.0 - (f * saturation)));
float t = (float)(value * (1.0 - ((1.0 - f) * saturation)));
if (hi == 0){
rgb[0] = (int)(value * 255);
rgb[1] = (int)(t * 255);
rgb[2] = (int)(p * 255);
}
else if (hi == 1){
rgb[0] = (int)(q * 255);
rgb[1] = (int)(value * 255);
rgb[2] = (int)(p * 255);
}
else if (hi == 2){
rgb[0] = (int)(p * 255);
rgb[1] = (int)(value * 255);
rgb[2] = (int)(t * 255);
}
else if (hi == 3){
rgb[0] = (int)(p * 255);
rgb[1] = (int)(value * 255);
rgb[2] = (int)(q * 255);
}
else if (hi == 4){
rgb[0] = (int)(t * 255);
rgb[1] = (int)(value * 255);
rgb[2] = (int)(p * 255);
}
else if (hi == 5){
rgb[0] = (int)(value * 255);
rgb[1] = (int)(p * 255);
rgb[2] = (int)(q * 255);
}
return rgb;
}
/**
* RGB -> YCC.
* @param red Red coefficient. Values in the range [0..255].
* @param green Green coefficient. Values in the range [0..255].
* @param blue Blue coefficient. Values in the range [0..255].
* @return YCC color space. In the range [0..1].
*/
public static float[] RGBtoYCC(int red, int green, int blue){
float[] ycc = new float[3];
float r = red / 255f;
float g = green / 255f;
float b = blue / 255f;
float y = 0.213f * r + 0.419f * g + 0.081f * b;
float c1 = -0.131f * r - 0.256f * g + 0.387f * b + 0.612f;
float c2 = 0.373f * r - 0.312f * r - 0.061f * b + 0.537f;
ycc[0] = y;
ycc[1] = c1;
ycc[2] = c2;
return ycc;
}
/**
* YCC -> RGB.
* @param y Y coefficient.
* @param c1 C coefficient.
* @param c2 C coefficient.
* @return RGB color space.
*/
public static int[] YCCtoRGB(float y, float c1, float c2){
int[] rgb = new int[3];
float r = 0.981f * y + 1.315f * (c2 - 0.537f);
float g = 0.981f * y - 0.311f * (c1 - 0.612f)- 0.669f * (c2 - 0.537f);
float b = 0.981f * y + 1.601f * (c1 - 0.612f);
rgb[0] = (int)(r * 255f);
rgb[1] = (int)(g * 255f);
rgb[2] = (int)(b * 255f);
return rgb;
}
/**
* RGB -> YCoCg.
* @param red Red coefficient. Values in the range [0..255].
* @param green Green coefficient. Values in the range [0..255].
* @param blue Blue coefficient. Values in the range [0..255].
* @return YCoCg color space.
*/
public static float[] RGBtoYCoCg(int red, int green, int blue){
float[] yCoCg = new float[3];
float r = red / 255f;
float g = green / 255f;
float b = blue / 255f;
float y = r / 4f + g / 2f + b / 4f;
float co = r / 2f - b / 2f;
float cg = -r / 4f + g / 2f - b / 4f;
yCoCg[0] = y;
yCoCg[1] = co;
yCoCg[2] = cg;
return yCoCg;
}
/**
* YCoCg -> RGB.
* @param y Pseudo luminance, or intensity.
* @param co Orange chrominance.
* @param cg Green chrominance.
* @return RGB color space.
*/
public static int[] YCoCgtoRGB(float y, float co, float cg){
int[] rgb = new int[3];
float r = y + co - cg;
float g = y + cg;
float b = y - co - cg;
rgb[0] = (int)(r * 255f);
rgb[1] = (int)(g * 255f);
rgb[2] = (int)(b * 255f);
return rgb;
}
/**
* RGB -> XYZ
* @param red Red coefficient. Values in the range [0..255].
* @param green Green coefficient. Values in the range [0..255].
* @param blue Blue coefficient. Values in the range [0..255].
* @return XYZ color space.
*/
public static float[] RGBtoXYZ(int red, int green, int blue){
float[] xyz = new float[3];
float r = red / 255f;
float g = green / 255f;
float b = blue / 255f;
//R
if ( r > 0.04045)
r = (float)Math.pow(( ( r + 0.055f ) / 1.055f ), 2.4f);
else
r /= 12.92f;
//G
if ( g > 0.04045)
g = (float)Math.pow(( ( g + 0.055f ) / 1.055f ), 2.4f);
else
g /= 12.92f;
//B
if ( b > 0.04045)
b = (float)Math.pow(( ( b + 0.055f ) / 1.055f ), 2.4f);
else
b /= 12.92f;
r *= 100;
g *= 100;
b *= 100;
float x = 0.412453f * r + 0.35758f * g + 0.180423f * b;
float y = 0.212671f * r + 0.71516f * g + 0.072169f * b;
float z = 0.019334f * r + 0.119193f * g + 0.950227f * b;
xyz[0] = x;
xyz[1] = y;
xyz[2] = z;
return xyz;
}
/**
* XYZ -> RGB
* @param x X coefficient.
* @param y Y coefficient.
* @param z Z coefficient.
* @return RGB color space.
*/
public static int[] XYZtoRGB(float x, float y, float z){
int[] rgb = new int[3];
x /= 100;
y /= 100;
z /= 100;
float r = 3.240479f * x - 1.53715f * y - 0.498535f * z;
float g = -0.969256f * x + 1.875991f * y + 0.041556f * z;
float b = 0.055648f * x - 0.204043f * y + 1.057311f * z;
if ( r > 0.0031308 )
r = 1.055f * ( (float)Math.pow(r, 0.4166f) ) - 0.055f;
else
r = 12.92f * r;
if ( g > 0.0031308 )
g = 1.055f * ( (float)Math.pow(g, 0.4166f) ) - 0.055f;
else
g = 12.92f * g;
if ( b > 0.0031308 )
b = 1.055f * ( (float)Math.pow(b, 0.4166f) ) - 0.055f;
else
b = 12.92f * b;
rgb[0] = (int)(r * 255);
rgb[1] = (int)(g * 255);
rgb[2] = (int)(b * 255);
return rgb;
}
/**
* XYZ -> HunterLAB
* @param x X coefficient.
* @param y Y coefficient.
* @param z Z coefficient.
* @return HunterLab coefficient.
*/
public static float[] XYZtoHunterLAB(float x, float y, float z){
float[] hunter = new float[3];
float sqrt = (float)Math.sqrt(y);
float l = 10 * sqrt;
float a = 17.5f * (((1.02f * x) - y) / sqrt);
float b = 7f * ((y - (0.847f * z)) / sqrt);
hunter[0] = l;
hunter[1] = a;
hunter[2] = b;
return hunter;
}
/**
* HunterLAB -> XYZ
* @param l L coefficient.
* @param a A coefficient.
* @param b B coefficient.
* @return XYZ color space.
*/
public static float[] HunterLABtoXYZ(float l, float a, float b){
float[] xyz = new float[3];
float tempY = l / 10f;
float tempX = a / 17.5f * l / 10f;
float tempZ = b / 7f * l / 10f;
float y = tempY * tempY;
float x = (tempX + y) / 1.02f;
float z = -(tempZ - y) / 0.847f;
xyz[0] = x;
xyz[1] = y;
xyz[2] = z;
return xyz;
}
/**
* RGB -> HunterLAB.
* @param red Red coefficient. Values in the range [0..255].
* @param green Green coefficient. Values in the range [0..255].
* @param blue Blue coefficient. Values in the range [0..255].
* @return HunterLAB color space.
*/
public static float[] RGBtoHunterLAB(int red, int green, int blue){
float[] xyz = RGBtoXYZ(red, green, blue);
return XYZtoHunterLAB(xyz[0], xyz[1], xyz[2]);
}
/**
* HunterLAB -> RGB.
* @param l L coefficient.
* @param a A coefficient.
* @param b B coefficient.
* @return RGB color space.
*/
public static int[] HunterLABtoRGB(float l, float a, float b){
float[] xyz = HunterLABtoXYZ(l, a, b);
return XYZtoRGB(xyz[0], xyz[1], xyz[2]);
}
/**
* RGB -> HLS.
* @param red Red coefficient. Values in the range [0..255].
* @param green Green coefficient. Values in the range [0..255].
* @param blue Blue coefficient. Values in the range [0..255].
* @return HLS color space.
*/
public static float[] RGBtoHLS(int red, int green, int blue){
float[] hsl = new float[3];
float r = red / 255f;
float g = green / 255f;
float b = blue / 255f;
float max = Math.max(r,Math.max(r,b));
float min = Math.min(r,Math.min(r,b));
float delta = max - min;
//HSK
float h = 0;
float s = 0;
float l = (max + min) / 2;
if ( delta == 0 ){
// gray color
h = 0;
s = 0.0f;
}
else
{
// get saturation value
s = ( l <= 0.5 ) ? ( delta / ( max + min ) ) : ( delta / ( 2 - max - min ) );
// get hue value
float hue;
if ( r == max )
{
hue = ( ( g - b ) / 6 ) / delta;
}
else if ( g == max )
{
hue = ( 1.0f / 3 ) + ( ( b - r ) / 6 ) / delta;
}
else
{
hue = ( 2.0f / 3 ) + ( ( r - g ) / 6 ) / delta;
}
// correct hue if needed
if ( hue < 0 )
hue += 1;
if ( hue > 1 )
hue -= 1;
h = (int) ( hue * 360 );
}
hsl[0] = h;
hsl[1] = s;
hsl[2] = l;
return hsl;
}
/**
* HLS -> RGB.
* @param hue Hue.
* @param saturation Saturation.
* @param luminance Luminance.
* @return RGB color space.
*/
public static int[] HSLtoRGB(float hue, float saturation, float luminance){
int[] rgb = new int[3];
float r = 0, g = 0, b = 0;
if ( saturation == 0 )
{
// gray values
r = g = b = (int) ( luminance * 255 );
}
else
{
float v1, v2;
float h = (float) hue / 360;
v2 = ( luminance < 0.5 ) ?
( luminance * ( 1 + saturation ) ) :
( ( luminance + saturation ) - ( luminance * saturation ) );
v1 = 2 * luminance - v2;
r = (int) ( 255 * Hue_2_RGB( v1, v2, h + ( 1.0f / 3 ) ) );
g = (int) ( 255 * Hue_2_RGB( v1, v2, h ) );
b = (int) ( 255 * Hue_2_RGB( v1, v2, h - ( 1.0f / 3 ) ) );
}
rgb[0] = (int)r;
rgb[1] = (int)g;
rgb[2] = (int)b;
return rgb;
}
private 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.0f / 3 ) - vH ) * 6 );
return v1;
}
/**
* RGB -> CIE-LAB.
* @param red Red coefficient. Values in the range [0..255].
* @param green Green coefficient. Values in the range [0..255].
* @param blue Blue coefficient. Values in the range [0..255].
* @param tristimulus XYZ Tristimulus.
* @return CIE-LAB color space.
*/
public static float[] RGBtoLAB(int red, int green, int blue, float[] tristimulus){
float[] xyz = RGBtoXYZ(red, green, blue);
float[] lab = XYZtoLAB(xyz[0], xyz[1], xyz[2], tristimulus);
return lab;
}
/**
* CIE-LAB -> RGB.
* @param l L coefficient.
* @param a A coefficient.
* @param b B coefficient.
* @param tristimulus XYZ Tristimulus.
* @return RGB color space.
*/
public static int[] LABtoRGB(float l, float a, float b, float[] tristimulus){
float[] xyz = LABtoXYZ(l, a, b, tristimulus);
return XYZtoRGB(xyz[0], xyz[1], xyz[2]);
}
/**
* XYZ -> CIE-LAB.
* @param x X coefficient.
* @param y Y coefficient.
* @param z Z coefficient.
* @param tristimulus XYZ Tristimulus.
* @return CIE-LAB color space.
*/
public static float[] XYZtoLAB(float x, float y, float z, float[] tristimulus){
float[] lab = new float[3];
x /= tristimulus[0];
y /= tristimulus[1];
z /= tristimulus[2];
if (x > 0.008856)
x = (float)Math.pow(x,0.33f);
else
x = (7.787f * x) + ( 0.1379310344827586f );
if (y > 0.008856)
y = (float)Math.pow(y,0.33f);
else
y = (7.787f * y) + ( 0.1379310344827586f );
if (z > 0.008856)
z = (float)Math.pow(z,0.33f);
else
z = (7.787f * z) + ( 0.1379310344827586f );
lab[0] = ( 116 * y ) - 16;
lab[1] = 500 * ( x - y );
lab[2] = 200 * ( y - z );
return lab;
}
/**
* CIE-LAB -> XYZ.
* @param l L coefficient.
* @param a A coefficient.
* @param b B coefficient.
* @param tristimulus XYZ Tristimulus.
* @return XYZ color space.
*/
public static float[] LABtoXYZ(float l, float a, float b, float[] tristimulus){
float[] xyz = new float[3];
float y = ( l + 16f ) / 116f;
float x = a / 500f + y;
float z = y - b / 200f;
//Y
if ( Math.pow(y,3) > 0.008856 )
y = (float)Math.pow(y,3);
else
y = (float)(( y - 16 / 116 ) / 7.787);
//X
if ( Math.pow(x,3) > 0.008856 )
x = (float)Math.pow(x,3);
else
x = (float)(( x - 16 / 116 ) / 7.787);
// Z
if ( Math.pow(z,3) > 0.008856 )
z = (float)Math.pow(z,3);
else
z = (float)(( z - 16 / 116 ) / 7.787);
xyz[0] = x * tristimulus[0];
xyz[1] = y * tristimulus[1];
xyz[2] = z * tristimulus[2];
return xyz;
}
/**
* RGB -> C1C2C3.
* @param r Red coefficient. Values in the range [0..255].
* @param g Green coefficient. Values in the range [0..255].
* @param b Blue coefficient. Values in the range [0..255].
* @return C1C2C3 color space.
*/
public static float[] RGBtoC1C2C3(int r, int g, int b){
float[] c = new float[3];
c[0] = (float)Math.atan(r / Math.max(g, b));
c[1] = (float)Math.atan(g / Math.max(r, b));
c[2] = (float)Math.atan(b / Math.max(r, g));
return c;
}
/**
* RGB -> O1O2.
* @param r Red coefficient. Values in the range [0..255].
* @param g Green coefficient. Values in the range [0..255].
* @param b Blue coefficient. Values in the range [0..255].
* @return O1O2 color space.
*/
public static float[] RGBtoO1O2(int r, int g, int b){
float[] o = new float[2];
o[0] = (r - g) / 2f;
o[1] = (r + g) / 4f - (b / 2f);
return o;
}
/**
* RGB -> Grayscale.
* @param r Red coefficient. Values in the range [0..255].
* @param g Green coefficient. Values in the range [0..255].
* @param b Blue coefficient. Values in the range [0..255].
* @return Grayscale color space.
*/
public static float RGBtoGrayscale(int r, int g, int b){
return r*0.2125f + g*0.7154f + b*0.0721f;
}
/**
* XYZ -> Philips Hue XY
* @param x X coefficient.
* @param y Y coefficient.
* @param z Z coefficient.
* @return Hue xy array
*/
public static XYColorSpace XYZtoXY(float x, float y, float z){
float[] xy = new float[2];
xy[0] = x / (x + y + z);
xy[1] = y / (x + y + z);
XYColorSpace xyColor = new XYColorSpace();
xyColor.setBrightness((int)Math.round(y * 254.0f));
xyColor.setXy(xy);
return xyColor;
}
/**
* Philips Hue XY -> XYZ
* @param x X coefficient.
* @param y Y coefficient.
* @return XYZ array
*/
public static float[] XYtoXYZ(XYColorSpace xy){
float[] xyz = new float[3];
xyz[0] = (xy.getBrightnessAdjusted() / xy.getXy()[1]) * xy.getXy()[0];
xyz[1] = xy.getBrightnessAdjusted();
xyz[2] = (xy.getBrightnessAdjusted() / xy.getXy()[1]) * (1.0f - xy.getXy()[0] - xy.getXy()[1]);
return xyz;
}
public static int[] normalizeRGB(int[] rgb) {
int[] newRGB = new int[3];
newRGB[0] = assureBounds(rgb[0]);
newRGB[1] = assureBounds(rgb[1]);
newRGB[2] = assureBounds(rgb[2]);
return newRGB;
}
private static int assureBounds(int value) {
if (value < 0.0) {
value = 0;
}
if (value > 255.0) {
value = 255;
}
return value;
}
}

View File

@@ -20,170 +20,120 @@ public class ColorDecode {
private static final String COLOR_GX = "${color.gx}"; private static final String COLOR_GX = "${color.gx}";
private static final String COLOR_BX = "${color.bx}"; private static final String COLOR_BX = "${color.bx}";
private static final String COLOR_RGBX = "${color.rgbx}"; private static final String COLOR_RGBX = "${color.rgbx}";
private static final String COLOR_HSL = "${color.hsl}"; private static final String COLOR_HSB = "${color.hsb}";
private static final String COLOR_H = "${color.h}";
private static final String COLOR_S = "${color.s}";
private static final String COLOR_XY = "${color.xy}";
private static final String COLOR_BRI = "${colorbri}";
private static final Pattern COLOR_MILIGHT = Pattern.compile("\\$\\{color.milight\\:([01234])\\}"); private static final Pattern COLOR_MILIGHT = Pattern.compile("\\$\\{color.milight\\:([01234])\\}");
public static List<Integer> convertHSLtoRGB(HueSatBri hsl) { public static List<Integer> convertHSBtoRGB(HueSatBri hsb) {
List<Integer> rgb; List<Integer> rgb;
float decimalBrightness = (float) 0.0; Float hue = (Float)(hsb.getHue()*1.0f);
float var_1 = (float) 0.0; Float saturation = (Float)(hsb.getSat()*1.0f);
float var_2 = (float) 0.0; Float brightness = (Float)(hsb.getBri()*1.0f);
float h = (float) 0.0; log.info("Hue = " + hue + ", Sat = " + saturation + ", Bri = " + brightness);
float h2 = (float) 0.0; //Convert Hue into degrees for HSB
float s = (float) 0.0; hue = hue / 182.04f;
double r = 0.0; //Bri and Sat must be values from 0-1 (~percentage)
double g = 0.0; brightness = brightness / 255.0f;
double b = 0.0; saturation = saturation / 255.0f;
if(hsl.getBri() > 0) Float r = 0f;
decimalBrightness = (float) (hsl.getBri() / 255.0); Float g = 0f;
Float b = 0f;
if(hsl.getHue() > 0) { if (saturation == 0)
h = ((float)hsl.getHue() / (float)65535.0); {
h2 = h + (float)0.5; r = g = b = brightness;
if(h2 > 1.0) {
h2 = h2 - (float)1.0;
}
} }
if(hsl.getSat() > 0) { else
s = (float)(hsl.getSat() / 254.0); {
// the color wheel consists of 6 sectors.
Float sectorPos = hue / 60.0f;
int sectorNumber = (int)(Math.floor(sectorPos));
// get the fractional part of the sector
Float fractionalSector = sectorPos - sectorNumber;
// calculate values for the three axes of the color.
Float p = brightness * (1.0f - saturation);
Float q = brightness * (1.0f - (saturation * fractionalSector));
Float t = brightness * (1.0f - (saturation * (1f - fractionalSector)));
// assign the fractional colors to r, g, and b based on the sector the angle is in.
switch (sectorNumber)
{
case 0:
r = brightness;
g = t;
b = p;
break;
case 1:
r = q;
g = brightness;
b = p;
break;
case 2:
r = p;
g = brightness;
b = t;
break;
case 3:
r = p;
g = q;
b = brightness;
break;
case 4:
r = t;
g = p;
b = brightness;
break;
case 5:
r = brightness;
g = p;
b = q;
break;
}
} }
if (s == 0) //Check if any value is out of byte range
{ if (r < 0f)
r = decimalBrightness * (float)255; {
g = decimalBrightness * (float)255; r = 0f;
b = decimalBrightness * (float)255; }
} if (g < 0f)
else {
{ g = 0f;
if (decimalBrightness < 0.5) }
{ if (b < 0f)
var_2 = decimalBrightness * (1 + s); {
} b = 0f;
else }
{
var_2 = (decimalBrightness + s) - (s * decimalBrightness);
};
var_1 = 2 * decimalBrightness - var_2;
float onethird = (float)0.33333;
float h2Plus = (h2 + onethird);
float h2Minus = (h2 - onethird);
log.debug("calculate HSL vars - var1: " + var_1 + ", var_2: " + var_2 + ", h2: " + h2 + ", h2 + 1/3: " + h2Plus + ", h2 - 1/3: " + h2Minus);
r = 255 * hue_2_rgb(var_1, var_2, h2Plus);
g = 255 * hue_2_rgb(var_1, var_2, h2);
b = 255 * hue_2_rgb(var_1, var_2, h2Minus);
};
rgb = new ArrayList<Integer>(); rgb = new ArrayList<Integer>();
rgb.add((int) Math.round(r)); rgb.add((int)Math.round(r*255));
rgb.add((int) Math.round(g)); rgb.add((int)Math.round(g*255));
rgb.add((int) Math.round(b)); rgb.add((int)Math.round(b*255));
log.debug("Color change with HSB: " + hsb + ". Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " "
log.debug("Color change with HSL: " + hsl + ". Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " "
+ rgb.get(2)); + rgb.get(2));
return rgb; return rgb;
} }
public static float hue_2_rgb(float v1, float v2, float vh) {
log.debug("hue_2_rgb vh: " + vh);
if (vh < 0.0)
{
vh = vh + (float)1;
};
if (vh > 1.0)
{
vh = vh - (float)1;
};
if (((float)6.0 * vh) < 1.0)
{
return (v1 + (v2 - v1) * (float)6.0 * vh);
};
if (((float)2.0 * vh) < 1.0)
{
return (v2);
};
if ((3.0 * vh) < 2.0)
{
return (v1 + (v2 - v1) * (((float)2.0 / (float)3.0 - vh) * (float)6.0));
};
return (v1);
}
public static List<Integer> convertCIEtoRGB(List<Double> xy, int brightness) { public static List<Integer> convertCIEtoRGB(List<Double> xy, int brightness) {
List<Integer> rgb; List<Integer> rgb;
double x = xy.get(0); // the given x value XYColorSpace xyColor = new XYColorSpace();
double y = xy.get(1); // the given y value xyColor.setBrightness(brightness);
double z = 1.0 - x - y; float[] xyFloat = new float[2];
double Y = (double) brightness / (double) 254.00; // The given brightness value xyFloat[0] = xy.get(0).floatValue();
double X = (Y / y) * x; xyFloat[1] = xy.get(1).floatValue();
double Z = (Y / y) * z; xyColor.setXy(xyFloat);
float[] xyz = ColorConverter.XYtoXYZ(xyColor);
double r = X * 1.656492 - Y * 0.354851 - Z * 0.255038; int[] rgbInt = ColorConverter.normalizeRGB(ColorConverter.XYZtoRGB(xyz[0], xyz[1], xyz[2]));
double g = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
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) {
r = r / g;
b = b / g;
g = 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<Integer>(); rgb = new ArrayList<Integer>();
rgb.add((int) Math.round(r * 255)); rgb.add(rgbInt[0]);
rgb.add((int) Math.round(g * 255)); rgb.add(rgbInt[1]);
rgb.add((int) Math.round(b * 255)); rgb.add(rgbInt[2]);
log.debug("Color change with XY: " + x + " " + y + " Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) log.debug("Color change with XY: " + xy.get(0) + " " + xy.get(1) + " Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1)
+ " " + rgb.get(2)); + " " + rgb.get(2));
return rgb; return rgb;
} }
@@ -253,7 +203,7 @@ public class ColorDecode {
} else if (colorMode == ColorData.ColorMode.CT) { } else if (colorMode == ColorData.ColorMode.CT) {
rgb = convertCTtoRGB((Integer) colorData.getData()); rgb = convertCTtoRGB((Integer) colorData.getData());
} else if (colorMode == ColorData.ColorMode.HS) { } else if (colorMode == ColorData.ColorMode.HS) {
rgb = convertHSLtoRGB((HueSatBri) colorData.getData()); rgb = convertHSBtoRGB((HueSatBri) colorData.getData());
} }
while (notDone) { while (notDone) {
@@ -297,13 +247,66 @@ public class ColorDecode {
notDone = true; notDone = true;
} }
if (request.contains(COLOR_HSL)) { if (request.contains(COLOR_XY)) {
float[] hsb = new float[3]; if (colorMode == ColorData.ColorMode.XY) {
Color.RGBtoHSB(rgb.get(0), rgb.get(1), rgb.get(2), hsb); List<Double> xyData = (List<Double>) colorData.getData();
float hue = hsb[0] * (float) 360.0; request = request.replace(COLOR_XY, String.format("%f,%f", xyData.get(0), xyData.get(1)));
float sat = hsb[1] * (float) 100.0; } else {
float bright = hsb[2] * (float) 100.0; List<Double> xyData = (List<Double>) colorData.getData();
request = request.replace(COLOR_HSL, String.format("%f,%f,%f", hue, sat, bright)); request = request.replace(COLOR_XY, String.format("%f,%f", xyData.get(0), xyData.get(1)));
}
notDone = true;
}
if (request.contains(COLOR_H)) {
if (colorMode == ColorData.ColorMode.HS) {
HueSatBri hslData = (HueSatBri) colorData.getData();
request = request.replace(COLOR_H, String.format("%d", hslData.getHue()));
} else {
float[] hsb = new float[3];
Color.RGBtoHSB(rgb.get(0), rgb.get(1), rgb.get(2), hsb);
float hue = hsb[0] * (float) 360.0;
request = request.replace(COLOR_H, String.format("%f", hue));
}
notDone = true;
}
if (request.contains(COLOR_S)) {
if (colorMode == ColorData.ColorMode.HS) {
HueSatBri hslData = (HueSatBri) colorData.getData();
request = request.replace(COLOR_S, String.format("%d", hslData.getSat()));
} else {
float[] hsb = new float[3];
Color.RGBtoHSB(rgb.get(0), rgb.get(1), rgb.get(2), hsb);
float sat = hsb[1] * (float) 100.0;
request = request.replace(COLOR_S, String.format("%f", sat));
}
notDone = true;
}
if (request.contains(COLOR_BRI)) {
if (colorMode == ColorData.ColorMode.HS) {
HueSatBri hslData = (HueSatBri) colorData.getData();
request = request.replace(COLOR_BRI, String.format("%d", hslData.getBri()));
} else {
request = request.replace(COLOR_BRI, String.format("%f", setIntensity));
}
notDone = true;
}
if (request.contains(COLOR_HSB)) {
if (colorMode == ColorData.ColorMode.HS) {
HueSatBri hslData = (HueSatBri) colorData.getData();
request = request.replace(COLOR_HSB,
String.format("%d,%d,%d", hslData.getHue(), hslData.getSat(), hslData.getBri()));
} else {
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_HSB, String.format("%f,%f,%f", hue, sat, bright));
}
notDone = true; notDone = true;
} }

View File

@@ -473,9 +473,9 @@ public class HueMulator {
if (deviceState != null) { if (deviceState != null) {
deviceState.setOn(stateChanges.isOn()); deviceState.setOn(stateChanges.isOn());
if (!deviceState.isOn() && deviceState.getBri() == 254) if (!deviceState.isOn() && deviceState.getBri() == 254)
deviceState.setBri(0); deviceState.setBri(1);
if (!deviceState.isOn() && offState) if (!deviceState.isOn() && offState)
deviceState.setBri(0); deviceState.setBri(1);
} }
notFirstChange = true; notFirstChange = true;
} }
@@ -609,7 +609,7 @@ public class HueMulator {
notFirstChange = true; notFirstChange = true;
} }
if ((deviceState != null) && deviceState.isOn() && deviceState.getBri() <= 0) if ((deviceState != null) && deviceState.isOn() && deviceState.getBri() <= 1)
deviceState.setBri(254); deviceState.setBri(254);
// if((deviceState != null) && !deviceState.isOn() && (targetBri != null || // if((deviceState != null) && !deviceState.isOn() && (targetBri != null ||
@@ -1416,8 +1416,8 @@ public class HueMulator {
int bri = 0; int bri = 0;
if (targetBriInc != null) { if (targetBriInc != null) {
bri = state.getBri() - targetBriInc; bri = state.getBri() - targetBriInc;
if (bri < 0) if (bri < 1)
bri = 0; bri = 1;
} else if (targetBri != null) { } else if (targetBri != null) {
bri = targetBri; bri = targetBri;
} else { } else {
@@ -1440,8 +1440,8 @@ public class HueMulator {
int bri = 0; int bri = 0;
if (targetBriInc != null) { if (targetBriInc != null) {
bri = state.getBri() - targetBriInc; bri = state.getBri() - targetBriInc;
if (bri < 0) if (bri < 1)
bri = 0; bri = 1;
} else if (targetBri != null) { } else if (targetBri != null) {
bri = targetBri; bri = targetBri;
} else { } else {

View File

@@ -0,0 +1,26 @@
package com.bwssystems.HABridge.hue;
public class XYColorSpace {
float[] xy;
int brightness;
public float[] getXy() {
return xy;
}
public void setXy(float[] xy) {
this.xy = xy;
}
public int getBrightness() {
return brightness;
}
public float getBrightnessAdjusted() {
return ((float) brightness / 254.0f) * 100f;
}
public void setBrightness(int brightness) {
this.brightness = brightness;
}
}

View File

@@ -41,10 +41,10 @@ public class MozIotCommandDetail {
public String getBody() { public String getBody() {
String theBody = ""; String theBody = "";
if(level != null && level != "") { if(level != null && !"".equals(level)) {
theBody = "{\"level\":" + level + "}"; theBody = "{\"level\":" + level + "}";
} }
else if(color != null && color != "") { else if(color != null && !"".equals(color)) {
theBody = "{\"color\":\"" + color + "\"}"; theBody = "{\"color\":\"" + color + "\"}";
} else { } else {
theBody = "{\"on\":" + on + "}"; theBody = "{\"on\":" + on + "}";

View File

@@ -9,6 +9,7 @@ import com.bwssystems.HABridge.Configuration;
import com.bwssystems.HABridge.api.hue.HueConstants; import com.bwssystems.HABridge.api.hue.HueConstants;
import com.bwssystems.HABridge.api.hue.HuePublicConfig; import com.bwssystems.HABridge.api.hue.HuePublicConfig;
import com.bwssystems.HABridge.util.UDPDatagramSender; import com.bwssystems.HABridge.util.UDPDatagramSender;
import com.bwssystems.HABridge.util.AddressUtil;
import java.io.IOException; import java.io.IOException;
import java.net.*; import java.net.*;
@@ -29,6 +30,7 @@ public class UpnpListener {
private String upnpConfigIP; private String upnpConfigIP;
// private boolean strict; // private boolean strict;
private boolean upnpOriginal; private boolean upnpOriginal;
private boolean upnpAdvanced;
private boolean traceupnp; private boolean traceupnp;
private boolean useUpnpIface; private boolean useUpnpIface;
private BridgeControlDescriptor bridgeControl; private BridgeControlDescriptor bridgeControl;
@@ -37,63 +39,39 @@ public class UpnpListener {
private String httpType; private String httpType;
private HuePublicConfig aHueConfig; private HuePublicConfig aHueConfig;
private Integer theUpnpSendDelay; private Integer theUpnpSendDelay;
private String responseTemplate1 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: %s://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: upnp:rootdevice\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: %s://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String responseTemplate3 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: %s://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate1 = "NOTIFY * HTTP/1.1\r\n" + /* This is the minimum response needed, all others are for the advanced setting */
"HOST: %s:%s\r\n" + private String responseTemplate1 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
"CACHE-CONTROL: max-age=100\r\n" + + "EXT:\r\n" + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/"
"LOCATION: %s://%s:%s/description.xml\r\n" + + HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: urn:schemas-upnp-org:device:basic:1\r\n"
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" + + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
"NTS: ssdp:alive\r\n" +
"hue-bridgeid: %s\r\n" +
"NT: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate2 = "NOTIFY * HTTP/1.1\r\n" + /* These next 2 templates are for the advanced upnp option */
"HOST: %s:%s\r\n" + private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
"CACHE-CONTROL: max-age=100\r\n" + + "EXT:\r\n" + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/"
"LOCATION: %s://%s:%s/description.xml\r\n" + + HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: uuid:" + HueConstants.UUID_PREFIX
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" + + "%s\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
"NTS: ssdp:alive\r\n" + private String responseTemplate3 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
"hue-bridgeid: %s\r\n" + + "EXT:\r\n" + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/"
"NT: upnp:rootdevice\r\n" + + HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: upnp:rootdevice\r\n" + "USN: uuid:"
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n"; + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String notifyTemplate3 = "NOTIFY * HTTP/1.1\r\n" +
"HOST: %s:%s\r\n" + /* These notify templates are for the advanced upnp option */
"CACHE-CONTROL: max-age=100\r\n" + private String notifyTemplate1 = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
"LOCATION: %s://%s:%s/description.xml\r\n" + + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/"
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" + + HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n" + "NT: uuid:"
"NTS: ssdp:alive\r\n" + + HueConstants.UUID_PREFIX + "%s\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
"hue-bridgeid: %s\r\n" +
"NT: urn:schemas-upnp-org:device:basic:1\r\n" + private String notifyTemplate2 = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n"; + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n"
+ "NT: upnp:rootdevice\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String notifyTemplate3 = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n"
+ "NT: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
public UpnpListener(BridgeSettings theSettings, BridgeControlDescriptor theControl, public UpnpListener(BridgeSettings theSettings, BridgeControlDescriptor theControl,
UDPDatagramSender aUdpDatagramSender) throws IOException { UDPDatagramSender aUdpDatagramSender) throws IOException {
@@ -104,6 +82,7 @@ public class UpnpListener {
upnpConfigIP = theSettings.getBridgeSettingsDescriptor().getUpnpConfigAddress(); upnpConfigIP = theSettings.getBridgeSettingsDescriptor().getUpnpConfigAddress();
// strict = theSettings.isUpnpStrict(); // strict = theSettings.isUpnpStrict();
upnpOriginal = theSettings.getBridgeSettingsDescriptor().isUpnporiginal(); upnpOriginal = theSettings.getBridgeSettingsDescriptor().isUpnporiginal();
upnpAdvanced = theSettings.getBridgeSettingsDescriptor().isUpnpadvanced();
traceupnp = theSettings.getBridgeSettingsDescriptor().isTraceupnp(); traceupnp = theSettings.getBridgeSettingsDescriptor().isTraceupnp();
useUpnpIface = theSettings.getBridgeSettingsDescriptor().isUseupnpiface(); useUpnpIface = theSettings.getBridgeSettingsDescriptor().isUseupnpiface();
theUpnpSendDelay = theSettings.getBridgeSettingsDescriptor().getUpnpsenddelay(); theUpnpSendDelay = theSettings.getBridgeSettingsDescriptor().getUpnpsenddelay();
@@ -112,17 +91,18 @@ public class UpnpListener {
theSettings.getBridgeSettingsDescriptor().getHubmac()); theSettings.getBridgeSettingsDescriptor().getHubmac());
bridgeId = aHueConfig.getBridgeid(); bridgeId = aHueConfig.getBridgeid();
bridgeSNUUID = aHueConfig.getSNUUIDFromMac(); bridgeSNUUID = aHueConfig.getSNUUIDFromMac();
if(theSettings.getBridgeSecurity().isUseHttps()) { if (theSettings.getBridgeSecurity().isUseHttps()) {
httpType = "https"; httpType = "https";
} else { } else {
httpType = "http"; httpType = "http";
} }
try { try {
if (useUpnpIface) // This commented out code does not work... leave for review
upnpMulticastSocket = new MulticastSocket( // if (useUpnpIface)
new InetSocketAddress(upnpConfigIP, Configuration.UPNP_DISCOVERY_PORT)); // upnpMulticastSocket = new MulticastSocket(
else // new InetSocketAddress(upnpConfigIP, Configuration.UPNP_DISCOVERY_PORT));
// else
upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT); upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
} catch (IOException e) { } catch (IOException e) {
log.error("Upnp Discovery Port is in use, or restricted by admin (try running with sudo or admin privs): " log.error("Upnp Discovery Port is in use, or restricted by admin (try running with sudo or admin privs): "
@@ -165,7 +145,7 @@ public class UpnpListener {
if (traceupnp) if (traceupnp)
log.info("Traceupnp: Interface: " + name + " valid usable IP address: " + addr); log.info("Traceupnp: Interface: " + name + " valid usable IP address: " + addr);
IPsPerNic++; IPsPerNic++;
} else if (addr.getHostAddress().equals(upnpConfigIP)) { } else if (useUpnpIface && (addr.getHostAddress().equals(upnpConfigIP) || name.equals("lo"))) {
if (traceupnp) if (traceupnp)
log.info("Traceupnp: Interface: " + name + " matches upnp config address of IP address: " log.info("Traceupnp: Interface: " + name + " matches upnp config address of IP address: "
+ addr); + addr);
@@ -203,7 +183,8 @@ public class UpnpListener {
final HashMap<String, String> values = new HashMap<String, String>(); final HashMap<String, String> values = new HashMap<String, String>();
values.put("modelid", HueConstants.MODEL_ID); values.put("modelid", HueConstants.MODEL_ID);
values.put("bridgeid", bridgeId); values.put("bridgeid", bridgeId);
ServiceInfo serviceInfo = ServiceInfo.create("_hue._tcp.local.", "Philips Hue - " + bridgeId.substring(bridgeId.length() - 6), httpServerPort, 0, 0, values); ServiceInfo serviceInfo = ServiceInfo.create("_hue._tcp.local.",
"Philips Hue - " + bridgeId.substring(bridgeId.length() - 6), httpServerPort, 0, 0, values);
jmdns.registerService(serviceInfo); jmdns.registerService(serviceInfo);
} catch (IOException e) { } catch (IOException e) {
@@ -219,13 +200,16 @@ public class UpnpListener {
log.info("UPNP Discovery Listener running and ready...."); log.info("UPNP Discovery Listener running and ready....");
boolean loopControl = true; boolean loopControl = true;
boolean error = false; boolean error = false;
try {
upnpMulticastSocket.setSoTimeout((int) Configuration.UPNP_NOTIFY_TIMEOUT); if(upnpAdvanced) {
} catch (SocketException e1) { try {
log.warn("Could not sent soTimeout on multi-cast socket"); upnpMulticastSocket.setSoTimeout((int) Configuration.UPNP_NOTIFY_TIMEOUT);
} catch (SocketException e1) {
log.warn("Could not sent soTimeout on multi-cast socket");
}
} }
// Instant current, previous; // Instant current, previous;
// previous = Instant.now(); // previous = Instant.now();
while (loopControl) { // trigger shutdown here while (loopControl) { // trigger shutdown here
byte[] buf = new byte[1024]; byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length); DatagramPacket packet = new DatagramPacket(buf, buf.length);
@@ -234,26 +218,23 @@ public class UpnpListener {
if (isSSDPDiscovery(packet)) { if (isSSDPDiscovery(packet)) {
try { try {
sendUpnpResponse(packet); sendUpnpResponse(packet);
} catch (IOException e) { } catch (Exception e) {
log.warn("UpnpListener encountered an error sending upnp response packet. IP: " log.warn("UpnpListener encountered an error sending upnp response packet. IP: "
+ packet.getAddress().getHostAddress() + " with message: " + e.getMessage()); + packet.getAddress().getHostAddress() + " with message: " + e.getMessage());
log.debug("UpnpListener send upnp exception: ", e); log.debug("UpnpListener send upnp exception: ", e);
} }
} }
/* /*
current = Instant.now(); * current = Instant.now(); if (ChronoUnit.MILLIS.between(previous, current) >
if (ChronoUnit.MILLIS.between(previous, current) > Configuration.UPNP_NOTIFY_TIMEOUT) { * Configuration.UPNP_NOTIFY_TIMEOUT) { try {
try { * sendUpnpNotify(socketAddress.getAddress()); } catch (IOException e) { log.
sendUpnpNotify(socketAddress.getAddress()); * warn("UpnpListener encountered an error sending upnp notify packets. IP: " +
} catch (IOException e) { * packet.getAddress().getHostAddress() + " with message: " + e.getMessage());
log.warn("UpnpListener encountered an error sending upnp notify packets. IP: " * log.debug("UpnpListener send upnp notify exception: ", e); } previous =
+ packet.getAddress().getHostAddress() + " with message: " + e.getMessage()); * Instant.now();
log.debug("UpnpListener send upnp notify exception: ", e); *
} * }
previous = Instant.now(); */
}
*/
} catch (SocketTimeoutException e) { } catch (SocketTimeoutException e) {
try { try {
sendUpnpNotify(socketAddress.getAddress()); sendUpnpNotify(socketAddress.getAddress());
@@ -276,7 +257,7 @@ public class UpnpListener {
} }
} }
upnpMulticastSocket.close(); upnpMulticastSocket.close();
if(jmdns != null) { if (jmdns != null) {
// Unregister all services // Unregister all services
jmdns.unregisterAllServices(); jmdns.unregisterAllServices();
} }
@@ -297,11 +278,11 @@ public class UpnpListener {
protected boolean isSSDPDiscovery(DatagramPacket packet) { protected boolean isSSDPDiscovery(DatagramPacket packet) {
// Only respond to discover request for strict upnp form // Only respond to discover request for strict upnp form
String packetString = new String(packet.getData(), 0, packet.getLength()); String packetString = new String(packet.getData(), 0, packet.getLength());
// log.info("Packet string <<<" + packetString + ">>>");
if (packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1") if (packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1")
&& packetString.contains("\"ssdp:discover\"")) { && packetString.contains("\"ssdp:discover\"")) {
if ((packetString.contains("ST: urn:schemas-upnp-org:device:basic:1") || if ((packetString.contains("ST: urn:schemas-upnp-org:device:basic:1")
packetString.contains("ST: upnp:rootdevice") || || packetString.contains("ST: upnp:rootdevice") || packetString.contains("ST: ssdp:all"))) {
packetString.contains("ST: ssdp:all"))) {
if (traceupnp) { if (traceupnp) {
log.info("Traceupnp: SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":" log.info("Traceupnp: SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":"
+ packet.getPort()); + packet.getPort());
@@ -327,41 +308,49 @@ public class UpnpListener {
} }
protected void sendUpnpResponse(DatagramPacket aPacket) throws IOException { protected void sendUpnpResponse(DatagramPacket aPacket) throws IOException {
SocketAddress requesterAddress = aPacket.getSocketAddress(); // SocketAddress requesterAddress = aPacket.getSocketAddress();
InetAddress requester = aPacket.getAddress(); InetAddress requester = aPacket.getAddress();
int sourcePort = aPacket.getPort(); int sourcePort = aPacket.getPort();
String discoveryResponse = null; String discoveryResponse = null;
// refactored suggestion by https://github.com/pvint String httpLocationAddress = null;
String httpLocationAddress = getOutboundAddress(requesterAddress).getHostAddress(); if (useUpnpIface) {
httpLocationAddress = upnpConfigIP;
} else {
// refactored suggestion by https://github.com/pvint
httpLocationAddress = AddressUtil.getOutboundAddress(requester.getHostAddress(), sourcePort).getHostAddress();
}
try { try {
Thread.sleep(theUpnpSendDelay); Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) { } catch (InterruptedException e) {
// noop // noop
} }
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS, discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID); Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId,
if (traceupnp) { bridgeSNUUID);
log.info("Traceupnp: send upnp discovery template 1 with response address: " + httpLocationAddress + ":" if (traceupnp) {
+ httpServerPort + " to address: " + requester + ":" + sourcePort); log.info("Traceupnp: send upnp discovery template 1 with response address: " + httpLocationAddress + ":"
} + httpServerPort + " to address: " + requester.getHostAddress() + ":" + sourcePort);
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort }
+ " with discovery responseTemplate1 is <<<" + discoveryResponse + ">>>"); log.debug("sendUpnpResponse to address: " + requester.getHostAddress() + ":" + sourcePort
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); + " with discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
if(upnpAdvanced) {
try { try {
Thread.sleep(theUpnpSendDelay); Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) { } catch (InterruptedException e) {
// noop // noop
} }
discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID, Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId,
bridgeSNUUID); bridgeSNUUID, bridgeSNUUID);
if (traceupnp) { if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":" log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort); + httpServerPort + " to address: " + requester.getHostAddress() + ":" + sourcePort);
} }
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort log.debug("sendUpnpResponse to address: " + requester.getHostAddress() + ":" + sourcePort
+ " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>"); + " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
@@ -370,20 +359,22 @@ public class UpnpListener {
} catch (InterruptedException e) { } catch (InterruptedException e) {
// noop // noop
} }
discoveryResponse = String.format(responseTemplate3,Configuration.UPNP_MULTICAST_ADDRESS, discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID); Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId,
bridgeSNUUID);
if (traceupnp) { if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":" log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort); + httpServerPort + " to address: " + requester.getHostAddress() + ":" + sourcePort);
} }
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort log.debug("sendUpnpResponse to address: " + requester.getHostAddress() + ":" + sourcePort
+ " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>"); + " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
}
} }
private void sendUDPResponse(byte[] udpMessage, InetAddress requester, int sourcePort) throws IOException { private void sendUDPResponse(byte[] udpMessage, InetAddress requester, int sourcePort) throws IOException {
log.debug("Sending response string: <<<" + new String(udpMessage) + ">>>"); log.debug("Sending response string: <<<" + new String(udpMessage) + ">>>");
if(upnpOriginal) { if (upnpOriginal) {
theUDPDatagramSender.sendUDPResponse(udpMessage, requester, sourcePort); theUDPDatagramSender.sendUDPResponse(udpMessage, requester, sourcePort);
} else { } else {
if (upnpMulticastSocket == null) if (upnpMulticastSocket == null)
@@ -402,7 +393,8 @@ public class UpnpListener {
} }
notifyData = String.format(notifyTemplate1, Configuration.UPNP_MULTICAST_ADDRESS, notifyData = String.format(notifyTemplate1, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpType, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID); Configuration.UPNP_DISCOVERY_PORT, httpType, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID,
bridgeSNUUID);
if (traceupnp) { if (traceupnp) {
log.info("Traceupnp: sendUpnpNotify notifyTemplate1"); log.info("Traceupnp: sendUpnpNotify notifyTemplate1");
} }
@@ -437,21 +429,4 @@ public class UpnpListener {
log.debug("sendUpnpNotify notifyTemplate3 is <<<{}>>>", notifyData); log.debug("sendUpnpNotify notifyTemplate3 is <<<{}>>>", notifyData);
sendUDPResponse(notifyData.getBytes(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT); sendUDPResponse(notifyData.getBytes(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
} }
// added by https://github.com/pvint
// Ruthlessly stolen from
// https://stackoverflow.com/questions/22045165/java-datagrampacket-receive-how-to-determine-local-ip-interface
// Try to get a source IP that makes sense for the requestor to contact for use
// in the LOCATION header in replies
private InetAddress getOutboundAddress(SocketAddress remoteAddress) throws SocketException {
DatagramSocket sock = new DatagramSocket();
// connect is needed to bind the socket and retrieve the local address
// later (it would return 0.0.0.0 otherwise)
sock.connect(remoteAddress);
final InetAddress localAddress = sock.getLocalAddress();
sock.disconnect();
sock.close();
sock = null;
return localAddress;
}
} }

View File

@@ -7,14 +7,10 @@ import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.BridgeSettingsDescriptor; import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.api.hue.HueConstants; import com.bwssystems.HABridge.api.hue.HueConstants;
import com.bwssystems.HABridge.api.hue.HuePublicConfig; import com.bwssystems.HABridge.api.hue.HuePublicConfig;
import com.bwssystems.HABridge.util.ParseRoute; import com.bwssystems.HABridge.util.AddressUtil;
import static spark.Spark.get; import static spark.Spark.get;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
/** /**
* *
*/ */
@@ -59,10 +55,12 @@ public class UpnpSettingsResource {
+ "<depth>24</depth>\n" + "<depth>24</depth>\n"
+ "<url>hue_logo_3.png</url>\n" + "<url>hue_logo_3.png</url>\n"
+ "</icon>\n" + "</icon>\n"
+ "</iconList>\n" + "</iconList>\n";
+ "</device>\n"
+ "</root>\n";
private String hueTemplate_end = "</device>\n"
+ "</root>\n";
/* not utilizing this section any more
private String hueTemplate_mid_orig = "<serviceList>\n" private String hueTemplate_mid_orig = "<serviceList>\n"
+ "<service>\n" + "<service>\n"
+ "<serviceType>(null)</serviceType>\n" + "<serviceType>(null)</serviceType>\n"
@@ -72,7 +70,7 @@ public class UpnpSettingsResource {
+ "<SCPDURL>(null)</SCPDURL>\n" + "<SCPDURL>(null)</SCPDURL>\n"
+ "</service>\n" + "</service>\n"
+ "</serviceList>\n"; + "</serviceList>\n";
*/
public UpnpSettingsResource(BridgeSettings theBridgeSettings) { public UpnpSettingsResource(BridgeSettings theBridgeSettings) {
super(); super();
@@ -96,10 +94,24 @@ public class UpnpSettingsResource {
String hueTemplate = null; String hueTemplate = null;
if(theSettings.isUpnporiginal()) { if(theSettings.isUpnporiginal()) {
httpLocationAddr = theSettings.getUpnpConfigAddress(); httpLocationAddr = theSettings.getUpnpConfigAddress();
hueTemplate = hueTemplate_pre + hueTemplate_mid_orig + hueTemplate_post; hueTemplate = hueTemplate_pre + hueTemplate_end;
} else if(!theSettings.isUpnpadvanced()) {
if(theSettings.isUseupnpiface()) {
httpLocationAddr = theSettings.getUpnpConfigAddress();
} else {
log.debug("Get Outbound address for ip:" + request.ip() + " and port:" + request.port());
httpLocationAddr = AddressUtil.getOutboundAddress(request.ip(), request.port()).getHostAddress();
}
hueTemplate = hueTemplate_pre + hueTemplate_end;
} else { } else {
httpLocationAddr = getOutboundAddress(request.ip(), request.port()).getHostAddress();
hueTemplate = hueTemplate_pre + hueTemplate_post; if(theSettings.isUseupnpiface()) {
httpLocationAddr = theSettings.getUpnpConfigAddress();
} else {
log.debug("Get Outbound address for ip:" + request.ip() + " and port:" + request.port());
httpLocationAddr = AddressUtil.getOutboundAddress(request.ip(), request.port()).getHostAddress();
}
hueTemplate = hueTemplate_pre + hueTemplate_post + hueTemplate_end;
} }
String bridgeIdMac = HuePublicConfig.createConfig("temp", httpLocationAddr, HueConstants.HUB_VERSION, theSettings.getHubmac()).getSNUUIDFromMac(); String bridgeIdMac = HuePublicConfig.createConfig("temp", httpLocationAddr, HueConstants.HUB_VERSION, theSettings.getHubmac()).getSNUUIDFromMac();
@@ -136,28 +148,4 @@ public class UpnpSettingsResource {
return ""; return "";
} ); } );
} }
// added by https://github.com/pvint
// Ruthlessly stolen from https://stackoverflow.com/questions/22045165/java-datagrampacket-receive-how-to-determine-local-ip-interface
// Try to get a source IP that makes sense for the requester to contact for use in the LOCATION header in replies
private InetAddress getOutboundAddress(String remoteAddress, int remotePort) {
InetAddress localAddress = null;
try {
DatagramSocket sock = new DatagramSocket();
// connect is needed to bind the socket and retrieve the local address
// later (it would return 0.0.0.0 otherwise)
sock.connect(new InetSocketAddress(remoteAddress, remotePort));
localAddress = sock.getLocalAddress();
sock.disconnect();
sock.close();
sock = null;
} catch(Exception e) {
ParseRoute theRoute = ParseRoute.getInstance();
try {
localAddress = InetAddress.getByName(theRoute.getLocalIPAddress());
} catch(Exception e1) {}
log.warn("Error <" + e.getMessage() + "> on determining interface to reply for <" + remoteAddress + ">. Using default route IP Address of " + localAddress.getHostAddress());
}
log.debug("getOutbountAddress returning IP Address of " + localAddress.getHostAddress());
return localAddress;
}
} }

View File

@@ -0,0 +1,62 @@
package com.bwssystems.HABridge.util;
import java.net.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AddressUtil {
final static Logger log = LoggerFactory.getLogger(AddressUtil.class);
// added by https://github.com/pvint
// Ruthlessly stolen from
// https://stackoverflow.com/questions/22045165/java-datagrampacket-receive-how-to-determine-local-ip-interface
// Try to get a source IP that makes sense for the requester to contact for use
// in the LOCATION header in replies
public static InetAddress getOutboundAddress(String remoteAddress, int remotePort) {
InetAddress localAddress = null;
log.debug("Entering getOutboundAddress with args.");
try {
localAddress = getOutboundAddress(new InetSocketAddress(remoteAddress, remotePort));
} catch (Exception e) {
log.debug("getOutboundAddress(SocketAddress) Threw an Exception: " + e.getMessage());
ParseRoute theRoute = ParseRoute.getInstance();
try {
localAddress = InetAddress.getByName(theRoute.getLocalIPAddress());
log.warn("Error <" + e.getMessage() + "> on determining interface to reply for <" + remoteAddress
+ ">. Using default route IP Address of " + localAddress.getHostAddress());
} catch (Exception e1) {
log.error("Cannot find address for parsed local ip address: " + e.getMessage());
}
}
if(localAddress != null)
log.debug("localAddress is IP Address of " + localAddress.getHostAddress());
else
log.debug("localAddress returning NULL");
return localAddress;
}
// added by https://github.com/pvint
// Ruthlessly stolen from
// https://stackoverflow.com/questions/22045165/java-datagrampacket-receive-how-to-determine-local-ip-interface
// Try to get a source IP that makes sense for the requestor to contact for use
// in the LOCATION header in replies
public static InetAddress getOutboundAddress(SocketAddress remoteAddress) throws SocketException {
DatagramSocket sock = new DatagramSocket();
// connect is needed to bind the socket and retrieve the local address
// later (it would return 0.0.0.0 otherwise)
log.debug("Entering getOutboundAddress with socket arg.");
sock.connect(remoteAddress);
log.debug("getOutboundAddress(SocketAddress) getLocalAddress.");
final InetAddress localAddress = sock.getLocalAddress();
sock.disconnect();
sock.close();
sock = null;
if(localAddress != null)
log.debug("getOutbountAddress(SocketAddress) returning IP Address of " + localAddress.getHostAddress());
else
log.debug("getOutboundAddress(SocketAddress) returning NULL");
return localAddress;
}
}

View File

@@ -824,6 +824,11 @@
<td><input type="checkbox" ng-model="bridge.settings.upnporiginal" ng-true-value=true <td><input type="checkbox" ng-model="bridge.settings.upnporiginal" ng-true-value=true
ng-false-value=false> {{bridge.settings.upnporiginal}}</td> ng-false-value=false> {{bridge.settings.upnporiginal}}</td>
</tr> </tr>
<tr>
<td>UPNP Advanced (use multiple responses and notifies)</td>
<td><input type="checkbox" ng-model="bridge.settings.upnpadvanced" ng-true-value=true
ng-false-value=false> {{bridge.settings.upnpadvanced}}</td>
</tr>
<tr> <tr>
<td>Trace UPNP Calls</td> <td>Trace UPNP Calls</td>
<td><input type="checkbox" ng-model="bridge.settings.traceupnp" ng-true-value=true <td><input type="checkbox" ng-model="bridge.settings.traceupnp" ng-true-value=true

View File

@@ -9,23 +9,92 @@ import org.junit.Test;
import com.bwssystems.HABridge.hue.ColorData; import com.bwssystems.HABridge.hue.ColorData;
import com.bwssystems.HABridge.hue.ColorDecode; import com.bwssystems.HABridge.hue.ColorDecode;
import com.bwssystems.HABridge.hue.HueSatBri;
import com.bwssystems.HABridge.hue.XYColorSpace;
import com.bwssystems.HABridge.hue.ColorConverter;
public class ConvertCIEColorTestCase { public class ConvertCIEColorTestCase {
@Test @Test
public void testColorConversion() { public void testColorConverterXYtoRGB() {
ArrayList<Double> xy = new ArrayList<Double>(Arrays.asList(Double.parseDouble("0.671254"), Double.parseDouble("0.303273"))); ArrayList<Double> xy = new ArrayList<Double>(Arrays.asList(Double.parseDouble("0.671254"), Double.parseDouble("0.303273")));
List<Integer> colorDecode = ColorDecode.convertCIEtoRGB(xy, 254); XYColorSpace xyColor = new XYColorSpace();
xyColor.setBrightness(50);
float[] xyFloat = new float[2];
xyFloat[0] = xy.get(0).floatValue();
xyFloat[1] = xy.get(1).floatValue();
xyColor.setXy(xyFloat);
float[] xyz = ColorConverter.XYtoXYZ(xyColor);
int[] rgb = ColorConverter.normalizeRGB(ColorConverter.XYZtoRGB(xyz[0], xyz[1], xyz[2]));
List<Integer> rgbDecode = new ArrayList<Integer>();
rgbDecode.add(0, rgb[0]);
rgbDecode.add(1, rgb[1]);
rgbDecode.add(2, rgb[2]);
List<Integer> assertDecode = new ArrayList<Integer>(); List<Integer> assertDecode = new ArrayList<Integer>();
assertDecode.add(0, 255); assertDecode.add(0, 255);
assertDecode.add(1, 47); assertDecode.add(1, 0);
assertDecode.add(2, 43); assertDecode.add(2, 5);
Assert.assertEquals(rgbDecode, assertDecode);
}
@Test
public void testColorConversionXYtoRGB1() {
ArrayList<Double> xy = new ArrayList<Double>(Arrays.asList(Double.parseDouble("0.671254"), Double.parseDouble("0.303273")));
List<Integer> colorDecode = ColorDecode.convertCIEtoRGB(xy, 50);
List<Integer> assertDecode = new ArrayList<Integer>();
assertDecode.add(0, 255);
assertDecode.add(1, 0);
assertDecode.add(2, 5);
Assert.assertEquals(colorDecode, assertDecode); Assert.assertEquals(colorDecode, assertDecode);
ColorData colorData = new ColorData(ColorData.ColorMode.XY, xy); ColorData colorData = new ColorData(ColorData.ColorMode.XY, xy);
int rgbIntVal = ColorDecode.getIntRGB(colorData, 254); int rgbIntVal = ColorDecode.getIntRGB(colorData, 50);
Assert.assertEquals(rgbIntVal, 16723755); Assert.assertEquals(rgbIntVal, 16711685);
}
@Test
public void testColorConversionXYtoRGB2() {
ArrayList<Double> xy = new ArrayList<Double>(Arrays.asList(Double.parseDouble("0.32312"), Double.parseDouble("0.15539")));
List<Integer> colorDecode = ColorDecode.convertCIEtoRGB(xy, 59);
List<Integer> assertDecode = new ArrayList<Integer>();
assertDecode.add(0, 233);
assertDecode.add(1, 0);
assertDecode.add(2, 231);
Assert.assertEquals(colorDecode, assertDecode);
ColorData colorData = new ColorData(ColorData.ColorMode.XY, xy);
int rgbIntVal = ColorDecode.getIntRGB(colorData, 59);
Assert.assertEquals(rgbIntVal, 15270119);
}
@Test
public void testColorConversionHSBtoRGB1() {
HueSatBri hsb = new HueSatBri();
hsb.setHue(37767);
hsb.setSat(135);
hsb.setBri(128);
List<Integer> colorDecode = ColorDecode.convertHSBtoRGB(hsb);
List<Integer> assertDecode = new ArrayList<Integer>();
assertDecode.add(0, 60);
assertDecode.add(1, 97);
assertDecode.add(2, 128);
Assert.assertEquals(colorDecode, assertDecode);
}
@Test
public void testColorConversionCTtoRGB() {
Integer ct = 500;
List<Integer> colorDecode = ColorDecode.convertCTtoRGB(ct);
List<Integer> assertDecode = new ArrayList<Integer>();
assertDecode.add(0, 255);
assertDecode.add(1, 137);
assertDecode.add(2, 14);
Assert.assertEquals(colorDecode, assertDecode);
} }
} }