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
This commit is contained in:
BWS Systems
2019-07-29 09:24:22 -05:00
committed by GitHub
112 changed files with 8454 additions and 3936 deletions

4
.gitignore vendored
View File

@@ -19,3 +19,7 @@ sftp-config\.json
.vscode/launch.test.json
.vscode/settings.json
.vscode/tasks.json
# dependencies
/node_modules
package-lock.json

140
README.md
View File

@@ -1,5 +1,5 @@
# ha-bridge
Emulates Philips Hue API to other home automation gateways such as an Amazon Echo/Dot Gen 1 (gen 2 has issues discovering ha-bridge) or other systems that support Philips Hue. The Bridge handles basic commands such as "On", "Off" and "brightness" commands of the hue protocol. This bridge can control most devices that have a distinct API.
Emulates Philips Hue API to other home automation gateways such as an Amazon Echo/Dot or other systems that support Philips Hue local network discovery. The ha-bridge is not part of meethue.philips.com so the cloud sign in does not apply to this system. The Bridge handles basic commands such as "On", "Off", "Brightness" and "Color" commands of the hue protocol. This bridge can control most devices that have a distinct API.
Here are some diagrams to put this software in perspective.
@@ -35,22 +35,18 @@ A Custom implementation path looks like this:
**NOTE: This software does not control Philips Hue devices directly. A physical Philips Hue Hub is required for that, by which the ha-bridge can then proxy all of your real Hue bridges behind this bridge.**
**ISSUE: Amazon Echo (2nd Generation) has issues finding the ha-bridge. The only workaround is to have a first generation Echo or Dot on your network that finds the ha-bridge.**
**ISSUE: Google Home now seems to not support local connection to Philips Hue Hubs and requires that it connect to meethue.com. Since the ha-bridge only emulates the local API, and is not associated with Philips, this method will not work. If you have an older Google Home application, this may still work. YMMV.**
**ISSUE: Google Home does NOT support local connection to Philips Hue Hubs and requires that it connect to meethue.com. Since the ha-bridge only emulates the local API, and is not associated with Philips, this method will not work. If you have an older Google Home application, this may still work. YMMV.**
**FAQ: Please look here for the current FAQs! https://github.com/bwssytems/ha-bridge/wiki/HA-Bridge-FAQs**
In the cases of systems that require authorization and/or have APIs that cannot be handled in the current method, a module may need to be built. The Harmony Hub is such a module and so is the Nest module. The Bridge has helpers to build devices for the gateway for the Logitech Harmony Hub, Vera, Vera Lite or Vera Edge, Nest, Somfy Tahoma, Home Assistant, Domoticz, MQTT, HAL, Fibaro, HomeWizard, LIFX, OpenHAB, FHEM, Broadlink and the ability to proxy all of your real Hue bridges behind this bridge.
In the cases of systems that require authorization and/or have APIs that cannot be handled in the current method, a module may need to be built. The Harmony Hub is such a module and so is the Nest module. The Bridge has helpers to build devices for the gateway for the Logitech Harmony Hub, Vera, Vera Lite or Vera Edge, Nest, Somfy Tahoma, Home Assistant, Domoticz, MQTT, HAL, Fibaro, HomeWizard, LIFX, OpenHAB, FHEM, Broadlink, Mozilla IOT, HomeGenie and the ability to proxy all of your real Hue bridges behind this bridge.
Alternatively the Bridge supports custom calls as well using http/https/udp and tcp such as the LimitlessLED/MiLight bulbs using the UDP protocol. Binary data is supported with UDP/TCP.
Alternatively the Bridge supports custom calls and executing programs/scripts as well using http/https/udp and tcp such as the LimitlessLED/MiLight bulbs using the UDP protocol. Binary data is supported with UDP/TCP.
This bridge was built to help put the Internet of Things together.
## Build
To customize and build it yourself, build a new jar with maven:
ATTENTION: This requires JDK 1.8 to build
```
mvn install
```
@@ -58,30 +54,23 @@ Otherwise, downloads are available at https://github.com/bwssytems/ha-bridge/rel
## Run
Then locate the jar and start the server with:
ATTENTION: This requires JDK 1.8 to run
ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below.
```
java -jar ha-bridge-5.2.2.jar
```
ATTENTION: If running Java9, you will need to add the xml bind module
```
java -jar --add-modules java.xml.bind ha-bridge-5.2.2.jar
java -jar ha-bridge-5.3.0.jar
```
## Manual installation of ha-bridge and setup of systemd service
Next gen Linux systems (this includes the Raspberry Pi), use systemd to run and manage services.
Here is a link on how to use systemd: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
Create the directory and make sure that ha-bridge-5.2.2.jar is in your /home/pi/ha-bridge directory.
Create the directory and make sure that ha-bridge-5.3.0.jar is in your /home/pi/ha-bridge directory.
```
pi@raspberrypi:~ $ mkdir ha-bridge
pi@raspberrypi:~ $ cd ha-bridge
pi@raspberrypi:~/ha-bridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v5.2.2/ha-bridge-5.2.2.jar
pi@raspberrypi:~/ha-bridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v5.3.0/ha-bridge-5.3.0.jar
```
Create the ha-bridge.service unit file:
@@ -100,7 +89,7 @@ After=network.target
Type=simple
WorkingDirectory=/home/pi/ha-bridge
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/ha-bridge/data/habridge.config /home/pi/ha-bridge/ha-bridge-5.2.2.jar
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/ha-bridge/data/habridge.config /home/pi/ha-bridge/ha-bridge-5.3.0.jar
[Install]
WantedBy=multi-user.target
@@ -273,15 +262,27 @@ java -jar -Dexec.garden=C:\Users\John\bin
```
## HA Bridge Usage and Configuration
This section will cover the basics of configuration and where this configuration can be done. This requires that you have started your bridge process and then have pointed your
favorite web interface by going to the http://<my ip address>:<port> or http://localhost:<port> with port you have assigned. The default quick link is http://localhost for your reference.
favorite web interface by going to the `http://<my ip address>:<port>` or `http://localhost:<port>` with port you have assigned. The default quick link is http://localhost for your reference.
### The Bridge Devices Tab
This screen allows you to see your devices you have configured for the ha-bridge to present to a controller, such as an Amazon Echo/Dot. It gives you a count of devices as there have been reports that the Echo only supports a limited number, but has been growing as of late, YMMV. You can test each device from this page as this calls the ha-bridge just as a controller would, i.e. the Echo. This is useful to make sure your configuration for each device is correct and for trouble shooting. You can also manages your devices as well by editing and making a new device copy as well as deleting it.
This screen allows you to see your devices you have configured for the ha-bridge to present to a controller, such as an Amazon Echo/Dot. You can test each device from this page as this calls the ha-bridge just as a controller would, i.e. the Echo. This is useful to make sure your configuration for each device is correct and for trouble shooting. You can also manage your devices as well by editing and making a new device copy as well as deleting it.
At the bottom of the screen is the "Bridge Device DB Backup" which can be accessed with clicking on the `+` to expand this frame. Here you can backup and restore configurations that you have saved. These configs can be named or by clicking the `Backup Device DB' button will create a backup and name it for you. You can manage these backups by restoring them or deleting them.
Each of the columns are sortable by the use of the little arrow in the upper left corner of the heading.
If you click on the ID number on a Device, this will toggle the ID number to lock/unlock that is used during renumbering. When the ID is '<b>Bolded</b>', it is locked. You can also hover your mouse to see the state. This allows you to renumber a part of your devices and keep the ID for others. All IDs will be unique.
There is a new feature for the Devices that can be selected on the 'Startup' column. If you choose to have certain devices executed when the bridge is started or restarted. You can can click this field to bring up the dialog which will allow you to enter what you want the device to do on startup. The default is to do nothing.
At the bottom of the screen is the "Bridge Device DB Backup" which can be accessed with clicking on the `+` to expand this frame. Here you can backup and restore configurations that you have saved. These configs can be named or by clicking the `Backup Device DB' button will create a backup and name it for you. You can manage these backups by restoring them, deleting them, downloading them by clicking on the file name and uploading a saved device DB backup on your local machine.
#### Renumber Devices
This changes the numbering of the added devices to start at 1 and goes up from there. It was originally intended for a conversion from the previous system version that used large numbers and was not necessary. This also allows the system to try and number sequentially. If you use this button, you will need to re-discover your devices as their ID's will have changed.
This changes the numbering of the added devices to start at the nmuber given in the 'Bridge Control' tab field 'ID Seed' and the default is 100 and goes up from there. It was originally intended for a conversion from the previous system version that used large numbers and was not necessary. This also allows the system to try and number sequentially. If you use this button, you will need to re-discover your devices as their ID's will have changed.
#### Link
If this is present, you have enabled the Hue link button feature for the ha-bridge. If you want a new system to recognize the ha-bridge, you will need to press this button when you are doing a discovery.
#### Manage Links
If this is present, you have enabled the Hue link button feature for the ha-bridge. This button will bring up a dialog which contains all of the registered users created by linking the ha-bridge with a device. This allows you to revoke control for a created user so that it cannot access the ha-bridge by deleting that link user.
#### Show devices visible to
This filter is to sort devices that have an IP filter set. Type in the IP that is used in your device filter field.
#### Filter device type
This filter is for a quick method to find specific devices by their type listed in the dropdown.
### The Bridge Control Tab
This is where all of the configuration occurs for what ports and IP's the bridge runs on. It also contains the configurations for target devices so that Helper Tabs for configuration can be added as well as the connection information to control those devices.
#### Bridge server
@@ -289,11 +290,35 @@ This field is used to test the bridge server with the UPNP IP Address and to mak
#### Bridge Control Buttons
These buttons are for managing the bridge. The Save button is enabled when there is a change to the configuration. The Bridge Reinitialize button will recycle the internal running of the bridge in the java process. The Stop button will stop the java process. The Refresh button will refresh the page and settings.
#### The Security Dialog
This is where you can set the different security settings for the ha-bridge. There are two settings, one for enabling Hue like operation to secure the Hue API with the internally generated user for the calls that are done after the link button. The other is to secure the hue API with a username/password that is created as well. The other fields are to add and delete users and to set and change passwords for those users. If there are no users in the system, the system will not require a username/password to operate.
This is where you can set the different security settings for the ha-bridge. The settings are the https enablement with the Keyfile path and password and described below, enabling Hue like operation to secure the Hue API with the internally generated user for the calls that are done after the link button. Another is to secure the hue API with a username/password that is created as well. The last one is the path to the exec garden if used for only allowing scripts and programs to be executed from this location only. The other fields are to add and delete users and to set and change passwords for those users. If there are no users in the system, the system will not require a username/password to operate.
The use https selection will require you to generate a java keytool keystore file for the ha-bridge to use. This will require the path to the keyfile and the password that was used to secure the keyfile. There are mulitple ways to add the key file. The basic way is to generate a self signed keystore using keytool as follows:
Step 1. Open the command console
Step 2. Run this command (Where indicate the number of days for which the certificate will be valid)
keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass ```<password>``` -validity 365 -keysize 2048
Step 3. Enter a password for the keystore. Note this ```<password>``` as you require this for configuring the server
Step 4. When prompted for first name and last name, enter the domain name of the server. For example, myserver or myserver.mycompany.com.
Step 5. Enter the other details, such as Organizational Unit, Organization, City, State, and Country.
Step 6. When prompted with Enter key ```<password>``` for , press Enter to use the same ```<password>``` as the keystore ```<password>```
Step 7. Run this command to verify the contents of the keystore
keytool -list -v -keystore selfsigned.jks
Step 8. When prompted, enter the keystore password note in Step 3. The basic information about the generated certificate is displayed. Verify that the Owner and Issuer are the same. Also, you should see the information you provided in Step 4 and 5.
The second way is to acquire a certified certificate to use in the keyfile. Such way to get one is use letsencrypt. Once you have the certifcate files you can follow this to create the keystore: https://community.letsencrypt.org/t/tutorial-java-keystores-jks-with-lets-encrypt/34754.
The easiest place to keep the keystore files is in your ha bridge data directory.
#### Configuration Path and File
The default location for the configuration file to contain the settings for the bridge is the relative path from where the bridge is started in "data/habridge.config". If you would like a different filename or directory, specify `<directory>/<filename>` explicitly.
#### Device DB Path and File
The default location for the db to contain the devices as they are added is "data/devices.db". If you would like a different filename or directory, specify `<directory>/<filename> explicitly.
The default location for the db to contain the devices as they are added is "data/devices.db". If you would like a different filename or directory, specify `<directory>/<filename>` explicitly.
#### UPNP IP Address
The server defaults to the first available address on the host if this is not given. This default may NOT be the correct IP that is your public IP for your host on the network. It is best to set this parameter to not have discovery issues. Replace this value with the server ipv4 address you would like to use as the address that any upnp device will call after discovery.
#### Use UPNP Address Interface
@@ -307,9 +332,9 @@ The server defaults to running on port 80. To override what the default is, spec
#### UPNP Response Port
The upnp response port that will be used. The default is 50000.
#### Vera Names and IP Addresses
Provide IP Addresses of your Veras that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Vera and device/scene you configure.
Provide IP Addresses of your Veras that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Vera device/scene you configure.
#### Fibaro Names and IP Addresses
Provide IP Addresses of your Fibaros that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Fibaro and device/scene you configure. There are filter switches available to limit some of the returns for devices and scenes such as use save logs, use user description, use only Lili command and a switch that cleans up format Trash Chars. The default filters are false for everything but Trash Chars.
Provide IP Addresses of your Fibaros that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Fibaro device/scene you configure. There are filter switches available to limit some of the returns for devices and scenes such as use save logs, use user description, use only Lili command and a switch that cleans up format Trash Chars. The default filters are false for everything but Trash Chars.
#### Harmony Names and IP Addresses
Provide IP Addresses of your Harmony Hubs that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the activity or buttons by the call it receives and send it to the target Harmony Hub and activity/button you configure. Also, an option of webhook can be called when the activity changes on the harmony hub that will send an HTTP GET call to the the address of your choosing. This can contain the replacement variables of ${activity.id} and/or ${activity.label}. Example : http://192.168.0.1/activity/${activity.id}/${activity.label} OR http://hook?a=${activity.label}
#### Hue Names and IP Addresses
@@ -317,27 +342,27 @@ Provide IP Addresses of your Hue Bridges that you want to proxy through the brid
Don't forget - You will need to push the link button when you got to the Hue Tab the first time after the process comes up. (The user name is not persistent when the process comes up.)
#### HAL Names and IP Addresses
Provide IP Addresses of your HAL Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target HAL and device/scene you configure.
Provide IP Addresses of your HAL Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target HAL device you configure.
#### MQTT Client IDs and IP Addresses
Provide Client ID and IP Addresses and ports of your MQTT Brokers that you want to utilize with the bridge. Also, you can provide the username and password if you have secured your MQTT broker which is optional. When these Client ID and IP's are given, the bridge will be able to publish MQTT messages by the call it receives and send it to the target MQTT Broker you configure. The MQTT Messages Tab will become available to help you build messages.
#### Home Assistant Names and IP Addresses
<Provide IP Addresses and ports of your Home Assistant that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Home Assistant and device/scene you configure.
<Provide IP Addresses and ports of your Home Assistant that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Home Assistant device you configure.
#### HomeWizard Gateways Names and IP Addresses
Provide IP Addresses of your HomeWizard Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target HomeWizard and device/scene you configure.
Provide IP Addresses of your HomeWizard Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target HomeWizard device you configure.
#### Domoticz Names and IP Addresses
Provide IP Addresses of your Domoticz Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Domoticz and device/scene you configure.
Provide IP Addresses of your Domoticz Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Domoticz device you configure.
#### Somfy Tahoma Names and IP Addresses
Provide user name and password used to login to www.tahomalink.com. This needs to be provided if you're using the Somfy Tahoma features (for connecting to IO Homecontrol used by Velux among others). There is no need to give any IP address or host information as this contacts your cloud account. *Note:* you have to 'turn on' a window to open it, and 'turn off' to close.
#### OpenHAB Names and IP Addresses
Provide IP Addresses of your OpenHAB Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target OpenHAB and device/scene you configure.
Provide IP Addresses of your OpenHAB Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target OpenHAB device you configure.
#### FHEM Names and IP Addresses
Provide IP Addresses of your FHEM Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target FHEM and device/scene you configure.
Provide IP Addresses of your FHEM Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target FHEM device you configure.
#### Mozilla IOT Names and IP Addresses
Provide IP Addresses of your Mozilla IOT Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices by the call it receives and send it to the target Mozilla IOT device you configure.
#### HomeGenie Names and IP Addresses
Provide IP Addresses of your HomeGenie Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices by the call it receives and send it to the target HomeGenie device you configure. There is an extra Other Types field that you can add types that are not within the defualt of "light", "Switch" and "Dimmer" that you may want to control.
#### Nest Username
The user name of the home.nest.com account for the Nest user. This needs to be given if you are using the Nest features. There is no need to give any ip address or host information as this contacts your cloud account.
#### Nest Password
The password for the user name of the home.nest.com account for the Nest user. This needs to be given if you are using the Nest features.
#### Nest Temp Fahrenheit
This setting allows the value being sent into the bridge to be interpreted as Fahrenheit or Celsius. The default is to have Fahrenheit.
Provide the username and password of the home.nest.com account for the Nest user. This needs to be given if you are using the Nest features. There is no need to give any ip address or host information as this contacts your cloud account. Also, you can set Nest Temp Fahrenheit that allows the value being sent into the bridge to be interpreted as Fahrenheit or Celsius. The default is to have Fahrenheit.
#### LIFX Support
This setting will have the ha-bridge look for LIFX devices on your network. Since this is broadcast based, there is no other info needed.
#### Broadlink Support
@@ -352,12 +377,16 @@ This setting is the time used in between button presses when there is multiple b
This controls how many log messages will be kept and displayed on the log tab. This does not affect what is written to the standard output for logging. The default is 512. Changing this will incur more memory usage of the process.
#### Trace UPNP Calls
Turn on tracing for upnp discovery messages to the log. The default is false.
#### Trace State Changes
Turn on tracing for calls to the ha-bridge and send these information messages to the log for debugging. This a quick way to watch the state changes without turning the HueMulator debugging. The default is false.
#### UPNP Send Delay
This setting is for the upnp spec to delay a certain amount between upnp response messages sent back to a client. This is defaulted to 650ms and can be tuned to any value up to 1500ms.
#### ID Seed
The seed that starts numbering from this value and is used in the 'Renumbering' button on the 'Bridge Devices' tab. The defaul for this value is 100.
#### My Echo URL
This sets the URL that is used in the menu bar to ge to your echo. For certain countries, this needs to be set to a different URL.
At the bottom of the screen is the "Bridge Settings Backup" which can be accessed with clicking on the `+` to expand this frame. Here you can backup and restore configurations that you have saved. These configs can be named or by clicking the `Backup Settings' button will create a backup and name it for you. You can manage these backups by restoring them or deleting them.
At the bottom of the screen is the "Bridge Settings Backup" which can be accessed with clicking on the `+` to expand this frame. Here you can backup and restore configurations that you have saved. These configs can be named or by clicking the `Backup Settings' button will create a backup and name it for you. You can manage these backups by restoring them, deleting them, downloading them by clicking on the file name and uploading a saved configuration backup on your local machine.
### The Logs Tab
This screen displays the last 512 or number of rows defined in the config screen of the log so you don't have to go to the output of your process. The `Update Log` button refreshes the log as this screen does not auto refresh. FYI, when the trace upnp setting is turned on in the configuration, the messages will show here.
@@ -367,13 +396,13 @@ You must configure devices before you will have anything for the Echo or other c
#### Helpers
The easy way to get devices configured is with the use of the helpers for the Vera or Harmony, Nest, Hue and others to create devices that the bridge will present.
For the Helpers, each item being presented from the target system has a button such as `Build Item`, `Build A Button` or specific tasks such as `Temp` for thermostats that is used to create the specific device parameters. The build action buttons will put you into the edit screen. The next thing to check is the name for the bridge device that it is something that makes sense especially if you using the ha-bridge with an Echo or Google Home as this is what the Echo or Google Home will interpret as the device you want. Also, you can go back to any helper tab and click a build action button to add another item for a multi-command. After you are done in the edit tab, click the `Add Bridge Device` to finish that selection setup.
For the Helpers, each item being presented from the target system has a button such as `Build Item`, `Build A Button` or specific tasks such as `Temp` for thermostats that is used to create the specific device parameters. The build action buttons will put you into the edit screen. The next thing to check is the name for the bridge device that it is something that makes sense especially if you using the ha-bridge with an Echo or Google Home as this is what the Echo or Google Home will interpret as the device you want. Also, you can go back to any helper tab and click a build action button to add another item for a multi-command. After you are done in the edit tab, click the `Add Bridge Device` to finish that selection setup. OR you can go back to any helper and use `Build Item` or as such to add another item into the current device being shown in the `Add/Edit` tab. This allows you to create custom devices that execute many devices at once.
The helper tabs will also show you what you have already configured for that target type. Click on the `+` and you will see them and be able to delete them.
#### The Add/Edit Tab
Another way to add a device is through the Manual Add Tab. This allows you to manually enter the name, the on and off URLs and select if there are custom handling with the type of call that can be made. This allows for control of anything that has a distinct request that can be executed so you are not limited to the Vera, Harmony, Nest or other Hue.
This is the main device editing page to modify details or you can add a device through this tab. This allows you to manually enter the name, the on/dim/off/color items and select if there are custom handling with the type of call that can be made. This allows for control of anything that has a distinct request that can be executed so you are not limited to the Vera, Harmony, Nest or other Hue.
There is a new format for the on/dim/off URL areas. The new editor handles the intricacies of the components, but is broken down here for explanation.
There is a new format for the on/dim/off/color URL areas. The new editor handles the intricacies of the components, but is broken down here for explanation.
It is imperative when adding a line by hand that you hit the ```Add``` button at the end of the line before adding or updating the whole entry.
@@ -452,34 +481,15 @@ Examples:
```
#### Multiple Call Construct
Also available is the ability to specify multiple commands in the On URL, Dim URL and Off URL areas by adding Json constructs listed here. This is only for the types of tcp, udp, http, https or a new exec type. Also within the item format you can specify delay in milliseconds and count per item. These new parameters work on device buttons for the Harmony as well.
Format Example in the URL areas:
```
[{"item":"http://192.168.1.1:8180/do/this/thing","type":"httpDevice"},
{"item":"http://192.168.1.1:8180/do/the/next/thing","delay":1000,"count":2,"type":"httpDevice"},
{"item":"http://192.168.1.1:8180/do/another/thing","type":"httpDevice"}]
[{"item":"udp://192.168.1.1:5000/0x450555","type":"udpDevice"},
{"item":"udp://192.168.1.1:5000/0x45${intensity.percent}55","type":"udpDevice"}]
[{"item":"udp://192.168.1.1:5000/0x450555","type":"udpDevice"},
{"item":"http://192.168.1.1:8180/do/this/thing","type":"httpDevice"},
{"item":"tcp://192.168.2.1/sendthisdata","type":"tcpDevice"},
{"item":"https://192.168.12.1/do/this/secure/thing","type":"httpDevice"},
{"item":"exec://notepad.exe","type":"cmdDevice"}]
```
Also available is the ability to specify multiple commands in the On Items, Dim Items, Off Items and Color Items by adding a new line to the item. When doing this manually, make sure to hit the `Add` button to the right of the row. If you do not hit the add button, it will not save when the add or update device buttons are selected. You can also add by going to a helper tab after you have entered the `Add/Edit` tab and using the helpers 'Build' buttons to add a new item line in the respected On/Dim/Off/Color items areas.
#### Script or Command Execution
The release as of v2.0.0 will now support the execution of a local script or program. This will blindly fire off a process to run and is bound by the privileges of the java process.
To configure this type of manual add, you will need to select the Device type of "Execute Script/Program".
In the URL areas, the format of the execution is just providing what command line you would like to run, or using the multiple call item construct described above.
```
[{"item":"exec://C:\\Users\\John\\Documents\\Applications\\putty.exe 192.168.1.1","type":"cmdDevice"},{"item":"exec://notepad.exe","type":"cmdDevice"}]
[{"item":"/home/pi/scripts/dothisscript.sh","type":"cmdDevice"}]
```
If you are running a script on a system, you will need to provide the shell interface it will use before the script such as `sh` or `cmd.exe`.
#### Value Passing Controls
There are multiple replacement constructs available to be put into any of the calls except Harmony items, Net Items and HAL items. These constructs are: "${time.format(Java time format string)}", "${intensity.percent}", "${intensity.percent.hex}", "${intensity.decimal_percent}", "${intensity.byte}", "${intensity.byte.hex}", "${intensity.math(using X in your calc)}" and "${intensity.math(using X in your calc).hex}".
@@ -489,6 +499,8 @@ For the items that want to have a date time put into the message, utilize ${time
Color has been added as a replacement control and the available values are ${color.r}, ${color.g}, ${color.b} which are representations of each color as 0 - 255. There are hex equivalents as well as ${color.rx}, ${color.gx}, ${color.bx} and ${color.rgbx} as 2 place hex representations except for rgbx which is a six place hex representation.
Color can also be replaced with ${color.hsl} which will output a string in the Hue/Saturation/Brightness comma-delineated format, useful for many devices under OpenHAB. The format will be a 0-360 hue value, a 0-100 saturation value, and a 0-100 brightness value, separated by commas. (Such devices usually accept either a full HSB value in that format, or a single ${intensity.percent} value for the dimming value, sent as raw text in a POST request.)
Special handling for milights is included and is handled by ${color.milight:x}. 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 necessary 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
@@ -530,7 +542,7 @@ To view or remove devices that Alexa knows about, you can use the mobile app `Me
## Google Assistant
Google Home is supported as of v3.2.0 and forward, but only if the bridge is running on port 80.
**ISSUE: Google Home now seems to not support local connection to Philips Hue Hubs and requires that it connect to meethue.com. Since the ha-bridge only emulates the local API, and is not associated with Philips, this method will not work. If you have an older Google Home application, this may still work. YMMV.**
**ISSUE: Google Home does NOT support local connection to Philips Hue Hubs and requires that it connect to meethue.com. Since the ha-bridge only emulates the local API, and is not associated with Philips, this method will not work. If you have an older Google Home application, this may still work. YMMV.**
Use the Google Home app on a phone to add new "home control" devices by going into `Settings / Home Control / +`
as described [here](https://support.google.com/googlehome/answer/7124115?hl=en&ref_topic=7125624#homecontrol).

267
pom.xml
View File

@@ -1,105 +1,102 @@
<?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>5.2.2</version>
<version>5.3.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>
<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>
<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>
<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>
<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>
<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>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>
<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>
@@ -107,68 +104,105 @@
<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.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-core</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>com.github.bwssytems</groupId>
<artifactId>lifx-sdk-java</artifactId>
<version>2.1.6</version>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.github.mob41</groupId>
<artifactId>broadlink-java-api</artifactId>
<version>master-SNAPSHOT</version>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
<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 -->
<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>2.22.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
@@ -230,8 +264,7 @@
</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>
@@ -241,4 +274,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View File

@@ -38,7 +38,7 @@ public class BridgeSecurity {
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
};
private char[] habridgeKey;
private static char[] habridgeKey;
private String execGarden;
private BridgeSecurityDescriptor securityDescriptor;
private boolean settingsChanged;
@@ -146,7 +146,16 @@ public class BridgeSecurity {
public String getExecGarden() {
return execGarden;
}
public void setUseLinkButton(boolean useThis) {
String getKeyfilePath() {
return securityDescriptor.getKeyfilePath();
}
String getKeyfilePassword() {
return securityDescriptor.getKeyfilePassword();
}
private void setUseLinkButton(boolean useThis) {
securityDescriptor.setUseLinkButton(useThis);
settingsChanged = true;
}
@@ -155,16 +164,77 @@ public class BridgeSecurity {
return securityDescriptor.isSecureHueApi();
}
public void setSecureHueApi(boolean theState) {
securityDescriptor.setSecureHueApi(theState);
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) {
@@ -306,6 +376,13 @@ public class BridgeSecurity {
return newUser;
}
public void removeHttpsSettings() {
securityDescriptor.setUseHttps(false);
securityDescriptor.setKeyfilePassword(null);
securityDescriptor.setKeyfilePath(null);
setSettingsChanged(true);
}
public void removeTestUsers() {
if (securityDescriptor.getWhitelist() != null) {
@@ -324,7 +401,7 @@ public class BridgeSecurity {
}
}
private String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
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");
@@ -336,7 +413,7 @@ public class BridgeSecurity {
return Base64.getEncoder().encodeToString(bytes);
}
private String decrypt(String property) throws GeneralSecurityException, IOException {
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");
@@ -355,7 +432,7 @@ public class BridgeSecurity {
public void removeAuthenticatedUser(Request request) {
request.session().removeAttribute(USER_SESSION_ID);
request.session().invalidate();
}
public User getAuthenticatedUser(Request request) {

View File

@@ -9,7 +9,10 @@ public class BridgeSecurityDescriptor {
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);
@@ -67,4 +70,28 @@ public class BridgeSecurityDescriptor {
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

@@ -12,8 +12,8 @@ import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.PosixFilePermission;
import java.security.GeneralSecurityException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
@@ -32,7 +32,7 @@ public class BridgeSettings extends BackupHandler {
private BridgeSettingsDescriptor theBridgeSettings;
private BridgeControlDescriptor bridgeControl;
private BridgeSecurity bridgeSecurity;
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
private static final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss");
public BridgeSettings() {
super();
@@ -59,10 +59,10 @@ public class BridgeSettings extends BackupHandler {
public BridgeSecurity getBridgeSecurity() {
return bridgeSecurity;
}
public static String getCurrentDate() {
return dateFormat.format(new Date());
}
public String getCurrentDate() {
return LocalDateTime.now().format(dateTimeFormat);
}
public void buildSettings() {
String addressString = null;
String theVeraAddress = null;
@@ -77,7 +77,6 @@ public class BridgeSettings extends BackupHandler {
}
String serverPortOverride = System.getProperty("server.port");
String serverIpOverride = System.getProperty("server.ip");
String upnpStrictOverride = System.getProperty("upnp.strict", "true");
if(configFileProperty != null)
{
log.info("reading from config file: " + configFileProperty);
@@ -160,7 +159,7 @@ public class BridgeSettings extends BackupHandler {
}
theBridgeSettings.setSomfyAddress(theSomfyList);
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
// 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"));
@@ -216,15 +215,16 @@ public class BridgeSettings extends BackupHandler {
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);
}
if(upnpStrictOverride != null)
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(upnpStrictOverride));
}
setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-");
bridgeSecurity.setSecurityData(theBridgeSettings.getSecurityData());

View File

@@ -90,6 +90,9 @@ public class BridgeSettingsDescriptor {
@SerializedName("openhabaddress")
@Expose
private IpList openhabaddress;
@SerializedName("moziotaddress")
@Expose
private IpList moziotaddress;
@SerializedName("hubversion")
@Expose
private String hubversion;
@@ -108,16 +111,31 @@ public class BridgeSettingsDescriptor {
@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("activeloggers")
// @Expose
// private List<NameValue> activeloggers;
@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("activeloggers")
// @Expose
// private List<NameValue> activeloggers;
private boolean settingsChanged;
private boolean veraconfigured;
private boolean fibaroconfigured;
@@ -132,14 +150,16 @@ public class BridgeSettingsDescriptor {
private boolean homewizardconfigured;
private boolean openhabconfigured;
private boolean fhemconfigured;
private boolean moziotconfigured;
private boolean homegenieconfigured;
// Deprecated settings
private String haltoken;
private boolean upnpstrict;
// private boolean upnpstrict;
public BridgeSettingsDescriptor() {
super();
this.upnpstrict = true;
// this.upnpstrict = true;
this.useupnpiface = false;
this.userooms = false;
this.traceupnp = false;
@@ -156,6 +176,8 @@ public class BridgeSettingsDescriptor {
this.homewizardconfigured = false;
this.lifxconfigured = false;
this.openhabconfigured = false;
this.moziotconfigured = false;
this.homegenieconfigured = false;
this.farenheit = true;
this.securityData = null;
this.settingsChanged = false;
@@ -163,456 +185,666 @@ public class BridgeSettingsDescriptor {
this.webaddress = "0.0.0.0";
this.hubversion = HueConstants.HUB_VERSION;
this.hubmac = null;
// this.activeloggers = 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;
}
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() {
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 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)
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))
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)
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))
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;
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))
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
return true;
}
public Boolean isValidNest() {
if(this.getNestpwd() == null || this.getNestpwd().equals(""))
if (this.getNestpwd() == null || this.getNestpwd().equals(""))
return false;
if(this.getNestuser() == null || this.getNestuser().equals(""))
if (this.getNestuser() == null || this.getNestuser().equals(""))
return false;
return true;
}
public Boolean isValidHue() {
if(this.getHueaddress() == null || this.getHueaddress().getDevices().size() <= 0)
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))
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))
if (this.getHaladdress() == null || this.getHaladdress().getDevices().size() <= 0)
return false;
if(devicesList.get(0).getPassword() == null || devicesList.get(0).getPassword().trim().isEmpty()) {
if(this.getHaltoken() == null || this.getHaltoken().equals(""))
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;
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))
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;
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))
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;
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))
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)
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))
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()))
for (int i = 0; i < hueaddress.getDevices().size(); i++) {
if (hueaddress.getDevices().get(i).getName().equals(aHue.getName()))
indexHue = i;
}
if(indexHue >= 0) {
if (indexHue >= 0) {
hueaddress.getDevices().set(indexHue, aHue);
this.setSettingsChanged(true);
}
}
public Boolean isValidHomeWizard() {
if(this.getHomeWizardAddress() == null || this.getHomeWizardAddress().getDevices().size() <= 0)
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))
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)
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))
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)
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))
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;
}
}

View File

@@ -34,6 +34,8 @@ public class DeviceMapTypes {
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;
@@ -69,6 +71,8 @@ public class DeviceMapTypes {
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;

View File

@@ -15,6 +15,7 @@ import com.bwssystems.HABridge.upnp.UpnpSettingsResource;
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:
@@ -39,12 +40,12 @@ public class HABridge {
UDPDatagramSender udpSender;
UpnpSettingsResource theSettingResponder;
UpnpListener theUpnpListener;
SystemControl theSystem;
BridgeSettings bridgeSettings;
Version theVersion;
@SuppressWarnings("unused")
HttpClientPool thePool;
ShutdownHook shutdownHook = null;
log.info("HA Bridge startup sequence...");
theVersion = new Version();
// Singleton initialization
@@ -53,9 +54,13 @@ public class HABridge {
bridgeSettings = new BridgeSettings();
// sparkjava config directive to set html static file location for Jetty
while(!bridgeSettings.getBridgeControl().isStop()) {
bridgeSettings.buildSettings();
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();
log.info("HA Bridge (v" + theVersion.getVersion() + ") initializing....");
// 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
@@ -68,6 +73,14 @@ public class HABridge {
// setup system control api first
theSystem = new SystemControl(bridgeSettings, theVersion);
theSystem.setupServer();
// 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) {
@@ -85,10 +98,13 @@ public class HABridge {
// wait for the sparkjava initialization of the rest api classes to be complete
awaitInitialization();
if(bridgeSettings.getBridgeSettingsDescriptor().isTraceupnp())
log.info("Traceupnp: upnp config address: " + bridgeSettings.getBridgeSettingsDescriptor().getUpnpConfigAddress() + "-useIface:" +
bridgeSettings.getBridgeSettingsDescriptor().isUseupnpiface() + " on web server: " +
bridgeSettings.getBridgeSettingsDescriptor().getWebaddress() + ":" + bridgeSettings.getBridgeSettingsDescriptor().getServerPort());
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();
@@ -96,13 +112,13 @@ public class HABridge {
// start the upnp ssdp discovery listener
theUpnpListener = null;
try {
theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl(), udpSender);
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" + theVersion.getVersion() + ") reinitialization requessted....");
log.info("HA Bridge (v{}) reinitialization requessted....", theVersion.getVersion());
else
bridgeSettings.getBridgeControl().setStop(true);
if(bridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged())
@@ -117,7 +133,7 @@ public class HABridge {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
log.error("Sleep error: " + e.getMessage());
log.error("Sleep error: {}", e.getMessage());
}
}
}
@@ -127,18 +143,24 @@ public class HABridge {
try {
HttpClientPool.shutdown();
} catch (InterruptedException e) {
log.warn("Error shutting down http pool: " + e.getMessage());;
log.warn("Error shutting down http pool: {}", e.getMessage());;
} catch (IOException e) {
log.warn("Error shutting down http pool: " + e.getMessage());;
log.warn("Error shutting down http pool: {}", e.getMessage());;
}
thePool = null;
log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting....");
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);
log.error("Could not start ha-bridge webservice on port [" + thePort + "] due to: " + e.getMessage());
System.exit(0);
}
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

@@ -20,6 +20,7 @@ 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;
@@ -27,6 +28,7 @@ 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 {
@@ -120,7 +122,7 @@ public class HomeManager {
aHome = new OpenHABHome(bridgeSettings);
resourceList.put(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the OpenHAB configuration if available
//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);
@@ -128,6 +130,14 @@ public class HomeManager {
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) {

View File

@@ -2,6 +2,8 @@ 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;
@@ -11,53 +13,101 @@ public class NamedIP {
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() {
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

@@ -4,6 +4,10 @@ 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;
@@ -23,4 +27,36 @@ public class SecurityInfo {
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

@@ -243,6 +243,24 @@ public class SystemControl {
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);
@@ -296,8 +314,7 @@ public class SystemControl {
post(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
log.debug("changesecurityinfo....");
SecurityInfo theInfo = new Gson().fromJson(request.body(), SecurityInfo.class);
bridgeSettings.getBridgeSecurity().setUseLinkButton(theInfo.isUseLinkButton());
bridgeSettings.getBridgeSecurity().setSecureHueApi(theInfo.isSecureHueApi());
bridgeSettings.getBridgeSecurity().setSecurityDataByInfo(theInfo);
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
response.status(HttpStatus.SC_OK);
response.type("application/json");
@@ -419,6 +436,41 @@ public class SystemControl {
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");
@@ -546,7 +598,12 @@ public class SystemControl {
log.warn("Error pinging listener. " + e.getMessage());
}
}
public String removeHttpsSettings() {
bridgeSettings.getBridgeSecurity().removeHttpsSettings();
return stop();
}
public String reinit() {
bridgeSettings.getBridgeControl().setReinit(true);
pingListener();
@@ -558,4 +615,5 @@ public class SystemControl {
pingListener();
return "{\"control\":\"stopping\"}";
}
}

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

@@ -2,6 +2,7 @@ package com.bwssystems.HABridge.dao;
public class BackupFilename {
private String filename;
private String file;
public String getFilename() {
return filename;
@@ -10,4 +11,13 @@ public class BackupFilename {
public void setFilename(String filename) {
this.filename = filename;
}
public String getFile() {
return file;
}
public void setFile(String file) {
this.file = file;
}
}

View File

@@ -86,7 +86,13 @@ public class DeviceDescriptor{
@SerializedName("onWhenDimPresent")
@Expose
private boolean onWhenDimPresent;
@SerializedName("lockDeviceId")
@Expose
private boolean lockDeviceId;
@SerializedName("startupActions")
@Expose
private String startupActions;
public String getName() {
return name;
}
@@ -333,4 +339,20 @@ public class DeviceDescriptor{
}
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;
}
}

View File

@@ -1,6 +1,5 @@
package com.bwssystems.HABridge.dao;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
@@ -18,6 +17,7 @@ import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.api.hue.DeviceResponse;
import com.bwssystems.HABridge.api.hue.DeviceState;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.plugins.hue.HueHome;
import com.bwssystems.HABridge.util.BackupHandler;
@@ -37,45 +37,47 @@ import java.util.Arrays;
*/
public class DeviceRepository extends BackupHandler {
private Map<String, DeviceDescriptor> devices;
private Path repositoryPath;
private Path repositoryPath;
private Gson gson;
private Integer nextId;
private Logger log = LoggerFactory.getLogger(DeviceRepository.class);
public DeviceRepository(String deviceDb) {
private Integer nextId;
private Integer seedId;
private Logger log = LoggerFactory.getLogger(DeviceRepository.class);
public DeviceRepository(String deviceDb, Integer seedid) {
super();
gson =
new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
repositoryPath = null;
repositoryPath = Paths.get(deviceDb);
setupParams(repositoryPath, ".bk", "device.db-");
nextId = 0;
nextId = seedid;
seedId = seedid;
_loadRepository(repositoryPath);
}
public void loadRepository() {
if(repositoryPath != null)
_loadRepository(repositoryPath);
}
private void _loadRepository(Path aPath){
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)
{
if (jsonContent != null) {
DeviceDescriptor list[] = gson.fromJson(jsonContent, DeviceDescriptor[].class);
for(int i = 0; i < list.length; i++) {
list[i].setDeviceState(null);
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) {
if (Integer.decode(list[i].getId()) > nextId) {
nextId = Integer.decode(list[i].getId());
}
}
}
}
}
}
public List<DeviceDescriptor> findAll() {
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
return list;
@@ -83,8 +85,8 @@ public class DeviceRepository extends BackupHandler {
public List<DeviceDescriptor> findActive() {
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>();
for(DeviceDescriptor aDevice : new ArrayList<DeviceDescriptor>(devices.values())) {
if(!aDevice.isInactive())
for (DeviceDescriptor aDevice : new ArrayList<DeviceDescriptor>(devices.values())) {
if (!aDevice.isInactive())
list.add(aDevice);
}
return list;
@@ -101,12 +103,12 @@ public class DeviceRepository extends BackupHandler {
DeviceDescriptor theDevice;
String theRequesterAddress;
HashMap<String,String > addressMap;
HashMap<String, String> addressMap;
while (anIterator.hasNext()) {
theDevice = anIterator.next();
theRequesterAddress = theDevice.getRequesterAddress();
addressMap = new HashMap<String, String>();
if(theRequesterAddress != null) {
if (theRequesterAddress != null) {
if (theRequesterAddress.contains(",")) {
String[] theArray = theRequesterAddress.split(",");
for (String v : theArray) {
@@ -121,151 +123,199 @@ public class DeviceRepository extends BackupHandler {
return theReturnList;
}
public Map<String, DeviceResponse> findAllByGroupWithState(String[] lightsInGroup, String anAddress, HueHome myHueHome, Gson aGsonBuilder) {
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) {
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()))) {
for (DeviceDescriptor light : (ignoreAddress ? lights.values()
: findAllByRequester(anAddress, lights.values()))) {
DeviceResponse deviceResponse = null;
if(!light.isInactive()) {
if (!light.isInactive()) {
if (light.containsType(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
CallItem[] callItems = null;
try {
if(light.getOnUrl() != null)
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());
} 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"))) {
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);
deviceResponseMap.put(light.getId(), deviceResponse);
}
}
return (deviceResponseMap.size() == 0) ? null : deviceResponseMap;
}
public DeviceDescriptor findOne(String id) {
return devices.get(id);
}
return devices.get(id);
}
private void put(String id, DeviceDescriptor aDescriptor) {
devices.put(id, 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 {
nextId++;
descriptors[i].setId(String.valueOf(nextId));
}
if(descriptors[i].getUniqueid() == null || descriptors[i].getUniqueid().length() == 0) {
String hexValue = HexLibrary.encodeUsingBigIntegerToString(descriptors[i].getId());
descriptors[i].setUniqueid("00:17:88:5E:D3:" + hexValue + "-" + hexValue);
}
put(descriptors[i].getId(), descriptors[i]);
theNames = theNames + " " + descriptors[i].getName() + ", ";
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("00:17:88:5E:D3:" + 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);
}
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>();;
nextId = 0;
log.debug("Renumber devices.");
while(deviceIterator.hasNext()) {
nextId++;
DeviceDescriptor theDevice = deviceIterator.next();
theDevice.setId(String.valueOf(nextId));
String hexValue = HexLibrary.encodeUsingBigIntegerToString(nextId.toString());
theDevice.setUniqueid("00:17:88:5E:D3:" + hexValue + "-" + hexValue);
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";
}
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("00:17:88:5E:D3:" + 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 String hueUniqueId(Integer anId) {
String theUniqueId;
Integer newValue;
String hexValueLeft;
String hexValueRight;
newValue = anId % 256;
if (newValue <= 0)
newValue = 1;
else if (newValue > 255)
newValue = 255;
hexValueLeft = HexLibrary.byteToHex(newValue.byteValue());
newValue = anId / 256;
newValue = newValue % 256;
if (newValue < 0)
newValue = 0;
else if (newValue > 255)
newValue = 255;
hexValueRight = HexLibrary.byteToHex(newValue.byteValue());
theUniqueId = String.format("%s-%s", hexValueLeft, hexValueRight).toUpperCase();
return theUniqueId;
}
}

View File

@@ -201,11 +201,15 @@ public class GroupRepository extends BackupHandler {
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)){
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));

View File

@@ -44,13 +44,15 @@ public class DeviceResource {
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, HomeManager aHomeManager) {
bridgeSettings = theSettings;
this.deviceRepository = new DeviceRepository(bridgeSettings.getBridgeSettingsDescriptor().getUpnpDeviceDb());
this.deviceRepository = new DeviceRepository(bridgeSettings.getBridgeSettingsDescriptor().getUpnpDeviceDb(), bridgeSettings.getBridgeSettingsDescriptor().getSeedid());
this.groupRepository = new GroupRepository(bridgeSettings.getBridgeSettingsDescriptor().getUpnpGroupDb());
homeManager = aHomeManager;
aGsonHandler = new GsonBuilder().create();
errorMessage = null;
setupEndpoints();
}
@@ -83,7 +85,7 @@ public class DeviceResource {
return "";
});
post(API_CONTEXT, "application/json", (request, response) -> {
log.debug("Create a Device(s) - request body: " + request.body());
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);
@@ -93,13 +95,12 @@ public class DeviceResource {
}
@SuppressWarnings("unused")
CallItem[] callItems = null;
String errorMessage = 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.debug(errorMessage);
log.warn(errorMessage);
return new ErrorMessage(errorMessage);
}
}
@@ -109,7 +110,7 @@ public class DeviceResource {
} catch(JsonSyntaxException e) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "Bad on URL JSON in create device(s) for name: " + devices[i].getName() + " with on URL: " + devices[i].getOnUrl();
log.debug(errorMessage);
log.warn(errorMessage);
return new ErrorMessage(errorMessage);
}
try {
@@ -118,7 +119,7 @@ public class DeviceResource {
} 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.debug(errorMessage);
log.warn(errorMessage);
return new ErrorMessage(errorMessage);
}
try {
@@ -127,7 +128,7 @@ public class DeviceResource {
} 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.debug(errorMessage);
log.warn(errorMessage);
return new ErrorMessage(errorMessage);
}
try {
@@ -136,13 +137,13 @@ public class DeviceResource {
} 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.debug(errorMessage);
log.warn(errorMessage);
return new ErrorMessage(errorMessage);
}
}
deviceRepository.save(devices);
log.debug("Created a Device(s): " + request.body());
log.debug("Created a Device(s): {}", request.body());
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.status(HttpStatus.SC_CREATED);
@@ -160,16 +161,17 @@ public class DeviceResource {
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);
if(deviceRepository.findOne(request.params(":id")) == null){
log.debug("Could not save an edited device, Device Id not found: " + request.params(":id"));
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("Could not save an edited device, Device Id not found: " + request.params(":id") + " ");
return new ErrorMessage(errorMessage);
}
else
{
log.debug("Saving an edited Device: " + device.getName());
log.debug("Saving an edited Device: {}", device.getName());
if (device.getDeviceType() != null)
device.setDeviceType(device.getDeviceType());
@@ -187,17 +189,19 @@ 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) {
errorMessage = "Could not find, id: " + request.params(":id");
log.warn(errorMessage);
response.status(HttpStatus.SC_NOT_FOUND);
return new ErrorMessage("Could not find, id: " + request.params(":id") + " ");
return new ErrorMessage(errorMessage);
}
else
response.status(HttpStatus.SC_OK);
@@ -206,11 +210,13 @@ public class DeviceResource {
delete (API_CONTEXT + "/:id", "application/json", (request, response) -> {
String anId = request.params(":id");
log.debug("Delete a device: " + anId);
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("Could not delete, id: " + anId + " not found. ");
return new ErrorMessage(errorMessage);
}
else
{
@@ -333,6 +339,18 @@ public class DeviceResource {
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();
@@ -340,7 +358,7 @@ public class DeviceResource {
get (API_CONTEXT + "/refresh/:typeIndex", "application/json", (request, response) -> {
String typeIndex = request.params(":typeIndex");
log.debug("Refresh Home: " + typeIndex);
log.debug("Refresh Home: {}", typeIndex);
response.status(HttpStatus.SC_OK);
homeManager.findResource(typeIndex).refresh();
return null;
@@ -362,11 +380,46 @@ public class DeviceResource {
}, new JsonTransformer());
get (API_CONTEXT + "/backup/available", "application/json", (request, response) -> {
log.debug("Get backup filenames");
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);
@@ -377,7 +430,7 @@ public class DeviceResource {
return "";
});
put (API_CONTEXT + "/backup/create", "application/json", (request, response) -> {
log.debug("Create backup: " + request.body());
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()));
@@ -394,7 +447,7 @@ public class DeviceResource {
return "";
});
post (API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
log.debug("Delete backup: " + request.body());
log.debug("Delete backup: {}", request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
if(aFilename != null)
deviceRepository.deleteBackup(aFilename.getFilename());
@@ -413,7 +466,7 @@ public class DeviceResource {
return "";
});
post (API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
log.debug("Restore backup: " + request.body());
log.debug("Restore backup: {}", request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
if(aFilename != null) {
deviceRepository.restoreBackup(aFilename.getFilename());

View File

@@ -19,4 +19,10 @@ public class ColorData {
return mode;
}
public String toString() {
String formatString;
formatString = "Color Data mode: " + mode + ", data: " + data;
return formatString;
}
}

View File

@@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.awt.Color;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,93 +20,184 @@ public class ColorDecode {
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_HSL = "${color.hsl}";
private static final Pattern COLOR_MILIGHT = Pattern.compile("\\$\\{color.milight\\:([01234])\\}");
public static List<Integer> convertHSLtoRGB(HueSatBri hsl) {
List<Integer> rgb;
float decimalBrightness = (float) 0.0;
float var_1 = (float) 0.0;
float var_2 = (float) 0.0;
float h = (float) 0.0;
float h2 = (float) 0.0;
float s = (float) 0.0;
double r = 0.0;
double g = 0.0;
double b = 0.0;
if(hsl.getBri() > 0)
decimalBrightness = (float) (hsl.getBri() / 255.0);
if(hsl.getHue() > 0) {
h = ((float)hsl.getHue() / (float)65535.0);
h2 = h + (float)0.5;
if(h2 > 1.0) {
h2 = h2 - (float)1.0;
}
}
if(hsl.getSat() > 0) {
s = (float)(hsl.getSat() / 254.0);
}
if (s == 0)
{
r = decimalBrightness * (float)255;
g = decimalBrightness * (float)255;
b = decimalBrightness * (float)255;
}
else
{
if (decimalBrightness < 0.5)
{
var_2 = decimalBrightness * (1 + s);
}
else
{
var_2 = (decimalBrightness + s) - (s * decimalBrightness);
};
var_1 = 2 * decimalBrightness - var_2;
float onethird = (float)0.33333;
float h2Plus = (h2 + onethird);
float h2Minus = (h2 - onethird);
log.debug("calculate HSL vars - var1: " + var_1 + ", var_2: " + var_2 + ", h2: " + h2 + ", h2 + 1/3: " + h2Plus + ", h2 - 1/3: " + h2Minus);
r = 255 * hue_2_rgb(var_1, var_2, h2Plus);
g = 255 * hue_2_rgb(var_1, var_2, h2);
b = 255 * hue_2_rgb(var_1, var_2, h2Minus);
};
rgb = new ArrayList<Integer>();
rgb.add((int) Math.round(r));
rgb.add((int) Math.round(g));
rgb.add((int) Math.round(b));
log.debug("Color change with HSL: " + hsl + ". Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " "
+ rgb.get(2));
return rgb;
}
public static float hue_2_rgb(float v1, float v2, float vh) {
log.debug("hue_2_rgb vh: " + vh);
if (vh < 0.0)
{
vh = vh + (float)1;
};
if (vh > 1.0)
{
vh = vh - (float)1;
};
if (((float)6.0 * vh) < 1.0)
{
return (v1 + (v2 - v1) * (float)6.0 * vh);
};
if (((float)2.0 * vh) < 1.0)
{
return (v2);
};
if ((3.0 * vh) < 2.0)
{
return (v1 + (v2 - v1) * (((float)2.0 / (float)3.0 - vh) * (float)6.0));
};
return (v1);
}
public static List<Integer> convertCIEtoRGB(List<Double> xy, int brightness) {
List<Integer> rgb;
double x = xy.get(0); // the given x value
double y = xy.get(1); // the given y value
double z = 1.0 - x - y;
double Y = (double)brightness/(double)254.00; // The given brightness value
double Y = (double) brightness / (double) 254.00; // The given brightness value
double X = (Y / y) * x;
double Z = (Y / y) * z;
double r = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
double r = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
double g = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
double b = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
double b = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
if (r > b && r > g && r > 1.0) {
g = g / r;
b = b / r;
r = 1.0;
}
else if (g > b && g > r && g > 1.0) {
} else if (g > b && g > r && g > 1.0) {
r = r / g;
b = b / g;
g = 1.0;
}
else if (b > r && b > g && b > 1.0) {
} else if (b > r && b > g && b > 1.0) {
r = r / b;
g = g / b;
b = 1.0;
}
r = r <= 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055;
g = g <= 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055;
b = b <= 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055;
if (r > b && r > g) {
// red is biggest
if (r > 1.0) {
g = g / r;
b = b / r;
r = 1.0;
}
}
else if (g > b && g > r) {
// green is biggest
if (g > 1.0) {
r = r / g;
b = b / g;
g = 1.0;
}
}
else if (b > r && b > g) {
// blue is biggest
if (b > 1.0) {
r = r / b;
g = g / b;
b = 1.0;
}
}
if(r < 0.0)
r = 0;
if(g < 0.0)
g = 0;
if(b < 0.0)
b = 0;
rgb = new ArrayList<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 XY: " + x + " " + y + " Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + rgb.get(2));
if (r > b && r > g) {
// red is biggest
if (r > 1.0) {
g = g / r;
b = b / r;
r = 1.0;
}
} else if (g > b && g > r) {
// green is biggest
if (g > 1.0) {
r = r / g;
b = b / g;
g = 1.0;
}
} else if (b > r && b > g) {
// blue is biggest
if (b > 1.0) {
r = r / b;
g = g / b;
b = 1.0;
}
}
if (r < 0.0)
r = 0;
if (g < 0.0)
g = 0;
if (b < 0.0)
b = 0;
rgb = new ArrayList<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 XY: " + x + " " + y + " Resulting RGB Values: " + rgb.get(0) + " " + rgb.get(1)
+ " " + rgb.get(2));
return rgb;
}
// took that approximation from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
// took that approximation from
// http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
public static List<Integer> convertCTtoRGB(Integer ct) {
double temperature = 1000000.0 / (double)ct;
double temperature = 1000000.0 / (double) ct;
temperature /= 100;
double r,g,b;
double r, g, b;
if (temperature <= 66) {
r = 255;
g = temperature;
g = 99.4708025861 * Math.log(g) - 161.1195681661;
g = 99.4708025861 * Math.log(g) - 161.1195681661;
} else {
r = temperature - 60;
r = 329.698727446 * (Math.pow(r, -0.1332047592));
@@ -124,25 +215,26 @@ public class ColorDecode {
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;
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;
}
if (value > 255.0) {
value = 255;
}
return value;
value = 0;
}
if (value > 255.0) {
value = 255;
}
return value;
}
@SuppressWarnings("unchecked")
@@ -157,25 +249,30 @@ public class ColorDecode {
ColorData.ColorMode colorMode = colorData.getColorMode();
List<Integer> rgb = null;
if (colorMode == ColorData.ColorMode.XY) {
rgb = convertCIEtoRGB((List<Double>)colorData.getData(), setIntensity);
rgb = convertCIEtoRGB((List<Double>) colorData.getData(), setIntensity);
} else if (colorMode == ColorData.ColorMode.CT) {
rgb = convertCTtoRGB((Integer)colorData.getData());
rgb = convertCTtoRGB((Integer) colorData.getData());
} else if (colorMode == ColorData.ColorMode.HS) {
rgb = convertHSLtoRGB((HueSatBri) colorData.getData());
}
while(notDone) {
while (notDone) {
notDone = false;
if (request.contains(COLOR_R)) {
request = request.replace(COLOR_R, isHex ? String.format("%02X", rgb.get(0)) : String.valueOf(rgb.get(0)));
request = request.replace(COLOR_R,
isHex ? String.format("%02X", rgb.get(0)) : String.valueOf(rgb.get(0)));
notDone = true;
}
if (request.contains(COLOR_G)) {
request = request.replace(COLOR_G, isHex ? String.format("%02X", rgb.get(1)) : String.valueOf(rgb.get(1)));
request = request.replace(COLOR_G,
isHex ? String.format("%02X", rgb.get(1)) : String.valueOf(rgb.get(1)));
notDone = true;
}
if (request.contains(COLOR_B)) {
request = request.replace(COLOR_B, isHex ? String.format("%02X", rgb.get(2)) : String.valueOf(rgb.get(2)));
request = request.replace(COLOR_B,
isHex ? String.format("%02X", rgb.get(2)) : String.valueOf(rgb.get(2)));
notDone = true;
}
@@ -195,65 +292,78 @@ public class ColorDecode {
}
if (request.contains(COLOR_RGBX)) {
request = request.replace(COLOR_RGBX, String.format("%02X%02X%02X", rgb.get(0), rgb.get(1), rgb.get(2)));
request = request.replace(COLOR_RGBX,
String.format("%02X%02X%02X", rgb.get(0), rgb.get(1), rgb.get(2)));
notDone = true;
}
if (request.contains(COLOR_HSL)) {
float[] hsb = new float[3];
Color.RGBtoHSB(rgb.get(0), rgb.get(1), rgb.get(2), hsb);
float hue = hsb[0] * (float) 360.0;
float sat = hsb[1] * (float) 100.0;
float bright = hsb[2] * (float) 100.0;
request = request.replace(COLOR_HSL, String.format("%f,%f,%f", hue, sat, bright));
notDone = true;
}
Matcher m = COLOR_MILIGHT.matcher(request);
while (m.find()) {
int group = Integer.parseInt(m.group(1));
request = m.replaceFirst(getMilightV5FromRgb(rgb, group));
m.reset(request);
int group = Integer.parseInt(m.group(1));
request = m.replaceFirst(getMilightV5FromRgb(rgb, group));
m.reset(request);
}
log.debug("Request <<" + request + ">>, not done: " + notDone);
}
return request;
}
private static String getMilightV5FromRgb(List<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;
double r = (double) rgb.get(0);
double g = (double) rgb.get(1);
double b = (double) rgb.get(2);
if (r > 245 && g > 245 && b > 245) { // it's white
String retVal = "";
if (group == 0) {
retVal += "C2";
} else if (group == 1) {
retVal += "C5";
} else if (group == 2) {
retVal += "C7";
} else if (group == 3) {
retVal += "C9";
} else if (group == 4) {
retVal += "CB";
}
log.debug("Convert RGB to Milight. Result: WHITE. RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " "
+ rgb.get(2));
return retVal + "0055";
} else { // normal color
r /= (double) 0xFF;
g /= (double) 0xFF;
b /= (double) 0xFF;
double max = Math.max(Math.max(r, g), b), min = Math.min(Math.min(r, g), b);
double h = 0;
double d = max - min;
if (max == min) {
h = 0;
} else {
if (max == r) {
h = ((g - b) / d + (g < b ? 6 : 0));
} else if (max == g) {
h = ((b - r) / d + 2);
} else if (max == b){
h = ((r - g) / d + 4);
}
h = Math.round(h * 60);
}
int milight = (int)((256 + 176 - Math.floor(h / 360.0 * 255.0)) % 256);
log.debug("Convert RGB to Milight. Result: " + milight + " RGB Values: " + rgb.get(0) + " " + rgb.get(1) + " " + rgb.get(2));
return "40" + String.format("%02X", milight) + "55";
}
if (max == min) {
h = 0;
} else {
if (max == r) {
h = ((g - b) / d + (g < b ? 6 : 0));
} else if (max == g) {
h = ((b - r) / d + 2);
} else if (max == b) {
h = ((r - g) / d + 4);
}
h = Math.round(h * 60);
}
int milight = (int) ((256 + 176 - Math.floor(h / 360.0 * 255.0)) % 256);
log.debug("Convert RGB to Milight. Result: " + milight + " RGB Values: " + rgb.get(0) + " " + rgb.get(1)
+ " " + rgb.get(2));
return "40" + String.format("%02X", milight) + "55";
}
}
@SuppressWarnings("unchecked")

File diff suppressed because it is too large Load Diff

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

@@ -119,13 +119,13 @@ public class NestHome implements com.bwssystems.HABridge.Home {
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\"}}]";
+ 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(), NestInstruction.class);
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;
@@ -167,7 +167,7 @@ public class NestHome implements com.bwssystems.HABridge.Home {
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\"}}]";
+ lightId + "/state\"}}]";
}
}
return responseString;

View File

@@ -106,11 +106,14 @@ public class BroadlinkHome implements Home {
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\"}}]";
+ lightId + "/state\"}}]";
} else {
BroadlinkEntry broadlinkCommand = null;
broadlinkCommand = new Gson().fromJson(anItem.getItem().getAsString(), BroadlinkEntry.class);
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());
@@ -126,32 +129,35 @@ public class BroadlinkHome implements Home {
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\"}}]";
+ 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\"}}]";
+ 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\"}}]";
+ lightId + "/state\"}}]";
}
if(broadlinkMap == null)
broadlinkMap = new HashMap<String, BLDevice>();
String newId = theDevice.getHost() + "-" + String.format("%04x", theDevice.getDeviceType());
if(broadlinkMap.get(newId) == null)
broadlinkMap.put(newId, theDevice);
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\"}}]";
+ lightId + "/state\"}}]";
} else {
log.debug("calling BroadlinkDevice: " + broadlinkCommand.getName());
try {
@@ -160,7 +166,7 @@ public class BroadlinkHome implements Home {
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\"}}]";
+ lightId + "/state\"}}]";
}
}
switch (theDevice.getDeviceType()) {
@@ -232,7 +238,7 @@ public class BroadlinkHome implements Home {
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\"}}]";
+ lightId + "/state\"}}]";
}
break;
@@ -241,7 +247,7 @@ public class BroadlinkHome implements Home {
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\"}}]";
+ lightId + "/state\"}}]";
}
}

View File

@@ -3,12 +3,10 @@ package com.bwssystems.HABridge.plugins.domoticz;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Base64;
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;
@@ -43,7 +41,7 @@ public class DomoticzHandler {
theUrl = buildUrl(rootRequest + type + postpend);
else
theUrl = buildUrl(rootRequest + type);
theData = httpClient.doHttpRequest(theUrl, null, null, null, buildHeaders());
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);
@@ -76,15 +74,7 @@ public class DomoticzHandler {
String newUrl = null;
if(thePayload != null && !thePayload.isEmpty()) {
if(domoticzAddress.getSecure() != null && domoticzAddress.getSecure())
newUrl = "https://";
else
newUrl = "http://";
newUrl = newUrl + domoticzAddress.getIp();
if(domoticzAddress.getPort() != null && !domoticzAddress.getPort().isEmpty())
newUrl = newUrl + ":" + domoticzAddress.getPort();
newUrl = domoticzAddress.getHttpPreamble();
if(thePayload.startsWith("/"))
newUrl = newUrl + thePayload;
@@ -95,21 +85,6 @@ public class DomoticzHandler {
return newUrl;
}
public NameValue[] buildHeaders() {
NameValue[] headers = null;
if(domoticzAddress.getUsername() != null && !domoticzAddress.getUsername().isEmpty()
&& domoticzAddress.getPassword() != null && !domoticzAddress.getPassword().isEmpty()) {
NameValue theAuth = new NameValue();
theAuth.setName("Authorization");
String encoding = Base64.getEncoder().encodeToString((domoticzAddress.getUsername() + ":" + domoticzAddress.getPassword()).getBytes());
theAuth.setValue("Basic " + encoding);
headers = new NameValue[1];
headers[0] = theAuth;
}
return headers;
}
public NamedIP getDomoticzAddress() {
return domoticzAddress;
}

View File

@@ -73,7 +73,7 @@ public class DomoticzHome implements Home {
private Boolean addDomoticzDevices(List<DomoticzDevice> theDeviceList, List<DomoticzDevice> theSourceList, String theKey) {
if(!validDomoticz)
return null;
return false;
Iterator<DomoticzDevice> devices = theSourceList.iterator();
while(devices.hasNext()) {
DomoticzDevice theDevice = devices.next();
@@ -88,7 +88,7 @@ public class DomoticzHome implements Home {
Devices theDomoticzApiResponse = null;
String responseString = null;
String theUrl = anItem.getItem().getAsString();
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('/'));
@@ -119,7 +119,7 @@ public class DomoticzHome implements Home {
aBody = DeviceDataDecode.replaceDeviceData(aBody, device);
aBody = TimeDecode.replaceTimeValue(aBody);
}
theData = httpClient.doHttpRequest(theHandler.buildUrl(anUrl), null, null, aBody, theHandler.buildHeaders());
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"))
@@ -128,25 +128,25 @@ public class DomoticzHome implements Home {
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);
+ 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);
+ 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);
+ 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);
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
}
return responseString;
}

View File

@@ -31,13 +31,14 @@ public class CommandHome implements Home {
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
log.debug("Exec Request called with url: " + anItem.getItem().getAsString() + " and exec Garden: " + (theSettings.getBridgeSecurity().getExecGarden() == null ? "not given" : theSettings.getBridgeSecurity().getExecGarden()));
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 (anItem.getItem().getAsString().contains("exec://"))
intermediate = anItem.getItem().getAsString().substring(anItem.getItem().getAsString().indexOf("://") + 3);
if (theItem.contains("exec://"))
intermediate = theItem.substring(anItem.getItem().getAsString().indexOf("://") + 3);
else
intermediate = anItem.getItem().getAsString();
intermediate = theItem;
intermediate = BrightnessDecode.calculateReplaceIntensityValue(intermediate, intensity, targetBri, targetBriInc, false);
if (colorData != null) {
intermediate = ColorDecode.replaceColorData(intermediate, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
@@ -67,13 +68,13 @@ public class CommandHome implements Home {
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\"}}]";
+ "/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\"}}]";
+ "/state\"}}]";
}
return responseString;

View File

@@ -55,12 +55,15 @@ public class FHEMHome implements Home {
if(theUrl != null && !theUrl.isEmpty()) {
FHEMCommand theCommand = null;
try {
theCommand = new Gson().fromJson(theUrl, FHEMCommand.class);
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);
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
return responseString;
}
String intermediate = theCommand.getUrl().substring(theCommand.getUrl().indexOf("://") + 3);

View File

@@ -33,6 +33,7 @@ public class FHEMInstance {
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
@@ -41,6 +42,10 @@ public class FHEMInstance {
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)
@@ -54,6 +59,7 @@ public class FHEMInstance {
String theUrl = null;
String theData;
NameValue[] headers = null;
/* Trying new code helpers
if(theFhem.getSecure() != null && theFhem.getSecure())
theUrl = "https://";
else
@@ -61,7 +67,10 @@ public class FHEMInstance {
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 = 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);

View File

@@ -6,7 +6,6 @@ import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,7 +29,7 @@ public class FibaroInfo
{
super();
fibaroAddress = addressName;
fibaroAuth = "Basic " + new String(Base64.encodeBase64((addressName.getUsername() + ":" + addressName.getPassword()).getBytes()));
fibaroAuth = "Basic " + addressName.getUserPass64();
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
gson = new Gson();
theFilters = null;

View File

@@ -104,7 +104,7 @@ public class HalHome implements Home {
private Boolean addHalDevices(List<HalDevice> theDeviceList, List<HalDevice> theSourceList, String theKey) {
if(!validHal)
return null;
return false;
Iterator<HalDevice> devices = theSourceList.iterator();
while(devices.hasNext()) {
HalDevice theDevice = devices.next();
@@ -123,7 +123,7 @@ public class HalHome implements Home {
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
boolean halFound = false;
String responseString = null;
String theUrl = anItem.getItem().getAsString();
String theUrl = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
if(theUrl != null && !theUrl.isEmpty () && theUrl.contains("http")) {
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
@@ -165,7 +165,7 @@ public class HalHome implements Home {
log.warn("Error on calling hal to change device state: " + anUrl);
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);
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
}
}
}
@@ -175,7 +175,7 @@ public class HalHome implements Home {
log.warn("No HAL found to call: " + theUrl);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"No HAL found.", "/lights/"
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
}
return responseString;
}

View File

@@ -187,14 +187,14 @@ public class HarmonyHome implements Home {
log.warn("Should not get here, no harmony configured");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no harmony configured\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else {
if (anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.HARMONY_ACTIVITY[DeviceMapTypes.typeIndex])) {
RunActivity anActivity = null;
if (anItem.getItem().isJsonObject())
anActivity = aGsonHandler.fromJson(anItem.getItem(), RunActivity.class);
else
anActivity = aGsonHandler.fromJson(anItem.getItem().getAsString(), RunActivity.class);
anActivity = aGsonHandler.fromJson(anItem.getItem().getAsString().replaceAll("^\"|\"$", ""), RunActivity.class);
if (anActivity.getHub() == null || anActivity.getHub().isEmpty())
anActivity.setHub(device.getTargetDevice());
HarmonyHandler myHarmony = getHarmonyHandler(anActivity.getHub());
@@ -202,7 +202,7 @@ public class HarmonyHome implements Home {
log.warn("Should not get here, no harmony hub available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no harmony hub available\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else {
if (!myHarmony.startActivity(anActivity)) {
if (resetHub(myHarmony)) {
@@ -212,7 +212,7 @@ public class HarmonyHome implements Home {
+ ", please restart...");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Could not communicate with harmony\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
}
}
}
@@ -265,7 +265,7 @@ public class HarmonyHome implements Home {
+ ", please restart...");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Could not communicate with harmony\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
}
}
}

View File

@@ -0,0 +1,13 @@
package com.bwssystems.HABridge.plugins.hass;
public class HassAuth {
boolean legacyauth;
public boolean isLegacyauth() {
return legacyauth;
}
public void setLegacyauth(boolean legacyauth) {
this.legacyauth = legacyauth;
}
}

View File

@@ -98,7 +98,7 @@ public class HassHome implements Home {
private Boolean addHassDevices(List<HassDevice> theDeviceList, List<State> theSourceList, String theKey) {
if(!validHass)
return null;
return false;
Iterator<State> devices = theSourceList.iterator();
while(devices.hasNext()) {
State theDevice = devices.next();
@@ -131,14 +131,14 @@ public class HassHome implements Home {
log.warn("Should not get here, no HomeAssistant clients configured");
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no HomeAssistants configured\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else {
HassCommand hassCommand = null;
if(anItem.getItem().isJsonObject())
hassCommand = aGsonHandler.fromJson(anItem.getItem(), HassCommand.class);
else
hassCommand = aGsonHandler.fromJson(anItem.getItem().getAsString(), HassCommand.class);
hassCommand = aGsonHandler.fromJson(anItem.getItem().getAsString().replaceAll("^\"|\"$", ""), HassCommand.class);
hassCommand.setBri(BrightnessDecode.replaceIntensityValue(hassCommand.getBri(),
BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false));
HomeAssistant homeAssistant = getHomeAssistant(hassCommand.getHassName());
@@ -146,7 +146,7 @@ public class HassHome implements Home {
log.warn("Should not get here, no HomeAssistants available");
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no HiomeAssistant clients available\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else {
log.debug("calling HomeAssistant: " + hassCommand.getHassName() + " - "
+ hassCommand.getEntityId() + " - " + hassCommand.getState() + " - " + hassCommand.getBri());

View File

@@ -16,14 +16,19 @@ import com.bwssystems.HABridge.plugins.http.HTTPHome;
import com.google.gson.Gson;
public class HomeAssistant {
private static final Logger log = LoggerFactory.getLogger(HomeAssistant.class);
private NamedIP hassAddress;
private HTTPHandler anHttpHandler;
private static final Logger log = LoggerFactory.getLogger(HomeAssistant.class);
private NamedIP hassAddress;
private HTTPHandler anHttpHandler;
private HassAuth theAuthType;
private NameValue[] headers;
public HomeAssistant(NamedIP addressName) {
super();
anHttpHandler = HTTPHome.getHandler();
hassAddress = addressName;
hassAddress = addressName;
theAuthType = null;
headers = null;
isLegacyAuth();
}
public NamedIP getHassAddress() {
@@ -35,42 +40,30 @@ public class HomeAssistant {
}
public Boolean callCommand(HassCommand aCommand) {
log.debug("calling HomeAssistant: " + aCommand.getHassName() + " - "
+ aCommand.getEntityId() + " - " + aCommand.getState() + " - " + aCommand.getBri());
log.debug("calling HomeAssistant: " + aCommand.getHassName() + " - " + aCommand.getEntityId() + " - "
+ aCommand.getState() + " - " + aCommand.getBri());
String aUrl = null;
if(hassAddress.getSecure() != null && hassAddress.getSecure())
aUrl = "https";
else
aUrl = "http";
String domain = aCommand.getEntityId().substring(0, aCommand.getEntityId().indexOf("."));
aUrl = aUrl + "://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/services/";
if(domain.equals("group"))
aUrl = hassAddress.getHttpPreamble() + "/api/services/";
if (domain.equals("group"))
aUrl = aUrl + "homeassistant";
else
aUrl = aUrl + domain;
String aBody = "{\"entity_id\":\"" + aCommand.getEntityId() + "\"";
NameValue[] headers = null;
if(hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
NameValue password = new NameValue();
password.setName("x-ha-access");
password.setValue(hassAddress.getPassword());
headers = new NameValue[1];
headers[0] = password;
}
if(aCommand.getState().equalsIgnoreCase("on")) {
headers = getAuthHeader();
if (aCommand.getState().equalsIgnoreCase("on")) {
aUrl = aUrl + "/turn_on";
if(aCommand.getBri() != null)
if (aCommand.getBri() != null)
aBody = aBody + ",\"brightness\":" + aCommand.getBri() + "}";
else
aBody = aBody + "}";
}
else {
} else {
aUrl = aUrl + "/turn_off";
aBody = aBody + "}";
aBody = aBody + "}";
}
log.debug("Calling HomeAssistant with url: " + aUrl);
String theData = anHttpHandler.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "application/json", aBody, headers);
log.debug("call Command return is: <" + theData + ">");
String theData = anHttpHandler.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "application/json", aBody, headers);
log.debug("call Command return is: <" + theData + ">");
return true;
}
@@ -78,39 +71,62 @@ public class HomeAssistant {
List<State> theDeviceStates = null;
State[] theHassStates;
String theUrl = null;
String theData;
NameValue[] headers = null;
if(hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
NameValue password = new NameValue();
password.setName("x-ha-access");
password.setValue(hassAddress.getPassword());
headers = new NameValue[1];
headers[0] = password;
}
if(hassAddress.getSecure() != null && hassAddress.getSecure())
String theData;
headers = getAuthHeader();
if (hassAddress.getSecure() != null && hassAddress.getSecure())
theUrl = "https";
else
theUrl = "http";
theUrl = theUrl + "://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/states";
theData = anHttpHandler.doHttpRequest(theUrl, HttpGet.METHOD_NAME, "application/json", null, headers);
if(theData != null) {
log.debug("GET Hass States - data: " + theData);
theHassStates = new Gson().fromJson(theData, State[].class);
if(theHassStates == null) {
log.warn("Cannot get an devices for HomeAssistant " + hassAddress.getName() + " as response is not parsable.");
}
else {
theDeviceStates = new ArrayList<State>(Arrays.asList(theHassStates));
}
}
else
log.warn("Cannot get an devices for HomeAssistant " + hassAddress.getName() + " http call failed.");
theUrl = theUrl + "://" + hassAddress.getIp() + ":" + hassAddress.getPort() + "/api/states";
theData = anHttpHandler.doHttpRequest(theUrl, HttpGet.METHOD_NAME, "application/json", null, headers);
if (theData != null) {
log.debug("GET Hass States - data: " + theData);
theHassStates = new Gson().fromJson(theData, State[].class);
if (theHassStates == null) {
log.warn("Cannot get an devices for HomeAssistant " + hassAddress.getName()
+ " as response is not parsable.");
} else {
theDeviceStates = new ArrayList<State>(Arrays.asList(theHassStates));
}
} else
log.warn("Cannot get an devices for HomeAssistant " + hassAddress.getName() + " http call failed.");
return theDeviceStates;
}
protected void closeClient() {
anHttpHandler.closeHandler();
anHttpHandler = null;
}
private boolean isLegacyAuth() {
if (theAuthType == null) {
try {
theAuthType = new Gson().fromJson(hassAddress.getExtensions(), HassAuth.class);
} catch (Exception e) {
log.warn("Could not read hass auth - continuing with defaults.");
theAuthType = new HassAuth();
theAuthType.setLegacyauth(false);
}
}
return theAuthType.isLegacyauth();
}
private NameValue[] getAuthHeader() {
if (headers == null) {
if (hassAddress.getPassword() != null && !hassAddress.getPassword().isEmpty()) {
headers = new NameValue[1];
headers[0] = new NameValue();
if (isLegacyAuth()) {
headers[0].setName("x-ha-access");
headers[0].setValue(hassAddress.getPassword());
} else {
headers[0].setName("Authorization");
headers[0].setValue("Bearer " + hassAddress.getPassword());
}
}
} else if(hassAddress.getPassword() == null || hassAddress.getPassword().isEmpty()) {
headers = null;
}
return headers;
}
}

View File

@@ -0,0 +1,40 @@
package com.bwssystems.HABridge.plugins.homegenie;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class HomeGenieCommand {
@SerializedName("moduleType")
@Expose
private String moduleType;
@SerializedName("deviceId")
@Expose
private String deviceId;
@SerializedName("command")
@Expose
private HomeGenieCommandDetail command;
public String getModuleType() {
return moduleType;
}
public void setModuleType(String moduleType) {
this.moduleType = moduleType;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public HomeGenieCommandDetail getCommand() {
return command;
}
public void setCommand(HomeGenieCommandDetail command) {
this.command = command;
}
}

View File

@@ -0,0 +1,40 @@
package com.bwssystems.HABridge.plugins.homegenie;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class HomeGenieCommandDetail {
@SerializedName("command")
@Expose
private String command;
@SerializedName("level")
@Expose
private String level;
@SerializedName("color")
@Expose
private String color;
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
}

View File

@@ -0,0 +1,29 @@
package com.bwssystems.HABridge.plugins.homegenie;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class HomeGenieDevice {
@SerializedName("gatewayName")
@Expose
private String gatewayName;
@SerializedName("deviceDetail")
@Expose
private Module deviceDetail;
public String getGatewayName() {
return gatewayName;
}
public void setGatewayName(String gatewayName) {
this.gatewayName = gatewayName;
}
public Module getDeviceDetail() {
return deviceDetail;
}
public void setDeviceDetail(Module deviceDetail) {
this.deviceDetail = deviceDetail;
}
}

View File

@@ -0,0 +1,190 @@
package com.bwssystems.HABridge.plugins.homegenie;
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 HomeGenieHome implements Home {
private static final Logger log = LoggerFactory.getLogger(HomeGenieHome.class);
private Map<String, HomeGenieInstance> homegenieMap;
private Boolean validHomeGenie;
private HTTPHandler httpClient;
private boolean closed;
public HomeGenieHome(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 theUrl = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
String responseString = null;
if (theUrl != null && !theUrl.isEmpty()) {
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrl, intensity, targetBri, targetBriInc,
false);
if (colorData != null) {
anUrl = ColorDecode.replaceColorData(anUrl, colorData,
BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), true);
}
anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device);
anUrl = TimeDecode.replaceTimeValue(anUrl);
anUrl = BrightnessDecode.calculateReplaceIntensityValue(anUrl, 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);
HomeGenieCommand theCommand = null;
try {
theCommand = new Gson().fromJson(anUrl, HomeGenieCommand.class);
} catch (Exception e) {
log.warn("Cannot parse command to HomeGenie <<<" + 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;
}
HomeGenieInstance theHandler = homegenieMap.get(device.getTargetDevice());
if (theHandler != null) {
try {
boolean success = theHandler.callCommand(theCommand.getDeviceId(), theCommand.getModuleType(), theCommand.getCommand(), httpClient);
if (!success) {
log.warn("Comand had error to HomeGenie");
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 send comand to HomeGenie", 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("HomeGenie 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(
"HomeGenie 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 Object getItems(String type) {
if (!validHomeGenie)
return null;
log.debug("consolidating devices for HomeGenie");
List<Module> theResponse = null;
Iterator<String> keys = homegenieMap.keySet().iterator();
List<HomeGenieDevice> deviceList = new ArrayList<HomeGenieDevice>();
while (keys.hasNext()) {
String key = keys.next();
theResponse = homegenieMap.get(key).getDevices(httpClient);
if (theResponse != null)
addHomeGenieDevices(deviceList, theResponse, key);
else {
log.warn("Cannot get devices for HomeGenie with name: " + key + ", skipping this HomeGenie.");
continue;
}
}
return deviceList;
}
private Boolean addHomeGenieDevices(List<HomeGenieDevice> theDeviceList, List<Module> theSourceList,
String theKey) {
Iterator<Module> hgModules = theSourceList.iterator();
while (hgModules.hasNext()) {
Module aModule = hgModules.next();
HomeGenieDevice theDevice = new HomeGenieDevice();
theDevice.setDeviceDetail(aModule);
theDevice.setGatewayName(theKey);
theDeviceList.add(theDevice);
}
return true;
}
@Override
public Home createHome(BridgeSettings bridgeSettings) {
homegenieMap = null;
validHomeGenie = bridgeSettings.getBridgeSettingsDescriptor().isValidHomeGenie();
log.info("HomeGenie Home created." + (validHomeGenie ? "" : " No HomeGenies configured."));
if (validHomeGenie) {
homegenieMap = new HashMap<String, HomeGenieInstance>();
httpClient = HTTPHome.getHandler();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHomegenieaddress().getDevices()
.iterator();
while (theList.hasNext() && validHomeGenie) {
NamedIP aHomeGenie = theList.next();
try {
homegenieMap.put(aHomeGenie.getName(), new HomeGenieInstance(aHomeGenie, httpClient));
} catch (Exception e) {
log.error("Cannot get HomeGenie (" + aHomeGenie.getName() + ") setup, Exiting with message: "
+ e.getMessage(), e);
validHomeGenie = false;
}
}
}
return this;
}
@Override
public void closeHome() {
log.debug("Closing Home.");
if (!closed && validHomeGenie) {
log.debug("Home is already closed....");
return;
}
if (httpClient != null)
httpClient.closeHandler();
homegenieMap = null;
closed = true;
}
@Override
public void refresh() {
// noop
}
}

View File

@@ -0,0 +1,85 @@
package com.bwssystems.HABridge.plugins.homegenie;
import java.util.ArrayList;
import java.util.List;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
import com.google.gson.Gson;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HomeGenieInstance {
private static final Logger log = LoggerFactory.getLogger(HomeGenieInstance.class);
private NamedIP homegenieIP;
public HomeGenieInstance(NamedIP theNamedIp, HTTPHandler httpClient) {
homegenieIP = theNamedIp;
}
public Boolean callCommand(String deviceId, String moduleType, HomeGenieCommandDetail commandData,
HTTPHandler httpClient) {
log.debug("calling HomeGenie: {}:{}{}{}", homegenieIP.getIp(), homegenieIP.getPort(), moduleType,
commandData.getCommand());
String aUrl = null;
aUrl = homegenieIP.getHttpPreamble() + "/api/" + moduleType + "/" + deviceId + "/" + commandData.getCommand();
String theLevel = commandData.getLevel();
if (commandData.getCommand().contains("Level")) {
if (theLevel != null && theLevel.length() > 0)
aUrl = aUrl + "/" + theLevel;
else
aUrl = aUrl + "100";
}
String theData = httpClient.doHttpRequest(aUrl, HttpPut.METHOD_NAME, "application/json", null, httpClient.addBasicAuthHeader(null, homegenieIP));
log.debug("call Command return is: <<<{}>>>", theData);
if (theData.toLowerCase().contains("error"))
return false;
return true;
}
public List<Module> getDevices(HTTPHandler httpClient) {
log.debug("calling HomeGenie: " + homegenieIP.getIp() + ":" + homegenieIP.getPort());
List<Module> deviceList = null;
Module[] hgModules;
String theUrl = null;
String theData;
theUrl = homegenieIP.getHttpPreamble() + "/api/HomeAutomation.HomeGenie/Config/Modules.List";
theData = httpClient.doHttpRequest(theUrl, HttpGet.METHOD_NAME, "application/json", null, httpClient.addBasicAuthHeader(null, homegenieIP));
if (theData != null) {
log.debug("GET HomeGenie Devices - data: " + theData);
try {
hgModules = new Gson().fromJson(theData, Module[].class);
if (hgModules != null && hgModules.length > 0) {
deviceList = new ArrayList<Module>();
for (int i = 0; i < hgModules.length; i++) {
if (hgModules[i].isModuleValid(homegenieIP.getExtensions()))
deviceList.add(hgModules[i]);
}
}
} catch (Exception e) {
log.warn("Cannot get devices for Homegenie {} Gson Parse Error.", homegenieIP.getName(), e);
}
}
return deviceList;
}
public NamedIP getHomegenieIP() {
return homegenieIP;
}
public void setHomegenieIP(NamedIP homegenieIP) {
this.homegenieIP = homegenieIP;
}
protected void closeClient() {
}
}

View File

@@ -0,0 +1,149 @@
package com.bwssystems.HABridge.plugins.homegenie;
import java.util.List;
// import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class Module {
private static final Logger log = LoggerFactory.getLogger(HomeGenieInstance.class);
@SerializedName("Name")
@Expose
private String name;
@SerializedName("Description")
@Expose
private String description;
@SerializedName("DeviceType")
@Expose
private String deviceType;
@SerializedName("Domain")
@Expose
private String domain;
@SerializedName("Address")
@Expose
private String address;
/*
* @SerializedName("Properties")
*
* @Expose private List<Property> properties = null;
*/
@SerializedName("RoutingNode")
@Expose
private String routingNode;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDeviceType() {
return deviceType;
}
public void setDeviceType(String deviceType) {
this.deviceType = deviceType;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
/*
* public List<Property> getProperties() { return properties; }
*
* public void setProperties(List<Property> properties) { this.properties =
* properties; }
*/
public String getRoutingNode() {
return routingNode;
}
public void setRoutingNode(String routingNode) {
this.routingNode = routingNode;
}
public boolean isSwitch() {
return isDeviceType("Switch");
}
public boolean isDimmer() {
return isDeviceType("Dimmer");
}
public boolean isLight() {
return isDeviceType("Light");
}
private boolean isDeviceType(String theType) {
if (deviceType.equals(theType)) {
return true;
}
return false;
}
public boolean isModuleValid(JsonObject theExtensions) {
ModuleTypes moduleTypes = null;
if (this.name == null || this.name.trim().isEmpty())
return false;
if (isSwitch())
return true;
if (isDimmer())
return true;
if (isLight())
return true;
if (theExtensions != null && theExtensions.isJsonObject() && theExtensions.get("moduleTypes").isJsonArray()) {
try {
moduleTypes = new Gson().fromJson(theExtensions, ModuleTypes.class);
} catch (Exception e) {
log.warn("Could not parse extensions for {} with {}", this.name, theExtensions);
return false;
}
if (moduleTypes == null)
return false;
for (ModuleType moduleType : moduleTypes.getModuleTypes()) {
if (isDeviceType(moduleType.getModuleType()))
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,20 @@
package com.bwssystems.HABridge.plugins.homegenie;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class ModuleType {
@SerializedName("moduleType")
@Expose
private String moduleType;
public String getModuleType() {
return moduleType;
}
public void setModuleType(String moduleType) {
this.moduleType = moduleType;
}
}

View File

@@ -0,0 +1,21 @@
package com.bwssystems.HABridge.plugins.homegenie;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class ModuleTypes {
@SerializedName("moduleTypes")
@Expose
private List<ModuleType> moduleTypes = null;
public List<ModuleType> getModuleTypes() {
return moduleTypes;
}
public void setModuleTypes(List<ModuleType> moduleTypes) {
this.moduleTypes = moduleTypes;
}
}

View File

@@ -0,0 +1,76 @@
package com.bwssystems.HABridge.plugins.homegenie;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Property {
@SerializedName("Name")
@Expose
private String name;
@SerializedName("Value")
@Expose
private String value;
@SerializedName("Description")
@Expose
private String description;
@SerializedName("FieldType")
@Expose
private String fieldType;
@SerializedName("UpdateTime")
@Expose
private String updateTime;
@SerializedName("NeedsUpdate")
@Expose
private Boolean needsUpdate;
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;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getFieldType() {
return fieldType;
}
public void setFieldType(String fieldType) {
this.fieldType = fieldType;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
public Boolean getNeedsUpdate() {
return needsUpdate;
}
public void setNeedsUpdate(Boolean needsUpdate) {
this.needsUpdate = needsUpdate;
}
}

View File

@@ -49,20 +49,20 @@ public class HomeWizardHome implements Home {
log.warn("Should not get here, no HomeWizard smart plug available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no HomeWizard smart plug available\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else {
if (anItem.getType() != null && anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex])) {
log.debug("Executing HUE api request to change activity to HomeWizard smart plug: " + anItem.getItem().toString());
String jsonToPost = anItem.getItem().toString();
String jsonToPost = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
log.debug("Executing HUE api request to change activity to HomeWizard smart plug: {}", jsonToPost);
HomeWizzardSmartPlugInfo homeWizzardHandler = getHomeWizzardHandler(device.getTargetDevice());
if(homeWizzardHandler == null) {
log.warn("Should not get here, no HomeWizard smart plug configured");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no HomeWizard smart plug configured\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else {
try {
homeWizzardHandler.execApply(jsonToPost);
@@ -70,7 +70,7 @@ public class HomeWizardHome implements Home {
log.warn("Error posting request to HomeWizard smart plug");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Error posting request to HomeWizard smart plug\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
+ "\",\"description\": \"Error posting request to HomeWizard smart plug\", \"parameter\": \"/lights/" + lightId + "/state\"}}]";
}
}
}

View File

@@ -9,8 +9,6 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,7 +44,7 @@ public class HomeWizzardSmartPlugInfo {
super();
cloudAuth = "Basic " + new String(Base64.encodeBase64((gateway.getUsername() + ":" + DigestUtils.sha1Hex(gateway.getPassword())).getBytes()));
cloudAuth = "Basic " + gateway.getUserPass64();
cloudPlugName = name;
gson = new Gson();
}

View File

@@ -18,42 +18,52 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.NameValue;
public class HTTPHandler {
private static final Logger log = LoggerFactory.getLogger(HTTPHandler.class);
private String callType;
public HTTPHandler() {
super();
callType = null;
}
public HTTPHandler(String type) {
super();
callType = type;
}
// This function executes the url from the device repository against the
// target as http or https as defined
public String doHttpRequest(String url, String httpVerb, String contentType, String body, NameValue[] headers) {
log.debug("doHttpRequest with url <<<" + url + ">>>, verb: " + httpVerb + ", contentType: " + contentType + ", body <<<" + body + ">>>" );
if(headers != null && headers.length > 0)
for(int i = 0; i < headers.length; i++)
log.debug("header index " + i + " name: <<<" + headers[i].getName() + ">>>, value: <<<" + headers[i].getValue() + ">>>");
HttpUriRequest request = null;
String theContent = null;
URI theURI = null;
boolean usingSSL = false;
ContentType parsedContentType = null;
StringEntity requestBody = null;
log.debug("doHttpRequest with url <<<" + url + ">>>, verb: " + httpVerb + ", contentType: " + contentType
+ ", body <<<" + body + ">>>");
if (headers != null && headers.length > 0) {
for (int i = 0; i < headers.length; i++) {
log.debug("header index " + i + " name: <<<" + headers[i].getName() + ">>>, value: <<<"
+ headers[i].getValue() + ">>>");
}
}
if (contentType != null && !contentType.trim().isEmpty()) {
parsedContentType = ContentType.parse(contentType);
if (body != null && body.length() > 0)
requestBody = new StringEntity(body, parsedContentType);
}
if (url.startsWith("https:")) {
usingSSL = true;
}
try {
theURI = new URI(url);
} catch (URISyntaxException e1) {
@@ -90,7 +100,11 @@ public class HTTPHandler {
CloseableHttpResponse response = null;
for (int retryCount = 0; retryCount < 2; retryCount++) {
try {
response = HttpClientPool.getClient().execute(request);
if (usingSSL) {
response = HttpClientPool.getSSLClient().execute(request);
} else {
response = HttpClientPool.getClient().execute(request);
}
log.debug((httpVerb == null ? "GET" : httpVerb) + " execute (" + retryCount + ") on URL responded: "
+ response.getStatusLine().getStatusCode());
if (response != null && response.getEntity() != null) {
@@ -106,22 +120,27 @@ public class HTTPHandler {
// ignore
// content
} catch (Exception e) {
log.debug("Error ocurred in handling response entity after successful call, still responding success. "
+ e.getMessage(), e);
log.debug(
"Error ocurred in handling response entity after successful call, still responding success. "
+ e.getMessage(),
e);
}
}
if (response != null && response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300) {
if(theContent == null)
if (response != null && response.getStatusLine().getStatusCode() >= 200
&& response.getStatusLine().getStatusCode() < 300) {
if (theContent == null)
theContent = "";
log.debug("Successfull response - The http response is <<<" + theContent + ">>>");
retryCount = 2;
} else if (callType != null && callType == DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex] && response.getStatusLine().getStatusCode() == 302) {
if(theContent == null)
} else if (DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex].equals(callType)
&& response.getStatusLine().getStatusCode() == 302) {
if (theContent == null)
theContent = "";
log.debug("Successfull response - The http response is <<<" + theContent + ">>>");
retryCount = 2;
} else if (response != null) {
log.warn("HTTP response code was not an expected successful response of between 200 - 299, the code was: "
log.warn(
"HTTP response code was not an expected successful response of between 200 - 299, the code was: "
+ response.getStatusLine() + " with the content of <<<" + theContent + ">>>");
if (response.getStatusLine().getStatusCode() == 504) {
log.warn("HTTP response code was 504, retrying...");
@@ -130,15 +149,15 @@ public class HTTPHandler {
} else
retryCount = 2;
}
} catch (ClientProtocolException e) {
log.warn("Client Protocol Exception received, retyring....");
}catch (IOException e) {
log.warn("Error calling out to HA gateway: IOException in log: " + e.getMessage());
} catch (IOException e) {
log.warn("Error calling out to HA gateway: IOException in log: " + e.getMessage(), e);
retryCount = 2;
}
if(retryCount < 2) {
if (retryCount < 2) {
theContent = null;
try {
Thread.sleep(1000);
@@ -149,11 +168,40 @@ public class HTTPHandler {
}
return theContent;
}
public NameValue[] addBasicAuthHeader(NameValue[] theHeaders, NamedIP theTarget) {
NameValue[] headers = null;
int index = 0;
String encodedLogin = theTarget.getUserPass64();
if (encodedLogin != null && !encodedLogin.trim().isEmpty()) {
if (theHeaders != null && theHeaders.length > 0) {
headers = new NameValue[theHeaders.length + 1];
for(int i = 0; i < theHeaders.length; i++) {
headers[i] = theHeaders[i];
}
index = theHeaders.length;
} else {
headers = new NameValue[1];
}
log.debug("creating login for {} with username {} password len {}", theTarget.getName(),
theTarget.getUsername(), theTarget.getPassword().length());
headers[index] = new NameValue();
headers[index].setName("Authorization");
// log.debug("Encoded login info {}", encodedLogin);
headers[index].setValue("Basic " + encodedLogin);
} else {
headers = theHeaders;
}
return headers;
}
public void setCallType(String callType) {
this.callType = callType;
}
public void closeHandler() {
}
}

View File

@@ -44,7 +44,7 @@ public class HTTPHome implements Home {
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
String responseString = null;
String theUrl = anItem.getItem().getAsString();
String theUrl = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
if(theUrl != null && !theUrl.isEmpty () && (theUrl.startsWith("http://") || theUrl.startsWith("https://"))) {
//Backwards Compatibility Items
if(anItem.getHttpVerb() == null || anItem.getHttpVerb().isEmpty())
@@ -86,7 +86,7 @@ public class HTTPHome implements Home {
log.warn("Error on calling url to change device state: " + anUrl);
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);
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
}
if(isDevMode)
@@ -95,7 +95,7 @@ public class HTTPHome implements Home {
log.warn("HTTP 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);
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
}
return responseString;

View File

@@ -4,10 +4,22 @@ import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,9 +43,7 @@ public final class HttpClientPool {
// Increase default max connection per route to 20
cm.setDefaultMaxPerRoute(20);
// Build the client.
threadSafeClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
threadSafeClient = HttpClients.custom().setConnectionManager(cm).build();
// Start up an eviction thread.
monitor = new IdleConnectionMonitorThread(cm);
// Don't stop quitting.
@@ -47,11 +57,67 @@ public final class HttpClientPool {
}
// Single-element enum to implement Singleton.
private static enum SingletonSSL {
// Just one of me so constructor will be called once.
SSLClient;
// The thread-safe client.
private CloseableHttpClient threadSafeClient;
// The pool monitor.
private IdleConnectionMonitorThread monitor = null;
private TrustStrategy acceptingTrustStrategy = null;
private SSLContext sslContext = null;
private SSLConnectionSocketFactory sslsf = null;
private Registry<ConnectionSocketFactory> socketFactoryRegistry = null;
private NoopHostnameVerifier hostnameVerifier = null;
// The constructor creates it - thus late
private SingletonSSL() {
try {
acceptingTrustStrategy = (cert, authType) -> true;
hostnameVerifier = new NoopHostnameVerifier();
sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
HttpClientPool.log.info("Instantiated SSL components.");
final Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", sslsf)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
// Increase max total connection to 200
cm.setMaxTotal(200);
// Increase default max connection per route to 20
cm.setDefaultMaxPerRoute(20);
// Build the client.
threadSafeClient = HttpClients.custom().setSSLSocketFactory(sslsf).setConnectionManager(cm).build();
// Start up an eviction thread.
monitor = new IdleConnectionMonitorThread(cm);
// Don't stop quitting.
monitor.setDaemon(true);
monitor.start();
} catch (Exception e) {
HttpClientPool.log.warn("SingletonSSL failed on SSL init");
threadSafeClient = null;
}
}
public CloseableHttpClient get() {
return threadSafeClient;
}
}
public static CloseableHttpClient getClient() {
// The thread safe client is held by the singleton.
return Singleton.Client.get();
}
public static CloseableHttpClient getSSLClient() {
// The thread safe client is held by the singleton.
return SingletonSSL.SSLClient.get();
}
// Watches for stale connections and evicts them.
private static class IdleConnectionMonitorThread extends Thread {
// The manager to watch.
@@ -123,6 +189,7 @@ public final class HttpClientPool {
public static void shutdown() throws InterruptedException, IOException {
// Shutdown the monitor.
Singleton.Client.monitor.shutdown();
SingletonSSL.SSLClient.monitor.shutdown();
}
}

View File

@@ -32,7 +32,7 @@ public class HttpTestHandler extends HTTPHandler {
}
else {
for(NameValue aTest:this.theData) {
if(aTest.getName().equals(compareValue));
if(aTest.getName().equals(compareValue))
aTest.setValue(testData);
}
}

View File

@@ -99,7 +99,7 @@ public class HueHome implements Home {
if(anItem.getItem().isJsonObject())
deviceId = aGsonHandler.fromJson(anItem.getItem(), HueDeviceIdentifier.class);
else
deviceId = aGsonHandler.fromJson(anItem.getItem().getAsString(), HueDeviceIdentifier.class);
deviceId = aGsonHandler.fromJson(anItem.getItem().getAsString().replaceAll("^\"|\"$", ""), HueDeviceIdentifier.class);
if(deviceId.getHueName() == null || deviceId.getHueName().isEmpty())
deviceId.setHueName(device.getTargetDevice());

View File

@@ -159,10 +159,10 @@ public class HueInfo {
log.warn("Error on calling Hue passthru to change device state: " + deviceId.getHueName());
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Error on calling HUE to change device state\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else if (responseString.contains("[{\"error\":")) {
if(responseString.contains("unauthorized user")) {
}
// if(responseString.contains("unauthorized user")) {
// }
log.warn("Error occurred when calling Hue Passthru: " + responseString);
}
} else {

View File

@@ -144,20 +144,20 @@ public class LifxHome implements Home {
log.warn("Should not get here, no LifxDevice clients configured");
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no LifxDevices configured\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else {
LifxEntry lifxCommand = null;
if(anItem.getItem().isJsonObject())
lifxCommand = aGsonHandler.fromJson(anItem.getItem(), LifxEntry.class);
else
lifxCommand = aGsonHandler.fromJson(anItem.getItem().getAsString(), LifxEntry.class);
lifxCommand = aGsonHandler.fromJson(anItem.getItem().getAsString().replaceAll("^\"|\"$", ""), LifxEntry.class);
LifxDevice theDevice = getLifxDevice(lifxCommand.getName());
if (theDevice == null) {
log.warn("Should not get here, no LifxDevices available");
theReturn = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no Lifx clients available\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else {
log.debug("calling LifxDevice: " + lifxCommand.getName());
if(theDevice.getType().equals(LifxDevice.LIGHT_TYPE)) {

View File

@@ -0,0 +1,8 @@
package com.bwssystems.HABridge.plugins.moziot;
public class Actions {
}

View File

@@ -0,0 +1,8 @@
package com.bwssystems.HABridge.plugins.moziot;
public class Events {
}

View File

@@ -0,0 +1,21 @@
package com.bwssystems.HABridge.plugins.moziot;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class JWT {
@SerializedName("jwt")
@Expose
private String jwt;
public String getJwt() {
return jwt;
}
public void setJwt(String jwt) {
this.jwt = jwt;
}
}

View File

@@ -0,0 +1,88 @@
package com.bwssystems.HABridge.plugins.moziot;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Level {
@SerializedName("title")
@Expose
private String title;
@SerializedName("type")
@Expose
private String type;
@SerializedName("@type")
@Expose
private String attype;
@SerializedName("unit")
@Expose
private String unit;
@SerializedName("minimum")
@Expose
private Integer minimum;
@SerializedName("maximum")
@Expose
private Integer maximum;
@SerializedName("links")
@Expose
private List<Link> links = null;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAttype() {
return attype;
}
public void setAttype(String attype) {
this.attype = attype;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public Integer getMinimum() {
return minimum;
}
public void setMinimum(Integer minimum) {
this.minimum = minimum;
}
public Integer getMaximum() {
return maximum;
}
public void setMaximum(Integer maximum) {
this.maximum = maximum;
}
public List<Link> getLinks() {
return links;
}
public void setLinks(List<Link> links) {
this.links = links;
}
}

View File

@@ -0,0 +1,43 @@
package com.bwssystems.HABridge.plugins.moziot;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Link {
@SerializedName("rel")
@Expose
private String rel;
@SerializedName("href")
@Expose
private String href;
@SerializedName("mediaType")
@Expose
private String mediaType;
public String getRel() {
return rel;
}
public void setRel(String rel) {
this.rel = rel;
}
public String getHref() {
return href;
}
public void setHref(String href) {
this.href = href;
}
public String getMediaType() {
return mediaType;
}
public void setMediaType(String mediaType) {
this.mediaType = mediaType;
}
}

View File

@@ -0,0 +1,26 @@
package com.bwssystems.HABridge.plugins.moziot;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class MozIotCommand {
@SerializedName("url")
@Expose
private String url;
@SerializedName("command")
@Expose
private MozIotCommandDetail command;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public MozIotCommandDetail getCommand() {
return command;
}
public void setCommand(MozIotCommandDetail command) {
this.command = command;
}
}

View File

@@ -0,0 +1,54 @@
package com.bwssystems.HABridge.plugins.moziot;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class MozIotCommandDetail {
@SerializedName("on")
@Expose
private boolean on;
@SerializedName("level")
@Expose
private String level;
@SerializedName("color")
@Expose
private String color;
public boolean isOn() {
return on;
}
public void setOn(boolean on) {
this.on = on;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getBody() {
String theBody = "";
if(level != null && level != "") {
theBody = "{\"level\":" + level + "}";
}
else if(color != null && color != "") {
theBody = "{\"color\":\"" + color + "\"}";
} else {
theBody = "{\"on\":" + on + "}";
}
return theBody;
}
}

View File

@@ -0,0 +1,29 @@
package com.bwssystems.HABridge.plugins.moziot;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class MozIotDevice {
@SerializedName("gatewayName")
@Expose
private String gatewayName;
@SerializedName("deviceDetail")
@Expose
private MozillaThing deviceDetail;
public String getGatewayName() {
return gatewayName;
}
public void setGatewayName(String gatewayName) {
this.gatewayName = gatewayName;
}
public MozillaThing getDeviceDetail() {
return deviceDetail;
}
public void setDeviceDetail(MozillaThing deviceDetail) {
this.deviceDetail = deviceDetail;
}
}

View File

@@ -0,0 +1,194 @@
package com.bwssystems.HABridge.plugins.moziot;
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 MozIotHome implements Home {
private static final Logger log = LoggerFactory.getLogger(MozIotHome.class);
private Map<String, MozIotInstance> moziotMap;
private Boolean validMoziot;
private HTTPHandler httpClient;
private boolean closed;
public MozIotHome(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 theUrl = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
String responseString = null;
if (theUrl != null && !theUrl.isEmpty()) {
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrl, intensity, targetBri, targetBriInc,
false);
if (colorData != null) {
anUrl = ColorDecode.replaceColorData(anUrl, colorData,
BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), true);
}
anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device);
anUrl = TimeDecode.replaceTimeValue(anUrl);
anUrl = BrightnessDecode.calculateReplaceIntensityValue(anUrl, 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);
MozIotCommand theCommand = null;
try {
theUrl = theUrl.replaceAll("^\"|\"$", "");
theCommand = new Gson().fromJson(anUrl, MozIotCommand.class);
} catch (Exception e) {
log.warn("Cannot parse command to Mozilla IOT <<<" + 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("/things/") + 8);
String devicePortion = intermediate.substring(0, intermediate.indexOf('/'));
String theUrlCommand = intermediate.substring(intermediate.indexOf('/') + 1);
MozIotInstance theHandler = moziotMap.get(device.getTargetDevice());
if (theHandler != null) {
try {
boolean success = theHandler.callCommand(devicePortion, theUrlCommand, theCommand.getCommand(), httpClient);
if (!success) {
log.warn("Comand had error to Mozilla IOT");
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 send comand to Mozilla IOT", 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("Mozilla IOT 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(
"Mozilla IOT 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 Object getItems(String type) {
if (!validMoziot)
return null;
log.debug("consolidating devices for Mozilla IOT");
List<MozillaThing> theResponse = null;
Iterator<String> keys = moziotMap.keySet().iterator();
List<MozIotDevice> deviceList = new ArrayList<MozIotDevice>();
while (keys.hasNext()) {
String key = keys.next();
theResponse = moziotMap.get(key).getDevices(httpClient);
if (theResponse != null)
addMozIotDevices(deviceList, theResponse, key);
else {
log.warn("Cannot get devices for Mozilla IOT with name: " + key + ", skipping this Mozilla IOT.");
continue;
}
}
return deviceList;
}
private Boolean addMozIotDevices(List<MozIotDevice> theDeviceList, List<MozillaThing> theSourceList,
String theKey) {
Iterator<MozillaThing> things = theSourceList.iterator();
while (things.hasNext()) {
MozillaThing theThing = things.next();
MozIotDevice theDevice = new MozIotDevice();
theDevice.setDeviceDetail(theThing);
theDevice.setGatewayName(theKey);
theDeviceList.add(theDevice);
}
return true;
}
@Override
public Home createHome(BridgeSettings bridgeSettings) {
moziotMap = null;
validMoziot = bridgeSettings.getBridgeSettingsDescriptor().isValidMozIot();
log.info("Mozilla IOT Home created." + (validMoziot ? "" : " No Mozilla IOTs configured."));
if (validMoziot) {
moziotMap = new HashMap<String, MozIotInstance>();
httpClient = HTTPHome.getHandler();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getMoziotaddress().getDevices()
.iterator();
while (theList.hasNext() && validMoziot) {
NamedIP aMoziot = theList.next();
try {
moziotMap.put(aMoziot.getName(), new MozIotInstance(aMoziot, httpClient));
} catch (Exception e) {
log.error("Cannot get Mozilla IOT (" + aMoziot.getName() + ") setup, Exiting with message: "
+ e.getMessage(), e);
validMoziot = false;
}
}
}
return this;
}
@Override
public void closeHome() {
log.debug("Closing Home.");
if (!closed && validMoziot) {
log.debug("Home is already closed....");
return;
}
if (httpClient != null)
httpClient.closeHandler();
moziotMap = null;
closed = true;
}
@Override
public void refresh() {
// noop
}
}

View File

@@ -0,0 +1,160 @@
package com.bwssystems.HABridge.plugins.moziot;
import java.util.ArrayList;
import java.util.List;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.NameValue;
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
import com.google.gson.Gson;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpPost;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MozIotInstance {
private static final Logger log = LoggerFactory.getLogger(MozIotInstance.class);
private JWT moziotToken;
private NamedIP mozIotIP;
private NameValue[] headers;
private HTTPHandler anHttpClient;
public MozIotInstance(NamedIP theNamedIp, HTTPHandler httpClient) {
mozIotIP = theNamedIp;
headers = null;
moziotToken = null;
anHttpClient = httpClient;
gatewayLogin();
}
public Boolean callCommand(String deviceId, String aCommand, MozIotCommandDetail commandData,
HTTPHandler httpClient) {
log.debug("calling Mozilla IOT: {}:{}{}{}", mozIotIP.getIp(), mozIotIP.getPort(), aCommand,
commandData.getBody());
String aUrl = null;
if (mozIotIP.getSecure() != null && mozIotIP.getSecure())
aUrl = "https://";
else
aUrl = "http://";
headers = getAuthHeader();
aUrl = aUrl + mozIotIP.getIp() + ":" + mozIotIP.getPort() + "/things/" + deviceId + "/" + aCommand;
String theData = httpClient.doHttpRequest(aUrl, HttpPut.METHOD_NAME, "application/json", commandData.getBody(),
headers);
log.debug("call Command return is: <<<{}>>>", theData);
if (theData.contains("error") || theData.contains("ERROR") || theData.contains("Error"))
return false;
return true;
}
public List<MozillaThing> getDevices(HTTPHandler httpClient) {
log.debug("calling Mozilla IOT: " + mozIotIP.getIp() + ":" + mozIotIP.getPort());
List<MozillaThing> deviceList = null;
MozillaThing[] theThings;
String theUrl = null;
String theData;
if (mozIotIP.getSecure() != null && mozIotIP.getSecure())
theUrl = "https://";
else
theUrl = "http://";
headers = getAuthHeader();
theUrl = theUrl + mozIotIP.getIp() + ":" + mozIotIP.getPort() + "/things";
theData = httpClient.doHttpRequest(theUrl, HttpGet.METHOD_NAME, "application/json", null, headers);
if (theData != null) {
log.debug("GET Mozilla IOT Devices - data: " + theData);
try {
theThings = new Gson().fromJson(theData, MozillaThing[].class);
if (theThings != null && theThings.length > 0) {
deviceList = new ArrayList<MozillaThing>();
for (int i = 0; i < theThings.length; i++) {
deviceList.add(theThings[i]);
}
}
} catch (Exception e) {
log.warn("Cannot get an devices for Mozilla IOT {} Gson Parse Error.", mozIotIP.getName());
}
}
return deviceList;
}
private NameValue[] getAuthHeader() {
if (moziotToken == null)
gatewayLogin();
if (headers == null && moziotToken != null) {
headers = new NameValue[3];
headers[0] = new NameValue();
headers[0].setName("Authorization");
headers[0].setValue("Bearer " + moziotToken.getJwt());
headers[1] = new NameValue();
headers[1].setName("Content-Type");
headers[1].setValue("application/json");
headers[2] = new NameValue();
headers[2].setName("Accept");
headers[2].setValue("application/json");
}
return headers;
}
private void gatewayLogin() {
String aUrl = null;
moziotToken = null;
if (mozIotIP.getSecure() != null && mozIotIP.getSecure())
aUrl = "https://";
else
aUrl = "http://";
headers = new NameValue[2];
headers[0] = new NameValue();
headers[0].setName("Content-Type");
headers[0].setValue("application/json");
headers[1] = new NameValue();
headers[1].setName("Accept");
headers[1].setValue("application/json");
aUrl = aUrl + mozIotIP.getIp() + ":" + mozIotIP.getPort() + "/login";
log.debug("gateway login URL: {}", aUrl);
String commandData = "{\"email\": \"" + mozIotIP.getUsername() + "\", \"password\":\"" + mozIotIP.getPassword()
+ "\"}";
log.debug("The login body: {}", commandData);
String theData = anHttpClient.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "application/json", commandData,
headers);
if (theData != null) {
log.debug("GET Mozilla login - data: {}", theData);
try {
moziotToken = new Gson().fromJson(theData, JWT.class);
} catch (Exception e) {
log.warn("Cannot get login for Mozilla IOT {} Gson Parse Error.", mozIotIP.getName());
moziotToken = null;
}
} else {
log.warn("Could not login {} error: <<<{}>>>", mozIotIP.getName(), theData);
}
headers = null;
}
public NamedIP getMozIotIP() {
return mozIotIP;
}
public void setMozIotIP(NamedIP mozIotIP) {
this.mozIotIP = mozIotIP;
}
protected void closeClient() {
}
public JWT getMoziotToken() {
return moziotToken;
}
public void setMoziotToken(JWT moziotToken) {
this.moziotToken = moziotToken;
}
}

View File

@@ -0,0 +1,154 @@
package com.bwssystems.HABridge.plugins.moziot;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class MozillaThing {
@SerializedName("title")
@Expose
private String title;
@SerializedName("type")
@Expose
private String type;
@SerializedName("@context")
@Expose
private String atcontext;
@SerializedName("@type")
@Expose
private List<String> attype = null;
@SerializedName("description")
@Expose
private String description;
@SerializedName("href")
@Expose
private String href;
@SerializedName("properties")
@Expose
private Properties properties;
@SerializedName("actions")
@Expose
private Actions actions;
@SerializedName("events")
@Expose
private Events events;
@SerializedName("links")
@Expose
private List<Link> links = null;
@SerializedName("layoutIndex")
@Expose
private Integer layoutIndex;
@SerializedName("selectedCapability")
@Expose
private String selectedCapability;
@SerializedName("iconHref")
@Expose
private Object iconHref;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAtcontext() {
return atcontext;
}
public void setAtcontext(String atcontext) {
this.atcontext = atcontext;
}
public List<String> getAttype() {
return attype;
}
public void setAttype(List<String> attype) {
this.attype = attype;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getHref() {
return href;
}
public void setHref(String href) {
this.href = href;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public Actions getActions() {
return actions;
}
public void setActions(Actions actions) {
this.actions = actions;
}
public Events getEvents() {
return events;
}
public void setEvents(Events events) {
this.events = events;
}
public List<Link> getLinks() {
return links;
}
public void setLinks(List<Link> links) {
this.links = links;
}
public Integer getLayoutIndex() {
return layoutIndex;
}
public void setLayoutIndex(Integer layoutIndex) {
this.layoutIndex = layoutIndex;
}
public String getSelectedCapability() {
return selectedCapability;
}
public void setSelectedCapability(String selectedCapability) {
this.selectedCapability = selectedCapability;
}
public Object getIconHref() {
return iconHref;
}
public void setIconHref(Object iconHref) {
this.iconHref = iconHref;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

View File

@@ -0,0 +1,55 @@
package com.bwssystems.HABridge.plugins.moziot;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class On {
@SerializedName("title")
@Expose
private String title;
@SerializedName("type")
@Expose
private String type;
@SerializedName("attype")
@Expose
private String attype;
@SerializedName("links")
@Expose
private List<Link> links = null;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAttype() {
return attype;
}
public void setAttype(String attype) {
this.attype = attype;
}
public List<Link> getLinks() {
return links;
}
public void setLinks(List<Link> links) {
this.links = links;
}
}

View File

@@ -0,0 +1,32 @@
package com.bwssystems.HABridge.plugins.moziot;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Properties {
@SerializedName("on")
@Expose
private On on;
@SerializedName("level")
@Expose
private Level level;
public On getOn() {
return on;
}
public void setOn(On on) {
this.on = on;
}
public Level getLevel() {
return level;
}
public void setLevel(Level level) {
this.level = level;
}
}

View File

@@ -96,7 +96,7 @@ public class MQTTHome implements Home {
mqttObject = aGsonHandler.toJson(anItem.getItem());
}
else
mqttObject =anItem.getItem().getAsString();
mqttObject = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
mqttObject = BrightnessDecode.calculateReplaceIntensityValue(mqttObject,
intensity, targetBri, targetBriInc, false);
mqttObject = DeviceDataDecode.replaceDeviceData(mqttObject, device);
@@ -128,7 +128,7 @@ public class MQTTHome implements Home {
log.warn("Should not get here, no mqtt brokers configured");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no mqtt brokers configured\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
}
return responseString;

View File

@@ -51,12 +51,15 @@ public class OpenHABHome implements Home {
if(theUrl != null && !theUrl.isEmpty()) {
OpenHABCommand theCommand = null;
try {
theCommand = new Gson().fromJson(theUrl, OpenHABCommand.class);
if(anItem.getItem().isJsonObject())
theCommand = new Gson().fromJson(anItem.getItem(), OpenHABCommand.class);
else
theCommand = new Gson().fromJson(anItem.getItem().getAsString().replaceAll("^\"|\"$", ""), OpenHABCommand.class);
} catch(Exception e) {
log.warn("Cannot parse command to OpenHAB <<<" + 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);
+ lightId + "/state", null, null).getTheErrors(), HueError[].class);
return responseString;
}
String intermediate = theCommand.getUrl().substring(theCommand.getUrl().indexOf("://") + 3);

View File

@@ -33,16 +33,13 @@ public class OpenHABInstance {
public Boolean callCommand(String aCommand, String commandData, HTTPHandler httpClient) {
log.debug("calling OpenHAB: " + theOpenHAB.getIp() + ":" + theOpenHAB.getPort() + aCommand);
String aUrl = null;
NameValue[] headers = null;
if(theOpenHAB.getSecure() != null && theOpenHAB.getSecure())
aUrl = "https://";
else
aUrl = "http://";
if(theOpenHAB.getUsername() != null && !theOpenHAB.getUsername().isEmpty() && theOpenHAB.getPassword() != null && !theOpenHAB.getPassword().isEmpty()) {
aUrl = aUrl + theOpenHAB.getUsername() + ":" + theOpenHAB.getPassword() + "@";
}
aUrl = aUrl + theOpenHAB.getIp() + ":" + theOpenHAB.getPort() + "/" + aCommand;
String theData = httpClient.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "text/plain", commandData, headers);
String theData = httpClient.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "text/plain", commandData, httpClient.addBasicAuthHeader(null, theOpenHAB));
log.debug("call Command return is: <" + theData + ">");
if(theData.contains("error") || theData.contains("ERROR") || theData.contains("Error"))
return false;

View File

@@ -72,26 +72,26 @@ public class SomfyHome implements Home {
log.warn("Should not get here, no somfy hub available");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no somfy hub available\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else {
if (anItem.getType() != null && anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.SOMFY_DEVICE[DeviceMapTypes.typeIndex])) {
log.debug("executing HUE api request to change activity to Somfy: " + anItem.getItem().toString());
String jsonToPost = anItem.getItem().toString();
String jsonToPost = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
log.debug("executing HUE api request to change activity to Somfy: {}", jsonToPost);
SomfyInfo somfyHandler = getSomfyHandler(device.getTargetDevice());
if(somfyHandler == null) {
log.warn("Should not get here, no Somfy configured");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Should not get here, no somfy configured\", \"parameter\": \"/lights/"
+ lightId + "state\"}}]";
+ lightId + "/state\"}}]";
} else {
try {
somfyHandler.execApply(jsonToPost);
} catch (Exception e) {
log.warn("Error posting request to Somfy");
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Error posting request to SomfyTahoma\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
+ "\",\"description\": \"Error posting request to SomfyTahoma\", \"parameter\": \"/lights/" + lightId + "/state\"}}]";
}
}
@@ -103,7 +103,7 @@ public class SomfyHome implements Home {
@Override
public Home createHome(BridgeSettings bridgeSettings) {
validSomfy = bridgeSettings.getBridgeSettingsDescriptor().isValidSomfy();
log.info("Somfy Home created." + (validSomfy ? "" : " No Somfys configured."));
log.info("Somfy Home created. {}", (validSomfy ? "" : " No Somfys configured."));
if(validSomfy) {
somfys = new HashMap<>();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getSomfyAddress().getDevices().iterator();

View File

@@ -74,8 +74,8 @@ public class SomfyInfo {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
urlEncodedFormEntity.writeTo(bos);
String body = bos.toString();
String response = httpClient.doHttpRequest(BASE_URL + "json/login",HttpPost.METHOD_NAME, "application/x-www-form-urlencoded", body,httpHeader);
log.debug("Somfy login response <<<" + response + ">>>");
String response = httpClient.doHttpRequest(BASE_URL + "json/login", HttpPost.METHOD_NAME, "application/x-www-form-urlencoded", body,httpHeader);
log.debug("Somfy login response <<<{}>>>", response);
}
private NameValue[] getHttpHeaders() {
@@ -89,16 +89,16 @@ public class SomfyInfo {
NameValue[] httpHeader = getHttpHeaders();
log.info("Making SOMFY http setup call");
String response = httpClient.doHttpRequest(BASE_URL + "json/getSetup", HttpGet.METHOD_NAME, "", "", httpHeader );
log.debug("Somfy getSetup response <<<" + response + ">>>");
log.debug("Somfy getSetup response <<<{}>>>", response);
GetSetup setupData = new Gson().fromJson(response, GetSetup.class);
return setupData;
}
public void execApply(String jsonToPost) throws Exception {
login(namedIP.getUsername(), namedIP.getPassword());
log.info("Making SOMFY http exec call");
log.info("Making SOMFY http exec call with json: {}", jsonToPost);
String response = httpClient.doHttpRequest(BASE_URL_ENDUSER + "exec/apply", HttpPost.METHOD_NAME, "application/json;charset=UTF-8", jsonToPost, getHttpHeaders());
log.debug("Somfy exec reply response <<<" + response + ">>>");
log.debug("Somfy exec reply response <<<{}>>>", response);
}

View File

@@ -48,7 +48,7 @@ public class TCPHome implements Home {
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
Socket dataSendSocket = null;
log.debug("executing HUE api request to TCP: " + anItem.getItem().getAsString());
String theUrl = anItem.getItem().getAsString();
String theUrl = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
if(theUrl != null && !theUrl.isEmpty () && theUrl.contains("tcp://")) {
if(!theUrl.startsWith("{\"tcpDevice\""))

View File

@@ -40,7 +40,7 @@ public class UDPHome implements Home {
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
Integer targetBri,Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
log.debug("executing HUE api request to UDP: " + anItem.getItem().getAsString());
String theUrl = anItem.getItem().getAsString();
String theUrl = anItem.getItem().getAsString().replaceAll("^\"|\"$", "");
if(theUrl != null && !theUrl.isEmpty () && theUrl.startsWith("udp://")) {
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));

View File

@@ -4,7 +4,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeControlDescriptor;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Configuration;
import com.bwssystems.HABridge.api.hue.HueConstants;
import com.bwssystems.HABridge.api.hue.HuePublicConfig;
@@ -12,100 +12,145 @@ import com.bwssystems.HABridge.util.UDPDatagramSender;
import java.io.IOException;
import java.net.*;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
// import java.time.Instant;
// import java.time.temporal.ChronoUnit;
import java.util.Enumeration;
import org.apache.http.conn.util.*;
import java.util.HashMap;
import org.apache.http.conn.util.*;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;
public class UpnpListener {
private Logger log = LoggerFactory.getLogger(UpnpListener.class);
private UDPDatagramSender theUDPDatagramSender;
private MulticastSocket upnpMulticastSocket;
private int httpServerPort;
private String upnpConfigIP;
private boolean strict;
// private boolean strict;
private boolean upnpOriginal;
private boolean traceupnp;
private boolean useUpnpIface;
private BridgeControlDescriptor bridgeControl;
private String bridgeId;
private String bridgeSNUUID;
private String httpType;
private HuePublicConfig aHueConfig;
private Integer theUpnpSendDelay;
private String responseTemplate1 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: upnp:rootdevice\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: %s://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: upnp:rootdevice\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: %s://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String responseTemplate3 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: %s://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate = "NOTIFY * HTTP/1.1\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"NTS: ssdp:alive\r\n" +
"hue-bridgeid: %s\r\n" +
"NT: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate1 = "NOTIFY * HTTP/1.1\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"LOCATION: %s://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"NTS: ssdp:alive\r\n" +
"hue-bridgeid: %s\r\n" +
"NT: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl, UDPDatagramSender aUdpDatagramSender) throws IOException {
private String notifyTemplate2 = "NOTIFY * HTTP/1.1\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"LOCATION: %s://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"NTS: ssdp:alive\r\n" +
"hue-bridgeid: %s\r\n" +
"NT: upnp:rootdevice\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String notifyTemplate3 = "NOTIFY * HTTP/1.1\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"LOCATION: %s://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"NTS: ssdp:alive\r\n" +
"hue-bridgeid: %s\r\n" +
"NT: urn:schemas-upnp-org:device:basic:1\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
public UpnpListener(BridgeSettings theSettings, BridgeControlDescriptor theControl,
UDPDatagramSender aUdpDatagramSender) throws IOException {
super();
theUDPDatagramSender = aUdpDatagramSender;
upnpMulticastSocket = null;
httpServerPort = Integer.valueOf(theSettings.getServerPort());
upnpConfigIP = theSettings.getUpnpConfigAddress();
strict = theSettings.isUpnpStrict();
traceupnp = theSettings.isTraceupnp();
useUpnpIface = theSettings.isUseupnpiface();
theUpnpSendDelay = theSettings.getUpnpsenddelay();
httpServerPort = Integer.valueOf(theSettings.getBridgeSettingsDescriptor().getServerPort());
upnpConfigIP = theSettings.getBridgeSettingsDescriptor().getUpnpConfigAddress();
// strict = theSettings.isUpnpStrict();
upnpOriginal = theSettings.getBridgeSettingsDescriptor().isUpnporiginal();
traceupnp = theSettings.getBridgeSettingsDescriptor().isTraceupnp();
useUpnpIface = theSettings.getBridgeSettingsDescriptor().isUseupnpiface();
theUpnpSendDelay = theSettings.getBridgeSettingsDescriptor().getUpnpsenddelay();
bridgeControl = theControl;
aHueConfig = HuePublicConfig.createConfig("temp", upnpConfigIP, HueConstants.HUB_VERSION, theSettings.getHubmac());
aHueConfig = HuePublicConfig.createConfig("temp", upnpConfigIP, HueConstants.HUB_VERSION,
theSettings.getBridgeSettingsDescriptor().getHubmac());
bridgeId = aHueConfig.getBridgeid();
bridgeSNUUID = aHueConfig.getSNUUIDFromMac();
try {
if(useUpnpIface)
upnpMulticastSocket = new MulticastSocket(new InetSocketAddress(upnpConfigIP, Configuration.UPNP_DISCOVERY_PORT));
else
upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
} catch(IOException e){
log.error("Upnp Discovery Port is in use, or restricted by admin (try running with sudo or admin privs): " + Configuration.UPNP_DISCOVERY_PORT + " with message: " + e.getMessage());
throw(e);
if(theSettings.getBridgeSecurity().isUseHttps()) {
httpType = "https";
} else {
httpType = "http";
}
try {
if (useUpnpIface)
upnpMulticastSocket = new MulticastSocket(
new InetSocketAddress(upnpConfigIP, Configuration.UPNP_DISCOVERY_PORT));
else
upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
} catch (IOException e) {
log.error("Upnp Discovery Port is in use, or restricted by admin (try running with sudo or admin privs): "
+ Configuration.UPNP_DISCOVERY_PORT + " with message: " + e.getMessage());
throw (e);
}
}
public boolean startListening(){
public boolean startListening() {
if (bridgeControl.isReinit() || bridgeControl.isStop()) {
log.warn("UPNP Listener exiting as reinit or stop requested....");
return false;
}
log.info("UPNP Discovery Listener starting....");
Enumeration<NetworkInterface> ifs = null;
InetSocketAddress socketAddress = new InetSocketAddress(Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT);
InetSocketAddress socketAddress = new InetSocketAddress(Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT);
try {
ifs = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
ifs = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
log.error("Could not get network interfaces for this machine: " + e.getMessage());
return false;
}
InetAddress theUpnpAddress = null;
while (ifs.hasMoreElements()) {
NetworkInterface xface = ifs.nextElement();
Enumeration<InetAddress> addrs = xface.getInetAddresses();
@@ -116,16 +161,20 @@ public class UpnpListener {
InetAddress addr = addrs.nextElement();
log.debug(name + " ... has addr " + addr);
if (InetAddressUtils.isIPv4Address(addr.getHostAddress())) {
if(!useUpnpIface) {
if(traceupnp)
if (!useUpnpIface) {
if (traceupnp)
log.info("Traceupnp: Interface: " + name + " valid usable IP address: " + addr);
IPsPerNic++;
}
else if(addr.getHostAddress().equals(upnpConfigIP)) {
if(traceupnp)
log.info("Traceupnp: Interface: " + name + " matches upnp config address of IP address: " + addr);
} else if (addr.getHostAddress().equals(upnpConfigIP)) {
if (traceupnp)
log.info("Traceupnp: Interface: " + name + " matches upnp config address of IP address: "
+ addr);
IPsPerNic++;
}
if (addr.getHostAddress().equals(upnpConfigIP)) {
theUpnpAddress = addr;
}
}
}
log.debug("Checking " + name + " to our interface set");
@@ -143,6 +192,30 @@ public class UpnpListener {
}
}
JmDNS jmdns = null;
if (theUpnpAddress != null) {
log.info("Create and run mDNS service.");
try {
// Create a JmDNS instance
jmdns = JmDNS.create(theUpnpAddress, "Philips-hue");
// Register a service - Defined TXT keys: bridgeid, modelid
final HashMap<String, String> values = new HashMap<String, String>();
values.put("modelid", HueConstants.MODEL_ID);
values.put("bridgeid", bridgeId);
ServiceInfo serviceInfo = ServiceInfo.create("_hue._tcp.local.", "Philips Hue - " + bridgeId.substring(bridgeId.length() - 6), httpServerPort, 0, 0, values);
jmdns.registerService(serviceInfo);
} catch (IOException e) {
log.warn("Could not start mDNS service for hue api. {}", e.getMessage());
}
}
if (bridgeControl.isReinit() || bridgeControl.isStop()) {
log.warn("UPNP Listener exiting as reinit or stop requested....");
return false;
}
log.info("UPNP Discovery Listener running and ready....");
boolean loopControl = true;
boolean error = false;
@@ -151,8 +224,8 @@ public class UpnpListener {
} catch (SocketException e1) {
log.warn("Could not sent soTimeout on multi-cast socket");
}
Instant current, previous;
previous = Instant.now();
// Instant current, previous;
// previous = Instant.now();
while (loopControl) { // trigger shutdown here
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
@@ -162,19 +235,33 @@ public class UpnpListener {
try {
sendUpnpResponse(packet);
} catch (IOException e) {
log.warn("UpnpListener encountered an error sending upnp response packet. IP: " + packet.getAddress().getHostAddress() + " with message: " + e.getMessage());
log.warn("UpnpListener encountered an error sending upnp response packet. IP: "
+ packet.getAddress().getHostAddress() + " with message: " + e.getMessage());
log.debug("UpnpListener send upnp exception: ", e);
}
}
/*
current = Instant.now();
if(ChronoUnit.MILLIS.between(previous, current) > Configuration.UPNP_NOTIFY_TIMEOUT) {
sendUpnpNotify(socketAddress.getAddress());
if (ChronoUnit.MILLIS.between(previous, current) > Configuration.UPNP_NOTIFY_TIMEOUT) {
try {
sendUpnpNotify(socketAddress.getAddress());
} catch (IOException e) {
log.warn("UpnpListener encountered an error sending upnp notify packets. IP: "
+ packet.getAddress().getHostAddress() + " with message: " + e.getMessage());
log.debug("UpnpListener send upnp notify exception: ", e);
}
previous = Instant.now();
}
}
*/
} catch (SocketTimeoutException e) {
sendUpnpNotify(socketAddress.getAddress());
try {
sendUpnpNotify(socketAddress.getAddress());
} catch (IOException en) {
log.warn("UpnpListener encountered an error sending upnp notify packets. IP: "
+ packet.getAddress().getHostAddress() + " with message: " + en.getMessage());
log.debug("UpnpListener send upnp notify exception: ", en);
}
} catch (IOException e) {
log.error("UpnpListener encountered an error reading socket. Shutting down", e);
error = true;
@@ -189,6 +276,10 @@ public class UpnpListener {
}
}
upnpMulticastSocket.close();
if(jmdns != null) {
// Unregister all services
jmdns.unregisterAllServices();
}
if (bridgeControl.isReinit())
log.info("UPNP Discovery Listener - ended, restart found");
if (bridgeControl.isStop())
@@ -203,32 +294,34 @@ public class UpnpListener {
/**
* ssdp discovery packet detection
*/
protected boolean isSSDPDiscovery(DatagramPacket packet){
//Only respond to discover request for strict upnp form
protected boolean isSSDPDiscovery(DatagramPacket packet) {
// Only respond to discover request for strict upnp form
String packetString = new String(packet.getData(), 0, packet.getLength());
if(packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1") && packetString.contains("\"ssdp:discover\"")){
if(strict && (packetString.contains("ST: urn:schemas-upnp-org:device:basic:1") || packetString.contains("ST: upnp:rootdevice") || packetString.contains("ST: ssdp:all")))
{
if(traceupnp) {
log.info("Traceupnp: SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
}
else
log.debug("SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: <<<" + packetString + ">>>");
if (packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1")
&& packetString.contains("\"ssdp:discover\"")) {
if ((packetString.contains("ST: urn:schemas-upnp-org:device:basic:1") ||
packetString.contains("ST: upnp:rootdevice") ||
packetString.contains("ST: ssdp:all"))) {
if (traceupnp) {
log.info("Traceupnp: SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":"
+ packet.getPort());
} else
log.debug("SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":"
+ packet.getPort() + ", body: <<<" + packetString + ">>>");
return true;
}
else if (!strict)
{
if(traceupnp) {
log.info("Traceupnp: SSDP M-SEARCH packet (!strict) from " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
}
else
log.debug("SSDP M-SEARCH packet (!strict) from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: <<<" + packetString + ">>>");
return true;
}
}
else {
// log.debug("isSSDPDiscovery found message to not be valid - strict: " + strict);
// log.debug("SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
} /*
* else if (!strict) { if(traceupnp) {
* log.info("Traceupnp: SSDP M-SEARCH packet (!strict) from " +
* packet.getAddress().getHostAddress() + ":" + packet.getPort()); } else
* log.debug("SSDP M-SEARCH packet (!strict) from " +
* packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: <<<"
* + packetString + ">>>"); return true; }
*/
} else {
// log.debug("isSSDPDiscovery found message to not be valid - strict: " +
// strict);
log.debug("SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: "
+ packetString);
}
return false;
}
@@ -245,65 +338,111 @@ public class UpnpListener {
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if(traceupnp) {
log.info("Traceupnp: send upnp discovery template 1 with response address: " + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort + " with discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
if(traceupnp) {
log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort + " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 1 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort);
}
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " with discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if(traceupnp) {
log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort + " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID,
bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort);
}
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate3,Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort);
}
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
}
private void sendUDPResponse(byte[] udpMessage, InetAddress requester, int sourcePort) throws IOException {
log.debug("Sending response string: <<<" + new String(udpMessage) + ">>>");
if(upnpMulticastSocket == null)
throw new IOException("Socket not initialized");
DatagramPacket response = new DatagramPacket(udpMessage, udpMessage.length, requester, sourcePort);
upnpMulticastSocket.send(response);
}
protected void sendUpnpNotify(InetAddress aSocketAddress) {
String notifyData = null;
notifyData = String.format(notifyTemplate, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
log.debug("sendUpnpNotify notifyTemplate is <<<" + notifyData + ">>>");
DatagramPacket notifyPacket = new DatagramPacket(notifyData.getBytes(), notifyData.length(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
try {
upnpMulticastSocket.send(notifyPacket);
} catch (IOException e1) {
log.warn("UpnpListener encountered an error sending upnp notify packet. IP: " + notifyPacket.getAddress().getHostAddress() + " with message: " + e1.getMessage());
log.debug("UpnpListener send upnp notify exception: ", e1);
if(upnpOriginal) {
theUDPDatagramSender.sendUDPResponse(udpMessage, requester, sourcePort);
} else {
if (upnpMulticastSocket == null)
throw new IOException("Socket not initialized");
DatagramPacket response = new DatagramPacket(udpMessage, udpMessage.length, requester, sourcePort);
upnpMulticastSocket.send(response);
}
}
protected void sendUpnpNotify(InetAddress aSocketAddress) throws IOException {
String notifyData = null;
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
notifyData = String.format(notifyTemplate1, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpType, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: sendUpnpNotify notifyTemplate1");
}
log.debug("sendUpnpNotify notifyTemplate1 is <<<{}>>>", notifyData);
sendUDPResponse(notifyData.getBytes(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
notifyData = String.format(notifyTemplate2, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpType, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: sendUpnpNotify notifyTemplate2");
}
log.debug("sendUpnpNotify notifyTemplate2 is <<<{}>>>", notifyData);
sendUDPResponse(notifyData.getBytes(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
notifyData = String.format(notifyTemplate3, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpType, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: sendUpnpNotify notifyTemplate3");
}
log.debug("sendUpnpNotify notifyTemplate3 is <<<{}>>>", notifyData);
sendUDPResponse(notifyData.getBytes(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
}
// added by https://github.com/pvint
// Ruthlessly stolen from https://stackoverflow.com/questions/22045165/java-datagrampacket-receive-how-to-determine-local-ip-interface
// Try to get a source IP that makes sense for the requestor to contact for use in the LOCATION header in replies
// Ruthlessly stolen from
// https://stackoverflow.com/questions/22045165/java-datagrampacket-receive-how-to-determine-local-ip-interface
// Try to get a source IP that makes sense for the requestor to contact for use
// in the LOCATION header in replies
private InetAddress getOutboundAddress(SocketAddress remoteAddress) throws SocketException {
DatagramSocket sock = new DatagramSocket();
// connect is needed to bind the socket and retrieve the local address

View File

@@ -24,7 +24,7 @@ public class UpnpSettingsResource {
private BridgeSettingsDescriptor theSettings;
private BridgeSettings bridgeSettings;
private String hueTemplate = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
private String hueTemplate_pre = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+ "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
+ "<specVersion>\n"
+ "<major>1</major>\n"
@@ -41,8 +41,9 @@ public class UpnpSettingsResource {
+ "<modelNumber>" + HueConstants.MODEL_ID + "</modelNumber>\n"
+ "<modelURL>http://www.meethue.com</modelURL>\n"
+ "<serialNumber>%s</serialNumber>\n"
+ "<UDN>uuid:" + HueConstants.UUID_PREFIX + "%s</UDN>\n"
+ "<presentationURL>index.html</presentationURL>\n"
+ "<UDN>uuid:" + HueConstants.UUID_PREFIX + "%s</UDN>\n";
private String hueTemplate_post = "<presentationURL>index.html</presentationURL>\n"
+ "<iconList>\n"
+ "<icon>\n"
+ "<mimetype>image/png</mimetype>\n"
@@ -62,6 +63,17 @@ public class UpnpSettingsResource {
+ "</device>\n"
+ "</root>\n";
private String hueTemplate_mid_orig = "<serviceList>\n"
+ "<service>\n"
+ "<serviceType>(null)</serviceType>\n"
+ "<serviceId>(null)</serviceId>\n"
+ "<controlURL>(null)</controlURL>\n"
+ "<eventSubURL>(null)</eventSubURL>\n"
+ "<SCPDURL>(null)</SCPDURL>\n"
+ "</service>\n"
+ "</serviceList>\n";
public UpnpSettingsResource(BridgeSettings theBridgeSettings) {
super();
this.bridgeSettings = theBridgeSettings;
@@ -80,7 +92,16 @@ public class UpnpSettingsResource {
String portNumber = Integer.toString(request.port());
String filledTemplate = null;
String httpLocationAddr = getOutboundAddress(request.ip(), request.port()).getHostAddress();
String httpLocationAddr = null;
String hueTemplate = null;
if(theSettings.isUpnporiginal()) {
httpLocationAddr = theSettings.getUpnpConfigAddress();
hueTemplate = hueTemplate_pre + hueTemplate_mid_orig + hueTemplate_post;
} else {
httpLocationAddr = getOutboundAddress(request.ip(), request.port()).getHostAddress();
hueTemplate = hueTemplate_pre + hueTemplate_post;
}
String bridgeIdMac = HuePublicConfig.createConfig("temp", httpLocationAddr, HueConstants.HUB_VERSION, theSettings.getHubmac()).getSNUUIDFromMac();
filledTemplate = String.format(hueTemplate, httpLocationAddr, portNumber, httpLocationAddr, bridgeIdMac, bridgeIdMac);
if(theSettings.isTraceupnp())

View File

@@ -6,6 +6,7 @@ import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -92,4 +93,67 @@ public abstract class BackupHandler {
return theFilenames;
}
public String downloadBackup(String aFilename) {
Path filePath = FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename);
String content = null;
if (Files.notExists(filePath) || !Files.isReadable(filePath)) {
log.warn("Error reading the file: {} - Does not exist or is not readable. continuing...", aFilename);
return null;
}
try {
content = new String(Files.readAllBytes(filePath));
} catch (IOException e) {
log.error("Error reading the file: {} message: {}", aFilename, e.getMessage(), e);
}
return content;
}
public String uploadBackup(String aFilename, String theContent) {
String successMessage = null;
if(aFilename == null || aFilename.isEmpty()) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
aFilename = defaultName + "upload-" + dateFormat.format(Calendar.getInstance().getTime()) + fileExtension;
} else {
if(!aFilename.endsWith(fileExtension)) {
aFilename = aFilename +fileExtension;
}
}
Path filePath = FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename);
successMessage = uploadWriter(theContent, filePath);
return successMessage;
}
private String uploadWriter(String content, Path filePath) {
String aMessage = null;
if (Files.exists(filePath)) {
aMessage = "Error: File exists, cannot write: " + filePath;
log.error(aMessage);
return aMessage;
}
if (Files.notExists(filePath.getParent())) {
try {
Files.createDirectories(filePath.getParent());
} catch (IOException e) {
aMessage = "Error: creating the directory: " + filePath + " message: " + e.getMessage();
log.error(aMessage, e);
return aMessage;
}
}
try {
Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE);
aMessage = "Success: creating file: " + filePath;
} catch (IOException e) {
aMessage = "Error: writing the file: " + filePath + " message: " + e.getMessage();
log.error(aMessage, e);
}
return aMessage;
}
}

View File

@@ -1,5 +1,7 @@
package com.bwssystems.logservices;
import java.io.Serializable;
import com.bwssystems.logservices.LoggingUtil.LogLevels;
/**
@@ -7,7 +9,10 @@ import com.bwssystems.logservices.LoggingUtil.LogLevels;
*
*
*/
public class LoggerInfo {
public class LoggerInfo implements Serializable {
/** serialVersionUID. */
private static final long serialVersionUID = 1085935297588739585L;
private String loggerName;
private LogLevels logLevel;

View File

@@ -60,4 +60,16 @@ legend.form-label {
.msg-error {
color:#F00;
font-size:14px;
}
}
.progress {
display: inline-block;
width: 100px;
border: 3px groove #CCC;
}
.progress div {
font-size: smaller;
background: greenyellow;
width: 0;
}

View File

@@ -29,7 +29,9 @@
.scrollArea {
height: 100%;
/* THis makes the table scroll - disabled as a feature for now
max-height: 800px;
*/
overflow-x: auto;
overflow-y: auto;
border: 1px solid #d5d5d5;

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -17,7 +17,24 @@
<link href="css/strength-meter.min.css" rel="stylesheet">
<link href="css/colorpicker.min.css" rel="stylesheet">
<!--[if lt IE 9]>
<style id="compiled-css" type="text/css">
.goToTop
{
position: fixed;
width: 100px;
height: 40px;
bottom: 0;
right: 0;
z-index: 100000;
cursor: pointer;
margin: 10px;
-moz-opacity: 0.60;
opacity: .60;
filter: alpha(opacity=60);
}
</style>
<!--[if lt IE 9]>
<script type="text/javascript" src="js/html5shiv.min.js"></script>
<script type="text/javascript" src="js/respond.min.js"></script>
<![endif]-->
@@ -75,8 +92,12 @@
</div>
<span class="goToTop">
<button type="button" class="btn btn-light"><i class="glyphicon glyphicon-chevron-up"></i> Back to Top</button>
</span>
<!-- <input type="button" class="goToTop" value="Back to Top" style="display:none;background-color:blue" /> -->
<script src="js/jquery-1.11.3.min.js"></script>
<script src="js/jquery-1.11.3.min.js"></script>
<script src="js/angular.min.js"></script>
<script src="js/angular-route.min.js"></script>
<script src="js/angular-sanitize.min.js"></script>
@@ -89,7 +110,39 @@
<script src="js/angular-base64.min.js"></script>
<script src="js/angular-resource.min.js"></script>
<script src="js/ngStorage.min.js"></script>
<script src="js/bootstrap-colorpicker-module.min.js"></script>
<script src="js/bootstrap-colorpicker-module.min.js"></script>
<script src="js/ng-file-upload-shim.min.js"></script>
<script src="js/ng-file-upload.min.js"></script>
<script src="scripts/app.js"></script>
</body>
<script type="text/javascript">
$(window).load(function(){
$(window).scroll(function () {
if ($(this).scrollTop() > 100) {
$('.goToTop').fadeIn();
} else {
$('.goToTop').fadeOut();
}
});
$('.goToTop').click(function () {
$("html, body").animate({ scrollTop: 0 }, 1000);
return false;
});
});
</script>
<script>
// tell the embed parent frame the height of the content
if (window.parent && window.parent.parent){
window.parent.parent.postMessage(["resultsFrame", {
height: document.body.getBoundingClientRect().height,
slug: "fvdrw83s"
}], "*")
}
// always overwrite window.name, in case users try to set it manually
window.name = "result"
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation" class="active"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -1,4 +1,3 @@
<ul class="nav nav-pills" role="tablist">
<li role="presentation" class="active"><a href="#!/">Bridge Devices</a></li>
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
@@ -20,97 +19,104 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>
<div postrender-action="goToRow()">
<div class="panel panel-default">
<div class="panel-heading">
<h1 class="panel-title">Current devices ({{bridge.devices.length}})</h1>
</div>
<div class="panel-body">
<div postrender-action="goToRow()">
<div class="panel panel-default">
<div class="panel-heading">
<h1 class="panel-title">Current devices ({{bridge.devices.length}})</h1>
</div>
<div class="panel-body">
<div class="form-group">
<button class="btn btn-primary" type="submit" ng-click="renumberDevices()">Renumber Devices</button>
<button ng-if="bridge.securityInfo.useLinkButton" class="btn btn-primary" type="submit" ng-click="pushLinkButton()">Link</button>
<button ng-if="bridge.securityInfo.useLinkButton" class="btn btn-primary" type="submit"
ng-click="pushLinkButton()">Link</button>
<button class="btn btn-primary" type="submit" ng-click="manageLinksButton()">Manage Links</button>
<label for="device-ip-filter">Show devices visible to: </label>
<input type="text" id="device-ip-filter" style="width:150px"
ng-model="bridge.state.filterDevicesByIpAddress" placeholder="">
<input type="checkbox" id="device-ip-filter-mode" ng-model="bridge.state.filterDevicesOnlyFiltered" ng-true-value=true ng-false-value=false style="margin-right: 3px">Must contain filter
ng-model="bridge.state.filterDevicesByIpAddress" placeholder="">
<input type="checkbox" id="device-ip-filter-mode" ng-model="bridge.state.filterDevicesOnlyFiltered"
ng-true-value=true ng-false-value=false style="margin-right: 3px">Must contain filter
<label for="device-type-filter" style="margin-left:50px">Filter device type: </label>
<select name="device-type" id="device-type-filter"
ng-model="bridge.state.filterDeviceType">
<option value="">---No Filter---</option>
<!-- not selected / blank option -->
<option value="custom">Custom</option>
<option value="UDP">UDP</option>
<option value="TCP">TCP</option>
<option value="exec">Execute Script/Program</option>
<option value="switch">Switch</option>
<option value="scene">Scene</option>
<option value="macro">Macro</option>
<option value="group">Group</option>
<option value="activity">Activity</option>
<option value="button">Button</option>
<option value="thermo">Thermo</option>
<option value="passthru">Pass Thru</option>
</select>
<select name="device-type" id="device-type-filter" ng-model="bridge.state.filterDeviceType">
<option value="">---No Filter---</option>
<!-- not selected / blank option -->
<option value="custom">Custom</option>
<option value="UDP">UDP</option>
<option value="TCP">TCP</option>
<option value="exec">Execute Script/Program</option>
<option value="switch">Switch</option>
<option value="scene">Scene</option>
<option value="macro">Macro</option>
<option value="group">Group</option>
<option value="activity">Activity</option>
<option value="button">Button</option>
<option value="thermo">Thermo</option>
<option value="passthru">Pass Thru</option>
<option value="home">Home</option>
</select>
</div>
<scrollable-table watch="bridge.devices">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th sortable-header col="id" comparator-fn="comparatorUniqueId">ID</th>
<th sortable-header col="name">Name</th>
<th sortable-header col="description">Description</th>
<th sortable-header col="devicestate">Device State</th>
<th sortable-header col="deviceType">Type</th>
<th sortable-header col="targetDevice">Target</th>
<th sortable-header col="inactive">Inactive</th>
<th sortable-header col="noState">No State</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="device in bridge.devices | filterDevicesByRequester:bridge.state.filterDevicesByIpAddress:bridge.state.filterDevicesOnlyFiltered:bridge.state.filterDeviceType" row-id="{{device.id}}" ng-class="{info: bridge.viewDevId == device.id}" >
<td>{{$index+1}}</td>
<td>{{device.id}}</td>
<td>{{device.name}}</td>
<td class="cr">{{device.description}}</td>
<td class="cr">on={{device.deviceState.on}},bri={{device.deviceState.bri}},hue={{device.deviceState.hue}},sat={{device.deviceState.sat}},effect={{device.deviceState.effect}},ct={{device.deviceState.ct}},alert={{device.deviceState.alert}},colormode={{device.deviceState.colormode}},reachable={{device.deviceState.reachable}},XYList={{device.deviceState.xy}}</td>
<td>{{device.deviceType}}</td>
<td>{{device.targetDevice}}</td>
<td>{{device.inactive}}</td>
<td>{{device.noState}}</td>
<td>
<p>
<button class="btn btn-info" type="submit"
ng-click="testUrl(device, 'on')">Test ON</button>
<button class="btn btn-info" type="submit"
ng-click="testUrl(device, 'dim')">Test Dim</button>
<button class="btn btn-info" type="submit"
ng-click="testUrl(device, 'off')">Test OFF</button>
<button class="btn btn-info" type="submit"
ng-click="testUrl(device, 'color')">Test Color</button>
<button class="btn btn-warning" type="submit"
ng-click="editDevice(device)">Edit/Copy</button>
<button class="btn btn-danger" type="submit"
ng-click="deleteDevice(device)">Delete</button>
</p>
</td>
</tr>
</table>
</scrollable-table>
<scrollable-table watch="bridge.devices">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th sortable-header col="id" comparator-fn="comparatorUniqueId">ID</th>
<th sortable-header col="name">Name</th>
<th sortable-header col="description">Description</th>
<th sortable-header col="deviceType">Type</th>
<th sortable-header col="targetDevice">Target</th>
<th sortable-header col="startupAction">Startup Action</th>
<th sortable-header col="inactive">Inactive</th>
<th sortable-header col="noState">No State</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="device in bridge.devices | filterDevicesByRequester:bridge.state.filterDevicesByIpAddress:bridge.state.filterDevicesOnlyFiltered:bridge.state.filterDeviceType"
row-id="{{device.id}}" ng-class="{info: bridge.viewDevId == device.id}">
<td title="Locked: {{device.lockDeviceId}} - Click to Toggle" ng-click="toggleLock(device)">
<b ng-if="device.lockDeviceId">{{device.id}}</b>
<p ng-if="!device.lockDeviceId">{{device.id}}</p>
</td>
<td
title="on={{device.deviceState.on}},bri={{device.deviceState.bri}},hue={{device.deviceState.hue}},sat={{device.deviceState.sat}},effect={{device.deviceState.effect}},ct={{device.deviceState.ct}},alert={{device.deviceState.alert}},colormode={{device.deviceState.colormode}},reachable={{device.deviceState.reachable}},XYList={{device.deviceState.xy}}">
{{device.name}}</td>
<td class="cr">{{device.description}}</td>
<td>{{device.deviceType}}</td>
<td>{{device.targetDevice}}</td>
<td class="cr" title="Click to set actions for device on ha-bridge startup." ng-click="setStartupAction(device)">{{device.startupActions}}</td>
<td>{{device.inactive}}</td>
<td>{{device.noState}}</td>
<td>
<p>
<button class="btn btn-info" type="submit" ng-click="testUrl(device, 'on')">Test
ON</button>
<button class="btn btn-info" type="submit" ng-click="testUrl(device, 'dim')">Test
Dim</button>
<button class="btn btn-info" type="submit" ng-click="testUrl(device, 'off')">Test
OFF</button>
<button class="btn btn-info" type="submit" ng-click="testUrl(device, 'color')">Test
Color</button>
<button class="btn btn-warning" type="submit"
ng-click="editDevice(device)">Edit/Copy</button>
<button class="btn btn-danger" type="submit"
ng-click="deleteDevice(device)">Delete</button>
</p>
</td>
</tr>
</table>
</scrollable-table>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h1 class="panel-title">
Bridge Device DB Backup <a ng-click="toggleBk()"><span
class={{imgBkUrl}} aria-hidden="true"></span></a>
Bridge Device DB Backup <a ng-click="toggleBk()"><span class={{imgBkUrl}} aria-hidden="true"></span></a>
</h1>
</div>
<div ng-if="visibleBk" class="panel-body">
@@ -121,14 +127,29 @@
File Name</label>
<div class="col-xs-8 col-sm-7">
<input id="backup-name" class="form-control" type="text"
ng-model="optionalbackupname" placeholder="Optional">
<input id="backup-name" class="form-control" type="text" ng-model="optionalbackupname"
placeholder="Optional">
</div>
<button type="submit" class="btn btn-primary"
ng-click="backupDeviceDb(optionalbackupname)">Backup
<button type="submit" class="btn btn-primary" ng-click="backupDeviceDb(optionalbackupname)">Backup
Device DB</button>
</div>
</form>
<form class="form-horizontal" name="myForm">
<div class="form-group">
<label class="col-xs-12 col-sm-2 control-label" for="backup-name">Device DB to upload</label>
<div class="col-xs-8 col-sm-7">
<input type="file" ngf-select="" ng-model="deviceFile" name="file">
</div>
<button ng-disabled="!myForm.$valid" type="submit" class="btn btn-primary"
ng-click="uploadDeviceFile(deviceFile.name, deviceFile)">Upload</button>
<span class="progress" ng-show="deviceFile.progress >= 0">
<div style="width:{{deviceFile.progress}}%" ng-bind="deviceFile.progress + '%'" class="ng-binding">
</div>
</span>
<span ng-show="deviceFile.result">Upload Successful</span>
</div>
</form>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
@@ -137,12 +158,10 @@
</tr>
</thead>
<tr ng-repeat="backup in bridge.backups">
<td>{{backup}}</td>
<td><a class="btn" ng-click="downloadBackup(backup)" ng-href="{{ url }}">{{backup}}</a></td>
<td>
<button class="btn btn-danger" type="submit"
ng-click="restoreBackup(backup)">Restore</button>
<button class="btn btn-warning" type="submit"
ng-click="deleteBackup(backup)">Delete</button>
<button class="btn btn-danger" type="submit" ng-click="restoreBackup(backup)">Restore</button>
<button class="btn btn-warning" type="submit" ng-click="deleteBackup(backup)">Delete</button>
</td>
</tr>
</table>
@@ -150,7 +169,7 @@
</div>
<script type="text/ng-template" id="valueDialog">
<div class="ngdialog-message">
<div class="ngdialog-message">
<h2>Select value</h2>
<p>
<input type="radio" ng-model="valueType" value="percentage" ng-change="changeScale()"> Percentage
@@ -165,7 +184,7 @@
</div>
</script>
<script type="text/ng-template" id="colorDialog">
<div class="ngdialog-message">
<div class="ngdialog-message">
<h2>Select Color</h2>
<p>
<input colorpicker="rgb" ng-model="rgbPicker.color" type="text">
@@ -176,7 +195,7 @@
</div>
</script>
<script type="text/ng-template" id="deleteDialog">
<div class="ngdialog-message">
<div class="ngdialog-message">
<h2>Device to Delete?</h2>
<p>{{device.name}}</p>
<p>Are you Sure?</p>
@@ -185,3 +204,43 @@
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteDevice(device)">Delete</button>
</div>
</script>
<script type="text/ng-template" id="startupActionDialog">
<div class="ngdialog-message">
<h2>Select Actions for startup on {{device.name}}</h2>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Setting</th>
<th>Value</th>
</tr>
</thead>
<tr>
<td>State</td>
<td>
<select name="the-state" id="the-state" ng-model="theState">
<option value="">---None---</option>
<!-- not selected / blank option -->
<option value="On">On</option>
<option value="Off">Off</option>
</select>
</td>
</tr>
<tr>
<td>Dim</td>
<td>
<input type="checkbox"
ng-model="setDim" ng-true-value=true
ng-false-value=false>
<rzslider rz-slider-model="slider.value" rz-slider-options="slider.options"></rzslider>
</td>
</tr>
<tr>
<td>Color RGB</td>
<td><input colorpicker="rgb" ng-model="rgbPicker.color" type="text"></td>
</tr>
</table>
</div>
<div class="ngdialog-buttons mt">
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="startupActionSave(device)">Save</button>
</div>
</script>

View File

@@ -19,7 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -20,7 +20,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation" class="active"><a href="#!/editdevice">Add/Edit</a></li>
</ul>
@@ -76,6 +78,12 @@
<td><input type="text" class="form-control" id="device-comments"
ng-model="device.comments" placeholder="Device Comments"></td>
</tr>
<tr>
<td><label>Lock Device ID</label></td>
<td><input type="checkbox"
ng-model="device.lockDeviceId" ng-true-value=true
ng-false-value=false> {{device.lockDeviceId}}</td>
</tr>
<tr>
<td><label>Inactive</label></td>
<td><input type="checkbox"
@@ -136,7 +144,8 @@
<option value="button">Button</option>
<option value="thermo">Thermo</option>
<option value="passthru">Pass Thru</option>
</select></td>
<option value="home">Home</option>
</select></td>
</tr>
<tr>
<td><label>Map Type (Legacy)</label></td>

View File

@@ -19,7 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation" class="active"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -19,7 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -19,7 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -19,7 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -19,7 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -19,7 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -19,7 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

View File

@@ -0,0 +1,143 @@
<ul class="nav nav-pills" role="tablist">
<li role="presentation"><a href="#!/">Bridge Devices</a></li>
<li role="presentation"><a href="#!/system">Bridge Control</a></li>
<li role="presentation"><a href="#!/logs">Logs</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#!/veradevices">Vera Devices</a></li>
<li ng-if="bridge.showVera" role="presentation"><a href="#!/verascenes">Vera Scenes</a></li>
<li ng-if="bridge.showFibaro" role="presentation"><a href="#!/fibarodevices">Fibaro Devices</a></li>
<li ng-if="bridge.showFibaro" role="presentation"><a href="#!/fibaroscenes">Fibaro Scenes</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#!/harmonyactivities">Harmony Activities</a></li>
<li ng-if="bridge.showHarmony" role="presentation"><a href="#!/harmonydevices">Harmony Devices</a></li>
<li ng-if="bridge.showNest" role="presentation"><a href="#!/nest">Nest</a></li>
<li ng-if="bridge.showHue" role="presentation"><a href="#!/huedevices">Hue Devices</a></li>
<li ng-if="bridge.showHal" role="presentation"><a href="#!/haldevices">HAL Devices</a></li>
<li ng-if="bridge.showMqtt" role="presentation"><a href="#!/mqttmessages">MQTT Messages</a></li>
<li ng-if="bridge.showHass" role="presentation"><a href="#!/hassdevices">HomeAssistant Devices</a></li>
<li ng-if="bridge.showDomoticz" role="presentation"><a href="#!/domoticzdevices">Domoticz Devices</a></li>
<li ng-if="bridge.showSomfy" role="presentation"><a href="#!/somfydevices">Somfy Devices</a></li>
<li ng-if="bridge.showLifx" role="presentation"><a href="#!/lifxdevices">LIFX Devices</a></li>
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation" class="active"><a href="#!/homegeniedevices">HomeGenie
Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">HomeGenie show device List
({{bridge.homegeniedevices.length}})</h2>
</div>
<div class="panel-body">
<p class="text-muted">For any HomeGenie Device, use the build action buttons
to generate the item addition information into the ha-bridge device and this will put you into the edit
screen. Then
you can modify the name to anything you want that will be the keyword
for the Echo or Google Home. Also, you can go back to any helper tab and click a build
action button to add another item for a multi-command. After you are
done in the edit tab, click the 'Add Bridge Device' to finish that selection
setup. The 'Already Configured HomeGenie Devices' list below will show what
is already setup for your HomeGenie.</p>
<p class="text-muted">
Also, use this select menu for which type of dim control you would
like to be generated: <select name="device-dim-control" id="device-dim-control"
ng-model="device_dim_control">
<option value="">none</option>
<option value="${intensity.byte}">Pass-thru Value</option>
<option value="${intensity.percent}">Percentage</option>
<option value="${intensity.decimal_percent}">Decimal Percentage</option>
<option value="${intensity.math(X*1)}">Custom Math</option>
</select>
</p>
<p class="text-muted">Use the check boxes by the names to use the bulk addition
feature. Select your items and dim control type if wanted, then click
bulk add below. Your items will be added with on and off or dim and
off if selected with the name of the device from the HomeGenie.</p>
<scrollable-table watch="bridge.homegeniedevices">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th sortable-header col="name">
<span><input type="checkbox" name="selectAll" value="{{selectAll}}" ng-checked="selectAll"
ng-click="toggleSelectAll()"> Name</span></th>
<th sortable-header col="domain">Domain</th>
<th sortable-header col="type">Type</th>
<th sortable-header col="homegeniename">HomeGenie</th>
<th>Build Actions</th>
</tr>
</thead>
<tr ng-repeat="homegeniedevice in bridge.homegeniedevices">
<td>{{$index+1}}</td>
<td><input type="checkbox" name="bulk.devices[]" value="{{homegeniedevice.deviceDetail.Name}}"
ng-checked="bulk.devices.indexOf(homegeniedevice.deviceDetail.Name) > -1"
ng-click="toggleSelection(homegeniedevice.deviceDetail.Name)">
{{homegeniedevice.deviceDetail.Name}}</td>
<td>{{homegeniedevice.deviceDetail.Domain}}</td>
<td>{{homegeniedevice.deviceDetail.DeviceType}}</td>
<td>{{homegeniedevice.gatewayName}}</td>
<td>
<button class="btn btn-success" type="submit"
ng-click="buildDeviceUrls(homegeniedevice, device_dim_control, null, null, false)">Build
Item</button>
</td>
</tr>
</table>
</scrollable-table>
<div class="panel-footer">
<button class="btn btn-success" type="submit" ng-click="bulkAddDevices(device_dim_control)">Bulk Add
({{bulk.devices.length}})</button>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
Already Configured HomeGenie Devices <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}}
aria-hidden="true"></span></a></a>
</h2>
</div>
<div ng-if="buttonsVisible" class="panel-body">
<scrollable-table watch="bridge.homegeniedevices">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Row</th>
<th sortable-header col="name">Name</th>
<th sortable-header col="category">Category</th>
<th sortable-header col="homegeniename">HomeGenie</th>
<th>Map Id</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="device in bridge.devices | configuredHomeGenieItems">
<td>{{$index+1}}</td>
<td>{{device.name}}</td>
<td>{{device.deviceType}}</td>
<td>{{device.targetDevice}}</td>
<td>{{device.mapId}}</td>
<td>
<p>
<button class="btn btn-warning" type="submit" ng-click="editDevice(device)">Edit</button>
<button class="btn btn-danger" type="submit" ng-click="deleteDevice(device)">Delete</button>
</p>
</td>
</tr>
</table>
</scrollable-table>
</div>
</div>
<script type="text/ng-template" id="deleteMapandIdDialog">
<div class="ngdialog-message">
<h2>Device Map and Id?</h2>
<p>{{mapandid.mapType}} with {{mapandid.id}}</p>
<p>Are you Sure?</p>
</div>
<div class="ngdialog-buttons mt">
<button type="button" class="ngdialog-button ngdialog-button-error" ng-click="deleteMapandId(mapandid)">Delete</button>
</div>
</script>

View File

@@ -19,7 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation" class="active"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>

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