Compare commits

...

598 Commits

Author SHA1 Message Date
dependabot[bot]
6234874cb3 Bump ch.qos.logback:logback-classic from 1.2.1 to 1.2.13
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.2.1 to 1.2.13.
- [Commits](https://github.com/qos-ch/logback/compare/v_1.2.1...v_1.2.13)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-05 22:08:33 +00:00
BWS Systems
483b165f2a update pom.xml to correct version umber 2022-02-10 10:32:19 -05:00
BWS Systems
e2969c1a49 Merge pull request #1358 from bwssytems/devbranch5.4.1
Devbranch5.4.1
fixes #1267
2022-01-30 15:17:42 -05:00
BWS Systems
c99777efe7 Merge pull request #1318 from bwssytems/dependabot/maven/org.apache.httpcomponents-httpclient-4.5.13
Bump httpclient from 4.5.1 to 4.5.13
2022-01-30 15:01:27 -05:00
BWS Systems
8363d4c78c Merge pull request #1314 from hanno2003/master
Fixed typo in docker setup script
2022-01-30 15:00:23 -05:00
dependabot[bot]
e015bca721 Bump httpclient from 4.5.1 to 4.5.13
Bumps httpclient from 4.5.1 to 4.5.13.

---
updated-dependencies:
- dependency-name: org.apache.httpcomponents:httpclient
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-04 01:05:03 +00:00
hgeorg
3b5eea0dce Merge pull request #1 from hgeorg/hgeorg-patch-1
Update README.md
2021-05-25 23:37:55 +02:00
hgeorg
25e8b3f24f Update README.md
Fixed typo in docker script
2021-05-25 23:37:35 +02:00
crush157
7f72b0311a Add option to set hue uid to 9 octets 2021-03-24 14:31:11 -04:00
BWS Systems
b3f2c25721 Merge pull request #1266 from bwssytems/dev_branch_5.3.x
Dev branch 5.3.x to create v5.4.0

#1044 Alexa app, error "Device doesn't support requested value" bug question
#1118 Device status update in Alexa App is not working correctly for ha-bridge devices bug duplicate question
#1126 Can't get XY ColorData into URL enhancement question
#1127 Can't get separate hue, saturation and brightness enhancement question
#1128 Wrong color calucation in HS mode bug question
#1131 Changing color temperatur is not working correctly bug question
#1132 Conversion from CIE to RGB incorrect bug question
#1139 Device ID creation fails bug question
#1150 Home Assistant gets null exception during initialization bug question
#1170 Alexa not sending the correct color codes duplicate
#1171 Please add option: If bright request present with on, only send dim enhancement
#1184 Alexa will not discover HABridge question
#1191 When "Use UPNP Address Interface Only" is selected, echo cannot find ha-bridge bug
#1210 [Enahnacement] Have link button timeout be configurable enhancement
#1215 Alexa unable to discover recently added devices to ha-bridge duplicate question
#1227 Feature request: ${intensity.previous} value passing control enhancement
#1239 HA-Bridge has suddenly given up itself - Exception in thread "main" java.lang.NullPointerException bug question
#1243 Alexa device discovery now broke question
#1253 HABridge hue ColorDecode 0 0 0 bug question
#1256 Homeassistant devices not appearing bug question
2020-12-14 12:20:52 -06:00
bwssystems
98008428a7 Fixed HomeAssistant issues and this is teh release for 5.4.0 2020-12-14 12:17:44 -06:00
bwssystems
675e74df7b Add switch for dim when color request is present and other items 2020-12-08 17:03:24 -06:00
bwssystems
fb7aabb780 Fix all color handling and dim with no on request 2020-12-07 18:07:42 -06:00
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
BWS Systems
d014240fba Update handling of https info on restart if failure 2019-07-25 13:20:43 -05:00
BWS Systems
5c2d30e24b a few fixes and trying to handle secure https fail on start 2019-07-24 16:10:59 -05:00
BWS Systems
28d84f667a Fixed broke upnp responses and notifies 2019-07-18 13:41:17 -05:00
BWS Systems
755533b30d changed hue-bridgeid to be lower case 2019-07-16 16:10:55 -05:00
BWS Systems
53208ddabc missed use https in the response of the upnp listener 2019-07-15 08:38:07 -05:00
BWS Systems
ff2973e473 Update Readme for https 2019-07-12 09:11:53 -05:00
BWS Systems
743656cab3 Updated mDNS host and naming 2019-07-11 13:41:34 -05:00
BWS Systems
53be3ba213 Added https to web and api that will use java keytool 2019-07-10 16:07:22 -05:00
BWS Systems
a5ee0aafc8 Update upnp original to be like v3.5.1 2019-07-02 10:57:46 -05:00
BWS Systems
aed8ffa8d3 Remove extra SEARCH response, use upnp original for udp send change 2019-07-01 15:47:23 -05:00
BWS Systems
369e5a25e6 Fixed bulk add and sort on moziot page 2019-06-28 15:05:57 -05:00
BWS Systems
3fe19f5d4e Updated a few issues
Updated the hue uniqueid generation. Updated the moziot login handling. updated upnp response message to have hue-bridgeId in capital letters - HUE-BRIDGEID
2019-06-28 15:02:28 -05:00
BWS Systems
5736bb92db Issue with tabs in moziot page 2019-06-28 10:11:32 -05:00
BWS Systems
b61f334826 updated tab issues and buildUrls call in app.js 2019-06-28 10:05:19 -05:00
BWS Systems
556a5fef1c Mozilla IOT changed Thing JSON object already for name to title 2019-06-27 16:00:05 -05:00
BWS Systems
4b0152060f Fix HomeGenie response review for errors. Updated Readme, removed the row index from the devices tab 2019-06-27 10:55:40 -05:00
BWS Systems
fcb31b8f76 Update for HomeGenie type handling 2019-06-26 14:05:36 -05:00
BWS Systems
0205633684 Update version to next release as the tag is on the wrong branch 2019-06-25 09:49:55 -05:00
BWS Systems
7b48590807 updated upnp notify to add root and basic schemas 2019-06-25 09:27:10 -05:00
BWS Systems
5f7cd70710 Added back to top button on long scroll 2019-06-17 13:04:07 -05:00
BWS Systems
46ad4489ad Finish HomeAssistant auth changes and implement no scroll for pages 2019-06-14 10:32:15 -05:00
BWS Systems
bfd1b94473 Start adding new Bearer Token for Home Assistant 2019-06-13 15:51:49 -05:00
BWS Systems
7d920d3885 Update pom.xml for build instructions for java 1.8 and raspberry pi 2019-06-13 10:11:24 -05:00
BWS Systems
2dbf4c96c4 Finished config up/down load impl and Finished startup action implementation 2019-06-12 16:05:29 -05:00
BWS Systems
69b510ae18 Finished upload portion of device db 2019-06-11 15:28:54 -05:00
BWS Systems
e86b700e93 Add lock id to device, adding download of backups 2019-06-10 16:35:10 -05:00
BWS Systems
a05b933e43 Completed HomeGenie implementation 2019-06-05 16:08:11 -05:00
BWS Systems
fe0b072b4e Add renumbering and HomeGenie Helper 2019-06-04 16:36:21 -05:00
BWS Systems
3e76e6298a tested moziot and working 2019-06-03 16:06:03 -05:00
BWS Systems
f266945b7e working on ssl calls 2019-05-31 15:19:24 -05:00
BWS Systems
2d3fac691b Update state error messages Homes, update gateway login 2019-05-30 15:45:35 -05:00
BWS Systems
5f6bfae41a Debugging MOzilla IOT impl 2019-05-24 15:22:34 -05:00
BWS Systems
79d5b5da28 Fixed color handling, starter Mozilla IOT integration 2019-05-22 15:30:50 -05:00
BWS Systems
45e2b63f98 Updating color handling 2019-05-21 16:44:38 -05:00
BWS Systems
b6b9089ec4 Merge pull request #1028 from 20goto10/feature/hsl-support
HSL color integration merge to target_5_2_next
2019-05-21 09:57:43 -05:00
BWS Systems
063055f5c8 Merge pull request #1045 from gaudryc/master
Logout request and Shutdown hook merge to target_5_2_next
2019-05-21 09:56:57 -05:00
BWS Systems
c1dc89704d Merge pull request #1085 from bwssytems/FixesTarget5.2.2
Fixes target5.2.2 completed and ready for release
2019-05-03 13:04:53 -05:00
BWS Systems
f97c718568 Update a few fixes
Fixed items for FHEM and Domoticz
2019-05-03 12:57:56 -05:00
BWS Systems
d05b6bea5c moved java target back to 1.8 2019-04-23 09:59:04 -05:00
BWS Systems
2f567cd604 remove JAXB dependency and update harmony disconnect detection 2019-04-23 09:56:49 -05:00
gaudryc
3971c81449 Bug: Comparison of String objects using == or != in
com.bwssystems.HABridge.plugins.http.HTTPHandler.doHttpRequest(String,
String, String, String, NameValue[])
2019-01-06 22:01:39 +01:00
gaudryc
e1b5aede66 Bug: Possible null pointer dereference in
com.bwssystems.HABridge.hue.HueMulator.formatSuccessHueResponse(StateChangeBody,
String, String, DeviceState, Integer, Integer, ColorData, boolean)
2019-01-06 21:58:01 +01:00
gaudryc
14c3614856 Bug: Possible null pointer dereference in
com.bwssystems.HABridge.hue.HueMulator.callUrl(String, DeviceDescriptor,
String, String, String, String, boolean, Integer, Integer, ColorData)
2019-01-06 21:54:23 +01:00
gaudryc
1311d4a68d Bug: Possible null pointer dereference in method
com.bwssystems.HABridge.plugins.broadlink.BroadlinkHome.deviceHandler(CallItem,
MultiCommandUtil, String, int, Integer, Integer, ColorData,
DeviceDescriptor, String) on exception path
2019-01-06 21:43:39 +01:00
gaudryc
9cbc290768 Bug: com.bwssystems.HABridge.plugins.hass.HassHome.addHassDevices(List,
List, String) has Boolean return type and returns explicit null
2019-01-06 20:09:49 +01:00
gaudryc
f90f39e5f1 Bug: com.bwssystems.HABridge.plugins.hal.HalHome.addHalDevices(List,
List, String) has Boolean return type and returns explicit null
2019-01-06 20:08:38 +01:00
gaudryc
07fa11b9ff Bug: com.bwssystems.HABridge.plugins.domoticz.DomoticzHome.addDomoticzDevices(List,
List, String) has Boolean return type and returns explicit null
2019-01-06 20:07:10 +01:00
gaudryc
df04d542db Bug: The method
com.bwssystems.HABridge.plugins.hue.HueInfo.changeState(HueDeviceIdentifier,
String, String) ignores the return value of
String.contains(CharSequence)
2019-01-06 20:03:24 +01:00
gaudryc
92ab02145e Bug: Non-transient non-serializable instance fields in serializable
class com.bwssystems.logservices.LoggingForm
2019-01-06 19:52:55 +01:00
gaudryc
d9916b7662 Bug: Useless control flow to next line in
com.bwssystems.HABridge.plugins.http.HttpTestHandler.updateTheData(String,
String)

This method contains a useless control flow statement in which control
flow follows to the same or following line regardless of whether or not
the branch is taken. Often, this is caused by inadvertently using an
empty statement as the body of an if statement, e.g.:
2019-01-06 19:18:27 +01:00
gaudryc
f8349f12bc Bug: Call to method of static java.text.DateFormat in
com.bwssystems.HABridge.api.hue.WhitelistEntry.getCurrentDate()

As the JavaDoc states, DateFormats are inherently unsafe for
multithreaded use. The detector has found a call to an instance of
DateFormat that has been obtained via a static field. This looks
suspicious.
2019-01-06 18:59:14 +01:00
gaudryc
12823704f3 Bug: Call to method of static java.text.DateFormat in
com.bwssystems.HABridge.BridgeSettings.getCurrentDate().

As the JavaDoc states, DateFormats are inherently unsafe for
multithreaded use. The detector has found a call to an instance of
DateFormat that has been obtained via a static field. This looks
suspicious.
2019-01-06 16:21:07 +01:00
gaudryc
7490cf72a3 Add shutdown hook before starting web server to be able to stop
HA-Bridge from the command line (using SIGTERM signal).
2019-01-03 18:39:01 +01:00
gaudryc
ce220d999a Invalidate authenticated user session. 2019-01-03 18:23:12 +01:00
gaudryc
9a438abf79 Add /system/logout request to remove current authenticated user and
invalidate his session.
2019-01-03 18:20:42 +01:00
BWS Systems
ce97e928ad Updated harmony hub disconnect handling 2018-12-31 14:06:48 -06:00
Ben Chadwick
56481f3f72 indentation and typo correction 2018-12-10 11:08:53 -05:00
Ben Chadwick
55ed9ba4c2 initial HSL replacement code 2018-12-10 11:03:53 -05:00
BWS Systems
df67980bd6 Fix compile error in bridghtnessdecode class. 2018-11-15 09:46:56 -06:00
BWS Systems
b6b78c4849 Update v to v5.2.2 2018-11-14 11:29:45 -06:00
BWS Systems
b1d1f2ac46 changes for dfim at 1percent and domoticz https usage 2018-11-14 11:25:54 -06:00
BWS Systems
750056df06 add new device param for on with dim and changed behavior for onFirstDim 2018-11-13 14:09:53 -06:00
BWS Systems
3f13e957ad update handling for upnp response with ip path 2018-11-12 16:25:15 -06:00
BWS Systems
afc254720c Update for parse default route 2018-11-06 16:11:42 -06:00
BWS Systems
943e4420e6 update .gitignore 2018-11-06 15:52:34 -06:00
BWS Systems
c9e6cd079f Update Harmonhy connection handling on disconnect. Add Route Parse 2018-11-06 15:46:45 -06:00
bsamuels
faae6aa31f Updating upnp address corrections 2018-08-07 16:20:48 -05:00
BWS Systems
c25f08f142 Merge pull request #927 from nel-stefan/master
Added quotes around strings in json response for huemulator
2018-08-07 13:42:23 -05:00
audiofreak9
d6ad9d288e Updated README Alexa device list
Updated README Alexa device list to include all names of physical Amazon devices for clarity.
2018-07-10 17:01:42 -04:00
BWS Systems
3400c4d43a Fix habridge directory to be ha-bridge
Fixes #947
2018-04-10 13:15:30 -05:00
BWS Systems
ddcbea001c add dash in path 2018-03-28 08:39:27 -05:00
Stefan Marchal
9a35e47c27 Added quotes around strings in json response for huemulator
Bumped version number
2018-03-19 22:44:53 +01:00
BWS Systems
199fcce549 Merge pull request #883 from escalate/master
Update README.md with better instructions for running created by @escalate
2018-03-16 09:17:38 -05:00
BWS Systems
bfeb382d1f Merge branch 'master' into master 2018-03-16 09:16:49 -05:00
bsamuels
3a93d9e98f Updated version for release and linked in the mob41 master branch for
broadlink api
2018-03-16 08:31:45 -05:00
bsamuels
3b22e3f711 Start bug fixing, update FHEM bulk add issue 2018-03-09 09:18:23 -06:00
BWS Systems
5c1f1f5b96 Merge pull request #914 from bwssytems/NewFeaturesfor5.1
New featuresfor5.1

Fixes #896 Incorrect examples in documentation for color. enhancement

Fixes #910 Order of data description fields seems to be hard-coded bug question

Fixes #891 Color replacement control in MQTT bug

Fixes #886 Exception every minute after linking harmony hub enhancement question

Fixes #879 Intensity.math(x).hex does not deliver hex values bug question

Fixes #809 Scanning Fibaro devices or scenes not working with 5.0.0 Informational bug

Fixes #236 Support for Broadlink Rm2/pro enhancement question

Fixes #874 Error on calling URL - Nanoleaf Aurora bug question

Fixes #808 In 5.0.0, colour UDP URLs not issued to milight hub enhancement question

Fixes #846 [5.1.0] Harmony commands not being split into on/dim enhancement question

Fixes #851 non-dimmable device enhancement question

Fixes #853 Turn Logging off enhancement question

Fixes #850 HB[5.1.0] Fibaro HomeCenter2: Devices "Build Item" not working bug question

Fixes #860 Discovery issues from Echo Dot 2nd Gen - potential fix enhancement

Fixes #675 FHEM home automation integration enhancement question

Fixes #516 OpenHAB integration enhancement
2018-03-08 14:00:27 -06:00
bsamuels
82788f1ecd Final fixes and ready for release 2018-03-08 13:17:26 -06:00
bsamuels
d5920e1454 Updated BL api lib 2018-03-07 11:35:40 -06:00
bsamuels
b26a63bb7a updated to new BL api lib added debug and fixed device build for BL in
app.js
2018-03-07 11:07:44 -06:00
bsamuels
f17cea3c9d Finished debugging broadlink implementation 2018-03-06 16:05:27 -06:00
bsamuels
7f68285a43 Updated broadlink api lib version 2018-03-05 15:34:35 -06:00
bsamuels
cb46a13802 Updating broadlink library 2018-03-01 16:23:02 -06:00
bsamuels
de07393e6e Update for broadlink api lib change 2018-02-16 11:32:52 -06:00
bsamuels
4b40b03da4 updated debugging and included new Broadlink api build 2018-02-15 13:31:04 -06:00
bsamuels
4f5d4acf56 updated broadlink api version 2018-02-14 09:17:17 -06:00
bsamuels
271bd3913c Check broadlink map before finding device to call for null. Initialize
broadlink Map on discover.
2018-02-13 16:12:05 -06:00
bsamuels
869ffaaf36 Updated broadlink discover and fixed list mapping issue. Added refresh
to ResourceHAndlers so Broadlink and Lifx can be refreshed from the tab.
2018-02-08 15:30:21 -06:00
bsamuels
5a73cc20a9 Fixed color for MQTT and TCP 2018-02-06 16:10:11 -06:00
bsamuels
1a936c631c Updated with debug info. 2018-02-05 15:14:40 -06:00
bsamuels
1a8a6f7a6f Added Tests for broadlink ircmomand conversion. Added checks for
successful auth with broadlink devices. Updated broadlink API CmdPacket
build.
2018-02-02 16:30:44 -06:00
bsamuels
879d3b5326 Fixed broadlink discover loop 2018-01-31 12:19:06 -06:00
bsamuels
3313548ec2 Updated Broadlink for port binding, added debug for RM2 Device ir call
data return, updated Harmony library to be the latest version.
2018-01-30 16:30:55 -06:00
bsamuels
e6db6e11e5 Update broadlinnk to dump clients when discovered in debug and to drop
clients when re-init.
2018-01-29 16:44:12 -06:00
Felix Boerner
dc28eb2984 docs: refine Docker container usage instructions 2018-01-29 08:15:34 +01:00
Felix Boerner
b8acb4a52c feat: add minus in ha-bridge names for consistency 2018-01-27 13:07:37 +01:00
bsamuels
c843e8d1ac Fix small issues and add pct check for FHEM 2018-01-26 13:41:57 -06:00
Felix Boerner
21fdaf4545 docs: remove trailing whitespaces 2018-01-26 13:42:49 +01:00
Felix Boerner
db192df2c6 docs: add section how to run docker container 2018-01-26 13:42:00 +01:00
Felix Boerner
4a24d263c1 docs: rewrite manual installation section 2018-01-26 13:02:59 +01:00
Felix Boerner
3394559539 docs: remove basic script setup section
It is not best practice to run an application this way.
2018-01-26 10:54:31 +01:00
bsamuels
47074ff60f Update Fibaro and Fhem for small changes. 2018-01-23 14:34:49 -06:00
bsamuels
047a7de612 deleted json 2018-01-22 16:27:26 -06:00
bsamuels
7fc7f00308 Updated FHEM call issues for success of 302
Streamlined Fibaro Json Decoding
2018-01-22 16:26:31 -06:00
bsamuels
14e940134c Fixed openhab build screen and openhab invalid responses.
Completed Broadlink impl

Testing
2018-01-19 14:46:45 -06:00
bsamuels
1897633e75 Forget new file 2018-01-18 16:54:44 -06:00
bsamuels
5a052d9374 Updated FHEM for command
Started Broadlink implementation
2018-01-18 16:54:21 -06:00
bsamuels
4b048c2a7f Updated with fixes for FHEM 2018-01-16 16:18:53 -06:00
bsamuels
27f77b9caa update 2018-01-16 16:14:11 -06:00
bsamuels
18c47ee5e4 update for changes 2018-01-16 16:13:24 -06:00
bsamuels
27dd8475e9 Huemulator state fixes
draft active logger control
2018-01-16 13:20:49 -06:00
bsamuels
37b381085c Fixed RC test issues for:
FHEM device decode
on for first dim
default OpoenHab Port
Fibaro request debugging enhancement
2018-01-12 15:44:10 -06:00
bsamuels
b2a30f5771 Remove test call for FHEM devices 2018-01-05 16:19:18 -06:00
bsamuels
b88b3fa245 Fininshed Testing color RGB to int for LIFX 2018-01-05 15:29:41 -06:00
bsamuels
52aac32474 Update color setting for LIFX 2018-01-05 15:15:22 -06:00
bsamuels
037b151729 Updates for RC2 fixing FHEM issues, Fiabaro Issues, adding functionality
for color tests, and sending multiple requests based on the Hue API
body.
2018-01-05 14:48:20 -06:00
bsamuels
3f3d643053 Update pom version 2018-01-02 16:22:39 -06:00
bsamuels
f27d905869 Updated FHEM integration for testing 2018-01-02 16:20:51 -06:00
bsamuels
4c694cb285 Updated description xml to remove null service list. Most likely culprit
of /null calls to spark.

Started adding FHEM integration
2017-12-29 15:57:37 -06:00
bwssystems
ca2c5f7b04 Finished OpenHAB integration.... 2017-12-20 15:36:08 -06:00
bwssystems
0c49df1473 Updated system.html with opnhab ports 2017-12-19 16:45:51 -06:00
Admin
fe613f7688 Basic OpenHAB functionality added. Need to add String selection
functionality.
2017-12-15 16:34:58 -06:00
Admin
4b247557d4 Start adding OpenHAB to the bridge. 2017-12-14 14:54:50 -06:00
BWS Systems
dc49de41ce Merge pull request #843 from bwssytems/FixesForV5
Fixes for v5

Fixes #842 Show and manage linked devices to the ha-bridge enhancement
Fixes #797 harmony hub not pairing bug question
Fixes #817 Log full of error: The requested route [/(null)] has not been mapped in Spark bug question
Fixes #821 Support HEX-Formatted RGB Values for color enhancement question
Fixes #837 -Dexec.garden breaks script execution with trailing slash enhancement question
Fixes #800 5.0.0 no longer connects to the vera enhancement question
Fixes #801 Fibaro scenes are not created bug question
Fixes #805 Fibaro HomeCenter2: Devices "Build Item" broken bug question
Fixes #841 add support for timestamp http URL variable
Fixes #836 Add support for cheap HomeWizard SmartPlugs (Smartwares Smarthome Controller)
2017-12-12 15:53:08 -06:00
Admin
9d07fac929 updated readme 2017-12-12 15:48:03 -06:00
Admin
4ecbad6558 Fixed enable rooms for Alexa. Removed huemulator redirect for Spark.
Added Link management as links are now stored with IP.
2017-12-12 15:21:45 -06:00
Admin
4c86e42776 Added hex color codes, added swith to utilize groups/rooms. updated exec
to use new path parsing. Revert back to Spark 2.3 due to issues.
2017-12-11 16:40:59 -06:00
BWS Systems
00dbea6dac Merge pull request #841 from rburgst/master
add support for timestamp http URL variable
2017-12-11 12:27:48 -06:00
BWS Systems
ed2bf3bd83 Merge pull request #836 from bjoernrennfanz/feature/HomeWizardIntegration
Add support for cheap HomeWizard SmartPlugs (Smartwares Smarthome Controller)
2017-12-11 12:27:24 -06:00
Rainer Burgstaller
58fb085180 renamed variable timestamp to time.millis 2017-12-11 18:26:26 +01:00
Rainer Burgstaller
800f5ec2aa add support for timestamp http URL variable
- certain devices require a current date/time in their HTTP request
  (Warema shades)
2017-12-11 08:30:33 +01:00
Björn Rennfanz
bdf5770ba0 Some corrections after rebasing code of HomeWizard SmartPlug plug-in 2017-12-09 22:05:00 +01:00
Björn Rennfanz
a213672341 Add web part of plug-in for HomeWizard SmartPlug support 2017-12-09 21:40:27 +01:00
Björn Rennfanz
d337546da7 Add java part of plug-in for HomeWizard SmartPlug support 2017-12-09 21:35:17 +01:00
Björn Rennfanz
b9437d42e8 Update README.md file 2017-12-09 21:15:02 +01:00
BWS Systems
71c4447d25 Merge pull request #796 from bwssytems/FeaturesfFor5.0
Features for 5.0

Fixes #749  No Login Feedback - No Password reset enhancement question
Fixes #717  Having to often repeat command enhancement question
Fixes #768  Hue Items error with ha-bridge-5.0.0rc2 bug question
Fixes #782  getting server error 500 out of nowhere enhancement question
Fixes #779  Allow custom hue-bridgeid? enhancement question
Fixes #450  Unable to edit on ipad enhancement question
Fixes #336  Fibaro support enhancement help wanted
Fixes #560  How to run Fibaro Scene in HA-Bridge? enhancement question
Fixes #737  2 HA-Bridge instances: UpnpListener encountered an error sending upnp notify packet. IP: 239.255.255.250 with message: Operation not permitted enhancement question
Fixes #761  Philips Hue app update - bridge update required enhancement question
Fixes #747  Issue with MQTT messages - intermittent success enhancement question
Fixes #722  Long time no operation causes failure of the first operation of home assistant devices enhancement question
Fixes #671  action taking place only on the second time i ask to do it enhancement question
Fixes #381  Resize Target Item Field enhancement question
Fixes #705  Item Delete Button deletes multiple items bug question
Fixes #327  Add Color or Raw Info enhancement question
Fixes #704  Rename "Clear Device" to "Clear" enhancement
Fixes #713  Room and basic color (especially limitless/milight) support enhancement
2017-11-22 13:50:31 -06:00
Admin
a489061bac Changed version to 5.0.0, Updated README, updated traceupnp messages in
log.
2017-11-22 12:44:38 -06:00
Admin
75b925992b Updated favicon, enhanced login/logout. 2017-11-17 15:56:23 -06:00
Admin
05b9f195d7 Fixed issue with http client pool and with test user for web interface. 2017-11-17 12:38:14 -06:00
Admin
5eca809c4a Updated Readme, Updated to the latest spark web REST Framework. Added
handling for spark initialization errors.
2017-11-16 16:23:33 -06:00
Admin
fa00b7106a Added failed logic 2017-11-15 14:59:26 -06:00
Admin
df85c8a349 Added new HTTP pool manager and http handling. 2017-11-15 14:27:44 -06:00
Admin
d8d5e8f39a Added hub mac setting, removed global hal token setting, removed upnp
strict setting (only from command line)

Working on http pool
2017-11-14 16:37:45 -06:00
Admin
690bdaa748 Finalized Fibaro merge correcting inconsistencies. 2017-11-14 13:36:07 -06:00
Admin
feee22dbac Merge branch 'refs/heads/pr/785' into FeaturesfFor5.0
# Conflicts:
#	src/main/resources/public/scripts/app.js
2017-11-14 13:13:22 -06:00
Admin
548da932ad Updated hue hub version to be 999999999 to help alleviate version
issues. Updated upnp multicast to use one socket. Updated http Handler
code to check for null on conn.
2017-10-20 11:04:05 -05:00
Admin
b147f6606e Update MQTT reconnect, and import statements and color convert test
case.
2017-10-19 15:56:15 -05:00
Dmitriy Ponomarev
31fe05fe2b fix autorization in requests and many changes. All working, except device statuses. Dont know how to do that. 2017-09-25 03:04:01 +03:00
diamond5170
924f3059fb Merge branch 'master' into master 2017-09-24 21:45:56 +03:00
Admin
5231eac4b0 Updating http connection management.... Broken 2017-08-30 17:04:17 -05:00
Admin
28144312ff Update echo url. Create Release Candidate 1 for 5.0.0 2017-08-29 16:24:02 -05:00
Admin
60e8855aa7 Updated scroll-table.css to have dynamic max-height, cleaned up
warnings, removed order by name.
2017-08-03 15:26:12 -05:00
BWS Systems
5ea14f9069 Merge pull request #715 from FloFoer/FeaturesfFor5.0
Light type detection and filter for device list
2017-08-03 09:51:53 -05:00
Florian Förderreuther
ce79fb4b82 Light type detection and filter for device list
"Extended color light" isn't used anymore for all devices without thinking about it. It will now automatically differentiate between Color and Dimmable light by the following logic: 
1. If it's  Philips Hue passthru look at the state: if it contains the attribute "colormode" it's a Extended color light, otherwise it's a Dimmable light.
2. If it's no passthru it's a dimmable light if the colorUrl has no content. 

I didn't use On/Off light because i disovered that a) the hue app treats these exactly the same as dimmable light, you can still "change the brightness" and b) the amazon echo doesn't find these lights without the skill

I also enhanced the filter options in the web ui.
You have a textbox "Show devices visible to". You can fill in an ip-address and there will only be devices displayed that a) have the ip address in the requesterFilter or b) don't filter by requester.
If you tick the checkbox "Must contain filter" option b isn't used. This means also if you check the box with no ip address in the textbox only devices without request filter will be shown.
Also there is a filter by device type.
All these 3 filter options will be remembered as long as the browser tab is closed.
2017-08-02 07:34:06 +02:00
BWS Systems
98ce4e2a3a Merge pull request #711 from ProZsolt/patch-1
Fix table in README.md
2017-08-01 16:26:26 -05:00
BWS Systems
026626b5ab Merge pull request #712 from FloFoer/FeaturesfFor5.0
Room and basic color support, group additions and some minor stuff
2017-08-01 16:25:54 -05:00
Florian Förderreuther
95c342b548 Amazon Echo support for groups
So the groups are now somewhat usable with an Amazon Echo. This is a bit of a workaround:
The group gets presented to the Echo as another fake light. For that to work you must manually enable this feature for every room by adding "exposeAsLight":"192.168.0.30" to the room in group.db (restart ha-bridge afterwards). Use the ip-address of your echo. No need to do that for other devices, because these can handle rooms directly. The fake light for the group will only be shown/usable to the specified ip-addresses.
2017-07-30 16:30:33 +02:00
Florian Förderreuther
cb9312f6c3 Filter lights in group and fixed handling of urls with trailing slash 2017-07-30 10:10:04 +02:00
Florian Förderreuther
8831fec6be Minor fixes and better color support
Fixed some minor bugs related to groups.
I implemented basic color capabilities. Extended ColorDecode.java to handle xy and ct values. Implemented the call to replaceColorData in various homes (command, http, tcp, udp).
Additional to color.r, color.g, color.b which return the color value in 0-255 there is also a value replacement "color.milight". 
The usage for that is as follows: udp://ip:port/0x${color.milight:x} where x is a number between 0 and 4 (0 all groups, 1-4 specific group). The group is neccessary in case the color turns out to be white. The correct group on must of course be sent before that udp packet.
Note that milight can only use 255 colors and white is handled completely separate for the rgbw strips, so setting temperature via ct with milight does something but not really the desired result.
2017-07-29 18:42:13 +02:00
Florian Förderreuther
0227a05970 Corrected group action success response 2017-07-28 14:47:58 +02:00
Florian Förderreuther
087d66694e Added filter to device list "show devices visible to ip-address"
If filter is active it will only show the devices that are visible for the given ip-address. Filter setting is saved until browser tab is closed.
2017-07-28 02:24:48 +02:00
Florian Förderreuther
20ad6891e8 Implemented support for rooms
I implemented full api support for rooms. That means:
- Create/Modify/Delete rooms/lightgroups
- Get information about group list / individual group
- Group actions: Change lighting for the whole group (except setting scenes, because scenes are not implemented in ha-bridge right now)
For now the rooms/groups can only be configured through the api and apps, it's not visible/changeable through the web gui.
2017-07-27 23:20:02 +02:00
Zsolt Prontvai
cd2bd072ed Fix table in README.md 2017-07-27 18:54:18 +02:00
Unknown
3a5262ff33 Added ColorUrl and alternative item edit mode
Additional to on/off/dim items i added color items. The colorUrl gets executed if a PUT is received with xy/ct/hue/sat in the body. Also changed the emulated bulb type to "Extended color light".
Added "Change Editmode" button in the editdevice screen. Switch between manual JSON edit and the tabular variant. Local unsaved changes in one mode carry over to the other. Through this edit variant it's possible to change the order of items and do copy/paste.
2017-07-23 10:15:00 +02:00
Admin
430eff958c Updateing color translation 2017-06-28 16:02:45 -05:00
Admin
86371c03b2 Updating Color calculation 2017-06-27 16:12:47 -05:00
Admin
3207b6b76e Updated version
Fixes #676 Server doesn't listen on IPv4 on Raspberry Pi enhancement
question

Fixes #673 ha-bridge not working after restart bug
 
Fixes #618 Long Press for Harmony enhancement question

Fixes #608 Link-Button-Feature and Hue App bug question

Fixes #669 Need to re-pair Philips Hue Bridge enhancement question

Fixes #671 action taking place only on the second time i ask to do it
enhancement question

Fixes #594 Buttons on Hue Devices page do not work bug question

Fixes #654 Add possibility to set qos and retained state of MQTT
messages

Fixes #665 Hue Constant updates
2017-06-26 14:11:57 -05:00
Admin
3d4724ac57 Fixed an introduced issue with the edit screen. Finished the press time
implementation.
2017-06-21 16:22:03 -05:00
Admin
63ac729967 Adding long press 2017-06-20 16:35:03 -05:00
Admin
0900fec60f Working on pairing with hue app 2017-06-19 16:22:03 -05:00
Admin
5ccb793ddb Merge and update changes from pull requests 2017-06-19 15:10:40 -05:00
BWS Systems
3ebab82977 Merge pull request #654 from fgather/master
Add possibility to set qos and retained state of MQTT messages
2017-06-19 15:01:32 -05:00
BWS Systems
e877f410f3 Merge pull request #665 from kvandt/master
Hue Constant updates
2017-06-19 15:01:01 -05:00
Admin
220e337d08 Fixed Hue bulk build and save hue registration 2017-06-19 15:00:10 -05:00
BWS Systems
92b7f4e260 corrected version 2017-06-09 13:22:06 -05:00
Admin
78d39325b0 Switch Status enhancement
Fixes #461
 Feature Request ${device.XXXX] enhancement
Fixes #577
 [Feature] Select Interface - UPnP Listening on all interfaces/IP
addresses enhancement
Fixes #591
 HAL http Control issue bug
Fixes #661
 Allow edit of configured endpoints in bridge control enhancement
Fixes #662
2017-06-09 13:17:40 -05:00
Admin
208b1cad2a Fixed HAL control issue, added switch status, added new ${device.xxx}
replacement items and interface selection for upnp
2017-06-09 12:57:18 -05:00
kvandt
ff760a488c Update HueConstants.java 2017-06-07 20:35:15 +02:00
kvandt
9200c0fc5d Update HueConstants.java 2017-06-06 23:23:13 +02:00
Admin
998450af4e Updating HAL changes 2017-05-31 16:32:33 -05:00
Admin
d78c7c536d Updateing HAL handling 2017-05-30 16:41:26 -05:00
fgather
a55c53299a Add possibility to set qos and retained state of MQTT messages 2017-05-30 21:49:31 +02:00
Admin
fb9cc64839 Fix HAL connect issues. 2017-05-26 15:36:01 -05:00
BWS Systems
409c93bfb2 Merge pull request #647 from bwssytems/Fixes-for-v4.5
Fixes for v4.5
2017-05-24 16:05:24 -05:00
Admin
543a79bb66 Update version 2017-05-24 16:02:12 -05:00
BWS Systems
16d1054b96 Merge pull request #646 from bwssytems/master
Update from master branch
2017-05-24 15:59:24 -05:00
BWS Systems
165b6ef9bb Merge branch 'Fixes-for-v4.5' into master 2017-05-24 15:59:13 -05:00
BWS Systems
fb94b858f6 Merge pull request #635 from ibenrodriguez/master
minor typos in README
2017-05-24 15:55:55 -05:00
Admin
ea80925533 remove .map files and remove async from run function. 2017-05-24 15:54:21 -05:00
Admin
62b896ee07 Implement devimal percent for no locale, add HAL https and debugging for
link buttton usage.
2017-05-19 15:45:56 -05:00
Iben Rodriguez
8657ea6704 removing duplicate wget instructions in README file
removing duplicate wget instructions in README file lines 73-79 were
duplicated at 80-81
2017-05-17 06:42:59 -07:00
Iben Rodriguez
05bd6b6d77 minor typos in README
Corrected a few minor typos in the README file such as API being UPPER
CASE.
2017-05-16 18:05:07 -07:00
Admin
19256e4eaa Updated code for issues and included new pull requests from others. 2017-05-11 15:30:34 -05:00
BWS Systems
905f6aa9ec Merge pull request #625 from ahertz/logitech_pop
Added error message for scene invocation, to allow Logitech Pop to work.
2017-05-10 11:17:54 -05:00
BWS Systems
daa0dac5b9 Merge pull request #622 from glynhudson/patch-1
readme fixes and tweaks
2017-05-10 11:17:33 -05:00
Aaron Hertz
0478fc69f4 Added error message for scene invocation, to allow Logitech Pop to work. 2017-05-07 11:24:55 -04:00
Glyn Hudson
b99e74823c readme fixes and tweaks 2017-05-05 01:54:54 +01:00
diamond
9aaab37a17 auth 2017-05-04 05:59:04 +03:00
diamond
df618afeda fix requests 2017-05-04 03:48:24 +03:00
diamond
d36769e2a0 fibaro HC2 support 2017-05-04 03:19:03 +03:00
BWS Systems
ebeb6be7a7 Update issue for google home
Fixes #572
2017-04-20 16:22:33 -05:00
BWS Systems
d3979da2b4 Fix ask Alexa table
Fixes #598
2017-04-18 11:12:20 -05:00
BWS Systems
f276f66991 Merge pull request #586 from bwssytems/SecurityImpl
Merge security impl branch

BridgeSettings.configWriter needs synchonized question
Fixes #571 

[feature request] Web Users/Login enhancement
Fixes #402 

User authentication... question
Fixes #270 

Security VS habridge enhancement question
Fixes #390 

issue tcp command bug
Fixes #564 

Device inactive not working bug
Fixes #565 opened 13 days ago
2017-04-07 11:55:51 -05:00
Admin
3ac5c10f08 Final updates for release 2017-04-07 11:51:00 -05:00
Admin
13c84ba174 Hopefully, final testing release 2017-04-06 16:23:14 -05:00
Admin
b19fe5c86a Took care of dynamic test user creation 2017-04-05 16:02:40 -05:00
Admin
ff9d0a5a77 Moving to release candidate 1 2017-04-04 15:54:58 -05:00
Admin
50af884563 Added settings write synchronization 2017-04-04 13:08:34 -05:00
Admin
0737c54a0e Updated security changes for exec command with the exec garden. 2017-04-04 11:47:04 -05:00
Admin
a7e516925c Updated security to have basic auth and also remove login when reinit
happens.
2017-04-03 16:40:46 -05:00
Admin
2f1adf9d4b First round of testing for Security complete, now alpha 2017-03-31 14:57:48 -05:00
Admin
cd5417c2e0 Testing security impl 2017-03-30 15:55:36 -05:00
Admin
ba621fcb85 Continue with security impl, starting to gell.... 2017-03-29 16:41:59 -05:00
bwssystems
7442b0d0ca more security handling 2017-03-28 18:58:33 -05:00
Admin
c275926117 continue security update 2017-03-28 16:39:30 -05:00
Admin
895a9ec99b Continue adding security 2017-03-27 16:50:29 -05:00
Admin
6dfd70dfee Continue security 2017-03-24 16:33:12 -05:00
Admin
0bdb321fd7 Continue security update 2017-03-24 13:38:08 -05:00
Admin
b508a8a16a Continue with security update 2017-03-23 16:36:25 -05:00
Admin
ddee3a42a9 add file 2017-03-22 16:36:24 -05:00
Admin
b000215b26 Continue security implementation 2017-03-22 16:35:19 -05:00
Admin
a2b652907f Star security implementation 2017-03-20 16:30:01 -05:00
bwssystems
6bf1bbc8a8 Update Readme 2017-03-18 17:27:05 -05:00
bwssystems
1a402e425e Merged msbg array fix.
Fixed #558 for null pointer on startup without habridge.conf
2017-03-18 17:26:25 -05:00
BWS Systems
59ef6e88de Merge pull request #559 from msbg/Array-bugfix
Array iteration bugfix
2017-03-18 13:21:03 -05:00
Monica Goward
7d4f953c89 Array iteration bugfix 2017-03-18 17:32:43 +00:00
BWS Systems
3bcec27861 Merge pull request #557 from bwssytems/NewConnectors3.1
New connectors3.1

Fixes #454 Somfy Tahoma plugin enhancement
Fixes #430 hex value needed for dimming enhancement question
Fixes #423 Naming & Notes Enhancement Suggestions enhancement
Fixes #512 Dim handling in V4 question
Fixes #467 Available syntaxes besides ${intensity.percent} ? enhancement
Fixes #416 Use ${intensity.percent} with Harmony Hub enhancement question
Fixes #548 Removing Delay or Repeat in Edit-Device results in bad Config bug
Fixes #537 Minor documentation error bug
Fixes #237 Upgrade resulted in duplicates - Need to keep ha-bridge created user for device enhancement question
Fixes #324 Added a optional webhook for harmony-activity changes enhancement
Fixes #492 Use relative path in URLs enhancement
2017-03-17 16:55:23 -05:00
Admin
bb0ffeb570 Added device data value passing, off state change for brigthness, hex
intensity values, updatd ui to cgo back to device touched after
edit/add.
2017-03-17 16:53:21 -05:00
Admin
0c4292bfd7 Update scrollable table and try implement scroll to row. 2017-03-16 17:05:40 -05:00
Admin
0083c5854f Merge branch 'master' into NewConnectors3.1
Conflicts:
	src/main/java/com/bwssystems/HABridge/BridgeSettings.java
2017-03-16 15:18:39 -05:00
Admin
c13b9bd8f4 upate for editing with fields that need not be saved when empty. 2017-03-16 14:57:21 -05:00
BWS Systems
ca5a6c6667 Merge pull request #454 from msbg/master
Somfy Tahoma plugin
2017-03-16 13:59:43 -05:00
Monica Goward
c9c6d6e66d Cleanup json->pojo mapping 2017-03-16 13:13:53 +00:00
Monica Goward
c8b1827150 Fixes post merge to comply with updated js code json 2017-03-16 12:59:43 +00:00
Monica Goward
d7d83e866e Post latest merge from master 2017-03-16 11:55:02 +00:00
Admin
fb24e9d1a3 Updated whitelist handling to save users when created. Also, added
default test user for habridge UI. Made change state to be validated
against whitelist.
2017-03-13 16:37:42 -05:00
Admin
d15a1c58d0 update readme for harmony webhook addition 2017-03-10 16:22:48 -06:00
BWS Systems
08c87eb3aa Merge pull request #324 from CrEaK/master
Added a optional webhook for harmony-activity changes
2017-03-10 08:59:14 -06:00
BWS Systems
7da4bf13e0 Merge pull request #492 from hobbe/patch-1
Use relative path in URLs
2017-03-10 08:58:43 -06:00
BWS Systems
cd701ca02e Merge pull request #531 from digiltd/patch-2
delete kodivolume.md as there is now a page in the wiki
2017-03-09 09:50:59 -06:00
Sam Turner
1b676632fe delete kodivolume.md as there is now a page in the wiki 2017-03-09 14:58:25 +00:00
BWS Systems
62e366c028 Add pics 2017-03-08 16:03:51 -06:00
BWS Systems
838b86a266 Merge pull request #529 from bwssytems/NewConnectors2
Fixed null http handler issue for close.
Fixed uniqueid on copy.
Update TCP handling to save connection. Added debug to http handler.
Fixed hex handling of intensity. Added handling for \n, \t, \r and so forth in tcp,udp and mqtt.
Fixed HA group support. Added JSON check for new devices. Added Device level filtering. Added decimal percentage.

Home assistant service call for groups from HA bridge device bug question
Fixes #444 opened on Feb 5 by papa-legba

Add filtering for getting of devices in the hue api by IP enhancement
Fixes #526 opened 6 hours ago by bwssytems

Dim Values 0.0 to 1.0 enhancement question
Fixes #401 opened on Jan 25 by beandi

Avoid JSON-Parse error in on/dim/offURL by parsing before saving enhancement question
Fixes #326 opened on Dec 23, 2016 by luke-ff

Nest account linked- JsonSyntaxException: enhancement question
Fixes #266 opened on Nov 22, 2016 by smithski

Xiaomi Yeelight support - TCP requests to use \n \r enhancement question
Fixes #415 opened on Jan 28 by mihaifireball

newline enhancement question
Fixes #448 opened 29 days ago by mbreunich

Using Escape Character enhancement question
Fixes #495 opened 12 days ago by craiglt

how to use "${intensity.math(X/4)}" bug question
Fixes #124 opened on Jun 4, 2016 by painkillerde

Can't get bridge to talk to Netcat to send commands via mochad bug question
Fixes #458 opened 26 days ago by instructor1361

hass HA bridge idle problem enhancement question
Fixes #501 opened 11 days ago by erroldee

HA-Bridge 4.2.0 - NullPointer on DomoticzHome bug
Fixes #507 opened 9 days ago by kaaspad

Unique ID - Not Unique bug question
Fixes #513 opened 5 days ago by merlin051
2017-03-08 15:24:52 -06:00
Admin
ed8fc95782 Fixed HA group support. Added JSON check for new devices. Added Device
level filtering. Added decimal percentage.
2017-03-08 14:59:04 -06:00
Admin
f6cb41b880 Fixed hex handling of intensity. Added handling for \n, \t, \r and so
forth in tcp,udp and mqtt.
2017-03-07 16:36:25 -06:00
Admin
a578aa9fd8 Update TCP handling to save connection. Added debug to http handler. 2017-03-06 16:30:23 -06:00
Admin
7b97bd75ae fixed uniqueid on copy 2017-03-03 16:25:45 -06:00
Admin
4de14217b4 Fixed null http handler issue for close 2017-03-02 16:36:56 -06:00
Olivier B
02918dc49d Merge pull request #2 from hobbe/master
Merge master into patch-1
2017-03-01 14:21:06 +01:00
Olivier B
3a8c64aac6 Merge branch 'patch-1' into master 2017-03-01 14:13:53 +01:00
Olivier B
10df2f1c3e Merge pull request #1 from bwssytems/master
Merge bwssystem/ha-bridge into master
2017-03-01 14:10:18 +01:00
BWS Systems
c7cf48bb6b Merge pull request #499 from bwssytems/NewConnectors1
New connectors and fixes

Fixes #124 
Fixes #396 
Fixes #466 
Fixes #483 
Fixes #488
2017-02-25 10:31:58 -06:00
Admin
54e9303708 merge reqdme 2017-02-24 15:53:35 -06:00
Admin
6b4344bbe8 Merge remote-tracking branch 'origin/master' into NewConnectors1 2017-02-24 15:47:23 -06:00
Admin
745986f08f Finished Domoticz updates 2017-02-24 15:45:45 -06:00
Admin
5e59b33ed7 Fixed hex conversions. Added checks for formats. Finished Bulk add
issues. Fixing Domoticz home and handler  for user/pwd.
2017-02-23 16:47:06 -06:00
Olivier B
c45cb24b20 Use relative path in URLs
This change allows to use HA-Bridge web app from a sub-folder of the web server, eg. http://example.com/habridge.
2017-02-23 21:22:47 +01:00
Admin
adc34ddaa6 Fix issue with LIFx bcast being null. Continue fixing bulk add. 2017-02-22 16:28:31 -06:00
Monica Goward
88f34f3221 Update to use HABridge HTTPHandler 2017-02-22 21:44:17 +00:00
Monica Goward
15223630eb Merge remote-tracking branch 'remotes/bwssystems/master' 2017-02-22 20:50:04 +00:00
BWS Systems
2f456aa0d0 Merge pull request #476 from matsahm/patch-1
Update README.md for Google Home table of sayings
2017-02-20 07:50:47 -06:00
matsahm
201aaa8bca Update README.md 2017-02-18 11:57:32 +01:00
Admin
6e7b48aa5b updated version 2017-02-16 15:22:54 -06:00
Admin
afd1af4094 Merge remote-tracking branch 'origin/master' into NewConnectors1
Conflicts:
	README.md
	pom.xml
2017-02-16 15:22:06 -06:00
Admin
61156e9820 Updated edit screen field layout with bootstrap grid. Added error
checking for HTTP handler.
2017-02-16 13:36:23 -06:00
Admin
6116d37675 update version 2017-02-09 08:41:40 -06:00
Monica Goward
f8474f5f41 Initial fixes and cleanup prior to pull request 1 2017-02-09 14:26:16 +00:00
Admin
0305646b4f Updated LIFX connection for broadcast address and changed the item
collection to be asynchronous with LIFX.
2017-02-08 15:15:53 -06:00
Niklas
3fea7f4f1a Change to Generic HTTPClient 2017-02-08 08:29:37 +01:00
Niklas
44fbaa68f8 Merge remote-tracking branch 'upstream/master' 2017-02-08 08:28:53 +01:00
Admin
dd0032a567 updatre lifx 2017-02-07 15:50:21 -06:00
Monica Goward
470f6b3c15 Merge branch 'master' of https://github.com/bwssytems/ha-bridge 2017-02-06 21:24:44 +00:00
bwssystems
3016712ad8 Fixed closing homes to actually call the close on the homes. Updated the
lowest setting on the test dim function to b 1.

Fixes #438
Fixes #440
2017-02-05 14:37:35 -06:00
Monica Goward
2fbf26a5fa Merge branch 'master' of https://github.com/bwssytems/ha-bridge 2017-02-05 20:02:14 +00:00
BWS Systems
6b3ae1b971 Fixed where version api call is 2017-02-04 18:44:25 -06:00
bwssystems
71258c7e52 Fix some bugs for brightness decode in tcp and udp plugins
Fixes #434
Fixes #435
2017-02-04 18:40:13 -06:00
Monica Goward
ee066f1449 Cleanup 2017-02-04 19:12:20 +00:00
Monica Goward
e5871e61b5 Added some initial docs 2017-02-04 19:04:34 +00:00
Monica Goward
6b8a714959 Re-instating line endings that were erroneously removed 2017-02-04 18:40:28 +00:00
Monica Goward
cf3ec7cfe4 Merge branch 'master' of https://github.com/bwssytems/ha-bridge 2017-02-04 18:30:07 +00:00
Monica Goward
805aac9fde Add Somfy devices 2017-02-04 18:29:50 +00:00
Monica Goward
1221df4c96 add Somfy settings 2017-02-04 18:28:59 +00:00
Monica Goward
1c897e3b36 Add Somfy devices 2017-02-04 18:28:25 +00:00
Admin
0ac8061118 Updated LIFX impl 2017-02-03 09:29:49 -06:00
Monica Goward
65b0d6e470 HTML headers updates only 2017-02-03 12:08:46 +00:00
Monica Goward
99e2243e2d HTML headers updates only 2017-02-03 12:07:44 +00:00
Monica Goward
7a354619d0 Generated pojos from JSON Somfy return data 2017-02-03 12:01:47 +00:00
Monica Goward
2dc245bb96 HTML headers updates only 2017-02-03 11:51:05 +00:00
Admin
c679548bbd Beta impl of LIFX complete 2017-02-02 11:00:50 -06:00
Admin
16a248ba8e Starting impl of LIFX devices 2017-02-01 16:53:08 -06:00
BWS Systems
0ce23c0f00 Update version 2017-02-01 11:38:41 -06:00
BWS Systems
babf81ea31 Update Version 2017-02-01 11:36:51 -06:00
Admin
1c7260600a Minor update for HAL decode issue
Fixes #428
2017-02-01 11:31:04 -06:00
Admin
f1592a1998 Issue with not checking for null 2017-01-30 15:57:35 -06:00
Admin
24dd427fb4 Fixed immediate bugs for
Fixes #129 Hue pass-thru always set to purple
Fixes #406 Dimming with Home Assistant
Fixes #414 Domoticz error retrieving devices
2017-01-30 15:40:58 -06:00
BWS Systems
f8de640f5d added missed quotes 2017-01-27 15:50:00 -06:00
BWS Systems
a091262b80 Merge pull request #413 from bwssytems/targetV4.1.0
Target v4.1.0
2017-01-27 15:30:44 -06:00
BWS Systems
0bcc1628c8 Merge branch 'master' into targetV4.1.0 2017-01-27 15:30:05 -06:00
Admin
ffd95aad87 Remerge 2017-01-27 15:17:37 -06:00
Admin
b5e6e3ad60 v4.1.0 Added feature for Domoticz and fixed some major bugs, ie: mqtt,
and some enhancements.
2017-01-27 15:11:30 -06:00
Admin
611cc7be4a Updated REAMDE to new constructs. Added Time repalcement Added ability
inactivate a device.
2017-01-26 16:46:17 -06:00
Admin
4b7ba0fabe Finished Domoticz impl and secure calls for Home Assistant 2017-01-25 16:24:44 -06:00
Admin
6abe1b082c Rebased to master 2017-01-25 15:39:02 -06:00
Admin
b08a285bd0 One more update for JsonArray detection. 2017-01-25 15:23:48 -06:00
Admin
e68282a230 Merge remote-tracking branch 'origin/master' 2017-01-25 15:21:37 -06:00
Admin
c9d55e26ac Fix immediate issues for JSON decoding
Fixes #391
Fixes #392
Fixes #398
2017-01-25 15:19:46 -06:00
BWS Systems
5a3a02cb34 Added security note. 2017-01-25 12:14:59 -06:00
Admin
a9f48e1f9c Add domoticz web page 2017-01-25 09:16:33 -06:00
bwssystems
8b9fd355b4 More updates 2017-01-25 06:58:51 -06:00
Admin
b4da321368 Continue Domoitcz impl 2017-01-24 16:49:56 -06:00
BWS Systems
881f739c0b Added proxy instructions 2017-01-24 12:47:36 -06:00
BWS Systems
f148c2b9fc updated jar version 2017-01-24 10:28:14 -06:00
Admin
fd486588f5 Fixed more immediate issues
Fixes #272
Fixes #384
Fixes #387
Fixes #389
2017-01-24 09:31:48 -06:00
Admin
d118dd8523 Continue on domoticz impl 2017-01-24 08:29:59 -06:00
BWS Systems
58e1679529 updated jar version 2017-01-23 19:05:29 -06:00
Admin
104d341864 Starting to build Domoticz handler 2017-01-23 16:35:34 -06:00
Admin
7772a3de0f Immediate bug fixes
Fixes #378
Fixes #380

Added FAQ link in Help menu
2017-01-23 15:44:53 -06:00
BWS Systems
a348086910 Add FAQ 2017-01-22 18:56:10 -06:00
BWS Systems
31cfcb650b Merge pull request #376 from bwssytems/postv3.5.1changes
Create V4.0.0 from branch
2017-01-22 17:37:54 -06:00
bwssystems
90d56ab686 Added donate button to about menu. Fixed layouts to be consistent for
all pages.
2017-01-22 17:10:54 -06:00
Admin
8dad922eec Updated last text instructions for the beta. 2017-01-19 16:43:27 -06:00
Admin
e5f088cfb9 Finished editor update 2017-01-19 15:57:33 -06:00
BWS Systems
a550288847 add more detail to the systemctl setup on linux 2017-01-19 12:26:26 -06:00
BWS Systems
e0be1507f6 updated info about restricted port 80 2017-01-19 09:07:26 -06:00
audiofreak9
e9dc023971 Update README.md
Fixed missing open brace in the first Multiple Call Construct example
2017-01-16 18:00:04 -05:00
bwssystems
56ebe2d929 Fixed initialization of bridge control entries 2017-01-16 14:21:10 -06:00
bwssystems
aecadc529b addded logging for url item json decoding, fixed harmony activity json
decoding
2017-01-15 13:28:52 -06:00
bwssystems
7a55f6875f Fixing issues with beta, reverted back to Spark 2.3 2017-01-14 15:39:09 -06:00
Admin
382828f615 Updated issues with edit and update device 2017-01-12 10:57:28 -06:00
bwssystems
cd62538785 Updated editor, put in fix for nest status deserialize, updated
configured items display in tabs
2017-01-10 16:56:53 -06:00
Admin
b644b5e3cc Updated scrollable table header sorts for config, vera and harmony
Added upnp notification logic
2017-01-09 17:28:41 -06:00
Admin
6f544e1a7d Updated release with new editor
new angularJS V1.6.1
new spark V2.5.4

testing for new 4beta3
2017-01-09 13:19:03 -06:00
bwssystems
4744a68906 working on editor 2017-01-08 18:24:16 -06:00
Admin
010260429e New edit device in testing 2017-01-06 17:13:37 -06:00
Admin
e694660af3 starting new editor 2017-01-05 16:05:20 -06:00
Admin
a0a2d90617 javascript updates 2017-01-04 11:21:23 -06:00
Admin
5a292c98a2 Continue testing and fixing 4 beta 2017-01-03 16:36:45 -06:00
bwssystems
9155989791 Fixing items 4beta2.2 2017-01-02 13:27:53 -06:00
BWS Systems
bf9a332ecf Merge pull request #347 from pascalschwientek/patch-1
little readme fix
2017-01-01 09:48:23 -06:00
Pascal Schwientek
123ac65723 little readme fix 2017-01-01 09:57:35 +01:00
Admin
9c4eb58739 Fixed issues with 4beta1 2016-12-30 15:00:31 -06:00
Admin
363fb605a6 Adding new things for 4beta2 2016-12-29 16:16:01 -06:00
Admin
ea6c31494b Refactoring for Beta release complete..testing ensues 2016-12-29 15:56:38 -06:00
bwssystems
a3325aa048 refactoring.... 2016-12-28 18:42:08 -06:00
Admin
66d7306cda Continue Refactoring 2016-12-28 16:44:41 -06:00
bwssystems
b7d6d099a6 Small update 2016-12-27 11:19:24 -06:00
Niklas
e36145f216 NPE-Check for harmony webhook 2016-12-27 13:33:27 +01:00
bwssystems
2e177f3d4a Refactoring ready for testing 2016-12-26 22:34:19 -06:00
bwssystems
d827605fa5 Conitnuation of refactoring 2016-12-26 11:28:06 -06:00
bwssystems
729d548d0e More refactoring 2016-12-24 12:59:25 -06:00
bwssystems
ca6b1ae32d continuation of HueMulator refactor 2016-12-24 12:09:50 -06:00
Niklas
8cd571c183 Added a optional webhook for harmony-activity changes 2016-12-23 19:57:34 +01:00
Admin
40a9eb95ac Continuation of refactoring hueMulator 2016-12-22 16:14:38 -06:00
BWS Systems
3a3e5180b1 Incorrect device update http type
Needs to be PUT instead of POST to update.
2016-12-22 09:52:27 -06:00
Admin
4225f4554e Add homemanager 2016-12-21 17:07:21 -06:00
Admin
b5a3bea803 Comitinue refactor of huemulator 2016-12-21 17:06:30 -06:00
Admin
2579949a23 Done testing HomeAssistant, refactoring HueMulator, fixed MQTT dim
control.
2016-12-20 18:20:10 -06:00
Admin
c9d76bed86 Testing HomeAssistant Implementation. 2016-12-19 15:57:45 -06:00
Admin
1500f7fb56 Merge remote-tracking branch 'origin/master' into postv3.5.1changes 2016-12-19 09:02:30 -06:00
BWS Systems
2f10135b1d Merge pull request #314 from melloware/master
Fixed spelling mistake in UDP Sender.
2016-12-19 08:39:18 -06:00
bwssystems
8b0aa6fe98 Added junit dependency 2016-12-17 13:07:58 -06:00
melloware
11eefa9efd Fixed spelling mistake in UDP Sender. 2016-12-17 14:04:06 -05:00
Admin
74c15db202 HomeAssistant continuation 2016-12-16 16:10:49 -06:00
BWS Systems
d0a63747c7 Add note for need of philps hue hub 2016-12-16 10:05:37 -06:00
Admin
74cccd6de3 implementing HomeAssistant 2016-12-15 16:56:25 -06:00
Admin
321b63f0fb update attributes for text files 2016-12-12 09:01:38 -06:00
Admin
4a105b94fd Continuing Home Assistant implementation. 2016-12-09 16:35:46 -06:00
Admin
7677ed08d8 Start building HomeAssistant interface. 2016-12-08 16:41:23 -06:00
Admin
2b1b70cf4f Dim/brighten rework. 2016-12-07 15:53:14 -06:00
Admin
9c252a61ba update nest-controller version 2016-12-05 16:47:30 -06:00
Admin
460931f6e0 continuation of requester filter code. 2016-11-30 15:05:54 -06:00
Admin
5746d271b1 Merge branch 'master' of https://github.com/bwssytems/ha-bridge 2016-11-29 16:40:12 -06:00
Admin
6f58d38224 finish base implementation of requester ip filtering within a url item.
not tested
2016-11-29 16:38:44 -06:00
BWS Systems
e6cde7a3bd Added warning Note and updated Alexa voice commands 2016-11-28 16:23:48 -06:00
BWS Systems
a325a28731 Merge pull request #264 from digiltd/patch-1
remove URL encode form Kodi volume instructions
2016-11-22 08:27:33 -06:00
Sam Turner
bf43d5c33e remove URL encode form Kodi volume instructions 2016-11-22 13:01:53 +00:00
Admin
dcc4041d7d Updating for requester filter and to be able to have multiple calls for
any type of device.
2016-11-21 16:45:01 -06:00
BWS Systems
a3f5b47260 update version 2016-11-21 08:18:46 -06:00
bwssystems
e7acff9f54 Fixed MQTT initialization error with maven build inclusion. Added URI
encoder to handle URI calls with URL characters.

Fixes #250
Fixes #251
Fixes #252
2016-11-19 14:12:32 -06:00
Admin
9c4b428c86 Added updates to README and also the latest release includes more
constructs for itmes and device buttons for delay and count.

Fixes #132
2016-11-18 15:34:20 -06:00
BWS Systems
80b3e87e44 remove harmony user and pass
These parameters are not needed anymore.
2016-11-18 15:20:56 -06:00
BWS Systems
e14482e232 Merge pull request #248 from bwssytems/postV3.2.2NewConnectors
Post v3.2.2 new connectors
2016-11-18 15:17:36 -06:00
Admin
08166c0ebf Finished MQTT support and some testing. Updated harmony-java-client to
v1.1.1 to fix authentication error.
2016-11-18 15:14:58 -06:00
BWS Systems
373515e1ed Clarify Software Alexa Statement
Thanks to digiltd for helping write the clarification.

Fixes #245
2016-11-17 09:52:50 -06:00
Admin
68f38e1d95 MQTT publish implementation, still without password. 2016-11-16 16:58:11 -06:00
Admin
90f2bce282 Fixed requester filter for multiple echos. Started MQTT MEssage screen. 2016-11-14 16:43:33 -06:00
Admin
3eba9b9245 Basic MQTT configuration implementation. Still needs username/pwd and
MQTT screen helper.
2016-11-11 16:42:20 -06:00
Admin
7d39b79e05 Added Requester logic filtering. Added logic for count and delay in
items and buttons.
2016-11-11 14:38:37 -06:00
BWS Systems
407b0e0bd5 Add declaration that software Alexa does not work with local hue bridges 2016-11-09 16:13:18 -06:00
BWS Systems
9baff8d403 Another update 2016-11-08 15:55:50 -06:00
BWS Systems
23a6c7059c Make directory in docs consistent
Fixes #226
2016-11-08 15:54:20 -06:00
Admin
84fb79f9d9 Fixed issue with "Test Dim" button not presenting the value selector
when there was a body with a dim command. Added ability to specifically
set the web server address port.

Fixes #225
Fixes #217
2016-11-08 11:27:43 -06:00
BWS Systems
3702de8efd Merge pull request #222 from TheOriginalAndrobot/master
Update documentation with Google Home info
2016-11-05 11:33:44 -05:00
Androbot
f0ab9afd66 Update documentation with Google Home info 2016-11-05 08:48:35 -07:00
Admin
e5c4e09543 Update readme for version. 2016-11-02 16:33:57 -05:00
Admin
425ac9fb91 Updated upnp response handling to warn on failure and not stop. Added
configuration of echo url for those overseas. Fixed overwriting device
issue when adding a manual device add or after a restart. Added default
IpV4 handling with override for ha-bridge. Fixed Dim URL http body
handling on PUT and POST. Added documentation for Kodi Volume handling.

Fixes #207
Fixes #202
Fixes #201
Fixes #198
Fixes #194
Fixes #193
2016-11-02 16:32:57 -05:00
BWS Systems
32203b65be Merge pull request #189 from bwssytems/postv3.1fixes
Fixes #129 
Fixes #161 
Fixes #166 
Fixes #168 
Fixes #172 
Fixes #173 
Fixes #174 
Fixes #181
2016-10-18 08:42:01 -05:00
Admin
7e9600d2a7 Update release version number. 2016-10-18 08:35:27 -05:00
BWS Systems
dcc470486f Merge pull request #185 from martinbutt/master
Docs fix
2016-10-14 08:17:26 -05:00
Martin Butt
330adbdd4c Docs fix 2016-10-13 15:21:27 -07:00
Admin
23d43b0136 I got some more info from another source about the discovery responses
of a real hue. It sends three responses not just one. Also I have
updated the S/N UUID and the Hue Bridge ID creation as they are
different.
2016-10-10 13:23:29 -05:00
Admin
004276d3ea Changed handling of upnp rsponses from the upnp listener and the
huemulator udp calls to use the same response port.
2016-10-07 16:29:55 -05:00
Admin
e90e0f69ef Updated headers content type to be just "application/json" instead of
"application/json; charset utf-8".
2016-10-04 13:34:47 -05:00
Admin
7163a12384 Updating upnp responses to be more inline with what the hue hub does. To
do so, added the mac address as the id in the bridge id an, serial
number and uuid
2016-10-03 15:07:09 -05:00
Admin
bb65650e53 Added ip check if upnp config address is not available on the current
host. Added checks for content type and body for put and post requests.
2016-09-29 16:21:15 -05:00
Admin
7f7e96465b Switched default web port to be 80. This should resolve some issues with
first time configs and external apps. Changed upnp listener response to
previous style. Set default for link button presses status to true.
2016-09-29 09:25:47 -05:00
Admin
8ff7bc0120 Updated upnp response for M-SEARCH again. Updated devices to have
numbering more in line with how the hue bridge is done. Added special
generation of hue device unique id as the philips api spec shows.  Added
a renumber function to convert id's.
2016-09-28 16:18:53 -05:00
Admin
4da5f65d89 Updatting upnp responses 2016-09-26 17:03:30 -05:00
Admin
faa67827c6 Updated configuration items for hue responses. ADded call thru for hue
devices configured for state info. added dim content body.
2016-09-23 15:46:39 -05:00
Admin
bbce1f4235 Merge remote-tracking branch 'origin/master' into postv3.1fixes 2016-09-22 09:27:02 -05:00
BWS Systems
8575deadf1 Update readne.md for a missed version change
Changed line for download of ha-bridge jar
2016-09-22 09:02:28 -05:00
Admin
ad4015927c Add check for host is down exception on upnp send in listener and not
shutdown only a warning.
2016-09-21 16:33:57 -05:00
BWS Systems
f7df6951b0 Update README.md
Updated version to be the latest in the command examples
2016-09-20 08:29:01 -05:00
Admin
c20d046b30 Updated upnp response to be closer to the hue bridge v2 spec. Updated
handling for is on when bri is 0. Updated exec to check for empty call.
2016-09-15 15:41:59 -05:00
BWS Systems
20dedec8ab Merge pull request #158 from bwssytems/v3-updates
V3.1.0 updates
2016-09-01 11:11:40 -05:00
Admin
580c037b1e updated for unused imports 2016-09-01 11:07:34 -05:00
Admin
77cb064d60 Finished testing fixes and enhancements. 2016-09-01 11:03:11 -05:00
Admin
0e4319ea1d testing for changes: 3.0.0 not storing extended values (2.0.7 does),
update state not always working, Pi3 systemd not starting,
habridge.config created readable by everyone
2016-08-31 16:31:51 -05:00
BWS Systems
c61e623e23 Merge pull request #150 from tylerstraub/patch-1
Update README.md with caveats for using rc.local for starting on raspbian.
2016-08-02 15:45:54 -05:00
Tyler Straub
669483f686 Update README.md
Proposal to add some additional text under the Linux Automation section related to Issue #148. 

see: https://github.com/bwssytems/ha-bridge/issues/148
2016-08-02 12:20:02 -07:00
BWS Systems
5a59747bc9 Merge pull request #141 from bwssytems/hal-integration
Hal integration and other enhancement and fixes
2016-07-19 11:48:49 -05:00
Admin
53ec096c95 Updated version based on enhancement and bug implementations
Fixes #120
Fixes #122
Fixes #123
Fixes #135
Fixes #137
Fixes #138
Fixes #139
2016-07-19 11:42:07 -05:00
Admin
8ccb768391 Updated group 0 handling to be per hue api requirements. 2016-07-15 16:27:53 -05:00
Admin
9d84e2a180 fixed dim addition in bulk add
modified savef button to be active always in bridge settings
2016-07-12 16:26:53 -05:00
Admin
6a5fae583f Updating bridge control to activate save button on list updates. 2016-07-11 16:32:54 -05:00
Admin
8d15d0a0fb Tested api changes 2016-06-09 16:44:11 -05:00
Admin
d4b8b70a83 Added Hue Error Handling objects. Adding whitelsit control. 2016-06-08 16:41:46 -05:00
Admin
a276f97776 Testing api 1.10.0 2016-06-07 16:39:59 -05:00
Admin
9438b25538 Testing upnp response structures. 2016-06-06 16:42:46 -05:00
Admin
6c15ca2c3b Updated the Hue API v 1.11 and tested.
Testing the upnp changes, ongoing.
2016-06-03 16:25:00 -05:00
Admin
39782fa339 Updateing for API V 1.11 and Bridge V2 HUE handling. 2016-06-02 16:51:02 -05:00
Admin
d7e29e2ee5 Finished implementation of HAL calls to the API. Testing next. 2016-05-26 16:06:48 -05:00
Admin
6b4693eaaf Added Home control, Thermostat and other enhancements. 2016-05-25 16:09:29 -05:00
Admin
7f816b03d5 Fixed sizing of scrollable table to be dynamic. Added Appliances,
Theater and Custom devices to HAL interface. Added Select ALL feature
for bulk add screens.
2016-05-24 16:42:05 -05:00
Admin
ed3db4427b Updated view consistency. Fixed HAL bulk add 2016-05-23 16:45:47 -05:00
bwssystems
80ca8c3ca3 updated html pages to reference hal tab 2016-05-21 16:59:03 -05:00
bwssystems
e43473734e Finished draft HAL integration for lights type only 2016-05-21 16:49:12 -05:00
Admin
8a468b8352 First cut at HAL itegration 2016-05-20 16:13:48 -05:00
bwssystems
51ce10cfc7 Fixed build device button in Harmony Device helper tab.
Fixes #116
2016-05-15 12:25:28 -05:00
Admin
b5f7144c9c Update readme with a script construction. 2016-05-11 14:36:58 -05:00
Admin
86a931d383 added exec:// type for loops. updated button maptypeid naming.
Fixes #114
Fixes #115
2016-05-11 12:23:43 -05:00
Admin
3e890721c5 Fixed handling of dim and brigthen requests as they were not setting the
state appropriately for other systems. Fixed handling of data return
from an http call if the entity was empty.

Fixes #102
Fixes #107
2016-05-10 12:29:37 -05:00
Admin
62d1c64a3d Updated entitiy handling in HueMulator http call. 2016-05-06 08:30:37 -05:00
Admin
c025b186cd Updated a comment for the seb server port 2016-05-05 14:13:06 -05:00
Admin
e999c3a969 Update spelling in readme 2016-05-05 10:45:03 -05:00
Admin
351403e611 Fixed success parsing on http to return success. 2016-05-04 14:38:37 -05:00
Admin
c773477a43 Added delete dialog for confirmation. Fixed bug with text in
intensity.byte for vera device tab. Fixed issue with parsing replies for
http requests. Fixed Hue device bulk add. recommit


Fixes #100
Fixes #102
Fixes #104
Fixes #105
2016-05-04 11:45:51 -05:00
BWS Systems
5d1f0ce3b6 update versions in unit file 2016-05-02 15:41:01 -05:00
Admin
7e0fd6c21b Updated the register with hue function to not send a user name when
linking as this has been deprecated by Philips.

Fixes #99
2016-04-29 16:12:52 -05:00
BWS Systems
3bf52f5da0 Updated Readme
Added comment for Hue proxy and using the link button.
2016-04-29 12:54:08 -05:00
BWS Systems
bd856d8f9e Fixed spelling 2016-04-29 12:44:30 -05:00
Admin
73b2be752e Issue with http/https handling trying to token out line. Updated Readme
Fixes #96
Fixes #98
2016-04-29 12:27:15 -05:00
Admin
dda7a7a34a Merge remote-tracking branch 'origin/add-color-args' 2016-04-29 11:26:06 -05:00
Admin
f238e05533 Fixed bug where device state object was dropped from hue api device
response object. This caused devices to appear offline and invalid
response interpretation by the echo.

Fixes #93
2016-04-28 12:12:49 -05:00
Admin
aaaebd0c05 Merged add-color-args branch to master.
Fixes #72
Fixes #81
Fixes #82
Fixes #85
Fixes #87
Fixes #88
Fixes #89
Fixes #90
2016-04-27 14:42:52 -05:00
Admin
9a1924422e Updated Readme 2016-04-27 13:07:49 -05:00
Admin
e446c618ce MAde the generate buttons consistent in the UI. Updated the Readme. 2016-04-27 12:40:54 -05:00
Admin
60d35acff9 update readme for formatting 2016-04-27 09:11:11 -05:00
Admin
c9adab53a9 Updated selections in device add or editor and updated loggin for upnp
listener and updated the readme
2016-04-27 09:08:41 -05:00
Admin
72b6b2027b Finished updating UPNP handling with ports and errors and testing for
new features.

Updateding README
2016-04-26 16:49:26 -05:00
Admin
60239bad82 Finished testing and refactoring for udp/tcp and http 2016-04-26 10:12:36 -05:00
Admin
aecd589308 Finished implementation of headers and tested. Added calls for TCP
request with UDP and also added the value replacement for dimming, this
needs to be tested.
2016-04-25 16:48:59 -05:00
Admin
ee45cee8e3 Fixed error handling in for loops 2016-04-22 14:00:47 -05:00
Admin
21e5dfb338 added multiple command execution. Added commandline Exec. NEeds testing 2016-04-22 13:59:12 -05:00
Admin
b73a4cd666 Finished testing and updating HUE passthru handling 2016-04-22 10:58:54 -05:00
Admin
05418fdda1 Updated passthru after more testing 2016-04-21 15:41:06 -05:00
Admin
a717fd7c68 Add new classes. 2016-04-20 16:44:39 -05:00
Admin
8408d7350e More testing on HUE passthru. 2016-04-20 16:44:02 -05:00
Admin
3ba8f56db2 Completed Hue passthru 2016-04-18 16:41:15 -05:00
Admin
7a0946e3b7 Continue adding HUE pass thru. 2016-04-15 16:30:34 -05:00
Admin
50c9369d71 Added config override for server port. Updated Readme. Continued
enhancements to pass color to a real hue.
2016-04-14 17:12:06 -05:00
Admin
6c2a34f507 Updated code for collor passing. 2016-04-04 16:36:05 -05:00
Admin
ee2c105040 Started implementation 2016-04-01 16:33:55 -05:00
Admin
3ac83912f3 Fixed success return validation on calls and implemented validation
catching logic for URL issues.

Fixes #73
Fixes #75
2016-03-29 16:24:12 -05:00
Admin
e62fcf7765 Adding URL formatting for various characters and updating test for
success return.
2016-03-28 16:36:21 -05:00
Admin
9c3d95f177 Finished implementing and testing state change and bug fixes from
testing

Fixes #44
Fixes #60
Fixes #61
Fixes #63
Fixes #66
Fixes #69
2016-03-21 16:37:13 -05:00
Admin
926a7f50dc Finished adding dim versus on, utf-8 and track state and return on api
calls.
2016-03-18 16:38:18 -05:00
Admin
ad820a68c9 Updated C/F setting. Started to implement UTF-8 2016-03-16 17:13:03 -05:00
Admin
73f0f766f7 Added more logging and updated nest controller version to fix issues
with range temp setting in nest.
2016-03-09 16:45:21 -06:00
Admin
113ce0ca59 Updated logging with new nest module. 2016-03-08 16:40:32 -06:00
bwssystems
a5860417c1 Update null numberoflogmessages 2016-02-27 12:56:42 -06:00
bwssystems
48af3d84a2 Finished configurable logging view and control.
Fixes #51
Fixes #52
2016-02-27 12:48:19 -06:00
Admin
8ce0483e54 Adding log level control 2016-02-26 16:38:41 -06:00
Admin
1a2024d92b Tables in all views updated to scrollable/sortable. Refactored
controllers for views. debugging logging message return for messages
with invalid text for json parsing.
2016-02-25 16:42:35 -06:00
Admin
8198919a27 playing with scrollable table for log 2016-02-24 16:46:52 -06:00
Admin
614734a2aa Adding logs window. 2016-02-23 16:52:00 -06:00
Admin
58fccb1fa7 fixed about issue. Looking into log view and changed log impl to logback
within slf4j.
2016-02-22 16:38:57 -06:00
Admin
77d3084b01 Finished slider for testint items with percentage items. Many refactors
and fixes.

Fixes #43
Fixes #46
Fixes #47
Fixes #48
2016-02-19 16:34:04 -06:00
Admin
922bb54143 add js and css dialog files 2016-02-18 16:40:25 -06:00
Admin
8586bbd965 Found slider and popup dialog. 2016-02-18 16:39:41 -06:00
Admin
d64a028f30 removed slider.js and .css changing to a nicer slider 2016-02-18 08:49:49 -06:00
Admin
fff30d17d6 Finished updating settings editor and reinit control. Starting number
slider for input.
2016-02-17 16:41:34 -06:00
Admin
13fa5dea73 update ui components. 2016-02-16 16:53:01 -06:00
Admin
1d6a4c1432 Updated UI for configuration and added shutdown of harmony and nest
connections.
2016-02-16 16:52:12 -06:00
bwssystems
9cb275230e Updating editing for Settings UI. 2016-02-13 17:02:51 -06:00
Admin
c97ab2cd38 Fixed bulk add race conditions. added save capability into UI. UI
settings layout update.
2016-02-12 15:51:38 -06:00
Admin
7b45ca9438 added class backup handler to git 2016-02-11 16:52:08 -06:00
Admin
e6da9950d6 Added config settings page and refactored backup handling and bridge
settings and cotntrol. Next is to add the editing to the config screen.
2016-02-11 16:51:11 -06:00
Admin
20328b15d8 Added config file handling for the next step in config screens. 2016-02-10 16:47:58 -06:00
Admin
2ff73e5672 Added reinitialize and stop commands in prep for building the config
file handling.
2016-02-09 16:41:05 -06:00
Admin
cca9a6be78 Finished button sleep time addition. Updated documentation for
button.sleep and corrected the device DB line and updated ip list
directives for vera.address and harmony.address.

Fixes #39
Fixes #40
Fixes #42
2016-02-08 16:21:34 -06:00
Admin
5d5b68209a Updated test notifcation to a toast, added config button sleep time. 2016-02-05 16:10:47 -06:00
Admin
d2e906caa3 Fixed variable mis name in apps.js for olddevicename 2016-02-03 14:32:33 -06:00
Admin
a1708f2a88 Vera device and scene creation testing and added Copy capability to add
an already configured device with a different name.

Fixes #32
Fixes #38
2016-02-02 12:04:48 -06:00
Admin
49c3b85894 Fixed the vera device and scene build. 2016-02-01 16:34:04 -06:00
Admin
f9f5a3a878 Updated code in the html and javascript to select the coorect name for
the button press command. Fixed the calculation of celsius conversion
for setting nest temperatures.

Fixes #33
Fixes #34
2016-01-29 13:49:34 -06:00
Admin
2565183ee9 Finished implementation for multiple vera support. Tweaks to the UI.
Fixes #19
Fixes #21
Fixes #23
Fixes #26
Fixes #28
Fixes #30
Fixes #31
2016-01-28 16:39:20 -06:00
Admin
a6bb1ae3aa Fixed the harmony button to be a label and not the name. Implementation
of multi batch add completed.
2016-01-27 16:54:23 -06:00
Admin
4bc91be88b Added backup and restore for device db functionality. Testing multi
button press for harmony.
2016-01-26 16:47:16 -06:00
Admin
315fd31270 implemented multi button build for devices. more testing needed. fixed
tab display on first view.
2016-01-25 16:35:22 -06:00
Admin
09787fe08d Fixed undefined name error in nest setup and celsius conversion for the
nest thermostat.
2016-01-22 12:20:24 -06:00
Admin
37d346f558 Finished Nest implementation and default ip selection to not be
127.0.0.1

Fixes #12
Fixes #20
2016-01-21 16:44:24 -06:00
Admin
ac59398aa0 Testing nest functionality 2016-01-20 16:46:21 -06:00
Admin
87073435fc Updated code for IP address selection on startup fi none given. 2016-01-13 14:54:35 -06:00
Admin
8687f3482a Continuation of nest implementation 2016-01-12 16:34:05 -06:00
Admin
32a5f26ddd Added new object 2016-01-11 16:45:49 -06:00
Admin
c28f07d628 Continuation of nest implementation. 2016-01-11 16:45:02 -06:00
Admin
d3cc961dfb Initial updates for nest control 2016-01-08 16:14:42 -06:00
Admin
1b3d826f28 Fixed Readme format errors 2015-12-29 10:18:51 -06:00
Admin
c8f4d89a45 Update Readme for clarity on upnp.config.address usage. Also, updated
the 'ask alexa' section with more details.
2015-12-29 10:14:26 -06:00
Admin
2b335d6b9b Updated upnp listener logging. Fixed issue with HUE emulation for
configuration. HUE mobile app now connects.
2015-12-17 16:13:46 -06:00
Admin
b27bb5eef8 Updating hue emulation repsonses using hue android app. fixed issues in
returning the hue config and for requesting the mac address.
2015-12-16 16:41:08 -06:00
Admin
3c54ccd56d Finished implementation of generic UDP handling. This will support the
LimitlessLED Bridge. Updated Readme for UDP and more on configuration
setup. Fixed issue with HueMulator handliong due to devices without
mapTypes.

Fixes #6.
2015-12-07 16:34:37 -06:00
Admin
cf772334c4 Start adding UDP handling. 2015-12-04 16:21:11 -06:00
Admin
5a843f7569 Fixed handling when no harmony hub is given. dev.mode was updated as
well.

This closes #14.
2015-12-03 14:36:23 -06:00
Admin
2a52783bb1 Final release of multiple harmony hubs and fixes for GUI.
This closes #5 and #13
2015-12-02 15:42:09 -06:00
Admin
195f1854ec Remove Current Activity object as it is not needed. 2015-12-02 15:35:35 -06:00
Admin
2e5596a6e4 Pre-release of multiple Harmony Hub Support 2015-12-02 15:34:30 -06:00
Admin
9fc13c6c45 Conitinuation of multiple harmony hub implementation. 2015-12-01 16:40:29 -06:00
Admin
4b4d4e36c7 Update filtering capability. Still errors in html/angular. 2015-11-30 16:43:14 -06:00
Admin
1e7bdc560b Start adding components to handle multiple harmony hubs. 2015-11-24 14:27:21 -06:00
Admin
aff0f8d64c Removed the doling out of hue devices on a get call. 2015-11-23 15:17:17 -06:00
Admin
7a812d6e6b Updated VeraInfo to use default http client. Finished work on doling out
devices in the resource manager. Caveat, this did not work well with the
echo. Next version will roll this back.
2015-11-23 15:14:38 -06:00
Admin
26f2105801 First try at doling out lights when requested. 2015-11-20 16:40:43 -06:00
Admin
feef345a3b Update for start of virtualize multiple respnses 2015-11-19 16:43:30 -06:00
Admin
314ae58ebd This closes #8, closes #9, closes #10 and closes #11.
Finished device, scene and activity tracking, updated upnp handling,
updated HUE API config handling and test on and off calls.
2015-11-18 16:31:11 -06:00
Admin
d8b6232ac1 First draft and start of tracking for scenes, devices, activities.
Implemented Harmony activity list so far.
2015-11-17 16:40:24 -06:00
Admin
41e22ee64d Refactor TestUrl call in apps.js. Updated UI to only have test buttons
after a device is added.
2015-11-16 16:38:29 -06:00
Admin
be2fbcd4cb Update script for test on and off. Incorrect body sent for off commands
error return was interpretted incorrectly.
2015-11-16 14:53:42 -06:00
Admin
14e7f37522 Updated HUE API for generic config requests. Updated config parameters
to be more real.
2015-11-13 16:17:49 -06:00
Admin
e3f5946c9d Updated handling to be CORS compatible. 2015-11-13 11:40:13 -06:00
Admin
12eab16f21 updated Readme and enhance emulator logging code. 2015-11-13 11:26:19 -06:00
Admin
3df68047a9 Updated handling of dimming/brightness request. This could be buggy if
the echo changed to not send an on command when changing dimming. The
HUE state change API is not consistent for brightness handling.
2015-11-13 10:57:18 -06:00
Admin
0dd652f82a Updated device return for HUE emulation based on new information of LUX
bulb handling in the API from the Philips dev info.
2015-11-12 16:31:01 -06:00
Admin
53af1a4dfd Final tweaks to the order of sections in the readme 2015-11-11 15:24:17 -06:00
Admin
816a0025b1 Finished updating upnp for the readme 2015-11-11 15:16:42 -06:00
Admin
405562809a finished adding harmony items to readme 2015-11-11 14:39:13 -06:00
Admin
4c87c6fce8 Update readme some more. 2015-11-11 13:29:06 -06:00
Admin
2fd0f7748b Some more readme updates. 2015-11-11 13:19:50 -06:00
Admin
ed5f3b4b3c More readme updates. 2015-11-11 12:43:38 -06:00
Admin
aad09b7527 More updates to readme 2015-11-11 11:28:41 -06:00
Admin
0ae66da085 Continuation of readme updating. 2015-11-11 11:26:42 -06:00
Admin
d344b764da Updated HUE REst descriptions. 2015-11-10 16:48:30 -06:00
Admin
c85b67fb9f Next update for HUE API description 2015-11-10 16:25:16 -06:00
Admin
a23d662444 Adding supported HUE API calls descriptions. 2015-11-10 13:57:01 -06:00
Admin
4b98f799c2 Updated vera returns for scened and devices. 2015-11-10 11:51:21 -06:00
Admin
acba2b5cae Another config rest api clarification. 2015-11-10 11:45:40 -06:00
Admin
4dc818296a Updated Configuration REST API commands. 2015-11-10 11:40:41 -06:00
Admin
40123ed858 Updating document for REST usage. 2015-11-09 16:47:38 -06:00
Admin
718ba5a5c2 Updated build in pom.xml to include classes that were removed for
minimize. This required a dummy clas setup as well in the Harmony
Server.
2015-11-04 11:00:46 -06:00
Admin
2e6944d840 Missed new java class in release. 2015-11-03 15:26:56 -06:00
293 changed files with 35939 additions and 3157 deletions

2
.gitattributes vendored
View File

@@ -1,5 +1,5 @@
# Auto detect text files and perform LF normalization
* text=auto
text=auto
# Custom for Visual Studio
*.cs diff=csharp

13
.gitignore vendored
View File

@@ -12,3 +12,16 @@ data
/.settings/
/start.bat
/.classpath
/.project
sftp-config\.json
/bin/
.vscode/launch.json
.vscode/launch.test.json
.vscode/settings.json
.vscode/tasks.json
# dependencies
/node_modules
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>

1375
README.md

File diff suppressed because one or more lines are too long

273
java8_pom.xml Normal file
View File

@@ -0,0 +1,273 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>5.4.0</version>
<packaging>jar</packaging>
<name>HA Bridge</name>
<description>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</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>Eclipse Paho Repo</id>
<url>https://repo.eclipse.org/content/repositories/paho-releases/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.bwssytems</groupId>
<artifactId>harmony-java-client</artifactId>
<version>master-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.bwssytems</groupId>
<artifactId>nest-controller</artifactId>
<version>1.0.14</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.7.2</version>
<exclusions>
<exclusion>
<artifactId>slf4j-simple</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.24</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>net.java.dev.eval</groupId>
<artifactId>eval</artifactId>
<version>0.5</version>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-core</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.bwssytems</groupId>
<artifactId>lifx-sdk-java</artifactId>
<version>2.1.6</version>
</dependency>
<dependency>
<groupId>com.github.mob41</groupId>
<artifactId>broadlink-java-api</artifactId>
<version>master-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<dependency>
<groupId>org.jmdns</groupId>
<artifactId>jmdns</artifactId>
<version>3.5.5</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>version.properties</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>version.properties</exclude>
</excludes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M2</version>
<executions>
<execution>
<id>enforce-maven</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireMavenVersion>
<version>3.3</version>
</requireMavenVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<skipTests>false</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/*.txt</exclude>
<exclude>META-INF/maven/**</exclude>
<exclude>about_files/**</exclude>
</excludes>
</filter>
<filter>
<artifact>*:*</artifact>
</filter>
<filter>
<artifact>org.slf4j:slf4j-api</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>commons-logging:commons-logging</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>xpp3:xpp3</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.igniterealtime.smack:*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>com.github.bwssytems:harmony-java-client</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.eclipse.paho:org.eclipse.paho.client.mqttv3</artifact>
<includes>
<include>**</include>
</includes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.bwssystems.HABridge.HABridge</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

264
pom.xml
View File

@@ -1,114 +1,208 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>1.0.5</version>
<version>5.4.1-java11</version>
<packaging>jar</packaging>
<name>HA Bridge</name>
<description>Emulates a Philips Hue bridge to allow the Amazon Echo to hook up to other HA systems, i.e. Vera or Harmony Hub, using lightweight frameworks</description>
<description>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</description>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>Eclipse Paho Repo</id>
<url>https://repo.eclipse.org/content/repositories/paho-releases/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.bwssytems</groupId>
<artifactId>harmony-java-client</artifactId>
<version>1.0.8</version>
<groupId>com.github.bwssytems</groupId>
<artifactId>harmony-java-client</artifactId>
<version>master-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.2</version>
<groupId>com.github.bwssytems</groupId>
<artifactId>nest-controller</artifactId>
<version>1.0.14</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.7.2</version>
<exclusions>
<exclusion>
<artifactId>slf4j-simple</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>com.cedarsoftware</groupId>
<artifactId>json-io</artifactId>
<version>4.1.6</version>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>net.java.dev.eval</groupId>
<artifactId>eval</artifactId>
<version>0.5</version>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.24</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>net.java.dev.eval</groupId>
<artifactId>eval</artifactId>
<version>0.5</version>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.0-beta4</version>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-core</artifactId>
<version>4.0.7</version>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-core</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-debug</artifactId>
<version>4.0.7</version>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.bwssytems</groupId>
<artifactId>lifx-sdk-java</artifactId>
<version>2.1.6</version>
</dependency>
<dependency>
<groupId>com.github.mob41</groupId>
<artifactId>broadlink-java-api</artifactId>
<version>master-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<dependency>
<groupId>org.jmdns</groupId>
<artifactId>jmdns</artifactId>
<version>3.5.5</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>version.properties</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>version.properties</exclude>
</excludes>
<filtering>false</filtering>
</resource>
</resources>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>version.properties</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>version.properties</exclude>
</excludes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M2</version>
<executions>
<execution>
<id>enforce-maven</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireMavenVersion>
<!-- Change this to Version 3.3 for Java 1.8 and Raspberry PI compilation, Java 11 is 3.6 -->
<version>3.6</version>
</requireMavenVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<!-- Comment this release line out for Java 1.8 and Raspberry PI compilation -->
<release>11</release>
<!-- Uncomment the next two lines for Java 1.8 and Raspberry PI compilation -->
<!-- <source>1.8</source> -->
<!-- <target>1.8</target> -->
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<skipTests>false</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
@@ -126,13 +220,14 @@
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/*.txt</exclude>
<exclude>META-INF/maven/**</exclude>
<exclude>META-INF/services/**</exclude>
<exclude>META-INF/DEPENDENCIES</exclude>
<exclude>about_files/**</exclude>
</excludes>
</filter>
<filter>
<artifact>org.slf4j:*</artifact>
<artifact>*:*</artifact>
</filter>
<filter>
<artifact>org.slf4j:slf4j-api</artifact>
<includes>
<include>**</include>
</includes>
@@ -143,10 +238,33 @@
<include>**</include>
</includes>
</filter>
<filter>
<artifact>xpp3:xpp3</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.igniterealtime.smack:*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>com.github.bwssytems:harmony-java-client</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.eclipse.paho:org.eclipse.paho.client.mqttv3</artifact>
<includes>
<include>**</include>
</includes>
</filter>
</filters>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.bwssystems.HABridge.HABridge</mainClass>
</transformer>
</transformers>

View File

@@ -0,0 +1,34 @@
package com.bwssystems.HABridge;
public class BridgeControlDescriptor {
private boolean reinit;
private boolean stop;
private boolean linkButton;
public BridgeControlDescriptor() {
super();
this.reinit = false;
this.stop = false;
}
public boolean isReinit() {
return reinit;
}
public void setReinit(boolean reinit) {
this.reinit = reinit;
}
public boolean isStop() {
return stop;
}
public void setStop(boolean stop) {
this.stop = stop;
}
public boolean isLinkButton() {
return linkButton;
}
public void setLinkButton(boolean linkButton) {
this.linkButton = linkButton;
}
}

View File

@@ -0,0 +1,470 @@
package com.bwssystems.HABridge;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.Map.Entry;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.api.hue.HueError;
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import spark.Request;
public class BridgeSecurity {
private static final Logger log = LoggerFactory.getLogger(BridgeSecurity.class);
private static final String USER_SESSION_ID = "user";
private static final String DEPRACATED_INTERNAL_USER = "thehabridgeuser";
private static final String TEST_USER_TYPE = "test_ha_bridge";
private static final byte[] SALT = {
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
};
private static char[] habridgeKey;
private String execGarden;
private BridgeSecurityDescriptor securityDescriptor;
private boolean settingsChanged;
public BridgeSecurity(char[] theKey, String theExecGarden) {
habridgeKey = theKey;
execGarden = theExecGarden;
securityDescriptor = null;
settingsChanged = false;
}
public void setSecurityData(String theData) {
String anError = null;
if(theData != null && !theData.isEmpty()) {
try {
securityDescriptor = new Gson().fromJson(decrypt(theData), BridgeSecurityDescriptor.class);
} catch (JsonSyntaxException e) {
anError = e.getMessage();
} catch (GeneralSecurityException e) {
anError = e.getMessage();
} catch (IOException e) {
anError = e.getMessage();
}
if(anError != null)
log.warn("Cound not get security data, using default security (none): " + anError);
}
if(theData == null || anError != null) {
securityDescriptor = new BridgeSecurityDescriptor();
}
}
public String getSecurityDescriptorData() throws UnsupportedEncodingException, GeneralSecurityException {
return encrypt(new Gson().toJson(securityDescriptor));
}
public boolean isUseLinkButton() {
return securityDescriptor.isUseLinkButton();
}
public String setPassword(User aUser) throws IOException {
String error = null;
if(aUser != null) {
error = aUser.validate();
if(error == null) {
if(securityDescriptor.getUsers() != null) {
User theUser = securityDescriptor.getUsers().get(aUser.getUsername());
if(theUser != null) {
theUser.setPassword(aUser.getPassword());
theUser.setPassword2(null);
settingsChanged = true;
}
else
error = "User not found";
}
else
error = "User not found";
}
}
else
error = "invalid user object given";
return error;
}
public String addUser(User aUser) throws IOException {
String error = null;
if(aUser != null) {
error = aUser.validate();
if(error == null) {
if(securityDescriptor.getUsers() == null)
securityDescriptor.setUsers(new HashMap<String, User>());
if(securityDescriptor.getUsers().get(aUser.getUsername()) == null) {
securityDescriptor.getUsers().put(aUser.getUsername(), aUser);
settingsChanged = true;
}
else
error = "Invalid request";
}
}
else
error = "invalid user object given";
return error;
}
public String delUser(User aUser) throws IOException {
String error = null;
if(aUser != null) {
if(securityDescriptor.getUsers() != null) {
if(securityDescriptor.getUsers().get(aUser.getUsername()) != null) {
securityDescriptor.getUsers().remove(aUser.getUsername());
settingsChanged = true;
}
else
error = "User not found";
}
}
else
error = "invalid user object given";
return error;
}
public String getExecGarden() {
return execGarden;
}
String getKeyfilePath() {
return securityDescriptor.getKeyfilePath();
}
String getKeyfilePassword() {
return securityDescriptor.getKeyfilePassword();
}
private void setUseLinkButton(boolean useThis) {
securityDescriptor.setUseLinkButton(useThis);
settingsChanged = true;
}
public boolean isSecureHueApi() {
return securityDescriptor.isSecureHueApi();
}
public boolean isUseHttps() {
return securityDescriptor.isUseHttps();
}
public boolean isKeyfilePW() {
if(securityDescriptor.getKeyfilePassword() != null && !securityDescriptor.getKeyfilePassword().trim().isEmpty()) {
return true;
}
return false;
}
private void setSecureHueApi(boolean theState) {
securityDescriptor.setSecureHueApi(theState);
settingsChanged = true;
}
private void setUseHttps(boolean usehttps, String keyfilepath, String keyfilepassword) {
if(usehttps) {
if(!isUseHttps()) {
securityDescriptor.setKeyfilePath(keyfilepath);
securityDescriptor.setKeyfilePassword(keyfilepassword);
securityDescriptor.setUseHttps(usehttps);
settingsChanged = true;
} else {
if(!keyfilepassword.equals("########")) {
securityDescriptor.setKeyfilePassword(keyfilepassword);
settingsChanged = true;
}
if(!securityDescriptor.getKeyfilePath().equals(keyfilepath)) {
securityDescriptor.setKeyfilePath(keyfilepath);
settingsChanged = true;
}
}
} else {
if(isUseHttps()) {
securityDescriptor.setKeyfilePassword("");
securityDescriptor.setKeyfilePath("");
securityDescriptor.setUseHttps(usehttps);
settingsChanged = true;
}
}
}
public SecurityInfo getSecurityInfo() {
SecurityInfo theInfo = new SecurityInfo();
theInfo.setUseLinkButton(isUseLinkButton());
theInfo.setSecureHueApi(isSecureHueApi());
theInfo.setSecure(isSecure());
theInfo.setUseHttps(isUseHttps());
theInfo.setKeyfilePath(securityDescriptor.getKeyfilePath());
if(isKeyfilePW()) {
theInfo.setKeyfilePassword("########");
}
else {
theInfo.setKeyfilePassword("");
}
if(isSecure()) {
theInfo.setExecGarden(execGarden);
}
return theInfo;
}
public void setSecurityDataByInfo(SecurityInfo theInfo) {
setUseLinkButton(theInfo.isUseLinkButton());
setSecureHueApi(theInfo.isSecureHueApi());
setUseHttps(theInfo.isUseHttps(), theInfo.getKeyfilePath(), theInfo.getKeyfilePassword());
}
public LoginResult validatePassword(User targetUser) throws IOException {
LoginResult result = new LoginResult();
if(targetUser != null && targetUser.getUsername() != null) {
if(securityDescriptor.getUsers() != null && securityDescriptor.getUsers().get(targetUser.getUsername()) != null) {
User theUser = securityDescriptor.getUsers().get(targetUser.getUsername());
if(theUser.getPassword() != null) {
theUser.setPassword2(targetUser.getPassword());
if(theUser.validatePassword()) {
theUser.setPassword2(null);
result.setUser(targetUser);
}
else
result.setError("user or password not correct");
} else {
result.setError("input password is not set....");
}
}
else
result.setError("user or password not correct");
}
else
result.setError("input user not given");
return result;
}
public boolean isSecure() {
return securityDescriptor.isSecure();
}
public boolean isSettingsChanged() {
return settingsChanged;
}
public void setSettingsChanged(boolean settingsChanged) {
this.settingsChanged = settingsChanged;
}
public Map<String, WhitelistEntry> getWhitelist() {
return securityDescriptor.getWhitelist();
}
public void setWhitelist(Map<String, WhitelistEntry> aWhitelist) {
securityDescriptor.setWhitelist(aWhitelist);
settingsChanged = true;
return;
}
public HueError[] validateWhitelistUser(String aUser, String userDescription, boolean strict) {
String validUser = null;
boolean found = false;
if (aUser != null && !aUser.equalsIgnoreCase("undefined") && !aUser.equalsIgnoreCase("null")
&& !aUser.equalsIgnoreCase("") && !aUser.equals(DEPRACATED_INTERNAL_USER)) {
if (securityDescriptor.getWhitelist() != null) {
Set<String> theUserIds = securityDescriptor.getWhitelist().keySet();
Iterator<String> userIterator = theUserIds.iterator();
while (userIterator.hasNext()) {
validUser = userIterator.next();
if (validUser.equals(aUser)) {
found = true;
log.debug("validateWhitelistUser: found a user <" + aUser + ">");
}
}
}
if(!found && !strict) {
log.debug("validateWhitelistUser: a user was not found and it is not strict rules <" + aUser + "> being created");
newWhitelistUser(aUser, userDescription);
found = true;
}
}
if (!found) {
log.debug("validateWhitelistUser: a user was not found and strict rules is set to: " + strict + "for user <" + aUser + ">");
return HueErrorResponse.createResponse("1", "/api/" + aUser == null ? "" : aUser, "unauthorized user", null, null, null).getTheErrors();
}
return null;
}
public String findWhitelistUserByDeviceType(String aDeviceType) {
String validUser = null;
boolean found = false;
WhitelistEntry anEntry = null;
if (aDeviceType != null) {
if (securityDescriptor.getWhitelist() != null) {
Set<String> theUserIds = securityDescriptor.getWhitelist().keySet();
Iterator<String> userIterator = theUserIds.iterator();
while (!found && userIterator.hasNext()) {
validUser = userIterator.next();
anEntry = securityDescriptor.getWhitelist().get(validUser);
if (anEntry.getName().equals(aDeviceType)) {
found = true;
log.debug("findWhitelistUserByDeviceType: found a user <" + validUser + "> for device type <" + aDeviceType + ">");
}
}
}
}
if(!found)
validUser = null;
return validUser;
}
private void newWhitelistUser(String aUser, String userDescription) {
if (securityDescriptor.getWhitelist() == null) {
securityDescriptor.setWhitelist(new HashMap<>());
}
if(userDescription == null)
userDescription = "auto insert user";
securityDescriptor.getWhitelist().put(aUser, WhitelistEntry.createEntry(userDescription));
setSettingsChanged(true);
}
public String createWhitelistUser(String userDescription) {
String aUser = null;
String theEntry = findWhitelistUserByDeviceType(userDescription);
if(theEntry == null) {
aUser = getNewUserID();
newWhitelistUser(aUser, userDescription);
} else {
aUser = theEntry;
}
return aUser;
}
public void convertWhitelist(Map<String, WhitelistEntry> whitelist) {
securityDescriptor.setWhitelist(whitelist);
}
private String getNewUserID() {
UUID uid = UUID.randomUUID();
StringTokenizer st = new StringTokenizer(uid.toString(), "-");
String newUser = "";
while (st.hasMoreTokens()) {
newUser = newUser + st.nextToken();
}
return newUser;
}
public void removeHttpsSettings() {
securityDescriptor.setUseHttps(false);
securityDescriptor.setKeyfilePassword(null);
securityDescriptor.setKeyfilePath(null);
setSettingsChanged(true);
}
public void removeTestUsers() {
if (securityDescriptor.getWhitelist() != null) {
Object anUser = securityDescriptor.getWhitelist().remove(DEPRACATED_INTERNAL_USER);
if(anUser != null)
setSettingsChanged(true);
Iterator<Entry<String, WhitelistEntry>> it = securityDescriptor.getWhitelist().entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, WhitelistEntry> pair = it.next();
if(pair.getValue().getName().equals(TEST_USER_TYPE)) {
it.remove(); // avoids a ConcurrentModificationException
setSettingsChanged(true);
}
}
}
}
static String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(habridgeKey));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
return base64Encode(pbeCipher.doFinal(property.getBytes("UTF-8")));
}
private static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
static String decrypt(String property) throws GeneralSecurityException, IOException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(habridgeKey));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
}
private static byte[] base64Decode(String property) throws IOException {
return Base64.getDecoder().decode(property);
}
public void addAuthenticatedUser(Request request, User u) {
request.session().attribute(USER_SESSION_ID, u);
}
public void removeAuthenticatedUser(Request request) {
request.session().removeAttribute(USER_SESSION_ID);
request.session().invalidate();
}
public User getAuthenticatedUser(Request request) {
User theUser = request.session().attribute(USER_SESSION_ID);
if(theUser == null) {
String authHeader = request.headers("Authorization");
if(authHeader != null) {
byte[] authData;
try {
authData = base64Decode(authHeader.substring(6));
} catch (IOException e1) {
// TODO Auto-generated catch block
return theUser;
}
String[] credentials = new String(authData).split(":");
String username = credentials[0];
String password = credentials[1];
theUser = new User();
theUser.setUsername(username);
theUser.setPassword(password);
LoginResult theResult = null;
try {
theResult = validatePassword(theUser);
} catch (IOException e) {
// TODO Auto-generated catch block
return null;
}
if(theResult != null && theResult.getError() == null) {
addAuthenticatedUser(request, theUser);
}
}
}
return theUser;
}
}

View File

@@ -0,0 +1,97 @@
package com.bwssystems.HABridge;
import java.util.Map;
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
public class BridgeSecurityDescriptor {
private Map<String, User> users;
private boolean useLinkButton;
private String execGarden;
private boolean secureHueApi;
private Map<String, WhitelistEntry> whitelist;
private boolean useHttps;
private String keyfilePassword;
private String keyfilePath;
public BridgeSecurityDescriptor() {
super();
this.setUseLinkButton(false);
}
public Map<String, User> getUsers() {
return users;
}
public void setUsers(Map<String, User> users) {
this.users = users;
}
public boolean isUseLinkButton() {
return useLinkButton;
}
public void setUseLinkButton(boolean useLinkButton) {
this.useLinkButton = useLinkButton;
}
public String getExecGarden() {
return execGarden;
}
public void setExecGarden(String execGarden) {
this.execGarden = execGarden;
}
public boolean isSecureHueApi() {
return secureHueApi;
}
public void setSecureHueApi(boolean secureHueApi) {
this.secureHueApi = secureHueApi;
}
public Map<String, WhitelistEntry> getWhitelist() {
return whitelist;
}
public void setWhitelist(Map<String, WhitelistEntry> whitelist) {
this.whitelist = whitelist;
}
public boolean isSecure() {
boolean secureFlag = false;
if(users != null && !users.isEmpty()) {
for (Map.Entry<String, User> entry : users.entrySet())
{
if(entry.getValue().getPassword() != null && !entry.getValue().getPassword().isEmpty()) {
secureFlag = true;
break;
}
}
}
return secureFlag;
}
public boolean isUseHttps() {
return useHttps;
}
public void setUseHttps(boolean useHttps) {
this.useHttps = useHttps;
}
public String getKeyfilePassword() {
return keyfilePassword;
}
public void setKeyfilePassword(String keyfilePassword) {
this.keyfilePassword = keyfilePassword;
}
public String getKeyfilePath() {
return keyfilePath;
}
public void setKeyfilePath(String keyfilePath) {
this.keyfilePath = keyfilePath;
}
}

View File

@@ -1,96 +1,401 @@
package com.bwssystems.HABridge;
public class BridgeSettings {
private String upnpconfigaddress;
private String serverport;
private String upnpresponseport;
private String upnpdevicedb;
private String veraaddress;
private String harmonyaddress;
private String harmonyuser;
private String harmonypwd;
private boolean upnpstrict;
private boolean traceupnp;
private boolean devmode;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.PosixFilePermission;
import java.security.GeneralSecurityException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import org.apache.http.conn.util.InetAddressUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.util.BackupHandler;
import com.bwssystems.HABridge.util.JsonTransformer;
import com.bwssystems.HABridge.util.ParseRoute;
import com.google.gson.Gson;
public class BridgeSettings extends BackupHandler {
private static final Logger log = LoggerFactory.getLogger(BridgeSettings.class);
private BridgeSettingsDescriptor theBridgeSettings;
private BridgeControlDescriptor bridgeControl;
private BridgeSecurity bridgeSecurity;
private static final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss");
public String getUpnpConfigAddress() {
return upnpconfigaddress;
public BridgeSettings() {
super();
bridgeControl = new BridgeControlDescriptor();
theBridgeSettings = new BridgeSettingsDescriptor();
bridgeSecurity = null;
String theKey = System.getProperty("security.key");
if(theKey == null)
theKey = "IWantMyPasswordsToBeAbleToBeDecodedPleaseSeeTheReadme";
String execGarden = System.getProperty("exec.garden");
bridgeSecurity = new BridgeSecurity(theKey.toCharArray(), execGarden);
String ipV6Stack = System.getProperty("ipV6Stack");
if(ipV6Stack == null || !ipV6Stack.equalsIgnoreCase("true")) {
System.setProperty("java.net.preferIPv4Stack" , "true");
}
}
public void setUpnpConfigAddress(String upnpConfigAddress) {
this.upnpconfigaddress = upnpConfigAddress;
public BridgeControlDescriptor getBridgeControl() {
return bridgeControl;
}
public String getServerPort() {
return serverport;
public BridgeSettingsDescriptor getBridgeSettingsDescriptor() {
return theBridgeSettings;
}
public void setServerPort(String serverPort) {
this.serverport = serverPort;
public BridgeSecurity getBridgeSecurity() {
return bridgeSecurity;
}
public String getUpnpResponsePort() {
return upnpresponseport;
public String getCurrentDate() {
return LocalDateTime.now().format(dateTimeFormat);
}
public void setUpnpResponsePort(String upnpResponsePort) {
this.upnpresponseport = upnpResponsePort;
public void buildSettings() {
String addressString = null;
String theVeraAddress = null;
String theFibaroAddress = null;
String theSomfyAddress = null;
String theHarmonyAddress = null;
String configFileProperty = System.getProperty("config.file");
if(configFileProperty == null) {
Path filePath = Paths.get(Configuration.CONFIG_FILE);
if(Files.exists(filePath) && Files.isReadable(filePath))
configFileProperty = Configuration.CONFIG_FILE;
}
String serverPortOverride = System.getProperty("server.port");
String serverIpOverride = System.getProperty("server.ip");
if(configFileProperty != null)
{
log.info("reading from config file: " + configFileProperty);
theBridgeSettings.setConfigfile(configFileProperty);
_loadConfig();
}
else
{
log.info("reading from system properties");
theBridgeSettings.setNumberoflogmessages(Configuration.NUMBER_OF_LOG_MESSAGES);
theBridgeSettings.setFarenheit(true);
theBridgeSettings.setConfigfile(Configuration.CONFIG_FILE);
theBridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DEFAULT_WEB_PORT));
theBridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address"));
theBridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db"));
theBridgeSettings.setUpnpGroupDb(System.getProperty("upnp.group.db"));
theBridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT));
theVeraAddress = System.getProperty("vera.address");
IpList theVeraList = null;
if(theVeraAddress != null) {
try {
theVeraList = new Gson().fromJson(theVeraAddress, IpList.class);
} catch (Exception e) {
try {
theVeraList = new Gson().fromJson("{devices:[{name:default,ip:" + theVeraAddress + "}]}", IpList.class);
} catch (Exception et) {
log.error("Cannot parse vera.address, not set with message: " + e.getMessage(), e);
theVeraList = null;
}
}
}
theBridgeSettings.setVeraAddress(theVeraList);
theFibaroAddress = System.getProperty("fibaro.address");
IpList theFibaroList = null;
if(theFibaroAddress != null) {
try {
theFibaroList = new Gson().fromJson(theFibaroAddress, IpList.class);
} catch (Exception e) {
try {
theFibaroList = new Gson().fromJson("{devices:[{name:default,ip:" + theFibaroAddress + "}]}", IpList.class);
} catch (Exception et) {
log.error("Cannot parse fibaro.address, not set with message: " + e.getMessage(), e);
theFibaroList = null;
}
}
}
theBridgeSettings.setFibaroAddress(theFibaroList);
theHarmonyAddress = System.getProperty("harmony.address");
IpList theHarmonyList = null;
if(theHarmonyAddress != null) {
try {
theHarmonyList = new Gson().fromJson(theHarmonyAddress, IpList.class);
} catch (Exception e) {
try {
theHarmonyList = new Gson().fromJson("{devices:[{name:default,ip:" + theHarmonyAddress + "}]}", IpList.class);
} catch (Exception et) {
log.error("Cannot parse harmony.address, not set with message: " + e.getMessage(), e);
theHarmonyList = null;
}
}
}
theBridgeSettings.setHarmonyAddress(theHarmonyList);
theSomfyAddress = System.getProperty("somfy.address");
IpList theSomfyList = null;
if(theSomfyAddress != null) {
try {
theSomfyList = new Gson().fromJson(theSomfyAddress, IpList.class);
} catch (Exception e) {
try {
theSomfyList = new Gson().fromJson("{devices:[{name:default,ip:" + theSomfyAddress + "}]}", IpList.class);
} catch (Exception et) {
log.error("Cannot parse somfy.address, not set with message: " + e.getMessage(), e);
theSomfyList = null;
}
}
}
theBridgeSettings.setSomfyAddress(theSomfyList);
// theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
theBridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
theBridgeSettings.setButtonsleep(Integer.parseInt(System.getProperty("button.sleep", Configuration.DEFAULT_BUTTON_SLEEP)));
theBridgeSettings.setNestuser(System.getProperty("nest.user"));
theBridgeSettings.setNestpwd(System.getProperty("nest.pwd"));
}
ParseRoute aDefaultRoute = ParseRoute.getInstance();
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().trim().equals("") || theBridgeSettings.getUpnpConfigAddress().trim().equals("0.0.0.0")) {
addressString = aDefaultRoute.getLocalIPAddress();
if(addressString != null) {
theBridgeSettings.setUpnpConfigAddress(addressString);
log.info("Adding " + addressString + " as our default upnp config address.");
}
else
log.error("Cannot get ip address of this host.");
}
else {
addressString = checkIpAddress(theBridgeSettings.getUpnpConfigAddress(), false);
if(addressString == null) {
addressString = aDefaultRoute.getLocalIPAddress();
log.warn("The upnp config address, " + theBridgeSettings.getUpnpConfigAddress() + ", does not match any known IP's on this host. Using default address: " + addressString);
}
}
if(theBridgeSettings.getUpnpResponsePort() == null)
theBridgeSettings.setUpnpResponsePort(Configuration.UPNP_RESPONSE_PORT);
if(theBridgeSettings.getServerPort() == null)
theBridgeSettings.setServerPort(Configuration.DEFAULT_WEB_PORT);
if(theBridgeSettings.getUpnpDeviceDb() == null)
theBridgeSettings.setUpnpDeviceDb(Configuration.DEVICE_DB_DIRECTORY);
if(theBridgeSettings.getUpnpGroupDb() == null)
theBridgeSettings.setUpnpGroupDb(Configuration.GROUP_DB_DIRECTORY);
if(theBridgeSettings.getNumberoflogmessages() == null || theBridgeSettings.getNumberoflogmessages() <= 0)
theBridgeSettings.setNumberoflogmessages(Integer.valueOf(Configuration.NUMBER_OF_LOG_MESSAGES));
if(theBridgeSettings.getButtonsleep() == null || theBridgeSettings.getButtonsleep() < 0)
theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP));
if(theBridgeSettings.getLinkbuttontimeout() < 30)
theBridgeSettings.setLinkbuttontimeout(Configuration.LINK_BUTTON_TIMEOUT);
theBridgeSettings.setVeraconfigured(theBridgeSettings.isValidVera());
theBridgeSettings.setFibaroconfigured(theBridgeSettings.isValidFibaro());
theBridgeSettings.setHarmonyconfigured(theBridgeSettings.isValidHarmony());
theBridgeSettings.setNestConfigured(theBridgeSettings.isValidNest());
theBridgeSettings.setHueconfigured(theBridgeSettings.isValidHue());
theBridgeSettings.setHalconfigured(theBridgeSettings.isValidHal());
theBridgeSettings.setMqttconfigured(theBridgeSettings.isValidMQTT());
theBridgeSettings.setHassconfigured(theBridgeSettings.isValidHass());
theBridgeSettings.setDomoticzconfigured(theBridgeSettings.isValidDomoticz());
theBridgeSettings.setSomfyconfigured(theBridgeSettings.isValidSomfy());
theBridgeSettings.setHomeWizardConfigured(theBridgeSettings.isValidHomeWizard());
theBridgeSettings.setOpenhabconfigured(theBridgeSettings.isValidOpenhab());
theBridgeSettings.setFhemconfigured(theBridgeSettings.isValidFhem());
theBridgeSettings.setMoziotconfigured(theBridgeSettings.isValidMozIot());
theBridgeSettings.setHomegenieconfigured(theBridgeSettings.isValidHomeGenie());
// Lifx is either configured or not, so it does not need an update.
if(serverPortOverride != null)
theBridgeSettings.setServerPort(serverPortOverride);
if(serverIpOverride != null) {
theBridgeSettings.setWebaddress(serverIpOverride);
theBridgeSettings.setUpnpConfigAddress(serverIpOverride);
}
setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-");
bridgeSecurity.setSecurityData(theBridgeSettings.getSecurityData());
if(theBridgeSettings.getWhitelist() != null) {
bridgeSecurity.convertWhitelist(theBridgeSettings.getWhitelist());
theBridgeSettings.removeWhitelist();
updateConfigFile();
}
}
public String getUpnpDeviceDb() {
return upnpdevicedb;
public void loadConfig() {
if(theBridgeSettings.getConfigfile() != null)
_loadConfig();
}
public void setUpnpDeviceDb(String upnpDeviceDb) {
this.upnpdevicedb = upnpDeviceDb;
private void _loadConfig() {
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
_loadConfig(configPath);
}
private void _loadConfig(Path aPath) {
String jsonContent = configReader(aPath);
if(jsonContent == null)
return;
try {
theBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettingsDescriptor.class);
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();
}
}
public void save(BridgeSettingsDescriptor newBridgeSettings) {
log.debug("Save HA Bridge settings.");
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
JsonTransformer aRenderer = new JsonTransformer();
if(bridgeSecurity.isSettingsChanged()) {
try {
newBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData());
} catch (UnsupportedEncodingException e) {
log.warn("could not get encoded security data: " + e.getMessage());
return;
} catch (GeneralSecurityException e) {
log.warn("could not get encoded security data: " + e.getMessage());
return;
}
bridgeSecurity.setSettingsChanged(false);
}
String jsonValue = aRenderer.render(newBridgeSettings);
configWriter(jsonValue, configPath);
_loadConfig(configPath);
}
public void updateConfigFile() {
log.debug("Save HA Bridge settings.");
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
JsonTransformer aRenderer = new JsonTransformer();
if(bridgeSecurity.isSettingsChanged()) {
try {
theBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData());
} catch (UnsupportedEncodingException e) {
log.warn("could not get encoded security data: " + e.getMessage());
return;
} catch (GeneralSecurityException e) {
log.warn("could not get encoded security data: " + e.getMessage());
return;
}
bridgeSecurity.setSettingsChanged(false);
}
String jsonValue = aRenderer.render(theBridgeSettings);
configWriter(jsonValue, configPath);
_loadConfig(configPath);
}
private synchronized void configWriter(String content, Path filePath) {
if(Files.exists(filePath) && !Files.isWritable(filePath)){
log.error("Error file is not writable: " + filePath);
return;
}
if(Files.notExists(filePath.getParent())) {
try {
Files.createDirectories(filePath.getParent());
} catch (IOException e) {
log.error("Error creating the directory: " + filePath + " message: " + e.getMessage(), e);
}
}
try {
Path target = null;
if(Files.exists(filePath)) {
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "habridge.config.old." + getCurrentDate());
Files.move(filePath, target);
}
Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE);
// set attributes to be for user only
// using PosixFilePermission to set file permissions
Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
// add owners permission
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
try {
String osName = System.getProperty("os.name");
if(osName.toLowerCase().indexOf("win") < 0)
Files.setPosixFilePermissions(filePath, perms);
} catch(UnsupportedOperationException e) {
log.info("Cannot set permissions for config file on this system as it is not supported. Continuing");
}
if(target != null)
Files.delete(target);
} catch (IOException e) {
log.error("Error writing the file: " + filePath + " message: " + e.getMessage(), e);
}
}
public String getVeraAddress() {
return veraaddress;
private String configReader(Path filePath) {
String content = null;
if(Files.notExists(filePath) || !Files.isReadable(filePath)){
log.warn("Error reading the file: " + filePath + " - Does not exist or is not readable. continuing...");
return null;
}
try {
content = new String(Files.readAllBytes(filePath));
} catch (IOException e) {
log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e);
}
return content;
}
public void setVeraAddress(String veraAddress) {
this.veraaddress = veraAddress;
}
public String getHarmonyAddress() {
return harmonyaddress;
}
public void setHarmonyAddress(String harmonyaddress) {
this.harmonyaddress = harmonyaddress;
}
public String getHarmonyUser() {
return harmonyuser;
}
public void setHarmonyUser(String harmonyuser) {
this.harmonyuser = harmonyuser;
}
public String getHarmonyPwd() {
return harmonypwd;
}
public void setHarmonyPwd(String harmonypwd) {
this.harmonypwd = harmonypwd;
}
public boolean isUpnpStrict() {
return upnpstrict;
}
public void setUpnpStrict(boolean upnpStrict) {
this.upnpstrict = upnpStrict;
}
public boolean isTraceupnp() {
return traceupnp;
}
public void setTraceupnp(boolean traceupnp) {
this.traceupnp = traceupnp;
}
public boolean isDevMode() {
return devmode;
}
public void setDevMode(boolean devmode) {
this.devmode = devmode;
}
public Boolean isValidVera() {
if(this.veraaddress.contains(Configuration.DEFAULT_VERA_ADDRESS))
return false;
return true;
}
public Boolean isValidHarmony() {
if(this.harmonyaddress.contains(Configuration.DEFAULT_HARMONY_ADDRESS))
return false;
if(this.harmonypwd == null || this.harmonypwd == "")
return false;
if(this.harmonyuser == null || this.harmonyuser == "")
return false;
return true;
private String checkIpAddress(String ipAddress, boolean checkForLocalhost) {
Enumeration<NetworkInterface> ifs = null;
try {
ifs = NetworkInterface.getNetworkInterfaces();
} catch(SocketException e) {
log.error("checkIpAddress cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
return null;
}
String addressString = null;
InetAddress address = null;
while (ifs.hasMoreElements() && addressString == null) {
NetworkInterface xface = ifs.nextElement();
Enumeration<InetAddress> addrs = xface.getInetAddresses();
String name = xface.getName();
int IPsPerNic = 0;
while (addrs.hasMoreElements() && IPsPerNic == 0) {
address = addrs.nextElement();
if (InetAddressUtils.isIPv4Address(address.getHostAddress())) {
log.debug(name + " ... has IPV4 addr " + address);
if(checkForLocalhost && (!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE) || !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS))) {
IPsPerNic++;
addressString = address.getHostAddress();
log.debug("checkIpAddress found " + addressString + " from interface " + name);
}
else if(ipAddress != null && ipAddress.equalsIgnoreCase(address.getHostAddress())){
addressString = ipAddress;
IPsPerNic++;
}
}
}
}
return addressString;
}
}

View File

@@ -0,0 +1,888 @@
package com.bwssystems.HABridge;
import java.util.List;
import java.util.Map;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
//import com.bwssystems.HABridge.api.NameValue;
import com.bwssystems.HABridge.api.hue.HueConstants;
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
public class BridgeSettingsDescriptor {
@SerializedName("upnpconfigaddress")
@Expose
private String upnpconfigaddress;
@SerializedName("useupnpiface")
@Expose
private boolean useupnpiface;
@SerializedName("userooms")
@Expose
private boolean userooms;
@SerializedName("serverport")
@Expose
private Integer serverport;
@SerializedName("upnpresponseport")
@Expose
private Integer upnpresponseport;
@SerializedName("upnpdevicedb")
@Expose
private String upnpdevicedb;
@SerializedName("upnpgroupdb")
@Expose
private String upnpgroupdb;
@SerializedName("veraaddress")
@Expose
private IpList veraaddress;
@SerializedName("fibaroaddress")
@Expose
private IpList fibaroaddress;
@SerializedName("harmonyaddress")
@Expose
private IpList harmonyaddress;
@SerializedName("buttonsleep")
@Expose
private Integer buttonsleep;
@SerializedName("traceupnp")
@Expose
private boolean traceupnp;
@SerializedName("nestuser")
@Expose
private String nestuser;
@SerializedName("nestpwd")
@Expose
private String nestpwd;
@SerializedName("farenheit")
@Expose
private boolean farenheit;
@SerializedName("configfile")
@Expose
private String configfile;
@SerializedName("numberoflogmessages")
@Expose
private Integer numberoflogmessages;
@SerializedName("hueaddress")
@Expose
private IpList hueaddress;
@SerializedName("haladdress")
@Expose
private IpList haladdress;
@SerializedName("whitelist")
@Expose
private Map<String, WhitelistEntry> whitelist;
@SerializedName("myechourl")
@Expose
private String myechourl;
@SerializedName("webaddress")
@Expose
private String webaddress;
@SerializedName("mqttaddress")
@Expose
private IpList mqttaddress;
@SerializedName("hassaddress")
@Expose
private IpList hassaddress;
@SerializedName("domoticzaddress")
@Expose
private IpList domoticzaddress;
@SerializedName("somfyaddress")
@Expose
private IpList somfyaddress;
@SerializedName("openhabaddress")
@Expose
private IpList openhabaddress;
@SerializedName("moziotaddress")
@Expose
private IpList moziotaddress;
@SerializedName("hubversion")
@Expose
private String hubversion;
@SerializedName("hubmac")
@Expose
private String hubmac;
@SerializedName("securityData")
@Expose
private String securityData;
@SerializedName("homewizardaddress")
@Expose
private IpList homewizardaddress;
@SerializedName("upnpsenddelay")
@Expose
private Integer upnpsenddelay;
@SerializedName("fhemaddress")
@Expose
private IpList fhemaddress;
@SerializedName("homegenieaddress")
@Expose
private IpList homegenieaddress;
@SerializedName("lifxconfigured")
@Expose
private boolean lifxconfigured;
@SerializedName("broadlinkconfigured")
@Expose
private boolean broadlinkconfigured;
@SerializedName("tracestate")
@Expose
private boolean tracestate;
@SerializedName("upnporiginal")
@Expose
private boolean upnporiginal;
@SerializedName("seedid")
@Expose
private Integer seedid;
@SerializedName("haaddressessecured")
@Expose
private boolean haaddressessecured;
@SerializedName("upnpadvanced")
@Expose
private boolean upnpadvanced;
@SerializedName("linkbuttontimeout")
@Expose
private Integer linkbuttontimeout;
@SerializedName("uidnineoctets")
@Expose
private boolean uidnineoctets;
// @SerializedName("activeloggers")
// @Expose
// private List<NameValue> activeloggers;
private boolean settingsChanged;
private boolean veraconfigured;
private boolean fibaroconfigured;
private boolean harmonyconfigured;
private boolean hueconfigured;
private boolean nestconfigured;
private boolean halconfigured;
private boolean mqttconfigured;
private boolean hassconfigured;
private boolean domoticzconfigured;
private boolean somfyconfigured;
private boolean homewizardconfigured;
private boolean openhabconfigured;
private boolean fhemconfigured;
private boolean moziotconfigured;
private boolean homegenieconfigured;
// Deprecated settings
private String haltoken;
// private boolean upnpstrict;
public BridgeSettingsDescriptor() {
super();
// this.upnpstrict = true;
this.useupnpiface = false;
this.userooms = false;
this.traceupnp = false;
this.nestconfigured = false;
this.veraconfigured = false;
this.fibaroconfigured = false;
this.somfyconfigured = false;
this.harmonyconfigured = false;
this.hueconfigured = false;
this.halconfigured = false;
this.mqttconfigured = false;
this.hassconfigured = false;
this.domoticzconfigured = false;
this.homewizardconfigured = false;
this.lifxconfigured = false;
this.openhabconfigured = false;
this.moziotconfigured = false;
this.homegenieconfigured = false;
this.farenheit = true;
this.securityData = null;
this.settingsChanged = false;
this.myechourl = "alexa.amazon.com/spa/index.html#cards";
this.webaddress = "0.0.0.0";
this.hubversion = HueConstants.HUB_VERSION;
this.hubmac = null;
// this.activeloggers = null;
this.upnpsenddelay = Configuration.UPNP_SEND_DELAY;
this.broadlinkconfigured = false;
this.tracestate = false;
this.upnporiginal = false;
this.seedid = 100;
this.haaddressessecured = false;
this.configfile = Configuration.CONFIG_FILE;
this.upnpadvanced = false;
this.linkbuttontimeout = Configuration.LINK_BUTTON_TIMEOUT;
this.uidnineoctets = false;
}
public String getUpnpConfigAddress() {
return upnpconfigaddress;
}
public void setUpnpConfigAddress(String upnpConfigAddress) {
this.upnpconfigaddress = upnpConfigAddress;
}
public boolean isUseupnpiface() {
return useupnpiface;
}
public void setUseupnpiface(boolean useupnpiface) {
this.useupnpiface = useupnpiface;
}
public boolean isUserooms() {
return userooms;
}
public void setUserooms(boolean userooms) {
this.userooms = userooms;
}
public Integer getServerPort() {
return serverport;
}
public void setServerPort(Integer serverPort) {
this.serverport = serverPort;
}
public void setServerPort(String serverPort) {
this.serverport = Integer.valueOf(serverPort);
}
public Integer getUpnpResponsePort() {
return upnpresponseport;
}
public void setUpnpResponsePort(Integer upnpResponsePort) {
this.upnpresponseport = upnpResponsePort;
}
public void setUpnpResponsePort(String upnpResponsePort) {
this.upnpresponseport = Integer.valueOf(upnpResponsePort);
}
public String getUpnpDeviceDb() {
return upnpdevicedb;
}
public void setUpnpDeviceDb(String upnpDeviceDb) {
this.upnpdevicedb = upnpDeviceDb;
}
public String getUpnpGroupDb() {
return upnpgroupdb;
}
public void setUpnpGroupDb(String upnpGroupDb) {
this.upnpgroupdb = upnpGroupDb;
}
public IpList getVeraAddress() {
return veraaddress;
}
public IpList getFibaroAddress() {
return fibaroaddress;
}
public IpList getSomfyAddress() {
return somfyaddress;
}
public IpList getHomeWizardAddress() {
return homewizardaddress;
}
public void setVeraAddress(IpList veraAddress) {
this.veraaddress = veraAddress;
}
public void setFibaroAddress(IpList fibaroAddress) {
this.fibaroaddress = fibaroAddress;
}
public void setSomfyAddress(IpList somfyAddress) {
this.somfyaddress = somfyAddress;
}
public void setHomeWizardAddress(IpList homewizardaddress) {
this.homewizardaddress = homewizardaddress;
}
public IpList getHarmonyAddress() {
return harmonyaddress;
}
public void setHarmonyAddress(IpList harmonyaddress) {
this.harmonyaddress = harmonyaddress;
}
/*
public boolean isUpnpStrict() {
return upnpstrict;
}
public void setUpnpStrict(boolean upnpStrict) {
this.upnpstrict = upnpStrict;
}
*/
public boolean isTraceupnp() {
return traceupnp;
}
public void setTraceupnp(boolean traceupnp) {
this.traceupnp = traceupnp;
}
public String getNestuser() {
return nestuser;
}
public void setNestuser(String nestuser) {
this.nestuser = nestuser;
}
public String getNestpwd() {
return nestpwd;
}
public void setNestpwd(String nestpwd) {
this.nestpwd = nestpwd;
}
public boolean isVeraconfigured() {
return veraconfigured;
}
public boolean isFibaroconfigured() {
return fibaroconfigured;
}
public boolean isSomfyconfigured() {
return somfyconfigured;
}
public boolean isHomeWizardConfigured() {
return homewizardconfigured;
}
public void setVeraconfigured(boolean veraconfigured) {
this.veraconfigured = veraconfigured;
}
public void setFibaroconfigured(boolean fibaroconfigured) {
this.fibaroconfigured = fibaroconfigured;
}
public void setSomfyconfigured(boolean somfyconfigured) {
this.somfyconfigured = somfyconfigured;
}
public void setHomeWizardConfigured(boolean homewizardconfigured) {
this.homewizardconfigured = homewizardconfigured;
}
public boolean isHarmonyconfigured() {
return harmonyconfigured;
}
public void setHarmonyconfigured(boolean harmonyconfigured) {
this.harmonyconfigured = harmonyconfigured;
}
public boolean isNestConfigured() {
return nestconfigured;
}
public void setNestConfigured(boolean isNestConfigured) {
this.nestconfigured = isNestConfigured;
}
public Integer getButtonsleep() {
return buttonsleep;
}
public void setButtonsleep(Integer buttonsleep) {
this.buttonsleep = buttonsleep;
}
public String getConfigfile() {
return configfile;
}
public void setConfigfile(String configfile) {
this.configfile = configfile;
}
public Integer getNumberoflogmessages() {
return numberoflogmessages;
}
public void setNumberoflogmessages(Integer numberoflogmessages) {
this.numberoflogmessages = numberoflogmessages;
}
public boolean isFarenheit() {
return farenheit;
}
public void setFarenheit(boolean farenheit) {
this.farenheit = farenheit;
}
public IpList getHueaddress() {
return hueaddress;
}
public void setHueaddress(IpList hueaddress) {
this.hueaddress = hueaddress;
}
public boolean isHueconfigured() {
return hueconfigured;
}
public void setHueconfigured(boolean hueconfigured) {
this.hueconfigured = hueconfigured;
}
public IpList getHaladdress() {
return haladdress;
}
public void setHaladdress(IpList haladdress) {
this.haladdress = haladdress;
}
public String getHaltoken() {
return haltoken;
}
public void setHaltoken(String haltoken) {
this.haltoken = haltoken;
}
public boolean isHalconfigured() {
return halconfigured;
}
public void setHalconfigured(boolean halconfigured) {
this.halconfigured = halconfigured;
}
public Map<String, WhitelistEntry> getWhitelist() {
return whitelist;
}
protected void removeWhitelist() {
whitelist = null;
}
public boolean isSettingsChanged() {
return settingsChanged;
}
public void setSettingsChanged(boolean settingsChanged) {
this.settingsChanged = settingsChanged;
}
public String getMyechourl() {
return myechourl;
}
public void setMyechourl(String myechourl) {
this.myechourl = myechourl;
}
public String getWebaddress() {
return webaddress;
}
public void setWebaddress(String webaddress) {
this.webaddress = webaddress;
}
public IpList getMqttaddress() {
return mqttaddress;
}
public void setMqttaddress(IpList mqttaddress) {
this.mqttaddress = mqttaddress;
}
public boolean isMqttconfigured() {
return mqttconfigured;
}
public void setMqttconfigured(boolean mqttconfigured) {
this.mqttconfigured = mqttconfigured;
}
public IpList getHassaddress() {
return hassaddress;
}
public void setHassaddress(IpList hassaddress) {
this.hassaddress = hassaddress;
}
public boolean isHassconfigured() {
return hassconfigured;
}
public void setHassconfigured(boolean hassconfigured) {
this.hassconfigured = hassconfigured;
}
public IpList getOpenhabaddress() {
return openhabaddress;
}
public void setOpenhabaddress(IpList openhabaddress) {
this.openhabaddress = openhabaddress;
}
public boolean isOpenhabconfigured() {
return openhabconfigured;
}
public void setOpenhabconfigured(boolean openhabconfigured) {
this.openhabconfigured = openhabconfigured;
}
public String getHubversion() {
return hubversion;
}
public void setHubversion(String hubversion) {
this.hubversion = hubversion;
}
public String getHubmac() {
return hubmac;
}
public void setHubmac(String hubmac) {
this.hubmac = hubmac;
}
public IpList getDomoticzaddress() {
return domoticzaddress;
}
public void setDomoticzaddress(IpList domoticzaddress) {
this.domoticzaddress = domoticzaddress;
}
public boolean isDomoticzconfigured() {
return domoticzconfigured;
}
public void setDomoticzconfigured(boolean domoticzconfigured) {
this.domoticzconfigured = domoticzconfigured;
}
public boolean isLifxconfigured() {
return lifxconfigured;
}
public void setLifxconfigured(boolean lifxconfigured) {
this.lifxconfigured = lifxconfigured;
}
public String getSecurityData() {
return securityData;
}
public void setSecurityData(String securityData) {
this.securityData = securityData;
}
public Integer getUpnpsenddelay() {
return upnpsenddelay;
}
public void setUpnpsenddelay(Integer upnpsenddelay) {
this.upnpsenddelay = upnpsenddelay;
}
public IpList getFhemaddress() {
return fhemaddress;
}
public void setFhemaddress(IpList fhemaddress) {
this.fhemaddress = fhemaddress;
}
public boolean isFhemconfigured() {
return fhemconfigured;
}
public void setFhemconfigured(boolean fhemconfigured) {
this.fhemconfigured = fhemconfigured;
}
// public List<NameValue> getActiveloggers() {
// return activeloggers;
// }
// public void setActiveloggers(List<NameValue> activeloggers) {
// this.activeloggers = activeloggers;
// }
public boolean isBroadlinkconfigured() {
return broadlinkconfigured;
}
public void setBroadlinkconfigured(boolean broadlinkconfigured) {
this.broadlinkconfigured = broadlinkconfigured;
}
public Boolean isValidVera() {
if (this.getVeraAddress() == null || this.getVeraAddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getVeraAddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidFibaro() {
if (this.getFibaroAddress() == null || this.getFibaroAddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getFibaroAddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidHarmony() {
if (this.getHarmonyAddress() == null || this.getHarmonyAddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getHarmonyAddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidNest() {
if (this.getNestpwd() == null || this.getNestpwd().equals(""))
return false;
if (this.getNestuser() == null || this.getNestuser().equals(""))
return false;
return true;
}
public Boolean isValidHue() {
if (this.getHueaddress() == null || this.getHueaddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getHueaddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidHal() {
if (this.getHaladdress() == null || this.getHaladdress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getHaladdress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
if (devicesList.get(0).getPassword() == null || devicesList.get(0).getPassword().trim().isEmpty()) {
if (this.getHaltoken() == null || this.getHaltoken().equals(""))
return false;
}
return true;
}
public Boolean isValidMQTT() {
if (this.getMqttaddress() == null || this.getMqttaddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getMqttaddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidHass() {
if (this.getHassaddress() == null || this.getHassaddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getHassaddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidDomoticz() {
if (this.getDomoticzaddress() == null || this.getDomoticzaddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getDomoticzaddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidSomfy() {
if (this.getSomfyAddress() == null || this.getSomfyAddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getSomfyAddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidLifx() {
return this.isLifxconfigured();
}
public void updateHue(NamedIP aHue) {
int indexHue = -1;
for (int i = 0; i < hueaddress.getDevices().size(); i++) {
if (hueaddress.getDevices().get(i).getName().equals(aHue.getName()))
indexHue = i;
}
if (indexHue >= 0) {
hueaddress.getDevices().set(indexHue, aHue);
this.setSettingsChanged(true);
}
}
public Boolean isValidHomeWizard() {
if (this.getHomeWizardAddress() == null || this.getHomeWizardAddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getHomeWizardAddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidOpenhab() {
if (this.getOpenhabaddress() == null || this.getOpenhabaddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getOpenhabaddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidFhem() {
if (this.getFhemaddress() == null || this.getFhemaddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getFhemaddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidBroadlink() {
return this.isBroadlinkconfigured();
}
public boolean isTracestate() {
return tracestate;
}
public void setTracestate(boolean tracestate) {
this.tracestate = tracestate;
}
public IpList getMoziotaddress() {
return moziotaddress;
}
public void setMoziotgaddress(IpList moziotgateway) {
this.moziotaddress = moziotgateway;
}
public Boolean isValidMozIot() {
if (this.getMoziotaddress() == null || this.getMoziotaddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getMoziotaddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public boolean isMoziotconfigured() {
return moziotconfigured;
}
public void setMoziotconfigured(boolean moziotconfigured) {
this.moziotconfigured = moziotconfigured;
}
public boolean isUpnporiginal() {
return upnporiginal;
}
public void setUpnporiginal(boolean upnporiginal) {
this.upnporiginal = upnporiginal;
}
public Integer getSeedid() {
return seedid;
}
public void setSeedid(Integer seedid) {
this.seedid = seedid;
}
public IpList getHomegenieaddress() {
return homegenieaddress;
}
public void setHomegenieaddress(IpList homegenieaddress) {
this.homegenieaddress = homegenieaddress;
}
public boolean isHomegenieconfigured() {
return homegenieconfigured;
}
public void setHomegenieconfigured(boolean homegenieconfigured) {
this.homegenieconfigured = homegenieconfigured;
}
public Boolean isValidHomeGenie() {
if (this.getHomegenieaddress() == null || this.getHomegenieaddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getHomegenieaddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public boolean isHaaddressessecured() {
return haaddressessecured;
}
public void setHaaddressessecured(boolean haaddressessecured) {
this.haaddressessecured = haaddressessecured;
}
public boolean isUpnpadvanced() {
return upnpadvanced;
}
public void setUpnpadvanced(boolean upnpadvanced) {
this.upnpadvanced = upnpadvanced;
}
public Integer getLinkbuttontimeout() {
return linkbuttontimeout;
}
public void setLinkbuttontimeout(Integer linkbuttontimeout) {
this.linkbuttontimeout = linkbuttontimeout;
}
public boolean isUidnineoctets() {
return uidnineoctets;
}
public void setUidnineoctets(boolean uidnineoctets) {
this.uidnineoctets = uidnineoctets;
}
}

View File

@@ -2,10 +2,20 @@ package com.bwssystems.HABridge;
public class Configuration {
public final static String DEVICE_DB_DIRECTORY = "data/device.db";
public final static String GROUP_DB_DIRECTORY = "data/group.db";
public final static String UPNP_RESPONSE_PORT = "50000";
public final static String DEFAULT_VERA_ADDRESS = "1.1.1.1";
public final static String DEFAULT_HARMONY_ADDRESS = "1.1.1.1";
public final static String DEFAULT_HARMONY_USER = "";
public final static String DEFAULT_HARMONY_PWD = "";
public final static String DFAULT_WEB_PORT = "8080";
public final static String DEFAULT_ADDRESS = "1.1.1.1";
public final static String LOOP_BACK_ADDRESS = "127.0.0.1";
public final static String LOOP_BACK_INTERFACE = "lo";
public final static String DEFAULT_WEB_PORT = "80";
public final static String DEFAULT_BUTTON_SLEEP = "100";
public static final int UPNP_DISCOVERY_PORT = 1900;
public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250";
public static final String CONFIG_FILE = "data/habridge.config";
public static final int NUMBER_OF_LOG_MESSAGES = 512;
public static final long UPNP_NOTIFY_TIMEOUT = 20000;
public static final int UPNP_SEND_DELAY = 650;
public static final int BROADLINK_DISCOVER_PORT = 40000;
public static final int BROADLINK_DISCONVER_TIMEOUT = 5000;
public static final int LINK_BUTTON_TIMEOUT = 45;
}

View File

@@ -0,0 +1,102 @@
package com.bwssystems.HABridge;
import java.util.ArrayList;
public class DeviceMapTypes {
public final static String[] CUSTOM_DEVICE = { "custom", "Custom"};
public final static String[] VERA_DEVICE = { "veraDevice", "Vera Device"};
public final static String[] VERA_SCENE = { "veraScene", "Vera Scene"};
public final static String[] FIBARO_DEVICE = { "fibaroDevice", "Fibaro Device"};
public final static String[] FIBARO_SCENE = { "fibaroScene", "Fibaro Scene"};
public final static String[] HARMONY_ACTIVITY = { "harmonyActivity", "Harmony Activity"};
public final static String[] HARMONY_BUTTON = { "harmonyButton", "Harmony Button"};
public final static String[] NEST_HOMEAWAY = { "nestHomeAway", "Nest Home Status"};
public final static String[] NEST_THERMO_SET = { "nestThermoSet", "Nest Thermostat"};
public final static String[] HUE_DEVICE = { "hueDevice", "Hue Device"};
public final static String[] HAL_DEVICE = { "halDevice", "HAL Device"};
public final static String[] HAL_BUTTON = { "halButton", "HAL Button"};
public final static String[] HAL_HOME = { "halHome", "HAL Home Status"};
public final static String[] HAL_THERMO_SET = { "halThermoSet", "HAL Thermostat"};
public final static String[] MQTT_MESSAGE = { "mqttMessage", "MQTT Message"};
public final static String[] EXEC_DEVICE_COMPAT = { "exec", "Execute Script/Program"};
public final static String[] CMD_DEVICE = { "cmdDevice", "Execute Command/Script/Program"};
public final static String[] HASS_DEVICE = { "hassDevice", "HomeAssistant Device"};
public final static String[] HOMEWIZARD_DEVICE = { "homewizardDevice", "HomeWizard Device"};
public final static String[] TCP_DEVICE = { "tcpDevice", "TCP Device"};
public final static String[] TCP_DEVICE_COMPAT = { "TCP", "TCP Device"};
public final static String[] UDP_DEVICE = { "udpDevice", "UDP Device"};
public final static String[] UDP_DEVICE_COMPAT = { "UDP", "UDP Device"};
public final static String[] HTTP_DEVICE = { "httpDevice", "HTTP Device"};
public final static String[] DOMOTICZ_DEVICE = { "domoticzDevice", "Domoticz Device"};
public final static String[] SOMFY_DEVICE = { "somfyDevice", "Somfy Device"};
public final static String[] LIFX_DEVICE = { "lifxDevice", "LIFX Device"};
public final static String[] OPENHAB_DEVICE = { "openhabDevice", "OpenHAB Device"};
public final static String[] FHEM_DEVICE = { "fhemDevice", "FHEM Device"};
public final static String[] BROADLINK_DEVICE = { "broadlinkDevice", "Broadlink Device"};
public final static String[] MOZIOT_DEVICE = { "moziotDevice", "Mozilla IOT Device"};
public final static String[] HOMEGENIE_DEVICE = { "homegenieDevice", "HomeGenie Device"};
public final static int typeIndex = 0;
public final static int displayIndex = 1;
ArrayList<String[]> deviceMapTypes;
public DeviceMapTypes() {
super();
deviceMapTypes = new ArrayList<String[]>();
deviceMapTypes.add(CMD_DEVICE);
deviceMapTypes.add(DOMOTICZ_DEVICE);
deviceMapTypes.add(HAL_DEVICE);
deviceMapTypes.add(HAL_HOME);
deviceMapTypes.add(HAL_THERMO_SET);
deviceMapTypes.add(HAL_BUTTON);
deviceMapTypes.add(HARMONY_ACTIVITY);
deviceMapTypes.add(HARMONY_BUTTON);
deviceMapTypes.add(HASS_DEVICE);
deviceMapTypes.add(HOMEWIZARD_DEVICE);
deviceMapTypes.add(HTTP_DEVICE);
deviceMapTypes.add(HUE_DEVICE);
deviceMapTypes.add(LIFX_DEVICE);
deviceMapTypes.add(MQTT_MESSAGE);
deviceMapTypes.add(NEST_HOMEAWAY);
deviceMapTypes.add(NEST_THERMO_SET);
deviceMapTypes.add(TCP_DEVICE);
deviceMapTypes.add(UDP_DEVICE);
deviceMapTypes.add(VERA_DEVICE);
deviceMapTypes.add(VERA_SCENE);
deviceMapTypes.add(FIBARO_DEVICE);
deviceMapTypes.add(FIBARO_SCENE);
deviceMapTypes.add(SOMFY_DEVICE);
deviceMapTypes.add(OPENHAB_DEVICE);
deviceMapTypes.add(FHEM_DEVICE);
deviceMapTypes.add(BROADLINK_DEVICE);
deviceMapTypes.add(MOZIOT_DEVICE);
deviceMapTypes.add(HOMEGENIE_DEVICE);
}
public static int getTypeIndex() {
return typeIndex;
}
public static int getDisplayIndex() {
return displayIndex;
}
public ArrayList<String[]> getDeviceMapTypes() {
return deviceMapTypes;
}
public Boolean validateType(String type) {
if(type == null || type.trim().isEmpty())
return false;
for(String[] mapType : deviceMapTypes) {
if(type.trim().contentEquals(mapType[typeIndex]))
return true;
}
if(type.trim().contentEquals(EXEC_DEVICE_COMPAT[typeIndex]))
return true;
if(type.trim().contentEquals(TCP_DEVICE_COMPAT[typeIndex]))
return true;
if(type.trim().contentEquals(UDP_DEVICE_COMPAT[typeIndex]))
return true;
return false;
}
}

View File

@@ -2,19 +2,20 @@ package com.bwssystems.HABridge;
import static spark.Spark.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.devicemanagmeent.*;
import com.bwssystems.HABridge.hue.HueMulator;
import com.bwssystems.HABridge.plugins.http.HttpClientPool;
import com.bwssystems.HABridge.upnp.UpnpListener;
import com.bwssystems.HABridge.upnp.UpnpSettingsResource;
import com.bwssystems.harmony.HarmonyServer;
import com.bwssystems.HABridge.util.UDPDatagramSender;
public class HABridge {
private static SystemControl theSystem;
/*
* This program is based on the work of armzilla from this github repository:
@@ -34,66 +35,132 @@ public class HABridge {
public static void main(String[] args) {
Logger log = LoggerFactory.getLogger(HABridge.class);
DeviceResource theResources;
HarmonyServer myHarmonyServer;
HomeManager homeManager;
HueMulator theHueMulator;
UDPDatagramSender udpSender;
UpnpSettingsResource theSettingResponder;
UpnpListener theUpnpListener;
InetAddress address;
String addressString;
BridgeSettings bridgeSettings;
Version theVersion;
@SuppressWarnings("unused")
HttpClientPool thePool;
ShutdownHook shutdownHook = null;
log.info("HA Bridge startup sequence...");
theVersion = new Version();
// Singleton initialization
thePool = new HttpClientPool();
log.info("HA Bridge (v" + theVersion.getVersion() + ") starting setup....");
//get ip address for upnp requests
try {
address = InetAddress.getLocalHost();
addressString = address.getHostAddress();
} catch (UnknownHostException e) {
log.error("Cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
return;
}
bridgeSettings = new BridgeSettings();
bridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DFAULT_WEB_PORT));
bridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address", addressString));
bridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db", Configuration.DEVICE_DB_DIRECTORY));
bridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT));
bridgeSettings.setVeraAddress(System.getProperty("vera.address", Configuration.DEFAULT_VERA_ADDRESS));
bridgeSettings.setHarmonyAddress(System.getProperty("harmony.address", Configuration.DEFAULT_HARMONY_ADDRESS));
bridgeSettings.setHarmonyUser(System.getProperty("harmony.user", Configuration.DEFAULT_HARMONY_USER));
bridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd", Configuration.DEFAULT_HARMONY_PWD));
bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
bridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
bridgeSettings.setDevMode(Boolean.parseBoolean(System.getProperty("dev.mode", "false")));
// sparkjava config directive to set html static file location for Jetty
while(!bridgeSettings.getBridgeControl().isStop()) {
log.info("HA Bridge (v{}) initializing....", theVersion.getVersion() );
bridgeSettings.buildSettings();
if(bridgeSettings.getBridgeSecurity().isUseHttps()) {
secure(bridgeSettings.getBridgeSecurity().getKeyfilePath(), bridgeSettings.getBridgeSecurity().getKeyfilePassword(), null, null);
log.info("Using https for web and api calls");
}
bridgeSettings.getBridgeSecurity().removeTestUsers();
// sparkjava config directive to set ip address for the web server to listen on
ipAddress(bridgeSettings.getBridgeSettingsDescriptor().getWebaddress());
// sparkjava config directive to set port for the web server to listen on
port(bridgeSettings.getBridgeSettingsDescriptor().getServerPort());
staticFileLocation("/public");
initExceptionHandler((e) -> HABridge.theExceptionHandler(e, bridgeSettings.getBridgeSettingsDescriptor().getServerPort()));
if(!bridgeSettings.getBridgeControl().isReinit())
init();
bridgeSettings.getBridgeControl().setReinit(false);
// setup system control api first
theSystem = new SystemControl(bridgeSettings, theVersion);
theSystem.setupServer();
// sparkjava config directive to set ip address for the web server to listen on
// ipAddress("0.0.0.0"); // not used
// sparkjava config directive to set port for the web server to listen on
port(Integer.valueOf(bridgeSettings.getServerPort()));
// sparkjava config directive to set html static file location for Jetty
staticFileLocation("/public");
//setup the harmony connection if available
try {
myHarmonyServer = HarmonyServer.setup(bridgeSettings);
} catch (Exception e) {
log.error("Cannot get harmony client setup, Exiting with message: " + e.getMessage(), e);
return;
// Add shutdown hook to be able to properly stop server
if (shutdownHook != null) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
}
shutdownHook = new ShutdownHook(bridgeSettings, theSystem);
Runtime.getRuntime().addShutdownHook(shutdownHook);
// setup the UDP Datagram socket to be used by the HueMulator and the upnpListener
udpSender = UDPDatagramSender.createUDPDatagramSender(bridgeSettings.getBridgeSettingsDescriptor().getUpnpResponsePort());
if(udpSender == null) {
bridgeSettings.getBridgeControl().setStop(true);
}
else {
//Setup the device connection homes through the manager
homeManager = new HomeManager();
homeManager.buildHomes(bridgeSettings, udpSender);
// setup the class to handle the resource setup rest api
theResources = new DeviceResource(bridgeSettings, homeManager);
// setup the class to handle the hue emulator rest api
theHueMulator = new HueMulator(bridgeSettings, theResources.getDeviceRepository(), theResources.getGroupRepository(), homeManager);
theHueMulator.setupServer();
// wait for the sparkjava initialization of the rest api classes to be complete
awaitInitialization();
if(bridgeSettings.getBridgeSettingsDescriptor().isTraceupnp()) {
log.info("Traceupnp: upnp config address: {} -useIface: {} on web server: {}:{}",
bridgeSettings.getBridgeSettingsDescriptor().getUpnpConfigAddress(),
bridgeSettings.getBridgeSettingsDescriptor().isUseupnpiface(),
bridgeSettings.getBridgeSettingsDescriptor().getWebaddress(),
bridgeSettings.getBridgeSettingsDescriptor().getServerPort());
}
// setup the class to handle the upnp response rest api
theSettingResponder = new UpnpSettingsResource(bridgeSettings);
theSettingResponder.setupServer();
// start the upnp ssdp discovery listener
theUpnpListener = null;
try {
theUpnpListener = new UpnpListener(bridgeSettings, bridgeSettings.getBridgeControl(), udpSender);
} catch (IOException e) {
log.error("Could not initialize UpnpListener, exiting....", e);
theUpnpListener = null;
}
if(theUpnpListener != null && theUpnpListener.startListening())
log.info("HA Bridge (v{}) reinitialization requessted....", theVersion.getVersion());
else
bridgeSettings.getBridgeControl().setStop(true);
if(bridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged())
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
log.info("Going to close all homes");
homeManager.closeHomes();
udpSender.closeResponseSocket();
udpSender = null;
}
stop();
if(!bridgeSettings.getBridgeControl().isStop()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
log.error("Sleep error: {}", e.getMessage());
}
}
}
bridgeSettings.getBridgeSecurity().removeTestUsers();
if(bridgeSettings.getBridgeSecurity().isSettingsChanged())
bridgeSettings.updateConfigFile();
try {
HttpClientPool.shutdown();
} catch (InterruptedException e) {
log.warn("Error shutting down http pool: {}", e.getMessage());;
} catch (IOException e) {
log.warn("Error shutting down http pool: {}", e.getMessage());;
}
// setup the class to handle the resource setup rest api
theResources = new DeviceResource(bridgeSettings, theVersion, myHarmonyServer.getMyHarmony());
// setup the class to handle the hue emulator rest api
theHueMulator = new HueMulator(theResources.getDeviceRepository(), myHarmonyServer.getMyHarmony());
theHueMulator.setupServer();
// setup the class to handle the upnp response rest api
theSettingResponder = new UpnpSettingsResource(bridgeSettings);
theSettingResponder.setupServer();
// wait for the sparkjava initialization of the rest api classes to be complete
awaitInitialization();
// start the upnp ssdp discovery listener
theUpnpListener = new UpnpListener(bridgeSettings);
theUpnpListener.startListening();
thePool = null;
log.info("HA Bridge (v{}) exiting....", theVersion.getVersion());
System.exit(0);
}
private static void theExceptionHandler(Exception e, Integer thePort) {
Logger log = LoggerFactory.getLogger(HABridge.class);
if(e.getMessage().equals("no valid keystore") || e.getMessage().equals("keystore password was incorrect")) {
log.error("Https settings have been removed as {}. Restart system manually after this process exits....", e.getMessage());
log.warn(theSystem.removeHttpsSettings());
}
else {
log.error("Could not start ha-bridge webservice on port [{}] due to: {}", thePort, e.getMessage());
log.warn(theSystem.stop());
}
}
}

View File

@@ -0,0 +1,9 @@
package com.bwssystems.HABridge;
import com.bwssystems.HABridge.devicemanagmeent.ResourceHandler;
import com.bwssystems.HABridge.hue.HueMulatorHandler;
public interface Home extends HueMulatorHandler, ResourceHandler {
public Home createHome(BridgeSettings bridgeSettings);
public void closeHome();
}

View File

@@ -0,0 +1,160 @@
package com.bwssystems.HABridge;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.devicemanagmeent.ResourceHandler;
import com.bwssystems.HABridge.plugins.NestBridge.NestHome;
import com.bwssystems.HABridge.plugins.broadlink.BroadlinkHome;
import com.bwssystems.HABridge.plugins.domoticz.DomoticzHome;
import com.bwssystems.HABridge.plugins.exec.CommandHome;
import com.bwssystems.HABridge.plugins.fhem.FHEMHome;
import com.bwssystems.HABridge.plugins.hal.HalHome;
import com.bwssystems.HABridge.plugins.harmony.HarmonyHome;
import com.bwssystems.HABridge.plugins.hass.HassHome;
import com.bwssystems.HABridge.plugins.homewizard.HomeWizardHome;
import com.bwssystems.HABridge.plugins.http.HTTPHome;
import com.bwssystems.HABridge.plugins.hue.HueHome;
import com.bwssystems.HABridge.plugins.lifx.LifxHome;
import com.bwssystems.HABridge.plugins.moziot.MozIotHome;
import com.bwssystems.HABridge.plugins.mqtt.MQTTHome;
import com.bwssystems.HABridge.plugins.openhab.OpenHABHome;
import com.bwssystems.HABridge.plugins.somfy.SomfyHome;
import com.bwssystems.HABridge.plugins.tcp.TCPHome;
import com.bwssystems.HABridge.plugins.udp.UDPHome;
import com.bwssystems.HABridge.plugins.vera.VeraHome;
import com.bwssystems.HABridge.plugins.fibaro.FibaroHome;
import com.bwssystems.HABridge.plugins.homegenie.HomeGenieHome;
import com.bwssystems.HABridge.util.UDPDatagramSender;
public class HomeManager {
private static final Logger log = LoggerFactory.getLogger(HomeManager.class);
Map<String, Home> homeList;
Map<String, Home> resourceList;
public HomeManager() {
homeList = new HashMap<String, Home>();
resourceList = new HashMap<String, Home>();
}
// factory method
public void buildHomes(BridgeSettings bridgeSettings, UDPDatagramSender aUdpDatagramSender) {
Home aHome = null;
//setup the http handler Home - This must be the first home created for devMode
aHome = new HTTPHome(bridgeSettings);
homeList.put(DeviceMapTypes.HTTP_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.CUSTOM_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.FIBARO_SCENE[DeviceMapTypes.typeIndex], aHome);
//setup the harmony connection if available
aHome = new HarmonyHome(bridgeSettings);
resourceList.put(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex], aHome);
resourceList.put(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex], aHome);
//setup the nest connection if available
aHome = new NestHome(bridgeSettings);
resourceList.put(DeviceMapTypes.NEST_HOMEAWAY[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.NEST_HOMEAWAY[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.NEST_THERMO_SET[DeviceMapTypes.typeIndex], aHome);
//setup the hue passtrhu configuration if available
aHome = new HueHome(bridgeSettings);
resourceList.put(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the hal configuration if available
aHome = new HalHome(bridgeSettings);
resourceList.put(DeviceMapTypes.HAL_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.HAL_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.HAL_BUTTON[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.HAL_HOME[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.HAL_THERMO_SET[DeviceMapTypes.typeIndex], aHome);
//setup the mqtt handlers if available
aHome = new MQTTHome(bridgeSettings);
resourceList.put(DeviceMapTypes.MQTT_MESSAGE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.MQTT_MESSAGE[DeviceMapTypes.typeIndex], aHome);
//setup the HomeAssistant configuration if available
aHome = new HassHome(bridgeSettings);
resourceList.put(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex], aHome);
// Setup the HomeWizard configuration if available
aHome = new HomeWizardHome(bridgeSettings);
resourceList.put(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the command execution Home
aHome = new CommandHome(bridgeSettings);
homeList.put(DeviceMapTypes.EXEC_DEVICE_COMPAT[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.CMD_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the tcp handler Home
aHome = new TCPHome(bridgeSettings);
homeList.put(DeviceMapTypes.TCP_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.TCP_DEVICE_COMPAT[DeviceMapTypes.typeIndex], aHome);
//setup the udp handler Home
aHome = new UDPHome(bridgeSettings, aUdpDatagramSender);
homeList.put(DeviceMapTypes.UDP_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.UDP_DEVICE_COMPAT[DeviceMapTypes.typeIndex], aHome);
// Setup Vera Home if available
aHome = new VeraHome(bridgeSettings);
resourceList.put(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex], aHome);
resourceList.put(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex], aHome);
// Setup Fibaro Home if available
aHome = new FibaroHome(bridgeSettings);
resourceList.put(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex], aHome);
resourceList.put(DeviceMapTypes.FIBARO_SCENE[DeviceMapTypes.typeIndex], aHome);
//setup the Domoticz configuration if available
aHome = new DomoticzHome(bridgeSettings);
homeList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
resourceList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the Somfy configuration if availableOPENHAB
aHome = new SomfyHome(bridgeSettings);
homeList.put(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex], aHome);
resourceList.put(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the Lifx configuration if available
aHome = new LifxHome(bridgeSettings);
resourceList.put(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the OpenHAB configuration if available
aHome = new OpenHABHome(bridgeSettings);
resourceList.put(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the FHEM configuration if available
aHome = new FHEMHome(bridgeSettings);
resourceList.put(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the Broadlink configuration if available
aHome = new BroadlinkHome(bridgeSettings);
resourceList.put(DeviceMapTypes.BROADLINK_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.BROADLINK_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the Mozilla IOT configuration if available
aHome = new MozIotHome(bridgeSettings);
resourceList.put(DeviceMapTypes.MOZIOT_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.MOZIOT_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the HomeGenie configuration if available
aHome = new HomeGenieHome(bridgeSettings);
resourceList.put(DeviceMapTypes.HOMEGENIE_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.HOMEGENIE_DEVICE[DeviceMapTypes.typeIndex], aHome);
}
public Home findHome(String type) {
return homeList.get(type);
}
public ResourceHandler findResource(String type) {
return resourceList.get(type);
}
public void closeHomes() {
log.info("Manager close homes called....");
Collection<Home> theHomes = homeList.values();
for(Home aHome : theHomes) {
log.info("Closing home: " + aHome.getClass().getCanonicalName());
aHome.closeHome();
}
homeList.clear();
homeList = null;
}
}

View File

@@ -0,0 +1,16 @@
package com.bwssystems.HABridge;
import java.util.List;
public class IpList {
private List<NamedIP> devices;
public List<NamedIP> getDevices() {
return devices;
}
public void setDevices(List<NamedIP> devices) {
this.devices = devices;
}
}

View File

@@ -0,0 +1,29 @@
package com.bwssystems.HABridge;
import java.util.Timer;
import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LinkButtonPressed extends TimerTask {
private static final Logger log = LoggerFactory.getLogger(LinkButtonPressed.class);
private BridgeControlDescriptor linkDescriptor;
private Timer myTimer;
private boolean isSilent;
public LinkButtonPressed(BridgeControlDescriptor theDescriptor, Timer aTimer, boolean keepSilent) {
linkDescriptor = theDescriptor;
myTimer = aTimer;
isSilent = keepSilent;
}
@Override
public void run() {
if(!isSilent)
log.info("Link button time ended....");
linkDescriptor.setLinkButton(false);
myTimer.cancel();
}
}

View File

@@ -0,0 +1,20 @@
package com.bwssystems.HABridge;
public class LinkParams {
private Integer seconds;
private boolean silent;
public Integer getSeconds() {
return seconds;
}
public void setSeconds(Integer seconds) {
this.seconds = seconds;
}
public boolean isSilent() {
return silent;
}
public void setSilent(boolean silent) {
this.silent = silent;
}
}

View File

@@ -0,0 +1,22 @@
package com.bwssystems.HABridge;
public class LoginResult {
private String error;
private User user;
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

View File

@@ -0,0 +1,113 @@
package com.bwssystems.HABridge;
import com.google.gson.JsonObject;
import org.apache.commons.codec.binary.Base64;
public class NamedIP {
private String name;
private String ip;
private String webhook;
private String port;
private String username;
private String password;
private JsonObject extensions;
private Boolean secure;
private String httpPreamble;
private String encodedLogin;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getWebhook() {
return webhook;
}
public void setWebhook(final String webhook) {
this.webhook = webhook;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean getSecure() {
return secure;
}
public void setSecure(Boolean secure) {
this.secure = secure;
}
public JsonObject getExtensions() {
return extensions;
}
public void setExtensions(JsonObject extensions) {
this.extensions = extensions;
}
public String getHttpPreamble() {
if (httpPreamble == null || !httpPreamble.trim().isEmpty()) {
if (getSecure() != null && getSecure())
httpPreamble = "https://";
else
httpPreamble = "http://";
httpPreamble = httpPreamble + getIp();
if (getPort() != null && !getPort().trim().isEmpty()) {
httpPreamble = httpPreamble + ":" + getPort();
}
}
return httpPreamble;
}
public void setHttpPreamble(String httpPreamble) {
this.httpPreamble = httpPreamble;
}
public String getUserPass64() {
if (encodedLogin == null || !encodedLogin.trim().isEmpty()) {
if (getUsername() != null && !getUsername().isEmpty() && getPassword() != null
&& !getPassword().isEmpty()) {
String userPass = getUsername() + ":" + getPassword();
encodedLogin = new String(Base64.encodeBase64(userPass.getBytes()));
}
}
return encodedLogin;
}
}

View File

@@ -0,0 +1,62 @@
package com.bwssystems.HABridge;
public class SecurityInfo {
private boolean useLinkButton;
private boolean secureHueApi;
private boolean isSecure;
private String execGarden;
private boolean useHttps;
private String keyfilePath;
private String keyfilePassword;
public boolean isUseLinkButton() {
return useLinkButton;
}
public void setUseLinkButton(boolean useLinkButton) {
this.useLinkButton = useLinkButton;
}
public boolean isSecureHueApi() {
return secureHueApi;
}
public void setSecureHueApi(boolean secureHueApi) {
this.secureHueApi = secureHueApi;
}
public boolean isSecure() {
return isSecure;
}
public void setSecure(boolean isSecure) {
this.isSecure = isSecure;
}
public boolean isUseHttps() {
return useHttps;
}
public void setUseHttps(boolean useHttps) {
this.useHttps = useHttps;
}
public String getKeyfilePath() {
return keyfilePath;
}
public void setKeyfilePath(String keyfilePath) {
this.keyfilePath = keyfilePath;
}
public String getExecGarden() {
return execGarden;
}
public void setExecGarden(String execGarden) {
this.execGarden = execGarden;
}
public String getKeyfilePassword() {
return keyfilePassword;
}
public void setKeyfilePassword(String keyfilePassword) {
this.keyfilePassword = keyfilePassword;
}
}

View File

@@ -0,0 +1,48 @@
package com.bwssystems.HABridge;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class implements the shutdown hook used to properly stop server from the
* command line (sending SIGTERM), or while shutting down the host machine.
*
* @author gaudryc
*/
public class ShutdownHook extends Thread {
private final BridgeSettings bridgeSettings;
private final SystemControl theSystem;
/**
* Constructor
*
* @param bridgeSettings
* bridge settings
* @param theSystem
*/
public ShutdownHook(final BridgeSettings bridgeSettings, final SystemControl theSystem) {
this.bridgeSettings = bridgeSettings;
this.theSystem = theSystem;
}
@Override
public void run() {
Logger log = LoggerFactory.getLogger(ShutdownHook.class);
log.info("Shutdown requested...");
if (bridgeSettings != null) {
if (!bridgeSettings.getBridgeControl().isStop() && (theSystem != null)) {
log.info("Forcing system stop...");
theSystem.stop();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
log.error("Sleep error: " + e.getMessage());
}
} else {
log.info("Already stopped");
}
}
}
}

View File

@@ -0,0 +1,619 @@
package com.bwssystems.HABridge;
import static spark.Spark.get;
import static spark.Spark.options;
import static spark.Spark.post;
import static spark.Spark.put;
import static spark.Spark.before;
import static spark.Spark.halt;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
//import java.util.ArrayList;
import java.util.Arrays;
import java.util.Timer;
import java.util.Base64;
//import java.util.Iterator;
//import java.util.List;
import java.util.Map;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//import com.bwssystems.HABridge.api.NameValue;
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
import com.bwssystems.HABridge.dao.BackupFilename;
import com.bwssystems.HABridge.util.JsonTransformer;
import com.bwssystems.HABridge.util.TextStringFormatter;
import com.bwssystems.logservices.LoggerInfo;
import com.bwssystems.logservices.LoggingForm;
import com.bwssystems.logservices.LoggingManager;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
//import ch.qos.logback.core.Appender;
import ch.qos.logback.core.read.CyclicBufferAppender;
public class SystemControl {
private static final Logger log = LoggerFactory.getLogger(SystemControl.class);
public static final String CYCLIC_BUFFER_APPENDER_NAME = "CYCLIC";
private LoggerContext lc;
private static final String SYSTEM_CONTEXT = "/system";
private BridgeSettings bridgeSettings;
private Version version;
private CyclicBufferAppender<ILoggingEvent> cyclicBufferAppender;
private DateFormat dateFormat;
private LoggingManager theLogServiceMgr;
public SystemControl(BridgeSettings theBridgeSettings, Version theVersion) {
this.bridgeSettings = theBridgeSettings;
this.version = theVersion;
this.lc = (LoggerContext) LoggerFactory.getILoggerFactory();
this.dateFormat = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss.SSS");
setupLoggerSettings();
theLogServiceMgr = new LoggingManager();
theLogServiceMgr.init();
}
// This function sets up the sparkjava rest calls for the hue api
public void setupServer() {
log.info("System control service started....");
before(SYSTEM_CONTEXT + "/*", (request, response) -> {
if(bridgeSettings.getBridgeSecurity().isSecure()) {
String pathInfo = request.pathInfo();
if(pathInfo == null || (!pathInfo.equals(SYSTEM_CONTEXT + "/login") && !pathInfo.equals(SYSTEM_CONTEXT + "/habridge/version"))) {
User authUser = bridgeSettings.getBridgeSecurity().getAuthenticatedUser(request);
if(authUser == null) {
halt(401, "{\"message\":\"User not authenticated\"}");
}
}
}
});
// http://ip_address:port/system/habridge/version gets the version of this bridge instance
get (SYSTEM_CONTEXT + "/habridge/version", (request, response) -> {
log.debug("Get HA Bridge version: v" + version.getVersion());
response.status(HttpStatus.SC_OK);
response.type("application/json");
return "{\"version\":\"" + version.getVersion() + "\",\"isSecure\":" + bridgeSettings.getBridgeSecurity().isSecure() + "}";
});
// http://ip_address:port/system/logmsgs gets the log messages for the bridge
get (SYSTEM_CONTEXT + "/logmsgs", (request, response) -> {
log.debug("Get logmsgs.");
String logMsgs;
int count = -1;
if(cyclicBufferAppender == null)
setupLoggerSettings();
if (cyclicBufferAppender != null) {
count = cyclicBufferAppender.getLength();
}
logMsgs = "[";
if (count == -1) {
logMsgs = logMsgs + "{\"message\":\"Failed to locate CyclicBuffer\"}";
} else if (count == 0) {
logMsgs = logMsgs + "{\"message\":\"No logging events to display\"}";
} else {
LoggingEvent le;
for (int i = 0; i < count; i++) {
le = (LoggingEvent) cyclicBufferAppender.get(i);
logMsgs = logMsgs + ( i > 0?",{":"{") + "\"time\":\"" + dateFormat.format(le.getTimeStamp()) + "\",\"level\":\"" + le.getLevel().levelStr + "\",\"component\":\"" + le.getLoggerName() + "\",\"message\":\"" + TextStringFormatter.forJSON(le.getFormattedMessage()) + "\"}";
}
}
logMsgs = logMsgs + "]";
response.status(HttpStatus.SC_OK);
response.type("application/json");
return logMsgs;
});
// http://ip_address:port/system/logmgmt/loggers gets the logger info for the bridge
get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", (request, response) -> {
log.debug("Get loggers info with showAll argument: " + request.params(":all"));
Boolean showAll = false;
if(request.params(":all").equals("true"))
showAll = true;
theLogServiceMgr.setShowAll(showAll);
theLogServiceMgr.init();
response.status(HttpStatus.SC_OK);
response.type("application/json");
return theLogServiceMgr.getConfiguredLoggers();
}, new JsonTransformer());
// http://ip_address:port/system/setpassword CORS request
options(SYSTEM_CONTEXT + "/setpassword", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/setpassword which sets a password for a given user
post(SYSTEM_CONTEXT + "/setpassword", (request, response) -> {
log.debug("setpassword....");
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
String errorMessage = bridgeSettings.getBridgeSecurity().setPassword(theUser);
if(errorMessage != null) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
} else {
response.status(HttpStatus.SC_OK);
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
}
if(errorMessage == null)
errorMessage = "{}";
response.type("application/json");
return errorMessage;
});
// http://ip_address:port/system/adduser CORS request
options(SYSTEM_CONTEXT + "/adduser", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/adduser which adds a new user
put(SYSTEM_CONTEXT + "/adduser", (request, response) -> {
log.debug("adduser....");
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
String errorMessage = theUser.validate();
if(errorMessage != null) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
} else {
errorMessage = bridgeSettings.getBridgeSecurity().addUser(theUser);
if(errorMessage == null) {
response.status(HttpStatus.SC_OK);
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
} else {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
}
}
if(errorMessage == null)
errorMessage = "{}";
response.type("application/json");
return errorMessage;
});
// http://ip_address:port/system/deluser CORS request
options(SYSTEM_CONTEXT + "/deluser", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/deluser which dels a user
put(SYSTEM_CONTEXT + "/deluser", (request, response) -> {
log.debug("deluser....");
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
String errorMessage = bridgeSettings.getBridgeSecurity().delUser(theUser);
if(errorMessage != null) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
} else {
response.status(HttpStatus.SC_OK);
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
}
if(errorMessage == null)
errorMessage = "{}";
response.type("application/json");
return errorMessage;
});
// http://ip_address:port/system/login CORS request
options(SYSTEM_CONTEXT + "/login", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/login validates the login
post(SYSTEM_CONTEXT + "/login", (request, response) -> {
log.debug("login....");
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
LoginResult result = bridgeSettings.getBridgeSecurity().validatePassword(theUser);
if(result.getUser() != null)
bridgeSettings.getBridgeSecurity().addAuthenticatedUser(request, theUser);
response.status(HttpStatus.SC_OK);
response.type("application/json");
return result;
}, new JsonTransformer());
// http://ip_address:port/system/logout CORS request
options(SYSTEM_CONTEXT + "/logout", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/logout invalidates user session
put(SYSTEM_CONTEXT + "/logout", (request, response) -> {
log.debug("logout....");
bridgeSettings.getBridgeSecurity().removeAuthenticatedUser(request);
response.status(HttpStatus.SC_OK);
response.type("application/json");
return "";
});
// http://ip_address:port/system/presslinkbutton CORS request
options(SYSTEM_CONTEXT + "/presslinkbutton", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/presslinkbutton which sets the link button for device registration
put(SYSTEM_CONTEXT + "/presslinkbutton", (request, response) -> {
LinkParams linkParams = null;
if(!request.body().isEmpty()) {
linkParams = new Gson().fromJson(request.body(), LinkParams.class);
if(linkParams.getSeconds() <= 0)
linkParams.setSeconds(3);
}
else {
linkParams = new LinkParams();
linkParams.setSilent(false);
linkParams.setSeconds(bridgeSettings.getBridgeSettingsDescriptor().getLinkbuttontimeout());
}
if(!linkParams.isSilent())
log.info("Link button pressed....");
bridgeSettings.getBridgeControl().setLinkButton(true);
Timer theTimer = new Timer();
theTimer.schedule(new LinkButtonPressed(bridgeSettings.getBridgeControl(), theTimer, linkParams.isSilent()), (linkParams.getSeconds() * 1000));
response.status(HttpStatus.SC_OK);
response.type("application/json");
return "";
}, new JsonTransformer());
// http://ip_address:port/system/securityinfo gets the security info for the bridge
get (SYSTEM_CONTEXT + "/securityinfo", (request, response) -> {
log.debug("Get security info");
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSecurity().getSecurityInfo();
}, new JsonTransformer());
// http://ip_address:port/system/changesecurityinfo CORS request
options(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/changesecurityinfo which sets the security settings other than passwords and users
post(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
log.debug("changesecurityinfo....");
SecurityInfo theInfo = new Gson().fromJson(request.body(), SecurityInfo.class);
bridgeSettings.getBridgeSecurity().setSecurityDataByInfo(theInfo);
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSecurity().getSecurityInfo();
}, new JsonTransformer());
// http://ip_address:port/system/whitelist gets the whitelist for the bridge
get (SYSTEM_CONTEXT + "/whitelist", (request, response) -> {
log.debug("Get whitelist");
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSecurity().getWhitelist();
}, new JsonTransformer());
// http://ip_address:port/system/setwhitelist CORS request
options(SYSTEM_CONTEXT + "/setwhitelist", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/setwhitelist which sets the whitelist after being managed
post(SYSTEM_CONTEXT + "/setwhitelist", (request, response) -> {
log.debug("setwhitelist....");
Type listType = new TypeToken<Map<String, WhitelistEntry>>() {
}.getType();
Map<String, WhitelistEntry> aWhitelist = new Gson().fromJson(request.body(), listType);
bridgeSettings.getBridgeSecurity().setWhitelist(aWhitelist);
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSecurity().getWhitelist();
}, new JsonTransformer());
// http://ip_address:port/system/logmgmt/update CORS request
options(SYSTEM_CONTEXT + "/logmgmt/update", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/logmgmt/update which changes logging parameters for the process
put(SYSTEM_CONTEXT + "/logmgmt/update", (request, response) -> {
log.debug("update loggers: " + request.body());
LoggerInfo updateLoggers[];
updateLoggers = new Gson().fromJson(request.body(), LoggerInfo[].class);
LoggingForm theModel = theLogServiceMgr.getModel();
theModel.setUpdatedLoggers(Arrays.asList(updateLoggers));
theLogServiceMgr.updateLogLevels();
response.status(HttpStatus.SC_OK);
response.type("application/json");
return theLogServiceMgr.getConfiguredLoggers();
}, new JsonTransformer());
// http://ip_address:port/system/settings which returns the bridge configuration settings
get(SYSTEM_CONTEXT + "/settings", (request, response) -> {
log.debug("bridge settings requested from " + request.ip());
response.status(HttpStatus.SC_OK);
response.type("application/json");
// if(bridgeSettings.getBridgeSettingsDescriptor().getActiveloggers() == null)
// bridgeSettings.getBridgeSettingsDescriptor().setActiveloggers(getLogAppenders());
return bridgeSettings.getBridgeSettingsDescriptor();
}, new JsonTransformer());
// http://ip_address:port/system/settings CORS request
options(SYSTEM_CONTEXT + "/settings", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/settings which returns the bridge configuration settings
put(SYSTEM_CONTEXT + "/settings", (request, response) -> {
log.debug("save bridge settings requested from " + request.ip() + " with body: " + request.body());
BridgeSettingsDescriptor newBridgeSettings = new Gson().fromJson(request.body(), BridgeSettingsDescriptor.class);
if(newBridgeSettings.getUpnpsenddelay() > 15000)
newBridgeSettings.setUpnpsenddelay(15000);
bridgeSettings.save(newBridgeSettings);
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSettingsDescriptor();
}, new JsonTransformer());
// http://ip_address:port/system/control/reinit CORS request
options(SYSTEM_CONTEXT + "/control/reinit", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/control/reinit sets the parameter reinit the server
put(SYSTEM_CONTEXT + "/control/reinit", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.type("application/json");
return reinit();
});
// http://ip_address:port/system/control/stop CORS request
options(SYSTEM_CONTEXT + "/control/stop", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/control/stop sets the parameter stop the server
put(SYSTEM_CONTEXT + "/control/stop", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.type("application/json");
return stop();
});
// http://ip_address:port/system/devices/backup/download CORS request
options(SYSTEM_CONTEXT + "/backup/download", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (SYSTEM_CONTEXT + "/backup/download", "application/json", (request, response) -> {
log.debug("Create download: {}", request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
String backupContent = bridgeSettings.downloadBackup(aFilename.getFilename());
return backupContent;
}, new JsonTransformer());
// http://ip_address:port/system/devices/backup/upload CORS request
options(SYSTEM_CONTEXT + "/backup/upload/:filename", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (SYSTEM_CONTEXT + "/backup/upload/:filename", "application/json", (request, response) -> {
log.debug("Create upload: {} - {}", request.params(":filename"), request.body());
String theSuccess = bridgeSettings.uploadBackup(request.params(":filename"), request.body());
if(theSuccess.contains("Error:"))
response.status(HttpStatus.SC_METHOD_FAILURE);
else
response.status(HttpStatus.SC_OK);
return theSuccess;
}, new JsonTransformer());
// http://ip_address:port/system/backup/available returns a list of config backup filenames
get (SYSTEM_CONTEXT + "/backup/available", (request, response) -> {
log.debug("Get backup filenames");
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBackups();
}, new JsonTransformer());
// http://ip_address:port/system/backup/create CORS request
options(SYSTEM_CONTEXT + "/backup/create", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (SYSTEM_CONTEXT + "/backup/create", (request, response) -> {
log.debug("Create backup: " + request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
BackupFilename returnFilename = new BackupFilename();
returnFilename.setFilename(bridgeSettings.backup(aFilename.getFilename()));
response.status(HttpStatus.SC_OK);
response.type("application/json");
return returnFilename;
}, new JsonTransformer());
// http://ip_address:port/system/backup/delete CORS request
options(SYSTEM_CONTEXT + "/backup/delete", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "POST");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post (SYSTEM_CONTEXT + "/backup/delete", (request, response) -> {
log.debug("Delete backup: " + request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
if(aFilename != null)
bridgeSettings.deleteBackup(aFilename.getFilename());
else
log.warn("No filename given for delete backup.");
response.status(HttpStatus.SC_OK);
response.type("application/json");
return "";
}, new JsonTransformer());
// http://ip_address:port/system/backup/restore CORS request
options(SYSTEM_CONTEXT + "/backup/restore", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "POST");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post (SYSTEM_CONTEXT + "/backup/restore", (request, response) -> {
log.debug("Restore backup: " + request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
if(aFilename != null) {
bridgeSettings.restoreBackup(aFilename.getFilename());
bridgeSettings.loadConfig();
}
else
log.warn("No filename given for restore backup.");
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSettingsDescriptor();
}, new JsonTransformer());
}
private void setupLoggerSettings() {
// final ch.qos.logback.classic.Logger logger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
cyclicBufferAppender = (CyclicBufferAppender<ILoggingEvent>) lc.getLogger(
Logger.ROOT_LOGGER_NAME).getAppender(CYCLIC_BUFFER_APPENDER_NAME);
cyclicBufferAppender.setMaxSize(bridgeSettings.getBridgeSettingsDescriptor().getNumberoflogmessages());
// if(bridgeSettings.getBridgeSettingsDescriptor().getActiveloggers() != null) {
// for (NameValue temp : bridgeSettings.getBridgeSettingsDescriptor().getActiveloggers()) {
// if(temp.getValue().equals("false"))
// logger.detachAppender(temp.getName());
// }
//
// }
}
// private List<NameValue> getLogAppenders() {
// final ch.qos.logback.classic.Logger logger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
//
// final Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders();
//
// List<NameValue> theLoggers = new ArrayList<NameValue>();
//
// while (it.hasNext()) {
//
// final Appender<ILoggingEvent> appender = it.next();
//
// if (!(appender instanceof CyclicBufferAppender)) {
// NameValue theLogger = new NameValue();
// theLogger.setName(appender.getName());
// theLogger.setValue("true");
// theLoggers.add(theLogger);
// }
// }
//
// return theLoggers;
// }
protected void pingListener() {
try {
byte[] buf = new byte[256];
String testData = "M-SEARCH * HTTP/1.1\nHOST: " + Configuration.UPNP_MULTICAST_ADDRESS + ":" + Configuration.UPNP_DISCOVERY_PORT + "ST: urn:schemas-upnp-org:device:CloudProxy:1\nMAN: \"ssdp:discover\"\nMX: 3";
buf = testData.getBytes();
MulticastSocket socket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
InetAddress group = InetAddress.getByName(Configuration.UPNP_MULTICAST_ADDRESS);
DatagramPacket packet;
packet = new DatagramPacket(buf, buf.length, group, Configuration.UPNP_DISCOVERY_PORT);
socket.send(packet);
socket.close();
}
catch (IOException e) {
log.warn("Error pinging listener. " + e.getMessage());
}
}
public String removeHttpsSettings() {
bridgeSettings.getBridgeSecurity().removeHttpsSettings();
return stop();
}
public String reinit() {
bridgeSettings.getBridgeControl().setReinit(true);
pingListener();
return "{\"control\":\"reiniting\"}";
}
public String stop() {
bridgeSettings.getBridgeControl().setStop(true);
pingListener();
return "{\"control\":\"stopping\"}";
}
}

View File

@@ -0,0 +1,65 @@
package com.bwssystems.HABridge;
import spark.utils.StringUtils;
public class User {
private int id;
private String username;
private String password;
private String password2;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword2() {
return password2;
}
public void setPassword2(String password2) {
this.password2 = password2;
}
public String validate() {
String error = null;
if(StringUtils.isEmpty(username)) {
error = "You have to enter a username";
} else if(StringUtils.isEmpty(password)) {
error = "You have to enter a password";
} else if(!password.equals(password2)) {
error = "The two passwords do not match";
}
return error;
}
public boolean validatePassword() {
if(password != null && password2 != null)
return password.equals(password2);
return false;
}
}

View File

@@ -0,0 +1,50 @@
package com.bwssystems.HABridge;
import java.io.InputStream;
import java.util.Properties;
public final class Version {
private String version;
private String groupId;
private String artifactId;
private Properties prop;
public Version()
{
InputStream resourceAsStream =
(InputStream) this.getClass().getResourceAsStream(
"/version.properties"
);
this.prop = new Properties();
try
{
this.prop.load( resourceAsStream );
this.version = this.prop.getProperty("version");
this.groupId = this.prop.getProperty("groupId");
this.artifactId = this.prop.getProperty("artifactId");
}
catch (Exception e)
{
this.version = "0.0.0";
this.groupId = "no group";
this.artifactId = "no artifact";
}
}
public String getVersion() {
return version;
}
public String getGroupId() {
return groupId;
}
public String getArtifactId() {
return artifactId;
}
}

View File

@@ -0,0 +1,87 @@
package com.bwssystems.HABridge.api;
import com.google.gson.JsonElement;
public class CallItem {
private JsonElement item;
private Integer count;
private Integer delay;
private String type;
private String filterIPs;
private String httpVerb;
private String httpBody;
private String httpHeaders;
private String contentType;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getFilterIPs() {
return filterIPs;
}
public void setFilterIPs(String filterIPs) {
this.filterIPs = filterIPs;
}
public JsonElement getItem() {
return item;
}
public void setItem(JsonElement item) {
this.item = item;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public Integer getDelay() {
return delay;
}
public void setDelay(Integer delay) {
this.delay = delay;
}
public String getHttpVerb() {
return httpVerb;
}
public void setHttpVerb(String httpVerb) {
this.httpVerb = httpVerb;
}
public String getHttpBody() {
return httpBody;
}
public void setHttpBody(String httpBody) {
this.httpBody = httpBody;
}
public String getHttpHeaders() {
return httpHeaders;
}
public void setHttpHeaders(String httpHeaders) {
this.httpHeaders = httpHeaders;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
}

View File

@@ -0,0 +1,30 @@
package com.bwssystems.HABridge.api;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class CallItemDeserializer implements JsonDeserializer<CallItem> {
@Override
public CallItem deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx)
{
CallItem aCallItem = new CallItem();
JsonObject jsonObj = json.getAsJsonObject();
JsonElement jsonElem;
jsonElem = jsonObj.get("item");
aCallItem.setItem(jsonElem);
jsonElem = jsonObj.get("delay");
aCallItem.setDelay(jsonElem.getAsInt());
jsonElem = jsonObj.get("count");
aCallItem.setCount(jsonElem.getAsInt());
jsonElem = jsonObj.get("type");
aCallItem.setType(jsonElem.getAsString());
jsonElem = jsonObj.get("filterIPs");
aCallItem.setFilterIPs(jsonElem.getAsString());
return aCallItem;
}
}

View File

@@ -0,0 +1,18 @@
package com.bwssystems.HABridge.api;
public class NameValue {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,13 @@
package com.bwssystems.HABridge.api;
public class SuccessUserResponse {
private UserCreateResponse success;
public UserCreateResponse getSuccess() {
return success;
}
public void setSuccess(UserCreateResponse success) {
this.success = success;
}
}

View File

@@ -0,0 +1,13 @@
package com.bwssystems.HABridge.api;
public class UserCreateResponse {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}

View File

@@ -1,7 +1,7 @@
package com.bwssystems.HABridge.api.hue;
import java.util.HashMap;
import java.util.Map;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.dao.GroupDescriptor;
/**
* Created by arm on 4/14/15.
@@ -12,9 +12,12 @@ public class DeviceResponse {
private String name;
private String modelid;
private String manufacturername;
private String luminaireuniqueid;
private String uniqueid;
private String swversion;
private Map<String, String> pointsymbol;
private String swconfigid;
private String productid;
private String productname;
public DeviceState getState() {
return state;
@@ -72,45 +75,83 @@ public class DeviceResponse {
this.swversion = swversion;
}
public Map<String, String> getPointsymbol() {
if(pointsymbol == null)
{
pointsymbol = new HashMap<>();
pointsymbol.put("1", "none");
pointsymbol.put("2", "none");
pointsymbol.put("3", "none");
pointsymbol.put("4", "none");
pointsymbol.put("5", "none");
pointsymbol.put("6", "none");
pointsymbol.put("7", "none");
pointsymbol.put("8", "none");
}
return pointsymbol;
public String getSwconfigid() {
return swconfigid;
}
public void setPointsymbol(Map<String, String> pointsymbol) {
this.pointsymbol = pointsymbol;
public void setSwconfigid(String swconfigid) {
this.swconfigid = swconfigid;
}
public String getProductid() {
return productid;
}
public static DeviceResponse createResponse(String name, String id){
DeviceState deviceState = new DeviceState();
public void setProductid(String productid) {
this.productid = productid;
}
public String getProductName() {
return productname;
}
public void setProductName(String productname) {
this.productname = productname;
}
public String getLuminaireuniqueid() {
return luminaireuniqueid;
}
public void setLuminaireuniqueid(String luminaireuniqueid) {
this.luminaireuniqueid = luminaireuniqueid;
}
public static DeviceResponse createResponse(DeviceDescriptor device){
DeviceResponse response = new DeviceResponse();
response.setState(deviceState);
deviceState.setOn(false);
deviceState.setReachable(true);
deviceState.setEffect("none");
deviceState.setAlert("none");
deviceState.setBri(254);
deviceState.setSat(254);
response.setState(device.getDeviceState());
response.setName(name);
response.setUniqueid(id);
response.setName(device.getName());
response.setUniqueid(device.getUniqueid());
response.setManufacturername("Philips");
response.setType("Dimmable light");
response.setModelid("LWB004");
response.setSwversion("65003148");
if (device.isColorDevice()) {
response.setType("Extended color light");
response.setModelid("LCT015");
response.setSwversion("1.46.13_r26312");
response.setSwconfigid("52E3234B");
response.setProductid("Philips-LCT015-1-A19ECLv5");
response.setProductName("Hue color lamp");
} else {
response.setType("Dimmable light");
response.setModelid("LWB007");
response.setSwversion("66012040");
}
response.setLuminaireuniqueid(null);
return response;
}
public static DeviceResponse createResponseForVirtualLight(GroupDescriptor group){
DeviceResponse response = new DeviceResponse();
response.setState(group.getAction());
response.setName(group.getName());
response.setUniqueid("00:11:22:33:44:55:66:77-" + String.format("%02X", Integer.parseInt(group.getId())));
response.setManufacturername("Philips");
response.setType("Extended color light");
response.setModelid("LCT015");
response.setSwversion("1.46.13_r26312");
response.setSwconfigid("52E3234B");
response.setProductid("Philips-LCT015-1-A19ECLv5");
response.setProductName("Hue color lamp");
response.setLuminaireuniqueid(null);
return response;
}
}

View File

@@ -1,5 +1,6 @@
package com.bwssystems.HABridge.api.hue;
import java.util.ArrayList;
import java.util.List;
/**
@@ -7,15 +8,17 @@ import java.util.List;
*/
public class DeviceState {
private boolean on;
private int bri = 255;
private int hue;
private int sat;
private int bri;
private Integer hue;
private Integer sat;
private String effect;
private int ct;
private List<Double> xy;
private Integer ct;
private String alert;
private String colormode;
private boolean reachable;
private List<Double> xy;
// private int transitiontime;
public boolean isOn() {
return on;
@@ -34,19 +37,21 @@ public class DeviceState {
}
public int getHue() {
return hue;
return hue != null ? hue.intValue() : 0;
}
public void setHue(int hue) {
this.hue = hue;
this.colormode = "hs";
}
public int getSat() {
return sat;
return sat != null ? sat.intValue() : 0;
}
public void setSat(int sat) {
this.sat = sat;
this.colormode = "hs";
}
public String getEffect() {
@@ -58,11 +63,12 @@ public class DeviceState {
}
public int getCt() {
return ct;
return ct != null ? ct.intValue() : 0;
}
public void setCt(int ct) {
this.ct = ct;
this.colormode = "ct";
}
public String getAlert() {
@@ -95,8 +101,40 @@ public class DeviceState {
public void setXy(List<Double> xy) {
this.xy = xy;
this.colormode = "xy";
}
// public int getTransitiontime() {
// return transitiontime;
// }
// public void setTransitiontime(int transitiontime) {
// this.transitiontime = transitiontime;
// }
public static DeviceState createDeviceState(boolean color) {
DeviceState newDeviceState = new DeviceState();
newDeviceState.fillIn(color);
if (color) {
newDeviceState.setColormode("xy");
newDeviceState.setHue(0);
newDeviceState.setSat(0);
newDeviceState.setCt(153);
ArrayList<Double> doubleArray = new ArrayList<Double>();
doubleArray.add(0.3146);
doubleArray.add(0.3303);
newDeviceState.setXy(doubleArray);
}
return newDeviceState;
}
public void fillIn(boolean color) {
if(this.getAlert() == null)
this.setAlert("none");
if (color) {
if(this.getEffect() == null)
this.setEffect("none");
}
this.setReachable(true);
}
@Override
public String toString() {
return "DeviceState{" +

View File

@@ -0,0 +1,18 @@
package com.bwssystems.HABridge.api.hue;
public class DeviceTypes {
private Boolean bridge;
private String[] lights;
public Boolean getBridge() {
return bridge;
}
public void setBridge(Boolean bridge) {
this.bridge = bridge;
}
public String[] getLights() {
return lights;
}
public void setLights(String[] lights) {
this.lights = lights;
}
}

View File

@@ -0,0 +1,61 @@
package com.bwssystems.HABridge.api.hue;
import java.util.ArrayList;
public class GroupClassTypes {
public final static String BATHROOM = "Bathroom";
public final static String BEDROOM = "Bedroom";
public final static String CARPORT = "Carport";
public final static String DINING = "Dining";
public final static String DRIVEWAY = "Driveway";
public final static String FRONT_DOOR = "Front door";
public final static String GARAGE = "Garage";
public final static String GARDEN = "Garden";
public final static String GYM = "Gym";
public final static String HALLWAY = "Hallway";
public final static String BEDROOM_KIDS = "Kids bedroom";
public final static String KITCHEN = "Kitchen";
public final static String LIVING_ROOM = "Living room";
public final static String NURSERY = "Nursery";
public final static String OFFICE = "Office";
public final static String OTHER = "Other";
public final static String RECREATION = "Recreation";
public final static String TERRACE = "Terrace";
public final static String TOILET = "Toilet";
ArrayList<String> groupClassTypes;
public GroupClassTypes() {
groupClassTypes = new ArrayList<String>();
groupClassTypes.add(BATHROOM);
groupClassTypes.add(BEDROOM);
groupClassTypes.add(CARPORT);
groupClassTypes.add(DINING);
groupClassTypes.add(DRIVEWAY);
groupClassTypes.add(FRONT_DOOR);
groupClassTypes.add(GARAGE);
groupClassTypes.add(GARDEN);
groupClassTypes.add(GYM);
groupClassTypes.add(HALLWAY);
groupClassTypes.add(BEDROOM_KIDS);
groupClassTypes.add(KITCHEN);
groupClassTypes.add(LIVING_ROOM);
groupClassTypes.add(NURSERY);
groupClassTypes.add(OFFICE);
groupClassTypes.add(OTHER);
groupClassTypes.add(RECREATION);
groupClassTypes.add(TERRACE);
groupClassTypes.add(TOILET);
}
public Boolean validateType(String type) {
if(type == null || type.trim().isEmpty())
return false;
for(String classType : groupClassTypes) {
if(type.trim().contentEquals(classType))
return true;
}
return false;
}
}

View File

@@ -0,0 +1,126 @@
package com.bwssystems.HABridge.api.hue;
import java.util.Map;
import com.bwssystems.HABridge.dao.GroupDescriptor;
import com.google.gson.annotations.SerializedName;
public class GroupResponse {
@SerializedName("action")
private DeviceState action;
@SerializedName("lights")
private String[] lights;
@SerializedName("name")
private String name;
@SerializedName("type")
private String type;
@SerializedName("class")
String class_name;
@SerializedName("state")
private GroupState state;
public DeviceState getAction() {
return action;
}
public void setAction(DeviceState action) {
this.action = action;
}
public GroupState getState() {
return state;
}
public void setState(GroupState state) {
this.state = state;
}
public String[] getLights() {
return lights;
}
public void setLights(String[] lights) {
this.lights = lights;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getClass_name() {
return class_name;
}
public void setClass_name(String class_name) {
this.class_name = class_name;
}
public static GroupResponse createDefaultGroupResponse(Map<String, DeviceResponse> deviceList) {
String[] theList = new String[deviceList.size()];
Boolean all_on = true;
Boolean any_on = false;
int i = 0;
for (Map.Entry<String, DeviceResponse> device : deviceList.entrySet()) {
if (Integer.parseInt(device.getKey()) >= 10000) { // don't show fake lights for other groups
continue;
}
theList[i] = device.getKey();
Boolean is_on = device.getValue().getState().isOn();
if (is_on)
any_on = true;
else
all_on = false;
i++;
}
GroupResponse theResponse = new GroupResponse();
theResponse.setAction(DeviceState.createDeviceState(true));
theResponse.setState(new GroupState(all_on, any_on));
theResponse.setName("Group 0");
theResponse.setLights(theList);
theResponse.setType("LightGroup");
return theResponse;
}
public static GroupResponse createResponse(GroupDescriptor group, Map<String, DeviceResponse> lights){
GroupResponse response = new GroupResponse();
Boolean all_on = true;
Boolean any_on = false;
String[] groupLights = null;
if (lights == null) {
all_on = false;
groupLights = group.getLights();
} else {
for (DeviceResponse light : lights.values()) {
Boolean is_on = light.getState().isOn();
if (is_on)
any_on = true;
else
all_on = false;
}
// group.getLights() is not filtered by requester, lights is
// we want the filtered version but keep the order from group.getLights()
groupLights = new String[lights.size()];
int i = 0;
for (String light : group.getLights()) {
if (lights.keySet().contains(light)) {
groupLights[i] = light;
i++;
}
}
}
response.setState(new GroupState(all_on, any_on));
response.setAction(group.getAction());
response.setName(group.getName());
response.setType(group.getGroupType());
response.setLights(groupLights);
response.setClass_name(group.getGroupClass());
return response;
}
}

View File

@@ -0,0 +1,35 @@
package com.bwssystems.HABridge.api.hue;
/**
* Created by Florian Foerderreuther on 07/23/17
*/
public class GroupState {
private boolean all_on;
private boolean any_on;
public boolean isAllOn() {
return all_on;
}
public boolean isAnyOn() {
return any_on;
}
public void setState(boolean all_on, boolean any_on) {
this.all_on = all_on;
this.any_on = any_on;
}
public GroupState(boolean all_on, boolean any_on) {
this.all_on = all_on;
this.any_on = any_on;
}
@Override
public String toString() {
return "GroupState{" +
"all_on=" + all_on +
", any_on=" + any_on +
'}';
}
}

View File

@@ -4,19 +4,26 @@ import java.util.HashMap;
import java.util.Map;
import com.bwssystems.HABridge.api.hue.DeviceResponse;
import com.google.gson.JsonObject;
/**
* Created by arm on 4/14/15.
*/
public class HueApiResponse {
private Map<String, DeviceResponse> lights;
private Map<String, String> scenes;
private Map<String, String> groups;
private Map<String, JsonObject> scenes;
private Map<String, GroupResponse> groups;
private Map<String, JsonObject> schedules;
private Map<String, JsonObject> sensors;
private Map<String, JsonObject> rules;
private HueConfig config;
public HueApiResponse(String name, String ipaddress, String username, String userid) {
public HueApiResponse(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist, String emulateHubVersion, boolean isLinkButtonPressed, String emulateMAC) {
super();
this.setConfig(HueConfig.createConfig(name, ipaddress, username, userid));
this.setConfig(HueConfig.createConfig(name, ipaddress, awhitelist, emulateHubVersion, isLinkButtonPressed, emulateMAC));
this.setRules(new HashMap<>());
this.setSensors(new HashMap<>());
this.setSchedules(new HashMap<>());
this.setGroups(new HashMap<>());
this.setScenes(new HashMap<>());
}
@@ -29,22 +36,46 @@ public class HueApiResponse {
this.lights = lights;
}
public Map<String, String> getScenes() {
public Map<String, JsonObject> getScenes() {
return scenes;
}
public void setScenes(Map<String, String> scenes) {
public void setScenes(Map<String, JsonObject> scenes) {
this.scenes = scenes;
}
public Map<String, String> getGroups() {
public Map<String, GroupResponse> getGroups() {
return groups;
}
public void setGroups(Map<String, String> groups) {
public void setGroups(Map<String, GroupResponse> groups) {
this.groups = groups;
}
public Map<String, JsonObject> getSchedules() {
return schedules;
}
public void setSchedules(Map<String, JsonObject> schedules) {
this.schedules = schedules;
}
public Map<String, JsonObject> getSensors() {
return sensors;
}
public void setSensors(Map<String, JsonObject> sensors) {
this.sensors = sensors;
}
public Map<String, JsonObject> getRules() {
return rules;
}
public void setRules(Map<String, JsonObject> rules) {
this.rules = rules;
}
public HueConfig getConfig() {
return config;
}

View File

@@ -1,7 +1,13 @@
package com.bwssystems.HABridge.api.hue;
import java.util.HashMap;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
public class HueConfig
{
@@ -22,36 +28,75 @@ public class HueConfig
private String localtime;
private String timezone;
private String zigbeechannel;
private String modelid;
private String bridgeid;
private Boolean factorynew;
private String replacesbridgeid;
private Map<String, WhitelistEntry> whitelist;
public static HueConfig createConfig(String name, String ipaddress, String devicetype, String userid) {
public static HueConfig createConfig(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist, String emulateHubVersion, boolean isLinkButtonPressed, String emulateMAC) {
HueConfig aConfig = new HueConfig();
aConfig.setApiversion("1.4.0");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC"));
aConfig.setMac(HueConfig.getMacAddress(ipaddress));
aConfig.setApiversion(HueConstants.API_VERSION);
aConfig.setPortalservices(false);
aConfig.setGateway("192.168.1.1");
aConfig.setMac("00:00:88:00:bb:ee");
aConfig.setSwversion("01005215");
aConfig.setLinkbutton(false);
aConfig.setGateway(ipaddress);
aConfig.setSwversion(emulateHubVersion);
aConfig.setLinkbutton(isLinkButtonPressed);
aConfig.setIpaddress(ipaddress);
aConfig.setProxyport(0);
aConfig.setSwupdate(Swupdate.createSwupdate());
aConfig.setNetmask("255.255.255.0");
aConfig.setName(name);
aConfig.setDhcp(true);
aConfig.setUtc("2014-07-17T09:27:35");
aConfig.setProxyaddress("0.0.0.0");
aConfig.setLocaltime("2014-07-17T11:27:35");
aConfig.setTimezone("America/Chicago");
aConfig.setUtc(dateFormatGmt.format(new Date()));
aConfig.setProxyaddress("none");
aConfig.setLocaltime(dateFormat.format(new Date()));
aConfig.setTimezone(TimeZone.getDefault().getID());
aConfig.setZigbeechannel("6");
Map<String, WhitelistEntry> awhitelist = new HashMap<>();
awhitelist.put(userid, WhitelistEntry.createEntry(devicetype));
aConfig.setBridgeid(HuePublicConfig.createConfig(name, ipaddress, emulateHubVersion, emulateMAC).getHueBridgeIdFromMac());
aConfig.setModelid(HueConstants.MODEL_ID);
aConfig.setFactorynew(false);
aConfig.setReplacesbridgeid(null);
aConfig.setWhitelist(awhitelist);
return aConfig;
}
private static String getMacAddress(String addr)
{
InetAddress ip;
StringBuilder sb = new StringBuilder();
try {
ip = InetAddress.getByName(addr);
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
byte[] mac = network.getHardwareAddress();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? ":" : ""));
}
} catch (UnknownHostException e) {
sb.append("00:00:88:00:bb:ee");
} catch (SocketException e){
sb.append("00:00:88:00:bb:ee");
} catch (Exception e){
sb.append("00:00:88:00:bb:ee");
}
return sb.toString();
}
public Boolean getPortalservices() {
return portalservices;
}
@@ -195,4 +240,36 @@ public class HueConfig
public void setZigbeechannel(String zigbeechannel) {
this.zigbeechannel = zigbeechannel;
}
public String getModelid() {
return modelid;
}
public void setModelid(String modelid) {
this.modelid = modelid;
}
public String getBridgeid() {
return bridgeid;
}
public void setBridgeid(String bridgeid) {
this.bridgeid = bridgeid;
}
public Boolean getFactorynew() {
return factorynew;
}
public void setFactorynew(Boolean factorynew) {
this.factorynew = factorynew;
}
public String getReplacesbridgeid() {
return replacesbridgeid;
}
public void setReplacesbridgeid(String replacesbridgeid) {
this.replacesbridgeid = replacesbridgeid;
}
}

View File

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

View File

@@ -0,0 +1,19 @@
package com.bwssystems.HABridge.api.hue;
public class HueError {
private HueErrorDetails error;
public HueError(HueErrorDetails error) {
super();
this.error = error;
}
public HueErrorDetails getError() {
return error;
}
public void setError(HueErrorDetails error) {
this.error = error;
}
}

View File

@@ -0,0 +1,56 @@
package com.bwssystems.HABridge.api.hue;
public class HueErrorDetails {
private String type;
private String address;
private String description;
private String method_name;
private String resource_name;
private String value;
public HueErrorDetails(String type, String address, String description, String method_name, String resource_name,
String value) {
super();
this.type = type;
this.address = address;
this.description = description;
this.method_name = method_name;
this.resource_name = resource_name;
this.value = value;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getMethod_name() {
return method_name;
}
public void setMethod_name(String method_name) {
this.method_name = method_name;
}
public String getResource_name() {
return resource_name;
}
public void setResource_name(String resource_name) {
this.resource_name = resource_name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,32 @@
package com.bwssystems.HABridge.api.hue;
import java.util.ArrayList;
public class HueErrorResponse {
private ArrayList<HueError> theErrors;
public static HueErrorResponse createResponse(String type, String address, String description, String method_name, String resource_name, String value) {
HueErrorResponse theErrorResp = new HueErrorResponse();
theErrorResp.addError(new HueError(new HueErrorDetails(type, address, description, method_name, resource_name, value)));
return theErrorResp;
}
public HueErrorResponse() {
super();
theErrors = new ArrayList<HueError>();
}
public void addError(HueError anError) {
theErrors.add(anError);
}
public HueError[] getTheErrors() {
HueError theList[] = new HueError[theErrors.size()];
theList = theErrors.toArray(theList);
return theList;
}
public void setTheErrors(ArrayList<HueError> theErrors) {
this.theErrors = theErrors;
}
}

View File

@@ -0,0 +1,156 @@
package com.bwssystems.HABridge.api.hue;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
public class HuePublicConfig
{
private String name;
private String apiversion;
private String swversion;
private String mac;
private String bridgeid;
private String replacesbridgeid;
private Boolean factorynew;
private String modelid;
public static HuePublicConfig createConfig(String name, String ipaddress, String emulateHubVersion, String emulateMAC) {
HuePublicConfig aConfig = new HuePublicConfig();
aConfig.setMac(HuePublicConfig.getMacAddress(ipaddress, emulateMAC));
aConfig.setApiversion(HueConstants.API_VERSION);
aConfig.setSwversion(emulateHubVersion);
aConfig.setName(name);
aConfig.setBridgeid(aConfig.getHueBridgeIdFromMac());
aConfig.setModelid(HueConstants.MODEL_ID);
aConfig.setFactorynew(false);
aConfig.setReplacesbridgeid(null);
return aConfig;
}
private static String getMacAddress(String addr, String aMAC)
{
InetAddress ip;
StringBuilder sb = new StringBuilder();
if(aMAC == null || aMAC.trim().length() <= 0) {
try {
ip = InetAddress.getByName(addr);
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
byte[] mac = network.getHardwareAddress();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? ":" : ""));
}
} catch (UnknownHostException e) {
sb.append("00:00:88:00:bb:ee");
} catch (SocketException e){
sb.append("00:00:88:00:bb:ee");
} catch (Exception e){
sb.append("00:00:88:00:bb:ee");
}
}
else {
sb.append(aMAC.trim());
}
return sb.toString();
}
public String getSNUUIDFromMac()
{
StringTokenizer st = new StringTokenizer(this.getMac(), ":");
String bridgeUUID = "";
while(st.hasMoreTokens()) {
bridgeUUID = bridgeUUID + st.nextToken();
}
bridgeUUID = bridgeUUID.toLowerCase();
return bridgeUUID.toLowerCase();
}
protected String getHueBridgeIdFromMac()
{
String cleanMac = this.getSNUUIDFromMac();
String bridgeId = cleanMac.substring(0, 6) + "FFFE" + cleanMac.substring(6);
return bridgeId.toUpperCase();
}
public String getMac() {
return mac;
}
public void setMac(String mac) {
this.mac = mac;
}
public String getSwversion() {
return swversion;
}
public void setSwversion(String swversion) {
this.swversion = swversion;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getApiversion() {
return apiversion;
}
public void setApiversion(String apiversion) {
this.apiversion = apiversion;
}
public String getModelid() {
return modelid;
}
public void setModelid(String modelid) {
this.modelid = modelid;
}
public String getBridgeid() {
return bridgeid;
}
public void setBridgeid(String bridgeid) {
this.bridgeid = bridgeid;
}
public Boolean getFactorynew() {
return factorynew;
}
public void setFactorynew(Boolean factorynew) {
this.factorynew = factorynew;
}
public String getReplacesbridgeid() {
return replacesbridgeid;
}
public void setReplacesbridgeid(String replacesbridgeid) {
this.replacesbridgeid = replacesbridgeid;
}
}

View File

@@ -0,0 +1,136 @@
package com.bwssystems.HABridge.api.hue;
// import java.util.ArrayList;
import java.util.List;
/**
* Created by arm on 4/14/15.
*/
public class StateChangeBody {
private boolean on;
private int bri;
private int hue;
private int sat;
private String effect;
private int ct;
private String alert;
private List<Double> xy;
private int transitiontime;
private int bri_inc;
private int hue_inc;
private int sat_inc;
private List<Double> xy_inc;
private int ct_inc;
public boolean isOn() {
return on;
}
public void setOn(boolean on) {
this.on = on;
}
public int getBri() {
return bri;
}
public void setBri(int bri) {
this.bri = bri;
}
public int getHue() {
return hue;
}
public void setHue(int hue) {
this.hue = hue;
}
public int getSat() {
return sat;
}
public void setSat(int sat) {
this.sat = sat;
}
public String getEffect() {
return effect;
}
public void setEffect(String effect) {
this.effect = effect;
}
public int getCt() {
return ct;
}
public void setCt(int ct) {
this.ct = ct;
}
public String getAlert() {
return alert;
}
public void setAlert(String alert) {
this.alert = alert;
}
public List<Double> getXy() {
return xy;
}
public void setXy(List<Double> xy) {
this.xy = xy;
}
public int getTransitiontime() {
return transitiontime;
}
public void setTransitiontime(int transitiontime) {
this.transitiontime = transitiontime;
}
public int getBri_inc() {
return bri_inc;
}
public void setBri_inc(int bri_inc) {
this.bri_inc = bri_inc;
}
public int getHue_inc() {
return hue_inc;
}
public void setHue_inc(int hue_inc) {
this.hue_inc = hue_inc;
}
public int getSat_inc() {
return sat_inc;
}
public void setSat_inc(int sat_inc) {
this.sat_inc = sat_inc;
}
public List<Double> getXy_inc() {
return xy_inc;
}
public void setXy_inc(List<Double> xy_inc) {
this.xy_inc = xy_inc;
}
public int getCt_inc() {
return ct_inc;
}
public void setCt_inc(int ct_inc) {
this.ct_inc = ct_inc;
}
}

View File

@@ -3,20 +3,36 @@ package com.bwssystems.HABridge.api.hue;
public class Swupdate
{
private Integer updatestate;
private Boolean checkforupdate;
private DeviceTypes devicetypes;
private String text;
private Boolean notify;
private Integer updatestate;
private String url;
public static Swupdate createSwupdate() {
Swupdate aSwupdate = new Swupdate();
aSwupdate.setUpdatestate(0);
aSwupdate.setCheckforupdate(false);
aSwupdate.setDevicetypes(new DeviceTypes());
aSwupdate.setNotify(false);
aSwupdate.setText("");
aSwupdate.setUpdatestate(0);
aSwupdate.setUrl("");
return aSwupdate;
}
public Boolean getCheckforupdate() {
return checkforupdate;
}
public void setCheckforupdate(Boolean checkforupdate) {
this.checkforupdate = checkforupdate;
}
public DeviceTypes getDevicetypes() {
return devicetypes;
}
public void setDevicetypes(DeviceTypes devicetypes) {
this.devicetypes = devicetypes;
}
public String getText() {
return text;
}

View File

@@ -1,14 +1,14 @@
package com.bwssystems.HABridge.api.hue;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class WhitelistEntry
{
private String lastUseDate;
private String createDate;
private String name;
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
private static final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
public static WhitelistEntry createEntry(String devicetype) {
WhitelistEntry anEntry = new WhitelistEntry();
@@ -18,9 +18,9 @@ public class WhitelistEntry
return anEntry;
}
public static String getCurrentDate() {
return dateFormat.format(new Date());
}
public static String getCurrentDate() {
return LocalDateTime.now().format(dateTimeFormat);
}
public String getLastUseDate() {
return lastUseDate;

View File

@@ -0,0 +1,23 @@
package com.bwssystems.HABridge.dao;
public class BackupFilename {
private String filename;
private String file;
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getFile() {
return file;
}
public void setFile(String file) {
this.file = file;
}
}

View File

@@ -1,18 +1,104 @@
package com.bwssystems.HABridge.dao;
import com.bwssystems.HABridge.api.hue.DeviceState;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/*
* Object to handle the device configuration
*/
public class DeviceDescriptor{
private String id;
@SerializedName("id")
@Expose
private String id;
@SerializedName("uniqueid")
@Expose
private String uniqueid;
@SerializedName("name")
@Expose
private String name;
@SerializedName("mapId")
@Expose
private String mapId;
@SerializedName("mapType")
@Expose
private String mapType;
@SerializedName("deviceType")
@Expose
private String deviceType;
@SerializedName("targetDevice")
@Expose
private String targetDevice;
@SerializedName("offUrl")
@Expose
private String offUrl;
@SerializedName("dimUrl")
@Expose
private String dimUrl;
@SerializedName("onUrl")
@Expose
private String onUrl;
@SerializedName("colorUrl")
@Expose
private String colorUrl;
@SerializedName("headers")
@Expose
private String headers;
@SerializedName("httpVerb")
@Expose
private String httpVerb;
@SerializedName("contentType")
@Expose
private String contentType;
@SerializedName("contentBody")
@Expose
private String contentBody;
@SerializedName("contentBodyOff")
@Expose
private String contentBodyOff;
@SerializedName("contentBodyDim")
@Expose
private String contentBodyDim;
@SerializedName("inactive")
@Expose
private boolean inactive;
@SerializedName("noState")
@Expose
private boolean noState;
@SerializedName("offState")
@Expose
private boolean offState;
@SerializedName("requesterAddress")
@Expose
private String requesterAddress;
@SerializedName("description")
@Expose
private String description;
@SerializedName("comments")
@Expose
private String comments;
@SerializedName("deviceState")
@Expose
private DeviceState deviceState;
@SerializedName("onFirstDim")
@Expose
private boolean onFirstDim;
@SerializedName("onWhenDimPresent")
@Expose
private boolean onWhenDimPresent;
@SerializedName("lockDeviceId")
@Expose
private boolean lockDeviceId;
@SerializedName("startupActions")
@Expose
private String startupActions;
@SerializedName("dimNoOn")
@Expose
private boolean dimNoOn;
@SerializedName("dimOnColor")
@Expose
private boolean dimOnColor;
public String getName() {
return name;
}
@@ -21,7 +107,23 @@ public class DeviceDescriptor{
this.name = name;
}
public String getDeviceType() {
public String getMapId() {
return mapId;
}
public void setMapId(String mapId) {
this.mapId = mapId;
}
public String getMapType() {
return mapType;
}
public void setMapType(String mapType) {
this.mapType = mapType;
}
public String getDeviceType() {
return deviceType;
}
@@ -29,7 +131,15 @@ public class DeviceDescriptor{
this.deviceType = deviceType;
}
public String getOffUrl() {
public String getTargetDevice() {
return targetDevice;
}
public void setTargetDevice(String targetDevice) {
this.targetDevice = targetDevice;
}
public String getOffUrl() {
return offUrl;
}
@@ -37,7 +147,15 @@ public class DeviceDescriptor{
this.offUrl = offUrl;
}
public String getOnUrl() {
public String getDimUrl() {
return dimUrl;
}
public void setDimUrl(String dimUrl) {
this.dimUrl = dimUrl;
}
public String getOnUrl() {
return onUrl;
}
@@ -45,6 +163,14 @@ public class DeviceDescriptor{
this.onUrl = onUrl;
}
public String getColorUrl() {
return colorUrl;
}
public void setColorUrl(String colorUrl) {
this.colorUrl = colorUrl;
}
public String getId() {
return id;
}
@@ -53,6 +179,22 @@ public class DeviceDescriptor{
this.id = id;
}
public String getUniqueid() {
return uniqueid;
}
public void setUniqueid(String uniqueid) {
this.uniqueid = uniqueid;
}
public String getHeaders() {
return headers;
}
public void setHeaders(String headers) {
this.headers = headers;
}
public String getHttpVerb() {
return httpVerb;
}
@@ -84,6 +226,155 @@ public class DeviceDescriptor{
public void setContentBodyOff(String contentBodyOff) {
this.contentBodyOff = contentBodyOff;
}
}
public String getContentBodyDim() {
return contentBodyDim;
}
public void setContentBodyDim(String contentBodyDim) {
this.contentBodyDim = contentBodyDim;
}
public DeviceState getDeviceState() {
if(deviceState == null)
deviceState = DeviceState.createDeviceState(this.isColorDevice());
return deviceState;
}
public void setDeviceState(DeviceState deviceState) {
this.deviceState = deviceState;
}
public boolean isInactive() {
return inactive;
}
public void setInactive(boolean inactive) {
this.inactive = inactive;
}
public boolean isNoState() {
return noState;
}
public void setNoState(boolean noState) {
this.noState = noState;
}
public boolean isOffState() {
return offState;
}
public void setOffState(boolean offState) {
this.offState = offState;
}
public String getRequesterAddress() {
return requesterAddress;
}
public void setRequesterAddress(String requesterAddress) {
this.requesterAddress = requesterAddress;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
public boolean isOnFirstDim() {
return onFirstDim;
}
public void setOnFirstDim(boolean onFirstDim) {
this.onFirstDim = onFirstDim;
}
public boolean isOnWhenDimPresent() {
return onWhenDimPresent;
}
public void setOnWhenDimPresent(boolean onWhenDimPresent) {
this.onWhenDimPresent = onWhenDimPresent;
}
public boolean containsType(String aType) {
if(aType == null)
return false;
if(this.mapType != null && this.mapType.contains(aType))
return true;
if(this.deviceType != null && this.deviceType.contains(aType))
return true;
if(this.onUrl != null && this.onUrl.contains(aType))
return true;
if(this.dimUrl != null && this.dimUrl.contains(aType))
return true;
if(this.offUrl != null && this.offUrl.contains(aType))
return true;
if(this.colorUrl != null && this.colorUrl.contains(aType))
return true;
return false;
}
public boolean isColorDevice() {
boolean color = true;
if ((deviceType == null || !deviceType.trim().equals("passthru")) && (colorUrl == null || colorUrl.trim().equals(""))) {
color = false;
} else if (deviceType != null && deviceType.trim().equals("passthru")) {
if (deviceState != null && (deviceState.getColormode() == null || deviceState.getColormode().equals(""))) {
color = false;
}
}
return color;
}
public boolean isLockDeviceId() {
return lockDeviceId;
}
public void setLockDeviceId(boolean lockDeviceId) {
this.lockDeviceId = lockDeviceId;
}
public String getStartupActions() {
return startupActions;
}
public void setStartupActions(String startupActions) {
this.startupActions = startupActions;
}
public boolean isDimNoOn() {
return dimNoOn;
}
public void setDimNoOn(boolean dimNoOn) {
this.dimNoOn = dimNoOn;
}
public boolean isDimOnColor() {
return dimOnColor;
}
public void setDimOnColor(boolean dimOnColor) {
this.dimOnColor = dimOnColor;
}
}

View File

@@ -1,8 +1,6 @@
package com.bwssystems.HABridge.dao;
import java.io.IOException;
import java.io.StringReader;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -10,200 +8,356 @@ import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.JsonTransformer;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.google.gson.stream.JsonReader;
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;
import com.bwssystems.HABridge.util.JsonTransformer;
import com.bwssystems.HABridge.util.HexLibrary;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
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
* loading.
*/
public class DeviceRepository {
Map<String, DeviceDescriptor> devices;
Path repositoryPath;
final Random random = new Random();
final Logger log = LoggerFactory.getLogger(DeviceRepository.class);
public DeviceRepository(String deviceDb) {
public class DeviceRepository extends BackupHandler {
private Map<String, DeviceDescriptor> devices;
private Path repositoryPath;
private Gson gson;
private Integer nextId;
private Integer seedId;
private boolean uidnineoctets;
private Logger log = LoggerFactory.getLogger(DeviceRepository.class);
public DeviceRepository(String deviceDb, Integer seedid, boolean uidnineoctets_setting) {
super();
gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
repositoryPath = null;
repositoryPath = Paths.get(deviceDb);
String jsonContent = repositoryReader(repositoryPath);
setupParams(repositoryPath, ".bk", "device.db-");
nextId = seedid;
seedId = seedid;
uidnineoctets = uidnineoctets_setting;
_loadRepository(repositoryPath);
}
public void loadRepository() {
if (repositoryPath != null)
_loadRepository(repositoryPath);
}
private void _loadRepository(Path aPath) {
String jsonContent = repositoryReader(aPath);
devices = new HashMap<String, DeviceDescriptor>();
if(jsonContent != null)
{
List<DeviceDescriptor> list = readJsonStream(jsonContent);
ListIterator<DeviceDescriptor> theIterator = list.listIterator();
DeviceDescriptor theDevice = null;
while (theIterator.hasNext()) {
theDevice = theIterator.next();
put(theDevice.getId(), theDevice);
if (jsonContent != null) {
DeviceDescriptor list[] = gson.fromJson(jsonContent, DeviceDescriptor[].class);
for (int i = 0; i < list.length; i++) {
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());
}
}
nextId = nextId + 1;
}
}
public List<DeviceDescriptor> findAll() {
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
return list;
}
public List<DeviceDescriptor> findByDeviceType(String aType) {
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
public List<DeviceDescriptor> findActive() {
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>();
for (DeviceDescriptor aDevice : new ArrayList<DeviceDescriptor>(devices.values())) {
if (!aDevice.isInactive())
list.add(aDevice);
}
return list;
}
public DeviceDescriptor findOne(String id) {
return devices.get(id);
}
private void put(String id, DeviceDescriptor aDescriptor) {
devices.put(id, aDescriptor);
}
public void save(DeviceDescriptor aDescriptor) {
if(aDescriptor.getId() != null)
devices.remove(aDescriptor.getId());
else
aDescriptor.setId(String.valueOf(random.nextInt(Integer.MAX_VALUE)));
put(aDescriptor.getId(), aDescriptor);
JsonTransformer aRenderer = new JsonTransformer();
String jsonValue = aRenderer.render(findAll());
repositoryWriter(jsonValue, repositoryPath);
log.debug("Save device: " + aDescriptor.getName());
}
public String delete(DeviceDescriptor aDescriptor) {
if (aDescriptor != null) {
devices.remove(aDescriptor.getId());
JsonTransformer aRenderer = new JsonTransformer();
String jsonValue = aRenderer.render(findAll());
repositoryWriter(jsonValue, repositoryPath);
return "Device with id '" + aDescriptor.getId() + "' deleted";
} else {
return "Device not found";
}
}
public List<DeviceDescriptor> findAllByRequester(String anAddress) {
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
return findAllByRequester(anAddress, list);
}
private List<DeviceDescriptor> findAllByRequester(String anAddress, Collection<DeviceDescriptor> list) {
List<DeviceDescriptor> theReturnList = new ArrayList<DeviceDescriptor>();
Iterator<DeviceDescriptor> anIterator = list.iterator();
DeviceDescriptor theDevice;
String theRequesterAddress;
HashMap<String, String> addressMap;
while (anIterator.hasNext()) {
theDevice = anIterator.next();
theRequesterAddress = theDevice.getRequesterAddress();
addressMap = new HashMap<String, String>();
if (theRequesterAddress != null) {
if (theRequesterAddress.contains(",")) {
String[] theArray = theRequesterAddress.split(",");
for (String v : theArray) {
addressMap.put(v.trim(), v.trim());
}
} else
addressMap.put(theRequesterAddress, theRequesterAddress);
}
if (theRequesterAddress == null || theRequesterAddress.length() == 0 || addressMap.containsKey(anAddress))
theReturnList.add(theDevice);
}
return theReturnList;
}
public Map<String, DeviceResponse> findAllByGroupWithState(String[] lightsInGroup, String anAddress,
HueHome myHueHome, Gson aGsonBuilder) {
return findAllByGroupWithState(lightsInGroup, anAddress, myHueHome, aGsonBuilder, false);
}
public Map<String, DeviceResponse> findAllByGroupWithState(String[] lightsInGroup, String anAddress,
HueHome myHueHome, Gson aGsonBuilder, boolean ignoreAddress) {
Map<String, DeviceResponse> deviceResponseMap = new HashMap<String, DeviceResponse>();
Map<String, DeviceDescriptor> lights = new HashMap<String, DeviceDescriptor>(devices);
lights.keySet().retainAll(Arrays.asList(lightsInGroup));
for (DeviceDescriptor light : (ignoreAddress ? lights.values()
: findAllByRequester(anAddress, lights.values()))) {
DeviceResponse deviceResponse = null;
if (!light.isInactive()) {
if (light.containsType(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
CallItem[] callItems = null;
try {
if (light.getOnUrl() != null)
callItems = aGsonBuilder.fromJson(light.getOnUrl(), CallItem[].class);
} catch (JsonSyntaxException e) {
log.warn("Could not decode Json for url items to get Hue state for device: {}",
light.getName());
callItems = null;
}
for (int i = 0; callItems != null && i < callItems.length; i++) {
if ((callItems[i].getType() != null
&& callItems[i].getType().equals(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex]))
|| (callItems[i].getItem() != null && callItems[i].getItem().getAsString() != null
&& callItems[i].getItem().getAsString().contains("hueName"))) {
deviceResponse = myHueHome.getHueDeviceInfo(callItems[i], light);
i = callItems.length;
}
}
}
if (deviceResponse == null) {
deviceResponse = DeviceResponse.createResponse(light);
}
deviceResponseMap.put(light.getId(), deviceResponse);
}
}
return (deviceResponseMap.size() == 0) ? null : deviceResponseMap;
}
public DeviceDescriptor findOne(String id) {
return devices.get(id);
}
private void put(String id, DeviceDescriptor aDescriptor) {
devices.put(id, aDescriptor);
}
public void save(DeviceDescriptor[] descriptors) {
String theNames = "";
for (int i = 0; i < descriptors.length; i++) {
if (descriptors[i].getId() != null && descriptors[i].getId().length() > 0)
devices.remove(descriptors[i].getId());
else {
descriptors[i].setId(String.valueOf(nextId));
nextId++;
}
if (descriptors[i].getUniqueid() == null || descriptors[i].getUniqueid().length() == 0) {
descriptors[i].setUniqueid(hueUniqueId(Integer.valueOf(descriptors[i].getId())));
}
put(descriptors[i].getId(), descriptors[i]);
theNames = theNames + " " + descriptors[i].getName() + ", ";
}
String jsonValue = gson.toJson(findAll());
repositoryWriter(jsonValue, repositoryPath);
log.debug("Save device(s): {}", theNames);
}
public void renumber() {
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
Iterator<DeviceDescriptor> deviceIterator = list.iterator();
Map<String, DeviceDescriptor> newdevices = new HashMap<String, DeviceDescriptor>();
List<String> lockedIds = new ArrayList<String>();
DeviceDescriptor theDevice;
boolean findNext = true;
nextId = seedId;
while (deviceIterator.hasNext()) {
theDevice = deviceIterator.next();
if (theDevice.isLockDeviceId()) {
lockedIds.add(theDevice.getId());
}
}
log.debug("Renumber devices starting with: {}", nextId);
deviceIterator = list.iterator();
while (deviceIterator.hasNext()) {
theDevice = deviceIterator.next();
if (!theDevice.isLockDeviceId()) {
findNext = true;
while (findNext) {
if (lockedIds.contains(String.valueOf(nextId))) {
nextId++;
} else {
findNext = false;
}
}
theDevice.setId(String.valueOf(nextId));
theDevice.setUniqueid(hueUniqueId(nextId));
nextId++;
}
newdevices.put(theDevice.getId(), theDevice);
}
devices = newdevices;
String jsonValue = gson.toJson(findAll());
repositoryWriter(jsonValue, repositoryPath);
}
public String delete(DeviceDescriptor aDescriptor) {
if (aDescriptor != null) {
devices.remove(aDescriptor.getId());
JsonTransformer aRenderer = new JsonTransformer();
String jsonValue = aRenderer.render(findAll());
repositoryWriter(jsonValue, repositoryPath);
return "Device with id '" + aDescriptor.getId() + "' deleted";
} else {
return "Device not found";
}
}
private void repositoryWriter(String content, Path filePath) {
if(Files.exists(filePath) && !Files.isWritable(filePath)){
log.error("Error file is not writable: " + filePath);
if (Files.exists(filePath) && !Files.isWritable(filePath)) {
log.error("Error file is not writable: {}", filePath);
return;
}
if(Files.notExists(filePath.getParent())) {
if (Files.notExists(filePath.getParent())) {
try {
Files.createDirectories(filePath.getParent());
} catch (IOException e) {
log.error("Error creating the directory: " + filePath + " message: " + e.getMessage(), e);
log.error("Error creating the directory: {} message: {}", filePath, e.getMessage(), e);
}
}
try {
Path target = null;
if(Files.exists(filePath)) {
if (Files.exists(filePath)) {
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "device.db.old");
Files.move(filePath, target);
}
Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE);
if(target != null)
if (target != null)
Files.delete(target);
} catch (IOException e) {
log.error("Error writing the file: " + filePath + " message: " + e.getMessage(), e);
log.error("Error writing the file: {} message: {}", filePath, e.getMessage(), e);
}
}
private String repositoryReader(Path filePath) {
String content = null;
if(Files.notExists(filePath) || !Files.isReadable(filePath)){
log.warn("Error reading the file: " + filePath + " - Does not exist or is not readable. continuing...");
if (Files.notExists(filePath) || !Files.isReadable(filePath)) {
log.warn("Error reading the file: {} - Does not exist or is not readable. continuing...", filePath);
return null;
}
try {
content = new String(Files.readAllBytes(filePath));
} catch (IOException e) {
log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e);
log.error("Error reading the file: {} message: {}", filePath, e.getMessage(), e);
}
return content;
}
private List<DeviceDescriptor> readJsonStream(String context) {
JsonReader reader = new JsonReader(new StringReader(context));
List<DeviceDescriptor> theDescriptors = null;
private String hueUniqueId(Integer anId) {
String theUniqueId = null;
Integer newValue;
String hexValueLeft;
String hexValueRight;
MessageDigest md = null;
try {
theDescriptors = readDescriptorArray(reader);
} catch (IOException e) {
log.error("Error reading json array: " + context + " message: " + e.getMessage(), e);
} finally {
try {
reader.close();
} catch (IOException e) {
log.error("Error closing json reader: " + context + " message: " + e.getMessage(), e);
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();
if (uidnineoctets) {
theUniqueId = String.format("00:%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));
} else {
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));
}
}
return theDescriptors;
}
public List<DeviceDescriptor> readDescriptorArray(JsonReader reader) throws IOException {
List<DeviceDescriptor> descriptors = new ArrayList<DeviceDescriptor>();
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());
reader.beginArray();
while (reader.hasNext()) {
descriptors.add(readDescriptor(reader));
theUniqueId = String.format("11:22:33:44:55:66:%s-%s", hexValueLeft, hexValueRight).toUpperCase();
}
reader.endArray();
return descriptors;
}
public DeviceDescriptor readDescriptor(JsonReader reader) throws IOException {
DeviceDescriptor deviceEntry = new DeviceDescriptor();
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("id")) {
deviceEntry.setId(reader.nextString());
log.debug("Read a Device - device json id: " + deviceEntry.getId());
} else if (name.equals("name")) {
deviceEntry.setName(reader.nextString());
log.debug("Read a Device - device json name: " + deviceEntry.getName());
} else if (name.equals("deviceType")) {
deviceEntry.setDeviceType(reader.nextString());
log.debug("Read a Device - device json type:" + deviceEntry.getDeviceType());
} else if (name.equals("offUrl")) {
deviceEntry.setOffUrl(reader.nextString());
log.debug("Read a Device - device json off URL:" + deviceEntry.getOffUrl());
} else if (name.equals("onUrl")) {
deviceEntry.setOnUrl(reader.nextString());
log.debug("Read a Device - device json on URL:" + deviceEntry.getOnUrl());
} else if (name.equals("httpVerb")) {
deviceEntry.setHttpVerb(reader.nextString());
log.debug("Read a Device - device json httpVerb:" + deviceEntry.getHttpVerb());
} else if (name.equals("contentType")) {
deviceEntry.setContentType(reader.nextString());
log.debug("Read a Device - device json contentType:" + deviceEntry.getContentType());
} else if (name.equals("contentBody")) {
deviceEntry.setContentBody(reader.nextString());
log.debug("Read a Device - device json contentBody:" + deviceEntry.getContentBody());
} else if (name.equals("contentBodyOff")) {
deviceEntry.setContentBodyOff(reader.nextString());
log.debug("Read a Device - device json contentBodyOff:" + deviceEntry.getContentBodyOff());
} else {
reader.skipValue();
}
}
reader.endObject();
return deviceEntry;
return theUniqueId;
}
}

View File

@@ -0,0 +1,18 @@
package com.bwssystems.HABridge.dao;
public class ErrorMessage {
private String message;
public ErrorMessage(String message) {
super();
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@@ -0,0 +1,147 @@
package com.bwssystems.HABridge.dao;
import com.bwssystems.HABridge.api.hue.DeviceState;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.bwssystems.HABridge.api.hue.GroupState;
/*
* Object to handle the device configuration
*/
public class GroupDescriptor{
@SerializedName("id")
@Expose
private String id;
@SerializedName("name")
@Expose
private String name;
@SerializedName("groupType")
@Expose
private String groupType;
@SerializedName("groupClass")
@Expose
private String groupClass;
@SerializedName("requesterAddress")
@Expose
private String requesterAddress;
@SerializedName("inactive")
@Expose
private boolean inactive;
@SerializedName("description")
@Expose
private String description;
@SerializedName("comments")
@Expose
private String comments;
private DeviceState action;
private GroupState groupState;
@SerializedName("lights")
@Expose
private String[] lights;
@SerializedName("exposeAsLight")
@Expose
private String exposeAsLight;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGroupType() {
return groupType;
}
public void setGroupType(String groupType) {
this.groupType = groupType;
}
public String getGroupClass() {
return groupClass;
}
public void setGroupClass(String groupClass) {
this.groupClass = groupClass;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public GroupState getGroupState() {
if(groupState == null)
groupState = new GroupState(false,false);
return groupState;
}
public void setGroupState(GroupState groupState) {
this.groupState = groupState;
}
public DeviceState getAction() {
if(action == null)
action = DeviceState.createDeviceState(true);
return action;
}
public void setAction(DeviceState action) {
this.action = action;
}
public boolean isInactive() {
return inactive;
}
public void setInactive(boolean inactive) {
this.inactive = inactive;
}
public String getRequesterAddress() {
return requesterAddress;
}
public void setRequesterAddress(String requesterAddress) {
this.requesterAddress = requesterAddress;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
public String[] getLights() {
return lights;
}
public void setLights(String[] lights) {
this.lights = lights;
}
public void setExposeAsLight(String exposeFor) {
this.exposeAsLight = exposeFor;
}
public String getExposeAsLight() {
return exposeAsLight;
}
}

View File

@@ -0,0 +1,224 @@
package com.bwssystems.HABridge.dao;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.dao.GroupDescriptor;
import com.bwssystems.HABridge.util.BackupHandler;
import com.bwssystems.HABridge.util.JsonTransformer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.List;
/*
* This is an in memory list to manage the configured groups and saves the list as a JSON string to a file for later
* loading.
*/
public class GroupRepository extends BackupHandler {
private Map<String, GroupDescriptor> groups;
private Path repositoryPath;
private Gson gson;
private Integer nextId;
private Logger log = LoggerFactory.getLogger(GroupRepository.class);
public GroupRepository(String groupDb) {
super();
gson =
new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
nextId = 0;
try {
repositoryPath = null;
repositoryPath = Paths.get(groupDb);
setupParams(repositoryPath, ".bk", "group.db-");
_loadRepository(repositoryPath);
} catch (Exception ex) {
groups = new HashMap<String, GroupDescriptor>();
}
}
public void loadRepository() {
if(repositoryPath != null)
_loadRepository(repositoryPath);
}
private void _loadRepository(Path aPath){
String jsonContent = repositoryReader(aPath);
groups = new HashMap<String, GroupDescriptor>();
if(jsonContent != null)
{
GroupDescriptor list[] = gson.fromJson(jsonContent, GroupDescriptor[].class);
for(int i = 0; i < list.length; i++) {
list[i].setGroupState(null);
put(list[i].getId(), list[i]);
if(Integer.decode(list[i].getId()) > nextId) {
nextId = Integer.decode(list[i].getId());
}
}
}
}
public List<GroupDescriptor> findAll() {
List<GroupDescriptor> list = new ArrayList<GroupDescriptor>(groups.values());
return list;
}
public List<GroupDescriptor> findActive() {
List<GroupDescriptor> list = new ArrayList<GroupDescriptor>();
for(GroupDescriptor aGroup : new ArrayList<GroupDescriptor>(groups.values())) {
if(!aGroup.isInactive())
list.add(aGroup);
}
return list;
}
public List<GroupDescriptor> findAllByRequester(String anAddress) {
List<GroupDescriptor> list = new ArrayList<GroupDescriptor>(groups.values());
List<GroupDescriptor> theReturnList = new ArrayList<GroupDescriptor>();
Iterator<GroupDescriptor> anIterator = list.iterator();
GroupDescriptor theGroup;
String theRequesterAddress;
HashMap<String,String > addressMap;
while (anIterator.hasNext()) {
theGroup = anIterator.next();
theRequesterAddress = theGroup.getRequesterAddress();
addressMap = new HashMap<String, String>();
if(theRequesterAddress != null) {
if (theRequesterAddress.contains(",")) {
String[] theArray = theRequesterAddress.split(",");
for (String v : theArray) {
addressMap.put(v.trim(), v.trim());
}
} else
addressMap.put(theRequesterAddress, theRequesterAddress);
}
if (theRequesterAddress == null || theRequesterAddress.length() == 0 || addressMap.containsKey(anAddress))
theReturnList.add(theGroup);
}
return theReturnList;
}
public List<GroupDescriptor> findVirtualLights(String anAddress) {
List<GroupDescriptor> list = new ArrayList<GroupDescriptor>();
for (GroupDescriptor group : groups.values()) {
String expose = group.getExposeAsLight();
if (expose != null && expose.contains(anAddress)) {
list.add(group);
}
}
return list;
}
public GroupDescriptor findOne(String id) {
return groups.get(id);
}
private void put(String id, GroupDescriptor aDescriptor) {
groups.put(id, aDescriptor);
}
public void save() {
save(groups.values().toArray(new GroupDescriptor[0]));
}
public void save(GroupDescriptor[] descriptors) {
String theNames = "";
for(int i = 0; i < descriptors.length; i++) {
if(descriptors[i].getId() != null && descriptors[i].getId().length() > 0)
groups.remove(descriptors[i].getId());
else {
nextId++;
descriptors[i].setId(String.valueOf(nextId));
}
put(descriptors[i].getId(), descriptors[i]);
theNames = theNames + " " + descriptors[i].getName() + ", ";
}
String jsonValue = gson.toJson(findAll());
repositoryWriter(jsonValue, repositoryPath);
log.debug("Save group(s): " + theNames);
}
public Integer getNewId() {
return nextId + 1;
}
public String delete(GroupDescriptor aDescriptor) {
if (aDescriptor != null) {
groups.remove(aDescriptor.getId());
JsonTransformer aRenderer = new JsonTransformer();
String jsonValue = aRenderer.render(findAll());
repositoryWriter(jsonValue, repositoryPath);
return "Group with id '" + aDescriptor.getId() + "' deleted";
} else {
return "Group not found";
}
}
private void repositoryWriter(String content, Path filePath) {
if(Files.exists(filePath) && !Files.isWritable(filePath)){
log.error("Error file is not writable: " + filePath);
return;
}
if(Files.notExists(filePath.getParent())) {
try {
Files.createDirectories(filePath.getParent());
} catch (IOException e) {
log.error("Error creating the directory: " + filePath + " message: " + e.getMessage(), e);
}
}
try {
Path target = null;
if(Files.exists(filePath)) {
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "group.db.old");
Files.move(filePath, target);
}
Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE);
if(target != null)
Files.delete(target);
} catch (IOException e) {
log.error("Error writing the file: " + filePath + " message: " + e.getMessage(), e);
}
}
private String repositoryReader(Path filePath) {
String content = null;
if(Files.notExists(filePath)){
log.debug("Error, the file: " + filePath + " - does not exist. continuing...");
return null;
}
if(!Files.isReadable(filePath)){
log.warn("Error, the file: " + filePath + " - is not readable. continuing...");
return null;
}
try {
content = new String(Files.readAllBytes(filePath));
} catch (IOException e) {
log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e);
}
return content;
}
}

View File

@@ -1,8 +1,11 @@
package com.bwssystems.HABridge.devicemanagmeent;
import static spark.Spark.get;
import static spark.Spark.halt;
import static spark.Spark.options;
import static spark.Spark.post;
import static spark.Spark.put;
import static spark.Spark.before;
import static spark.Spark.delete;
import java.util.Arrays;
@@ -15,14 +18,19 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.JsonTransformer;
import com.bwssystems.HABridge.Version;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.HomeManager;
import com.bwssystems.HABridge.User;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.BackupFilename;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.dao.DeviceRepository;
import com.bwssystems.harmony.HarmonyHandler;
import com.bwssystems.luupRequests.Sdata;
import com.bwssystems.vera.VeraInfo;
import com.bwssystems.HABridge.dao.GroupRepository;
import com.bwssystems.HABridge.dao.ErrorMessage;
import com.bwssystems.HABridge.util.JsonTransformer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
/**
spark core server for bridge configuration
@@ -30,74 +38,150 @@ import com.google.gson.Gson;
public class DeviceResource {
private static final String API_CONTEXT = "/api/devices";
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
private DeviceRepository deviceRepository;
private VeraInfo veraInfo;
private Version version;
private HarmonyHandler myHarmonyHandler;
private GroupRepository groupRepository;
private HomeManager homeManager;
private BridgeSettings bridgeSettings;
private Gson aGsonHandler;
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
private String errorMessage;
public DeviceResource(BridgeSettings theSettings, Version theVersion, HarmonyHandler myHarmony) {
super();
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
this.veraInfo = new VeraInfo(theSettings.getVeraAddress(), theSettings.isValidVera());
this.myHarmonyHandler = myHarmony;
this.version = theVersion;
setupEndpoints();
public DeviceResource(BridgeSettings theSettings, HomeManager aHomeManager) {
bridgeSettings = theSettings;
this.deviceRepository = new DeviceRepository(bridgeSettings.getBridgeSettingsDescriptor().getUpnpDeviceDb(), bridgeSettings.getBridgeSettingsDescriptor().getSeedid(), bridgeSettings.getBridgeSettingsDescriptor().isUidnineoctets());
this.groupRepository = new GroupRepository(bridgeSettings.getBridgeSettingsDescriptor().getUpnpGroupDb());
homeManager = aHomeManager;
aGsonHandler = new GsonBuilder().create();
errorMessage = null;
setupEndpoints();
}
public DeviceRepository getDeviceRepository() {
return deviceRepository;
}
public GroupRepository getGroupRepository() {
return groupRepository;
}
private void setupEndpoints() {
log.info("HABridge device management service started.... ");
before(API_CONTEXT + "/*", (request, response) -> {
// This never gets called as the HueMulator class covers this path. This is here for backup
if(bridgeSettings.getBridgeSecurity().isSecure()) {
User authUser = bridgeSettings.getBridgeSecurity().getAuthenticatedUser(request);
if(authUser == null) {
halt(401, "{\"message\":\"User not authenticated\"}");
}
}
});
// http://ip_address:port/api/devices CORS request
options(API_CONTEXT, "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post(API_CONTEXT, "application/json", (request, response) -> {
log.debug("Create a Device - request body: " + request.body());
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
if(device.getContentBody() != null ) {
if (device.getContentType() == null || device.getHttpVerb() == null || !supportedVerbs.contains(device.getHttpVerb().toLowerCase())) {
device = null;
log.debug("Create a Device(s) - request body: {}", request.body());
DeviceDescriptor devices[];
if(request.body().substring(0,1).equalsIgnoreCase("[") == true) {
devices = new Gson().fromJson(request.body(), DeviceDescriptor[].class);
}
else {
devices = new Gson().fromJson("[" + request.body() + "]", DeviceDescriptor[].class);
}
@SuppressWarnings("unused")
CallItem[] callItems = null;
for(int i = 0; i < devices.length; i++) {
if(devices[i].getContentBody() != null ) {
if (devices[i].getContentType() == null || devices[i].getHttpVerb() == null || !supportedVerbs.contains(devices[i].getHttpVerb().toLowerCase())) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "Bad http verb in create device(s) for name: " + devices[i].getName() + " with verb: " + devices[i].getHttpVerb();
log.warn(errorMessage);
return new ErrorMessage(errorMessage);
}
}
try {
if(devices[i].getOnUrl() != null && !devices[i].getOnUrl().isEmpty())
callItems = aGsonHandler.fromJson(devices[i].getOnUrl(), CallItem[].class);
} catch(JsonSyntaxException e) {
response.status(HttpStatus.SC_BAD_REQUEST);
log.debug("Bad http verb in create a Device: " + request.body());
return device;
}
}
errorMessage = "Bad on URL JSON in create device(s) for name: " + devices[i].getName() + " with on URL: " + devices[i].getOnUrl();
log.warn(errorMessage);
return new ErrorMessage(errorMessage);
}
try {
if(devices[i].getDimUrl() != null && !devices[i].getDimUrl().isEmpty())
callItems = aGsonHandler.fromJson(devices[i].getDimUrl(), CallItem[].class);
} catch(JsonSyntaxException e) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "Bad dim URL JSON in create device(s) for name: " + devices[i].getName() + " with dim URL: " + devices[i].getDimUrl();
log.warn(errorMessage);
return new ErrorMessage(errorMessage);
}
try {
if(devices[i].getOffUrl() != null && !devices[i].getOffUrl().isEmpty())
callItems = aGsonHandler.fromJson(devices[i].getOffUrl(), CallItem[].class);
} catch(JsonSyntaxException e) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "Bad off URL JSON in create device(s) for name: " + devices[i].getName() + " with off URL: " + devices[i].getOffUrl();
log.warn(errorMessage);
return new ErrorMessage(errorMessage);
}
try {
if(devices[i].getColorUrl() != null && !devices[i].getColorUrl().isEmpty())
callItems = aGsonHandler.fromJson(devices[i].getColorUrl(), CallItem[].class);
} catch(JsonSyntaxException e) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "Bad color URL JSON in create device(s) for name: " + devices[i].getName() + " with color URL: " + devices[i].getColorUrl();
log.warn(errorMessage);
return new ErrorMessage(errorMessage);
}
}
deviceRepository.save(device);
log.debug("Created a Device: " + request.body());
deviceRepository.save(devices);
log.debug("Created a Device(s): {}", request.body());
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.status(HttpStatus.SC_CREATED);
return device;
return devices;
}, new JsonTransformer());
// http://ip_address:port/api/devices/:id CORS request
options(API_CONTEXT + "/:id", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (API_CONTEXT + "/:id", "application/json", (request, response) -> {
log.debug("Edit a Device - request body: " + request.body());
log.debug("Edit a Device - request body: {}", request.body());
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
DeviceDescriptor deviceEntry = deviceRepository.findOne(request.params(":id"));
if(deviceEntry == null){
log.debug("Could not save an edited Device Id: " + request.params(":id"));
if(deviceRepository.findOne(request.params(":id")) == null){
errorMessage = "Could not save an edited device, Device Id not found: " + request.params(":id");
log.warn(errorMessage);
response.status(HttpStatus.SC_BAD_REQUEST);
return new ErrorMessage(errorMessage);
}
else
{
log.debug("Saving an edited Device: " + deviceEntry.getName());
log.debug("Saving an edited Device: {}", device.getName());
deviceEntry.setName(device.getName());
if (device.getDeviceType() != null)
deviceEntry.setDeviceType(device.getDeviceType());
deviceEntry.setOnUrl(device.getOnUrl());
deviceEntry.setOffUrl(device.getOffUrl());
deviceEntry.setHttpVerb(device.getHttpVerb());
deviceEntry.setContentType(device.getContentType());
deviceEntry.setContentBody(device.getContentBody());
deviceEntry.setContentBodyOff(device.getContentBodyOff());
device.setDeviceType(device.getDeviceType());
deviceRepository.save(deviceEntry);
DeviceDescriptor[] theDevices = new DeviceDescriptor[1];
theDevices[0] = device;
deviceRepository.save(theDevices);
response.status(HttpStatus.SC_OK);
}
return deviceEntry;
return device;
}, new JsonTransformer());
get (API_CONTEXT, "application/json", (request, response) -> {
@@ -105,26 +189,35 @@ public class DeviceResource {
log.debug("Get all devices");
JsonTransformer aRenderer = new JsonTransformer();
String theStream = aRenderer.render(deviceList);
log.debug("The Device List: " + theStream);
log.debug("The Device List: {}", theStream);
response.status(HttpStatus.SC_OK);
return deviceList;
}, new JsonTransformer());
get (API_CONTEXT + "/:id", "application/json", (request, response) -> {
log.debug("Get a device");
log.debug("Get a device: {}", request.params(":id"));
DeviceDescriptor descriptor = deviceRepository.findOne(request.params(":id"));
if(descriptor == null)
if(descriptor == null) {
errorMessage = "Could not find, id: " + request.params(":id");
log.warn(errorMessage);
response.status(HttpStatus.SC_NOT_FOUND);
return new ErrorMessage(errorMessage);
}
else
response.status(HttpStatus.SC_OK);
return descriptor;
}, new JsonTransformer());
delete (API_CONTEXT + "/:id", "application/json", (request, response) -> {
log.debug("Delete a device");
DeviceDescriptor deleted = deviceRepository.findOne(request.params(":id"));
if(deleted == null)
String anId = request.params(":id");
log.debug("Delete a device: {}", anId);
DeviceDescriptor deleted = deviceRepository.findOne(anId);
if(deleted == null) {
errorMessage = "Could not delete, id: " + anId + " not found. ";
log.warn(errorMessage);
response.status(HttpStatus.SC_NOT_FOUND);
return new ErrorMessage(errorMessage);
}
else
{
deviceRepository.delete(deleted);
@@ -133,64 +226,255 @@ public class DeviceResource {
return null;
}, new JsonTransformer());
get (API_CONTEXT + "/habridge/version", "application/json", (request, response) -> {
log.debug("Get HA Bridge version: v" + version.getVersion());
response.status(HttpStatus.SC_OK);
return "{\"version\":\"" + version.getVersion() + "\"}";
});
get (API_CONTEXT + "/vera/devices", "application/json", (request, response) -> {
log.debug("Get vera devices");
Sdata sData = veraInfo.getSdata();
if(sData == null){
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return sData.getDevices();
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/vera/scenes", "application/json", (request, response) -> {
log.debug("Get vera scenes");
Sdata sData = veraInfo.getSdata();
if(sData == null){
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return sData.getScenes();
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.VERA_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.VERA_SCENE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/fibaro/devices", "application/json", (request, response) -> {
log.debug("Get fibaro devices");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/fibaro/scenes", "application/json", (request, response) -> {
log.debug("Get fibaro scenes");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.FIBARO_SCENE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/harmony/activities", "application/json", (request, response) -> {
log.debug("Get harmony activities");
if(myHarmonyHandler == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return myHarmonyHandler.getActivities();
return homeManager.findResource(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/harmony/show", "application/json", (request, response) -> {
log.debug("Get harmony current activity");
if(myHarmonyHandler == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return myHarmonyHandler.getCurrentActivity();
return homeManager.findResource(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex]).getItems("current_activity");
}, new JsonTransformer());
get (API_CONTEXT + "/harmony/devices", "application/json", (request, response) -> {
log.debug("Get harmony devices");
if(myHarmonyHandler == null) {
response.status(HttpStatus.SC_NOT_FOUND);
return null;
}
response.status(HttpStatus.SC_OK);
return myHarmonyHandler.getDevices();
return homeManager.findResource(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HARMONY_BUTTON[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/nest/items", "application/json", (request, response) -> {
log.debug("Get nest items");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.NEST_HOMEAWAY[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.NEST_HOMEAWAY[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/hue/devices", "application/json", (request, response) -> {
log.debug("Get hue items");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/hal/devices", "application/json", (request, response) -> {
log.debug("Get hal items");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.HAL_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HAL_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/mqtt/devices", "application/json", (request, response) -> {
log.debug("Get MQTT brokers");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.MQTT_MESSAGE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.MQTT_MESSAGE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/hass/devices", "application/json", (request, response) -> {
log.debug("Get HomeAssistant Clients");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/homewizard/devices", "application/json", (request, response) -> {
log.debug("Get HomeWizard Clients");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/domoticz/devices", "application/json", (request, response) -> {
log.debug("Get Domoticz Clients");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/lifx/devices", "application/json", (request, response) -> {
log.debug("Get LIFX devices");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.LIFX_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/somfy/devices", "application/json", (request, response) -> {
log.debug("Get somfy devices");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/openhab/devices", "application/json", (request, response) -> {
log.debug("Get OpenHAB devices");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/fhem/devices", "application/json", (request, response) -> {
log.debug("Get FHEM devices");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/broadlink/devices", "application/json", (request, response) -> {
log.debug("Get Broadlink devices");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.BROADLINK_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.BROADLINK_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/moziot/devices", "application/json", (request, response) -> {
log.debug("Get Mozilla IOT devices");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.MOZIOT_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.MOZIOT_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/homegenie/devices", "application/json", (request, response) -> {
log.debug("Get HomeGenie devices");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.HOMEGENIE_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HOMEGENIE_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/map/types", "application/json", (request, response) -> {
log.debug("Get map types");
return new DeviceMapTypes().getDeviceMapTypes();
}, new JsonTransformer());
get (API_CONTEXT + "/refresh/:typeIndex", "application/json", (request, response) -> {
String typeIndex = request.params(":typeIndex");
log.debug("Refresh Home: {}", typeIndex);
response.status(HttpStatus.SC_OK);
homeManager.findResource(typeIndex).refresh();
return null;
}, new JsonTransformer());
// http://ip_address:port/api/devices/exec/renumber CORS request
options(API_CONTEXT + "/exec/renumber", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "POST");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post (API_CONTEXT + "/exec/renumber", "application/json", (request, response) -> {
log.debug("Renumber devices.");
deviceRepository.renumber();
return null;
}, new JsonTransformer());
get (API_CONTEXT + "/backup/available", "application/json", (request, response) -> {
log.debug("Get backup filenames.");
response.status(HttpStatus.SC_OK);
return deviceRepository.getBackups();
}, new JsonTransformer());
// http://ip_address:port/api/devices/backup/download CORS request
options(API_CONTEXT + "/backup/download", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (API_CONTEXT + "/backup/download", "application/json", (request, response) -> {
log.debug("Create download: {}", request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
String backupContent = deviceRepository.downloadBackup(aFilename.getFilename());
return backupContent;
}, new JsonTransformer());
// http://ip_address:port/api/devices/backup/upload CORS request
options(API_CONTEXT + "/backup/upload/:filename", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (API_CONTEXT + "/backup/upload/:filename", "application/json", (request, response) -> {
log.debug("Create upload: {} - {}", request.params(":filename"), request.body());
String theSuccess = deviceRepository.uploadBackup(request.params(":filename"), request.body());
if(theSuccess.contains("Error:"))
response.status(HttpStatus.SC_METHOD_FAILURE);
else
response.status(HttpStatus.SC_OK);
return theSuccess;
}, new JsonTransformer());
// http://ip_address:port/api/devices/backup/create CORS request
options(API_CONTEXT + "/backup/create", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (API_CONTEXT + "/backup/create", "application/json", (request, response) -> {
log.debug("Create backup: {}", request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
BackupFilename returnFilename = new BackupFilename();
returnFilename.setFilename(deviceRepository.backup(aFilename.getFilename()));
return returnFilename;
}, new JsonTransformer());
// http://ip_address:port/api/devices/backup/delete CORS request
options(API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "POST");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post (API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
log.debug("Delete backup: {}", request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
if(aFilename != null)
deviceRepository.deleteBackup(aFilename.getFilename());
else
log.warn("No filename given for delete backup.");
return null;
}, new JsonTransformer());
// http://ip_address:port/api/devices/backup/restore CORS request
options(API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "POST");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post (API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
log.debug("Restore backup: {}", request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
if(aFilename != null) {
deviceRepository.restoreBackup(aFilename.getFilename());
deviceRepository.loadRepository();
}
else
log.warn("No filename given for restore backup.");
return null;
}, new JsonTransformer());
}
}

View File

@@ -0,0 +1,6 @@
package com.bwssystems.HABridge.devicemanagmeent;
public interface ResourceHandler {
public Object getItems(String type);
public void refresh();
}

View File

@@ -0,0 +1,200 @@
package com.bwssystems.HABridge.hue;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.lang3.Conversion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.java.dev.eval.Expression;
public class BrightnessDecode {
private static final Logger log = LoggerFactory.getLogger(BrightnessDecode.class);
private static final String INTENSITY_PERCENT = "${intensity.percent}";
private static final String INTENSITY_DECIMAL_PERCENT = "${intensity.decimal_percent}";
private static final String INTENSITY_BYTE = "${intensity.byte}";
private static final String INTENSITY_MATH = "${intensity.math(";
private static final String INTENSITY_MATH_VALUE = "X";
private static final String INTENSITY_MATH_CLOSE = ")}";
private static final String INTENSITY_MATH_CLOSE_HEX = ").hex}";
private static final String INTENSITY_PERCENT_HEX = "${intensity.percent.hex}";
private static final String INTENSITY_BYTE_HEX = "${intensity.byte.hex}";
private static final String INTENSITY_PREVIOUS_PERCENT = "${intensity.previous_percent}";
private static final String INTENSITY_PREVIOUS_DECIMAL_PERCENT = "${intensity.previous_decimal_percent}";
private static final String INTENSITY_PREVIOUS_BYTE = "${intensity.previous_byte}";
public static int calculateIntensity(int setIntensity, Integer targetBri, Integer targetBriInc) {
if (targetBri != null) {
setIntensity = targetBri;
} else if (targetBriInc != null) {
if ((setIntensity + targetBriInc) <= 1)
setIntensity = targetBriInc;
else if ((setIntensity + targetBriInc) > 254)
setIntensity = targetBriInc;
else
setIntensity = setIntensity + targetBriInc;
}
return setIntensity;
}
/*
* light weight templating here, was going to use free marker but it was a
* bit too heavy for what we were trying to do.
*
* currently provides: intensity.byte : 0-254 brightness. this is raw from
* the echo intensity.percent : 0-100, adjusted for the vera
* intensity.math(X*1) : where X is the value from the interface call and
* can use net.java.dev.eval math
*/
private static String replaceIntensityValue(String request, int previous_intensity, int intensity, boolean isHex) {
if (request == null) {
return null;
}
boolean notDone = true;
String replaceValue = null;
String replaceTarget = null;
int percentBrightness = 0;
float decimalBrightness = (float) 1.0;
int previousPercentBrightness = 0;
float previousDecimalBrightness = (float) 1.0;
Map<String, BigDecimal> variables = new HashMap<String, BigDecimal>();
String mathDescriptor = null;
if(intensity > 0) {
decimalBrightness = (float) (intensity / 255.0);
if(intensity > 0 && intensity < 5)
percentBrightness = 1;
else
percentBrightness = (int) Math.round(intensity / 255.0 * 100);
} else {
decimalBrightness = (float) 1.0;
percentBrightness = 1;
}
if(previous_intensity > 0) {
previousDecimalBrightness = (float) (previous_intensity / 255.0);
if(previous_intensity > 0 && previous_intensity < 5)
previousPercentBrightness = 1;
else
previousPercentBrightness = (int) Math.round(previous_intensity / 255.0 * 100);
} else {
previousDecimalBrightness = (float) 1.0;
previousPercentBrightness = 1;
}
while(notDone) {
notDone = false;
if (request.contains(INTENSITY_BYTE)) {
if (isHex) {
replaceValue = convertToHex(intensity);
} else {
replaceValue = String.valueOf(intensity);
}
replaceTarget = INTENSITY_BYTE;
notDone = true;
} else if (request.contains(INTENSITY_PREVIOUS_BYTE)) {
if (isHex) {
replaceValue = convertToHex(previous_intensity);
} else {
replaceValue = String.valueOf(previous_intensity);
}
replaceTarget = INTENSITY_PREVIOUS_BYTE;
notDone = true;
} else if (request.contains(INTENSITY_BYTE_HEX)) {
replaceValue = convertToHex(intensity);
replaceTarget = INTENSITY_BYTE_HEX;
notDone = true;
} else if (request.contains(INTENSITY_PERCENT)) {
if (isHex) {
replaceValue = convertToHex(percentBrightness);
} else {
replaceValue = String.valueOf(percentBrightness);
}
replaceTarget = INTENSITY_PERCENT;
notDone = true;
} else if (request.contains(INTENSITY_PREVIOUS_PERCENT)) {
if (isHex) {
replaceValue = convertToHex(previousPercentBrightness);
} else {
replaceValue = String.valueOf(previousPercentBrightness);
}
replaceTarget = INTENSITY_PREVIOUS_PERCENT;
notDone = true;
} else if (request.contains(INTENSITY_PERCENT_HEX)) {
replaceValue = convertToHex(percentBrightness);
replaceTarget = INTENSITY_PERCENT_HEX;
notDone = true;
} else if (request.contains(INTENSITY_DECIMAL_PERCENT)) {
replaceValue = String.format(Locale.ROOT, "%1.2f", decimalBrightness);
replaceTarget = INTENSITY_DECIMAL_PERCENT;
notDone = true;
} else if (request.contains(INTENSITY_PREVIOUS_DECIMAL_PERCENT)) {
replaceValue = String.format(Locale.ROOT, "%1.2f", previousDecimalBrightness);
replaceTarget = INTENSITY_PREVIOUS_DECIMAL_PERCENT;
notDone = true;
} else if (request.contains(INTENSITY_MATH_CLOSE)) {
mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(),
request.indexOf(INTENSITY_MATH_CLOSE));
variables.put(INTENSITY_MATH_VALUE, new BigDecimal(intensity));
log.debug("Math eval is: " + mathDescriptor + ", Where " + INTENSITY_MATH_VALUE + " is: "
+ String.valueOf(intensity));
Integer endResult = calculateMath(variables, mathDescriptor);
if(endResult != null) {
if (isHex) {
replaceValue = convertToHex(endResult);
} else {
replaceValue = endResult.toString();
}
replaceTarget = INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE;
notDone = true;
}
} else if (request.contains(INTENSITY_MATH_CLOSE_HEX)) {
mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(),
request.indexOf(INTENSITY_MATH_CLOSE_HEX));
variables.put(INTENSITY_MATH_VALUE, new BigDecimal(intensity));
Integer endResult = calculateMath(variables, mathDescriptor);
if(endResult != null) {
replaceValue = convertToHex(endResult);
replaceTarget = INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE_HEX;
notDone = true;
}
}
if(notDone)
request = request.replace(replaceTarget, replaceValue);
}
return request;
}
// Helper Method
public static String calculateReplaceIntensityValue(String request, int theIntensity, Integer targetBri, Integer targetBriInc, boolean isHex) {
return replaceIntensityValue(request, theIntensity, calculateIntensity(theIntensity, targetBri, targetBriInc), isHex);
}
// Apache Commons Conversion utils likes little endian too much
private static String convertToHex(int theValue) {
String destHex = "00";
String hexValue = Conversion.intToHex(theValue, 0, destHex, 0, 2);
byte[] theBytes = hexValue.getBytes();
byte[] newBytes = new byte[2];
newBytes[0] = theBytes[1];
newBytes[1] = theBytes[0];
return new String(newBytes);
}
private static Integer calculateMath(Map<String, BigDecimal> variables, String mathDescriptor) {
Integer endResult = null;
try {
Expression exp = new Expression(mathDescriptor);
BigDecimal result = exp.eval(variables);
endResult = Math.round(result.floatValue());
} catch (Exception e) {
log.warn("Could not execute Math: " + mathDescriptor, e);
endResult = null;
}
return endResult;
}
}

View File

@@ -0,0 +1,954 @@
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};
private final static double EPSILON = 0.00001;
// 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 float[] 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;
//saturationBRGBtoHSV(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 -> HSL.
* @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 HSL color space.
*/
public static float[] RGBtoHSL(int red, int green, int blue){
float[] hsl = new float[3];
double r = red;
double g = green;
double b = blue;
double max = Math.max(r,Math.max(g,b));
double min = Math.min(r,Math.min(g,b));
// double delta = max - min;
//HSK
Double h = 0d;
Double s = 0d;
Double l = 0d;
//saturation
double cnt = (max + min) / 2d;
if (cnt <= 127d) {
s = ((max - min) / (max + min));
}
else {
s = ((max - min) / (510d - max - min));
}
//lightness
l = ((max + min) / 2d) / 255d;
//hue
if (Math.abs(max - min) <= EPSILON) {
h = 0d;
s = 0d;
}
else {
double diff = max - min;
if (Math.abs(max - r) <= EPSILON) {
h = 60d * (g - b) / diff;
}
else if (Math.abs(max - g) <= EPSILON) {
h = 60d * (b - r) / diff + 120d;
}
else {
h = 60d * (r - g) / diff + 240d;
}
if (h < 0d) {
h += 360d;
}
}
hsl[0] = h.floatValue();
hsl[1] = s.floatValue();
hsl[2] = l.floatValue();
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];
/* Old Way
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]);
*/
// New Way
xyz[0] = xy.getXy()[0] * (xy.getBrightnessAdjusted() / xy.getXy()[1]) ;
xyz[1] = xy.getBrightnessAdjusted();
xyz[2] = (float) ((1.0 - xy.getXy()[0] - xy.getXy()[1]) * (xy.getBrightnessAdjusted() / 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

@@ -0,0 +1,28 @@
package com.bwssystems.HABridge.hue;
public class ColorData {
public enum ColorMode { XY, CT, HS}
private ColorMode mode;
private Object data;
public ColorData(ColorMode mode, Object value) {
this.mode = mode;
this.data = value;
}
public Object getData() {
return data;
}
public ColorMode getColorMode() {
return mode;
}
public String toString() {
String formatString;
formatString = "Color Data mode: " + mode + ", data: " + data;
return formatString;
}
}

View File

@@ -0,0 +1,492 @@
package com.bwssystems.HABridge.hue;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// import java.awt.Color;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// import com.bwssystems.HABridge.hue.ColorData;
public class ColorDecode {
private static final Logger log = LoggerFactory.getLogger(ColorDecode.class);
private static final String COLOR_R = "${color.r}";
private static final String COLOR_G = "${color.g}";
private static final String COLOR_B = "${color.b}";
private static final String COLOR_RX = "${color.rx}";
private static final String COLOR_GX = "${color.gx}";
private static final String COLOR_BX = "${color.bx}";
private static final String COLOR_RGBX = "${color.rgbx}";
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])\\}");
/* This is supersceded by the next iteration function below this original one
public static List<Integer> convertHSBtoRGBOrig(HueSatBri hsb) {
List<Integer> rgb;
Float hue = (Float)(hsb.getHue()*1.0f);
Float saturation = (Float)(hsb.getSat()*1.0f);
Float brightness = (Float)(hsb.getBri()*1.0f);
log.info("Hue = " + hue + ", Sat = " + saturation + ", Bri = " + brightness);
//Convert Hue into degrees for HSB
// hue = hue / 182.04f;
hue = (hue / 65535.0f) * 360.0f;
//Bri and Sat must be values from 0-1 (~percentage)
// ightness = brightness / 255.0f;
// saturation = saturation / 255.0f;
brightness = brightness / 254.0f;
saturation = saturation / 254.0f;
Float r = 0f;
Float g = 0f;
Float b = 0f;
if(brightness > 0.0f) {
if (saturation == 0)
{
r = g = b = brightness;
}
else
{
// 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;
}
}
}
//Check if any value is out of byte range
if (r < 0f)
{
r = 0f;
}
if (g < 0f)
{
g = 0f;
}
if (b < 0f)
{
b = 0f;
}
rgb = new ArrayList<Integer>();
rgb.add((int)Math.round(r*255));
rgb.add((int)Math.round(g*255));
rgb.add((int)Math.round(b*255));
log.info("Color change with HSB: " + hsb + ". Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " "
+ rgb.get(2));
int theRGB = Color.HSBtoRGB(hue, saturation, brightness);
Color decodedRGB = new Color(theRGB);
log.info("Color change with HSB using java Color: " + hsb + ". Resulting RGB Values: " + decodedRGB.getRed() + " " + decodedRGB.getGreen() + " "
+ decodedRGB.getBlue());
return rgb;
}
*/
public static List<Integer> convertHSBtoRGB(HueSatBri hsb) {
List<Integer> rgb;
Float hue = (Float)(hsb.getHue()*1.0f);
Float saturation = (Float)(hsb.getSat()*1.0f);
Float brightness = (Float)(hsb.getBri()*1.0f);
log.info("Hue = " + hue + ", Sat = " + saturation + ", Bri = " + brightness);
//Convert Hue into degrees for HSB
// hue = hue / 182.04f;
hue = (hue / 65535.0f);
//Bri and Sat must be values from 0-1 (~percentage)
// ightness = brightness / 255.0f;
// saturation = saturation / 255.0f;
brightness = brightness / 254.0f;
saturation = saturation / 254.0f;
Float r = 0f;
Float g = 0f;
Float b = 0f;
Float temp2 = 0f;
Float temp1 = 0f;
if(brightness > 0.0f) {
if (saturation == 0)
{
r = g = b = brightness;
}
else
{
temp2 = (brightness < 0.5f) ? brightness * (1.0f + saturation) : brightness + saturation - (brightness * saturation);
temp1 = 2.0f * brightness - temp2;
r = GetColorComponent(temp1, temp2, hue + 1.0f/3.0f);
g = GetColorComponent(temp1, temp2, hue);
b = GetColorComponent(temp1, temp2, hue - 1.0f/3.0f);
}
}
//Check if any value is out of byte range
if (r < 0f)
{
r = 0f;
}
if (g < 0f)
{
g = 0f;
}
if (b < 0f)
{
b = 0f;
}
rgb = new ArrayList<Integer>();
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 HSB New: " + hsb + ". Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " "
+ rgb.get(2));
return rgb;
}
private static Float GetColorComponent(Float temp1, Float temp2, Float temp3)
{
temp3 = MoveIntoRange(temp3);
if (temp3 < 1.0f/6.0f)
{
return temp1 + (temp2 - temp1) * 6.0f * temp3;
}
if (temp3 < 0.5f)
{
return temp2;
}
if (temp3 < 2.0f/3.0f)
{
return temp1 + ((temp2 - temp1) * ((2.0f/3.0f) - temp3) * 6.0f);
}
return temp1;
}
private static Float MoveIntoRange(Float temp3)
{
if (temp3 < 0.0f) return temp3 + 1f;
if (temp3 > 1.0f) return temp3 - 1f;
return temp3;
}
public static List<Integer> convertCIEtoRGB(List<Double> xy, int brightness) {
List<Integer> rgb;
XYColorSpace xyColor = new XYColorSpace();
xyColor.setBrightness(brightness);
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[] rgbInt = ColorConverter.normalizeRGB(ColorConverter.XYZtoRGB(xyz[0], xyz[1], xyz[2]));
rgb = new ArrayList<Integer>();
rgb.add(rgbInt[0]);
rgb.add(rgbInt[1]);
rgb.add(rgbInt[2]);
log.debug("Color change with XY: " + xy.get(0) + " " + xy.get(1) + " " + brightness + " 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/
public static List<Integer> convertCTtoRGB(Integer ct) {
double temperature = 1000000.0 / (double) ct;
temperature /= 100;
double r, g, b;
if (temperature <= 66) {
r = 255;
g = temperature;
g = 99.4708025861 * Math.log(g) - 161.1195681661;
} else {
r = temperature - 60;
r = 329.698727446 * (Math.pow(r, -0.1332047592));
g = temperature - 60;
g = 288.1221695283 * (Math.pow(g, -0.0755148492));
}
if (temperature >= 66) {
b = 255;
} else {
if (temperature <= 19) {
b = 0;
} else {
b = temperature - 10;
b = 138.5177312231 * Math.log(b) - 305.0447927307;
}
}
r = assureBounds(r);
g = assureBounds(g);
b = assureBounds(b);
List<Integer> rgb = new ArrayList<Integer>();
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.0;
}
if (value > 255.0) {
value = 255.0;
}
return value;
}
@SuppressWarnings("unchecked")
public static String replaceColorData(String request, ColorData colorData, int setIntensity, boolean isHex) {
if (request == null) {
return null;
}
if (colorData == null) {
return request;
}
boolean notDone = true;
ColorData.ColorMode colorMode = colorData.getColorMode();
List<Integer> rgb = null;
if (colorMode == ColorData.ColorMode.XY) {
rgb = convertCIEtoRGB((List<Double>) colorData.getData(), setIntensity);
} else if (colorMode == ColorData.ColorMode.CT) {
rgb = convertCTtoRGB((Integer) colorData.getData());
} else if (colorMode == ColorData.ColorMode.HS) {
rgb = convertHSBtoRGB((HueSatBri) colorData.getData());
}
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)));
notDone = true;
}
if (request.contains(COLOR_G)) {
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)));
notDone = true;
}
if (request.contains(COLOR_RX)) {
request = request.replace(COLOR_RX, String.format("%02X", rgb.get(0)));
notDone = true;
}
if (request.contains(COLOR_GX)) {
request = request.replace(COLOR_GX, String.format("%02X", rgb.get(1)));
notDone = true;
}
if (request.contains(COLOR_BX)) {
request = request.replace(COLOR_BX, String.format("%02X", rgb.get(2)));
notDone = true;
}
if (request.contains(COLOR_RGBX)) {
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_XY)) {
if (colorMode == ColorData.ColorMode.XY) {
List<Double> xyData = (List<Double>) colorData.getData();
request = request.replace(COLOR_XY, String.format("%f,%f", xyData.get(0), xyData.get(1)));
} else {
float[] xyz = ColorConverter.RGBtoXYZ(rgb.get(0), rgb.get(1), rgb.get(2));
XYColorSpace theXYcolor = ColorConverter.XYZtoXY(xyz[0], xyz[1], xyz[2]);
request = request.replace(COLOR_XY, String.format("%f,%f",theXYcolor.getXy()[0], theXYcolor.getXy()[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;
hsb = ColorConverter.RGBtoHSL(rgb.get(0), rgb.get(1), rgb.get(2));
float hue = hsb[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;
hsb = ColorConverter.RGBtoHSL(rgb.get(0), rgb.get(1), rgb.get(2));
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("%d", 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];
hsb = ColorConverter.RGBtoHSL(rgb.get(0), rgb.get(1), rgb.get(2));
float hue = hsb[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;
}
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);
}
log.debug("Request <<" + request + ">>, not done: " + notDone);
}
return request;
}
private static String getMilightV5FromRgb(List<Integer> 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;
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")
public static int getIntRGB(ColorData colorData, int setIntensity) {
ColorData.ColorMode colorMode = colorData.getColorMode();
List<Integer> rgb = null;
if (colorMode == ColorData.ColorMode.XY) {
rgb = convertCIEtoRGB((List<Double>) colorData.getData(), setIntensity);
} else if (colorMode == ColorData.ColorMode.CT) {
rgb = convertCTtoRGB((Integer) colorData.getData());
}
int rgbIntVal = Integer.parseInt(String.format("%02X%02X%02X", rgb.get(0), rgb.get(1), rgb.get(2)), 16);
log.debug("Convert RGB to int. Result: " + rgbIntVal + " RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " "
+ rgb.get(2));
return rgbIntVal;
}
}

View File

@@ -0,0 +1,75 @@
package com.bwssystems.HABridge.hue;
public class ColorMap {
private double red;
private double green;
private double blue;
private long R;
private long G;
private long B;
private double X;
private double Y;
private Double Z;
private double z;
public double getRed() {
return red;
}
public void setRed(double red) {
this.red = red;
}
public double getGreen() {
return green;
}
public void setGreen(double green) {
this.green = green;
}
public double getBlue() {
return blue;
}
public void setBlue(double blue) {
this.blue = blue;
}
public long getR() {
return R;
}
public void setR(long r) {
R = r;
}
public long getG() {
return G;
}
public void setG(long g) {
G = g;
}
public long getB() {
return B;
}
public void setB(long b) {
B = b;
}
public double getX() {
return X;
}
public void setX(double x) {
X = x;
}
public double getY() {
return Y;
}
public void setY(double y) {
Y = y;
}
public Double getZ() {
return Z;
}
public void setZ(Double z) {
Z = z;
}
public double getz() {
return z;
}
public void setz(double z) {
this.z = z;
}
}

View File

@@ -0,0 +1,84 @@
package com.bwssystems.HABridge.hue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
public class DeviceDataDecode {
private static final Logger log = LoggerFactory.getLogger(DeviceDataDecode.class);
private static final String DEVICE_ID = "${device.id}";
private static final String DEVICE_UNIQUEID = "${device.uniqueid}";
private static final String DEVICE_NAME = "${device.name}";
private static final String DEVICE_MAPID = "${device.mapId}";
private static final String DEVICE_MAPTYPE = "${device.mapType}";
private static final String DEVICE_DEVICETYPE = "${device.deviceType}";
private static final String DEVICE_TARGETDEVICE = "${device.targetDevice}";
private static final String DEVICE_REQUESTERADDRESS = "${device.requesterAddress}";
private static final String DEVICE_DESCRIPTION = "${device.description}";
private static final String DEVICE_COMMENTS = "${device.comments}";
public static String replaceDeviceData(String request, DeviceDescriptor device) {
if (request == null) {
return null;
}
boolean notDone = true;
while(notDone) {
notDone = false;
if (request.contains(DEVICE_ID)) {
request = request.replace(DEVICE_ID, device.getId());
notDone = true;
}
if (request.contains(DEVICE_UNIQUEID)) {
request = request.replace(DEVICE_UNIQUEID, device.getUniqueid());
notDone = true;
}
if (request.contains(DEVICE_NAME)) {
request = request.replace(DEVICE_NAME, device.getName());
notDone = true;
}
if (request.contains(DEVICE_MAPID)) {
request = request.replace(DEVICE_MAPID, device.getMapId());
notDone = true;
}
if (request.contains(DEVICE_MAPTYPE)) {
request = request.replace(DEVICE_MAPTYPE, device.getMapType());
notDone = true;
}
if (request.contains(DEVICE_DEVICETYPE)) {
request = request.replace(DEVICE_DEVICETYPE, device.getDeviceType());
notDone = true;
}
if (request.contains(DEVICE_TARGETDEVICE)) {
request = request.replace(DEVICE_TARGETDEVICE, device.getTargetDevice());
notDone = true;
}
if (request.contains(DEVICE_REQUESTERADDRESS)) {
request = request.replace(DEVICE_REQUESTERADDRESS, device.getRequesterAddress());
notDone = true;
}
if (request.contains(DEVICE_DESCRIPTION)) {
request = request.replace(DEVICE_DESCRIPTION, device.getDescription());
notDone = true;
}
if (request.contains(DEVICE_COMMENTS)) {
request = request.replace(DEVICE_COMMENTS, device.getComments());
notDone = true;
}
log.debug("Request <<" + request + ">>, not done: " + notDone);
}
return request;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
package com.bwssystems.HABridge.hue;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.ColorData;
public interface HueMulatorHandler {
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body);
}

View File

@@ -0,0 +1,38 @@
package com.bwssystems.HABridge.hue;
public class HueSatBri {
int hue;
int sat;
int bri;
public int getHue() {
return hue;
}
public void setHue(int hue) {
this.hue = hue;
}
public int getSat() {
return sat;
}
public void setSat(int sat) {
this.sat = sat;
}
public int getBri() {
return bri;
}
public void setBri(int bri) {
this.bri = bri;
}
public String toString() {
String formatString = new String();
formatString = "Hue: " + Integer.toString(hue) + ", Sat: " + Integer.toString(sat) + ", Bri: " + Integer.toString(bri);
return formatString;
}
}

View File

@@ -0,0 +1,31 @@
package com.bwssystems.HABridge.hue;
public class MultiCommandUtil {
private Integer setCount;
private Integer theDelay;
private Integer delayDefault;
public Integer getSetCount() {
return setCount;
}
public void setSetCount(Integer setCount) {
this.setCount = setCount;
}
public Integer getTheDelay() {
return theDelay;
}
public void setTheDelay(Integer theDelay) {
this.theDelay = theDelay;
}
public Integer getDelayDefault() {
return delayDefault;
}
public void setDelayDefault(Integer delayDefault) {
this.delayDefault = delayDefault;
}
}

View File

@@ -0,0 +1,49 @@
package com.bwssystems.HABridge.hue;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TimeDecode {
private static final Logger log = LoggerFactory.getLogger(TimeDecode.class);
private static final String TIME_FORMAT = "${time.format(";
private static final String TIMESTAMP = "${time.millis}";
private static final String TIME_FORMAT_CLOSE = ")}";
/*
* light weight templating here, was going to use free marker but it was a
* bit too heavy for what we were trying to do.
*
* currently provides: time format using Java DateTimeFormatter options
*/
public static String replaceTimeValue(String request) {
if (request == null) {
return null;
}
boolean notDone = true;
while(notDone) {
notDone = false;
if (request.contains(TIME_FORMAT)) {
String timeFormatDescriptor = request.substring(request.indexOf(TIME_FORMAT) + TIME_FORMAT.length(),
request.indexOf(TIME_FORMAT_CLOSE));
try {
log.debug("Time eval is: " + timeFormatDescriptor);
SimpleDateFormat dateFormat = new SimpleDateFormat(timeFormatDescriptor);
request = request.replace(TIME_FORMAT + timeFormatDescriptor + TIME_FORMAT_CLOSE, dateFormat.format(new Date()));
notDone = true;
} catch (Exception e) {
log.warn("Could not format current time: " + timeFormatDescriptor, e);
}
}
if (request.contains(TIMESTAMP)) {
request = request.replace(TIMESTAMP, String.valueOf(System.currentTimeMillis()));
notDone = true;
}
}
return request;
}
}

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

@@ -0,0 +1,206 @@
package com.bwssystems.HABridge.plugins.NestBridge;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.ColorData;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.nest.controller.Home;
import com.bwssystems.nest.controller.Nest;
import com.bwssystems.nest.controller.NestSession;
import com.bwssystems.nest.controller.Thermostat;
import com.bwssystems.nest.protocol.error.LoginException;
import com.bwssystems.nest.protocol.status.WhereDetail;
import com.bwssystems.nest.protocol.status.WhereItem;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class NestHome implements com.bwssystems.HABridge.Home {
private static final Logger log = LoggerFactory.getLogger(NestHome.class);
private NestSession theSession;
private Nest theNest;
private ArrayList<NestItem> nestItems;
private Gson aGsonHandler;
private Boolean isFarenheit;
private Boolean validNest;
private boolean closed;
public NestHome(BridgeSettings bridgeSettings) {
super();
closed = true;
createHome(bridgeSettings);
closed = false;
}
@Override
public Object getItems(String type) {
if(!validNest)
return null;
if(nestItems == null) {
nestItems = new ArrayList<NestItem>();
Set<String> homeNames = theNest.getHomeNames();
Home aHome = null;
NestItem anItem = null;
for(String name : homeNames) {
aHome = theNest.getHome(name);
anItem = new NestItem();
anItem.setId(name);
anItem.setName(aHome.getDetail().getName());
anItem.setType("Home");
anItem.setLocation(aHome.getDetail().getLocation());
nestItems.add(anItem);
}
Thermostat thermo = null;
Set<String> thermoNames = theNest.getThermostatNames();
for(String name : thermoNames) {
thermo = theNest.getThermostat(name);
anItem = new NestItem();
anItem.setId(name);
anItem.setType("Thermostat");
String where = null;
String homeName= null;
Boolean found = false;
for(String aHomeName : homeNames) {
WhereDetail aDetail = theNest.getWhere(aHomeName);
ListIterator<WhereItem> anIterator = aDetail.getWheres().listIterator();
while(anIterator.hasNext()) {
WhereItem aWhereItem = (WhereItem) anIterator.next();
if(aWhereItem.getWhereId().equals(thermo.getDeviceDetail().getWhereId())) {
where = aWhereItem.getName();
homeName = theNest.getHome(aHomeName).getDetail().getName();
found = true;
break;
}
}
if(found)
break;
}
anItem.setName(where + "(" + name.substring(name.length() - 4) + ")");
anItem.setLocation(where + " - " + homeName);
nestItems.add(anItem);
}
}
return nestItems;
}
@Override
public void closeHome() {
log.debug("Closing Home.");
if(closed) {
log.debug("Home is already closed....");
return;
}
if(theSession != null) {
theNest.endNestSession();
}
theNest = null;
theSession = null;
nestItems = null;
closed = true;
}
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
String responseString = null;
log.debug("executing HUE api request to set away for nest " + anItem.getType() + ": " + anItem.getItem().toString());
if(!validNest) {
log.warn("Should not get here, no Nest available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no Nest available\", \"parameter\": \"/lights/"
+ lightId + "/state\"}}]";
} else if (anItem.getType() != null && anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.NEST_HOMEAWAY[DeviceMapTypes.typeIndex])) {
NestInstruction homeAway = null;
if(anItem.getItem().isJsonObject())
homeAway = aGsonHandler.fromJson(anItem.getItem(), NestInstruction.class);
else
homeAway = aGsonHandler.fromJson(anItem.getItem().getAsString().replaceAll("^\"|\"$", ""), NestInstruction.class);
theNest.getHome(homeAway.getName()).setAway(homeAway.getAway());
} else if (anItem.getType() != null && anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.NEST_THERMO_SET[DeviceMapTypes.typeIndex])) {
NestInstruction thermoSetting = null;
if(anItem.getItem().isJsonObject())
thermoSetting = aGsonHandler.fromJson(anItem.getItem(), NestInstruction.class);
else
thermoSetting = aGsonHandler.fromJson(anItem.getItem().getAsString(), NestInstruction.class);
if (thermoSetting.getControl().equalsIgnoreCase("temp")) {
if (targetBri != null) {
if (isFarenheit)
thermoSetting
.setTemp(
String.valueOf((Double
.parseDouble(BrightnessDecode.calculateReplaceIntensityValue(thermoSetting.getTemp(),
intensity, targetBri, targetBriInc, false)) - 32.0) / 1.8));
else
thermoSetting
.setTemp(
String.valueOf(Double.parseDouble(BrightnessDecode.calculateReplaceIntensityValue(thermoSetting.getTemp(),
intensity, targetBri, targetBriInc, false))));
log.debug("Setting thermostat: " + thermoSetting.getName() + " to "
+ thermoSetting.getTemp() + "C");
theNest.getThermostat(thermoSetting.getName())
.setTargetTemperature(Float.parseFloat(thermoSetting.getTemp()));
}
} else if (thermoSetting.getControl().contains("range")
|| thermoSetting.getControl().contains("heat")
|| thermoSetting.getControl().contains("cool")
|| thermoSetting.getControl().contains("off")) {
log.debug("Setting thermostat target type: " + thermoSetting.getName() + " to "
+ thermoSetting.getControl());
theNest.getThermostat(thermoSetting.getName()).setTargetType(thermoSetting.getControl());
} else if (thermoSetting.getControl().contains("fan")) {
log.debug("Setting thermostat fan mode: " + thermoSetting.getName() + " to "
+ thermoSetting.getControl().substring(4));
theNest.getThermostat(thermoSetting.getName())
.setFanMode(thermoSetting.getControl().substring(4));
} else {
log.warn("no valid Nest control info: " + thermoSetting.getControl());
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"no valid Nest control info\", \"parameter\": \"/lights/"
+ lightId + "/state\"}}]";
}
}
return responseString;
}
@Override
public com.bwssystems.HABridge.Home createHome(BridgeSettings bridgeSettings) {
theSession = null;
theNest = null;
nestItems = null;
validNest = bridgeSettings.getBridgeSettingsDescriptor().isValidNest();
aGsonHandler = null;
log.info("Nest Home created." + (validNest ? "" : " No Nest configured."));
if(validNest) {
aGsonHandler = new GsonBuilder().create();
isFarenheit = bridgeSettings.getBridgeSettingsDescriptor().isFarenheit();
try {
theSession = new NestSession(bridgeSettings.getBridgeSettingsDescriptor().getNestuser(), bridgeSettings.getBridgeSettingsDescriptor().getNestpwd());
theNest = new Nest(theSession);
} catch (LoginException e) {
log.error("Caught Login Exception, setting Nest to invalid....");
validNest = false;
theSession = null;
}
}
return this;
}
@Override
public void refresh() {
// noop
}
}

View File

@@ -0,0 +1,33 @@
package com.bwssystems.HABridge.plugins.NestBridge;
public class NestInstruction {
private String name;
private Boolean away;
private String control;
private String temp;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getAway() {
return away;
}
public void setAway(Boolean away) {
this.away = away;
}
public String getControl() {
return control;
}
public void setControl(String control) {
this.control = control;
}
public String getTemp() {
return temp;
}
public void setTemp(String temp) {
this.temp = temp;
}
}

View File

@@ -0,0 +1,41 @@
package com.bwssystems.HABridge.plugins.NestBridge;
import java.io.UnsupportedEncodingException;
public class NestItem {
private String name;
private String id;
private String type;
private String location;
public String getName() {
return name;
}
public void setName(String name) {
byte ptext[];
String theLabel = new String(name);
try {
ptext = theLabel.getBytes("ISO-8859-1");
this.name = new String(ptext, "UTF-8");
} catch (UnsupportedEncodingException e) {
this.name = theLabel;
}
}
public String getId() {
return id;
}
public void setId(String anid) {
id = anid;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}

View File

@@ -0,0 +1,77 @@
package com.bwssystems.HABridge.plugins.broadlink;
public class BroadlinkEntry {
private String name;
private String id;
private String ipAddr;
private String macAddr;
private String command;
private String data;
private String type;
private String baseType;
private String desc;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public String getIpAddr() {
return ipAddr;
}
public void setIpAddr(String ipAddr) {
this.ipAddr = ipAddr;
}
public String getMacAddr() {
return macAddr;
}
public void setMacAddr(String macAddr) {
this.macAddr = macAddr;
}
public void setId(String id) {
this.id = id;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getBaseType() {
return baseType;
}
public void setBaseType(String baseType) {
this.baseType = baseType;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public boolean hasIpAndMac() {
boolean deviceOk = true;
if(ipAddr == null || ipAddr.trim().isEmpty())
deviceOk = false;
else if(macAddr == null || macAddr.trim().isEmpty())
deviceOk = false;
else if(type == null || type.trim().isEmpty())
deviceOk = false;
return deviceOk;
}
}

View File

@@ -0,0 +1,360 @@
package com.bwssystems.HABridge.plugins.broadlink;
import java.io.IOException;
import java.math.BigInteger;
import java.net.BindException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.Configuration;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.ColorData;
import com.bwssystems.HABridge.hue.ColorDecode;
import com.bwssystems.HABridge.hue.DeviceDataDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
import com.bwssystems.HABridge.util.HexLibrary;
import com.github.mob41.blapi.BLDevice;
import com.github.mob41.blapi.MP1Device;
import com.github.mob41.blapi.SP1Device;
import com.github.mob41.blapi.SP2Device;
import com.github.mob41.blapi.mac.Mac;
import com.github.mob41.blapi.mac.MacFormatException;
import com.github.mob41.blapi.pkt.cmd.rm2.SendDataCmdPayload;
import com.google.gson.Gson;
public class BroadlinkHome implements Home {
private static final Logger log = LoggerFactory.getLogger(BroadlinkHome.class);
private Map<String, BLDevice> broadlinkMap;
private Boolean validBroadlink;
private boolean closed;
private Boolean isDevMode;
private BridgeSettingsDescriptor bridgeSettingsDesc;
public BroadlinkHome(BridgeSettings bridgeSettings) {
super();
closed = true;
createHome(bridgeSettings);
closed = false;
}
@Override
public Home createHome(BridgeSettings bridgeSettings) {
broadlinkMap = null;
bridgeSettingsDesc = bridgeSettings.getBridgeSettingsDescriptor();
validBroadlink = bridgeSettings.getBridgeSettingsDescriptor().isValidBroadlink();
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
if (isDevMode)
validBroadlink = true;
if(validBroadlink)
broadlinkDiscover();
log.info("Broadlink Home created." + (validBroadlink ? "" : " No Broadlinks configured.") + (isDevMode ? " DevMode is set." : ""));
return this;
}
@Override
public Object getItems(String type) {
List<BroadlinkEntry> deviceList = new ArrayList<BroadlinkEntry>();
if(!validBroadlink || broadlinkMap == null)
return deviceList;
BroadlinkEntry theResponse = null;
log.debug("consolidating devices for Broadlink");
Iterator<String> keys = broadlinkMap.keySet().iterator();
while(keys.hasNext()) {
String key = keys.next();
theResponse = toEntry(broadlinkMap.get(key));
if(theResponse != null)
deviceList.add(theResponse);
else {
log.warn("Cannot get BroadlinkDevice with name: " + key + ", skipping this Broadlink.");
continue;
}
}
return deviceList;
}
@Override
public void refresh() {
if(validBroadlink)
broadlinkDiscover();
}
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
String theReturn = null;
boolean changeState = false;
String theStringData = null;
log.debug("executing HUE api request to send message to BroadlinkDevice: " + anItem.getItem().toString());
if(!validBroadlink) {
log.warn("Should not get here, no Broadlinks configured");
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no LifxDevices configured\", \"parameter\": \"/lights/"
+ lightId + "/state\"}}]";
} else {
BroadlinkEntry broadlinkCommand = null;
if(anItem.getItem().isJsonObject())
broadlinkCommand = new Gson().fromJson(anItem.getItem(), BroadlinkEntry.class);
else
broadlinkCommand = new Gson().fromJson(anItem.getItem().getAsString().replaceAll("^\"|\"$", ""), BroadlinkEntry.class);
BLDevice theDevice = null;
if(broadlinkMap != null && !broadlinkMap.isEmpty())
theDevice = broadlinkMap.get(broadlinkCommand.getId());
if (theDevice == null) {
if(broadlinkCommand.hasIpAndMac()) {
byte[] intBytes = HexLibrary.decodeHexString(broadlinkCommand.getType());
BigInteger theBig = new BigInteger(intBytes);
int theType = theBig.intValue();
try {
theDevice = BLDevice.createInstance((short)theType, broadlinkCommand.getIpAddr(), new Mac(broadlinkCommand.getMacAddr()));
} catch (MacFormatException e) {
log.warn("Could not initialize BroadlinkDevice device due to Mac (" + broadlinkCommand.getId() + ") format exception: " + e.getMessage());
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Could not initialize BroadlinkDevice device due to Mac format exception\", \"parameter\": \"/lights/"
+ lightId + "/state\"}}]";
} catch (IOException e) {
log.warn("Could not initialize BroadlinkDevice device due to IP Address (" + broadlinkCommand.getId() + ") exception: " + e.getMessage());
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Could not initialize BroadlinkDevice device due to IP Address exception\", \"parameter\": \"/lights/"
+ lightId + "/state\"}}]";
} catch (Exception e) {
log.warn("Could not initialize BroadlinkDevice device due to (" + broadlinkCommand.getId() + ") exception: " + e.getMessage());
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Could not initialize BroadlinkDevice device due to exception\", \"parameter\": \"/lights/"
+ lightId + "/state\"}}]";
}
if(broadlinkMap == null)
broadlinkMap = new HashMap<String, BLDevice>();
if (theDevice != null) {
String newId = theDevice.getHost() + "-" + String.format("%04x", theDevice.getDeviceType());
if (broadlinkMap.get(newId) == null) {
broadlinkMap.put(newId, theDevice);
}
}
}
}
if (theDevice == null) {
log.warn("Should not get here, no BroadlinkDevice not available");
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no Broadlinks available\", \"parameter\": \"/lights/"
+ lightId + "/state\"}}]";
} else {
log.debug("calling BroadlinkDevice: " + broadlinkCommand.getName());
try {
if(!isDevMode) {
if(!theDevice.auth()) {
log.error("Call to " + broadlinkCommand.getId() + " device authorization failed.");
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"" + broadlinkCommand.getId() + " device auth error.\", \"parameter\": \"/lights/"
+ lightId + "/state\"}}]";
}
}
switch (theDevice.getDeviceType()) {
case BLDevice.DEV_A1:
log.debug("Broadlink A1 device called and not supported. No Action, device name = " + device.getName() + ", id= " + broadlinkCommand.getId());
break;
case BLDevice.DEV_MP1:
if(broadlinkCommand.getCommand().equals("on"))
changeState = true;
else
changeState = false;
((MP1Device) theDevice).setState(Integer.parseInt(broadlinkCommand.getData()), changeState);
break;
case BLDevice.DEV_SP2:
case BLDevice.DEV_SP2_HONEYWELL_ALT1:
case BLDevice.DEV_SP2_HONEYWELL_ALT2:
case BLDevice.DEV_SP2_HONEYWELL_ALT3:
case BLDevice.DEV_SP2_HONEYWELL_ALT4:
case BLDevice.DEV_SPMINI:
case BLDevice.DEV_SP3:
case BLDevice.DEV_SPMINI2:
case BLDevice.DEV_SPMINI_OEM_ALT1:
case BLDevice.DEV_SPMINI_OEM_ALT2:
case BLDevice.DEV_SPMINI_PLUS:
if(broadlinkCommand.getCommand().equals("on"))
changeState = true;
else
changeState = false;
((SP2Device) theDevice).setState(changeState);
break;
case BLDevice.DEV_SP1:
if(broadlinkCommand.getCommand().equals("on"))
changeState = true;
else
changeState = false;
((SP1Device) theDevice).setPower(changeState);
break;
case BLDevice.DEV_RM_2:
case BLDevice.DEV_RM_MINI:
case BLDevice.DEV_RM_PRO_PHICOMM:
case BLDevice.DEV_RM_2_HOME_PLUS:
case BLDevice.DEV_RM_2_2HOME_PLUS_GDT:
case BLDevice.DEV_RM_2_PRO_PLUS:
case BLDevice.DEV_RM_2_PRO_PLUS_2:
case BLDevice.DEV_RM_2_PRO_PLUS_2_BL:
case BLDevice.DEV_RM_MINI_SHATE:
if(broadlinkCommand.getData() != null && !broadlinkCommand.getData().trim().isEmpty()) {
theStringData = broadlinkCommand.getData().trim();
if(targetBri != null || targetBriInc != null) {
theStringData = BrightnessDecode.calculateReplaceIntensityValue(theStringData, intensity, targetBri, targetBriInc, true);
}
if(colorData != null) {
theStringData = ColorDecode.replaceColorData(theStringData, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), true);
}
theStringData = DeviceDataDecode.replaceDeviceData(theStringData, device);
theStringData = TimeDecode.replaceTimeValue(theStringData);
byte[] theData = HexLibrary.decodeHexString(theStringData);
SendDataCmdPayload thePayload = new SendDataCmdPayload(theData);
DatagramPacket thePacket = theDevice.sendCmdPkt(Configuration.BROADLINK_DISCONVER_TIMEOUT, thePayload);
String returnData = null;
if(thePacket != null)
returnData = HexLibrary.encodeHexString(thePacket.getData());
else
returnData = "No Data - null";
log.debug("RM2 Device data return: <<<" + returnData + ">>>");
}
else {
log.error("Call to " + broadlinkCommand.getId() + " with no data, noop");
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"" + broadlinkCommand.getId() + " could not call device without data.\", \"parameter\": \"/lights/"
+ lightId + "/state\"}}]";
}
break;
}
} catch (Exception e) {
log.error("Call to " + broadlinkCommand.getId() + " device failed with exception: " + e.getMessage(), e);
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"" + broadlinkCommand.getId() + " device call error.\", \"parameter\": \"/lights/"
+ lightId + "/state\"}}]";
}
}
}
return theReturn;
}
private BroadlinkEntry toEntry(BLDevice broadlinkObject) {
short baseType = 0;
switch (broadlinkObject.getDeviceType()) {
case BLDevice.DEV_MP1:
baseType = BLDevice.DEV_MP1;
break;
case BLDevice.DEV_SP2:
case BLDevice.DEV_SP2_HONEYWELL_ALT1:
case BLDevice.DEV_SP2_HONEYWELL_ALT2:
case BLDevice.DEV_SP2_HONEYWELL_ALT3:
case BLDevice.DEV_SP2_HONEYWELL_ALT4:
case BLDevice.DEV_SPMINI:
case BLDevice.DEV_SP3:
case BLDevice.DEV_SPMINI2:
case BLDevice.DEV_SPMINI_OEM_ALT1:
case BLDevice.DEV_SPMINI_OEM_ALT2:
case BLDevice.DEV_SPMINI_PLUS:
baseType = BLDevice.DEV_SP2;
break;
case BLDevice.DEV_SP1:
baseType = BLDevice.DEV_SP1;
break;
case BLDevice.DEV_RM_2:
case BLDevice.DEV_RM_MINI:
case BLDevice.DEV_RM_PRO_PHICOMM:
case BLDevice.DEV_RM_2_HOME_PLUS:
case BLDevice.DEV_RM_2_2HOME_PLUS_GDT:
case BLDevice.DEV_RM_2_PRO_PLUS:
case BLDevice.DEV_RM_2_PRO_PLUS_2:
case BLDevice.DEV_RM_2_PRO_PLUS_2_BL:
case BLDevice.DEV_RM_MINI_SHATE:
baseType = BLDevice.DEV_RM_2;
break;
}
BroadlinkEntry anEntry = new BroadlinkEntry();
anEntry.setId(broadlinkObject.getHost() + "-" + String.format("%04x", broadlinkObject.getDeviceType()));
anEntry.setName(broadlinkObject.getDeviceDescription());
anEntry.setType(String.format("%04x", broadlinkObject.getDeviceType()));
anEntry.setBaseType(String.format("%04x", baseType));
anEntry.setDesc(BLDevice.getDescOfType(broadlinkObject.getDeviceType()));
anEntry.setIpAddr(broadlinkObject.getHost());
anEntry.setMacAddr(broadlinkObject.getMac().getMacString());
return anEntry;
}
public BLDevice[] broadlinkDiscover () {
BLDevice[] clients = null;
int aDiscoverPort = Configuration.BROADLINK_DISCOVER_PORT;
broadlinkMap = new HashMap<String, BLDevice>();
while(aDiscoverPort > 0) {
try {
log.info("Broadlink discover....");
if(isDevMode) {
clients = TestBLDevice.discoverDevices(InetAddress.getByName(bridgeSettingsDesc.getUpnpConfigAddress()), aDiscoverPort, Configuration.BROADLINK_DISCONVER_TIMEOUT);
}
else
clients = BLDevice.discoverDevices(InetAddress.getByName(bridgeSettingsDesc.getUpnpConfigAddress()), aDiscoverPort, Configuration.BROADLINK_DISCONVER_TIMEOUT);
for(int i = 0; i < clients.length; i++) {
if(clients[i].getDeviceType() != BLDevice.DEV_A1 && broadlinkMap.get(clients[i].getHost() + "-" + String.format("%04x", clients[i].getDeviceType())) == null) {
broadlinkMap.put(clients[i].getHost() + "-" + String.format("%04x", clients[i].getDeviceType()), clients[i]);
log.debug("Adding Device to Map - host: " + clients[i].getHost() + ", device Type: " + clients[i].getDeviceDescription() + ", mac: " + (clients[i].getMac() == null ? "no Mac in client" : clients[i].getMac().getMacString()));
} else {
log.debug("Ignoring Device (already in the list or an A1 Device) - host: " + clients[i].getHost() + ", device Type: " + clients[i].getDeviceDescription() + ", mac: " + (clients[i].getMac() == null ? "no Mac in client" : clients[i].getMac().getMacString()));
}
}
aDiscoverPort = 0;
} catch (BindException e) {
log.warn("Could not discover Broadlinks, Port in use, increasing by 11");
aDiscoverPort += 11;
if(aDiscoverPort > Configuration.BROADLINK_DISCOVER_PORT + 110)
aDiscoverPort = 0;
} catch (IOException e) {
log.warn("Could not discover Broadlinks, with IO Exception", e);
broadlinkMap = null;
aDiscoverPort = 0;
}
}
if(clients == null || clients.length <= 0) {
log.warn("Did not discover any Broadlinks.");
broadlinkMap = null;
} else {
log.info("Broadlink discover found " + clients.length + " clients.");
}
return clients;
}
@Override
public void closeHome() {
if(!validBroadlink)
return;
log.debug("Closing Home.");
if(broadlinkMap != null) {
broadlinkMap.clear();
broadlinkMap = null;
}
if(closed) {
log.debug("Home is already closed....");
return;
}
closed = true;
}
}

View File

@@ -0,0 +1,73 @@
package com.bwssystems.HABridge.plugins.broadlink;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import com.bwssystems.HABridge.util.HexLibrary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.mob41.blapi.BLDevice;
import com.github.mob41.blapi.mac.Mac;
import com.github.mob41.blapi.pkt.CmdPayload;
public class TestBLDevice extends BLDevice {
private static final Logger log = LoggerFactory.getLogger(TestBLDevice.class);
protected TestBLDevice(short deviceType, String deviceDesc, String host, Mac mac) throws IOException {
super(deviceType, deviceDesc, host, mac);
}
public void setState(boolean aState) {
log.info("setState called with " + aState);
}
public void setState(int anIndex, boolean aState) {
log.info("setState called with index " + anIndex + " and state " + aState);
}
public void setPower(boolean aState) {
log.info("setPower called with " + aState);
}
public DatagramPacket sendCmdPkt(int timeout, CmdPayload aCmd) {
log.info("sendCmdPkt called with " + HexLibrary.encodeHexString(aCmd.getPayload().getData()));
return null;
}
public static BLDevice[] discoverDevices(InetAddress theAddress, int aport, int timeout) {
TestMP1Device mp1Device = null;
TestSP1Device sp1Device = null;
TestSP2Device sp2Device = null;
TestRM2Device rm2Device = null;
try {
mp1Device = new TestMP1Device("mp1host", null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
sp1Device = new TestSP1Device("sp1host", null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
sp2Device = new TestSP2Device("sp2host", null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
rm2Device = new TestRM2Device("rm2host", null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BLDevice[] devices = { mp1Device, sp1Device, sp2Device, rm2Device };
log.info("Created test devices");
return devices;
}
}

View File

@@ -0,0 +1,29 @@
package com.bwssystems.HABridge.plugins.broadlink;
import java.io.IOException;
import java.net.DatagramPacket;
import com.bwssystems.HABridge.util.HexLibrary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.mob41.blapi.MP1Device;
import com.github.mob41.blapi.mac.Mac;
import com.github.mob41.blapi.pkt.CmdPayload;
public class TestMP1Device extends MP1Device {
private static final Logger log = LoggerFactory.getLogger(TestMP1Device.class);
protected TestMP1Device(String host, Mac mac) throws IOException {
super(host, mac);
}
public void setState(int anIndex, boolean aState) {
log.info("setState called with index " + anIndex + " and state " + aState);
}
public DatagramPacket sendCmdPkt(int timeout, CmdPayload aCmd) {
log.info("sendCmdPkt called with " + HexLibrary.encodeHexString(aCmd.getPayload().getData()));
return null;
}
}

View File

@@ -0,0 +1,27 @@
package com.bwssystems.HABridge.plugins.broadlink;
import java.io.IOException;
import java.net.DatagramPacket;
import com.bwssystems.HABridge.util.HexLibrary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.mob41.blapi.RM2Device;
import com.github.mob41.blapi.mac.Mac;
import com.github.mob41.blapi.pkt.CmdPayload;
import com.github.mob41.blapi.pkt.cmd.rm2.SendDataCmdPayload;
public class TestRM2Device extends RM2Device {
private static final Logger log = LoggerFactory.getLogger(TestRM2Device.class);
protected TestRM2Device(String host, Mac mac) throws IOException {
super(host, mac);
}
public DatagramPacket sendCmdPkt(int timeout, CmdPayload aCmd) {
log.info("sendCmdPkt called with " + HexLibrary.encodeHexString(((SendDataCmdPayload)aCmd).getData()));
return null;
}
}

View File

@@ -0,0 +1,30 @@
package com.bwssystems.HABridge.plugins.broadlink;
import java.io.IOException;
import java.net.DatagramPacket;
import com.bwssystems.HABridge.util.HexLibrary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.mob41.blapi.SP1Device;
import com.github.mob41.blapi.mac.Mac;
import com.github.mob41.blapi.pkt.CmdPayload;
public class TestSP1Device extends SP1Device {
private static final Logger log = LoggerFactory.getLogger(TestSP1Device.class);
protected TestSP1Device(String host, Mac mac) throws IOException {
super(host, mac);
}
public void setPower(boolean aState) {
log.info("setPower called with " + aState);
}
public DatagramPacket sendCmdPkt(int timeout, CmdPayload aCmd) {
log.info("sendCmdPkt called with " + HexLibrary.encodeHexString(aCmd.getPayload().getData()));
return null;
}
}

View File

@@ -0,0 +1,30 @@
package com.bwssystems.HABridge.plugins.broadlink;
import java.io.IOException;
import java.net.DatagramPacket;
import com.bwssystems.HABridge.util.HexLibrary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.mob41.blapi.SP2Device;
import com.github.mob41.blapi.mac.Mac;
import com.github.mob41.blapi.pkt.CmdPayload;
public class TestSP2Device extends SP2Device {
private static final Logger log = LoggerFactory.getLogger(TestSP2Device.class);
protected TestSP2Device(String host, Mac mac) throws IOException {
super(host, mac);
}
public void setState(boolean aState) {
log.info("setState called with " + aState);
}
public DatagramPacket sendCmdPkt(int timeout, CmdPayload aCmd) {
log.info("sendCmdPkt called with " + HexLibrary.encodeHexString(aCmd.getPayload().getData()));
return null;
}
}

View File

@@ -0,0 +1,55 @@
package com.bwssystems.HABridge.plugins.domoticz;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class DeviceResult {
@SerializedName("Description")
@Expose
private String description;
@SerializedName("Name")
@Expose
private String name;
@SerializedName("Type")
@Expose
private String type;
@SerializedName("idx")
@Expose
private String idx;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getIdx() {
return idx;
}
public void setIdx(String idx) {
this.idx = idx;
}
}

View File

@@ -0,0 +1,99 @@
package com.bwssystems.HABridge.plugins.domoticz;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Devices {
@SerializedName("ActTime")
@Expose
private Integer actTime;
@SerializedName("AllowWidgetOrdering")
@Expose
private Boolean allowWidgetOrdering;
@SerializedName("ServerTime")
@Expose
private String serverTime;
@SerializedName("Sunrise")
@Expose
private String sunrise;
@SerializedName("Sunset")
@Expose
private String sunset;
@SerializedName("result")
@Expose
private List<DeviceResult> result = null;
@SerializedName("status")
@Expose
private String status;
@SerializedName("title")
@Expose
private String title;
public Integer getActTime() {
return actTime;
}
public void setActTime(Integer actTime) {
this.actTime = actTime;
}
public Boolean getAllowWidgetOrdering() {
return allowWidgetOrdering;
}
public void setAllowWidgetOrdering(Boolean allowWidgetOrdering) {
this.allowWidgetOrdering = allowWidgetOrdering;
}
public String getServerTime() {
return serverTime;
}
public void setServerTime(String serverTime) {
this.serverTime = serverTime;
}
public String getSunrise() {
return sunrise;
}
public void setSunrise(String sunrise) {
this.sunrise = sunrise;
}
public String getSunset() {
return sunset;
}
public void setSunset(String sunset) {
this.sunset = sunset;
}
public List<DeviceResult> getResult() {
return result;
}
public void setResult(List<DeviceResult> result) {
this.result = result;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

View File

@@ -0,0 +1,39 @@
package com.bwssystems.HABridge.plugins.domoticz;
public class DomoticzDevice {
private String devicetype;
private String devicename;
private String idx;
private String domoticzaddress;
private String domoticzname;
public String getDevicetype() {
return devicetype;
}
public void setDevicetype(String devicetype) {
this.devicetype = devicetype;
}
public String getDevicename() {
return devicename;
}
public void setDevicename(String devicename) {
this.devicename = devicename;
}
public String getIdx() {
return idx;
}
public void setIdx(String idx) {
this.idx = idx;
}
public String getDomoticzaddress() {
return domoticzaddress;
}
public void setDomoticzaddress(String domoticzaddress) {
this.domoticzaddress = domoticzaddress;
}
public String getDomoticzname() {
return domoticzname;
}
public void setDomoticzname(String domoticzname) {
this.domoticzname = domoticzname;
}
}

View File

@@ -0,0 +1,96 @@
package com.bwssystems.HABridge.plugins.domoticz;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
import com.google.gson.Gson;
public class DomoticzHandler {
private static final Logger log = LoggerFactory.getLogger(DomoticzHandler.class);
private static final String GET_REQUEST = "/json.htm?type=";
private static final String DEVICES_TYPE = "devices";
private static final String SCENES_TYPE = "scenes";
private static final String FILTER_USED = "&used=true";
private NamedIP domoticzAddress;
public DomoticzHandler(NamedIP addressName) {
super();
domoticzAddress = addressName;
}
public List<DomoticzDevice> getDevices(HTTPHandler httpClient) {
return getDomoticzDevices(GET_REQUEST, DEVICES_TYPE, FILTER_USED, httpClient);
}
public List<DomoticzDevice> getScenes(HTTPHandler httpClient) {
return getDomoticzDevices(GET_REQUEST, SCENES_TYPE, null, httpClient);
}
private List<DomoticzDevice> getDomoticzDevices(String rootRequest, String type, String postpend, HTTPHandler httpClient) {
Devices theDomoticzApiResponse = null;
List<DomoticzDevice> deviceList = null;
String theUrl = null;
String theData;
if(postpend != null && !postpend.isEmpty())
theUrl = buildUrl(rootRequest + type + postpend);
else
theUrl = buildUrl(rootRequest + type);
theData = httpClient.doHttpRequest(theUrl, null, null, null, httpClient.addBasicAuthHeader(null, domoticzAddress));
if(theData != null) {
log.debug("GET " + type + " DomoticzApiResponse - data: " + theData);
theDomoticzApiResponse = new Gson().fromJson(theData, Devices.class);
if(theDomoticzApiResponse.getResult() == null) {
log.warn("Cannot get any devices for type " + type + " for Domoticz " + domoticzAddress.getName() + " as response is not parsable.");
return deviceList;
}
deviceList = new ArrayList<DomoticzDevice>();
Iterator<DeviceResult> theDeviceNames = theDomoticzApiResponse.getResult().iterator();
while(theDeviceNames.hasNext()) {
DeviceResult theDevice = theDeviceNames.next();
DomoticzDevice aNewDomoticzDevice = new DomoticzDevice();
aNewDomoticzDevice.setDevicetype(theDevice.getType());
aNewDomoticzDevice.setDevicename(theDevice.getName());
aNewDomoticzDevice.setIdx(theDevice.getIdx());
aNewDomoticzDevice.setDomoticzaddress(domoticzAddress.getIp() + ":" + domoticzAddress.getPort());
aNewDomoticzDevice.setDomoticzname(domoticzAddress.getName());
deviceList.add(aNewDomoticzDevice);
}
}
else {
log.warn("Get Domoticz device types " + type + " for " + domoticzAddress.getName() + " - returned null, no data.");
}
return deviceList;
}
public String buildUrl(String thePayload) {
String newUrl = null;
if(thePayload != null && !thePayload.isEmpty()) {
newUrl = domoticzAddress.getHttpPreamble();
if(thePayload.startsWith("/"))
newUrl = newUrl + thePayload;
else
newUrl = newUrl + "/" + thePayload;
}
return newUrl;
}
public NamedIP getDomoticzAddress() {
return domoticzAddress;
}
public void setDomoticzAddress(NamedIP DomoticzAddress) {
this.domoticzAddress = DomoticzAddress;
}
}

View File

@@ -0,0 +1,205 @@
package com.bwssystems.HABridge.plugins.domoticz;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.api.hue.HueError;
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.ColorData;
import com.bwssystems.HABridge.hue.ColorDecode;
import com.bwssystems.HABridge.hue.DeviceDataDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
import com.bwssystems.HABridge.plugins.http.HTTPHome;
import com.google.gson.Gson;
public class DomoticzHome implements Home {
private static final Logger log = LoggerFactory.getLogger(DomoticzHome.class);
private Map<String, DomoticzHandler> domoticzs;
private Boolean validDomoticz;
private HTTPHandler httpClient;
private boolean closed;
public DomoticzHome(BridgeSettings bridgeSettings) {
super();
closed = true;
createHome(bridgeSettings);
closed = false;
}
@Override
public Object getItems(String type) {
if(!validDomoticz)
return null;
log.debug("consolidating devices for Domoticzs");
List<DomoticzDevice> theResponse = null;
Iterator<String> keys = domoticzs.keySet().iterator();
List<DomoticzDevice> deviceList = new ArrayList<DomoticzDevice>();
while(keys.hasNext()) {
String key = keys.next();
theResponse = domoticzs.get(key).getDevices(httpClient);
if(theResponse != null)
addDomoticzDevices(deviceList, theResponse, key);
else {
log.warn("Cannot get lights for Domoticz with name: " + key + ", skipping this Domoticz.");
continue;
}
theResponse = domoticzs.get(key).getScenes(httpClient);
if(theResponse != null)
addDomoticzDevices(deviceList, theResponse, key);
else
log.warn("Cannot get Scenes for Domoticz with name: " + key);
}
return deviceList;
}
@Override
public void refresh() {
// noop
}
private Boolean addDomoticzDevices(List<DomoticzDevice> theDeviceList, List<DomoticzDevice> theSourceList, String theKey) {
if(!validDomoticz)
return false;
Iterator<DomoticzDevice> devices = theSourceList.iterator();
while(devices.hasNext()) {
DomoticzDevice theDevice = devices.next();
theDeviceList.add(theDevice);
}
return true;
}
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
Devices theDomoticzApiResponse = null;
String responseString = null;
String theUrl = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
if(theUrl != null && !theUrl.isEmpty () && (theUrl.startsWith("http://") || theUrl.startsWith("https://"))) {
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
String hostAddr = null;
if (hostPortion.contains(":")) {
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
} else
hostAddr = hostPortion;
DomoticzHandler theHandler = findHandlerByAddress(hostAddr);
if(theHandler != null){
String theData;
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody,
intensity, targetBri, targetBriInc, false);
if (colorData != null) {
anUrl = ColorDecode.replaceColorData(anUrl, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
}
anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device);
anUrl = TimeDecode.replaceTimeValue(anUrl);
String aBody = null;
if(anItem.getHttpBody()!= null && !anItem.getHttpBody().isEmpty()) {
aBody = BrightnessDecode.calculateReplaceIntensityValue(anItem.getHttpBody(),
intensity, targetBri, targetBriInc, false);
if (colorData != null) {
aBody = ColorDecode.replaceColorData(aBody, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
}
aBody = DeviceDataDecode.replaceDeviceData(aBody, device);
aBody = TimeDecode.replaceTimeValue(aBody);
}
theData = httpClient.doHttpRequest(theHandler.buildUrl(anUrl), null, null, aBody, httpClient.addBasicAuthHeader(null, theHandler.getDomoticzAddress()));
try {
theDomoticzApiResponse = new Gson().fromJson(theData, Devices.class);
if(theDomoticzApiResponse.getStatus().equals("OK"))
responseString = null;
else {
log.warn("Call failed for Domoticz " + theHandler.getDomoticzAddress().getName() + " with status " + theDomoticzApiResponse.getStatus() + " for item " + theDomoticzApiResponse.getTitle());
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
}
} catch (Exception e) {
log.warn("Cannot interrpret result from call for Domoticz " + theHandler.getDomoticzAddress().getName() + " as response is not parsable.");
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
}
} else {
log.warn("Domoticz Call could not complete, no address found: " + theUrl);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
}
} else {
log.warn("Domoticz Call to be presented as http(s)://<ip_address>(:<port>)/payload, format of request unknown: " + theUrl);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
}
return responseString;
}
@Override
public Home createHome(BridgeSettings bridgeSettings) {
validDomoticz = bridgeSettings.getBridgeSettingsDescriptor().isValidDomoticz();
log.info("Domoticz Home created." + (validDomoticz ? "" : " No Domoticz devices configured."));
if(!validDomoticz)
return null;
httpClient = HTTPHome.getHandler();
domoticzs = new HashMap<String, DomoticzHandler>();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getDomoticzaddress().getDevices().iterator();
while(theList.hasNext()) {
NamedIP aDomoticz = theList.next();
try {
domoticzs.put(aDomoticz.getName(), new DomoticzHandler(aDomoticz));
} catch (Exception e) {
log.error("Cannot get Domoticz client (" + aDomoticz.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
return null;
}
}
return this;
}
private DomoticzHandler findHandlerByAddress(String hostAddress) {
DomoticzHandler aHandler = null;
boolean found = false;
Iterator<String> keys = domoticzs.keySet().iterator();
while(keys.hasNext()) {
String key = keys.next();
aHandler = domoticzs.get(key);
if(aHandler != null && aHandler.getDomoticzAddress().getIp().equals(hostAddress)) {
found = true;
break;
}
}
if(!found)
aHandler = null;
return aHandler;
}
@Override
public void closeHome() {
log.debug("Closing Home.");
if(closed || !validDomoticz) {
log.debug("Home is already closed....");
return;
}
if(httpClient != null)
httpClient.closeHandler();
domoticzs = null;
closed = true;
}
}

View File

@@ -0,0 +1,112 @@
package com.bwssystems.HABridge.plugins.exec;
import java.io.File;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.ColorData;
import com.bwssystems.HABridge.hue.ColorDecode;
import com.bwssystems.HABridge.hue.DeviceDataDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
public class CommandHome implements Home {
private static final Logger log = LoggerFactory.getLogger(CommandHome.class);
private BridgeSettings theSettings;
private boolean closed;
public CommandHome(BridgeSettings bridgeSettings) {
super();
closed = true;
createHome(bridgeSettings);
closed = false;
}
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
String theItem = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
log.debug("Exec Request called with url: {} and exec Garden: {}", theItem, (theSettings.getBridgeSecurity().getExecGarden() == null ? "not given" : theSettings.getBridgeSecurity().getExecGarden()));
String responseString = null;
String intermediate;
if (theItem.contains("exec://"))
intermediate = theItem.substring(anItem.getItem().getAsString().indexOf("://") + 3);
else
intermediate = theItem;
intermediate = BrightnessDecode.calculateReplaceIntensityValue(intermediate, intensity, targetBri, targetBriInc, false);
if (colorData != null) {
intermediate = ColorDecode.replaceColorData(intermediate, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
}
intermediate = DeviceDataDecode.replaceDeviceData(intermediate, device);
intermediate = TimeDecode.replaceTimeValue(intermediate);
String execGarden = theSettings.getBridgeSecurity().getExecGarden();
if(execGarden != null && !execGarden.trim().isEmpty()) {
intermediate = new File(execGarden.trim(), intermediate).getAbsolutePath();
}
String anError = doExecRequest(intermediate, lightId);
if (anError != null) {
responseString = anError;
}
return responseString;
}
private String doExecRequest(String anItem, String lightId) {
log.debug("Executing request: " + anItem);
String responseString = null;
if (anItem != null && !anItem.equalsIgnoreCase("")) {
try {
Process p = Runtime.getRuntime().exec(anItem);
log.debug("Process running: " + p.isAlive());
} catch (IOException e) {
log.warn("Could not execute request: " + anItem + " with message: " + e.getMessage());
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId
+ "/state\"}}]";
}
} else {
log.warn("Could not execute request. Request is empty.");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId
+ "/state\"}}]";
}
return responseString;
}
@Override
public Home createHome(BridgeSettings bridgeSettings) {
log.info("Command Home for system program execution created.");
this.theSettings = bridgeSettings;
return this;
}
@Override
public Object getItems(String type) {
// noop
return null;
}
@Override
public void refresh() {
// noop
}
@Override
public void closeHome() {
log.debug("Closing Home.");
if(closed) {
log.debug("Home is already closed....");
return;
}
closed = true;
}
}

View File

@@ -0,0 +1,19 @@
package com.bwssystems.HABridge.plugins.fhem;
public class FHEMCommand {
private String url;
private String command;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
}

View File

@@ -0,0 +1,26 @@
package com.bwssystems.HABridge.plugins.fhem;
public class FHEMDevice {
private String address;
private String name;
private Result item;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Result getItem() {
return item;
}
public void setItem(Result item) {
this.item = item;
}
}

View File

@@ -0,0 +1,218 @@
package com.bwssystems.HABridge.plugins.fhem;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.api.hue.HueError;
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.ColorData;
import com.bwssystems.HABridge.hue.ColorDecode;
import com.bwssystems.HABridge.hue.DeviceDataDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
import com.bwssystems.HABridge.plugins.http.HTTPHome;
import com.bwssystems.HABridge.plugins.http.HttpTestHandler;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
public class FHEMHome implements Home {
private static final Logger log = LoggerFactory.getLogger(FHEMHome.class);
private Map<String, FHEMInstance> fhemMap;
private Boolean validFhem;
private Boolean isDevMode;
private HTTPHandler httpClient;
private boolean closed;
public FHEMHome(BridgeSettings bridgeSettings) {
super();
closed = true;
createHome(bridgeSettings);
closed = false;
}
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
JsonElement jsonUrl = anItem.getItem();
String theUrl = jsonUrl.toString();
String responseString = null;
if(theUrl != null && !theUrl.isEmpty()) {
FHEMCommand theCommand = null;
try {
if(anItem.getItem().isJsonObject())
theCommand = new Gson().fromJson(anItem.getItem(), FHEMCommand.class);
else
theCommand = new Gson().fromJson(anItem.getItem().getAsString().replaceAll("^\"|\"$", ""), FHEMCommand.class);
} catch(Exception e) {
log.warn("Cannot parse command to FHEM <<<" + theUrl + ">>>", e);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
return responseString;
}
String intermediate = theCommand.getUrl().substring(theCommand.getUrl().indexOf("://") + 3);
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
String hostAddr = null;
if (hostPortion.contains(":")) {
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
} else
hostAddr = hostPortion;
FHEMInstance theHandler = findHandlerByAddress(hostAddr);
if(theHandler != null) {
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody,
intensity, targetBri, targetBriInc, false);
if (colorData != null) {
anUrl = ColorDecode.replaceColorData(anUrl, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
}
anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device);
anUrl = TimeDecode.replaceTimeValue(anUrl);
String aCommand = null;
if(theCommand.getCommand() != null && !theCommand.getCommand().isEmpty()) {
aCommand = BrightnessDecode.calculateReplaceIntensityValue(theCommand.getCommand(),
intensity, targetBri, targetBriInc, false);
if (colorData != null) {
aCommand = ColorDecode.replaceColorData(aCommand, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
}
aCommand = DeviceDataDecode.replaceDeviceData(aCommand, device);
aCommand = TimeDecode.replaceTimeValue(aCommand);
}
try {
theHandler.callCommand(anUrl, aCommand, httpClient);
} catch (Exception e) {
log.warn("Cannot send comand to FHEM", e);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
}
} else {
log.warn("FHEM Call could not complete, no address found: " + theUrl);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
}
} else {
log.warn("FHEM Call to be presented as http(s)://<ip_address>(:<port>)/payload, format of request unknown: " + theUrl);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
}
return responseString;
}
private FHEMInstance findHandlerByAddress(String hostAddress) {
FHEMInstance aHandler = null;
boolean found = false;
Iterator<String> keys = fhemMap.keySet().iterator();
while(keys.hasNext()) {
String key = keys.next();
aHandler = fhemMap.get(key);
if(aHandler != null && aHandler.getFhemAddress().getIp().equals(hostAddress)) {
found = true;
break;
}
}
if(!found)
aHandler = null;
return aHandler;
}
@Override
public Object getItems(String type) {
if(!validFhem)
return null;
log.debug("consolidating devices for FHEM");
List<FHEMDevice> theResponse = null;
Iterator<String> keys = fhemMap.keySet().iterator();
List<FHEMDevice> deviceList = new ArrayList<FHEMDevice>();
while(keys.hasNext()) {
String key = keys.next();
theResponse = fhemMap.get(key).getDevices(httpClient);
if(theResponse != null)
addFHEMDevices(deviceList, theResponse, key);
else {
log.warn("Cannot get devices for FHEM: " + key + ", skipping this FHEM.");
continue;
}
}
return deviceList;
}
@Override
public void refresh() {
// noop
}
private Boolean addFHEMDevices(List<FHEMDevice> theDeviceList, List<FHEMDevice> theSourceList, String theKey) {
Iterator<FHEMDevice> devices = theSourceList.iterator();
while(devices.hasNext()) {
FHEMDevice theDevice = devices.next();
theDeviceList.add(theDevice);
}
return true;
}
@Override
public Home createHome(BridgeSettings bridgeSettings) {
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
fhemMap = null;
validFhem = bridgeSettings.getBridgeSettingsDescriptor().isValidFhem();
log.info("FHEM Home created." + (validFhem ? "" : " No FHEMs configured.") + (isDevMode ? " DevMode is set." : ""));
if(validFhem) {
fhemMap = new HashMap<String,FHEMInstance>();
httpClient = HTTPHome.getHandler();
if(isDevMode) {
httpClient = new HttpTestHandler();
((HttpTestHandler)httpClient).setTheData("cmd=jsonlist2", FHEMTestData.TestData);
((HttpTestHandler)httpClient).setTheData("set", "FHEM Command Received");
((HttpTestHandler)httpClient).setTheData(null, "FHEM no match");
}
httpClient.setCallType(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex]);
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getFhemaddress().getDevices().iterator();
while(theList.hasNext() && validFhem) {
NamedIP aFhem = theList.next();
try {
fhemMap.put(aFhem.getName(), new FHEMInstance(aFhem));
} catch (Exception e) {
log.error("Cannot get FHEM (" + aFhem.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
validFhem = false;
}
}
}
return this;
}
@Override
public void closeHome() {
log.debug("Closing Home.");
if(!closed && validFhem) {
log.debug("Home is already closed....");
return;
}
if(httpClient != null)
httpClient.closeHandler();
fhemMap = null;
closed = true;
}
}

View File

@@ -0,0 +1,120 @@
package com.bwssystems.HABridge.plugins.fhem;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.NameValue;
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
import com.google.gson.Gson;
public class FHEMInstance {
private static final Logger log = LoggerFactory.getLogger(FHEMInstance.class);
private NamedIP theFhem;
public FHEMInstance(NamedIP fhemLocation) {
super();
theFhem = fhemLocation;
}
public NamedIP getFhemAddress() {
return theFhem;
}
public void setFhemAddress(NamedIP fhemAddress) {
this.theFhem = fhemAddress;
}
public Boolean callCommand(String aCommand, String commandData, HTTPHandler httpClient) {
String aUrl = null;
NameValue[] headers = null;
/* Trying new code helpers
if(theFhem.getSecure() != null && theFhem.getSecure())
aUrl = "https://";
else
aUrl = "http://";
if(theFhem.getUsername() != null && !theFhem.getUsername().isEmpty() && theFhem.getPassword() != null && !theFhem.getPassword().isEmpty()) {
aUrl = aUrl + theFhem.getUsername() + ":" + theFhem.getPassword() + "@";
}
aUrl = aUrl + theFhem.getIp() + ":" + theFhem.getPort() + "/" + aCommand + commandData;
*/
// New style http creation
aUrl = theFhem.getHttpPreamble() + "/" + aCommand + commandData;
headers = httpClient.addBasicAuthHeader(null, theFhem);
log.debug("calling FHEM: " + aUrl);
String theData = httpClient.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "text/plain", null, headers);
if(theData != null)
log.debug("doHttpRequest returned data: <" + theData + ">");
return true;
}
public List<FHEMDevice> getDevices(HTTPHandler httpClient) {
List<FHEMDevice> deviceList = null;
FHEMItem theFhemStates;
String theUrl = null;
String theData;
NameValue[] headers = null;
/* Trying new code helpers
if(theFhem.getSecure() != null && theFhem.getSecure())
theUrl = "https://";
else
theUrl = "http://";
if(theFhem.getUsername() != null && !theFhem.getUsername().isEmpty() && theFhem.getPassword() != null && !theFhem.getPassword().isEmpty()) {
theUrl = theUrl + theFhem.getUsername() + ":" + theFhem.getPassword() + "@";
}
theUrl = theUrl + theFhem.getIp() + ":" + theFhem.getPort() + "/fhem?cmd=jsonlist2";
*/
theUrl = theFhem.getHttpPreamble() + "/fhem?cmd=jsonlist2";
headers = httpClient.addBasicAuthHeader(null, theFhem);
if(theFhem.getWebhook() != null && !theFhem.getWebhook().trim().isEmpty())
theUrl = theUrl + "%20room=" + theFhem.getWebhook().trim();
theData = httpClient.doHttpRequest(theUrl, HttpGet.METHOD_NAME, "application/json", null, headers);
if(theData != null) {
log.debug("GET FHEM States - data: " + theData);
theData = getJSONData(theData);
theFhemStates = new Gson().fromJson(theData, FHEMItem.class);
if(theFhemStates == null) {
log.warn("Cannot get any devices for FHEM " + theFhem.getName() + " as response is not parsable.");
}
else {
deviceList = new ArrayList<FHEMDevice>();
for (Result aResult:theFhemStates.getResults()) {
String name = aResult.getName();
if(name.contains("<a href=")) {
name = name.substring(name.indexOf("<a href=") + name.indexOf(">"));
name = name.substring(1, name.indexOf("</a"));
aResult.setName(name);
}
FHEMDevice aNewFhemDeviceDevice = new FHEMDevice();
aNewFhemDeviceDevice.setItem(aResult);
aNewFhemDeviceDevice.setAddress(theFhem.getIp() + ":" + theFhem.getPort());
aNewFhemDeviceDevice.setName(theFhem.getName());
deviceList.add(aNewFhemDeviceDevice);
}
}
}
else
log.warn("Cannot get an devices for FHEM " + theFhem.getName() + " http call failed.");
return deviceList;
}
public String getJSONData(String response) {
String theData;
theData = response.substring(response.indexOf("<pre>") + 4);
theData = theData.substring(1, theData.indexOf("</pre>") - 1);
theData = theData.replace("\n", "");
theData = theData.replace("\r", "");
theData = theData.replace("<a href=\"", "<a href=\\\"");
theData = theData.replace("\">", "\\\">");
return theData;
}
protected void closeClient() {
}
}

View File

@@ -0,0 +1,44 @@
package com.bwssystems.HABridge.plugins.fhem;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class FHEMItem {
@SerializedName("Arg")
@Expose
private String arg;
@SerializedName("Results")
@Expose
private List<Result> results = null;
@SerializedName("totalResultsReturned")
@Expose
private Integer totalResultsReturned;
public String getArg() {
return arg;
}
public void setArg(String arg) {
this.arg = arg;
}
public List<Result> getResults() {
return results;
}
public void setResults(List<Result> results) {
this.results = results;
}
public Integer getTotalResultsReturned() {
return totalResultsReturned;
}
public void setTotalResultsReturned(Integer totalResultsReturned) {
this.totalResultsReturned = totalResultsReturned;
}
}

View File

@@ -0,0 +1,288 @@
package com.bwssystems.HABridge.plugins.fhem;
public class FHEMTestData {
public final static String TestData = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" +
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +
" <head root=\"/fhem\">\n" +
" <title>Home, Sweet Home</title>\n" +
" <link rel=\"shortcut icon\" href=\"/fhem/icons/favicon\" />\n" +
" <meta charset=\"UTF-8\">\n" +
" <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n" +
" <link href=\"/fhem/pgm2/style.css?v=1513026539\" rel=\"stylesheet\"/>\n" +
" <link href=\"/fhem/pgm2/jquery-ui.min.css\" rel=\"stylesheet\"/>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/jquery.min.js\"></script>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/jquery-ui.min.js\"></script>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb.js\"></script>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_colorpicker.js\"></script>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_fbcalllist.js\"></script>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_knob.js\"></script>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_readingsGroup.js\"></script>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_readingsHistory.js\"></script>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_sortable.js\"></script>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_uzsu.js\"></script>\n" +
" <script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_weekprofile.js\"></script>\n" +
" </head>\n" +
" <body name='Home, Sweet Home' fw_id='7880' generated=\"1513272732\" longpoll=\"1\" data-confirmDelete='1' data-confirmJSError='1' data-webName='haBridgeWeb '>\n" +
" <div id=\"menuScrollArea\">\n" +
" <div>\n" +
" <a href=\"/fhem?\">\n" +
" <div id=\"logo\"></div>\n" +
" </a>\n" +
" </div>\n" +
" <div id=\"menu\">\n" +
" <table>\n" +
" <tr>\n" +
" <td>\n" +
" <table class=\"room roomBlock1\">\n" +
" <tr>\n" +
" <td>\n" +
" <div class=\"menu_Save_config\">\n" +
" <a href=\"/fhem?cmd=save\">Save config</a>\n" +
" <a id=\"saveCheck\" class=\"changed\" style=\"visibility:visible\">?</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" </table>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <table class=\"room roomBlock2\">\n" +
" <tr>\n" +
" <td>\n" +
" <div class=\"menu_Alexa\">\n" +
" <a href=\"/fhem?room=Alexa\">Alexa</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <div class=\"menu_System\">\n" +
" <a href=\"/fhem?room=System\">System</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <div class=\"menu_WG_Zimmer\">\n" +
" <a href=\"/fhem?room=WG%2dZimmer\">WG-Zimmer</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <div class=\"menu_habridge\">\n" +
" <a href=\"/fhem?room=habridge\">habridge</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <div class=\"menu_Everything\">\n" +
" <a href=\"/fhem?room=all\">\n" +
" <img class='icon icoEverything' src=\"/fhem/images/default/icoEverything.png\" alt=\"icoEverything\" title=\"icoEverything\">&nbsp;Everything\n" +
" </a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" </table>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <table class=\"room roomBlock3\">\n" +
" <tr>\n" +
" <td>\n" +
" <div class=\"menu_Logfile\">\n" +
" <a href=\"/fhem/FileLog_logWrapper?dev=Logfile&type=text&file=fhem-2017-12.log\">Logfile</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <div>\n" +
" <a href=\"/fhem/docs/commandref.html\" target=\"_blank\" >Commandref</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <div>\n" +
" <a href=\"http://fhem.de/fhem.html#Documentation\" target=\"_blank\" >Remote doc</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <div class=\"menu_Edit_files\">\n" +
" <a href=\"/fhem?cmd=style%20list\">Edit files</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <div class=\"menu_Select_style\">\n" +
" <a href=\"/fhem?cmd=style%20select\">Select style</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td>\n" +
" <div class=\"menu_Event_monitor\">\n" +
" <a href=\"/fhem?cmd=style%20eventMonitor\">Event monitor</a>\n" +
" </div>\n" +
" </td>\n" +
" </tr>\n" +
" </table>\n" +
" </td>\n" +
" </tr>\n" +
" </table>\n" +
" </div>\n" +
" </div>\n" +
" <div id=\"hdr\">\n" +
" <table border=\"0\" class=\"header\">\n" +
" <tr>\n" +
" <td style=\"padding:0\">\n" +
" <form method=\"post\" action=\"/fhem\">\n" +
" <input type=\"hidden\" name=\"fw_id\" value=\"7880\"/>\n" +
" <input type=\"text\" name=\"cmd\" class=\"maininput\" size=\"40\" value=\"\"/>\n" +
" </form>\n" +
" </td>\n" +
" </tr>\n" +
" </table>\n" +
" </div>\n" +
" <div id='content' >\n" +
" <pre>{ \n" +
" \"Arg\":\"room=habridge\", \n" +
" \"Results\": [ \n" +
" { \n" +
" \"Name\":\"Arbeitslicht\", \n" +
" \"PossibleSets\":\"on off\", \n" +
" \"PossibleAttrs\":\"alias comment:textField-long eventMap group room suppressReading userReadings:textField-long verbose:0,1,2,3,4,5 readingList setList useSetExtensions disable disabledForIntervals event-on-change-reading event-on-update-reading event-aggregator event-min-interval stateFormat:textField-long timestamp-on-change-reading alexaName alexaRoom cmdIcon devStateIcon devStateStyle fhem_widget_command fhem_widget_command_2 fhem_widget_command_3 genericDeviceType:security,ignore,switch,outlet,light,blind,thermometer,thermostat,contact,garage,window,lock homebridgeMapping:textField-long icon sortby webCmd widgetOverride userattr\", \n" +
" \"Internals\": { \n" +
" \"NAME\": \"Arbeitslicht\", \n" +
" \"NR\": \"28\", \n" +
" \"STATE\": \"-\", \n" +
" \"TYPE\": \"dummy\" \n" +
" }, \n" +
" \"Readings\": { \"state\": { \"Value\":\"on\", \"Time\":\"2017-12-14 15:41:05\" } }, \n" +
" \"Attributes\": { \n" +
" \"alexaName\": \"Arbeitslicht\", \n" +
" \"alexaRoom\": \"alexaroom\", \n" +
" \"fhem_widget_command\": \"{ \\u0022allowed_values\\u0022 : [ \\u0022on\\u0022 ], \\u0022order\\u0022 : 0}\", \n" +
" \"icon\": \"scene_office\", \n" +
" \"room\": \"Alexa,habridge\", \n" +
" \"setList\": \"on off\", \n" +
" \"stateFormat\": \"-\", \n" +
" \"webCmd\": \"on\" \n" +
" } \n" +
" }, \n" +
" { \n" +
" \"Name\":\"DeckenlampeLinks\", \n" +
" \"PossibleSets\":\"on off dim dimup dimdown HSV RGB sync pair unpair\", \n" +
" \"PossibleAttrs\":\"alias comment:textField-long eventMap group room suppressReading userReadings:textField-long verbose:0,1,2,3,4,5 gamma dimStep defaultColor defaultRamp colorCast whitePoint event-on-change-reading event-on-update-reading event-aggregator event-min-interval stateFormat:textField-long timestamp-on-change-reading alexaName alexaRoom cmdIcon devStateIcon devStateStyle fhem_widget_command fhem_widget_command_2 fhem_widget_command_3 genericDeviceType:security,ignore,switch,outlet,light,blind,thermometer,thermostat,contact,garage,window,lock homebridgeMapping:textField-long icon sortby webCmd widgetOverride \n" +
" <a href=\"/fhem?detail=AlleLampen\">AlleLampen</a> AlleLampen_map\n" +
" <a href=\"/fhem?detail=DeckenLampen\">DeckenLampen</a> DeckenLampen_map structexclude userattr\", \n" +
" \"Internals\": { \n" +
" \"CONNECTION\": \"bridge-V3\", \n" +
" \"DEF\": \"RGBW2 bridge-V3:10.2.3.3\", \n" +
" \"IP\": \"10.2.3.3\", \n" +
" \"LEDTYPE\": \"RGBW2\", \n" +
" \"NAME\": \"DeckenlampeLinks\", \n" +
" \"NR\": \"18\", \n" +
" \"NTFY_ORDER\": \"50-DeckenlampeLinks\", \n" +
" \"PORT\": \"8899\", \n" +
" \"PROTO\": \"0\", \n" +
" \"SLOT\": \"5\", \n" +
" \"STATE\": \"off\", \n" +
" \"TYPE\": \"WifiLight\" \n" +
" }, \n" +
" \"Readings\": { \n" +
" \"RGB\": { \"Value\":\"000000\", \"Time\":\"2017-12-14 15:41:10\" }, \n" +
" \"brightness\": { \"Value\":\"0\", \"Time\":\"2017-12-14 15:41:10\" }, \n" +
" \"hue\": { \"Value\":\"0\", \"Time\":\"2017-12-14 15:41:10\" }, \n" +
" \"saturation\": { \"Value\":\"0\", \"Time\":\"2017-12-14 15:41:10\" }, \n" +
" \"state\": { \"Value\":\"off\", \"Time\":\"2017-12-14 15:41:10\" } \n" +
" }, \n" +
" \"Attributes\": { \n" +
" \"AlleLampen\": \"AlleLampen\", \n" +
" \"DeckenLampen\": \"DeckenLampen\", \n" +
" \"fhem_widget_command\": \"{ \\u0022locations\\u0022 : [ \\u0022APP\\u0022, \\u0022WATCH\\u0022, \\u0022WIDGET\\u0022 ], \\u0022allowed_values\\u0022 : [ \\u0022off\\u0022, \\u0022on\\u0022 ], \\u0022order\\u0022 : 6}\", \n" +
" \"room\": \"habridge,Alexa,WG-Zimmer\", \n" +
" \"userattr\": \"AlleLampen AlleLampen_map\n" +
" <a href=\"/fhem?detail=DeckenLampen\">DeckenLampen</a> DeckenLampen_map structexclude\", \n" +
" \"webCmd\": \"RGB\", \n" +
" \"widgetOverride\": \"RGB:colorpicker,RGB\" \n" +
" } \n" +
" } ], \n" +
" \"totalResultsReturned\":2 \n" +
"}\n" +
" </pre>\n" +
" </div>\n" +
" </body>\n" +
"</html>";
public final static String TestData2 = " <div id='content' >\n" +
" <pre>\n" + "{ \"Arg\":\"room=HaBridge\", \"Results\": [ { \"Name\":\"wifi_steckdose3\", \"PossibleSets\":\"on:noArg off:noArg off on toggle\", \"PossibleAttrs\":\"alias comment:textField-long eventMap group room suppressReading userReadings:textField-long verbose:0,1,2,3,4,5 IODev qos retain publishSet publishSet_.* subscribeReading_.* autoSubscribeReadings event-on-change-reading event-on-update-reading event-aggregator event-min-interval stateFormat:textField-long timestamp-on-change-reading alarmDevice:Actor,Sensor alarmSettings cmdIcon devStateIcon devStateStyle icon lightSceneParamsToSave lightSceneRestoreOnlyIfChanged:1,0 sortby structexclude webCmd webCmdLabel:textField-long widgetOverride userattr\", \"Internals\": { \"CHANGED\": \"null\", \"NAME\": \"wifi_steckdose3\", \"NR\": \"270\", \"STATE\": \"off\", \"TYPE\": \"MQTT_DEVICE\", \"retain\": \"*:1 \" }, \"Readings\": { \"state\": { \"Value\":\"OFF\", \"Time\":\"2018-01-01 23:01:21\" }, \"transmission-state\": { \"Value\":\"subscription acknowledged\", \"Time\":\"2018-01-03 22:34:00\" } }, \"Attributes\": { \"IODev\": \"myBroker\", \"alias\": \"Stecki\", \"devStateIcon\": \"on:black_Steckdose.on off:black_Steckdose.off\", \"event-on-change-reading\": \"state\", \"eventMap\": \"ON:on OFF:off\", \"publishSet\": \"on off toggle /SmartHome/az/stecker/cmnd/POWER\", \"retain\": \"1\", \"room\": \"HaBridge,Arbeitszimmer,mqtt\", \"stateFormat\": \"state\", \"subscribeReading_state\": \"/SmartHome/az/stecker/stat/POWER\", \"webCmd\": \"on:off:toggle\" } } ], \"totalResultsReturned\":1 }" +
" </pre>\n" +
" </div>\n" +
" </body>\n" +
"</html>";
public final static String TestData3 ="DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" +
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +
"<head root=\"/fhem\">\n" +
"<title>Home, Sweet Home</title>\n" +
"<link rel=\"shortcut icon\" href=\"/fhem/icons/favicon\" />\n" +
"<meta charset=\"UTF-8\">\n" +
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n" +
"<link href=\"/fhem/pgm2/style.css?v=1515015198\" rel=\"stylesheet\"/>\n" +
"<link href=\"/fhem/pgm2/jquery-ui.min.css\" rel=\"stylesheet\"/>\n" +
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/jquery.min.js\"></script>\n" +
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/jquery-ui.min.js\"></script>\n" +
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb.js\"></script>\n" +
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/doif.js\"></script>\n" +
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fronthemEditor.js\"></script>\n" +
"<script attr='' type=\"text/javascript\" src=\"/fhem/pgm2/fhemweb_readingsGroup.js\"></script>\n" +
"</head>\n" +
"<body name='Home, Sweet Home' fw_id='1490' generated=\"1515770038\" longpoll=\"websocket\" data-confirmDelete='1' data-confirmJSError='1' data-addHtmlTitle='1' data-availableJs='sortable,iconLabel,readingsHistory,colorpicker,iconButtons,fbcalllist,knob,weekprofile,iconRadio,readingsGroup,iconSwitch,uzsu' data-webName='WEB '>\n" +
"<div id=\"menuScrollArea\">\n" +
"</div>\n" +
"<div id='content' >\n" +
"<pre>{\n" +
"\"Arg\":\"room=HaBridge\",\n" +
"\"Results\": [\n" +
"{\n" +
"\"Name\":\"<a href='/fhem?detail=wifi_steckdose3'>wifi_steckdose3</a>\",\n" +
"\"PossibleSets\":\"on:noArg off:noArg off on toggle\",\n" +
"\"PossibleAttrs\":\"alias comment:textField-long eventMap group room suppressReading userReadings:textField-long verbose:0,1,2,3,4,5 IODev qos retain publishSet publishSet_.* subscribeReading_.* autoSubscribeReadings event-on-change-reading event-on-update-reading event-aggregator event-min-interval stateFormat:textField-long timestamp-on-change-reading alarmDevice:Actor,Sensor alarmSettings cmdIcon devStateIcon devStateStyle icon lightSceneParamsToSave lightSceneRestoreOnlyIfChanged:1,0 sortby structexclude webCmd webCmdLabel:textField-long widgetOverride userattr\",\n" +
"\"Internals\": {\n" +
"\"NAME\": \"<a href='/fhem?detail=wifi_steckdose3'>wifi_steckdose3</a>\",\n" +
"\"NR\": \"270\",\n" +
"\"STATE\": \"off\",\n" +
"\"TYPE\": \"MQTT_DEVICE\",\n" +
"\"retain\": \"*:1 \"\n" +
"},\n" +
"\"Readings\": {\n" +
"\"state\": { \"Value\":\"OFF\", \"Time\":\"2018-01-07 05:16:01\" },\n" +
"\"transmission-state\": { \"Value\":\"incoming publish received\", \"Time\":\"2018-01-07 05:16:01\" }\n" +
"},\n" +
"\"Attributes\": {\n" +
"\"IODev\": \"<a href='/fhem?detail=myBroker'>myBroker</a>\",\n" +
"\"alias\": \"Stecki\",\n" +
"\"devStateIcon\": \"on:black_Steckdose.on off:black_Steckdose.off\",\n" +
"\"event-on-change-reading\": \"state\",\n" +
"\"eventMap\": \"ON:on OFF:off\",\n" +
"\"publishSet\": \"on off toggle /SmartHome/az/stecker/cmnd/POWER\",\n" +
"\"retain\": \"1\",\n" +
"\"room\": \"HaBridge,Arbeitszimmer,mqtt\",\n" +
"\"stateFormat\": \"state\",\n" +
"\"subscribeReading_state\": \"/SmartHome/az/stecker/stat/POWER\",\n" +
"\"webCmd\": \"on:off:toggle\"\n" +
"}\n" +
"} ],\n" +
"\"totalResultsReturned\":1\n" +
"}\n" +
"</pre>\n" +
"</div>\n" +
"</body></html>";
}

View File

@@ -0,0 +1,43 @@
package com.bwssystems.HABridge.plugins.fhem;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Result {
@SerializedName("Name")
@Expose
private String name;
@SerializedName("PossibleSets")
@Expose
private String possibleSets;
@SerializedName("PossibleAttrs")
@Expose
private String possibleAttrs;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPossibleSets() {
return possibleSets;
}
public void setPossibleSets(String possibleSets) {
this.possibleSets = possibleSets;
}
public String getPossibleAttrs() {
return possibleAttrs;
}
public void setPossibleAttrs(String possibleAttrs) {
this.possibleAttrs = possibleAttrs;
}
}

View File

@@ -0,0 +1,34 @@
package com.bwssystems.HABridge.plugins.fibaro;
public class FibaroFilter {
boolean useSaveLogs;
boolean useUserDescription;
boolean scenesLiliCmddOnly;
boolean replaceTrash;
public boolean isUseSaveLogs() {
return useSaveLogs;
}
public void setUseSaveLogs(boolean useSaveLogs) {
this.useSaveLogs = useSaveLogs;
}
public boolean isUseUserDescription() {
return useUserDescription;
}
public void setUseUserDescription(boolean useUserDescription) {
this.useUserDescription = useUserDescription;
}
public boolean isReplaceTrash() {
return replaceTrash;
}
public void setReplaceTrash(boolean replaceTrash) {
this.replaceTrash = replaceTrash;
}
public boolean isScenesLiliCmddOnly() {
return scenesLiliCmddOnly;
}
public void setScenesLiliCmddOnly(boolean scenesLiliCmddOnly) {
this.scenesLiliCmddOnly = scenesLiliCmddOnly;
}
}

View File

@@ -0,0 +1,121 @@
package com.bwssystems.HABridge.plugins.fibaro;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.ColorData;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.plugins.fibaro.json.Device;
import com.bwssystems.HABridge.plugins.fibaro.json.Scene;
public class FibaroHome implements Home
{
private static final Logger log = LoggerFactory.getLogger(FibaroHome.class);
private Map<String, FibaroInfo> fibaros;
private Boolean validFibaro;
private boolean closed;
public FibaroHome(BridgeSettings bridgeSettings)
{
super();
closed = true;
createHome(bridgeSettings);
closed = false;
}
public List<Device> getDevices()
{
log.debug("consolidating devices for fibaros");
Iterator<String> keys = fibaros.keySet().iterator();
ArrayList<Device> deviceList = new ArrayList<>();
while(keys.hasNext())
{
String key = keys.next();
for(Device device : fibaros.get(key).getDevices())
deviceList.add(device);
}
return deviceList;
}
public List<Scene> getScenes()
{
log.debug("consolidating scenes for fibaros");
Iterator<String> keys = fibaros.keySet().iterator();
ArrayList<Scene> sceneList = new ArrayList<>();
while(keys.hasNext())
{
String key = keys.next();
for(Scene scene : fibaros.get(key).getScenes())
sceneList.add(scene);
}
return sceneList;
}
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body)
{
// Not a device handler
return null;
}
@Override
public Object getItems(String type)
{
if(validFibaro)
{
if(type.equalsIgnoreCase(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex]))
return getDevices();
if(type.equalsIgnoreCase(DeviceMapTypes.FIBARO_SCENE[DeviceMapTypes.typeIndex]))
return getScenes();
}
return null;
}
@Override
public void refresh() {
// noop
}
@Override
public Home createHome(BridgeSettings bridgeSettings)
{
validFibaro = bridgeSettings.getBridgeSettingsDescriptor().isValidFibaro();
log.info("Fibaro Home created." + (validFibaro ? "" : " No Fibaros configured."));
if(validFibaro)
{
fibaros = new HashMap<String, FibaroInfo>();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getFibaroAddress().getDevices().iterator();
while(theList.hasNext())
{
NamedIP aFibaro = theList.next();
fibaros.put(aFibaro.getName(), new FibaroInfo(aFibaro));
}
}
return this;
}
@Override
public void closeHome()
{
log.debug("Closing Home.");
if(closed) {
log.debug("Home is already closed....");
return;
}
fibaros = null;
closed = true;
}
}

View File

@@ -0,0 +1,188 @@
package com.bwssystems.HABridge.plugins.fibaro;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.plugins.fibaro.json.Device;
import com.bwssystems.HABridge.plugins.fibaro.json.Room;
import com.bwssystems.HABridge.plugins.fibaro.json.Scene;
import com.google.gson.Gson;
public class FibaroInfo
{
private static final Logger log = LoggerFactory.getLogger(FibaroInfo.class);
private final NamedIP fibaroAddress;
private final String fibaroAuth;
private final Gson gson;
private Boolean isDevMode;
private FibaroFilter theFilters;
public FibaroInfo(NamedIP addressName)
{
super();
fibaroAddress = addressName;
fibaroAuth = "Basic " + addressName.getUserPass64();
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
gson = new Gson();
theFilters = null;
if(fibaroAddress.getExtensions() != null) {
try {
theFilters = gson.fromJson(fibaroAddress.getExtensions(), FibaroFilter.class);
} catch(Exception e) {
log.warn("Could not read fibaro filters - continuing with defaults.");
theFilters = null;
}
}
if(theFilters == null) {
theFilters = new FibaroFilter();
theFilters.setUseSaveLogs(false);
theFilters.setUseUserDescription(false);
theFilters.setScenesLiliCmddOnly(false);
theFilters.setReplaceTrash(true);
}
}
private String request(String request)
{
String result = null;
try
{
URL url = new URL("http://" + fibaroAddress.getIp() + ":" + fibaroAddress.getPort() + request);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Authorization", fibaroAuth);
connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
connection.connect();
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder buffer = new StringBuilder();
String line;
while((line = br.readLine()) != null)
{
buffer.append(line).append("\n");
}
br.close();
result = buffer.toString();
}
catch(IOException e)
{
log.warn("Error while get getJson: {} ", request, e);
}
return result;
}
private String replaceTrash(String name)
{
String sanitizedName = name.replaceAll("[0-9:/-]", "");
sanitizedName = name.replaceAll("\\s+", " ");
return sanitizedName.trim();
}
private Room[] getRooms()
{
String result = null;
if(isDevMode)
result = FibaroTestData.RoomTestData;
else
result = request("/api/rooms");
log.debug("getRooms response: <<<" + result + ">>>");
Room[] rooms = result == null ? new Room[0] : gson.fromJson(result, Room[].class);
if(theFilters.isReplaceTrash())
for(Room r : rooms)
r.setName(replaceTrash(r.getName()));
return rooms;
}
public Device[] getDevices()
{
Room[] rooms = getRooms();
log.debug("getDevices Found: " + rooms.length + " rooms");
String result = null;
if(isDevMode)
result = FibaroTestData.DeviceTestData;
else
result = request("/api/devices?enabled=true&visible=true");
log.debug("getDevices response: <<<" + result + ">>>");
Device[] all_devices = result == null ? new Device[0] : gson.fromJson(result, Device[].class);
int count = 0;
for(Device d : all_devices)
if(d.getRoomID() > 0 && (theFilters.isUseSaveLogs() ? "true".equals(d.getProperties().getSaveLogs()) : true))
count++;
Device[] devices = new Device[count];
int i = 0;
for(Device d : all_devices)
if(d.getRoomID() > 0 && (theFilters.isUseSaveLogs() ? "true".equals(d.getProperties().getSaveLogs()) : true))
{
if(theFilters.isUseUserDescription() && d.getProperties().getUserDescription() != null && !d.getProperties().getUserDescription().isEmpty())
d.setName(d.getProperties().getUserDescription());
if(theFilters.isReplaceTrash())
d.setName(replaceTrash(d.getName()));
devices[i++] = d;
for(Room room : rooms)
if(d.getRoomID() == room.getId())
d.setRoomName(room.getName());
d.fibaroaddress = fibaroAddress.getIp();
d.fibaroport = fibaroAddress.getPort();
d.fibaroAuth = fibaroAuth;
d.fibaroname = fibaroAddress.getName();
}
log.debug("getDevices Found: " + devices.length + " devices");
return devices;
}
public Scene[] getScenes()
{
Room[] rooms = getRooms();
String result = null;
if(isDevMode)
result = FibaroTestData.SceneTestData;
else
result = request("/api/scenes?enabled=true&visible=true");
log.debug("getScenes response: <<<" + result + ">>>");
Scene[] all_scenes = result == null ? new Scene[0] : gson.fromJson(result, Scene[].class);
int count = 0;
for(Scene s : all_scenes)
if(!theFilters.isScenesLiliCmddOnly() || s.getLiliStartCommand() != null && !s.getLiliStartCommand().isEmpty())
count++;
Scene[] scenes = new Scene[count];
int i = 0;
for(Scene s : all_scenes)
if(!theFilters.isScenesLiliCmddOnly() || s.getLiliStartCommand() != null && !s.getLiliStartCommand().isEmpty())
{
if(theFilters.isReplaceTrash())
s.setName(replaceTrash(s.getName()));
scenes[i++] = s;
for(Room room : rooms)
if(s.getRoomID() == room.getId())
s.setRoomName(room.getName());
s.fibaroaddress = fibaroAddress.getIp();
s.fibaroport = fibaroAddress.getPort();
s.fibaroAuth = fibaroAuth;
s.fibaroname = fibaroAddress.getName();
}
log.debug("getScenes Found: " + count + " scenes");
return scenes;
}
}

View File

@@ -0,0 +1,34 @@
package com.bwssystems.HABridge.plugins.fibaro;
public class FibaroTestData {
public final static String DeviceTestData = "[{\"id\":7,\"name\":\"Deckenspots\",\"roomID\":12,\"type\":\"com.fibaro.FGD212\",\"baseType\":\"com.fibaro.multilevelSwitch\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":5,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"energy\",\"levelChange\",\"light\",\"power\",\"zwave\",\"zwaveAlarm\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"3.5\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"alarmLevel\":\"0\",\"alarmType\":\"0\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"23\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"15\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"1\",\"energy\":\"7.23\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"2\",\"parametersTemplate\":\"796\",\"power\":\"5.80\",\"productInfo\":\"1,15,1,2,16,0,3,5\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"25\",\"serialNumber\":\"h'000000000001c1b4\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"5\"},\"actions\":{\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stopLevelChange\":0,\"turnOff\":0,\"turnOn\":0},\"created\":1516643104,\"modified\":1516643104,\"sortOrder\":1},{\"id\":13,\"name\":\"Licht Küche\",\"roomID\":13,\"type\":\"com.fibaro.binarySwitch\",\"baseType\":\"com.fibaro.actor\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":10,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":fals" +
"e,\"interfaces\":[\"deviceGrouping\",\"energy\",\"light\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"3.2\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"2\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"2\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"1\",\"energy\":\"9.51\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"3\",\"parametersTemplate\":\"781\",\"power\":\"16.00\",\"productInfo\":\"1,15,2,3,16,0,3,2\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"serialNumber\":\"h'000000000000450d\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"true\"},\"actions\":{\"reconfigure\":0,\"reset\":0,\"turnOff\":0,\"turnOn\":0},\"created\":1516643104,\"modified\":1516643104,\"sortOrder\":2},{\"id\":14,\"name\":\"Lampe Tisch\",\"roomID\":14,\"type\":\"com.fibaro.binarySwitch\",\"baseType\":\"com.fibaro.actor\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":10,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"energy\",\"light\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"3.2\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"2\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"2\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"2\",\"energy\":\"29.15\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":" +
"\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"3\",\"parametersTemplate\":\"781\",\"power\":\"11.50\",\"productInfo\":\"1,15,2,3,16,0,3,2\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"serialNumber\":\"h'000000000000450d\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"true\"},\"actions\":{\"reconfigure\":0,\"reset\":0,\"turnOff\":0,\"turnOn\":0},\"created\":1516643104,\"modified\":1516643104,\"sortOrder\":3},{\"id\":18,\"name\":\"Licht\",\"roomID\":16,\"type\":\"com.fibaro.binarySwitch\",\"baseType\":\"com.fibaro.actor\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":15,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"energy\",\"light\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"3.2\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"2\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"2\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"1\",\"energy\":\"2.70\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"4\",\"parametersTemplate\":\"781\",\"power\":\"0.00\",\"productInfo\":\"1,15,2,3,16,0,3,2\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"serialNumber\":\"h'00000000000044b4\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTempl" +
"ate\":\"true\",\"userDescription\":\"\",\"value\":\"false\"},\"actions\":{\"reconfigure\":0,\"reset\":0,\"turnOff\":0,\"turnOn\":0},\"created\":1516643104,\"modified\":1516643104,\"sortOrder\":4},{\"id\":26,\"name\":\"Licht\",\"roomID\":5,\"type\":\"com.fibaro.FGD212\",\"baseType\":\"com.fibaro.multilevelSwitch\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":24,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"energy\",\"levelChange\",\"light\",\"power\",\"zwave\",\"zwaveAlarm\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"3.5\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"alarmLevel\":\"0\",\"alarmType\":\"0\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"23\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"15\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"1\",\"energy\":\"28.02\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"7\",\"parametersTemplate\":\"796\",\"power\":\"0.00\",\"productInfo\":\"1,15,1,2,16,0,3,5\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"0\",\"serialNumber\":\"h'0000000000001fec\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\"},\"actions\":{\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stopLevelChange\":0,\"turnOff\":0,\"turnOn\":0},\"created\":1516643105,\"modified\":1516643105,\"sortOrder\":5},{\"id\":57,\"name\":\"Fenster rechts\",\"roomID\":14,\"type\":\"com.fibaro.FGRM222\",\"baseType\":\"com.fibaro.FGR\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":56,\"remoteGatewayId\"" +
":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"energy\",\"levelChange\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"25.25\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"54\",\"deviceIcon\":\"87\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"0.78\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"19\",\"parametersTemplate\":\"721\",\"power\":\"0.00\",\"productInfo\":\"1,15,3,2,16,0,25,25\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionLocal\":\"0\",\"protectionLocalSupport\":\"5\",\"protectionRF\":\"0\",\"protectionRFSupport\":\"3\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"0\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\",\"value2\":\"0\"},\"actions\":{\"close\":0,\"open\":0,\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"setValue2\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stop\":0,\"stopLevelChange\":0},\"created\":1516643105,\"modified\":1516643105,\"sortOrder\":12},{\"id\":60,\"name\":\"Fenster mitte\",\"roomID\":14,\"type\":\"com.fibaro.FGRM222\",\"baseType\":\"com.fibaro.FGR\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":59,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"energy\",\"levelChange\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"25.25\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlT" +
"ype\":\"54\",\"deviceIcon\":\"87\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"0.71\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"20\",\"parametersTemplate\":\"721\",\"power\":\"0.00\",\"productInfo\":\"1,15,3,2,16,0,25,25\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionLocal\":\"0\",\"protectionLocalSupport\":\"5\",\"protectionRF\":\"0\",\"protectionRFSupport\":\"3\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"26\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\",\"value2\":\"0\"},\"actions\":{\"close\":0,\"open\":0,\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"setValue2\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stop\":0,\"stopLevelChange\":0},\"created\":1516643105,\"modified\":1516643105,\"sortOrder\":13},{\"id\":74,\"name\":\"Fenster links\",\"roomID\":14,\"type\":\"com.fibaro.FGRM222\",\"baseType\":\"com.fibaro.FGR\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":73,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"energy\",\"levelChange\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"25.25\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"54\",\"deviceIcon\":\"87\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"0.64\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"21\",\"parametersTemplate\":\"721\",\"power\":\"0.00\",\"productInfo\":\"1,15,3,2,16,0" +
",25,25\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionLocal\":\"0\",\"protectionLocalSupport\":\"5\",\"protectionRF\":\"0\",\"protectionRFSupport\":\"3\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"0\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\",\"value2\":\"0\"},\"actions\":{\"close\":0,\"open\":0,\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"setValue2\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stop\":0,\"stopLevelChange\":0},\"created\":1516643106,\"modified\":1516643106,\"sortOrder\":14},{\"id\":77,\"name\":\"Fenster Sideboard\",\"roomID\":14,\"type\":\"com.fibaro.FGRM222\",\"baseType\":\"com.fibaro.FGR\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":76,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"energy\",\"levelChange\",\"power\",\"zwave\",\"zwaveMultiChannelAssociation\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"25.25\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"54\",\"deviceIcon\":\"87\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"0.63\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"22\",\"parametersTemplate\":\"721\",\"power\":\"0.00\",\"productInfo\":\"1,15,3,2,16,0,25,25\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionLocal\":\"0\",\"protectionLocalSupport\":\"5\",\"protectionRF\":\"0\",\"protectionRFSupport\":\"3\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneAc" +
"tivation\":\"0\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\",\"value2\":\"0\"},\"actions\":{\"close\":0,\"open\":0,\"reconfigure\":0,\"reset\":0,\"sceneActivationSet\":0,\"setValue\":1,\"setValue2\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"stop\":0,\"stopLevelChange\":0},\"created\":1516643106,\"modified\":1516643106,\"sortOrder\":15},{\"id\":112,\"name\":\"Stehlampe\",\"roomID\":12,\"type\":\"com.fibaro.multilevelSwitch\",\"baseType\":\"com.fibaro.binarySwitch\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":111,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"fibaroFirmwareUpdate\",\"levelChange\",\"light\",\"zwave\",\"zwaveSwitchAll\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Domitech Products\",\"zwaveInfo\":\"3,4,5\",\"zwaveVersion\":\"5.14\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"23\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"15\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"firmwareUpdate\":\"{\\\"info\\\":\\\"\\\",\\\"progress\\\":0,\\\"status\\\":\\\"UpToDate\\\",\\\"updateVersion\\\":\\\"5.14\\\"}\",\"isLight\":\"true\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"27\",\"parametersTemplate\":\"807\",\"productInfo\":\"2,14,76,66,49,52,5,14\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"serialNumber\":\"\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"switchAllMode\":\"SWITCH_ALL_INCLUDED_IN_THE_ALL_ON_ALL_OFF_FUNCTIONALITY\",\"updateVersion\":\"\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\"},\"actions\":{\"abortUpdate\":1,\"reconfigure\":0,\"retryUpdate\":1,\"setValue\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"startUpdate\":1,\"stopLevelChange\":0,\"turnOff\":0,\"turnOn\":0,\"updateFirmware\":1},\"created\":1516643107,\"modified\":1516643107,\"sortOrder\":6},{\"id\":114,\"name\":\"RGBW\",\"roomID\":12,\"type\":\"com.fibaro.FGRGBW441M\",\"baseType\":\"com.fibaro.colorController\",\"enabled\":true,\"visible\":tr" +
"ue,\"isPlugin\":false,\"parentId\":113,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"deviceGrouping\",\"energy\",\"fibaroFirmwareUpdate\",\"levelChange\",\"light\",\"power\",\"zwave\",\"zwaveSwitchAll\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"26.25\",\"associationMode\":\"0\",\"brightness\":\"0\",\"buttonType\":\"0\",\"color\":\"0,0,0,0\",\"configured\":true,\"currentProgram\":\"0\",\"currentProgramID\":\"0\",\"dead\":\"false\",\"deviceControlType\":\"50\",\"deviceGroup\":\"[]\",\"deviceGroupMaster\":\"0\",\"deviceIcon\":\"15\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"1.72\",\"favoriteProgram\":\"0\",\"firmwareUpdate\":\"{\\\"info\\\":\\\"\\\",\\\"progress\\\":0,\\\"status\\\":\\\"UpToDate\\\",\\\"updateVersion\\\":\\\"26.25\\\"}\",\"isLight\":\"true\",\"lastColorSet\":\"0,0,0,0\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"mode\":\"0\",\"model\":\"\",\"nodeId\":\"28\",\"parametersTemplate\":\"231\",\"power\":\"0.00\",\"productInfo\":\"1,15,9,0,16,0,26,25\",\"programsSortOrder\":\"1,2,3,4,5\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"rememberColor\":\"false\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"switchAllMode\":\"SWITCH_ALL_INCLUDED_IN_THE_ALL_ON_ALL_OFF_FUNCTIONALITY\",\"updateVersion\":\"\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\"},\"actions\":{\"abortUpdate\":1,\"reconfigure\":0,\"reset\":0,\"retryUpdate\":1,\"setB\":1,\"setBrightness\":1,\"setColor\":1,\"setFavoriteProgram\":2,\"setG\":1,\"setR\":1,\"setValue\":1,\"setW\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"startProgram\":1,\"startUpdate\":1,\"stopLevelChange\":0,\"turnOff\":0,\"turnOn\":0,\"updateFirmware\":1},\"created\":1516643107,\"modified\":1516643107,\"sortOrder\":7},{\"id\":122,\"name\":\"Fenster\",\"roomID\":12,\"type\":\"com.fibaro.FGRM222\",\"baseType\":\"com.fibaro.FGR\",\"enabled\":true,\"visible\":true,\"isPlugin\":false,\"parentId\":121,\"remoteGatewayId\":0,\"viewXml\":false,\"configXml\":false,\"interfaces\":[\"energy\",\"fibaroFirmwareUpdate\",\"levelChange\",\"powe" +
"r\",\"zwave\",\"zwaveConfiguration\",\"zwaveProtection\",\"zwaveSceneActivation\"],\"properties\":{\"pollingTimeSec\":0,\"zwaveCompany\":\"Fibargroup\",\"zwaveInfo\":\"3,3,52\",\"zwaveVersion\":\"24.24\",\"RFProtectionState\":\"0\",\"RFProtectionSupport\":\"3\",\"configured\":true,\"dead\":\"false\",\"deviceControlType\":\"54\",\"deviceIcon\":\"87\",\"emailNotificationID\":\"0\",\"emailNotificationType\":\"0\",\"endPointId\":\"0\",\"energy\":\"0.17\",\"firmwareUpdate\":\"{\\\"info\\\":\\\"\\\",\\\"progress\\\":0,\\\"status\\\":\\\"UpToDate\\\",\\\"updateVersion\\\":\\\"24.24\\\"}\",\"liliOffCommand\":\"\",\"liliOnCommand\":\"\",\"localProtectionState\":\"0\",\"localProtectionSupport\":\"5\",\"log\":\"\",\"logTemp\":\"\",\"manufacturer\":\"\",\"markAsDead\":\"true\",\"model\":\"\",\"nodeId\":\"29\",\"parametersTemplate\":\"249\",\"power\":\"0.00\",\"productInfo\":\"1,15,3,1,16,1,24,24\",\"protectionExclusiveControl\":\"0\",\"protectionExclusiveControlSupport\":\"false\",\"protectionState\":\"0\",\"protectionTimeout\":\"0\",\"protectionTimeoutSupport\":\"false\",\"pushNotificationID\":\"0\",\"pushNotificationType\":\"0\",\"remoteGatewayId\":\"0\",\"saveLogs\":\"true\",\"sceneActivation\":\"0\",\"serialNumber\":\"\",\"showEnergy\":\"true\",\"smsNotificationID\":\"0\",\"smsNotificationType\":\"0\",\"updateVersion\":\"\",\"useTemplate\":\"true\",\"userDescription\":\"\",\"value\":\"0\",\"value2\":\"0\"},\"actions\":{\"abortUpdate\":1,\"close\":0,\"getParameter\":1,\"open\":0,\"reconfigure\":0,\"reset\":0,\"retryUpdate\":1,\"sceneActivationSet\":0,\"setParameter\":2,\"setValue\":1,\"setValue2\":1,\"startLevelDecrease\":0,\"startLevelIncrease\":0,\"startUpdate\":1,\"stop\":0,\"stopLevelChange\":0,\"updateFirmware\":1},\"created\":1516643108,\"modified\":1516643108,\"sortOrder\":16}]";
public final static String RoomTestData = "[{\"id\":4,\"name\":\"Dachboden\",\"sectionID\":4,\"icon\":\"room_bedroom\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":1},{\"id\":5,\"name\":\"Badezimmer\",\"sectionID\":5,\"icon\":\"room_wanna\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":2},{\"id\":6,\"name\":\"Büro\",\"sectionID\":5,\"icon\":\"room_office\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":3},{\"id\":7,\"name\":\"Flur\",\"sectionID\":5,\"icon\":\"room_schody2\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":4},{\"id\":8,\"name\":\"Schlafzimmer\",\"sectionID\":5,\"icon\":\"room_bedroom\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":5},{\"id\":9,\"name\":\"Emelie\",\"sectionID\":5,\"icon\":\"room_wardrobe\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":6},{\"id\":10,\"name\":\"Mino\",\"sectionID\":5,\"icon\":\"room_garage\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":7},{\"id\":11,\"name\":\"Flur\",\"sectionID\":6,\"icon\":\"room_drzwiwejsciowe\",\"defaultSensors\":{\"temperature\":181,\"humidity\":0,\"light\":182},\"defaultThermostat\":0,\"sortOrder\":8},{\"id\":12,\"name\":\"Wohnzimmer\",\"sectionID\":6,\"icon\":\"room_sofa\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":9},{\"id\":13,\"name\":\"Küche\",\"sectionID\":6,\"icon\":\"room_dining\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":10},{\"id\":14,\"name\":\"Esszimmer\",\"sectionID\":6,\"icon\":\"room_jadalnia\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":11},{\"id\":15,\"name\":\"HWR\",\"sectionID\":6,\"icon\":\"room_laundry\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":12},{\"id\":16,\"name\":\"Gäste WC\",\"sectionID\":6," +
"\"icon\":\"room_toilet\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":13},{\"id\":17,\"name\":\"Haus\",\"sectionID\":7,\"icon\":\"room_bedroom\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":14},{\"id\":18,\"name\":\"Terrasse\",\"sectionID\":7,\"icon\":\"room_bedroom\",\"defaultSensors\":{\"temperature\":0,\"humidity\":0,\"light\":0},\"defaultThermostat\":0,\"sortOrder\":15}]";
public final static String SceneTestData = "[" +
"{\"id\":33,\"name\":\"Fernsehlicht aus\",\"type\":\"com.fibaro.luaScene\",\"roomID\":12,\"iconID\":6,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":true,\"autostart\":true,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":4,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":true,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":17}," +
"{\"id\":68,\"name\":\"Rollos runterfahren\",\"type\":\"com.fibaro.luaScene\",\"roomID\":14,\"iconID\":6,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":true,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":true,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":8}," +
"{\"id\":69,\"name\":\"Rollos hochfahren\",\"type\":\"com.fibaro.luaScene\",\"roomID\":14,\"iconID\":6,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":true,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":true,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":9}," +
"{\"id\":93,\"name\":\"Alles aus Haus\",\"type\":\"com.fibaro.magicScene\",\"roomID\":12,\"runConfig\":\"TRIGGER_AND_MANUAL\",\"alexaProhibited\":false,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":false,\"properties\":\"{\\\"conditionDeviceId\\\":163,\\\"conditionObjectType\\\":\\\"device\\\",\\\"conditionId\\\":\\\"condition_centralSceneEvent1HeldDown_163\\\",\\\"conditionLua\\\":\\\"true\\\",\\\"conditionValue\\\":\\\"\\\",\\\"conditionRooms\\\":[],\\\"trigger\\\":\\\"163 CentralSceneEvent 1 HeldDown\\\\n163 CentralSceneEvent 1 Released\\\",\\\"actionDeviceId\\\":92,\\\"actionObjectType\\\":\\\"scene\\\",\\\"actionId\\\":\\\"action_runScene_92\\\",\\\"actionLua\\\":\\\"fibaro:startScene(92);\\\",\\\"actionValue\\\":\\\"\\\",\\\"actionRooms\\\":[],\\\"looping\\\":false}\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[{\"deviceId\":163,\"eventName\":\"CentralSceneEvent\",\"args\":[\"1\",\"Released\"]}],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[92],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":18}," +
"{\"id\":94,\"name\":\"Fernsehlicht an\",\"type\":\"com.fibaro.blockScene\",\"roomID\":12,\"iconID\":5,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":false,\"autostart\":true,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":false,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[112,114],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":10}," +
"{\"id\":95,\"name\":\"Fernsehlicht + aus\",\"type\":\"com.fibaro.blockScene\",\"roomID\":12,\"iconID\":5,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":false,\"autostart\":true,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":false,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[7,10,11,12,13,14,57,60,74,77,97,112,114],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":19}," +
"{\"id\":96,\"name\":\"Fernsehlicht Wohnzim\",\"type\":\"com.fibaro.magicScene\",\"roomID\":12,\"runConfig\":\"TRIGGER_AND_MANUAL\",\"alexaProhibited\":false,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":false,\"properties\":\"{\\\"conditionDeviceId\\\":163,\\\"conditionObjectType\\\":\\\"device\\\",\\\"conditionId\\\":\\\"condition_centralSceneEvent2Pressed_163\\\",\\\"conditionLua\\\":\\\"true\\\",\\\"conditionValue\\\":\\\"\\\",\\\"conditionRooms\\\":[],\\\"trigger\\\":\\\"163 CentralSceneEvent 2 Pressed\\\",\\\"actionDeviceId\\\":94,\\\"actionObjectType\\\":\\\"scene\\\",\\\"actionId\\\":\\\"action_runScene_94\\\",\\\"actionLua\\\":\\\"fibaro:startScene(94);\\\",\\\"actionValue\\\":\\\"\\\",\\\"actionRooms\\\":[],\\\"looping\\\":false}\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[{\"deviceId\":163,\"eventName\":\"CentralSceneEvent\",\"args\":[\"2\",\"Pressed\"]}],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[94],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":20}," +
"{\"id\":98,\"name\":\"Fersehlicht blau\",\"type\":\"com.fibaro.blockScene\",\"roomID\":12,\"iconID\":5,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":false,\"autostart\":true,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":false,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[112,114],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":11}," +
"{\"id\":99,\"name\":\"Fernsehlicht blau+au\",\"type\":\"com.fibaro.blockScene\",\"roomID\":12,\"iconID\":5,\"runConfig\":\"MANUAL_ONLY\",\"alexaProhibited\":false,\"autostart\":true,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":true,\"isLua\":false,\"properties\":\"\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[],\"weather\":[]},\"actions\":{\"devices\":[7,10,11,12,13,14,112,114],\"scenes\":[],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":21}," +
"{\"id\":100,\"name\":\"Fernsehlicht blau+au\",\"type\":\"com.fibaro.magicScene\",\"roomID\":12,\"runConfig\":\"TRIGGER_AND_MANUAL\",\"alexaProhibited\":false,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":false,\"properties\":\"{\\\"conditionDeviceId\\\":163,\\\"conditionObjectType\\\":\\\"device\\\",\\\"conditionId\\\":\\\"condition_centralSceneEvent3HeldDown_163\\\",\\\"conditionLua\\\":\\\"true\\\",\\\"conditionValue\\\":\\\"\\\",\\\"conditionRooms\\\":[],\\\"trigger\\\":\\\"163 CentralSceneEvent 3 HeldDown\\\\n163 CentralSceneEvent 3 Released\\\",\\\"actionDeviceId\\\":99,\\\"actionObjectType\\\":\\\"scene\\\",\\\"actionId\\\":\\\"action_runScene_99\\\",\\\"actionLua\\\":\\\"fibaro:startScene(99);\\\",\\\"actionValue\\\":\\\"\\\",\\\"actionRooms\\\":[],\\\"looping\\\":false}\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[{\"deviceId\":163,\"eventName\":\"CentralSceneEvent\",\"args\":[\"3\",\"Released\"]}],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[99],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":22}," +
"{\"id\":101,\"name\":\"Fersehlicht blau Woh\",\"type\":\"com.fibaro.magicScene\",\"roomID\":12,\"runConfig\":\"TRIGGER_AND_MANUAL\",\"alexaProhibited\":false,\"autostart\":false,\"protectedByPIN\":false,\"killable\":true,\"killOtherInstances\":false,\"maxRunningInstances\":2,\"runningInstances\":0,\"instances\":[],\"runningManualInstances\":0,\"visible\":false,\"properties\":\"{\\\"conditionDeviceId\\\":163,\\\"conditionObjectType\\\":\\\"device\\\",\\\"conditionId\\\":\\\"condition_centralSceneEvent3Pressed_163\\\",\\\"conditionLua\\\":\\\"true\\\",\\\"conditionValue\\\":\\\"\\\",\\\"conditionRooms\\\":[],\\\"trigger\\\":\\\"163 CentralSceneEvent 3 Pressed\\\",\\\"actionDeviceId\\\":98,\\\"actionObjectType\\\":\\\"scene\\\",\\\"actionId\\\":\\\"action_runScene_98\\\",\\\"actionLua\\\":\\\"fibaro:startScene(98);\\\",\\\"actionValue\\\":\\\"\\\",\\\"actionRooms\\\":[],\\\"looping\\\":false}\",\"triggers\":{\"properties\":[],\"globals\":[],\"events\":[{\"deviceId\":163,\"eventName\":\"CentralSceneEvent\",\"args\":[\"3\",\"Pressed\"]}],\"weather\":[]},\"actions\":{\"devices\":[],\"scenes\":[98],\"groups\":[]},\"liliStartCommand\":\"\",\"liliStopCommand\":\"\",\"sortOrder\":23}" +
"]";
public final static String CallActionTestData = "Fiabro action received";
public final static String SceneControlTestData = "Fiabro scene control received";
}

View File

@@ -0,0 +1,69 @@
package com.bwssystems.HABridge.plugins.fibaro.json;
import com.google.gson.annotations.SerializedName;
public class Device {
private String roomName;
@SerializedName("id")
private String id;
@SerializedName("name")
private String name;
@SerializedName("roomID")
private int roomID;
@SerializedName("type")
private String type;
@SerializedName("properties")
private DeviceProperties properties;
public String getRoomName() {
return roomName;
}
public void setRoomName(String roomName) {
this.roomName = roomName;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getRoomID() {
return roomID;
}
public String getType() {
return type;
}
public DeviceProperties getProperties() {
return properties;
}
public boolean isThermostat() {
return type.equals("com.fibaro.setPoint") || type.equals("com.fibaro.thermostatDanfoss")
|| type.equals("com.fibaro.thermostatHorstmann");
}
@Override
public String toString() {
return "{" + id + ", " + name + "}";
}
public String fibaroaddress;
public String fibaroport;
public String fibaroAuth;
public String fibaroname;
}

View File

@@ -0,0 +1,23 @@
package com.bwssystems.HABridge.plugins.fibaro.json;
import com.google.gson.annotations.SerializedName;
public class DeviceProperties {
@SerializedName("value")
private String value;
@SerializedName("saveLogs")
private String saveLogs;
@SerializedName("userDescription")
private String userDescription;
public String getValue() {
return value;
}
public String getSaveLogs() {
return saveLogs;
}
public String getUserDescription() {
return userDescription;
}
}

View File

@@ -0,0 +1,31 @@
package com.bwssystems.HABridge.plugins.fibaro.json;
import com.google.gson.annotations.SerializedName;
public class Room {
@SerializedName("id")
private int id;
@SerializedName("name")
private String name;
@SerializedName("sectionID")
private int sectionID;
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSectionID()
{
return sectionID;
}
}

View File

@@ -0,0 +1,61 @@
package com.bwssystems.HABridge.plugins.fibaro.json;
import com.google.gson.annotations.SerializedName;
public class Scene {
private String roomName;
@SerializedName("id")
private String id;
@SerializedName("name")
private String name;
@SerializedName("type")
private String type;
@SerializedName("roomID")
private int roomID;
@SerializedName("liliStartCommand")
private String liliStartCommand;
public String getRoomName() {
return roomName;
}
public void setRoomName(String roomName) {
this.roomName = roomName;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getRoomID() {
return roomID;
}
public String getLiliStartCommand()
{
return liliStartCommand;
}
@Override
public String toString() {
return "{" + id + ", " + name + "}";
}
public String fibaroaddress;
public String fibaroport;
public String fibaroAuth;
public String fibaroname;
}

View File

@@ -0,0 +1,17 @@
package com.bwssystems.HABridge.plugins.hal;
import java.util.List;
import com.google.gson.annotations.SerializedName;
public class DeviceElements {
@SerializedName(value="DeviceElements", alternate={"SceneElements", "GroupElements", "HVACElements", "MacroElements", "IrElements", "IrButtons"})
private List<DeviceName> DeviceElements;
public List<DeviceName> getDeviceElements() {
return DeviceElements;
}
public void setDeviceElements(List<DeviceName> deviceElements) {
DeviceElements = deviceElements;
}
}

View File

@@ -0,0 +1,17 @@
package com.bwssystems.HABridge.plugins.hal;
import com.google.gson.annotations.SerializedName;
public class DeviceName {
@SerializedName(value="DeviceName", alternate={"SceneName", "GroupName", "HVACName", "MacroName", "IrName", "IrButton"})
private String DeviceName;
public String getDeviceName() {
return DeviceName;
}
public void setDeviceName(String deviceName) {
DeviceName = deviceName;
}
}

Some files were not shown because too many files have changed in this diff Show More