mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-18 08:13:23 +00:00
Compare commits
230 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84fb79f9d9 | ||
|
|
3702de8efd | ||
|
|
f0ab9afd66 | ||
|
|
e5c4e09543 | ||
|
|
425ac9fb91 | ||
|
|
32203b65be | ||
|
|
7e9600d2a7 | ||
|
|
dcc470486f | ||
|
|
330adbdd4c | ||
|
|
23d43b0136 | ||
|
|
004276d3ea | ||
|
|
e90e0f69ef | ||
|
|
7163a12384 | ||
|
|
bb65650e53 | ||
|
|
7f7e96465b | ||
|
|
8ff7bc0120 | ||
|
|
4da5f65d89 | ||
|
|
faa67827c6 | ||
|
|
bbce1f4235 | ||
|
|
8575deadf1 | ||
|
|
ad4015927c | ||
|
|
f7df6951b0 | ||
|
|
c20d046b30 | ||
|
|
20dedec8ab | ||
|
|
580c037b1e | ||
|
|
77cb064d60 | ||
|
|
0e4319ea1d | ||
|
|
c61e623e23 | ||
|
|
669483f686 | ||
|
|
5a59747bc9 | ||
|
|
53ec096c95 | ||
|
|
8ccb768391 | ||
|
|
9d84e2a180 | ||
|
|
6a5fae583f | ||
|
|
8d15d0a0fb | ||
|
|
d4b8b70a83 | ||
|
|
a276f97776 | ||
|
|
9438b25538 | ||
|
|
6c15ca2c3b | ||
|
|
39782fa339 | ||
|
|
d7e29e2ee5 | ||
|
|
6b4693eaaf | ||
|
|
7f816b03d5 | ||
|
|
ed3db4427b | ||
|
|
80ca8c3ca3 | ||
|
|
e43473734e | ||
|
|
8a468b8352 | ||
|
|
51ce10cfc7 | ||
|
|
b5f7144c9c | ||
|
|
86a931d383 | ||
|
|
3e890721c5 | ||
|
|
62d1c64a3d | ||
|
|
c025b186cd | ||
|
|
e999c3a969 | ||
|
|
351403e611 | ||
|
|
c773477a43 | ||
|
|
5d1f0ce3b6 | ||
|
|
7e0fd6c21b | ||
|
|
3bf52f5da0 | ||
|
|
bd856d8f9e | ||
|
|
73b2be752e | ||
|
|
dda7a7a34a | ||
|
|
f238e05533 | ||
|
|
aaaebd0c05 | ||
|
|
9a1924422e | ||
|
|
e446c618ce | ||
|
|
60d35acff9 | ||
|
|
c9adab53a9 | ||
|
|
72b6b2027b | ||
|
|
60239bad82 | ||
|
|
aecd589308 | ||
|
|
ee45cee8e3 | ||
|
|
21e5dfb338 | ||
|
|
b73a4cd666 | ||
|
|
05418fdda1 | ||
|
|
a717fd7c68 | ||
|
|
8408d7350e | ||
|
|
3ba8f56db2 | ||
|
|
7a0946e3b7 | ||
|
|
50c9369d71 | ||
|
|
6c2a34f507 | ||
|
|
ee2c105040 | ||
|
|
3ac83912f3 | ||
|
|
e62fcf7765 | ||
|
|
9c3d95f177 | ||
|
|
926a7f50dc | ||
|
|
ad820a68c9 | ||
|
|
73f0f766f7 | ||
|
|
113ce0ca59 | ||
|
|
a5860417c1 | ||
|
|
48af3d84a2 | ||
|
|
8ce0483e54 | ||
|
|
1a2024d92b | ||
|
|
8198919a27 | ||
|
|
614734a2aa | ||
|
|
58fccb1fa7 | ||
|
|
77d3084b01 | ||
|
|
922bb54143 | ||
|
|
8586bbd965 | ||
|
|
d64a028f30 | ||
|
|
fff30d17d6 | ||
|
|
13fa5dea73 | ||
|
|
1d6a4c1432 | ||
|
|
9cb275230e | ||
|
|
c97ab2cd38 | ||
|
|
7b45ca9438 | ||
|
|
e6da9950d6 | ||
|
|
20328b15d8 | ||
|
|
2ff73e5672 | ||
|
|
cca9a6be78 | ||
|
|
5d5b68209a | ||
|
|
d2e906caa3 | ||
|
|
a1708f2a88 | ||
|
|
49c3b85894 | ||
|
|
f9f5a3a878 | ||
|
|
2565183ee9 | ||
|
|
a6bb1ae3aa | ||
|
|
4bc91be88b | ||
|
|
315fd31270 | ||
|
|
09787fe08d | ||
|
|
37d346f558 | ||
|
|
ac59398aa0 | ||
|
|
87073435fc | ||
|
|
8687f3482a | ||
|
|
32a5f26ddd | ||
|
|
c28f07d628 | ||
|
|
d3cc961dfb | ||
|
|
1b3d826f28 | ||
|
|
c8f4d89a45 | ||
|
|
2b335d6b9b | ||
|
|
b27bb5eef8 | ||
|
|
3c54ccd56d | ||
|
|
cf772334c4 | ||
|
|
5a843f7569 | ||
|
|
2a52783bb1 | ||
|
|
195f1854ec | ||
|
|
2e5596a6e4 | ||
|
|
9fc13c6c45 | ||
|
|
4b4d4e36c7 | ||
|
|
1e7bdc560b | ||
|
|
aff0f8d64c | ||
|
|
7a812d6e6b | ||
|
|
26f2105801 | ||
|
|
feef345a3b | ||
|
|
314ae58ebd | ||
|
|
d8b6232ac1 | ||
|
|
41e22ee64d | ||
|
|
be2fbcd4cb | ||
|
|
14e7f37522 | ||
|
|
e3f5946c9d | ||
|
|
12eab16f21 | ||
|
|
3df68047a9 | ||
|
|
0dd652f82a | ||
|
|
53af1a4dfd | ||
|
|
816a0025b1 | ||
|
|
405562809a | ||
|
|
4c87c6fce8 | ||
|
|
2fd0f7748b | ||
|
|
ed5f3b4b3c | ||
|
|
aad09b7527 | ||
|
|
0ae66da085 | ||
|
|
d344b764da | ||
|
|
c85b67fb9f | ||
|
|
a23d662444 | ||
|
|
4b98f799c2 | ||
|
|
acba2b5cae | ||
|
|
4dc818296a | ||
|
|
40123ed858 | ||
|
|
718ba5a5c2 | ||
|
|
2e6944d840 | ||
|
|
e29f12905d | ||
|
|
408b79d5d8 | ||
|
|
bf5ad2e23c | ||
|
|
59f1db285d | ||
|
|
203ed0b5d3 | ||
|
|
b443d16a11 | ||
|
|
295b1e1a30 | ||
|
|
c872f3543d | ||
|
|
23f2d2716d | ||
|
|
7c1d6e40b8 | ||
|
|
c5fbd5d1f0 | ||
|
|
aebde7ee48 | ||
|
|
c8fb93eeb6 | ||
|
|
1602ed004a | ||
|
|
7514e36edb | ||
|
|
2789d8c180 | ||
|
|
af1777aeb3 | ||
|
|
fc2d587e1a | ||
|
|
416b4d3fda | ||
|
|
9666273840 | ||
|
|
774bc8a36b | ||
|
|
74d4548beb | ||
|
|
eecf0f9875 | ||
|
|
ed96b5ad81 | ||
|
|
9f7d3ea331 | ||
|
|
68de92bb74 | ||
|
|
392a46c3d8 | ||
|
|
568569248a | ||
|
|
d61d10b5b6 | ||
|
|
7294dbf175 | ||
|
|
6c99358f95 | ||
|
|
eee0394f20 | ||
|
|
d87f3bc541 | ||
|
|
e38374f749 | ||
|
|
937fb5d32d | ||
|
|
bd60d63d0f | ||
|
|
439b081bd5 | ||
|
|
41f68f58b0 | ||
|
|
fa15cf3952 | ||
|
|
3ea7f2903f | ||
|
|
7746938c62 | ||
|
|
96074628fb | ||
|
|
9dc8d8f8bc | ||
|
|
8de39a8bee | ||
|
|
626f0641cc | ||
|
|
020da99e1c | ||
|
|
014911a568 | ||
|
|
77ec373381 | ||
|
|
8d9357cf9e | ||
|
|
b9076d1f2e | ||
|
|
4a80ce4230 | ||
|
|
29e7980939 | ||
|
|
c6132f2b03 | ||
|
|
f8bebe38c0 | ||
|
|
3911f802ee | ||
|
|
e52e0150fc | ||
|
|
561d3cfed5 | ||
|
|
cf92620617 | ||
|
|
5bdbca72c8 | ||
|
|
c31d0bc709 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
@@ -9,3 +8,7 @@ buildNumber.properties
|
||||
*.iml
|
||||
data
|
||||
.idea
|
||||
/target/
|
||||
/.settings/
|
||||
/start.bat
|
||||
/.classpath
|
||||
|
||||
23
.project
Normal file
23
.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>ha-bridge</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,5 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
@@ -1,4 +0,0 @@
|
||||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
||||
160
kodivolume.md
Normal file
160
kodivolume.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# Kodi volume control using dim commands & JSON-RPC
|
||||
|
||||
You can use HA Bridge to adjust the Kodi software volume output. This allows you to use dim commands and set the volume level with percentage values.
|
||||
### What is JSON-RPC?
|
||||
|
||||
The short answer is JSON-RPC an interface to communicate with Kodi. [See the official Kodi Wiki for more info](http://kodi.wiki/view/JSON-RPC_API)
|
||||
### Setup Kodi to allow control through JSON-RPC
|
||||
|
||||
In Kodi navigate to Settings/Services/Control ([screenshot](http://kodi.wiki/view/Settings/Services/Control))
|
||||
|
||||
Turn **ON** the following:
|
||||
- Allow control of Kodi via HTTP
|
||||
- Allow remote control from applications on this system
|
||||
- Allow remote control from applications on other systems
|
||||
|
||||
Change the **username** to something unique and set a strong **password**.
|
||||
|
||||
Make a note of the **PORT**
|
||||
### Adding the device to HA Bridge
|
||||
|
||||
Access the HA Bridge Configuration in your browser and open the **Manual Add** tab.
|
||||
#### Name
|
||||
|
||||
Give the device a unique name that doesn<73>t include **<EFBFBD>volume<EFBFBD>** as it will cause conflicts with the Echo<68>s built in volume controls. A device name of **<EFBFBD>cody sound<6E>** works well.
|
||||
#### Device type
|
||||
|
||||
Select **TCP** in the dropdown
|
||||
### URLs
|
||||
|
||||
This section might seem a little long winded and if you know what you are doing then feel free to jump ahead.
|
||||
#### Building the URL
|
||||
|
||||
We need to log into the Kodi web server without having to fill in the popup each time. You can do this by putting the username and password in the URL. It is not a good idea to do this on other websites as it does put your password in clear view, but for your local network it is fine.
|
||||
|
||||
Use the example below replacing the relevant sections with the details that you defined in the Kodi settings screen in the first step. Replacing the IP with the address of the machine that Kodi is running on.
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc
|
||||
```
|
||||
##### Testing the URL in a browser
|
||||
|
||||
Before you continue, open your custom URL in a browser (making sure Kodi is running). If all is working as it should you will see a big page of JSON that starts with:
|
||||
|
||||
``` json
|
||||
{
|
||||
"description": "JSON-RPC API of XBMC",
|
||||
"id": "http://xbmc.org/jsonrpc/ServiceDescription.json",
|
||||
```
|
||||
|
||||
If you don<6F>t see something that looks like the code above, then go back and double check your settings.
|
||||
#### The JSON request
|
||||
|
||||
The URL is what connects you to Kodi, JSON is what is used to communicate with it. The JSON that is used to set the volume level is:
|
||||
|
||||
``` json
|
||||
{"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":100},"id":1}
|
||||
```
|
||||
##### Joining the URL and JSON
|
||||
|
||||
Join the two together by adding `?request=` to the end of the URL and then add the JSON to the end of the request. You will end up with something like:
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request={"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":100},"id":1}
|
||||
```
|
||||
##### Testing the request in a browser
|
||||
|
||||
Go ahead and test the combined URL/JSON in a browser changing **100** to whatever level you want to set. Kodi should adjust the volume accordingly, try a few different levels to be sure it is working correctly.
|
||||
|
||||
The browser will reformat the URL each time you press return so don<6F>t build the URL in the browser bar without making a copy first.
|
||||
|
||||
Ideally build the URL in a text document which you can easily edit and then copy/paste each time.
|
||||
#### Prepare the three URLs
|
||||
|
||||
You want to end up with three full URLs in your text file, one for each of the commands.
|
||||
|
||||
**ON**
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request={"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":100},"id":1}
|
||||
```
|
||||
|
||||
**DIM**
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request={"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":45},"id":1}
|
||||
```
|
||||
|
||||
**OFF**
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request={"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":0},"id":1}
|
||||
```
|
||||
#### Encoding the JSON part
|
||||
|
||||
You should now be able to control the volume of Kodi using the structured URL you built above in a browser. If you can<61>t get it to work in a browser then you won<6F>t be able to get it to work in HA Bridge.
|
||||
|
||||
In order for HA Bridge to send the JSON request you need to encode the URL. This means we take this:
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request={"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":100},"id":1}
|
||||
```
|
||||
|
||||
And turn it into this:
|
||||
|
||||
```
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request=%7B%22jsonrpc%22%3A%222.0%22%2C%22method%22%3A%22Application.SetVolume%22%2C%22params%22%3A%7B%22volume%22%3A100%7D%2C%22id%22%3A1%7D
|
||||
```
|
||||
|
||||
Note that we are only encoding the JSON, the URL part we leave as is.
|
||||
|
||||
Using the online tool [www.url-encode-decode.com](http://www.url-encode-decode.com/), copy the JSON part (shown in bold below) and paste it into the left hand field.
|
||||
|
||||
http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request=**{"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":100},"id":1}**
|
||||
|
||||
Click the **Encode url** button.
|
||||
|
||||
Copy the output from the right hand box and paste it on a new line in your text file.
|
||||
|
||||
```
|
||||
%7B%22jsonrpc%22%3A%222.0%22%2C%22method%22%3A%22Application.SetVolume%22%2C%22params%22%3A%7B%22volume%22%3A100%7D%2C%22id%22%3A1%7D
|
||||
```
|
||||
#### Join the URL and JSON
|
||||
|
||||
Finally you want to insert the URL part before the request (shown in bold below)
|
||||
|
||||
**http://KODI_USERNAME:KODI_PASSWORD@192.168.1.123:8080/jsonrpc?request=**%7B%22jsonrpc%22%3A%222.0%22%2C%22method%22%3A%22Application.SetVolume%22%2C%22params%22%3A%7B%22volume%22%3A100%7D%2C%22id%22%3A1%7D
|
||||
### HA Bridge
|
||||
|
||||
Paste the final URL in **On URL** field. Just do the one for now, add the device and in the Bridge Control tab click Save.
|
||||
|
||||
Test the button in the Bridge Devices tab and hopefully it should turn the volume up in Kodi.
|
||||
|
||||
Go ahead and repeat the steps for the **Off URL**. All the same steps but change 100% to 0%. If you want to use the previous URL you can, just find the 100% in the encoded URL.
|
||||
|
||||
`<EFBFBD>%22%3A%7B%22volume%22%3A`**100**`%7D%2C%22id%22%3A1%7D`
|
||||
|
||||
and change it to 0
|
||||
|
||||
`<EFBFBD>%22%3A%7B%22volume%22%3A`**0**`%7D%2C%22id%22%3A1%7D`
|
||||
#### Dim JSON
|
||||
|
||||
The Dim URL uses the `${intensity.percent}` to take the given number from your voice command and pass it to Kodi.
|
||||
|
||||
Here is the JSON to <20>Dim<69> the volume
|
||||
|
||||
``` json
|
||||
{"jsonrpc":"2.0","method":"Application.SetVolume","params":{"volume":"${intensity.percent}""},"id":1}
|
||||
```
|
||||
|
||||
You don<6F>t need to encode the `${intensity.percent}` part. This means you can simply replace the number value (100/0) with `${intensity.percent}` as shown below.
|
||||
|
||||
`<EFBFBD>%22%3A%7B%22volume%22%3A`**${intensity.percent}**`%7D%2C%22id%22%3A1%7D`
|
||||
### Controlling the Device
|
||||
|
||||
You can use the commands as listed in the [README](https://github.com/bwssytems/ha-bridge#ask-alexa)
|
||||
|
||||
<EFBFBD>Set Cody Sound to 50 percent<6E>
|
||||
<EFBFBD>Cody Sound to 70 percent<6E>
|
||||
|
||||
Remembering that <20>Turn on Cody Sound<6E> will set the volume to 100%, and <20>Turn off Cody Sound<6E> will mute.
|
||||
215
pom.xml
215
pom.xml
@@ -3,13 +3,13 @@
|
||||
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.bwssytems.HABridge</groupId>
|
||||
<artifactId>amazon-echo-bridge-compact</artifactId>
|
||||
<version>0.1.0</version>
|
||||
<groupId>com.bwssystems.HABridge</groupId>
|
||||
<artifactId>ha-bridge</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Amazon Echo Bridge Compact</name>
|
||||
<description>Emulates a Philips Hue bridge to allow the Amazon Echo to hook up to other HA using lightweight frameworks</description>
|
||||
<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>
|
||||
@@ -17,70 +17,183 @@
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>harmony-java-client</artifactId>
|
||||
<version>1.0.8</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.9</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sparkjava</groupId>
|
||||
<artifactId>spark-core</artifactId>
|
||||
<version>2.2</version>
|
||||
<version>2.3</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.3.6</version>
|
||||
<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-simple</artifactId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.2.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.6.0</version>
|
||||
<version>2.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.eval</groupId>
|
||||
<artifactId>eval</artifactId>
|
||||
<version>0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<version>4.0-beta4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.igniterealtime.smack</groupId>
|
||||
<artifactId>smack-core</artifactId>
|
||||
<version>4.0.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>true</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.bwssytems.HABridge.AmazonEchoBridge</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<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-shade-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
<exclude>META-INF/*.txt</exclude>
|
||||
<exclude>META-INF/maven/**</exclude>
|
||||
<exclude>about_files/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>org.slf4j:slf4j-api</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>commons-logging:commons-logging</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>xpp3:xpp3</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>org.igniterealtime.smack:*</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>com.github.bwssytems:harmony-java-client</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.bwssystems.HABridge.HABridge</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class BridgeControlDescriptor {
|
||||
private boolean reinit;
|
||||
private boolean stop;
|
||||
|
||||
public BridgeControlDescriptor() {
|
||||
super();
|
||||
this.reinit = false;
|
||||
this.stop = false;
|
||||
}
|
||||
|
||||
public boolean isReinit() {
|
||||
return reinit;
|
||||
}
|
||||
public void setReinit(boolean reinit) {
|
||||
this.reinit = reinit;
|
||||
}
|
||||
public boolean isStop() {
|
||||
return stop;
|
||||
}
|
||||
public void setStop(boolean stop) {
|
||||
this.stop = stop;
|
||||
}
|
||||
}
|
||||
283
src/main/java/com/bwssystems/HABridge/BridgeSettings.java
Normal file
283
src/main/java/com/bwssystems/HABridge/BridgeSettings.java
Normal file
@@ -0,0 +1,283 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.util.BackupHandler;
|
||||
import com.bwssystems.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class BridgeSettings extends BackupHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
private BridgeSettingsDescriptor theBridgeSettings;
|
||||
private BridgeControlDescriptor bridgeControl;
|
||||
|
||||
public BridgeSettings() {
|
||||
super();
|
||||
bridgeControl = new BridgeControlDescriptor();
|
||||
theBridgeSettings = new BridgeSettingsDescriptor();
|
||||
String ipV6Stack = System.getProperty("ipV6Stack");
|
||||
if(ipV6Stack == null || !ipV6Stack.equalsIgnoreCase("true")) {
|
||||
System.setProperty("java.net.preferIPv4Stack" , "true");
|
||||
}
|
||||
|
||||
}
|
||||
public BridgeControlDescriptor getBridgeControl() {
|
||||
return bridgeControl;
|
||||
}
|
||||
public BridgeSettingsDescriptor getBridgeSettingsDescriptor() {
|
||||
return theBridgeSettings;
|
||||
}
|
||||
public void buildSettings() {
|
||||
String addressString = null;
|
||||
String theVeraAddress = null;
|
||||
String theHarmonyAddress = null;
|
||||
String configFileProperty = System.getProperty("config.file");
|
||||
if(configFileProperty == null) {
|
||||
Path filePath = Paths.get(Configuration.CONFIG_FILE);
|
||||
if(Files.exists(filePath) && Files.isReadable(filePath))
|
||||
configFileProperty = Configuration.CONFIG_FILE;
|
||||
}
|
||||
String serverPortOverride = System.getProperty("server.port");
|
||||
if(configFileProperty != null)
|
||||
{
|
||||
log.info("reading from config file: " + configFileProperty);
|
||||
theBridgeSettings.setConfigfile(configFileProperty);
|
||||
_loadConfig();
|
||||
}
|
||||
else
|
||||
{
|
||||
log.info("reading from system properties");
|
||||
theBridgeSettings.setNumberoflogmessages(Configuration.NUMBER_OF_LOG_MESSAGES);
|
||||
theBridgeSettings.setFarenheit(true);
|
||||
theBridgeSettings.setConfigfile(Configuration.CONFIG_FILE);
|
||||
theBridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DEFAULT_WEB_PORT));
|
||||
theBridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address"));
|
||||
theBridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db"));
|
||||
theBridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT));
|
||||
|
||||
theVeraAddress = System.getProperty("vera.address");
|
||||
IpList theVeraList = null;
|
||||
if(theVeraAddress != null) {
|
||||
try {
|
||||
theVeraList = new Gson().fromJson(theVeraAddress, IpList.class);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
theVeraList = new Gson().fromJson("{devices:[{name:default,ip:" + theVeraAddress + "}]}", IpList.class);
|
||||
} catch (Exception et) {
|
||||
log.error("Cannot parse vera.address, not set with message: " + e.getMessage(), e);
|
||||
theVeraList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
theBridgeSettings.setVeraAddress(theVeraList);
|
||||
|
||||
theHarmonyAddress = System.getProperty("harmony.address");
|
||||
IpList theHarmonyList = null;
|
||||
if(theHarmonyAddress != null) {
|
||||
try {
|
||||
theHarmonyList = new Gson().fromJson(theHarmonyAddress, IpList.class);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
theHarmonyList = new Gson().fromJson("{devices:[{name:default,ip:" + theHarmonyAddress + "}]}", IpList.class);
|
||||
} catch (Exception et) {
|
||||
log.error("Cannot parse harmony.address, not set with message: " + e.getMessage(), e);
|
||||
theHarmonyList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
theBridgeSettings.setHarmonyAddress(theHarmonyList);
|
||||
theBridgeSettings.setHarmonyUser(System.getProperty("harmony.user"));
|
||||
theBridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd"));
|
||||
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
|
||||
theBridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
|
||||
theBridgeSettings.setButtonsleep(Integer.parseInt(System.getProperty("button.sleep", Configuration.DEFAULT_BUTTON_SLEEP)));
|
||||
theBridgeSettings.setNestuser(System.getProperty("nest.user"));
|
||||
theBridgeSettings.setNestpwd(System.getProperty("nest.pwd"));
|
||||
}
|
||||
|
||||
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().equals("")) {
|
||||
addressString = checkIpAddress(null, true);
|
||||
if(addressString != null) {
|
||||
theBridgeSettings.setUpnpConfigAddress(addressString);
|
||||
log.info("Adding " + addressString + " as our default upnp config address.");
|
||||
}
|
||||
else
|
||||
log.error("Cannot get ip address of this host.");
|
||||
}
|
||||
else {
|
||||
addressString = checkIpAddress(theBridgeSettings.getUpnpConfigAddress(), false);
|
||||
if(addressString == null)
|
||||
log.warn("The upnp config address, " + theBridgeSettings.getUpnpConfigAddress() + ", does not match any known IP's on this host.");
|
||||
}
|
||||
|
||||
if(theBridgeSettings.getUpnpResponsePort() == null)
|
||||
theBridgeSettings.setUpnpResponsePort(Configuration.UPNP_RESPONSE_PORT);
|
||||
|
||||
if(theBridgeSettings.getServerPort() == null)
|
||||
theBridgeSettings.setServerPort(Configuration.DEFAULT_WEB_PORT);
|
||||
|
||||
if(theBridgeSettings.getUpnpDeviceDb() == null)
|
||||
theBridgeSettings.setUpnpDeviceDb(Configuration.DEVICE_DB_DIRECTORY);
|
||||
|
||||
if(theBridgeSettings.getNumberoflogmessages() == null)
|
||||
theBridgeSettings.setNumberoflogmessages(new Integer(Configuration.NUMBER_OF_LOG_MESSAGES));
|
||||
|
||||
if(theBridgeSettings.getNumberoflogmessages() <= 0)
|
||||
theBridgeSettings.setNumberoflogmessages(new Integer(Configuration.NUMBER_OF_LOG_MESSAGES));
|
||||
|
||||
if(theBridgeSettings.getButtonsleep() == null)
|
||||
theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP));
|
||||
|
||||
if(theBridgeSettings.getButtonsleep() < 0)
|
||||
theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP));
|
||||
|
||||
theBridgeSettings.setVeraconfigured(theBridgeSettings.isValidVera());
|
||||
theBridgeSettings.setHarmonyconfigured(theBridgeSettings.isValidHarmony());
|
||||
theBridgeSettings.setNestConfigured(theBridgeSettings.isValidNest());
|
||||
theBridgeSettings.setHueconfigured(theBridgeSettings.isValidHue());
|
||||
theBridgeSettings.setHalconfigured(theBridgeSettings.isValidHal());
|
||||
if(serverPortOverride != null)
|
||||
theBridgeSettings.setServerPort(serverPortOverride);
|
||||
setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-");
|
||||
}
|
||||
|
||||
public void loadConfig() {
|
||||
if(theBridgeSettings.getConfigfile() != null)
|
||||
_loadConfig();
|
||||
}
|
||||
private void _loadConfig() {
|
||||
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
|
||||
_loadConfig(configPath);
|
||||
}
|
||||
|
||||
private void _loadConfig(Path aPath) {
|
||||
String jsonContent = configReader(aPath);
|
||||
if(jsonContent == null)
|
||||
return;
|
||||
try {
|
||||
theBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettingsDescriptor.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("Issue loading values from file: " + aPath.toUri().toString() + ", Gson convert failed.");
|
||||
theBridgeSettings = new BridgeSettingsDescriptor();
|
||||
theBridgeSettings.setConfigfile(aPath.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void save(BridgeSettingsDescriptor newBridgeSettings) {
|
||||
log.debug("Save HA Bridge settings.");
|
||||
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
String jsonValue = aRenderer.render(newBridgeSettings);
|
||||
configWriter(jsonValue, configPath);
|
||||
_loadConfig(configPath);
|
||||
}
|
||||
|
||||
|
||||
private void configWriter(String content, Path filePath) {
|
||||
if(Files.exists(filePath) && !Files.isWritable(filePath)){
|
||||
log.error("Error file is not writable: " + filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Files.notExists(filePath.getParent())) {
|
||||
try {
|
||||
Files.createDirectories(filePath.getParent());
|
||||
} catch (IOException e) {
|
||||
log.error("Error creating the directory: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Path target = null;
|
||||
if(Files.exists(filePath)) {
|
||||
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "habridge.config.old");
|
||||
Files.move(filePath, target);
|
||||
}
|
||||
Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE);
|
||||
|
||||
// set attributes to be for user only
|
||||
// using PosixFilePermission to set file permissions
|
||||
Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
|
||||
// add owners permission
|
||||
perms.add(PosixFilePermission.OWNER_READ);
|
||||
perms.add(PosixFilePermission.OWNER_WRITE);
|
||||
|
||||
try {
|
||||
Files.setPosixFilePermissions(filePath, perms);
|
||||
} catch(UnsupportedOperationException e) {
|
||||
log.info("Cannot set permissions for config file on this system as it is not supported. Continuing");
|
||||
}
|
||||
if(target != null)
|
||||
Files.delete(target);
|
||||
} catch (IOException e) {
|
||||
log.error("Error writing the file: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String configReader(Path filePath) {
|
||||
String content = null;
|
||||
if(Files.notExists(filePath) || !Files.isReadable(filePath)){
|
||||
log.warn("Error reading the file: " + filePath + " - Does not exist or is not readable. continuing...");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
content = new String(Files.readAllBytes(filePath));
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private String checkIpAddress(String ipAddress, boolean checkForLocalhost) {
|
||||
Enumeration<NetworkInterface> ifs = null;
|
||||
try {
|
||||
ifs = NetworkInterface.getNetworkInterfaces();
|
||||
} catch(SocketException e) {
|
||||
log.error("checkIpAddress cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
String addressString = null;
|
||||
InetAddress address = null;
|
||||
while (ifs.hasMoreElements() && addressString == null) {
|
||||
NetworkInterface xface = ifs.nextElement();
|
||||
Enumeration<InetAddress> addrs = xface.getInetAddresses();
|
||||
String name = xface.getName();
|
||||
int IPsPerNic = 0;
|
||||
|
||||
while (addrs.hasMoreElements() && IPsPerNic == 0) {
|
||||
address = addrs.nextElement();
|
||||
if (InetAddressUtils.isIPv4Address(address.getHostAddress())) {
|
||||
log.debug(name + " ... has IPV4 addr " + address);
|
||||
if(checkForLocalhost && (!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE) || !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS))) {
|
||||
IPsPerNic++;
|
||||
addressString = address.getHostAddress();
|
||||
log.debug("checkIpAddress found " + addressString + " from interface " + name);
|
||||
}
|
||||
else if(ipAddress != null && ipAddress.equalsIgnoreCase(address.getHostAddress())){
|
||||
addressString = ipAddress;
|
||||
IPsPerNic++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return addressString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
|
||||
|
||||
public class BridgeSettingsDescriptor {
|
||||
private String upnpconfigaddress;
|
||||
private Integer serverport;
|
||||
private Integer upnpresponseport;
|
||||
private String upnpdevicedb;
|
||||
private IpList veraaddress;
|
||||
private IpList harmonyaddress;
|
||||
private String harmonyuser;
|
||||
private String harmonypwd;
|
||||
private Integer buttonsleep;
|
||||
private boolean upnpstrict;
|
||||
private boolean traceupnp;
|
||||
private String nestuser;
|
||||
private String nestpwd;
|
||||
private boolean veraconfigured;
|
||||
private boolean harmonyconfigured;
|
||||
private boolean nestconfigured;
|
||||
private boolean farenheit;
|
||||
private String configfile;
|
||||
private Integer numberoflogmessages;
|
||||
private IpList hueaddress;
|
||||
private boolean hueconfigured;
|
||||
private IpList haladdress;
|
||||
private String haltoken;
|
||||
private boolean halconfigured;
|
||||
private Map<String, WhitelistEntry> whitelist;
|
||||
private boolean settingsChanged;
|
||||
private String myechourl;
|
||||
private String webaddress;
|
||||
|
||||
public BridgeSettingsDescriptor() {
|
||||
super();
|
||||
this.upnpstrict = true;
|
||||
this.traceupnp = false;
|
||||
this.nestconfigured = false;
|
||||
this.veraconfigured = false;
|
||||
this.harmonyconfigured = false;
|
||||
this.hueconfigured = false;
|
||||
this.halconfigured = false;
|
||||
this.farenheit = true;
|
||||
this.whitelist = null;
|
||||
this.settingsChanged = false;
|
||||
this.myechourl = "echo.amazon.com/#cards";
|
||||
this.webaddress = "0.0.0.0";
|
||||
}
|
||||
public String getUpnpConfigAddress() {
|
||||
return upnpconfigaddress;
|
||||
}
|
||||
public void setUpnpConfigAddress(String upnpConfigAddress) {
|
||||
this.upnpconfigaddress = upnpConfigAddress;
|
||||
}
|
||||
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 IpList getVeraAddress() {
|
||||
return veraaddress;
|
||||
}
|
||||
public void setVeraAddress(IpList veraAddress) {
|
||||
this.veraaddress = veraAddress;
|
||||
}
|
||||
public IpList getHarmonyAddress() {
|
||||
return harmonyaddress;
|
||||
}
|
||||
public void setHarmonyAddress(IpList harmonyaddress) {
|
||||
this.harmonyaddress = harmonyaddress;
|
||||
}
|
||||
public String getHarmonyUser() {
|
||||
return harmonyuser;
|
||||
}
|
||||
public void setHarmonyUser(String harmonyuser) {
|
||||
this.harmonyuser = harmonyuser;
|
||||
}
|
||||
public String getHarmonyPwd() {
|
||||
return harmonypwd;
|
||||
}
|
||||
public void setHarmonyPwd(String harmonypwd) {
|
||||
this.harmonypwd = harmonypwd;
|
||||
}
|
||||
public boolean isUpnpStrict() {
|
||||
return upnpstrict;
|
||||
}
|
||||
public void setUpnpStrict(boolean upnpStrict) {
|
||||
this.upnpstrict = upnpStrict;
|
||||
}
|
||||
public boolean isTraceupnp() {
|
||||
return traceupnp;
|
||||
}
|
||||
public void setTraceupnp(boolean traceupnp) {
|
||||
this.traceupnp = traceupnp;
|
||||
}
|
||||
public 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 void setVeraconfigured(boolean veraconfigured) {
|
||||
this.veraconfigured = veraconfigured;
|
||||
}
|
||||
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;
|
||||
}
|
||||
public void setWhitelist(Map<String, WhitelistEntry> whitelist) {
|
||||
this.whitelist = whitelist;
|
||||
}
|
||||
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 Boolean isValidVera() {
|
||||
if(this.getVeraAddress() == null || this.getVeraAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getVeraAddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidHarmony() {
|
||||
if(this.getHarmonyAddress() == null || this.getHarmonyAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getHarmonyAddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
if(this.getHarmonyPwd() == null || this.getHarmonyPwd().equals(""))
|
||||
return false;
|
||||
if(this.getHarmonyUser() == null || this.getHarmonyUser().equals(""))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidNest() {
|
||||
if(this.getNestpwd() == null || this.getNestpwd().equals(""))
|
||||
return false;
|
||||
if(this.getNestuser() == null || this.getNestuser().equals(""))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidHue() {
|
||||
if(this.getHueaddress() == null || this.getHueaddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getHueaddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidHal() {
|
||||
if(this.getHaladdress() == null || this.getHaladdress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getHaladdress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
if(this.getHaltoken() == null || this.getHaltoken().equals(""))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
15
src/main/java/com/bwssystems/HABridge/Configuration.java
Normal file
15
src/main/java/com/bwssystems/HABridge/Configuration.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class Configuration {
|
||||
public final static String DEVICE_DB_DIRECTORY = "data/device.db";
|
||||
public final static String UPNP_RESPONSE_PORT = "50000";
|
||||
public final static String DEFAULT_ADDRESS = "1.1.1.1";
|
||||
public final static String LOOP_BACK_ADDRESS = "127.0.0.1";
|
||||
public final static String LOOP_BACK_INTERFACE = "lo";
|
||||
public final static String DEFAULT_WEB_PORT = "80";
|
||||
public final static String DEFAULT_BUTTON_SLEEP = "100";
|
||||
public static final int UPNP_DISCOVERY_PORT = 1900;
|
||||
public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250";
|
||||
public static final String CONFIG_FILE = "data/habridge.config";
|
||||
public static final int NUMBER_OF_LOG_MESSAGES = 512;
|
||||
}
|
||||
112
src/main/java/com/bwssystems/HABridge/HABridge.java
Normal file
112
src/main/java/com/bwssystems/HABridge/HABridge.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import static spark.Spark.*;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.devicemanagmeent.*;
|
||||
import com.bwssystems.HABridge.hue.HueMulator;
|
||||
import com.bwssystems.HABridge.upnp.UpnpListener;
|
||||
import com.bwssystems.HABridge.upnp.UpnpSettingsResource;
|
||||
import com.bwssystems.NestBridge.NestHome;
|
||||
import com.bwssystems.hal.HalHome;
|
||||
import com.bwssystems.harmony.HarmonyHome;
|
||||
import com.bwssystems.hue.HueHome;
|
||||
import com.bwssystems.util.UDPDatagramSender;
|
||||
|
||||
public class HABridge {
|
||||
|
||||
/*
|
||||
* This program is based on the work of armzilla from this github repository:
|
||||
* https://github.com/armzilla/amazon-echo-ha-bridge
|
||||
*
|
||||
* This is the main entry point to start the amazon echo bridge.
|
||||
*
|
||||
* This program is using sparkjava rest server to build all the http calls.
|
||||
* Sparkjava is a microframework that uses Jetty webserver module to host
|
||||
* its' calls. This is a very compact system than using the spring frameworks
|
||||
* that was previously used.
|
||||
*
|
||||
* There is a custom upnp listener that is started to handle discovery.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Logger log = LoggerFactory.getLogger(HABridge.class);
|
||||
DeviceResource theResources;
|
||||
HarmonyHome harmonyHome;
|
||||
NestHome nestHome;
|
||||
HueHome hueHome;
|
||||
HalHome halHome;
|
||||
HueMulator theHueMulator;
|
||||
UDPDatagramSender udpSender;
|
||||
UpnpSettingsResource theSettingResponder;
|
||||
UpnpListener theUpnpListener;
|
||||
SystemControl theSystem;
|
||||
BridgeSettings bridgeSettings;
|
||||
Version theVersion;
|
||||
|
||||
theVersion = new Version();
|
||||
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") starting....");
|
||||
|
||||
bridgeSettings = new BridgeSettings();
|
||||
while(!bridgeSettings.getBridgeControl().isStop()) {
|
||||
bridgeSettings.buildSettings();
|
||||
log.info("HA Bridge 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
|
||||
port(bridgeSettings.getBridgeSettingsDescriptor().getServerPort());
|
||||
// sparkjava config directive to set html static file location for Jetty
|
||||
staticFileLocation("/public");
|
||||
// setup system control api first
|
||||
theSystem = new SystemControl(bridgeSettings, theVersion);
|
||||
theSystem.setupServer();
|
||||
//setup the harmony connection if available
|
||||
harmonyHome = new HarmonyHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
//setup the nest connection if available
|
||||
nestHome = new NestHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
//setup the hue passtrhu configuration if available
|
||||
hueHome = new HueHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
//setup the hal configuration if available
|
||||
halHome = new HalHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
// setup the class to handle the resource setup rest api
|
||||
theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), harmonyHome, nestHome, hueHome, halHome);
|
||||
// setup the class to handle the upnp response rest api
|
||||
theSettingResponder = new UpnpSettingsResource(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
theSettingResponder.setupServer();
|
||||
// setup the UDP Datagram socket to be used by the HueMulator and the upnpListener
|
||||
udpSender = UDPDatagramSender.createUDPDatagramSender(bridgeSettings.getBridgeSettingsDescriptor().getUpnpResponsePort());
|
||||
if(udpSender == null) {
|
||||
bridgeSettings.getBridgeControl().setStop(true);
|
||||
}
|
||||
else {
|
||||
// setup the class to handle the hue emulator rest api
|
||||
theHueMulator = new HueMulator(bridgeSettings.getBridgeSettingsDescriptor(), theResources.getDeviceRepository(), harmonyHome, nestHome, hueHome, udpSender);
|
||||
theHueMulator.setupServer();
|
||||
// wait for the sparkjava initialization of the rest api classes to be complete
|
||||
awaitInitialization();
|
||||
|
||||
// start the upnp ssdp discovery listener
|
||||
theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl(), udpSender);
|
||||
if(theUpnpListener.startListening())
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") reinitialization requessted....");
|
||||
else
|
||||
bridgeSettings.getBridgeControl().setStop(true);
|
||||
if(bridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged())
|
||||
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
}
|
||||
bridgeSettings.getBridgeControl().setReinit(false);
|
||||
stop();
|
||||
nestHome.closeTheNest();
|
||||
nestHome = null;
|
||||
harmonyHome.shutdownHarmonyHubs();
|
||||
harmonyHome = null;
|
||||
udpSender.closeResponseSocket();
|
||||
}
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting....");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
16
src/main/java/com/bwssystems/HABridge/IpList.java
Normal file
16
src/main/java/com/bwssystems/HABridge/IpList.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IpList {
|
||||
private List<NamedIP> devices;
|
||||
|
||||
public List<NamedIP> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
public void setDevices(List<NamedIP> devices) {
|
||||
this.devices = devices;
|
||||
}
|
||||
|
||||
}
|
||||
25
src/main/java/com/bwssystems/HABridge/NamedIP.java
Normal file
25
src/main/java/com/bwssystems/HABridge/NamedIP.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class NamedIP {
|
||||
private String name;
|
||||
private String ip;
|
||||
private String port;
|
||||
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 getPort() {
|
||||
return port;
|
||||
}
|
||||
public void setPort(String port) {
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
283
src/main/java/com/bwssystems/HABridge/SystemControl.java
Normal file
283
src/main/java/com/bwssystems/HABridge/SystemControl.java
Normal file
@@ -0,0 +1,283 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import static spark.Spark.get;
|
||||
import static spark.Spark.options;
|
||||
import static spark.Spark.post;
|
||||
import static spark.Spark.put;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MulticastSocket;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.dao.BackupFilename;
|
||||
import com.bwssystems.logservices.LoggerInfo;
|
||||
import com.bwssystems.logservices.LoggingForm;
|
||||
import com.bwssystems.logservices.LoggingManager;
|
||||
import com.bwssystems.util.TextStringFormatter;
|
||||
import com.bwssystems.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.classic.spi.LoggingEvent;
|
||||
import ch.qos.logback.core.read.CyclicBufferAppender;
|
||||
|
||||
public class SystemControl {
|
||||
private static final Logger log = LoggerFactory.getLogger(SystemControl.class);
|
||||
public static final String CYCLIC_BUFFER_APPENDER_NAME = "CYCLIC";
|
||||
private LoggerContext lc;
|
||||
private static final String SYSTEM_CONTEXT = "/system";
|
||||
private BridgeSettings bridgeSettings;
|
||||
private Version version;
|
||||
private CyclicBufferAppender<ILoggingEvent> cyclicBufferAppender;
|
||||
private DateFormat dateFormat;
|
||||
private LoggingManager theLogServiceMgr;
|
||||
|
||||
|
||||
public SystemControl(BridgeSettings theBridgeSettings, Version theVersion) {
|
||||
this.bridgeSettings = theBridgeSettings;
|
||||
this.version = theVersion;
|
||||
this.lc = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
this.dateFormat = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss.SSS");
|
||||
reacquireCBA();
|
||||
theLogServiceMgr = new LoggingManager();
|
||||
theLogServiceMgr.init();
|
||||
}
|
||||
|
||||
// This function sets up the sparkjava rest calls for the hue api
|
||||
public void setupServer() {
|
||||
log.info("System control service started....");
|
||||
// http://ip_address:port/system/habridge/version gets the version of this bridge instance
|
||||
get (SYSTEM_CONTEXT + "/habridge/version", "application/json", (request, response) -> {
|
||||
log.debug("Get HA Bridge version: v" + version.getVersion());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return "{\"version\":\"" + version.getVersion() + "\"}";
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/logmsgs gets the log messages for the bridge
|
||||
get (SYSTEM_CONTEXT + "/logmsgs", "application/json", (request, response) -> {
|
||||
log.debug("Get logmsgs.");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
String logMsgs;
|
||||
int count = -1;
|
||||
if(cyclicBufferAppender == null)
|
||||
reacquireCBA();
|
||||
if (cyclicBufferAppender != null) {
|
||||
count = cyclicBufferAppender.getLength();
|
||||
}
|
||||
logMsgs = "[";
|
||||
if (count == -1) {
|
||||
logMsgs = logMsgs + "{\"message\":\"Failed to locate CyclicBuffer\"}";
|
||||
} else if (count == 0) {
|
||||
logMsgs = logMsgs + "{\"message\":\"No logging events to display\"}";
|
||||
} else {
|
||||
LoggingEvent le;
|
||||
for (int i = 0; i < count; i++) {
|
||||
le = (LoggingEvent) cyclicBufferAppender.get(i);
|
||||
logMsgs = logMsgs + ( i > 0?",{":"{") + "\"time\":\"" + dateFormat.format(le.getTimeStamp()) + "\",\"level\":\"" + le.getLevel().levelStr + "\",\"component\":\"" + le.getLoggerName() + "\",\"message\":\"" + TextStringFormatter.forJSON(le.getFormattedMessage()) + "\"}";
|
||||
}
|
||||
}
|
||||
logMsgs = logMsgs + "]";
|
||||
response.status(200);
|
||||
return logMsgs;
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/logmgmt/loggers gets the logger info for the bridge
|
||||
get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", "application/json", (request, response) -> {
|
||||
log.debug("Get loggers info with showAll argument: " + request.params(":all"));
|
||||
Boolean showAll = false;
|
||||
if(request.params(":all").equals("true"))
|
||||
showAll = true;
|
||||
theLogServiceMgr.setShowAll(showAll);
|
||||
theLogServiceMgr.init();
|
||||
response.status(200);
|
||||
return theLogServiceMgr.getConfiguredLoggers();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/logmgmt/update CORS request
|
||||
options(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/logmgmt/update which changes logging parameters for the process
|
||||
put(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
|
||||
log.debug("update loggers: " + request.body());
|
||||
response.status(200);
|
||||
LoggerInfo updateLoggers[];
|
||||
updateLoggers = new Gson().fromJson(request.body(), LoggerInfo[].class);
|
||||
LoggingForm theModel = theLogServiceMgr.getModel();
|
||||
theModel.setUpdatedLoggers(Arrays.asList(updateLoggers));
|
||||
theLogServiceMgr.updateLogLevels();
|
||||
return theLogServiceMgr.getConfiguredLoggers();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/settings which returns the bridge configuration settings
|
||||
get(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
log.debug("bridge settings requested from " + request.ip());
|
||||
|
||||
response.status(200);
|
||||
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/settings CORS request
|
||||
options(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/settings which returns the bridge configuration settings
|
||||
put(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
log.debug("save bridge settings requested from " + request.ip() + " with body: " + request.body());
|
||||
BridgeSettingsDescriptor newBridgeSettings = new Gson().fromJson(request.body(), BridgeSettingsDescriptor.class);
|
||||
bridgeSettings.save(newBridgeSettings);
|
||||
response.status(200);
|
||||
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/control/reinit CORS request
|
||||
options(SYSTEM_CONTEXT + "/control/reinit", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/control/reinit sets the parameter reinit the server
|
||||
put(SYSTEM_CONTEXT + "/control/reinit", "application/json", (request, response) -> {
|
||||
return reinit();
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/control/stop CORS request
|
||||
options(SYSTEM_CONTEXT + "/control/stop", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/control/stop sets the parameter stop the server
|
||||
put(SYSTEM_CONTEXT + "/control/stop", "application/json", (request, response) -> {
|
||||
return stop();
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/backup/available returns a list of config backup filenames
|
||||
get (SYSTEM_CONTEXT + "/backup/available", "application/json", (request, response) -> {
|
||||
log.debug("Get backup filenames");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return bridgeSettings.getBackups();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/create CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
put (SYSTEM_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
log.debug("Create backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
BackupFilename returnFilename = new BackupFilename();
|
||||
returnFilename.setFilename(bridgeSettings.backup(aFilename.getFilename()));
|
||||
return returnFilename;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/delete CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (SYSTEM_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
log.debug("Delete backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null)
|
||||
bridgeSettings.deleteBackup(aFilename.getFilename());
|
||||
else
|
||||
log.warn("No filename given for delete backup.");
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/restore CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (SYSTEM_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
log.debug("Restore backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null) {
|
||||
bridgeSettings.restoreBackup(aFilename.getFilename());
|
||||
bridgeSettings.loadConfig();
|
||||
}
|
||||
else
|
||||
log.warn("No filename given for restore backup.");
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
}
|
||||
|
||||
void reacquireCBA() {
|
||||
cyclicBufferAppender = (CyclicBufferAppender<ILoggingEvent>) lc.getLogger(
|
||||
Logger.ROOT_LOGGER_NAME).getAppender(CYCLIC_BUFFER_APPENDER_NAME);
|
||||
cyclicBufferAppender.setMaxSize(bridgeSettings.getBridgeSettingsDescriptor().getNumberoflogmessages());
|
||||
}
|
||||
|
||||
protected void pingListener() {
|
||||
try {
|
||||
byte[] buf = new byte[256];
|
||||
String testData = "M-SEARCH * HTTP/1.1\nHOST: " + Configuration.UPNP_MULTICAST_ADDRESS + ":" + Configuration.UPNP_DISCOVERY_PORT + "ST: urn:schemas-upnp-org:device:CloudProxy:1\nMAN: \"ssdp:discover\"\nMX: 3";
|
||||
buf = testData.getBytes();
|
||||
MulticastSocket socket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
|
||||
|
||||
InetAddress group = InetAddress.getByName(Configuration.UPNP_MULTICAST_ADDRESS);
|
||||
DatagramPacket packet;
|
||||
packet = new DatagramPacket(buf, buf.length, group, Configuration.UPNP_DISCOVERY_PORT);
|
||||
socket.send(packet);
|
||||
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.warn("Error pinging listener. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String reinit() {
|
||||
bridgeSettings.getBridgeControl().setReinit(true);
|
||||
pingListener();
|
||||
return "{\"control\":\"reiniting\"}";
|
||||
}
|
||||
|
||||
public String stop() {
|
||||
bridgeSettings.getBridgeControl().setStop(true);
|
||||
pingListener();
|
||||
return "{\"control\":\"stopping\"}";
|
||||
}
|
||||
}
|
||||
50
src/main/java/com/bwssystems/HABridge/Version.java
Normal file
50
src/main/java/com/bwssystems/HABridge/Version.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
public final class Version {
|
||||
|
||||
private String version;
|
||||
private String groupId;
|
||||
private String artifactId;
|
||||
private Properties prop;
|
||||
|
||||
public Version()
|
||||
{
|
||||
InputStream resourceAsStream =
|
||||
(InputStream) this.getClass().getResourceAsStream(
|
||||
"/version.properties"
|
||||
);
|
||||
this.prop = new Properties();
|
||||
|
||||
try
|
||||
{
|
||||
this.prop.load( resourceAsStream );
|
||||
this.version = this.prop.getProperty("version");
|
||||
this.groupId = this.prop.getProperty("groupId");
|
||||
this.artifactId = this.prop.getProperty("artifactId");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.version = "0.0.0";
|
||||
this.groupId = "no group";
|
||||
this.artifactId = "no artifact";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public String getArtifactId() {
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
}
|
||||
13
src/main/java/com/bwssystems/HABridge/api/CallItem.java
Normal file
13
src/main/java/com/bwssystems/HABridge/api/CallItem.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.bwssystems.HABridge.api;
|
||||
|
||||
public class CallItem {
|
||||
private String item;
|
||||
|
||||
public String getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public void setItem(String anitem) {
|
||||
item = anitem;
|
||||
}
|
||||
}
|
||||
18
src/main/java/com/bwssystems/HABridge/api/NameValue.java
Normal file
18
src/main/java/com/bwssystems/HABridge/api/NameValue.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.bwssystems.HABridge.api;
|
||||
|
||||
public class NameValue {
|
||||
private String name;
|
||||
private String value;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.bwssystems.HABridge.api;
|
||||
|
||||
public class SuccessUserResponse {
|
||||
private UserCreateResponse success;
|
||||
|
||||
public UserCreateResponse getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(UserCreateResponse success) {
|
||||
this.success = success;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.bwssystems.HABridge.api;
|
||||
|
||||
public class UserCreateRequest {
|
||||
private String devicetype;
|
||||
private String username;
|
||||
|
||||
public String getDevicetype() {
|
||||
return devicetype;
|
||||
}
|
||||
public void setDevicetype(String devicetype) {
|
||||
this.devicetype = devicetype;
|
||||
}
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.bwssystems.HABridge.api;
|
||||
|
||||
public class UserCreateResponse {
|
||||
private String username;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
|
||||
/**
|
||||
* Created by arm on 4/14/15.
|
||||
*/
|
||||
public class DeviceResponse {
|
||||
private DeviceState state;
|
||||
private String type;
|
||||
private String name;
|
||||
private String modelid;
|
||||
private String manufacturername;
|
||||
private String luminaireuniqueid;
|
||||
private String uniqueid;
|
||||
private String swversion;
|
||||
|
||||
public DeviceState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(DeviceState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getModelid() {
|
||||
return modelid;
|
||||
}
|
||||
|
||||
public void setModelid(String modelid) {
|
||||
this.modelid = modelid;
|
||||
}
|
||||
|
||||
public String getManufacturername() {
|
||||
return manufacturername;
|
||||
}
|
||||
|
||||
public void setManufacturername(String manufacturername) {
|
||||
this.manufacturername = manufacturername;
|
||||
}
|
||||
|
||||
public String getUniqueid() {
|
||||
return uniqueid;
|
||||
}
|
||||
|
||||
public void setUniqueid(String uniqueid) {
|
||||
this.uniqueid = uniqueid;
|
||||
}
|
||||
|
||||
public String getSwversion() {
|
||||
return swversion;
|
||||
}
|
||||
|
||||
public void setSwversion(String swversion) {
|
||||
this.swversion = swversion;
|
||||
}
|
||||
|
||||
public String getLuminaireuniqueid() {
|
||||
return luminaireuniqueid;
|
||||
}
|
||||
|
||||
public void setLuminaireuniqueid(String luminaireuniqueid) {
|
||||
this.luminaireuniqueid = luminaireuniqueid;
|
||||
}
|
||||
|
||||
public static DeviceResponse createResponse(DeviceDescriptor device){
|
||||
DeviceResponse response = new DeviceResponse();
|
||||
response.setState(device.getDeviceState());
|
||||
|
||||
response.setName(device.getName());
|
||||
response.setUniqueid(device.getUniqueid());
|
||||
response.setManufacturername("Philips");
|
||||
response.setType("Dimmable light");
|
||||
response.setModelid("LWB004");
|
||||
response.setSwversion("66012040");
|
||||
response.setLuminaireuniqueid(null);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bwssytems.HABridge.api.hue;
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
// import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -7,7 +8,7 @@ import java.util.List;
|
||||
*/
|
||||
public class DeviceState {
|
||||
private boolean on;
|
||||
private int bri = 255;
|
||||
private int bri;
|
||||
private int hue;
|
||||
private int sat;
|
||||
private String effect;
|
||||
@@ -16,6 +17,7 @@ public class DeviceState {
|
||||
private String colormode;
|
||||
private boolean reachable;
|
||||
private List<Double> xy;
|
||||
// private int transitiontime;
|
||||
|
||||
public boolean isOn() {
|
||||
return on;
|
||||
@@ -96,7 +98,32 @@ public class DeviceState {
|
||||
public void setXy(List<Double> xy) {
|
||||
this.xy = xy;
|
||||
}
|
||||
// public int getTransitiontime() {
|
||||
// return transitiontime;
|
||||
// }
|
||||
|
||||
// public void setTransitiontime(int transitiontime) {
|
||||
// this.transitiontime = transitiontime;
|
||||
// }
|
||||
|
||||
public static DeviceState createDeviceState() {
|
||||
DeviceState newDeviceState = new DeviceState();
|
||||
newDeviceState.fillIn();
|
||||
// newDeviceState.setColormode("none");
|
||||
// ArrayList<Double> doubleArray = new ArrayList<Double>();
|
||||
// doubleArray.add(new Double(0));
|
||||
// doubleArray.add(new Double(0));
|
||||
// newDeviceState.setXy(doubleArray);
|
||||
|
||||
return newDeviceState;
|
||||
}
|
||||
public void fillIn() {
|
||||
if(this.getAlert() == null)
|
||||
this.setAlert("none");
|
||||
if(this.getEffect() == null)
|
||||
this.setEffect("none");
|
||||
this.setReachable(true);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DeviceState{" +
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
public class DeviceTypes {
|
||||
private Boolean bridge;
|
||||
private String[] lights;
|
||||
public Boolean getBridge() {
|
||||
return bridge;
|
||||
}
|
||||
public void setBridge(Boolean bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
public String[] getLights() {
|
||||
return lights;
|
||||
}
|
||||
public void setLights(String[] lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.dao.DeviceRepository;
|
||||
|
||||
public class GroupResponse {
|
||||
private DeviceState action;
|
||||
private String[] lights;
|
||||
private String name;
|
||||
public DeviceState getAction() {
|
||||
return action;
|
||||
}
|
||||
public void setAction(DeviceState action) {
|
||||
this.action = action;
|
||||
}
|
||||
public String[] getLights() {
|
||||
return lights;
|
||||
}
|
||||
public void setLights(String[] lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static GroupResponse createGroupResponse(List<DeviceDescriptor> deviceList) {
|
||||
String[] theList = new String[deviceList.size()];
|
||||
int i = 0;
|
||||
for (DeviceDescriptor device : deviceList) {
|
||||
theList[i] = device.getId();
|
||||
i++;
|
||||
}
|
||||
GroupResponse theResponse = new GroupResponse();
|
||||
theResponse.setAction(DeviceState.createDeviceState());
|
||||
theResponse.setName("Lightset 0");
|
||||
theResponse.setLights(theList);
|
||||
return theResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* Created by arm on 4/14/15.
|
||||
*/
|
||||
public class HueApiResponse {
|
||||
private Map<String, DeviceResponse> lights;
|
||||
private Map<String, JsonObject> scenes;
|
||||
private Map<String, JsonObject> groups;
|
||||
private Map<String, JsonObject> schedules;
|
||||
private Map<String, JsonObject> sensors;
|
||||
private Map<String, JsonObject> rules;
|
||||
private HueConfig config;
|
||||
|
||||
public HueApiResponse(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist) {
|
||||
super();
|
||||
this.setConfig(HueConfig.createConfig(name, ipaddress, awhitelist));
|
||||
this.setRules(new HashMap<>());
|
||||
this.setSensors(new HashMap<>());
|
||||
this.setSchedules(new HashMap<>());
|
||||
this.setGroups(new HashMap<>());
|
||||
this.setScenes(new HashMap<>());
|
||||
}
|
||||
|
||||
public Map<String, DeviceResponse> getLights() {
|
||||
return lights;
|
||||
}
|
||||
|
||||
public void setLights(Map<String, DeviceResponse> lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
|
||||
public Map<String, JsonObject> getScenes() {
|
||||
return scenes;
|
||||
}
|
||||
|
||||
public void setScenes(Map<String, JsonObject> scenes) {
|
||||
this.scenes = scenes;
|
||||
}
|
||||
|
||||
public Map<String, JsonObject> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
|
||||
public void setGroups(Map<String, JsonObject> groups) {
|
||||
this.groups = groups;
|
||||
}
|
||||
|
||||
public Map<String, JsonObject> getSchedules() {
|
||||
return schedules;
|
||||
}
|
||||
|
||||
public void setSchedules(Map<String, JsonObject> schedules) {
|
||||
this.schedules = schedules;
|
||||
}
|
||||
|
||||
public Map<String, JsonObject> getSensors() {
|
||||
return sensors;
|
||||
}
|
||||
|
||||
public void setSensors(Map<String, JsonObject> sensors) {
|
||||
this.sensors = sensors;
|
||||
}
|
||||
|
||||
public Map<String, JsonObject> getRules() {
|
||||
return rules;
|
||||
}
|
||||
|
||||
public void setRules(Map<String, JsonObject> rules) {
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
public HueConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(HueConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
275
src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java
Normal file
275
src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java
Normal file
@@ -0,0 +1,275 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class HueConfig
|
||||
{
|
||||
private Boolean portalservices;
|
||||
private String gateway;
|
||||
private String mac;
|
||||
private String swversion;
|
||||
private String apiversion;
|
||||
private Boolean linkbutton;
|
||||
private String ipaddress;
|
||||
private Integer proxyport;
|
||||
private Swupdate swupdate;
|
||||
private String netmask;
|
||||
private String name;
|
||||
private Boolean dhcp;
|
||||
private String UTC;
|
||||
private String proxyaddress;
|
||||
private String localtime;
|
||||
private String timezone;
|
||||
private String zigbeechannel;
|
||||
private String modelid;
|
||||
private String bridgeid;
|
||||
private Boolean factorynew;
|
||||
private String replacesbridgeid;
|
||||
private Map<String, WhitelistEntry> whitelist;
|
||||
|
||||
public static HueConfig createConfig(String name, String ipaddress, Map<String, WhitelistEntry> awhitelist) {
|
||||
HueConfig aConfig = new HueConfig();
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
aConfig.setMac(HueConfig.getMacAddress(ipaddress));
|
||||
aConfig.setApiversion("1.15.0");
|
||||
aConfig.setPortalservices(false);
|
||||
aConfig.setGateway(ipaddress);
|
||||
aConfig.setSwversion("01035934");
|
||||
aConfig.setLinkbutton(true);
|
||||
aConfig.setIpaddress(ipaddress);
|
||||
aConfig.setProxyport(0);
|
||||
aConfig.setSwupdate(Swupdate.createSwupdate());
|
||||
aConfig.setNetmask("255.255.255.0");
|
||||
aConfig.setName(name);
|
||||
aConfig.setDhcp(true);
|
||||
aConfig.setUtc(dateFormatGmt.format(new Date()));
|
||||
aConfig.setProxyaddress("none");
|
||||
aConfig.setLocaltime(dateFormat.format(new Date()));
|
||||
aConfig.setTimezone(TimeZone.getDefault().getID());
|
||||
aConfig.setZigbeechannel("6");
|
||||
aConfig.setBridgeid(HuePublicConfig.createConfig(name, ipaddress).getHueBridgeIdFromMac());
|
||||
aConfig.setModelid("BSB002");
|
||||
aConfig.setFactorynew(false);
|
||||
aConfig.setReplacesbridgeid(null);
|
||||
aConfig.setWhitelist(awhitelist);
|
||||
|
||||
return aConfig;
|
||||
}
|
||||
|
||||
private static String getMacAddress(String addr)
|
||||
{
|
||||
InetAddress ip;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
|
||||
ip = InetAddress.getByName(addr);
|
||||
|
||||
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
|
||||
|
||||
byte[] mac = network.getHardwareAddress();
|
||||
|
||||
for (int i = 0; i < mac.length; i++) {
|
||||
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? ":" : ""));
|
||||
}
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
} catch (SocketException e){
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
} catch (Exception e){
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
public Boolean getPortalservices() {
|
||||
return portalservices;
|
||||
}
|
||||
|
||||
public void setPortalservices(Boolean portalservices) {
|
||||
this.portalservices = portalservices;
|
||||
}
|
||||
|
||||
public String getGateway() {
|
||||
return gateway;
|
||||
}
|
||||
|
||||
public void setGateway(String gateway) {
|
||||
this.gateway = gateway;
|
||||
}
|
||||
|
||||
public String getMac() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
public void setMac(String mac) {
|
||||
this.mac = mac;
|
||||
}
|
||||
|
||||
public String getSwversion() {
|
||||
return swversion;
|
||||
}
|
||||
|
||||
public void setSwversion(String swversion) {
|
||||
this.swversion = swversion;
|
||||
}
|
||||
|
||||
public Boolean getLinkbutton() {
|
||||
return linkbutton;
|
||||
}
|
||||
|
||||
public void setLinkbutton(Boolean linkbutton) {
|
||||
this.linkbutton = linkbutton;
|
||||
}
|
||||
|
||||
public String getIpaddress() {
|
||||
return ipaddress;
|
||||
}
|
||||
|
||||
public void setIpaddress(String ipaddress) {
|
||||
this.ipaddress = ipaddress;
|
||||
}
|
||||
|
||||
public Integer getProxyport() {
|
||||
return proxyport;
|
||||
}
|
||||
|
||||
public void setProxyport(Integer proxyport) {
|
||||
this.proxyport = proxyport;
|
||||
}
|
||||
|
||||
public Swupdate getSwupdate() {
|
||||
return swupdate;
|
||||
}
|
||||
|
||||
public void setSwupdate(Swupdate swupdate) {
|
||||
this.swupdate = swupdate;
|
||||
}
|
||||
|
||||
public String getNetmask() {
|
||||
return netmask;
|
||||
}
|
||||
|
||||
public void setNetmask(String netmask) {
|
||||
this.netmask = netmask;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Boolean getDhcp() {
|
||||
return dhcp;
|
||||
}
|
||||
|
||||
public void setDhcp(Boolean dhcp) {
|
||||
this.dhcp = dhcp;
|
||||
}
|
||||
|
||||
public String getUtc() {
|
||||
return UTC;
|
||||
}
|
||||
|
||||
public void setUtc(String utc) {
|
||||
this.UTC = utc;
|
||||
}
|
||||
|
||||
public String getProxyaddress() {
|
||||
return proxyaddress;
|
||||
}
|
||||
|
||||
public void setProxyaddress(String proxyaddress) {
|
||||
this.proxyaddress = proxyaddress;
|
||||
}
|
||||
|
||||
public Map<String, WhitelistEntry> getWhitelist() {
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
public void setWhitelist(Map<String, WhitelistEntry> whitelist) {
|
||||
this.whitelist = whitelist;
|
||||
}
|
||||
|
||||
public String getApiversion() {
|
||||
return apiversion;
|
||||
}
|
||||
|
||||
public void setApiversion(String apiversion) {
|
||||
this.apiversion = apiversion;
|
||||
}
|
||||
|
||||
public String getLocaltime() {
|
||||
return localtime;
|
||||
}
|
||||
|
||||
public void setLocaltime(String localtime) {
|
||||
this.localtime = localtime;
|
||||
}
|
||||
|
||||
public String getTimezone() {
|
||||
return timezone;
|
||||
}
|
||||
|
||||
public void setTimezone(String timezone) {
|
||||
this.timezone = timezone;
|
||||
}
|
||||
|
||||
public String getZigbeechannel() {
|
||||
return zigbeechannel;
|
||||
}
|
||||
|
||||
public void setZigbeechannel(String zigbeechannel) {
|
||||
this.zigbeechannel = zigbeechannel;
|
||||
}
|
||||
|
||||
public String getModelid() {
|
||||
return modelid;
|
||||
}
|
||||
|
||||
public void setModelid(String modelid) {
|
||||
this.modelid = modelid;
|
||||
}
|
||||
|
||||
public String getBridgeid() {
|
||||
return bridgeid;
|
||||
}
|
||||
|
||||
public void setBridgeid(String bridgeid) {
|
||||
this.bridgeid = bridgeid;
|
||||
}
|
||||
|
||||
public Boolean getFactorynew() {
|
||||
return factorynew;
|
||||
}
|
||||
|
||||
public void setFactorynew(Boolean factorynew) {
|
||||
this.factorynew = factorynew;
|
||||
}
|
||||
|
||||
public String getReplacesbridgeid() {
|
||||
return replacesbridgeid;
|
||||
}
|
||||
|
||||
public void setReplacesbridgeid(String replacesbridgeid) {
|
||||
this.replacesbridgeid = replacesbridgeid;
|
||||
}
|
||||
}
|
||||
19
src/main/java/com/bwssystems/HABridge/api/hue/HueError.java
Normal file
19
src/main/java/com/bwssystems/HABridge/api/hue/HueError.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
public class HueError {
|
||||
|
||||
private HueErrorDetails error;
|
||||
|
||||
public HueError(HueErrorDetails error) {
|
||||
super();
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public HueErrorDetails getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(HueErrorDetails error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
public class HueErrorDetails {
|
||||
private String type;
|
||||
private String address;
|
||||
private String description;
|
||||
private String method_name;
|
||||
private String resource_name;
|
||||
private String value;
|
||||
public HueErrorDetails(String type, String address, String description, String method_name, String resource_name,
|
||||
String value) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.address = address;
|
||||
this.description = description;
|
||||
this.method_name = method_name;
|
||||
this.resource_name = resource_name;
|
||||
this.value = value;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
public String getMethod_name() {
|
||||
return method_name;
|
||||
}
|
||||
public void setMethod_name(String method_name) {
|
||||
this.method_name = method_name;
|
||||
}
|
||||
public String getResource_name() {
|
||||
return resource_name;
|
||||
}
|
||||
public void setResource_name(String resource_name) {
|
||||
this.resource_name = resource_name;
|
||||
}
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class HueErrorResponse {
|
||||
private ArrayList<HueError> theErrors;
|
||||
|
||||
public HueErrorResponse() {
|
||||
super();
|
||||
theErrors = new ArrayList<HueError>();
|
||||
}
|
||||
|
||||
public void addError(HueError anError) {
|
||||
theErrors.add(anError);
|
||||
}
|
||||
|
||||
public HueError[] getTheErrors() {
|
||||
HueError theList[] = new HueError[theErrors.size()];
|
||||
theList = theErrors.toArray(theList);
|
||||
return theList;
|
||||
}
|
||||
|
||||
public void setTheErrors(ArrayList<HueError> theErrors) {
|
||||
this.theErrors = theErrors;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
|
||||
public class HuePublicConfig
|
||||
{
|
||||
private String name;
|
||||
private String apiversion;
|
||||
private String swversion;
|
||||
private String mac;
|
||||
private String bridgeid;
|
||||
private String replacesbridgeid;
|
||||
private Boolean factorynew;
|
||||
private String modelid;
|
||||
|
||||
public static HuePublicConfig createConfig(String name, String ipaddress) {
|
||||
HuePublicConfig aConfig = new HuePublicConfig();
|
||||
aConfig.setMac(HuePublicConfig.getMacAddress(ipaddress));
|
||||
aConfig.setApiversion("1.15.0");
|
||||
aConfig.setSwversion("01035934");
|
||||
aConfig.setName(name);
|
||||
aConfig.setBridgeid(aConfig.getHueBridgeIdFromMac());
|
||||
aConfig.setModelid("BSB002");
|
||||
aConfig.setFactorynew(false);
|
||||
aConfig.setReplacesbridgeid(null);
|
||||
|
||||
return aConfig;
|
||||
}
|
||||
|
||||
private static String getMacAddress(String addr)
|
||||
{
|
||||
InetAddress ip;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
|
||||
ip = InetAddress.getByName(addr);
|
||||
|
||||
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
|
||||
|
||||
byte[] mac = network.getHardwareAddress();
|
||||
|
||||
for (int i = 0; i < mac.length; i++) {
|
||||
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? ":" : ""));
|
||||
}
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
} catch (SocketException e){
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
} catch (Exception e){
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getSNUUIDFromMac()
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(this.getMac(), ":");
|
||||
String bridgeUUID = "";
|
||||
while(st.hasMoreTokens()) {
|
||||
bridgeUUID = bridgeUUID + st.nextToken();
|
||||
}
|
||||
bridgeUUID = bridgeUUID.toLowerCase();
|
||||
return bridgeUUID.toLowerCase();
|
||||
}
|
||||
|
||||
protected String getHueBridgeIdFromMac()
|
||||
{
|
||||
String cleanMac = this.getSNUUIDFromMac();
|
||||
String bridgeId = cleanMac.substring(0, 6) + "FFFE" + cleanMac.substring(6);
|
||||
return bridgeId.toUpperCase();
|
||||
}
|
||||
|
||||
public String getMac() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
public void setMac(String mac) {
|
||||
this.mac = mac;
|
||||
}
|
||||
|
||||
public String getSwversion() {
|
||||
return swversion;
|
||||
}
|
||||
|
||||
public void setSwversion(String swversion) {
|
||||
this.swversion = swversion;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public String getApiversion() {
|
||||
return apiversion;
|
||||
}
|
||||
|
||||
public void setApiversion(String apiversion) {
|
||||
this.apiversion = apiversion;
|
||||
}
|
||||
|
||||
|
||||
public String getModelid() {
|
||||
return modelid;
|
||||
}
|
||||
|
||||
public void setModelid(String modelid) {
|
||||
this.modelid = modelid;
|
||||
}
|
||||
|
||||
public String getBridgeid() {
|
||||
return bridgeid;
|
||||
}
|
||||
|
||||
public void setBridgeid(String bridgeid) {
|
||||
this.bridgeid = bridgeid;
|
||||
}
|
||||
|
||||
public Boolean getFactorynew() {
|
||||
return factorynew;
|
||||
}
|
||||
|
||||
public void setFactorynew(Boolean factorynew) {
|
||||
this.factorynew = factorynew;
|
||||
}
|
||||
|
||||
public String getReplacesbridgeid() {
|
||||
return replacesbridgeid;
|
||||
}
|
||||
|
||||
public void setReplacesbridgeid(String replacesbridgeid) {
|
||||
this.replacesbridgeid = replacesbridgeid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
// import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by arm on 4/14/15.
|
||||
*/
|
||||
public class StateChangeBody {
|
||||
private boolean on;
|
||||
private int bri;
|
||||
private int hue;
|
||||
private int sat;
|
||||
private String effect;
|
||||
private int ct;
|
||||
private String alert;
|
||||
private List<Double> xy;
|
||||
private int transitiontime;
|
||||
private int bri_inc;
|
||||
private int hue_inc;
|
||||
private int sat_inc;
|
||||
private List<Double> xy_inc;
|
||||
private int ct_inc;
|
||||
|
||||
public boolean isOn() {
|
||||
return on;
|
||||
}
|
||||
|
||||
public void setOn(boolean on) {
|
||||
this.on = on;
|
||||
}
|
||||
|
||||
public int getBri() {
|
||||
return bri;
|
||||
}
|
||||
|
||||
public void setBri(int bri) {
|
||||
this.bri = bri;
|
||||
}
|
||||
|
||||
public int getHue() {
|
||||
return hue;
|
||||
}
|
||||
|
||||
public void setHue(int hue) {
|
||||
this.hue = hue;
|
||||
}
|
||||
|
||||
public int getSat() {
|
||||
return sat;
|
||||
}
|
||||
|
||||
public void setSat(int sat) {
|
||||
this.sat = sat;
|
||||
}
|
||||
|
||||
public String getEffect() {
|
||||
return effect;
|
||||
}
|
||||
|
||||
public void setEffect(String effect) {
|
||||
this.effect = effect;
|
||||
}
|
||||
|
||||
public int getCt() {
|
||||
return ct;
|
||||
}
|
||||
|
||||
public void setCt(int ct) {
|
||||
this.ct = ct;
|
||||
}
|
||||
|
||||
public String getAlert() {
|
||||
return alert;
|
||||
}
|
||||
|
||||
public void setAlert(String alert) {
|
||||
this.alert = alert;
|
||||
}
|
||||
|
||||
public List<Double> getXy() {
|
||||
return xy;
|
||||
}
|
||||
|
||||
public void setXy(List<Double> xy) {
|
||||
this.xy = xy;
|
||||
}
|
||||
|
||||
public int getTransitiontime() {
|
||||
return transitiontime;
|
||||
}
|
||||
|
||||
public void setTransitiontime(int transitiontime) {
|
||||
this.transitiontime = transitiontime;
|
||||
}
|
||||
|
||||
public int getBri_inc() {
|
||||
return bri_inc;
|
||||
}
|
||||
|
||||
public void setBri_inc(int bri_inc) {
|
||||
this.bri_inc = bri_inc;
|
||||
}
|
||||
|
||||
public int getHue_inc() {
|
||||
return hue_inc;
|
||||
}
|
||||
|
||||
public void setHue_inc(int hue_inc) {
|
||||
this.hue_inc = hue_inc;
|
||||
}
|
||||
|
||||
public int getSat_inc() {
|
||||
return sat_inc;
|
||||
}
|
||||
|
||||
public void setSat_inc(int sat_inc) {
|
||||
this.sat_inc = sat_inc;
|
||||
}
|
||||
|
||||
public List<Double> getXy_inc() {
|
||||
return xy_inc;
|
||||
}
|
||||
|
||||
public void setXy_inc(List<Double> xy_inc) {
|
||||
this.xy_inc = xy_inc;
|
||||
}
|
||||
|
||||
public int getCt_inc() {
|
||||
return ct_inc;
|
||||
}
|
||||
|
||||
public void setCt_inc(int ct_inc) {
|
||||
this.ct_inc = ct_inc;
|
||||
}
|
||||
}
|
||||
68
src/main/java/com/bwssystems/HABridge/api/hue/Swupdate.java
Normal file
68
src/main/java/com/bwssystems/HABridge/api/hue/Swupdate.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
|
||||
public class Swupdate
|
||||
{
|
||||
private Integer updatestate;
|
||||
private Boolean checkforupdate;
|
||||
private DeviceTypes devicetypes;
|
||||
private String text;
|
||||
private Boolean notify;
|
||||
private String url;
|
||||
|
||||
public static Swupdate createSwupdate() {
|
||||
Swupdate aSwupdate = new Swupdate();
|
||||
aSwupdate.setUpdatestate(0);
|
||||
aSwupdate.setCheckforupdate(false);
|
||||
aSwupdate.setDevicetypes(new DeviceTypes());
|
||||
aSwupdate.setNotify(false);
|
||||
aSwupdate.setText("");
|
||||
aSwupdate.setUrl("");
|
||||
return aSwupdate;
|
||||
}
|
||||
|
||||
public Boolean getCheckforupdate() {
|
||||
return checkforupdate;
|
||||
}
|
||||
public void setCheckforupdate(Boolean checkforupdate) {
|
||||
this.checkforupdate = checkforupdate;
|
||||
}
|
||||
public DeviceTypes getDevicetypes() {
|
||||
return devicetypes;
|
||||
}
|
||||
public void setDevicetypes(DeviceTypes devicetypes) {
|
||||
this.devicetypes = devicetypes;
|
||||
}
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public Boolean getNotify() {
|
||||
return notify;
|
||||
}
|
||||
|
||||
public void setNotify(Boolean notify) {
|
||||
this.notify = notify;
|
||||
}
|
||||
|
||||
public Integer getUpdatestate() {
|
||||
return updatestate;
|
||||
}
|
||||
|
||||
public void setUpdatestate(Integer updatestate) {
|
||||
this.updatestate = updatestate;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.bwssystems.HABridge.api.hue;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
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");
|
||||
|
||||
public static WhitelistEntry createEntry(String devicetype) {
|
||||
WhitelistEntry anEntry = new WhitelistEntry();
|
||||
anEntry.setName(devicetype);
|
||||
anEntry.setCreateDate(getCurrentDate());
|
||||
anEntry.setLastUseDate(getCurrentDate());
|
||||
return anEntry;
|
||||
}
|
||||
|
||||
public static String getCurrentDate() {
|
||||
return dateFormat.format(new Date());
|
||||
}
|
||||
|
||||
public String getLastUseDate() {
|
||||
return lastUseDate;
|
||||
}
|
||||
|
||||
public void setLastUseDate(String lastUseDate) {
|
||||
this.lastUseDate = lastUseDate;
|
||||
}
|
||||
|
||||
public String getCreateDate() {
|
||||
return createDate;
|
||||
}
|
||||
|
||||
public void setCreateDate(String createDate) {
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.bwssystems.HABridge.dao;
|
||||
|
||||
public class BackupFilename {
|
||||
private String filename;
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public void setFilename(String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
}
|
||||
200
src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java
Normal file
200
src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java
Normal file
@@ -0,0 +1,200 @@
|
||||
package com.bwssystems.HABridge.dao;
|
||||
|
||||
import com.bwssystems.HABridge.api.hue.DeviceState;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/*
|
||||
* Object to handle the device configuration
|
||||
*/
|
||||
public class DeviceDescriptor{
|
||||
@SerializedName("id")
|
||||
@Expose
|
||||
private String id;
|
||||
@SerializedName("uniqueid")
|
||||
@Expose
|
||||
private String uniqueid;
|
||||
@SerializedName("name")
|
||||
@Expose
|
||||
private String name;
|
||||
@SerializedName("mapId")
|
||||
@Expose
|
||||
private String mapId;
|
||||
@SerializedName("mapType")
|
||||
@Expose
|
||||
private String mapType;
|
||||
@SerializedName("deviceType")
|
||||
@Expose
|
||||
private String deviceType;
|
||||
@SerializedName("targetDevice")
|
||||
@Expose
|
||||
private String targetDevice;
|
||||
@SerializedName("offUrl")
|
||||
@Expose
|
||||
private String offUrl;
|
||||
@SerializedName("dimUrl")
|
||||
@Expose
|
||||
private String dimUrl;
|
||||
@SerializedName("onUrl")
|
||||
@Expose
|
||||
private String onUrl;
|
||||
@SerializedName("headers")
|
||||
@Expose
|
||||
private String headers;
|
||||
@SerializedName("httpVerb")
|
||||
@Expose
|
||||
private String httpVerb;
|
||||
@SerializedName("contentType")
|
||||
@Expose
|
||||
private String contentType;
|
||||
@SerializedName("contentBody")
|
||||
@Expose
|
||||
private String contentBody;
|
||||
@SerializedName("contentBodyOff")
|
||||
@Expose
|
||||
private String contentBodyOff;
|
||||
@SerializedName("contentBodyDim")
|
||||
@Expose
|
||||
private String contentBodyDim;
|
||||
|
||||
private DeviceState deviceState;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getMapId() {
|
||||
return mapId;
|
||||
}
|
||||
|
||||
public void setMapId(String mapId) {
|
||||
this.mapId = mapId;
|
||||
}
|
||||
|
||||
public String getMapType() {
|
||||
return mapType;
|
||||
}
|
||||
|
||||
public void setMapType(String mapType) {
|
||||
this.mapType = mapType;
|
||||
}
|
||||
|
||||
public String getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
public void setDeviceType(String deviceType) {
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public String getTargetDevice() {
|
||||
return targetDevice;
|
||||
}
|
||||
|
||||
public void setTargetDevice(String targetDevice) {
|
||||
this.targetDevice = targetDevice;
|
||||
}
|
||||
|
||||
public String getOffUrl() {
|
||||
return offUrl;
|
||||
}
|
||||
|
||||
public void setOffUrl(String offUrl) {
|
||||
this.offUrl = offUrl;
|
||||
}
|
||||
|
||||
public String getDimUrl() {
|
||||
return dimUrl;
|
||||
}
|
||||
|
||||
public void setDimUrl(String dimUrl) {
|
||||
this.dimUrl = dimUrl;
|
||||
}
|
||||
|
||||
public String getOnUrl() {
|
||||
return onUrl;
|
||||
}
|
||||
|
||||
public void setOnUrl(String onUrl) {
|
||||
this.onUrl = onUrl;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUniqueid() {
|
||||
return uniqueid;
|
||||
}
|
||||
|
||||
public void setUniqueid(String uniqueid) {
|
||||
this.uniqueid = uniqueid;
|
||||
}
|
||||
|
||||
public String getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public void setHeaders(String headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
public String getHttpVerb() {
|
||||
return httpVerb;
|
||||
}
|
||||
|
||||
public void setHttpVerb(String httpVerb) {
|
||||
this.httpVerb = httpVerb;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public String getContentBody() {
|
||||
return contentBody;
|
||||
}
|
||||
|
||||
public void setContentBody(String contentBody) {
|
||||
this.contentBody = contentBody;
|
||||
}
|
||||
|
||||
public String getContentBodyOff() {
|
||||
return contentBodyOff;
|
||||
}
|
||||
|
||||
public void setContentBodyOff(String contentBodyOff) {
|
||||
this.contentBodyOff = contentBodyOff;
|
||||
}
|
||||
|
||||
public String getContentBodyDim() {
|
||||
return contentBodyDim;
|
||||
}
|
||||
|
||||
public void setContentBodyDim(String contentBodyDim) {
|
||||
this.contentBodyDim = contentBodyDim;
|
||||
}
|
||||
|
||||
public DeviceState getDeviceState() {
|
||||
if(deviceState == null)
|
||||
deviceState = DeviceState.createDeviceState();
|
||||
return deviceState;
|
||||
}
|
||||
|
||||
public void setDeviceState(DeviceState deviceState) {
|
||||
this.deviceState = deviceState;
|
||||
}
|
||||
|
||||
}
|
||||
194
src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java
Normal file
194
src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java
Normal file
@@ -0,0 +1,194 @@
|
||||
package com.bwssystems.HABridge.dao;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.util.BackupHandler;
|
||||
import com.bwssystems.util.JsonTransformer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import java.util.List;
|
||||
/*
|
||||
* This is an in memory list to manage the configured devices and saves the list as a JSON string to a file for later
|
||||
* loading.
|
||||
*/
|
||||
public class DeviceRepository extends BackupHandler {
|
||||
private Map<String, DeviceDescriptor> devices;
|
||||
private Path repositoryPath;
|
||||
private Gson gson;
|
||||
private Integer nextId;
|
||||
private Logger log = LoggerFactory.getLogger(DeviceRepository.class);
|
||||
|
||||
public DeviceRepository(String deviceDb) {
|
||||
super();
|
||||
gson =
|
||||
new GsonBuilder()
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.create();
|
||||
repositoryPath = null;
|
||||
repositoryPath = Paths.get(deviceDb);
|
||||
setupParams(repositoryPath, ".bk", "device.db-");
|
||||
nextId = 0;
|
||||
_loadRepository(repositoryPath);
|
||||
}
|
||||
|
||||
public void loadRepository() {
|
||||
if(repositoryPath != null)
|
||||
_loadRepository(repositoryPath);
|
||||
}
|
||||
private void _loadRepository(Path aPath){
|
||||
String jsonContent = repositoryReader(aPath);
|
||||
devices = new HashMap<String, DeviceDescriptor>();
|
||||
|
||||
if(jsonContent != null)
|
||||
{
|
||||
DeviceDescriptor list[] = gson.fromJson(jsonContent, DeviceDescriptor[].class);
|
||||
for(int i = 0; i < list.length; i++) {
|
||||
put(list[i].getId(), list[i]);
|
||||
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;
|
||||
}
|
||||
|
||||
public List<DeviceDescriptor> findByDeviceType(String aType) {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
return list;
|
||||
}
|
||||
|
||||
public DeviceDescriptor findOne(String id) {
|
||||
return devices.get(id);
|
||||
}
|
||||
|
||||
private void put(String id, DeviceDescriptor aDescriptor) {
|
||||
devices.put(id, aDescriptor);
|
||||
}
|
||||
|
||||
public void save(DeviceDescriptor[] 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) {
|
||||
BigInteger bigInt = BigInteger.valueOf(Integer.decode(descriptors[i].getId()));
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||
|
||||
descriptors[i].setUniqueid("00:17:88:5E:D3:" + hexValue + "-" + hexValue);
|
||||
}
|
||||
put(descriptors[i].getId(), descriptors[i]);
|
||||
theNames = theNames + " " + descriptors[i].getName() + ", ";
|
||||
}
|
||||
String jsonValue = gson.toJson(findAll());
|
||||
repositoryWriter(jsonValue, repositoryPath);
|
||||
log.debug("Save device(s): " + theNames);
|
||||
}
|
||||
|
||||
public void renumber() {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
Iterator<DeviceDescriptor> deviceIterator = list.iterator();
|
||||
Map<String, DeviceDescriptor> newdevices = new HashMap<String, DeviceDescriptor>();;
|
||||
nextId = 0;
|
||||
log.debug("Renumber devices.");
|
||||
while(deviceIterator.hasNext()) {
|
||||
nextId++;
|
||||
DeviceDescriptor theDevice = deviceIterator.next();
|
||||
theDevice.setId(String.valueOf(nextId));
|
||||
BigInteger bigInt = BigInteger.valueOf(nextId);
|
||||
byte[] theBytes = bigInt.toByteArray();
|
||||
String hexValue = DatatypeConverter.printHexBinary(theBytes);
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void repositoryWriter(String content, Path filePath) {
|
||||
if(Files.exists(filePath) && !Files.isWritable(filePath)){
|
||||
log.error("Error file is not writable: " + filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Files.notExists(filePath.getParent())) {
|
||||
try {
|
||||
Files.createDirectories(filePath.getParent());
|
||||
} catch (IOException e) {
|
||||
log.error("Error creating the directory: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Path target = null;
|
||||
if(Files.exists(filePath)) {
|
||||
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "device.db.old");
|
||||
Files.move(filePath, target);
|
||||
}
|
||||
Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE);
|
||||
if(target != null)
|
||||
Files.delete(target);
|
||||
} catch (IOException e) {
|
||||
log.error("Error writing the file: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String repositoryReader(Path filePath) {
|
||||
|
||||
String content = null;
|
||||
if(Files.notExists(filePath) || !Files.isReadable(filePath)){
|
||||
log.warn("Error reading the file: " + filePath + " - Does not exist or is not readable. continuing...");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
content = new String(Files.readAllBytes(filePath));
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
18
src/main/java/com/bwssystems/HABridge/dao/ErrorMessage.java
Normal file
18
src/main/java/com/bwssystems/HABridge/dao/ErrorMessage.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.bwssystems.HABridge.dao;
|
||||
|
||||
public class ErrorMessage {
|
||||
private String message;
|
||||
|
||||
public ErrorMessage(String message) {
|
||||
super();
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
package com.bwssystems.HABridge.devicemanagmeent;
|
||||
|
||||
import static spark.Spark.get;
|
||||
import static spark.Spark.options;
|
||||
import static spark.Spark.post;
|
||||
import static spark.Spark.put;
|
||||
import static spark.Spark.delete;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.dao.BackupFilename;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.dao.DeviceRepository;
|
||||
import com.bwssystems.HABridge.dao.ErrorMessage;
|
||||
import com.bwssystems.NestBridge.NestHome;
|
||||
import com.bwssystems.hal.HalHome;
|
||||
import com.bwssystems.harmony.HarmonyHome;
|
||||
import com.bwssystems.hue.HueHome;
|
||||
import com.bwssystems.luupRequests.Device;
|
||||
import com.bwssystems.luupRequests.Scene;
|
||||
import com.bwssystems.util.JsonTransformer;
|
||||
import com.bwssystems.vera.VeraHome;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
spark core server for bridge configuration
|
||||
*/
|
||||
public class DeviceResource {
|
||||
private static final String API_CONTEXT = "/api/devices";
|
||||
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
|
||||
private DeviceRepository deviceRepository;
|
||||
private VeraHome veraHome;
|
||||
private HarmonyHome myHarmonyHome;
|
||||
private NestHome nestHome;
|
||||
private HueHome hueHome;
|
||||
private HalHome halHome;
|
||||
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
|
||||
|
||||
public DeviceResource(BridgeSettingsDescriptor theSettings, HarmonyHome theHarmonyHome, NestHome aNestHome, HueHome aHueHome, HalHome aHalHome) {
|
||||
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
|
||||
|
||||
if(theSettings.isValidVera())
|
||||
this.veraHome = new VeraHome(theSettings);
|
||||
else
|
||||
this.veraHome = null;
|
||||
|
||||
if(theSettings.isValidHarmony())
|
||||
this.myHarmonyHome = theHarmonyHome;
|
||||
else
|
||||
this.myHarmonyHome = null;
|
||||
|
||||
if(theSettings.isValidNest())
|
||||
this.nestHome = aNestHome;
|
||||
else
|
||||
this.nestHome = null;
|
||||
|
||||
if(theSettings.isValidHue())
|
||||
this.hueHome = aHueHome;
|
||||
else
|
||||
this.hueHome = null;
|
||||
|
||||
if(theSettings.isValidHal())
|
||||
this.halHome = aHalHome;
|
||||
else
|
||||
this.halHome = null;
|
||||
|
||||
setupEndpoints();
|
||||
}
|
||||
|
||||
public DeviceRepository getDeviceRepository() {
|
||||
return deviceRepository;
|
||||
}
|
||||
|
||||
private void setupEndpoints() {
|
||||
log.info("HABridge device management service started.... ");
|
||||
// http://ip_address:port/api/devices CORS request
|
||||
options(API_CONTEXT, "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post(API_CONTEXT, "application/json", (request, response) -> {
|
||||
log.debug("Create a Device(s) - request body: " + request.body());
|
||||
DeviceDescriptor devices[];
|
||||
if(request.body().substring(0,1).equalsIgnoreCase("[") == true) {
|
||||
devices = new Gson().fromJson(request.body(), DeviceDescriptor[].class);
|
||||
}
|
||||
else {
|
||||
devices = new Gson().fromJson("[" + request.body() + "]", DeviceDescriptor[].class);
|
||||
}
|
||||
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);
|
||||
log.debug("Bad http verb in create a Device(s): " + request.body());
|
||||
return new ErrorMessage("Bad http verb in create a Device(s): " + request.body() + " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviceRepository.save(devices);
|
||||
log.debug("Created a Device(s): " + request.body());
|
||||
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.status(HttpStatus.SC_CREATED);
|
||||
|
||||
return devices;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/devices/:id CORS request
|
||||
options(API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
put (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
||||
log.debug("Edit a Device - request body: " + request.body());
|
||||
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"));
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
return new ErrorMessage("Could not save an edited device, Device Id not found: " + request.params(":id") + " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.debug("Saving an edited Device: " + device.getName());
|
||||
|
||||
if (device.getDeviceType() != null)
|
||||
device.setDeviceType(device.getDeviceType());
|
||||
|
||||
DeviceDescriptor[] theDevices = new DeviceDescriptor[1];
|
||||
theDevices[0] = device;
|
||||
deviceRepository.save(theDevices);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
}
|
||||
return device;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT, "application/json", (request, response) -> {
|
||||
List<DeviceDescriptor> deviceList = deviceRepository.findAll();
|
||||
log.debug("Get all devices");
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
String theStream = aRenderer.render(deviceList);
|
||||
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");
|
||||
DeviceDescriptor descriptor = deviceRepository.findOne(request.params(":id"));
|
||||
if(descriptor == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("Could not find, id: " + request.params(":id") + " ");
|
||||
}
|
||||
else
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return descriptor;
|
||||
}, new JsonTransformer());
|
||||
|
||||
delete (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
||||
String anId = request.params(":id");
|
||||
log.debug("Delete a device: " + anId);
|
||||
DeviceDescriptor deleted = deviceRepository.findOne(anId);
|
||||
if(deleted == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("Could not delete, id: " + anId + " not found. ");
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceRepository.delete(deleted);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
}
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/vera/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get vera devices");
|
||||
if(veraHome == null){
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Vera is not available.");
|
||||
}
|
||||
List<Device> theDevices = veraHome.getDevices();
|
||||
if(theDevices == null) {
|
||||
response.status(HttpStatus.SC_SERVICE_UNAVAILABLE);
|
||||
return new ErrorMessage("A Vera request failed to get devices. Check your Vera IP addresses.");
|
||||
}
|
||||
else
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return theDevices;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/vera/scenes", "application/json", (request, response) -> {
|
||||
log.debug("Get vera scenes");
|
||||
if(veraHome == null){
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Vera is not available.");
|
||||
}
|
||||
List<Scene> theScenes = veraHome.getScenes();
|
||||
if(theScenes == null) {
|
||||
response.status(HttpStatus.SC_SERVICE_UNAVAILABLE);
|
||||
return new ErrorMessage("A Vera is not available and failed to get scenes. Check your Vera IP addresses.");
|
||||
}
|
||||
else
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return theScenes;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/harmony/activities", "application/json", (request, response) -> {
|
||||
log.debug("Get harmony activities");
|
||||
if(myHarmonyHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Harmony is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return myHarmonyHome.getActivities();
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/harmony/show", "application/json", (request, response) -> {
|
||||
log.debug("Get harmony current activity");
|
||||
if(myHarmonyHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Harmony is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return myHarmonyHome.getCurrentActivities();
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/harmony/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get harmony devices");
|
||||
if(myHarmonyHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Harmony is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return myHarmonyHome.getDevices();
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/nest/items", "application/json", (request, response) -> {
|
||||
log.debug("Get nest items");
|
||||
if(nestHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Nest is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return nestHome.getItems();
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/hue/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get hue items");
|
||||
if(hueHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Hue is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return hueHome.getDevices();
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/hal/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get hal items");
|
||||
if(halHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Hal is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return halHome.getDevices();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/devices/exec/renumber CORS request
|
||||
options(API_CONTEXT + "/exec/renumber", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (API_CONTEXT + "/exec/renumber", "application/json", (request, response) -> {
|
||||
log.debug("Renumber devices.");
|
||||
deviceRepository.renumber();
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/backup/available", "application/json", (request, response) -> {
|
||||
log.debug("Get backup filenames");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return deviceRepository.getBackups();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/devices/backup/create CORS request
|
||||
options(API_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
put (API_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
log.debug("Create backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
BackupFilename returnFilename = new BackupFilename();
|
||||
returnFilename.setFilename(deviceRepository.backup(aFilename.getFilename()));
|
||||
return returnFilename;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/devices/backup/delete CORS request
|
||||
options(API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
log.debug("Delete backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null)
|
||||
deviceRepository.deleteBackup(aFilename.getFilename());
|
||||
else
|
||||
log.warn("No filename given for delete backup.");
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/devices/backup/restore CORS request
|
||||
options(API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
log.debug("Restore backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null) {
|
||||
deviceRepository.restoreBackup(aFilename.getFilename());
|
||||
deviceRepository.loadRepository();
|
||||
}
|
||||
else
|
||||
log.warn("No filename given for restore backup.");
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
}
|
||||
}
|
||||
1322
src/main/java/com/bwssystems/HABridge/hue/HueMulator.java
Normal file
1322
src/main/java/com/bwssystems/HABridge/hue/HueMulator.java
Normal file
File diff suppressed because it is too large
Load Diff
229
src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java
Normal file
229
src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java
Normal file
@@ -0,0 +1,229 @@
|
||||
package com.bwssystems.HABridge.upnp;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeControlDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.Configuration;
|
||||
import com.bwssystems.HABridge.api.hue.HuePublicConfig;
|
||||
import com.bwssystems.util.UDPDatagramSender;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import org.apache.http.conn.util.*;
|
||||
|
||||
|
||||
public class UpnpListener {
|
||||
private Logger log = LoggerFactory.getLogger(UpnpListener.class);
|
||||
private UDPDatagramSender theUDPDatagramSender;
|
||||
private int httpServerPort;
|
||||
private String responseAddress;
|
||||
private boolean strict;
|
||||
private boolean traceupnp;
|
||||
private BridgeControlDescriptor bridgeControl;
|
||||
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/1.15.0\r\n" +
|
||||
"hue-bridgeid: %s\r\n" +
|
||||
"ST: upnp:rootdevice\r\n" +
|
||||
"USN: uuid:2f402f80-da50-11e1-9b23-%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/1.15.0\r\n" +
|
||||
"hue-bridgeid: %s\r\n" +
|
||||
"ST: uuid:2f402f80-da50-11e1-9b23-%s\r\n" +
|
||||
"USN: uuid:2f402f80-da50-11e1-9b23-%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/1.15.0\r\n" +
|
||||
"hue-bridgeid: %s\r\n" +
|
||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
|
||||
"USN: uuid:2f402f80-da50-11e1-9b23-%s\r\n\r\n";
|
||||
|
||||
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl, UDPDatagramSender aUdpDatagramSender) {
|
||||
super();
|
||||
theUDPDatagramSender = aUdpDatagramSender;
|
||||
httpServerPort = Integer.valueOf(theSettings.getServerPort());
|
||||
responseAddress = theSettings.getUpnpConfigAddress();
|
||||
strict = theSettings.isUpnpStrict();
|
||||
traceupnp = theSettings.isTraceupnp();
|
||||
bridgeControl = theControl;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public boolean startListening(){
|
||||
log.info("UPNP Discovery Listener starting....");
|
||||
MulticastSocket upnpMulticastSocket = null;
|
||||
Enumeration<NetworkInterface> ifs = null;
|
||||
|
||||
try {
|
||||
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());
|
||||
return false;
|
||||
}
|
||||
|
||||
InetSocketAddress socketAddress = new InetSocketAddress(Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT);
|
||||
try {
|
||||
ifs = NetworkInterface.getNetworkInterfaces();
|
||||
} catch (SocketException e) {
|
||||
log.error("Could not get network interfaces for this machine: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
while (ifs.hasMoreElements()) {
|
||||
NetworkInterface xface = ifs.nextElement();
|
||||
Enumeration<InetAddress> addrs = xface.getInetAddresses();
|
||||
String name = xface.getName();
|
||||
int IPsPerNic = 0;
|
||||
|
||||
while (addrs.hasMoreElements()) {
|
||||
InetAddress addr = addrs.nextElement();
|
||||
if (traceupnp)
|
||||
log.info("Traceupnp: " + name + " ... has addr " + addr);
|
||||
else
|
||||
log.debug(name + " ... has addr " + addr);
|
||||
if (InetAddressUtils.isIPv4Address(addr.getHostAddress())) {
|
||||
IPsPerNic++;
|
||||
}
|
||||
}
|
||||
log.debug("Checking " + name + " to our interface set");
|
||||
if (IPsPerNic > 0) {
|
||||
try {
|
||||
upnpMulticastSocket.joinGroup(socketAddress, xface);
|
||||
if (traceupnp)
|
||||
log.info("Traceupnp: Adding " + name + " to our interface set");
|
||||
else
|
||||
log.debug("Adding " + name + " to our interface set");
|
||||
} catch (IOException e) {
|
||||
log.warn("Multicast join failed for: " + socketAddress.getHostName() + " to interface: "
|
||||
+ xface.getName() + " with message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("UPNP Discovery Listener running and ready....");
|
||||
boolean loopControl = true;
|
||||
boolean error = false;
|
||||
while (loopControl) { // trigger shutdown here
|
||||
byte[] buf = new byte[1024];
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||
try {
|
||||
upnpMulticastSocket.receive(packet);
|
||||
if (isSSDPDiscovery(packet)) {
|
||||
try {
|
||||
sendUpnpResponse(packet.getAddress(), packet.getPort());
|
||||
} catch (IOException e) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("UpnpListener encountered an error reading socket. Shutting down", e);
|
||||
error = true;
|
||||
}
|
||||
if (error || bridgeControl.isReinit() || bridgeControl.isStop()) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// noop
|
||||
}
|
||||
loopControl = false;
|
||||
}
|
||||
}
|
||||
upnpMulticastSocket.close();
|
||||
if (bridgeControl.isReinit())
|
||||
log.info("UPNP Discovery Listener - ended, restart found");
|
||||
if (bridgeControl.isStop())
|
||||
log.info("UPNP Discovery Listener - ended, stop found");
|
||||
if (!bridgeControl.isStop() && !bridgeControl.isReinit()) {
|
||||
log.info("UPNP Discovery Listener - ended, error found");
|
||||
return false;
|
||||
}
|
||||
return bridgeControl.isReinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* ssdp discovery packet detection
|
||||
*/
|
||||
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\"")){
|
||||
log.debug("isSSDPDiscovery Found message to be an M-SEARCH message.");
|
||||
log.debug("isSSDPDiscovery Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
|
||||
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: isSSDPDiscovery found message to be an M-SEARCH message.");
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
||||
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
}
|
||||
else
|
||||
log.debug("isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
||||
return true;
|
||||
}
|
||||
else if (!strict)
|
||||
{
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to be an M-SEARCH message.");
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
||||
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
}
|
||||
else
|
||||
log.debug("isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
||||
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;
|
||||
}
|
||||
|
||||
protected void sendUpnpResponse(InetAddress requester, int sourcePort) throws IOException {
|
||||
String discoveryResponse = null;
|
||||
String bridgeId = null;
|
||||
String bridgeSNUUID = null;
|
||||
HuePublicConfig aHueConfig = HuePublicConfig.createConfig("temp", responseAddress);
|
||||
bridgeId = aHueConfig.getBridgeid();
|
||||
bridgeSNUUID = aHueConfig.getSNUUIDFromMac();
|
||||
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID);
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: sendUpnpResponse discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
|
||||
}
|
||||
else
|
||||
log.debug("sendUpnpResponse discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
|
||||
theUDPDatagramSender.sendUDPResponse(discoveryResponse, requester, sourcePort);
|
||||
|
||||
discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: sendUpnpResponse discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
|
||||
}
|
||||
else
|
||||
log.debug("sendUpnpResponse discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
|
||||
theUDPDatagramSender.sendUDPResponse(discoveryResponse, requester, sourcePort);
|
||||
|
||||
discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID);
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: sendUpnpResponse discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
|
||||
}
|
||||
else
|
||||
log.debug("sendUpnpResponse discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
|
||||
theUDPDatagramSender.sendUDPResponse(discoveryResponse, requester, sourcePort);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.bwssystems.HABridge.upnp;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.api.hue.HuePublicConfig;
|
||||
|
||||
import static spark.Spark.get;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class UpnpSettingsResource {
|
||||
private Logger log = LoggerFactory.getLogger(UpnpSettingsResource.class);
|
||||
|
||||
private BridgeSettingsDescriptor theSettings;
|
||||
|
||||
private String hueTemplate = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
|
||||
+ "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
|
||||
+ "<specVersion>\n"
|
||||
+ "<major>1</major>\n"
|
||||
+ "<minor>0</minor>\n"
|
||||
+ "</specVersion>\n"
|
||||
+ "<URLBase>http://%s:%s/</URLBase>\n"
|
||||
+ "<device>\n"
|
||||
+ "<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\n"
|
||||
+ "<friendlyName>Philips hue (%s)</friendlyName>\n"
|
||||
+ "<manufacturer>Royal Philips Electronics</manufacturer>\n"
|
||||
+ "<manufacturerURL>http://www.philips.com</manufacturerURL>\n"
|
||||
+ "<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>\n"
|
||||
+ "<modelName>Philips hue bridge 2015</modelName>\n"
|
||||
+ "<modelNumber>BSB002</modelNumber>\n"
|
||||
+ "<modelURL>http://www.meethue.com</modelURL>\n"
|
||||
+ "<serialNumber>%s</serialNumber>\n"
|
||||
+ "<UDN>uuid:2f402f80-da50-11e1-9b23-%s</UDN>\n"
|
||||
+ "<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"
|
||||
+ "<presentationURL>index.html</presentationURL>\n"
|
||||
+ "<iconList>\n"
|
||||
+ "<icon>\n"
|
||||
+ "<mimetype>image/png</mimetype>\n"
|
||||
+ "<height>48</height>\n"
|
||||
+ "<width>48</width>\n"
|
||||
+ "<depth>24</depth>\n"
|
||||
+ "<url>hue_logo_0.png</url>\n"
|
||||
+ "</icon>\n"
|
||||
+ "<icon>\n"
|
||||
+ "<mimetype>image/png</mimetype>\n"
|
||||
+ "<height>120</height>\n"
|
||||
+ "<width>120</width>\n"
|
||||
+ "<depth>24</depth>\n"
|
||||
+ "<url>hue_logo_3.png</url>\n"
|
||||
+ "</icon>\n"
|
||||
+ "</iconList>\n"
|
||||
+ "</device>\n"
|
||||
+ "</root>\n";
|
||||
|
||||
public UpnpSettingsResource(BridgeSettingsDescriptor theBridgeSettings) {
|
||||
super();
|
||||
this.theSettings = theBridgeSettings;
|
||||
}
|
||||
|
||||
public void setupServer() {
|
||||
log.info("Hue description service started....");
|
||||
// http://ip_adress:port/description.xml which returns the xml configuration for the hue emulator
|
||||
get("/description.xml", "application/xml; charset=utf-8", (request, response) -> {
|
||||
if(theSettings.isTraceupnp())
|
||||
log.info("Traceupnp: upnp device settings requested: " + " from " + request.ip() + ":" + request.port());
|
||||
else
|
||||
log.debug("upnp device settings requested: " + " from " + request.ip() + ":" + request.port());
|
||||
String portNumber = Integer.toString(request.port());
|
||||
String filledTemplate = null;
|
||||
String bridgeIdMac = HuePublicConfig.createConfig("temp", theSettings.getUpnpConfigAddress()).getSNUUIDFromMac();
|
||||
filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress(), bridgeIdMac, bridgeIdMac);
|
||||
if(theSettings.isTraceupnp())
|
||||
log.info("Traceupnp: upnp device settings template filled with address: " + theSettings.getUpnpConfigAddress() + " and port: " + portNumber);
|
||||
else
|
||||
log.debug("Traceupnp: upnp device settings template filled with address: " + theSettings.getUpnpConfigAddress() + " and port: " + portNumber);
|
||||
// response.header("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
|
||||
// response.header("Pragma", "no-cache");
|
||||
// response.header("Expires", "Mon, 1 Aug 2011 09:00:00 GMT");
|
||||
// response.header("Connection", "close"); // Not sure if the server will actually close the connections by just setting the header
|
||||
// response.header("Access-Control-Max-Age", "0");
|
||||
// response.header("Access-Control-Allow-Origin", "*");
|
||||
// response.header("Access-Control-Allow-Credentials", "true");
|
||||
// response.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
|
||||
// response.header("Access-Control-Allow-Headers", "Content-Type");
|
||||
// response.header("Content-Type", "application/xml; charset=utf-8");
|
||||
response.type("application/xml; charset=utf-8");
|
||||
response.status(200);
|
||||
|
||||
return filledTemplate;
|
||||
} );
|
||||
// http://ip_adress:port/favicon.ico
|
||||
get("/favicon.ico", "application/xml; charset=utf-8", (request, response) -> {
|
||||
return "";
|
||||
} );
|
||||
// http://ip_adress:port/hue_logo_0.png
|
||||
get("/hue_logo_0.png", "application/xml; charset=utf-8", (request, response) -> {
|
||||
return "";
|
||||
} );
|
||||
// http://ip_adress:port/hue_logo_3.png
|
||||
get("/hue_logo_3.png", "application/xml; charset=utf-8", (request, response) -> {
|
||||
return "";
|
||||
} );
|
||||
}
|
||||
}
|
||||
110
src/main/java/com/bwssystems/NestBridge/NestHome.java
Normal file
110
src/main/java/com/bwssystems/NestBridge/NestHome.java
Normal file
@@ -0,0 +1,110 @@
|
||||
package com.bwssystems.NestBridge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.nest.controller.Home;
|
||||
import com.bwssystems.nest.controller.Nest;
|
||||
import com.bwssystems.nest.controller.NestSession;
|
||||
import com.bwssystems.nest.controller.Thermostat;
|
||||
import com.bwssystems.nest.protocol.error.LoginException;
|
||||
import com.bwssystems.nest.protocol.status.WhereDetail;
|
||||
import com.bwssystems.nest.protocol.status.WhereItem;
|
||||
|
||||
public class NestHome {
|
||||
private static final Logger log = LoggerFactory.getLogger(NestHome.class);
|
||||
private NestSession theSession;
|
||||
private Nest theNest;
|
||||
private ArrayList<NestItem> nestItems;
|
||||
|
||||
public NestHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
theSession = null;
|
||||
theNest = null;
|
||||
nestItems = null;
|
||||
|
||||
if(!bridgeSettings.isValidNest()) {
|
||||
log.debug("not a valid nest");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
theSession = new NestSession(bridgeSettings.getNestuser(), bridgeSettings.getNestpwd());
|
||||
theNest = new Nest(theSession);
|
||||
} catch (LoginException e) {
|
||||
log.error("Caught Login Exception, exiting....");
|
||||
theSession = null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<NestItem> getItems() {
|
||||
if(theNest == null)
|
||||
return null;
|
||||
|
||||
if(nestItems == null) {
|
||||
nestItems = new ArrayList<NestItem>();
|
||||
Set<String> homeNames = theNest.getHomeNames();
|
||||
Home aHome = null;
|
||||
NestItem anItem = null;
|
||||
for(String name : homeNames) {
|
||||
aHome = theNest.getHome(name);
|
||||
anItem = new NestItem();
|
||||
anItem.setId(name);
|
||||
anItem.setName(aHome.getDetail().getName());
|
||||
anItem.setType("Home");
|
||||
anItem.setLocation(aHome.getDetail().getLocation());
|
||||
nestItems.add(anItem);
|
||||
}
|
||||
Thermostat thermo = null;
|
||||
Set<String> thermoNames = theNest.getThermostatNames();
|
||||
for(String name : thermoNames) {
|
||||
thermo = theNest.getThermostat(name);
|
||||
anItem = new NestItem();
|
||||
anItem.setId(name);
|
||||
anItem.setType("Thermostat");
|
||||
String where = null;
|
||||
String homeName= null;
|
||||
Boolean found = false;
|
||||
for(String aHomeName : homeNames) {
|
||||
WhereDetail aDetail = theNest.getWhere(aHomeName);
|
||||
ListIterator<WhereItem> anIterator = aDetail.getWheres().listIterator();
|
||||
while(anIterator.hasNext()) {
|
||||
WhereItem aWhereItem = (WhereItem) anIterator.next();
|
||||
if(aWhereItem.getWhereId().equals(thermo.getDeviceDetail().getWhereId())) {
|
||||
where = aWhereItem.getName();
|
||||
homeName = theNest.getHome(aHomeName).getDetail().getName();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found)
|
||||
break;
|
||||
}
|
||||
anItem.setName(where + "(" + name.substring(name.length() - 4) + ")");
|
||||
anItem.setLocation(where + " - " + homeName);
|
||||
nestItems.add(anItem);
|
||||
}
|
||||
}
|
||||
|
||||
return nestItems;
|
||||
}
|
||||
|
||||
public Nest getTheNest() {
|
||||
return theNest;
|
||||
}
|
||||
|
||||
public void closeTheNest() {
|
||||
if(theSession != null) {
|
||||
theNest.endNestSession();
|
||||
theNest = null;
|
||||
theSession = null;
|
||||
nestItems = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
src/main/java/com/bwssystems/NestBridge/NestInstruction.java
Normal file
33
src/main/java/com/bwssystems/NestBridge/NestInstruction.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.bwssystems.NestBridge;
|
||||
|
||||
public class NestInstruction {
|
||||
private String name;
|
||||
private Boolean away;
|
||||
private String control;
|
||||
private String temp;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public Boolean getAway() {
|
||||
return away;
|
||||
}
|
||||
public void setAway(Boolean away) {
|
||||
this.away = away;
|
||||
}
|
||||
public String getControl() {
|
||||
return control;
|
||||
}
|
||||
public void setControl(String control) {
|
||||
this.control = control;
|
||||
}
|
||||
public String getTemp() {
|
||||
return temp;
|
||||
}
|
||||
public void setTemp(String temp) {
|
||||
this.temp = temp;
|
||||
}
|
||||
}
|
||||
41
src/main/java/com/bwssystems/NestBridge/NestItem.java
Normal file
41
src/main/java/com/bwssystems/NestBridge/NestItem.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package com.bwssystems.NestBridge;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public class NestItem {
|
||||
private String name;
|
||||
private String id;
|
||||
private String type;
|
||||
private String location;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
byte ptext[];
|
||||
String theLabel = new String(name);
|
||||
try {
|
||||
ptext = theLabel.getBytes("ISO-8859-1");
|
||||
this.name = new String(ptext, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
this.name = theLabel;
|
||||
}
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String anid) {
|
||||
id = anid;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
}
|
||||
17
src/main/java/com/bwssystems/hal/DeviceElements.java
Normal file
17
src/main/java/com/bwssystems/hal/DeviceElements.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.bwssystems.hal;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class DeviceElements {
|
||||
@SerializedName(value="DeviceElements", alternate={"SceneElements", "GroupElements", "HVACElements", "MacroElements", "IrElements", "IrButtons"})
|
||||
private List<DeviceName> DeviceElements;
|
||||
|
||||
public List<DeviceName> getDeviceElements() {
|
||||
return DeviceElements;
|
||||
}
|
||||
|
||||
public void setDeviceElements(List<DeviceName> deviceElements) {
|
||||
DeviceElements = deviceElements;
|
||||
}
|
||||
}
|
||||
17
src/main/java/com/bwssystems/hal/DeviceName.java
Normal file
17
src/main/java/com/bwssystems/hal/DeviceName.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.bwssystems.hal;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class DeviceName {
|
||||
@SerializedName(value="DeviceName", alternate={"SceneName", "GroupName", "HVACName", "MacroName", "IrName", "IrButton"})
|
||||
private String DeviceName;
|
||||
|
||||
public String getDeviceName() {
|
||||
return DeviceName;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
DeviceName = deviceName;
|
||||
}
|
||||
|
||||
}
|
||||
39
src/main/java/com/bwssystems/hal/HalDevice.java
Normal file
39
src/main/java/com/bwssystems/hal/HalDevice.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.bwssystems.hal;
|
||||
|
||||
public class HalDevice {
|
||||
private String haldevicetype;
|
||||
private String haldevicename;
|
||||
private String haladdress;
|
||||
private String halname;
|
||||
private DeviceElements buttons;
|
||||
public String getHaldevicetype() {
|
||||
return haldevicetype;
|
||||
}
|
||||
public void setHaldevicetype(String haldevicetype) {
|
||||
this.haldevicetype = haldevicetype;
|
||||
}
|
||||
public String getHaldevicename() {
|
||||
return haldevicename;
|
||||
}
|
||||
public void setHaldevicename(String haldevicename) {
|
||||
this.haldevicename = haldevicename;
|
||||
}
|
||||
public String getHaladdress() {
|
||||
return haladdress;
|
||||
}
|
||||
public void setHaladdress(String haladdress) {
|
||||
this.haladdress = haladdress;
|
||||
}
|
||||
public String getHalname() {
|
||||
return halname;
|
||||
}
|
||||
public void setHalname(String halname) {
|
||||
this.halname = halname;
|
||||
}
|
||||
public DeviceElements getButtons() {
|
||||
return buttons;
|
||||
}
|
||||
public void setButtons(DeviceElements buttons) {
|
||||
this.buttons = buttons;
|
||||
}
|
||||
}
|
||||
113
src/main/java/com/bwssystems/hal/HalHome.java
Normal file
113
src/main/java/com/bwssystems/hal/HalHome.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package com.bwssystems.hal;
|
||||
|
||||
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.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
|
||||
public class HalHome {
|
||||
private static final Logger log = LoggerFactory.getLogger(HalHome.class);
|
||||
private Map<String, HalInfo> hals;
|
||||
|
||||
public HalHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
hals = new HashMap<String, HalInfo>();
|
||||
if(!bridgeSettings.isValidHal())
|
||||
return;
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHaladdress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHal = theList.next();
|
||||
try {
|
||||
hals.put(aHal.getName(), new HalInfo(aHal, bridgeSettings.getHaltoken()));
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot get harmony client (" + aHal.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<HalDevice> getDevices() {
|
||||
log.debug("consolidating devices for hues");
|
||||
List<HalDevice> theResponse = null;
|
||||
Iterator<String> keys = hals.keySet().iterator();
|
||||
List<HalDevice> deviceList = new ArrayList<HalDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
theResponse = hals.get(key).getLights();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else {
|
||||
log.warn("Cannot get lights for Hal with name: " + key + ", skipping this HAL.");
|
||||
continue;
|
||||
}
|
||||
theResponse = hals.get(key).getAppliances();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get appliances for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getTheatre();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get theatre for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getCustom();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get custom for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getHVAC();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get HVAC for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getHome(key);
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get Homes for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getGroups();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get Groups for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getMacros();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get Macros for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getScenes();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get Scenes for Hal with name: " + key);
|
||||
theResponse = hals.get(key).getButtons();
|
||||
if(theResponse != null)
|
||||
addHalDevices(deviceList, theResponse, key);
|
||||
else
|
||||
log.warn("Cannot get Buttons for Hal with name: " + key);
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
private Boolean addHalDevices(List<HalDevice> theDeviceList, List<HalDevice> theSourceList, String theKey) {
|
||||
Iterator<HalDevice> devices = theSourceList.iterator();
|
||||
while(devices.hasNext()) {
|
||||
HalDevice theDevice = devices.next();
|
||||
HalDevice aNewHalDevice = new HalDevice();
|
||||
aNewHalDevice.setHaldevicetype(theDevice.getHaldevicetype());
|
||||
aNewHalDevice.setHaldevicename(theDevice.getHaldevicename());
|
||||
aNewHalDevice.setButtons(theDevice.getButtons());
|
||||
aNewHalDevice.setHaladdress(hals.get(theKey).getHalAddress().getIp());
|
||||
aNewHalDevice.setHalname(theKey);
|
||||
theDeviceList.add(aNewHalDevice);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
206
src/main/java/com/bwssystems/hal/HalInfo.java
Normal file
206
src/main/java/com/bwssystems/hal/HalInfo.java
Normal file
@@ -0,0 +1,206 @@
|
||||
package com.bwssystems.hal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.util.TextStringFormatter;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class HalInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(HalInfo.class);
|
||||
private static final String DEVICE_REQUEST = "/DeviceData!DeviceCmd=GetNames!DeviceType=";
|
||||
private static final String HVAC_REQUEST = "/HVACData!HVACCmd=GetNames";
|
||||
private static final String GROUP_REQUEST = "/GroupData!GroupCmd=GetNames";
|
||||
private static final String MACRO_REQUEST = "/MacroData!MacroCmd=GetNames";
|
||||
private static final String SCENE_REQUEST = "/SceneData!SceneCmd=GetNames";
|
||||
private static final String IRDATA_REQUEST = "/IrData!IRCmd=GetNames";
|
||||
private static final String IRBUTTON_REQUEST = "/IrData!IRCmd=GetButtons!IrDevice=";
|
||||
private static final String TOKEN_REQUEST = "?Token=";
|
||||
private static final String LIGHT_REQUEST = "Light";
|
||||
private static final String APPL_REQUEST = "Appl";
|
||||
// private static final String VIDEO_REQUEST = "Video";
|
||||
private static final String THEATRE_REQUEST = "Theatre";
|
||||
private static final String CUSTOM_REQUEST = "Custom";
|
||||
private static final String HVAC_TYPE = "HVAC";
|
||||
private static final String HOME_TYPE = "Home";
|
||||
private static final String GROUP_TYPE = "Group";
|
||||
private static final String MACRO_TYPE = "Macro";
|
||||
private static final String SCENE_TYPE = "Scene";
|
||||
private static final String IRDATA_TYPE = "IrData";
|
||||
private HttpClient httpClient;
|
||||
private NamedIP halAddress;
|
||||
private String theToken;
|
||||
|
||||
public HalInfo(NamedIP addressName, String aGivenToken) {
|
||||
super();
|
||||
httpClient = HttpClients.createDefault();
|
||||
halAddress = addressName;
|
||||
theToken = aGivenToken;
|
||||
}
|
||||
|
||||
public List<HalDevice> getLights() {
|
||||
return getHalDevices(DEVICE_REQUEST + LIGHT_REQUEST + TOKEN_REQUEST, LIGHT_REQUEST);
|
||||
}
|
||||
|
||||
public List<HalDevice> getAppliances() {
|
||||
return getHalDevices(DEVICE_REQUEST + APPL_REQUEST + TOKEN_REQUEST, APPL_REQUEST);
|
||||
}
|
||||
|
||||
public List<HalDevice> getTheatre() {
|
||||
return getHalDevices(DEVICE_REQUEST + THEATRE_REQUEST + TOKEN_REQUEST, THEATRE_REQUEST);
|
||||
}
|
||||
|
||||
public List<HalDevice> getCustom() {
|
||||
return getHalDevices(DEVICE_REQUEST + CUSTOM_REQUEST + TOKEN_REQUEST, CUSTOM_REQUEST);
|
||||
}
|
||||
|
||||
public List<HalDevice> getHVAC() {
|
||||
return getHalDevices(HVAC_REQUEST + TOKEN_REQUEST, HVAC_TYPE);
|
||||
}
|
||||
|
||||
public List<HalDevice> getGroups() {
|
||||
return getHalDevices(GROUP_REQUEST + TOKEN_REQUEST, GROUP_TYPE);
|
||||
}
|
||||
|
||||
public List<HalDevice> getMacros() {
|
||||
return getHalDevices(MACRO_REQUEST + TOKEN_REQUEST, MACRO_TYPE);
|
||||
}
|
||||
|
||||
public List<HalDevice> getScenes() {
|
||||
return getHalDevices(SCENE_REQUEST + TOKEN_REQUEST, SCENE_TYPE);
|
||||
}
|
||||
|
||||
public List<HalDevice> getButtons() {
|
||||
List<HalDevice> irDataDevices = getHalDevices(IRDATA_REQUEST + TOKEN_REQUEST, IRDATA_TYPE);
|
||||
|
||||
return getDeviceButtons(irDataDevices);
|
||||
}
|
||||
|
||||
public List<HalDevice> getHome(String theDeviceName) {
|
||||
List<HalDevice> deviceList = null;
|
||||
deviceList = new ArrayList<HalDevice>();
|
||||
HalDevice aNewHalDevice = new HalDevice();
|
||||
aNewHalDevice.setHaldevicetype(HOME_TYPE);
|
||||
aNewHalDevice.setHaldevicename(theDeviceName);
|
||||
deviceList.add(aNewHalDevice);
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
private List<HalDevice> getHalDevices(String apiType, String deviceType) {
|
||||
DeviceElements theHalApiResponse = null;
|
||||
List<HalDevice> deviceList = null;
|
||||
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
theUrl = "http://" + halAddress.getIp() + apiType + theToken;
|
||||
theData = doHttpGETRequest(theUrl);
|
||||
if(theData != null) {
|
||||
log.debug("GET " + deviceType + " HalApiResponse - data: " + theData);
|
||||
theHalApiResponse = new Gson().fromJson(theData, DeviceElements.class);
|
||||
if(theHalApiResponse.getDeviceElements() == null) {
|
||||
StatusDescription theStatus = new Gson().fromJson(theData, StatusDescription.class);
|
||||
if(theStatus.getStatus() == null) {
|
||||
log.warn("Cannot get an devices for type " + deviceType + " for hal " + halAddress.getName() + " as response is not parsable.");
|
||||
}
|
||||
else {
|
||||
log.warn("Cannot get an devices for type " + deviceType + " for hal " + halAddress.getName() + ". Status: " + theStatus.getStatus() + ", with description: " + theStatus.getDescription());
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
deviceList = new ArrayList<HalDevice>();
|
||||
|
||||
Iterator<DeviceName> theDeviceNames = theHalApiResponse.getDeviceElements().iterator();
|
||||
while(theDeviceNames.hasNext()) {
|
||||
DeviceName theDevice = theDeviceNames.next();
|
||||
HalDevice aNewHalDevice = new HalDevice();
|
||||
aNewHalDevice.setHaldevicetype(deviceType);
|
||||
aNewHalDevice.setHaldevicename(theDevice.getDeviceName());
|
||||
deviceList.add(aNewHalDevice);
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn("Get Hal device types " + deviceType + " for " + halAddress.getName() + " - returned null, no data.");
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
private List<HalDevice> getDeviceButtons(List<HalDevice> theIrDevices) {
|
||||
DeviceElements theHalApiResponse = null;
|
||||
List<HalDevice> deviceList = null;
|
||||
|
||||
String theUrl = null;
|
||||
String theData;
|
||||
if(theIrDevices == null)
|
||||
return null;
|
||||
Iterator<HalDevice> theHalDevices = theIrDevices.iterator();
|
||||
deviceList = new ArrayList<HalDevice>();
|
||||
while (theHalDevices.hasNext()) {
|
||||
HalDevice theHalDevice = theHalDevices.next();
|
||||
theUrl = "http://" + halAddress.getIp() + IRBUTTON_REQUEST + TextStringFormatter.forQuerySpaceUrl(theHalDevice.getHaldevicename()) + TOKEN_REQUEST + theToken;
|
||||
theData = doHttpGETRequest(theUrl);
|
||||
if (theData != null) {
|
||||
log.debug("GET IrData for IR Device " + theHalDevice.getHaldevicename() + " HalApiResponse - data: " + theData);
|
||||
theHalApiResponse = new Gson().fromJson(theData, DeviceElements.class);
|
||||
if (theHalApiResponse.getDeviceElements() == null) {
|
||||
StatusDescription theStatus = new Gson().fromJson(theData, StatusDescription.class);
|
||||
if (theStatus.getStatus() == null) {
|
||||
log.warn("Cannot get buttons for IR Device " + theHalDevice.getHaldevicename() + " for hal "
|
||||
+ halAddress.getName() + " as response is not parsable.");
|
||||
} else {
|
||||
log.warn("Cannot get buttons for IR Device " + theHalDevice.getHaldevicename() + " for hal "
|
||||
+ halAddress.getName() + ". Status: " + theStatus.getStatus() + ", with description: "
|
||||
+ theStatus.getDescription());
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
theHalDevice.setButtons(theHalApiResponse);
|
||||
deviceList.add(theHalDevice);
|
||||
|
||||
} else {
|
||||
log.warn("Get Hal buttons for IR Device " + theHalDevice.getHaldevicename() + " for "
|
||||
+ halAddress.getName() + " - returned null, no data.");
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
// This function executes the url against the hal
|
||||
protected String doHttpGETRequest(String url) {
|
||||
String theContent = null;
|
||||
log.debug("calling GET on URL: " + url);
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
try {
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
log.debug("GET on URL responded: " + response.getStatusLine().getStatusCode());
|
||||
if(response.getStatusLine().getStatusCode() == 200){
|
||||
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); //read content for data
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("doHttpGETRequest: Error calling out to HA gateway: " + e.getMessage());
|
||||
}
|
||||
return theContent;
|
||||
}
|
||||
|
||||
public NamedIP getHalAddress() {
|
||||
return halAddress;
|
||||
}
|
||||
|
||||
public void setHalAddress(NamedIP halAddress) {
|
||||
this.halAddress = halAddress;
|
||||
}
|
||||
|
||||
}
|
||||
18
src/main/java/com/bwssystems/hal/StatusDescription.java
Normal file
18
src/main/java/com/bwssystems/hal/StatusDescription.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.bwssystems.hal;
|
||||
|
||||
public class StatusDescription {
|
||||
private String Status;
|
||||
private String Description;
|
||||
public String getStatus() {
|
||||
return Status;
|
||||
}
|
||||
public void setStatus(String status) {
|
||||
Status = status;
|
||||
}
|
||||
public String getDescription() {
|
||||
return Description;
|
||||
}
|
||||
public void setDescription(String description) {
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
25
src/main/java/com/bwssystems/harmony/ButtonPress.java
Normal file
25
src/main/java/com/bwssystems/harmony/ButtonPress.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
public class ButtonPress {
|
||||
private String device;
|
||||
private String button;
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
public void setDevice(String device) {
|
||||
this.device = device;
|
||||
}
|
||||
public String getButton() {
|
||||
return button;
|
||||
}
|
||||
public void setButton(String button) {
|
||||
this.button = button;
|
||||
}
|
||||
public Boolean isValid() {
|
||||
if (device != null && !device.isEmpty()){
|
||||
if (button != null && !button.isEmpty())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
69
src/main/java/com/bwssystems/harmony/DevModeResponse.java
Normal file
69
src/main/java/com/bwssystems/harmony/DevModeResponse.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.whistlingfish.harmony.config.Activity;
|
||||
import net.whistlingfish.harmony.config.Device;
|
||||
import net.whistlingfish.harmony.config.HarmonyConfig;
|
||||
|
||||
public class DevModeResponse {
|
||||
final Logger log = LoggerFactory.getLogger(DevModeResponse.class);
|
||||
|
||||
private final static String powerOff = "PowerOff";
|
||||
private HarmonyConfig harmonyConfig;
|
||||
private Activity currentActivity;
|
||||
|
||||
public DevModeResponse() {
|
||||
super();
|
||||
harmonyConfig = HarmonyConfig.parse(dataReader("/config.data"));
|
||||
this.currentActivity = harmonyConfig.getActivityByName(powerOff);
|
||||
}
|
||||
|
||||
public Activity getCurrentActivity() {
|
||||
return currentActivity;
|
||||
}
|
||||
|
||||
public void setCurrentActivity(Activity currentActivity) {
|
||||
this.currentActivity = currentActivity;
|
||||
}
|
||||
|
||||
public List<Activity> getActivities() {
|
||||
return harmonyConfig.getActivities();
|
||||
}
|
||||
|
||||
public List<Device> getDevices() {
|
||||
return harmonyConfig.getDevices();
|
||||
}
|
||||
|
||||
public HarmonyConfig getConfig() {
|
||||
return harmonyConfig;
|
||||
}
|
||||
|
||||
private String dataReader(String filePath) {
|
||||
|
||||
String content = null;
|
||||
try {
|
||||
InputStream input = getClass().getResourceAsStream(filePath);
|
||||
OutputStream out = new ByteArrayOutputStream();
|
||||
int read;
|
||||
byte[] bytes = new byte[1024];
|
||||
|
||||
while ((read = input.read(bytes)) != -1) {
|
||||
out.write(bytes, 0, read);
|
||||
}
|
||||
content = out.toString();
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
}
|
||||
31
src/main/java/com/bwssystems/harmony/HarmonyActivity.java
Normal file
31
src/main/java/com/bwssystems/harmony/HarmonyActivity.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import net.whistlingfish.harmony.config.Activity;
|
||||
|
||||
public class HarmonyActivity {
|
||||
private String hub;
|
||||
private Activity activity;
|
||||
public String getHub() {
|
||||
return hub;
|
||||
}
|
||||
public void setHub(String hub) {
|
||||
this.hub = hub;
|
||||
}
|
||||
public Activity getActivity() {
|
||||
return activity;
|
||||
}
|
||||
public void setActivity(Activity activity) {
|
||||
byte ptext[];
|
||||
String theLabel = activity.getLabel();
|
||||
try {
|
||||
ptext = theLabel.getBytes("ISO-8859-1");
|
||||
activity.setLabel(new String(ptext, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
activity.setLabel(theLabel);
|
||||
}
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
}
|
||||
30
src/main/java/com/bwssystems/harmony/HarmonyDevice.java
Normal file
30
src/main/java/com/bwssystems/harmony/HarmonyDevice.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import net.whistlingfish.harmony.config.Device;
|
||||
|
||||
public class HarmonyDevice {
|
||||
private Device device;
|
||||
private String hub;
|
||||
public Device getDevice() {
|
||||
return device;
|
||||
}
|
||||
public void setDevice(Device device) {
|
||||
byte ptext[];
|
||||
String theLabel = device.getLabel();
|
||||
try {
|
||||
ptext = theLabel.getBytes("ISO-8859-1");
|
||||
device.setLabel(new String(ptext, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
device.setLabel(theLabel);
|
||||
}
|
||||
this.device = device;
|
||||
}
|
||||
public String getHub() {
|
||||
return hub;
|
||||
}
|
||||
public void setHub(String hub) {
|
||||
this.hub = hub;
|
||||
}
|
||||
}
|
||||
132
src/main/java/com/bwssystems/harmony/HarmonyHandler.java
Normal file
132
src/main/java/com/bwssystems/harmony/HarmonyHandler.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.whistlingfish.harmony.HarmonyClient;
|
||||
import net.whistlingfish.harmony.config.Activity;
|
||||
import net.whistlingfish.harmony.config.Device;
|
||||
import net.whistlingfish.harmony.config.HarmonyConfig;
|
||||
|
||||
public class HarmonyHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(HarmonyHandler.class);
|
||||
private HarmonyClient harmonyClient;
|
||||
private Boolean noopCalls;
|
||||
private Boolean devMode;
|
||||
private DevModeResponse devResponse;
|
||||
|
||||
public HarmonyHandler(HarmonyClient theClient, Boolean noopCallsSetting, DevModeResponse devResponseSetting) {
|
||||
super();
|
||||
noopCalls = noopCallsSetting;
|
||||
devMode = Boolean.TRUE;
|
||||
devResponse = null;
|
||||
if(devResponseSetting == null)
|
||||
devMode = Boolean.FALSE;
|
||||
else
|
||||
devResponse = devResponseSetting;
|
||||
harmonyClient = theClient;
|
||||
}
|
||||
|
||||
public List<Activity> getActivities() {
|
||||
log.debug("Harmony api activities list requested.");
|
||||
if(devMode)
|
||||
return devResponse.getActivities();
|
||||
|
||||
return harmonyClient.getConfig().getActivities();
|
||||
}
|
||||
|
||||
public List<Device> getDevices() {
|
||||
log.debug("Harmony api device list requested.");
|
||||
if(devMode)
|
||||
return devResponse.getDevices();
|
||||
|
||||
return harmonyClient.getConfig().getDevices();
|
||||
}
|
||||
|
||||
public HarmonyConfig getConfig() {
|
||||
log.debug("Harmony api config requested.");
|
||||
if(devMode)
|
||||
return devResponse.getConfig();
|
||||
|
||||
return harmonyClient.getConfig();
|
||||
}
|
||||
|
||||
public Activity getCurrentActivity() {
|
||||
log.debug("Harmony api current sctivity requested.");
|
||||
if(devMode)
|
||||
return devResponse.getCurrentActivity();
|
||||
|
||||
return harmonyClient.getCurrentActivity();
|
||||
}
|
||||
|
||||
public Boolean startActivity(RunActivity anActivity) {
|
||||
log.debug("Harmony api start activity requested for: " + anActivity.getName() + " noop mode: " + noopCalls);
|
||||
if (anActivity.isValid()) {
|
||||
try {
|
||||
if (noopCalls || devMode) {
|
||||
if(devMode)
|
||||
{
|
||||
if(anActivity != null)
|
||||
devResponse.setCurrentActivity(devResponse.getConfig().getActivityByName(anActivity.getName()));
|
||||
}
|
||||
|
||||
log.info("noop mode: Harmony api start activity requested for: " + anActivity.getName());
|
||||
}
|
||||
else
|
||||
harmonyClient.startActivity(Integer.parseInt(anActivity.getName()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
if (!noopCalls)
|
||||
harmonyClient.startActivityByName(anActivity.getName());
|
||||
} catch (IllegalArgumentException ei) {
|
||||
log.error("Error in finding activity: " + anActivity.getName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("Error in finding activity: " + anActivity.getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Boolean pressButton(ButtonPress aDeviceButton) {
|
||||
log.debug("Harmony api press a button requested for device: " + aDeviceButton.getDevice() + " and a for button: " + aDeviceButton.getButton() + " noop mode: " + noopCalls);
|
||||
if (aDeviceButton.isValid()) {
|
||||
try {
|
||||
if (noopCalls || devMode) {
|
||||
log.info("noop mode: Harmony api press a button requested for device: " + aDeviceButton.getDevice() + " and a for button: " + aDeviceButton.getButton());
|
||||
}
|
||||
else
|
||||
harmonyClient.pressButton(Integer.parseInt(aDeviceButton.getDevice()), aDeviceButton.getButton());
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
if (!noopCalls)
|
||||
harmonyClient.pressButton(aDeviceButton.getDevice(), aDeviceButton.getButton());
|
||||
} catch (IllegalArgumentException ei) {
|
||||
log.error("Error in finding device: " + aDeviceButton.getDevice() +" and a button: " + aDeviceButton.getButton());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("Error in finding device: " + aDeviceButton.getDevice() +" and a button: " + aDeviceButton.getButton());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
log.debug("Harmony api shutdown requested.");
|
||||
if(devMode)
|
||||
return;
|
||||
|
||||
harmonyClient.disconnect();
|
||||
harmonyClient = null;
|
||||
}
|
||||
|
||||
}
|
||||
127
src/main/java/com/bwssystems/harmony/HarmonyHome.java
Normal file
127
src/main/java/com/bwssystems/harmony/HarmonyHome.java
Normal file
@@ -0,0 +1,127 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.IpList;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
|
||||
import net.whistlingfish.harmony.config.Activity;
|
||||
import net.whistlingfish.harmony.config.Device;
|
||||
|
||||
public class HarmonyHome {
|
||||
private static final Logger log = LoggerFactory.getLogger(HarmonyHome.class);
|
||||
private Map<String, HarmonyServer> hubs;
|
||||
private Boolean isDevMode;
|
||||
|
||||
public HarmonyHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
|
||||
hubs = new HashMap<String, HarmonyServer>();
|
||||
if(!bridgeSettings.isValidHarmony() && !isDevMode)
|
||||
return;
|
||||
if(isDevMode) {
|
||||
NamedIP devModeIp = new NamedIP();
|
||||
devModeIp.setIp("10.10.10.10");
|
||||
devModeIp.setName("devMode");
|
||||
List<NamedIP> theList = new ArrayList<NamedIP>();
|
||||
theList.add(devModeIp);
|
||||
IpList thedevList = new IpList();
|
||||
thedevList.setDevices(theList);
|
||||
bridgeSettings.setHarmonyAddress(thedevList);
|
||||
}
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHarmonyAddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHub = theList.next();
|
||||
try {
|
||||
hubs.put(aHub.getName(), HarmonyServer.setup(bridgeSettings, isDevMode, aHub));
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot get harmony client (" + aHub.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdownHarmonyHubs() {
|
||||
if(isDevMode)
|
||||
return;
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
hubs.get(key).getMyHarmony().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public HarmonyHandler getHarmonyHandler(String aName) {
|
||||
HarmonyHandler aHandler = null;
|
||||
if(aName == null || aName.equals("")) {
|
||||
aName = "default";
|
||||
}
|
||||
|
||||
if(hubs.get(aName) == null) {
|
||||
Set<String> keys = hubs.keySet();
|
||||
if(!keys.isEmpty()) {
|
||||
aHandler = hubs.get(keys.toArray()[0]).getMyHarmony();
|
||||
}
|
||||
else
|
||||
aHandler = null;
|
||||
}
|
||||
else
|
||||
aHandler = hubs.get(aName).getMyHarmony();
|
||||
return aHandler;
|
||||
}
|
||||
|
||||
public List<HarmonyActivity> getActivities() {
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
ArrayList<HarmonyActivity> activityList = new ArrayList<HarmonyActivity>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Iterator<Activity> activities = hubs.get(key).getMyHarmony().getActivities().iterator();
|
||||
while(activities.hasNext()) {
|
||||
HarmonyActivity anActivity = new HarmonyActivity();
|
||||
anActivity.setActivity(activities.next());
|
||||
anActivity.setHub(key);
|
||||
activityList.add(anActivity);
|
||||
}
|
||||
}
|
||||
return activityList;
|
||||
}
|
||||
public List<HarmonyActivity> getCurrentActivities() {
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
ArrayList<HarmonyActivity> activityList = new ArrayList<HarmonyActivity>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Iterator<Activity> activities = hubs.get(key).getMyHarmony().getActivities().iterator();
|
||||
while(activities.hasNext()) {
|
||||
HarmonyActivity anActivity = new HarmonyActivity();
|
||||
anActivity.setActivity(activities.next());
|
||||
anActivity.setHub(key);
|
||||
activityList.add(anActivity);
|
||||
}
|
||||
}
|
||||
return activityList;
|
||||
}
|
||||
public List<HarmonyDevice> getDevices() {
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
ArrayList<HarmonyDevice> deviceList = new ArrayList<HarmonyDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Iterator<Device> devices = hubs.get(key).getMyHarmony().getDevices().iterator();
|
||||
while(devices.hasNext()) {
|
||||
HarmonyDevice aDevice = new HarmonyDevice();
|
||||
aDevice.setDevice(devices.next());
|
||||
aDevice.setHub(key);
|
||||
deviceList.add(aDevice);
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
}
|
||||
86
src/main/java/com/bwssystems/harmony/HarmonyServer.java
Normal file
86
src/main/java/com/bwssystems/harmony/HarmonyServer.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import net.whistlingfish.harmony.ActivityChangeListener;
|
||||
import net.whistlingfish.harmony.HarmonyClient;
|
||||
import net.whistlingfish.harmony.HarmonyClientModule;
|
||||
import net.whistlingfish.harmony.config.Activity;
|
||||
import net.whistlingfish.harmony.protocol.OAReplyProvider;
|
||||
|
||||
public class HarmonyServer {
|
||||
@Inject
|
||||
private HarmonyClient harmonyClient;
|
||||
|
||||
private HarmonyHandler myHarmony;
|
||||
private DevModeResponse devResponse;
|
||||
private OAReplyProvider dummyProvider;
|
||||
private NamedIP myNameAndIP;
|
||||
private Boolean isDevMode;
|
||||
private Logger log = LoggerFactory.getLogger(HarmonyServer.class);
|
||||
|
||||
public HarmonyServer(NamedIP theHarmonyAddress) {
|
||||
super();
|
||||
myHarmony = null;
|
||||
dummyProvider = null;
|
||||
myNameAndIP = theHarmonyAddress;
|
||||
isDevMode = false;
|
||||
}
|
||||
|
||||
public static HarmonyServer setup(BridgeSettingsDescriptor bridgeSettings, Boolean harmonyDevMode, NamedIP theHarmonyAddress) throws Exception {
|
||||
if(!bridgeSettings.isValidHarmony() && harmonyDevMode) {
|
||||
return new HarmonyServer(theHarmonyAddress);
|
||||
}
|
||||
Injector injector = null;
|
||||
if(!harmonyDevMode)
|
||||
injector = Guice.createInjector(new HarmonyClientModule());
|
||||
HarmonyServer mainObject = new HarmonyServer(theHarmonyAddress);
|
||||
if(!harmonyDevMode)
|
||||
injector.injectMembers(mainObject);
|
||||
mainObject.execute(bridgeSettings, harmonyDevMode);
|
||||
return mainObject;
|
||||
}
|
||||
|
||||
private void execute(BridgeSettingsDescriptor mySettings, Boolean harmonyDevMode) throws Exception {
|
||||
Boolean noopCalls = Boolean.parseBoolean(System.getProperty("noop.calls", "false"));
|
||||
isDevMode = harmonyDevMode;
|
||||
String modeString = "";
|
||||
if(dummyProvider != null)
|
||||
log.debug("something is very wrong as dummyProvider is not null...");
|
||||
if(isDevMode)
|
||||
modeString = " (development mode)";
|
||||
else if(noopCalls)
|
||||
modeString = " (no op calls to harmony)";
|
||||
log.info("setup initiated " + modeString + "....");
|
||||
if(isDevMode)
|
||||
{
|
||||
harmonyClient = null;
|
||||
devResponse = new DevModeResponse();
|
||||
}
|
||||
else {
|
||||
devResponse = null;
|
||||
harmonyClient.addListener(new ActivityChangeListener() {
|
||||
@Override
|
||||
public void activityStarted(Activity activity) {
|
||||
log.info(format("activity changed: [%d] %s", activity.getId(), activity.getLabel()));
|
||||
}
|
||||
});
|
||||
harmonyClient.connect(myNameAndIP.getIp(), mySettings.getHarmonyUser(), mySettings.getHarmonyPwd());
|
||||
}
|
||||
myHarmony = new HarmonyHandler(harmonyClient, noopCalls, devResponse);
|
||||
}
|
||||
|
||||
public HarmonyHandler getMyHarmony() {
|
||||
return myHarmony;
|
||||
}
|
||||
}
|
||||
18
src/main/java/com/bwssystems/harmony/RunActivity.java
Normal file
18
src/main/java/com/bwssystems/harmony/RunActivity.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
public class RunActivity {
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public Boolean isValid() {
|
||||
if (name != null && !name.isEmpty())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
35
src/main/java/com/bwssystems/hue/HueDevice.java
Normal file
35
src/main/java/com/bwssystems/hue/HueDevice.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.bwssystems.hue;
|
||||
|
||||
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
||||
|
||||
|
||||
public class HueDevice {
|
||||
private DeviceResponse device;
|
||||
private String huedeviceid;
|
||||
private String hueaddress;
|
||||
private String huename;
|
||||
public DeviceResponse getDevice() {
|
||||
return device;
|
||||
}
|
||||
public void setDevice(DeviceResponse adevice) {
|
||||
this.device = adevice;
|
||||
}
|
||||
public String getHuedeviceid() {
|
||||
return huedeviceid;
|
||||
}
|
||||
public void setHuedeviceid(String huedeviceid) {
|
||||
this.huedeviceid = huedeviceid;
|
||||
}
|
||||
public String getHueaddress() {
|
||||
return hueaddress;
|
||||
}
|
||||
public void setHueaddress(String ahueaddress) {
|
||||
this.hueaddress = ahueaddress;
|
||||
}
|
||||
public String getHuename() {
|
||||
return huename;
|
||||
}
|
||||
public void setHuename(String ahuename) {
|
||||
this.huename = ahuename;
|
||||
}
|
||||
}
|
||||
18
src/main/java/com/bwssystems/hue/HueDeviceIdentifier.java
Normal file
18
src/main/java/com/bwssystems/hue/HueDeviceIdentifier.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.bwssystems.hue;
|
||||
|
||||
public class HueDeviceIdentifier {
|
||||
private String ipAddress;
|
||||
private String deviceId;
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
}
|
||||
5
src/main/java/com/bwssystems/hue/HueErrorStringSet.java
Normal file
5
src/main/java/com/bwssystems/hue/HueErrorStringSet.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package com.bwssystems.hue;
|
||||
|
||||
public interface HueErrorStringSet {
|
||||
public void setErrorString(String anError);
|
||||
}
|
||||
73
src/main/java/com/bwssystems/hue/HueHome.java
Normal file
73
src/main/java/com/bwssystems/hue/HueHome.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package com.bwssystems.hue;
|
||||
|
||||
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.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
||||
import com.bwssystems.HABridge.api.hue.HueApiResponse;
|
||||
|
||||
public class HueHome {
|
||||
private static final Logger log = LoggerFactory.getLogger(HueHome.class);
|
||||
private Map<String, HueInfo> hues;
|
||||
private String theHUERegisteredUser;
|
||||
|
||||
public HueHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
hues = new HashMap<String, HueInfo>();
|
||||
if(!bridgeSettings.isValidHue())
|
||||
return;
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHueaddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHue = theList.next();
|
||||
hues.put(aHue.getName(), new HueInfo(aHue, this));
|
||||
}
|
||||
theHUERegisteredUser = null;
|
||||
}
|
||||
|
||||
public List<HueDevice> getDevices() {
|
||||
log.debug("consolidating devices for hues");
|
||||
Iterator<String> keys = hues.keySet().iterator();
|
||||
ArrayList<HueDevice> deviceList = new ArrayList<HueDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
HueApiResponse theResponse = hues.get(key).getHueApiResponse();
|
||||
if(theResponse != null) {
|
||||
Map<String, DeviceResponse> theDevices = theResponse.getLights();
|
||||
if(theDevices != null) {
|
||||
Iterator<String> deviceKeys = theDevices.keySet().iterator();
|
||||
while(deviceKeys.hasNext()) {
|
||||
String theDeviceKey = deviceKeys.next();
|
||||
HueDevice aNewHueDevice = new HueDevice();
|
||||
aNewHueDevice.setDevice(theDevices.get(theDeviceKey));
|
||||
aNewHueDevice.setHuedeviceid(theDeviceKey);
|
||||
aNewHueDevice.setHueaddress(hues.get(key).getHueAddress().getIp());
|
||||
aNewHueDevice.setHuename(key);
|
||||
deviceList.add(aNewHueDevice);
|
||||
}
|
||||
}
|
||||
else {
|
||||
deviceList = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
log.warn("Cannot get lights for Hue with name: " + key);
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
public String getTheHUERegisteredUser() {
|
||||
return theHUERegisteredUser;
|
||||
}
|
||||
|
||||
public void setTheHUERegisteredUser(String theHUERegisteredUser) {
|
||||
this.theHUERegisteredUser = theHUERegisteredUser;
|
||||
}
|
||||
}
|
||||
111
src/main/java/com/bwssystems/hue/HueInfo.java
Normal file
111
src/main/java/com/bwssystems/hue/HueInfo.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package com.bwssystems.hue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.HABridge.api.hue.HueApiResponse;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
|
||||
public class HueInfo implements HueErrorStringSet {
|
||||
private static final Logger log = LoggerFactory.getLogger(HueInfo.class);
|
||||
private HttpClient httpClient;
|
||||
private NamedIP hueAddress;
|
||||
private String theUser;
|
||||
private HueHome theHueHome;
|
||||
private String errorString = null;
|
||||
|
||||
public HueInfo(NamedIP addressName, HueHome aHueHome) {
|
||||
super();
|
||||
httpClient = HttpClients.createDefault();
|
||||
hueAddress = addressName;
|
||||
theUser = "habridge";
|
||||
theHueHome = aHueHome;
|
||||
}
|
||||
|
||||
public HueApiResponse getHueApiResponse() {
|
||||
HueApiResponse theHueApiResponse = null;
|
||||
|
||||
String theUrl = "http://" + hueAddress.getIp() + HueUtil.HUE_REQUEST + "/" + theUser;
|
||||
String theData;
|
||||
boolean loopControl = true;
|
||||
int retryCount = 0;
|
||||
while(loopControl) {
|
||||
if(retryCount > 3) {
|
||||
log.warn("Max Retry reached to get Hue data from " + hueAddress.getName());
|
||||
loopControl = false;
|
||||
break;
|
||||
}
|
||||
theUrl = "http://" + hueAddress.getIp() + HueUtil.HUE_REQUEST + "/" + theUser;
|
||||
theData = doHttpGETRequest(theUrl);
|
||||
if(theData != null) {
|
||||
log.debug("GET HueApiResponse - data: " + theData);
|
||||
if(theData.contains("[{\"error\":")) {
|
||||
if(theData.contains("unauthorized user")) {
|
||||
theUser = HueUtil.registerWithHue(httpClient, hueAddress.getIp(), hueAddress.getName(), theHueHome.getTheHUERegisteredUser(), this);
|
||||
if(theUser == null) {
|
||||
log.warn("Register to Hue for " + hueAddress.getName() + " returned error: " + errorString);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
theHueHome.setTheHUERegisteredUser(theUser);
|
||||
retryCount++;
|
||||
}
|
||||
else {
|
||||
log.warn("GET HueApiResponse for " + hueAddress.getName() + " - returned error: " + theData);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
theHueApiResponse = new Gson().fromJson(theData, HueApiResponse.class);
|
||||
log.debug("GET HueApiResponse for " + hueAddress.getName() + " - Gson parse - name: " + theHueApiResponse.getConfig().getName() + ", mac addr: " + theHueApiResponse.getConfig().getMac());
|
||||
loopControl = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn("GET HueApiResponse for " + hueAddress.getName() + " - returned null, no data.");
|
||||
loopControl = false;
|
||||
}
|
||||
}
|
||||
return theHueApiResponse;
|
||||
}
|
||||
|
||||
// This function executes the url against the vera
|
||||
protected String doHttpGETRequest(String url) {
|
||||
String theContent = null;
|
||||
log.debug("calling GET on URL: " + url);
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
try {
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
log.debug("GET on URL responded: " + response.getStatusLine().getStatusCode());
|
||||
if(response.getStatusLine().getStatusCode() == 200){
|
||||
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); //read content for data
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("doHttpGETRequest: Error calling out to HA gateway: " + e.getMessage());
|
||||
}
|
||||
return theContent;
|
||||
}
|
||||
|
||||
public NamedIP getHueAddress() {
|
||||
return hueAddress;
|
||||
}
|
||||
|
||||
public void setHueAddress(NamedIP hueAddress) {
|
||||
this.hueAddress = hueAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorString(String anError) {
|
||||
errorString = anError;
|
||||
}
|
||||
}
|
||||
55
src/main/java/com/bwssystems/hue/HueUtil.java
Normal file
55
src/main/java/com/bwssystems/hue/HueUtil.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package com.bwssystems.hue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.api.SuccessUserResponse;
|
||||
import com.bwssystems.HABridge.api.UserCreateRequest;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class HueUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(HueUtil.class);
|
||||
public static final String HUE_REQUEST = "/api";
|
||||
|
||||
public static final String registerWithHue(HttpClient anHttpClient, String ipAddress, String aName, String theUser, HueErrorStringSet errorStringSet) {
|
||||
UserCreateRequest theLogin = new UserCreateRequest();
|
||||
theLogin.setDevicetype("HABridge#MyMachine");
|
||||
HttpPost postRequest = new HttpPost("http://" + ipAddress + HUE_REQUEST);
|
||||
ContentType parsedContentType = ContentType.parse("application/json");
|
||||
StringEntity requestBody = new StringEntity(new Gson().toJson(theLogin), parsedContentType);
|
||||
HttpResponse response = null;
|
||||
postRequest.setEntity(requestBody);
|
||||
try {
|
||||
response = anHttpClient.execute(postRequest);
|
||||
log.debug("POST execute on URL responded: " + response.getStatusLine().getStatusCode());
|
||||
if(response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300){
|
||||
String theBody = EntityUtils.toString(response.getEntity());
|
||||
log.debug("registerWithHue response data: " + theBody);
|
||||
if(theBody.contains("[{\"error\":")) {
|
||||
if(theBody.contains("link button not")) {
|
||||
log.warn("registerWithHue needs link button pressed on HUE bridge: " + aName);
|
||||
}
|
||||
else
|
||||
log.warn("registerWithHue returned an unexpected error: " + theBody);
|
||||
errorStringSet.setErrorString(theBody);
|
||||
}
|
||||
else {
|
||||
SuccessUserResponse[] theResponses = new Gson().fromJson(theBody, SuccessUserResponse[].class); //read content for data, SuccessUserResponse[].class);
|
||||
theUser = theResponses[0].getSuccess().getUsername();
|
||||
}
|
||||
}
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
} catch (IOException e) {
|
||||
log.warn("Error logging into HUE: IOException in log", e);
|
||||
}
|
||||
return theUser;
|
||||
}
|
||||
}
|
||||
50
src/main/java/com/bwssystems/logservices/LogFileInfo.java
Normal file
50
src/main/java/com/bwssystems/logservices/LogFileInfo.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* http://www.jrecruiter.org
|
||||
*
|
||||
* Disclaimer of Warranty.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, Licensor provides
|
||||
* the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
* including, without limitation, any warranties or conditions of TITLE,
|
||||
* NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
* solely responsible for determining the appropriateness of using or
|
||||
* redistributing the Work and assume any risks associated with Your exercise of
|
||||
* permissions under this License.
|
||||
*
|
||||
*/
|
||||
package com.bwssystems.logservices;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Log file information.
|
||||
*
|
||||
*/
|
||||
public class LogFileInfo {
|
||||
|
||||
private String fileName;
|
||||
private Long fileSize;
|
||||
private Date fileLastChanged;
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
public Long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
public void setFileSize(Long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
public Date getFileLastChanged() {
|
||||
return fileLastChanged;
|
||||
}
|
||||
public void setFileLastChanged(Date fileLastChanged) {
|
||||
this.fileLastChanged = fileLastChanged;
|
||||
}
|
||||
|
||||
}
|
||||
51
src/main/java/com/bwssystems/logservices/LoggerInfo.java
Normal file
51
src/main/java/com/bwssystems/logservices/LoggerInfo.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package com.bwssystems.logservices;
|
||||
|
||||
import com.bwssystems.logservices.LoggingUtil.LogLevels;
|
||||
|
||||
/**
|
||||
* Logger information.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class LoggerInfo {
|
||||
|
||||
private String loggerName;
|
||||
private LogLevels logLevel;
|
||||
private LogLevels newLogLevel;
|
||||
|
||||
//~~~~~Constructors~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public LoggerInfo() {
|
||||
super();
|
||||
}
|
||||
public LoggerInfo(String loggerName, int logLevelAsInt) {
|
||||
super();
|
||||
this.loggerName = loggerName;
|
||||
this.logLevel = LogLevels.getLogLevelFromId(logLevelAsInt);
|
||||
}
|
||||
|
||||
//~~~~~Getters and Setters~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public String getLoggerName() {
|
||||
return loggerName;
|
||||
}
|
||||
public void setLoggerName(String loggerName) {
|
||||
this.loggerName = loggerName;
|
||||
}
|
||||
public LogLevels getLogLevel() {
|
||||
return logLevel;
|
||||
}
|
||||
public void setLogLevel(LogLevels logLevel) {
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
public LogLevels getNewLogLevel() {
|
||||
return newLogLevel;
|
||||
}
|
||||
public void setNewLogLevel(LogLevels newLogLevel) {
|
||||
this.newLogLevel = newLogLevel;
|
||||
}
|
||||
|
||||
}
|
||||
58
src/main/java/com/bwssystems/logservices/LoggingForm.java
Normal file
58
src/main/java/com/bwssystems/logservices/LoggingForm.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.bwssystems.logservices;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Form for the Logging Action.
|
||||
*/
|
||||
public class LoggingForm implements Serializable {
|
||||
|
||||
/** serialVersionUID. */
|
||||
private static final long serialVersionUID = 5970927715241338665L;
|
||||
|
||||
/** List of Loggers, simplified for display purposes */
|
||||
private List<LoggerInfo> updatedLoggers = new ArrayList<LoggerInfo>();
|
||||
|
||||
/** The user can enter a new logger to be configured */
|
||||
private LoggerInfo newLogger;
|
||||
|
||||
/** Used for requesting a logfile download */
|
||||
private String fileName;
|
||||
|
||||
//~~~~~Constructors~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public LoggingForm() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
//~~~~~Getters and Setters~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
public List<LoggerInfo> getUpdatedLoggers() {
|
||||
return updatedLoggers;
|
||||
}
|
||||
|
||||
public void setUpdatedLoggers(List<LoggerInfo> updatedLoggers) {
|
||||
this.updatedLoggers = updatedLoggers;
|
||||
}
|
||||
|
||||
public LoggerInfo getNewLogger() {
|
||||
return newLogger;
|
||||
}
|
||||
|
||||
public void setNewLogger(LoggerInfo newLogger) {
|
||||
this.newLogger = newLogger;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
}
|
||||
163
src/main/java/com/bwssystems/logservices/LoggingManager.java
Normal file
163
src/main/java/com/bwssystems/logservices/LoggingManager.java
Normal file
@@ -0,0 +1,163 @@
|
||||
package com.bwssystems.logservices;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.bwssystems.logservices.LoggingUtil;
|
||||
import com.bwssystems.logservices.LoggingUtil.LogLevels;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.Logger;
|
||||
|
||||
/**
|
||||
* Show log files and allow to set log levels for the configured loggers (logback)
|
||||
* to be changed dynamically at runtime.
|
||||
*/
|
||||
|
||||
public class LoggingManager {
|
||||
|
||||
/** Show all loggers or only the configured loggers */
|
||||
private boolean showAll = false;
|
||||
|
||||
/** List of log files and associated information */
|
||||
private List<LogFileInfo> logFileInfos = new ArrayList<LogFileInfo>();
|
||||
|
||||
/** List of Loggers, simplified for display purposes */
|
||||
private List<LoggerInfo> configuredLoggers = new ArrayList<LoggerInfo>();
|
||||
|
||||
/** Used to stream a logfile back to the client */
|
||||
private transient InputStream fileToDownLoad;
|
||||
|
||||
private LoggingForm model = new LoggingForm();
|
||||
|
||||
//~~~~~Reference Data~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
/** Defines a collection of log levels. Unfortunately logback does not use
|
||||
* enums to define its log levels. */
|
||||
private Set<LogLevels> logLevels = EnumSet.allOf(LogLevels.class);
|
||||
|
||||
//~~~~~Prepare data~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public void init() {
|
||||
loadLoggers();
|
||||
loadLogFiles();
|
||||
}
|
||||
|
||||
//~~~~~Helper Methods~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
private void loadLoggers() {
|
||||
this.configuredLoggers.clear();
|
||||
|
||||
final List<Logger> loggers = LoggingUtil.getLoggers(this.showAll);
|
||||
|
||||
for (Logger logger : loggers) {
|
||||
|
||||
this.configuredLoggers.add(new LoggerInfo(logger.getName(), logger.getEffectiveLevel().levelInt));
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLogFiles() {
|
||||
this.logFileInfos = LoggingUtil.getLogFileInfos();
|
||||
}
|
||||
|
||||
/** Updates loglevels for loggers */
|
||||
public String updateLogLevels() {
|
||||
|
||||
if (this.model.getUpdatedLoggers() != null && !this.model.getUpdatedLoggers().isEmpty()) {
|
||||
|
||||
for (LoggerInfo loggerInfo : this.model.getUpdatedLoggers()) {
|
||||
|
||||
if (loggerInfo != null && loggerInfo.getNewLogLevel() != null) {
|
||||
|
||||
LoggingUtil.getLogger(loggerInfo.getLoggerName())
|
||||
.setLevel(Level.toLevel(loggerInfo.getNewLogLevel().getLogLevel()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Need to refresh the loggers
|
||||
loadLoggers();
|
||||
}
|
||||
|
||||
|
||||
return "successRedirect";
|
||||
|
||||
}
|
||||
|
||||
/** Adds a new logger at runtime. */
|
||||
public String addNewLogger() {
|
||||
|
||||
if (this.model.getNewLogger() != null
|
||||
&& this.model.getNewLogger().getLoggerName() != null
|
||||
&& this.model.getNewLogger().getNewLogLevel() != null) {
|
||||
final Logger newLogger = LoggingUtil.getLogger(this.model.getNewLogger().getLoggerName());
|
||||
newLogger.setLevel(Level.toLevel(this.model.getNewLogger().getNewLogLevel().getLogLevel()));
|
||||
|
||||
//Need to refresh the loggers
|
||||
loadLoggers();
|
||||
}
|
||||
|
||||
return "successRedirect";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the requested log file.
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public String download() throws Exception {
|
||||
|
||||
if (this.model.getFileName() == null) {
|
||||
throw new IllegalArgumentException("FileName must not be null.");
|
||||
}
|
||||
|
||||
final File logFile = LoggingUtil.getLogFile(this.model.getFileName());
|
||||
|
||||
if (logFile != null) {
|
||||
this.fileToDownLoad = new FileInputStream(logFile);
|
||||
}
|
||||
|
||||
return "download";
|
||||
}
|
||||
|
||||
//~~~~~Getters and Setters~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public List<LogFileInfo> getLogFileInfos() {
|
||||
return logFileInfos;
|
||||
}
|
||||
|
||||
public InputStream getFileToDownLoad() {
|
||||
return fileToDownLoad;
|
||||
}
|
||||
|
||||
public List<LoggerInfo> getConfiguredLoggers() {
|
||||
return configuredLoggers;
|
||||
}
|
||||
|
||||
public boolean isShowAll() {
|
||||
return showAll;
|
||||
}
|
||||
|
||||
public void setShowAll(boolean showAll) {
|
||||
this.showAll = showAll;
|
||||
}
|
||||
|
||||
public Set<LogLevels> getLogLevels() {
|
||||
return logLevels;
|
||||
}
|
||||
|
||||
public LoggingForm getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(LoggingForm model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
}
|
||||
198
src/main/java/com/bwssystems/logservices/LoggingUtil.java
Normal file
198
src/main/java/com/bwssystems/logservices/LoggingUtil.java
Normal file
@@ -0,0 +1,198 @@
|
||||
package com.bwssystems.logservices;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.bwssystems.logservices.LogFileInfo;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.Appender;
|
||||
import ch.qos.logback.core.FileAppender;
|
||||
|
||||
/**
|
||||
* Contains utility methods to interact with the logback during runtime.
|
||||
*/
|
||||
public class LoggingUtil {
|
||||
|
||||
/**
|
||||
* re-defines the logback logging levels as a Java enumeration. This is quite
|
||||
* helpful if you need to render the various log-levels as select-box. I wish
|
||||
* logback @see {@link ch.qos.logback.classic.Level} would not use static variables
|
||||
* but use enums instead.
|
||||
*/
|
||||
public enum LogLevels {
|
||||
|
||||
OFF(Integer.MAX_VALUE, "Off"),
|
||||
ERROR_INT(40000, "Error"),
|
||||
WARN_INT(30000, "Warn"),
|
||||
INFO_INT(20000, "Info"),
|
||||
DEBUG_INT(10000, "Debug"),
|
||||
TRACE(5000, "Trace"),
|
||||
ALL(Integer.MIN_VALUE, "All");
|
||||
|
||||
private int logLevel;
|
||||
private String logLevelName;
|
||||
|
||||
LogLevels(final int logLevel, final String logLevelName) {
|
||||
this.logLevel = logLevel;
|
||||
this.logLevelName = logLevelName;
|
||||
}
|
||||
|
||||
public int getLogLevel() {
|
||||
return this.logLevel;
|
||||
}
|
||||
|
||||
public String getLogLevelName() {
|
||||
return this.logLevelName;
|
||||
}
|
||||
|
||||
public static LogLevels getLogLevelFromId(final int logLevelAsInt) {
|
||||
|
||||
for (LogLevels logLevel : LogLevels.values()) {
|
||||
|
||||
if (logLevelAsInt == logLevel.logLevel) {
|
||||
return logLevel;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Loglevel " + logLevelAsInt + " does not exist.");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.logLevelName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all configured logback loggers.
|
||||
*
|
||||
* @param showAll If set return ALL loggers, not only the configured ones.
|
||||
* @return List of Loggers
|
||||
*/
|
||||
public static List<ch.qos.logback.classic.Logger> getLoggers(final boolean showAll) {
|
||||
|
||||
final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
final List<ch.qos.logback.classic.Logger> loggers = new ArrayList<ch.qos.logback.classic.Logger>();
|
||||
|
||||
for (ch.qos.logback.classic.Logger log : lc.getLoggerList()) {
|
||||
if(showAll == false) {
|
||||
if(log.getLevel() != null || LoggingUtil.hasAppenders(log)) {
|
||||
loggers.add(log);
|
||||
}
|
||||
} else {
|
||||
loggers.add(log);
|
||||
}
|
||||
}
|
||||
|
||||
return loggers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single logger.
|
||||
*
|
||||
* @return Logger
|
||||
*/
|
||||
public static ch.qos.logback.classic.Logger getLogger(final String loggerName) {
|
||||
|
||||
final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
|
||||
return lc.getLogger(loggerName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether the provided logger has appenders.
|
||||
*
|
||||
* @param logger The logger to test
|
||||
* @return true if the logger has appenders.
|
||||
*/
|
||||
public static boolean hasAppenders(ch.qos.logback.classic.Logger logger) {
|
||||
Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders();
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logfile information for the roor logger.
|
||||
*
|
||||
* @return List of LogFileInfo obejcts
|
||||
*/
|
||||
public static List<LogFileInfo> getLogFileInfos() {
|
||||
|
||||
final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
|
||||
final List<LogFileInfo> logFileInfos = new ArrayList<LogFileInfo>();
|
||||
|
||||
final Logger logger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
|
||||
|
||||
final Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders();
|
||||
|
||||
while (it.hasNext()) {
|
||||
|
||||
final Appender<ILoggingEvent> appender = it.next();
|
||||
|
||||
if (appender instanceof FileAppender) {
|
||||
|
||||
final FileAppender<ILoggingEvent> fileAppender = (FileAppender<ILoggingEvent>) appender;
|
||||
|
||||
final File logFile = new File(fileAppender.getFile());
|
||||
final LogFileInfo logFileInfo = new LogFileInfo();
|
||||
|
||||
logFileInfo.setFileName(logFile.getName());
|
||||
logFileInfo.setFileLastChanged(new Date(logFile.lastModified()));
|
||||
logFileInfo.setFileSize(logFile.length());
|
||||
logFileInfos.add(logFileInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return logFileInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the log file.
|
||||
*
|
||||
* @param logFileName The name of the log file
|
||||
* @return The actual file
|
||||
*/
|
||||
public static File getLogFile(final String logFileName) {
|
||||
|
||||
if (logFileName == null) {
|
||||
throw new IllegalArgumentException("logFileName cannot be null.");
|
||||
}
|
||||
|
||||
final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
|
||||
final Logger logger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
|
||||
|
||||
final Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders();
|
||||
|
||||
while (it.hasNext()) {
|
||||
|
||||
final Appender<ILoggingEvent> appender = it.next();
|
||||
|
||||
if (appender instanceof FileAppender) {
|
||||
|
||||
final FileAppender<ILoggingEvent> fileAppender = (FileAppender<ILoggingEvent>) appender;
|
||||
|
||||
final File logFile = new File(fileAppender.getFile());
|
||||
|
||||
if (logFile.getName().equalsIgnoreCase(logFileName)) {
|
||||
return logFile;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
19
src/main/java/com/bwssystems/luupRequests/Categorie.java
Normal file
19
src/main/java/com/bwssystems/luupRequests/Categorie.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
|
||||
public class Categorie {
|
||||
private String name;
|
||||
private String id;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
97
src/main/java/com/bwssystems/luupRequests/Device.java
Normal file
97
src/main/java/com/bwssystems/luupRequests/Device.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
|
||||
public class Device {
|
||||
|
||||
private String name;
|
||||
private String altid;
|
||||
private String id;
|
||||
private String category;
|
||||
private String subcategory;
|
||||
private String room;
|
||||
private String parent;
|
||||
private String status;
|
||||
private String level;
|
||||
private String state;
|
||||
private String comment;
|
||||
private String veraname;
|
||||
private String veraaddress;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getAltid() {
|
||||
return altid;
|
||||
}
|
||||
public void setAltid(String altid) {
|
||||
this.altid = altid;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
public String getSubcategory() {
|
||||
return subcategory;
|
||||
}
|
||||
public void setSubcategory(String subcategory) {
|
||||
this.subcategory = subcategory;
|
||||
}
|
||||
public String getRoom() {
|
||||
return room;
|
||||
}
|
||||
public void setRoom(String room) {
|
||||
this.room = room;
|
||||
}
|
||||
public String getParent() {
|
||||
return parent;
|
||||
}
|
||||
public void setParent(String parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
public String getLevel() {
|
||||
return level;
|
||||
}
|
||||
public void setLevel(String level) {
|
||||
this.level = level;
|
||||
}
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
public String getVeraname() {
|
||||
return veraname;
|
||||
}
|
||||
public void setVeraname(String veraname) {
|
||||
this.veraname = veraname;
|
||||
}
|
||||
public String getVeraaddress() {
|
||||
return veraaddress;
|
||||
}
|
||||
public void setVeraaddress(String veraaddress) {
|
||||
this.veraaddress = veraaddress;
|
||||
}
|
||||
|
||||
}
|
||||
26
src/main/java/com/bwssystems/luupRequests/Room.java
Normal file
26
src/main/java/com/bwssystems/luupRequests/Room.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
|
||||
public class Room {
|
||||
private String name;
|
||||
private String id;
|
||||
private String section;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getSection() {
|
||||
return section;
|
||||
}
|
||||
public void setSection(String section) {
|
||||
this.section = section;
|
||||
}
|
||||
}
|
||||
47
src/main/java/com/bwssystems/luupRequests/Scene.java
Normal file
47
src/main/java/com/bwssystems/luupRequests/Scene.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
|
||||
public class Scene {
|
||||
private String active;
|
||||
private String name;
|
||||
private String id;
|
||||
private String room;
|
||||
private String veraname;
|
||||
private String veraaddress;
|
||||
public String getActive() {
|
||||
return active;
|
||||
}
|
||||
public void setActive(String active) {
|
||||
this.active = active;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getRoom() {
|
||||
return room;
|
||||
}
|
||||
public void setRoom(String room) {
|
||||
this.room = room;
|
||||
}
|
||||
public String getVeraname() {
|
||||
return veraname;
|
||||
}
|
||||
public void setVeraname(String veraname) {
|
||||
this.veraname = veraname;
|
||||
}
|
||||
public String getVeraaddress() {
|
||||
return veraaddress;
|
||||
}
|
||||
public void setVeraaddress(String veraaddress) {
|
||||
this.veraaddress = veraaddress;
|
||||
}
|
||||
|
||||
}
|
||||
140
src/main/java/com/bwssystems/luupRequests/Sdata.java
Normal file
140
src/main/java/com/bwssystems/luupRequests/Sdata.java
Normal file
@@ -0,0 +1,140 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Sdata {
|
||||
private String full;
|
||||
private String version;
|
||||
private String model;
|
||||
private String zwave_heal;
|
||||
private String temperature;
|
||||
private String serial_number;
|
||||
private String fwd1;
|
||||
private String fwd2;
|
||||
private String ir;
|
||||
private String irtx;
|
||||
private String loadtime;
|
||||
private String dataversion;
|
||||
private String state;
|
||||
private String comment;
|
||||
private List<Section> sections;
|
||||
private List<Room> rooms;
|
||||
private List<Scene> scenes;
|
||||
private List<Device> devices;
|
||||
private List<Categorie> categories;
|
||||
|
||||
public String getFull() {
|
||||
return full;
|
||||
}
|
||||
public void setFull(String full) {
|
||||
this.full = full;
|
||||
}
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
public String getZwave_heal() {
|
||||
return zwave_heal;
|
||||
}
|
||||
public void setZwave_heal(String zwave_heal) {
|
||||
this.zwave_heal = zwave_heal;
|
||||
}
|
||||
public String getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
public void setTemperature(String temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
public String getSerial_number() {
|
||||
return serial_number;
|
||||
}
|
||||
public void setSerial_number(String serial_number) {
|
||||
this.serial_number = serial_number;
|
||||
}
|
||||
public String getFwd1() {
|
||||
return fwd1;
|
||||
}
|
||||
public void setFwd1(String fwd1) {
|
||||
this.fwd1 = fwd1;
|
||||
}
|
||||
public String getFwd2() {
|
||||
return fwd2;
|
||||
}
|
||||
public void setFwd2(String fwd2) {
|
||||
this.fwd2 = fwd2;
|
||||
}
|
||||
public String getIr() {
|
||||
return ir;
|
||||
}
|
||||
public void setIr(String ir) {
|
||||
this.ir = ir;
|
||||
}
|
||||
public String getIrtx() {
|
||||
return irtx;
|
||||
}
|
||||
public void setIrtx(String irtx) {
|
||||
this.irtx = irtx;
|
||||
}
|
||||
public String getLoadtime() {
|
||||
return loadtime;
|
||||
}
|
||||
public void setLoadtime(String loadtime) {
|
||||
this.loadtime = loadtime;
|
||||
}
|
||||
public String getDataversion() {
|
||||
return dataversion;
|
||||
}
|
||||
public void setDataversion(String dataversion) {
|
||||
this.dataversion = dataversion;
|
||||
}
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
public List<Section> getSections() {
|
||||
return sections;
|
||||
}
|
||||
public void setSections(List<Section> sections) {
|
||||
this.sections = sections;
|
||||
}
|
||||
public List<Room> getRooms() {
|
||||
return rooms;
|
||||
}
|
||||
public void setRooms(List<Room> rooms) {
|
||||
this.rooms = rooms;
|
||||
}
|
||||
public List<Scene> getScenes() {
|
||||
return scenes;
|
||||
}
|
||||
public void setScenes(List<Scene> scenes) {
|
||||
this.scenes = scenes;
|
||||
}
|
||||
public List<Device> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
public void setDevices(List<Device> devices) {
|
||||
this.devices = devices;
|
||||
}
|
||||
public List<Categorie> getCategoriess() {
|
||||
return categories;
|
||||
}
|
||||
public void setCategoriess(List<Categorie> categories) {
|
||||
this.categories = categories;
|
||||
}
|
||||
}
|
||||
19
src/main/java/com/bwssystems/luupRequests/Section.java
Normal file
19
src/main/java/com/bwssystems/luupRequests/Section.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.bwssystems.luupRequests;
|
||||
|
||||
public class Section {
|
||||
private String name;
|
||||
private String id;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
95
src/main/java/com/bwssystems/util/BackupHandler.java
Normal file
95
src/main/java/com/bwssystems/util/BackupHandler.java
Normal file
@@ -0,0 +1,95 @@
|
||||
package com.bwssystems.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class BackupHandler {
|
||||
final Logger log = LoggerFactory.getLogger(BackupHandler.class);
|
||||
private Path repositoryPath;
|
||||
private String fileExtension;
|
||||
private String defaultName;
|
||||
|
||||
protected void setupParams(Path aPath, String anExtension, String adefaultName) {
|
||||
repositoryPath = aPath;
|
||||
if(anExtension.substring(0, 1).equalsIgnoreCase("."))
|
||||
fileExtension = anExtension;
|
||||
else
|
||||
fileExtension = "." + anExtension;
|
||||
|
||||
defaultName = adefaultName;
|
||||
|
||||
log.debug("setupParams has defaultName: " + defaultName + " and file extension as: " + fileExtension);
|
||||
}
|
||||
|
||||
public String backup(String aFilename) {
|
||||
if(aFilename == null || aFilename.equalsIgnoreCase("")) {
|
||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||
aFilename = defaultName + dateFormat.format(Calendar.getInstance().getTime()) + fileExtension;
|
||||
}
|
||||
else
|
||||
aFilename = aFilename + fileExtension;
|
||||
try {
|
||||
Files.copy(repositoryPath, FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename), StandardCopyOption.COPY_ATTRIBUTES);
|
||||
} catch (IOException e) {
|
||||
log.error("Could not backup to file: " + aFilename + " message: " + e.getMessage(), e);
|
||||
}
|
||||
log.debug("Backup repository: " + aFilename);
|
||||
return aFilename;
|
||||
}
|
||||
|
||||
public String deleteBackup(String aFilename) {
|
||||
log.debug("Delete backup repository: " + aFilename);
|
||||
try {
|
||||
Files.delete(FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename));
|
||||
} catch (IOException e) {
|
||||
log.error("Could not delete file: " + aFilename + " message: " + e.getMessage(), e);
|
||||
}
|
||||
return aFilename;
|
||||
}
|
||||
|
||||
public String restoreBackup(String aFilename) {
|
||||
log.debug("Restore backup repository: " + aFilename);
|
||||
try {
|
||||
Path target = null;
|
||||
if(Files.exists(repositoryPath)) {
|
||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||
target = FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), defaultName + dateFormat.format(Calendar.getInstance().getTime()) + fileExtension);
|
||||
Files.move(repositoryPath, target);
|
||||
}
|
||||
Files.copy(FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename), repositoryPath, StandardCopyOption.COPY_ATTRIBUTES);
|
||||
} catch (IOException e) {
|
||||
log.error("Error restoring the file: " + aFilename + " message: " + e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
return aFilename;
|
||||
}
|
||||
|
||||
public List<String> getBackups() {
|
||||
List<String> theFilenames = new ArrayList<String>();
|
||||
Path dir = repositoryPath.getParent();
|
||||
try (DirectoryStream<Path> stream =
|
||||
Files.newDirectoryStream(dir, "*.{"+ fileExtension.substring(1) + "}")) {
|
||||
for (Path entry: stream) {
|
||||
theFilenames.add(entry.getFileName().toString());
|
||||
}
|
||||
} catch (IOException x) {
|
||||
// IOException can never be thrown by the iteration.
|
||||
// In this snippet, it can // only be thrown by newDirectoryStream.
|
||||
log.warn("Issue getting directory listing for backups in directory: " + x.getMessage());
|
||||
}
|
||||
return theFilenames;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bwssytems.HABridge;
|
||||
package com.bwssystems.util;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import spark.ResponseTransformer;
|
||||
262
src/main/java/com/bwssystems/util/TextStringFormatter.java
Normal file
262
src/main/java/com/bwssystems/util/TextStringFormatter.java
Normal file
@@ -0,0 +1,262 @@
|
||||
package com.bwssystems.util;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.StringCharacterIterator;
|
||||
|
||||
public final class TextStringFormatter {
|
||||
private TextStringFormatter() {
|
||||
// empty - prevent construction
|
||||
}
|
||||
|
||||
/**
|
||||
Escapes characters for text appearing as data in the
|
||||
<a href='http://www.json.org/'>Javascript Object Notation</a>
|
||||
(JSON) data interchange format.
|
||||
|
||||
<P>The following commonly used control characters are escaped :
|
||||
<table border='1' cellpadding='3' cellspacing='0'>
|
||||
<tr><th> Character </th><th> Escaped As </th></tr>
|
||||
<tr><td> " </td><td> \" </td></tr>
|
||||
<tr><td> \ </td><td> \\ </td></tr>
|
||||
<tr><td> / </td><td> \/ </td></tr>
|
||||
<tr><td> back space </td><td> \b </td></tr>
|
||||
<tr><td> form feed </td><td> \f </td></tr>
|
||||
<tr><td> line feed </td><td> \n </td></tr>
|
||||
<tr><td> carriage return </td><td> \r </td></tr>
|
||||
<tr><td> tab </td><td> \t </td></tr>
|
||||
</table>
|
||||
|
||||
<P>See <a href='http://www.ietf.org/rfc/rfc4627.txt'>RFC 4627</a> for more information.
|
||||
*/
|
||||
public static String forJSON(String aText) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
StringCharacterIterator iterator = new StringCharacterIterator(aText);
|
||||
char character = iterator.current();
|
||||
while (character != StringCharacterIterator.DONE) {
|
||||
if (character == '\"') {
|
||||
result.append("\\\"");
|
||||
} else if (character == '\\') {
|
||||
result.append("\\\\");
|
||||
} else if (character == '/') {
|
||||
result.append("\\/");
|
||||
} else if (character == '\b') {
|
||||
result.append("\\b");
|
||||
} else if (character == '\f') {
|
||||
result.append("\\f");
|
||||
} else if (character == '\n') {
|
||||
result.append("\\n");
|
||||
} else if (character == '\r') {
|
||||
result.append("\\r");
|
||||
} else if (character == '\t') {
|
||||
result.append("\\t");
|
||||
} else {
|
||||
// the char is not a special one
|
||||
// add it to the result as is
|
||||
result.append(character);
|
||||
}
|
||||
character = iterator.next();
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
Escape characters for text appearing in HTML markup.
|
||||
|
||||
<P>This method exists as a defence against Cross Site Scripting (XSS) hacks.
|
||||
The idea is to neutralize control characters commonly used by scripts, such that
|
||||
they will not be executed by the browser. This is done by replacing the control
|
||||
characters with their escaped equivalents.
|
||||
See {@link hirondelle.web4j.security.SafeText} as well.
|
||||
|
||||
<P>The following characters are replaced with corresponding
|
||||
HTML character entities :
|
||||
<table border='1' cellpadding='3' cellspacing='0'>
|
||||
<tr><th> Character </th><th>Replacement</th></tr>
|
||||
<tr><td> < </td><td> < </td></tr>
|
||||
<tr><td> > </td><td> > </td></tr>
|
||||
<tr><td> & </td><td> & </td></tr>
|
||||
<tr><td> " </td><td> "</td></tr>
|
||||
<tr><td> \t </td><td> 	</td></tr>
|
||||
<tr><td> ! </td><td> !</td></tr>
|
||||
<tr><td> # </td><td> #</td></tr>
|
||||
<tr><td> $ </td><td> $</td></tr>
|
||||
<tr><td> % </td><td> %</td></tr>
|
||||
<tr><td> ' </td><td> '</td></tr>
|
||||
<tr><td> ( </td><td> (</td></tr>
|
||||
<tr><td> ) </td><td> )</td></tr>
|
||||
<tr><td> * </td><td> *</td></tr>
|
||||
<tr><td> + </td><td> + </td></tr>
|
||||
<tr><td> , </td><td> , </td></tr>
|
||||
<tr><td> - </td><td> - </td></tr>
|
||||
<tr><td> . </td><td> . </td></tr>
|
||||
<tr><td> / </td><td> / </td></tr>
|
||||
<tr><td> : </td><td> :</td></tr>
|
||||
<tr><td> ; </td><td> ;</td></tr>
|
||||
<tr><td> = </td><td> =</td></tr>
|
||||
<tr><td> ? </td><td> ?</td></tr>
|
||||
<tr><td> @ </td><td> @</td></tr>
|
||||
<tr><td> [ </td><td> [</td></tr>
|
||||
<tr><td> \ </td><td> \</td></tr>
|
||||
<tr><td> ] </td><td> ]</td></tr>
|
||||
<tr><td> ^ </td><td> ^</td></tr>
|
||||
<tr><td> _ </td><td> _</td></tr>
|
||||
<tr><td> ` </td><td> `</td></tr>
|
||||
<tr><td> { </td><td> {</td></tr>
|
||||
<tr><td> | </td><td> |</td></tr>
|
||||
<tr><td> } </td><td> }</td></tr>
|
||||
<tr><td> ~ </td><td> ~</td></tr>
|
||||
</table>
|
||||
|
||||
<P>Note that JSTL's {@code <c:out>} escapes <em>only the first
|
||||
five</em> of the above characters.
|
||||
*/
|
||||
|
||||
public static String forHTML(String aText) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
final StringCharacterIterator iterator = new StringCharacterIterator(aText);
|
||||
char character = iterator.current();
|
||||
while (character != CharacterIterator.DONE) {
|
||||
if (character == '<') {
|
||||
result.append("<");
|
||||
} else if (character == '>') {
|
||||
result.append(">");
|
||||
} else if (character == '&') {
|
||||
result.append("&");
|
||||
} else if (character == '\"') {
|
||||
result.append(""");
|
||||
} else if (character == '\t') {
|
||||
addCharEntity(9, result);
|
||||
} else if (character == '!') {
|
||||
addCharEntity(33, result);
|
||||
} else if (character == '#') {
|
||||
addCharEntity(35, result);
|
||||
} else if (character == '$') {
|
||||
addCharEntity(36, result);
|
||||
} else if (character == '%') {
|
||||
addCharEntity(37, result);
|
||||
} else if (character == '\'') {
|
||||
addCharEntity(39, result);
|
||||
} else if (character == '(') {
|
||||
addCharEntity(40, result);
|
||||
} else if (character == ')') {
|
||||
addCharEntity(41, result);
|
||||
} else if (character == '*') {
|
||||
addCharEntity(42, result);
|
||||
} else if (character == '+') {
|
||||
addCharEntity(43, result);
|
||||
} else if (character == ',') {
|
||||
addCharEntity(44, result);
|
||||
} else if (character == '-') {
|
||||
addCharEntity(45, result);
|
||||
} else if (character == '.') {
|
||||
addCharEntity(46, result);
|
||||
} else if (character == '/') {
|
||||
addCharEntity(47, result);
|
||||
} else if (character == ':') {
|
||||
addCharEntity(58, result);
|
||||
} else if (character == ';') {
|
||||
addCharEntity(59, result);
|
||||
} else if (character == '=') {
|
||||
addCharEntity(61, result);
|
||||
} else if (character == '?') {
|
||||
addCharEntity(63, result);
|
||||
} else if (character == '@') {
|
||||
addCharEntity(64, result);
|
||||
} else if (character == '[') {
|
||||
addCharEntity(91, result);
|
||||
} else if (character == '\\') {
|
||||
addCharEntity(92, result);
|
||||
} else if (character == ']') {
|
||||
addCharEntity(93, result);
|
||||
} else if (character == '^') {
|
||||
addCharEntity(94, result);
|
||||
} else if (character == '_') {
|
||||
addCharEntity(95, result);
|
||||
} else if (character == '`') {
|
||||
addCharEntity(96, result);
|
||||
} else if (character == '{') {
|
||||
addCharEntity(123, result);
|
||||
} else if (character == '|') {
|
||||
addCharEntity(124, result);
|
||||
} else if (character == '}') {
|
||||
addCharEntity(125, result);
|
||||
} else if (character == '~') {
|
||||
addCharEntity(126, result);
|
||||
} else {
|
||||
// the char is not a special one
|
||||
// add it to the result as is
|
||||
result.append(character);
|
||||
}
|
||||
character = iterator.next();
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape all ampersand characters in a URL.
|
||||
*
|
||||
* <P>
|
||||
* Replaces all <tt>'&'</tt> characters with <tt>'&'</tt>.
|
||||
*
|
||||
* <P>
|
||||
* An ampersand character may appear in the query string of a URL. The
|
||||
* ampersand character is indeed valid in a URL.
|
||||
* <em>However, URLs usually appear as an <tt>HREF</tt> attribute, and such
|
||||
* attributes have the additional constraint that ampersands must be
|
||||
* escaped.</em>
|
||||
*
|
||||
* <P>
|
||||
* The JSTL <c:url> tag does indeed perform proper URL encoding of query
|
||||
* parameters. But it does not, in general, produce text which is valid as
|
||||
* an <tt>HREF</tt> attribute, simply because it does not escape the
|
||||
* ampersand character. This is a nuisance when multiple query parameters
|
||||
* appear in the URL, since it requires a little extra work.
|
||||
*/
|
||||
public static String forHrefAmpersand(String aURL) {
|
||||
return aURL.replace("&", "&");
|
||||
}
|
||||
|
||||
public static String forQuerySpace(String aURL) {
|
||||
return aURL.replace(" ", "\u0020");
|
||||
}
|
||||
public static String forQuerySpaceUrl(String aURL) {
|
||||
return aURL.replace(" ", "%20");
|
||||
}
|
||||
/**
|
||||
* Synonym for <tt>URLEncoder.encode(String, "UTF-8")</tt>.
|
||||
*
|
||||
* <P>
|
||||
* Used to ensure that HTTP query strings are in proper form, by escaping
|
||||
* special characters such as spaces.
|
||||
*
|
||||
* <P>
|
||||
* It is important to note that if a query string appears in an
|
||||
* <tt>HREF</tt> attribute, then there are two issues - ensuring the query
|
||||
* string is valid HTTP (it is URL-encoded), and ensuring it is valid HTML
|
||||
* (ensuring the ampersand is escaped).
|
||||
*/
|
||||
public static String forURL(String aURLFragment) {
|
||||
String result = null;
|
||||
try {
|
||||
result = URLEncoder.encode(aURLFragment, "UTF-8");
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw new RuntimeException("UTF-8 not supported", ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void addCharEntity(Integer aIdx, StringBuilder aBuilder) {
|
||||
String padding = "";
|
||||
if (aIdx <= 9) {
|
||||
padding = "00";
|
||||
} else if (aIdx <= 99) {
|
||||
padding = "0";
|
||||
} else {
|
||||
// no prefix
|
||||
}
|
||||
String number = padding + aIdx.toString();
|
||||
aBuilder.append("&#" + number + ";");
|
||||
}
|
||||
}
|
||||
72
src/main/java/com/bwssystems/util/UDPDatagramSender.java
Normal file
72
src/main/java/com/bwssystems/util/UDPDatagramSender.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package com.bwssystems.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class UDPDatagramSender {
|
||||
private Logger log = LoggerFactory.getLogger(UDPDatagramSender.class);
|
||||
private DatagramSocket responseSocket = null;
|
||||
private int udpResponsePort;
|
||||
|
||||
public UDPDatagramSender() {
|
||||
super();
|
||||
udpResponsePort = 0;
|
||||
}
|
||||
|
||||
public static UDPDatagramSender createUDPDatagramSender(int udpResponsePort) {
|
||||
UDPDatagramSender aDatagramSender = new UDPDatagramSender();
|
||||
if(aDatagramSender.initializeSocket(udpResponsePort))
|
||||
return aDatagramSender;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean initializeSocket(int port) {
|
||||
log.info("Initializing UDP response Seocket...");
|
||||
udpResponsePort = port;
|
||||
boolean portLoopControl = true;
|
||||
int retryCount = 0;
|
||||
while(portLoopControl) {
|
||||
try {
|
||||
responseSocket = new DatagramSocket(udpResponsePort);
|
||||
portLoopControl = false;
|
||||
} catch(SocketException e) {
|
||||
if(retryCount == 0)
|
||||
log.warn("UDP Response Port is in use, starting loop to find open port for 20 tries - configured port is: " + udpResponsePort);
|
||||
if(retryCount >= 20) {
|
||||
portLoopControl = false;
|
||||
log.error("UDP Response Port issue, could not find open port - last port tried: " + udpResponsePort + " with message: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(portLoopControl) {
|
||||
retryCount++;
|
||||
udpResponsePort++;
|
||||
}
|
||||
}
|
||||
log.info("UDP response Seocket initialized to: " + udpResponsePort);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getUdpResponsePort() {
|
||||
return udpResponsePort;
|
||||
}
|
||||
|
||||
public void closeResponseSocket() {
|
||||
responseSocket.close();
|
||||
}
|
||||
|
||||
public void sendUDPResponse(String udpResponse, InetAddress requester, int sourcePort) throws IOException {
|
||||
log.debug("Sending response string: <<<" + udpResponse + ">>>");
|
||||
if(responseSocket == null)
|
||||
throw new IOException("Socket not initialized");
|
||||
DatagramPacket response = new DatagramPacket(udpResponse.getBytes(), udpResponse.length(), requester, sourcePort);
|
||||
responseSocket.send(response);
|
||||
}
|
||||
}
|
||||
73
src/main/java/com/bwssystems/vera/VeraHome.java
Normal file
73
src/main/java/com/bwssystems/vera/VeraHome.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package com.bwssystems.vera;
|
||||
|
||||
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.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.luupRequests.Device;
|
||||
import com.bwssystems.luupRequests.Scene;
|
||||
import com.bwssystems.luupRequests.Sdata;
|
||||
|
||||
public class VeraHome {
|
||||
private static final Logger log = LoggerFactory.getLogger(VeraHome.class);
|
||||
private Map<String, VeraInfo> veras;
|
||||
|
||||
public VeraHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
veras = new HashMap<String, VeraInfo>();
|
||||
if(!bridgeSettings.isValidVera())
|
||||
return;
|
||||
Iterator<NamedIP> theList = bridgeSettings.getVeraAddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aVera = theList.next();
|
||||
veras.put(aVera.getName(), new VeraInfo(aVera));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Device> getDevices() {
|
||||
log.debug("consolidating devices for veras");
|
||||
Iterator<String> keys = veras.keySet().iterator();
|
||||
ArrayList<Device> deviceList = new ArrayList<Device>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Sdata theSdata = veras.get(key).getSdata();
|
||||
if(theSdata != null) {
|
||||
Iterator<Device> devices = theSdata.getDevices().iterator();
|
||||
while(devices.hasNext()) {
|
||||
deviceList.add(devices.next());
|
||||
}
|
||||
}
|
||||
else {
|
||||
deviceList = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
public List<Scene> getScenes() {
|
||||
log.debug("consolidating scenes for veras");
|
||||
Iterator<String> keys = veras.keySet().iterator();
|
||||
ArrayList<Scene> sceneList = new ArrayList<Scene>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Sdata theSdata = veras.get(key).getSdata();
|
||||
if(theSdata != null) {
|
||||
Iterator<Scene> scenes = theSdata.getScenes().iterator();
|
||||
while(scenes.hasNext()) {
|
||||
sceneList.add(scenes.next());
|
||||
}
|
||||
}
|
||||
else {
|
||||
sceneList = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sceneList;
|
||||
}
|
||||
}
|
||||
109
src/main/java/com/bwssystems/vera/VeraInfo.java
Normal file
109
src/main/java/com/bwssystems/vera/VeraInfo.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package com.bwssystems.vera;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.luupRequests.Categorie;
|
||||
import com.bwssystems.luupRequests.Device;
|
||||
import com.bwssystems.luupRequests.Room;
|
||||
import com.bwssystems.luupRequests.Scene;
|
||||
import com.bwssystems.luupRequests.Sdata;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
|
||||
public class VeraInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(VeraInfo.class);
|
||||
private HttpClient httpClient;
|
||||
private static final String SDATA_REQUEST = ":3480/data_request?id=sdata&output_format=json";
|
||||
private NamedIP veraAddress;
|
||||
|
||||
public VeraInfo(NamedIP addressName) {
|
||||
super();
|
||||
httpClient = HttpClients.createDefault();
|
||||
veraAddress = addressName;
|
||||
}
|
||||
|
||||
public Sdata getSdata() {
|
||||
Sdata theSdata = null;
|
||||
|
||||
String theUrl = "http://" + veraAddress.getIp() + SDATA_REQUEST;
|
||||
String theData;
|
||||
|
||||
theData = doHttpGETRequest(theUrl);
|
||||
if(theData != null) {
|
||||
theSdata = new Gson().fromJson(theData, Sdata.class);
|
||||
log.debug("GET sdata - full: " + theSdata.getFull() + ", version: " + theSdata.getVersion());
|
||||
denormalizeSdata(theSdata);
|
||||
}
|
||||
return theSdata;
|
||||
}
|
||||
|
||||
private void denormalizeSdata(Sdata theSdata) {
|
||||
Map<String,Room> roomMap = new HashMap<String,Room>();
|
||||
for (Room i : theSdata.getRooms()) roomMap.put(i.getId(),i);
|
||||
Map<String,Categorie> categoryMap = new HashMap<String,Categorie>();
|
||||
for (Categorie i : theSdata.getCategoriess()) categoryMap.put(i.getId(),i);
|
||||
Categorie controllerCat = new Categorie();
|
||||
controllerCat.setName("Controller");
|
||||
controllerCat.setId("0");
|
||||
categoryMap.put(controllerCat.getId(),controllerCat);
|
||||
ListIterator<Device> theIterator = theSdata.getDevices().listIterator();
|
||||
Device theDevice = null;
|
||||
while (theIterator.hasNext()) {
|
||||
theDevice = theIterator.next();
|
||||
if(theDevice.getRoom() != null && roomMap.get(theDevice.getRoom()) != null)
|
||||
theDevice.setRoom(roomMap.get(theDevice.getRoom()).getName());
|
||||
else
|
||||
theDevice.setRoom("no room");
|
||||
|
||||
if(theDevice.getCategory() != null && categoryMap.get(theDevice.getCategory()) != null)
|
||||
theDevice.setCategory(categoryMap.get(theDevice.getCategory()).getName());
|
||||
else
|
||||
theDevice.setCategory("<unknown>");
|
||||
theDevice.setVeraaddress(veraAddress.getIp());
|
||||
theDevice.setVeraname(veraAddress.getName());
|
||||
}
|
||||
|
||||
ListIterator<Scene> theSecneIter = theSdata.getScenes().listIterator();
|
||||
Scene theScene = null;
|
||||
while (theSecneIter.hasNext()) {
|
||||
theScene = theSecneIter.next();
|
||||
if(theScene.getRoom() != null && roomMap.get(theScene.getRoom()) != null)
|
||||
theScene.setRoom(roomMap.get(theScene.getRoom()).getName());
|
||||
else
|
||||
theScene.setRoom("no room");
|
||||
theScene.setVeraaddress(veraAddress.getIp());
|
||||
theScene.setVeraname(veraAddress.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// This function executes the url against the vera
|
||||
protected String doHttpGETRequest(String url) {
|
||||
String theContent = null;
|
||||
log.debug("calling GET on URL: " + url);
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
try {
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
log.debug("GET on URL responded: " + response.getStatusLine().getStatusCode());
|
||||
if(response.getStatusLine().getStatusCode() == 200){
|
||||
theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); //read content for data
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("doHttpGETRequest: Error calling out to HA gateway: " + e.getMessage());
|
||||
}
|
||||
return theContent;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.bwssytems.HABridge;
|
||||
|
||||
import static spark.Spark.*;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssytems.HABridge.devicemanagmeent.*;
|
||||
import com.bwssytems.HABridge.hue.HueMulator;
|
||||
import com.bwssytems.HABridge.upnp.UpnpListener;
|
||||
import com.bwssytems.HABridge.upnp.UpnpSettingsResource;
|
||||
|
||||
public class AmazonEchoBridge {
|
||||
|
||||
/*
|
||||
* This program is based on the work of armzilla from this github repository:
|
||||
* https://github.com/armzilla/amazon-echo-ha-bridge
|
||||
*
|
||||
* This is the main entry point to start the amazon echo bridge.
|
||||
*
|
||||
* This program is using sparkjava rest server to build all the http calls.
|
||||
* Sparkjava is a microframework that uses Jetty webserver module to host
|
||||
* its' calls. This is a very compact system than using the spring frameworks
|
||||
* that was previously used.
|
||||
*
|
||||
* There is a custom upnp listener that is started to handle discovery.
|
||||
*
|
||||
* This application does not store the lights configuration persistently.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Logger log = LoggerFactory.getLogger(AmazonEchoBridge.class);
|
||||
DeviceResource theResources;
|
||||
HueMulator theHueMulator;
|
||||
UpnpSettingsResource theSettingResponder;
|
||||
UpnpListener theUpnpListener;
|
||||
|
||||
// sparkjava config directive to set ip address for the web server to listen on
|
||||
ipAddress(System.getProperty("upnp.config.address", "0.0.0.0"));
|
||||
// sparkjava config directive to set port for the web server to listen on
|
||||
port(Integer.valueOf(System.getProperty("server.port", "8080")));
|
||||
// sparkjava config directive to set html static file location for Jetty
|
||||
staticFileLocation("/public");
|
||||
log.debug("Starting setup....");
|
||||
// setup the class to handle the resource setup rest api
|
||||
theResources = new DeviceResource();
|
||||
// setup the class to handle the hue emulator rest api
|
||||
theHueMulator = new HueMulator(theResources.getDeviceRepository());
|
||||
// setup the class to handle the upnp response rest api
|
||||
theSettingResponder = new UpnpSettingsResource();
|
||||
// wait for the sparkjava initialization of the rest api classes to be complete
|
||||
awaitInitialization();
|
||||
// start the upnp ssdp discovery listener
|
||||
theUpnpListener = new UpnpListener();
|
||||
log.debug("Done setup, application to run....");
|
||||
theUpnpListener.startListening();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.bwssytems.HABridge.api;
|
||||
|
||||
/**
|
||||
* Created by arm on 4/13/15.
|
||||
*/
|
||||
public class Device {
|
||||
private String name;
|
||||
private String deviceType;
|
||||
private String offUrl;
|
||||
private String onUrl;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
public void setDeviceType(String deviceType) {
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public String getOffUrl() {
|
||||
return offUrl;
|
||||
}
|
||||
|
||||
public void setOffUrl(String offUrl) {
|
||||
this.offUrl = offUrl;
|
||||
}
|
||||
|
||||
public String getOnUrl() {
|
||||
return onUrl;
|
||||
}
|
||||
|
||||
public void setOnUrl(String onUrl) {
|
||||
this.onUrl = onUrl;
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package com.bwssytems.HABridge.api.hue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by arm on 4/14/15.
|
||||
*/
|
||||
public class DeviceResponse {
|
||||
private DeviceState state;
|
||||
private String type;
|
||||
private String name;
|
||||
private String modelid;
|
||||
private String manufacturername;
|
||||
private String uniqueid;
|
||||
private String swversion;
|
||||
private Map<String, String> pointsymbol;
|
||||
|
||||
public DeviceState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(DeviceState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getModelid() {
|
||||
return modelid;
|
||||
}
|
||||
|
||||
public void setModelid(String modelid) {
|
||||
this.modelid = modelid;
|
||||
}
|
||||
|
||||
public String getManufacturername() {
|
||||
return manufacturername;
|
||||
}
|
||||
|
||||
public void setManufacturername(String manufacturername) {
|
||||
this.manufacturername = manufacturername;
|
||||
}
|
||||
|
||||
public String getUniqueid() {
|
||||
return uniqueid;
|
||||
}
|
||||
|
||||
public void setUniqueid(String uniqueid) {
|
||||
this.uniqueid = uniqueid;
|
||||
}
|
||||
|
||||
public String getSwversion() {
|
||||
return swversion;
|
||||
}
|
||||
|
||||
public void setSwversion(String swversion) {
|
||||
this.swversion = swversion;
|
||||
}
|
||||
|
||||
public Map<String, String> getPointsymbol() {
|
||||
Map<String, String> dummyValue = new HashMap<>();
|
||||
dummyValue.put("1", "none");
|
||||
dummyValue.put("2", "none");
|
||||
dummyValue.put("3", "none");
|
||||
dummyValue.put("4", "none");
|
||||
dummyValue.put("5", "none");
|
||||
dummyValue.put("6", "none");
|
||||
dummyValue.put("7", "none");
|
||||
dummyValue.put("8", "none");
|
||||
|
||||
return dummyValue;
|
||||
}
|
||||
|
||||
public void setPointsymbol(Map<String, String> pointsymbol) {
|
||||
this.pointsymbol = pointsymbol;
|
||||
}
|
||||
|
||||
public static DeviceResponse createResponse(String name, String id){
|
||||
DeviceState deviceState = new DeviceState();
|
||||
DeviceResponse response = new DeviceResponse();
|
||||
response.setState(deviceState);
|
||||
deviceState.setOn(false);
|
||||
deviceState.setReachable(true);
|
||||
deviceState.setEffect("none");
|
||||
deviceState.setAlert("none");
|
||||
deviceState.setBri(254);
|
||||
deviceState.setHue(15823);
|
||||
deviceState.setSat(88);
|
||||
deviceState.setCt(313);
|
||||
|
||||
List<Double> xv = new LinkedList<>();
|
||||
xv.add(0.4255);
|
||||
xv.add(0.3998);
|
||||
deviceState.setXy(xv);
|
||||
deviceState.setColormode("ct");
|
||||
response.setName(name);
|
||||
response.setUniqueid(id);
|
||||
response.setManufacturername("Philips");
|
||||
response.setType("Extended color light");
|
||||
response.setModelid("LCT001");
|
||||
response.setSwversion("65003148");
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.bwssytems.HABridge.api.hue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.bwssytems.HABridge.api.hue.DeviceResponse;
|
||||
|
||||
/**
|
||||
* Created by arm on 4/14/15.
|
||||
*/
|
||||
public class HueApiResponse {
|
||||
private Map<String, DeviceResponse> lights;
|
||||
|
||||
public Map<String, DeviceResponse> getLights() {
|
||||
return lights;
|
||||
}
|
||||
|
||||
public void setLights(Map<String, DeviceResponse> lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.bwssytems.HABridge.dao;
|
||||
/*
|
||||
* Object to handle the device configuration
|
||||
*/
|
||||
public class DeviceDescriptor{
|
||||
private String id;
|
||||
private String name;
|
||||
private String deviceType;
|
||||
private String offUrl;
|
||||
private String onUrl;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
public void setDeviceType(String deviceType) {
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public String getOffUrl() {
|
||||
return offUrl;
|
||||
}
|
||||
|
||||
public void setOffUrl(String offUrl) {
|
||||
this.offUrl = offUrl;
|
||||
}
|
||||
|
||||
public String getOnUrl() {
|
||||
return onUrl;
|
||||
}
|
||||
|
||||
public void setOnUrl(String onUrl) {
|
||||
this.onUrl = onUrl;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.bwssytems.HABridge.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import com.bwssytems.HABridge.dao.DeviceDescriptor;
|
||||
|
||||
import java.util.List;
|
||||
/*
|
||||
* This is an in memory list to manage the configured devices.
|
||||
*
|
||||
*/
|
||||
public class DeviceRepository {
|
||||
Map<String, DeviceDescriptor> devices;
|
||||
final Random random = new Random();
|
||||
|
||||
public DeviceRepository() {
|
||||
super();
|
||||
devices = new HashMap<String, DeviceDescriptor>();
|
||||
}
|
||||
|
||||
public List<DeviceDescriptor> findAll() {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<DeviceDescriptor> findByDeviceType(String aType) {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
return list;
|
||||
}
|
||||
|
||||
public DeviceDescriptor findOne(String id) {
|
||||
return devices.get(id);
|
||||
|
||||
}
|
||||
|
||||
public void save(DeviceDescriptor aDescriptor) {
|
||||
int id = random.nextInt(Integer.MAX_VALUE);
|
||||
aDescriptor.setId(String.valueOf(id));
|
||||
devices.put(String.valueOf(id),aDescriptor);
|
||||
}
|
||||
|
||||
public String delete(DeviceDescriptor aDescriptor) {
|
||||
if (aDescriptor != null) {
|
||||
devices.remove(aDescriptor.getId());
|
||||
return "Device with id '" + aDescriptor.getId() + "' deleted";
|
||||
} else {
|
||||
return "Device not found";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package com.bwssytems.HABridge.devicemanagmeent;
|
||||
|
||||
import com.bwssytems.HABridge.JsonTransformer;
|
||||
import com.bwssytems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssytems.HABridge.dao.DeviceRepository;
|
||||
|
||||
import static spark.Spark.get;
|
||||
import static spark.Spark.post;
|
||||
import static spark.Spark.put;
|
||||
import static spark.Spark.delete;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
spark core server for bridge configuration
|
||||
*/
|
||||
public class DeviceResource {
|
||||
private static final String API_CONTEXT = "/api/devices";
|
||||
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
|
||||
|
||||
private DeviceRepository deviceRepository;
|
||||
|
||||
|
||||
public DeviceResource() {
|
||||
super();
|
||||
deviceRepository = new DeviceRepository();
|
||||
setupEndpoints();
|
||||
}
|
||||
|
||||
public DeviceRepository getDeviceRepository() {
|
||||
return deviceRepository;
|
||||
}
|
||||
|
||||
private void setupEndpoints() {
|
||||
log.debug("Setting up endpoints");
|
||||
post(API_CONTEXT + "/", "application/json", (request, response) -> {
|
||||
log.debug("Create a Device - request body: " + request.body());
|
||||
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
|
||||
DeviceDescriptor deviceEntry = new DeviceDescriptor();
|
||||
deviceEntry.setName(device.getName());
|
||||
log.debug("Create a Device - device json name: " + deviceEntry.getName());
|
||||
deviceEntry.setDeviceType(device.getDeviceType());
|
||||
log.debug("Create a Device - device json type:" + deviceEntry.getDeviceType());
|
||||
deviceEntry.setOnUrl(device.getOnUrl());
|
||||
log.debug("Create a Device - device json on URL:" + deviceEntry.getOnUrl());
|
||||
deviceEntry.setOffUrl(device.getOffUrl());
|
||||
log.debug("Create a Device - device json off URL:" + deviceEntry.getOffUrl());
|
||||
|
||||
deviceRepository.save(deviceEntry);
|
||||
log.debug("Created a Device");
|
||||
|
||||
response.status(201);
|
||||
return deviceEntry;
|
||||
}, new JsonTransformer());
|
||||
|
||||
put (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
||||
log.debug("Saved a Device");
|
||||
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
|
||||
DeviceDescriptor deviceEntry = deviceRepository.findOne(request.params(":id"));
|
||||
if(deviceEntry == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
deviceEntry.setName(device.getName());
|
||||
deviceEntry.setDeviceType(device.getDeviceType());
|
||||
deviceEntry.setOnUrl(device.getOnUrl());
|
||||
deviceEntry.setOffUrl(device.getOffUrl());
|
||||
|
||||
deviceRepository.save(deviceEntry);
|
||||
return deviceEntry;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/", "application/json", (request, response) -> {
|
||||
List<DeviceDescriptor> deviceList = deviceRepository.findAll();
|
||||
log.debug("Get all devices");
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
String theStream = aRenderer.render(deviceList);
|
||||
log.debug("The Device List: " + theStream);
|
||||
return deviceList;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
||||
log.debug("Get a device");
|
||||
DeviceDescriptor descriptor = deviceRepository.findOne(request.params(":id"));
|
||||
if(descriptor == null){
|
||||
return null;
|
||||
}
|
||||
return descriptor;
|
||||
}, new JsonTransformer());
|
||||
|
||||
delete (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
||||
log.debug("Delete a device");
|
||||
DeviceDescriptor deleted = deviceRepository.findOne(request.params(":id"));
|
||||
if(deleted == null){
|
||||
return null;
|
||||
}
|
||||
deviceRepository.delete(deleted);
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
package com.bwssytems.HABridge.hue;
|
||||
|
||||
import com.bwssytems.HABridge.api.hue.DeviceResponse;
|
||||
import com.bwssytems.HABridge.api.hue.DeviceState;
|
||||
import com.bwssytems.HABridge.api.hue.HueApiResponse;
|
||||
import com.bwssytems.HABridge.dao.*;
|
||||
import com.bwssytems.HABridge.JsonTransformer;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import static spark.Spark.get;
|
||||
import static spark.Spark.post;
|
||||
import static spark.Spark.put;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Based on Armzilla's HueMulator - a Philips Hue emulator using sparkjava rest server
|
||||
*/
|
||||
|
||||
public class HueMulator {
|
||||
private static final Logger log = LoggerFactory.getLogger(HueMulator.class);
|
||||
private static final String INTENSITY_PERCENT = "${intensity.percent}";
|
||||
private static final String INTENSITY_BYTE = "${intensity.byte}";
|
||||
private static final String HUE_CONTEXT = "/api";
|
||||
|
||||
private DeviceRepository repository;
|
||||
private HttpClient httpClient;
|
||||
private ObjectMapper mapper;
|
||||
|
||||
|
||||
public HueMulator(DeviceRepository aDeviceRepository){
|
||||
httpClient = HttpClients.createMinimal();
|
||||
mapper = new ObjectMapper(); //armzilla: work around Echo incorrect content type and breaking mapping. Map manually
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
repository = aDeviceRepository;
|
||||
setupEndpoints();
|
||||
}
|
||||
|
||||
// This function sets up the sparkjava rest calls for the hue api
|
||||
private void setupEndpoints() {
|
||||
// http://ip_address:port/api/{userId}/lights returns json objects of all lights configured
|
||||
get(HUE_CONTEXT + "/:userid/lights", "application/json", (request, response) -> {
|
||||
String userId = request.params(":userid");
|
||||
log.info("hue lights list requested: " + userId + " from " + request.ip());
|
||||
List<DeviceDescriptor> deviceList = repository.findByDeviceType("switch");
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
String theStream = aRenderer.render(deviceList);
|
||||
log.debug("The Device List: " + theStream);
|
||||
Map<String, String> deviceResponseMap = new HashMap<>();
|
||||
for (DeviceDescriptor device : deviceList) {
|
||||
deviceResponseMap.put(device.getId(), device.getName());
|
||||
}
|
||||
response.status(200);
|
||||
return deviceResponseMap;
|
||||
} , new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/* returns json object for a test call
|
||||
post(HUE_CONTEXT + "/*", "application/json", (request, response) -> {
|
||||
response.status(200);
|
||||
return "[{\"success\":{\"username\":\"lights\"}}]";
|
||||
} );
|
||||
|
||||
// http://ip_address:port/api/{userId} returns json objects for the list of names of lights
|
||||
get(HUE_CONTEXT + "/:userid", "application/json", (request, response) -> {
|
||||
String userId = request.params(":userid");
|
||||
log.info("hue api root requested: " + userId + " from " + request.ip());
|
||||
List<DeviceDescriptor> descriptorList = repository.findByDeviceType("switch");
|
||||
if (descriptorList == null) {
|
||||
response.status(404);
|
||||
return null;
|
||||
}
|
||||
Map<String, DeviceResponse> deviceList = new HashMap<>();
|
||||
|
||||
descriptorList.forEach(descriptor -> {
|
||||
DeviceResponse deviceResponse = DeviceResponse.createResponse(descriptor.getName(), descriptor.getId());
|
||||
deviceList.put(descriptor.getId(), deviceResponse);
|
||||
}
|
||||
);
|
||||
HueApiResponse apiResponse = new HueApiResponse();
|
||||
apiResponse.setLights(deviceList);
|
||||
|
||||
response.status(200);
|
||||
return apiResponse;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/{userId}/lights/{lightId} returns json object for a given light
|
||||
get(HUE_CONTEXT + "/:userid/lights/:id", "application/json", (request, response) -> {
|
||||
String userId = request.params(":userid");
|
||||
String lightId = request.params(":id");
|
||||
log.info("hue light requested: " + lightId + "for user: " + userId + " from " + request.ip());
|
||||
DeviceDescriptor device = repository.findOne(lightId);
|
||||
if (device == null) {
|
||||
response.status(404);
|
||||
return null;
|
||||
} else {
|
||||
log.info("found device named: " + device.getName());
|
||||
}
|
||||
DeviceResponse lightResponse = DeviceResponse.createResponse(device.getName(), device.getId());
|
||||
|
||||
response.status(200);
|
||||
return lightResponse;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/{userId}/lights/{lightId}/state uses json object to set the lights state
|
||||
put(HUE_CONTEXT + "/:userid/lights/:id/state", "application/json", (request, response) -> {
|
||||
/**
|
||||
* strangely enough the Echo sends a content type of application/x-www-form-urlencoded even though
|
||||
* it sends a json object
|
||||
*/
|
||||
String userId = request.params(":userid");
|
||||
String lightId = request.params(":id");
|
||||
log.info("hue state change requested: " + userId + " from " + request.ip());
|
||||
log.info("hue stage change body: " + request.body() );
|
||||
|
||||
DeviceState state = null;
|
||||
try {
|
||||
state = mapper.readValue(request.body(), DeviceState.class);
|
||||
} catch (IOException e) {
|
||||
log.info("object mapper barfed on input", e);
|
||||
response.status(400);
|
||||
return null;
|
||||
}
|
||||
|
||||
DeviceDescriptor device = repository.findOne(lightId);
|
||||
if (device == null) {
|
||||
response.status(404);
|
||||
return null;
|
||||
}
|
||||
|
||||
String responseString;
|
||||
String url;
|
||||
if (state.isOn()) {
|
||||
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":true}}]";
|
||||
url = device.getOnUrl();
|
||||
} else {
|
||||
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":false}}]";
|
||||
url = device.getOffUrl();
|
||||
}
|
||||
|
||||
/* light weight templating here, was going to use free marker but it was a bit too
|
||||
* heavy for what we were trying to do.
|
||||
*
|
||||
* currently provides only two variables:
|
||||
* intensity.byte : 0-255 brightness. this is raw from the echo
|
||||
* intensity.percent : 0-100, adjusted for the vera
|
||||
*/
|
||||
if(url.contains(INTENSITY_BYTE)){
|
||||
String intensityByte = String.valueOf(state.getBri());
|
||||
url = url.replace(INTENSITY_BYTE, intensityByte);
|
||||
}else if(url.contains(INTENSITY_PERCENT)){
|
||||
int percentBrightness = (int) Math.round(state.getBri()/255.0*100);
|
||||
String intensityPercent = String.valueOf(percentBrightness);
|
||||
url = url.replace(INTENSITY_PERCENT, intensityPercent);
|
||||
}
|
||||
|
||||
//make call
|
||||
if(!doHttpGETRequest(url)){
|
||||
response.status(503);
|
||||
return null;
|
||||
}
|
||||
|
||||
response.status(200);
|
||||
return responseString;
|
||||
});
|
||||
}
|
||||
|
||||
// This function executes the url from the device repository against the vera
|
||||
protected boolean doHttpGETRequest(String url) {
|
||||
log.info("calling GET on URL: " + url);
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
try {
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
log.info("GET on URL responded: " + response.getStatusLine().getStatusCode());
|
||||
if(response.getStatusLine().getStatusCode() == 200){
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error calling out to HA gateway", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package com.bwssytems.HABridge.upnp;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import org.apache.http.conn.util.*;
|
||||
|
||||
|
||||
public class UpnpListener {
|
||||
private Logger log = LoggerFactory.getLogger(UpnpListener.class);
|
||||
private static final int UPNP_DISCOVERY_PORT = 1900;
|
||||
private static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250";
|
||||
|
||||
private int upnpResponsePort;
|
||||
|
||||
private int httpServerPort;
|
||||
|
||||
private String responseAddress;
|
||||
|
||||
public UpnpListener() {
|
||||
super();
|
||||
upnpResponsePort = Integer.valueOf(System.getProperty("upnp.response.port", "50000"));
|
||||
httpServerPort = Integer.valueOf(System.getProperty("server.port", "8080"));
|
||||
responseAddress = System.getProperty("upnp.config.address", "192.168.14.136");
|
||||
}
|
||||
|
||||
public void startListening(){
|
||||
log.info("Starting UPNP Discovery Listener");
|
||||
|
||||
try (DatagramSocket responseSocket = new DatagramSocket(upnpResponsePort);
|
||||
MulticastSocket upnpMulticastSocket = new MulticastSocket(UPNP_DISCOVERY_PORT);) {
|
||||
InetSocketAddress socketAddress = new InetSocketAddress(UPNP_MULTICAST_ADDRESS, UPNP_DISCOVERY_PORT);
|
||||
Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();
|
||||
|
||||
while (ifs.hasMoreElements()) {
|
||||
NetworkInterface xface = ifs.nextElement();
|
||||
Enumeration<InetAddress> addrs = xface.getInetAddresses();
|
||||
String name = xface.getName();
|
||||
int IPsPerNic = 0;
|
||||
|
||||
while (addrs.hasMoreElements()) {
|
||||
InetAddress addr = addrs.nextElement();
|
||||
log.debug(name + " ... has addr " + addr);
|
||||
if (InetAddressUtils.isIPv4Address(addr.getHostAddress())) {
|
||||
IPsPerNic++;
|
||||
}
|
||||
}
|
||||
log.debug("Checking " + name + " to our interface set");
|
||||
if (IPsPerNic > 0) {
|
||||
upnpMulticastSocket.joinGroup(socketAddress, xface);
|
||||
log.debug("Adding " + name + " to our interface set");
|
||||
}
|
||||
}
|
||||
|
||||
while(true){ //trigger shutdown here
|
||||
byte[] buf = new byte[1024];
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||
upnpMulticastSocket.receive(packet);
|
||||
String packetString = new String(packet.getData());
|
||||
if(isSSDPDiscovery(packetString)){
|
||||
log.debug("Got SSDP Discovery packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
|
||||
sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("UpnpListener encountered an error. Shutting down", e);
|
||||
|
||||
}
|
||||
log.info("UPNP Discovery Listener Stopped");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* very naive ssdp discovery packet detection
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
protected boolean isSSDPDiscovery(String body){
|
||||
if(body != null && body.startsWith("M-SEARCH * HTTP/1.1") && body.contains("MAN: \"ssdp:discover\"")){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String discoveryTemplate = "HTTP/1.1 200 OK\r\n" +
|
||||
"CACHE-CONTROL: max-age=86400\r\n" +
|
||||
"EXT:\r\n" +
|
||||
"LOCATION: http://%s:%s/upnp/amazon-ha-bridge/setup.xml\r\n" +
|
||||
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" +
|
||||
"01-NLS: %s\r\n" +
|
||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
|
||||
"USN: uuid:Socket-1_0-221438K0100073::urn:Belkin:device:**\r\n\r\n";
|
||||
protected void sendUpnpResponse(DatagramSocket socket, InetAddress requester, int sourcePort) throws IOException {
|
||||
String discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort, getRandomUUIDString());
|
||||
log.debug("sndUpnpResponse: " + discoveryResponse);
|
||||
DatagramPacket response = new DatagramPacket(discoveryResponse.getBytes(), discoveryResponse.length(), requester, sourcePort);
|
||||
socket.send(response);
|
||||
}
|
||||
|
||||
protected String getRandomUUIDString(){
|
||||
return "88f6698f-2c83-4393-bd03-cd54a9f8595"; // https://xkcd.com/221/
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.bwssytems.HABridge.upnp;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static spark.Spark.get;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class UpnpSettingsResource {
|
||||
private static final String UPNP_CONTEXT = "/upnp";
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(UpnpSettingsResource.class);
|
||||
|
||||
private String hueTemplate = "<?xml version=\"1.0\"?>\n" + "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
|
||||
+ "<specVersion>\n" + "<major>1</major>\n" + "<minor>0</minor>\n" + "</specVersion>\n"
|
||||
+ "<URLBase>http://%s:%s/</URLBase>\n" + // hostname string
|
||||
"<device>\n" + "<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\n"
|
||||
+ "<friendlyName>Amazon-Echo-HA-Bridge (%s)</friendlyName>\n"
|
||||
+ "<manufacturer>Royal Philips Electronics</manufacturer>\n"
|
||||
+ "<manufacturerURL>http://www.armzilla..com</manufacturerURL>\n"
|
||||
+ "<modelDescription>Hue Emulator for Amazon Echo bridge</modelDescription>\n"
|
||||
+ "<modelName>Philips hue bridge 2012</modelName>\n" + "<modelNumber>929000226503</modelNumber>\n"
|
||||
+ "<modelURL>http://www.armzilla.com/amazon-echo-ha-bridge</modelURL>\n"
|
||||
+ "<serialNumber>01189998819991197253</serialNumber>\n"
|
||||
+ "<UDN>uuid:88f6698f-2c83-4393-bd03-cd54a9f8595</UDN>\n" + "<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"
|
||||
+ "<presentationURL>index.html</presentationURL>\n" + "<iconList>\n" + "<icon>\n"
|
||||
+ "<mimetype>image/png</mimetype>\n" + "<height>48</height>\n" + "<width>48</width>\n"
|
||||
+ "<depth>24</depth>\n" + "<url>hue_logo_0.png</url>\n" + "</icon>\n" + "<icon>\n"
|
||||
+ "<mimetype>image/png</mimetype>\n" + "<height>120</height>\n" + "<width>120</width>\n"
|
||||
+ "<depth>24</depth>\n" + "<url>hue_logo_3.png</url>\n" + "</icon>\n" + "</iconList>\n" + "</device>\n"
|
||||
+ "</root>\n";
|
||||
|
||||
public UpnpSettingsResource() {
|
||||
super();
|
||||
setupListener();
|
||||
}
|
||||
|
||||
private void setupListener () {
|
||||
// http://ip_address:port/upnp/:id/setup.xml which returns the xml configuration for the location of the hue emulator
|
||||
get(UPNP_CONTEXT + "/:id/setup.xml", "application/xml", (request, response) -> {
|
||||
log.info("upnp device settings requested: " + request.params(":id") + " from " + request.ip());
|
||||
String hostName = System.getProperty("upnp.config.address", "192.168.1.1");
|
||||
String portNumber = Integer.toString(request.port());
|
||||
String filledTemplate = String.format(hueTemplate, hostName, portNumber, hostName);
|
||||
log.debug("upnp device settings response: " + filledTemplate);
|
||||
response.status(201);
|
||||
|
||||
return filledTemplate;
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
upnp.response.port=50000
|
||||
server.port=8080
|
||||
upnp.config.address=192.168.4.136
|
||||
1
src/main/resources/config.data
Normal file
1
src/main/resources/config.data
Normal file
File diff suppressed because one or more lines are too long
18
src/main/resources/logback.xml
Normal file
18
src/main/resources/logback.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d [%thread] %-5level %logger - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="CYCLIC" class="ch.qos.logback.core.read.CyclicBufferAppender">
|
||||
<MaxSize>512</MaxSize>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="CYCLIC" />
|
||||
</root>
|
||||
</configuration>
|
||||
6
src/main/resources/public/css/bootstrap-theme.min.css
vendored
Normal file
6
src/main/resources/public/css/bootstrap-theme.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
5
src/main/resources/public/css/bootstrap.min.css
vendored
Normal file
5
src/main/resources/public/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
11
src/main/resources/public/css/main.css
Normal file
11
src/main/resources/public/css/main.css
Normal file
@@ -0,0 +1,11 @@
|
||||
body {
|
||||
padding-top: 60px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.sortorder:after {
|
||||
content: '\25b2';
|
||||
}
|
||||
.sortorder.reverse:after {
|
||||
content: '\25bc';
|
||||
}
|
||||
1
src/main/resources/public/css/ngDialog-theme-default.min.css
vendored
Normal file
1
src/main/resources/public/css/ngDialog-theme-default.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
@-webkit-keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}.ngdialog.ngdialog-theme-default{padding-bottom:160px;padding-top:160px}.ngdialog.ngdialog-theme-default.ngdialog-closing .ngdialog-content{-webkit-animation:ngdialog-flyout .5s;animation:ngdialog-flyout .5s}.ngdialog.ngdialog-theme-default .ngdialog-content{-webkit-animation:ngdialog-flyin .5s;animation:ngdialog-flyin .5s;background:#f0f0f0;border-radius:5px;color:#444;font-family:Helvetica,sans-serif;font-size:1.1em;line-height:1.5em;margin:0 auto;max-width:100%;padding:1em;position:relative;width:450px}.ngdialog.ngdialog-theme-default .ngdialog-close{border-radius:5px;cursor:pointer;position:absolute;right:0;top:0}.ngdialog.ngdialog-theme-default .ngdialog-close:before{background:0 0;border-radius:3px;color:#bbb;content:'\00D7';font-size:26px;font-weight:400;height:30px;line-height:26px;position:absolute;right:3px;text-align:center;top:3px;width:30px}.ngdialog.ngdialog-theme-default .ngdialog-close:active:before,.ngdialog.ngdialog-theme-default .ngdialog-close:hover:before{color:#777}.ngdialog.ngdialog-theme-default .ngdialog-message{margin-bottom:.5em}.ngdialog.ngdialog-theme-default .ngdialog-input{margin-bottom:1em}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url],.ngdialog.ngdialog-theme-default .ngdialog-input textarea{background:#fff;border:0;border-radius:3px;font-family:inherit;font-size:inherit;font-weight:inherit;margin:0 0 .25em;min-height:2.5em;padding:.25em .67em;width:100%}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input textarea:focus{box-shadow:inset 0 0 0 2px #8dbdf1;outline:0}.ngdialog.ngdialog-theme-default .ngdialog-buttons:after{content:'';display:table;clear:both}.ngdialog.ngdialog-theme-default .ngdialog-button{border:0;border-radius:3px;cursor:pointer;float:right;font-family:inherit;font-size:.8em;letter-spacing:.1em;line-height:1em;margin:0 0 0 .5em;padding:.75em 2em;text-transform:uppercase}.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:ngdialog-pulse 1.1s infinite;animation:ngdialog-pulse 1.1s infinite;outline:0}@media (max-width:568px){.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:none;animation:none}}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-primary{background:#3288e6;color:#fff}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-secondary{background:#e0e0e0;color:#777}
|
||||
1
src/main/resources/public/css/ngDialog.min.css
vendored
Normal file
1
src/main/resources/public/css/ngDialog.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.ngdialog,.ngdialog-overlay{position:fixed;top:0;right:0;bottom:0;left:0}@-webkit-keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}@keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}.ngdialog{box-sizing:border-box;overflow:auto;-webkit-overflow-scrolling:touch;z-index:10000}.ngdialog *,.ngdialog :after,.ngdialog :before{box-sizing:inherit}.ngdialog.ngdialog-disabled-animation,.ngdialog.ngdialog-disabled-animation .ngdialog-content,.ngdialog.ngdialog-disabled-animation .ngdialog-overlay{-webkit-animation:none!important;animation:none!important}.ngdialog-overlay{background:rgba(0,0,0,.4);-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s}.ngdialog-no-overlay{pointer-events:none}.ngdialog.ngdialog-closing .ngdialog-overlay{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-content{background:#fff;-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s;pointer-events:all}.ngdialog.ngdialog-closing .ngdialog-content{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-close:before{font-family:Helvetica,Arial,sans-serif;content:'\00D7';cursor:pointer}body.ngdialog-open,html.ngdialog-open{overflow:hidden}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user