Merge pull request #586 from bwssytems/SecurityImpl

Merge security impl branch

BridgeSettings.configWriter needs synchonized question
Fixes #571 

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

User authentication... question
Fixes #270 

Security VS habridge enhancement question
Fixes #390 

issue tcp command bug
Fixes #564 

Device inactive not working bug
Fixes #565 opened 13 days ago
This commit is contained in:
BWS Systems
2017-04-07 11:55:51 -05:00
committed by GitHub
48 changed files with 2036 additions and 378 deletions

View File

@@ -59,23 +59,23 @@ ATTENTION: This requires JDK 1.8 to run
ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below.
```
java -jar ha-bridge-4.3.1.jar
java -jar ha-bridge-4.5.0.jar
```
### Automation on Linux systems
To have this configured and running automatically there are a few resources to use. One is using Docker and a docker container has been built for this and can be gotten here: https://github.com/aptalca/docker-ha-bridge
Create the directory and make sure that ha-bridge-4.3.1.jar is in your /home/pi/habridge directory.
Create the directory and make sure that ha-bridge-4.5.0.jar is in your /home/pi/habridge directory.
```
pi@raspberrypi:~ $ mkdir habridge
pi@raspberrypi:~ $ cd habridge
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.3.1/ha-bridge-4.3.1.jar
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.5.0/ha-bridge-4.5.0.jar
```
Create the directory and make sure that ha-bridge-4.3.1.jar is in your /home/pi/habridge directory.
Create the directory and make sure that ha-bridge-4.5.0.jar is in your /home/pi/habridge directory.
```
pi@raspberrypi:~ $ mkdir habridge
pi@raspberrypi:~ $ cd habridge
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.3.1/ha-bridge-4.3.1.jar
pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.5.0/ha-bridge-4.5.0.jar
```
#### System Control Setup on a pi (preferred)
For next gen Linux systems (this includes the Raspberry Pi), here is a systemctl unit file that you can install. Here is a link on how to do this: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
@@ -94,8 +94,8 @@ After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.3.1.jar
WorkingDirectory=/home/pi/habridge
ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.5.0.jar
[Install]
WantedBy=multi-user.target
@@ -130,7 +130,7 @@ Then cut and past this, modify any locations that are not correct
```
cd /home/pi/habridge
rm /home/pi/habridge/habridge-log.txt
nohup java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.3.1.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
nohup java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.5.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 &
chmod 777 /home/pi/habridge/habridge-log.txt
```
@@ -213,6 +213,16 @@ The default ip address for the bridge to listen on is all interfaces (0.0.0.0).
```
java -jar -Dserver.ip=192.168.1.1 ha-bridge-W.X.Y.jar
```
### -Dsecurity.key=`<Your Key To Encrypt Security Data>`
This option is very important to set if you will be using username/passwords to secure the ha-bridge. The ha-bridge needs to encrypt the settings in the config file and to make sure they are secured specifically to you is to provide this key. Otherwise a default key is used and it is available in the code on github for the ha-bridge here, so not very secure in that sense. **It is very important provide this if you are using username/password.** To override the default, specify -Dsecurity.key=`<Your Key To Encrypt Security Data>` explicitly on the command line. This is will prevent any issues if your config file gets hacked. The command line example:
```
java -jar -Dsecurity.key=Xfawer354WertSdf321234asd ha-bridge-W.X.Y.jar
```
### -Dexec.garden=`<The path to your scripts and program directory>`
This sets a directory of your choosing to have a walled area for what can be executed by the Exec Command type. This is a good feature to use if you use the capabilities of executing a script or program from the ha-bridge. The default is not set which allows any program or script to be called and anyone with access to the your system could create an exec command call and execute it from the api. This is will prevent any issues if your system gets hacked. To override the default, specify -Dexec.garden=`<The path to your scripts and program directory>` explicitly on the command line. The command line example:
```
java -jar -Dexec.garden=C:\Users\John\bin
```
## HA Bridge Usage and Configuration
This section will cover the basics of configuration and where this configuration can be done. This requires that you have started your bridge process and then have pointed your
favorite web interface by going to the http://<my ip address>:<port> or http://localhost:<port> with port you have assigned. The default quick link is http://localhost for your reference.
@@ -220,12 +230,18 @@ favorite web interface by going to the http://<my ip address>:<port> or http://l
This screen allows you to see your devices you have configured for the ha-bridge to present to a controller, such as an Amazon Echo/Dot. It gives you a count of devices as there have been reports that the Echo only supports a limited number, but has been growing as of late, YMMV. You can test each device from this page as this calls the ha-bridge just as a controller would, i.e. the Echo. This is useful to make sure your configuration for each device is correct and for trouble shooting. You can also manages your devices as well by editing and making a new device copy as well as deleting it.
At the bottom of the screen is the "Bridge Device DB Backup" which can be accessed with clicking on the `+` to expand this frame. Here you can backup and restore configurations that you have saved. These configs can be named or by clicking the `Backup Device DB' button will create a backup and name it for you. You can manage these backups by restoring them or deleting them.
#### Renumber Devices
This changes the numbering of the added devices to start at 1 and goes up from there. It was originally intended for a conversion from the previous system version that used large numbers and was not necessary. This also allows the system to try and number sequentially. If you use this button, you will need to re-discover your devices as their ID's will have changed.
#### Link
If this is present, you have enabled the ue link button feature for the ha-bridge. If you want a new system to recognize the ha-bridge, you will need to press this button when you are doign a discovery.
### The Bridge Control Tab
This is where all of the configuration occurs for what ports and IP's the bridge runs on. It also contains the configurations for target devices so that Helper Tabs for configuration can be added as well as the connection information to control those devices.
#### Bridge server
This field is used to test the bridge server with the UPNP IP Address and to make sure that the bridge is responding.
#### Bridge Control Buttons
These buttons are for managing the bridge. The Save button is enabled when there is a change to the configuration. The Bridge Reinitialize button will recycle the internal running of the bridge in the java process. The Stop button will stop the java process. The Refresh button will refresh the page and settings.
#### The Security Dialog
This is where you can set the different security settings for the ha-bridge. There are two settings, one for enabling Hue like operation to secure the Hue api with the internally generated user for the calls that are done after the link button. The other is to secure the hue api with a username/password that is created as well. The other fields are to add and delete users and to set and change passwords for those users. If there are no users in the system, the system will not require a username/password to operate.
#### Configuration Path and File
The default location for the configuration file to contain the settings for the bridge is the relative path from where the bridge is started in "data/habridge.config". If you would like a different filename or directory, specify `<directory>/<filename>` explicitly.
#### Device DB Path and File
@@ -307,7 +323,9 @@ Example from device.db:
[{"item":<a String that is quoted or another JSON object>,"type":"<atype>"."count":X."delay":X."filterIPs":"<comma separated list of IP addresses that are valid>"."httpVerb":"<GET,PUT,POST>","httpBody":"<body info>","httpHeaders":[{"name":"header name","value":"header value"},{"name":"another header","value":"another value"}],"contentType":"<http content type i.e application/json>"},{"item":<another item>,"type":"<aType>"}]
```
The Add/Edit tab will show you the fields to fill in for the above in a form, when you hcae completed putting in the things you want, make sure to hit the `Add` button at the right.
The format of the example is in JSON where the JSON tags equate to the UI labels of the On Items/Dim Items/Off Items. i.e.: JSON item = UI Target Item, JSON type = UI Type, etc...
The Add/Edit tab will show you the fields to fill in for the above in a form, when you have completed putting in the things you want, make sure to hit the `Add` button at the right.
The format of the item can be the default HTTP request which executes the URLs formatted as `http://<your stuff here>` as a GET. Other options to this are to select the HTTP Verb and add the data type and add a body that is passed with the request. Secure https is supported as well, just use `https://<your secure call here>`. When using POST and PUT, you have the ability to specify the body that will be sent with the request as well as the application type for the http call.

View File

@@ -5,7 +5,7 @@
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>4.3.1</version>
<version>4.5.0</version>
<packaging>jar</packaging>
<name>HA Bridge</name>

View File

@@ -3,6 +3,7 @@ package com.bwssystems.HABridge;
public class BridgeControlDescriptor {
private boolean reinit;
private boolean stop;
private boolean linkButton;
public BridgeControlDescriptor() {
super();
@@ -22,4 +23,12 @@ public class BridgeControlDescriptor {
public void setStop(boolean stop) {
this.stop = stop;
}
public boolean isLinkButton() {
return linkButton;
}
public void setLinkButton(boolean linkButton) {
this.linkButton = linkButton;
}
}

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
package com.bwssystems.HABridge;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
@@ -10,6 +11,9 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.PosixFilePermission;
import java.security.GeneralSecurityException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
@@ -26,12 +30,20 @@ public class BridgeSettings extends BackupHandler {
private static final Logger log = LoggerFactory.getLogger(BridgeSettings.class);
private BridgeSettingsDescriptor theBridgeSettings;
private BridgeControlDescriptor bridgeControl;
private BridgeSecurity bridgeSecurity;
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
public BridgeSettings() {
super();
bridgeControl = new BridgeControlDescriptor();
theBridgeSettings = new BridgeSettingsDescriptor();
String ipV6Stack = System.getProperty("ipV6Stack");
bridgeSecurity = null;
String theKey = System.getProperty("security.key");
if(theKey == null)
theKey = "IWantMyPasswordsToBeAbleToBeDecodedPleaseSeeTheReadme";
String execGarden = System.getProperty("exec.garden");
bridgeSecurity = new BridgeSecurity(theKey.toCharArray(), execGarden);
String ipV6Stack = System.getProperty("ipV6Stack");
if(ipV6Stack == null || !ipV6Stack.equalsIgnoreCase("true")) {
System.setProperty("java.net.preferIPv4Stack" , "true");
}
@@ -43,6 +55,13 @@ public class BridgeSettings extends BackupHandler {
public BridgeSettingsDescriptor getBridgeSettingsDescriptor() {
return theBridgeSettings;
}
public BridgeSecurity getBridgeSecurity() {
return bridgeSecurity;
}
public static String getCurrentDate() {
return dateFormat.format(new Date());
}
public void buildSettings() {
String addressString = null;
String theVeraAddress = null;
@@ -128,7 +147,7 @@ public class BridgeSettings extends BackupHandler {
theBridgeSettings.setNestpwd(System.getProperty("nest.pwd"));
}
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().equals("")) {
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().trim().equals("") || theBridgeSettings.getUpnpConfigAddress().trim().equals("0.0.0.0")) {
addressString = checkIpAddress(null, true);
if(addressString != null) {
theBridgeSettings.setUpnpConfigAddress(addressString);
@@ -174,7 +193,12 @@ public class BridgeSettings extends BackupHandler {
theBridgeSettings.setWebaddress(serverIpOverride);
setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-");
setupInternalTestUser();
bridgeSecurity.setSecurityData(theBridgeSettings.getSecurityData());
if(theBridgeSettings.getWhitelist() != null) {
bridgeSecurity.convertWhitelist(theBridgeSettings.getWhitelist());
theBridgeSettings.removeWhitelist();
updateConfigFile();
}
}
public void loadConfig() {
@@ -203,6 +227,18 @@ public class BridgeSettings extends BackupHandler {
log.debug("Save HA Bridge settings.");
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
JsonTransformer aRenderer = new JsonTransformer();
if(bridgeSecurity.isSettingsChanged()) {
try {
newBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData());
} catch (UnsupportedEncodingException e) {
log.warn("could not get encoded security data: " + e.getMessage());
return;
} catch (GeneralSecurityException e) {
log.warn("could not get encoded security data: " + e.getMessage());
return;
}
bridgeSecurity.setSettingsChanged(false);
}
String jsonValue = aRenderer.render(newBridgeSettings);
configWriter(jsonValue, configPath);
_loadConfig(configPath);
@@ -213,13 +249,25 @@ public class BridgeSettings extends BackupHandler {
log.debug("Save HA Bridge settings.");
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
JsonTransformer aRenderer = new JsonTransformer();
if(bridgeSecurity.isSettingsChanged()) {
try {
theBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData());
} catch (UnsupportedEncodingException e) {
log.warn("could not get encoded security data: " + e.getMessage());
return;
} catch (GeneralSecurityException e) {
log.warn("could not get encoded security data: " + e.getMessage());
return;
}
bridgeSecurity.setSettingsChanged(false);
}
String jsonValue = aRenderer.render(theBridgeSettings);
configWriter(jsonValue, configPath);
_loadConfig(configPath);
}
private void configWriter(String content, Path filePath) {
private synchronized void configWriter(String content, Path filePath) {
if(Files.exists(filePath) && !Files.isWritable(filePath)){
log.error("Error file is not writable: " + filePath);
return;
@@ -236,7 +284,7 @@ public class BridgeSettings extends BackupHandler {
try {
Path target = null;
if(Files.exists(filePath)) {
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "habridge.config.old");
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "habridge.config.old." + getCurrentDate());
Files.move(filePath, target);
}
Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE);
@@ -249,7 +297,9 @@ public class BridgeSettings extends BackupHandler {
perms.add(PosixFilePermission.OWNER_WRITE);
try {
Files.setPosixFilePermissions(filePath, perms);
String osName = System.getProperty("os.name");
if(osName.toLowerCase().indexOf("win") < 0)
Files.setPosixFilePermissions(filePath, perms);
} catch(UnsupportedOperationException e) {
log.info("Cannot set permissions for config file on this system as it is not supported. Continuing");
}
@@ -284,6 +334,7 @@ public class BridgeSettings extends BackupHandler {
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) {
@@ -310,9 +361,4 @@ public class BridgeSettings extends BackupHandler {
}
return addressString;
}
private void setupInternalTestUser() {
theBridgeSettings.setupInternalTestUser();
if(theBridgeSettings.isSettingsChanged())
this.updateConfigFile();
}
}

View File

@@ -1,57 +1,104 @@
package com.bwssystems.HABridge;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.UUID;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.bwssystems.HABridge.api.hue.HueConstants;
import com.bwssystems.HABridge.api.hue.HueError;
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
import com.bwssystems.HABridge.api.hue.WhitelistEntry;
public class BridgeSettingsDescriptor {
private static final String DEFAULT_INTERNAL_USER = "thehabridgeuser";
private static final String DEFAULT_USER_DESCRIPTION = "default_test_user";
@SerializedName("upnpconfigaddress")
@Expose
private String upnpconfigaddress;
@SerializedName("serverport")
@Expose
private Integer serverport;
@SerializedName("upnpresponseport")
@Expose
private Integer upnpresponseport;
@SerializedName("upnpdevicedb")
@Expose
private String upnpdevicedb;
@SerializedName("veraaddress")
@Expose
private IpList veraaddress;
@SerializedName("harmonyaddress")
@Expose
private IpList harmonyaddress;
@SerializedName("buttonsleep")
@Expose
private Integer buttonsleep;
@SerializedName("upnpstrict")
@Expose
private boolean upnpstrict;
@SerializedName("traceupnp")
@Expose
private boolean traceupnp;
@SerializedName("nestuser")
@Expose
private String nestuser;
@SerializedName("nestpwd")
@Expose
private String nestpwd;
@SerializedName("farenheit")
@Expose
private boolean farenheit;
@SerializedName("configfile")
@Expose
private String configfile;
@SerializedName("numberoflogmessages")
@Expose
private Integer numberoflogmessages;
@SerializedName("hueaddress")
@Expose
private IpList hueaddress;
@SerializedName("haladdress")
@Expose
private IpList haladdress;
@SerializedName("haltoken")
@Expose
private String haltoken;
@SerializedName("whitelist")
@Expose
private Map<String, WhitelistEntry> whitelist;
@SerializedName("myechourl")
@Expose
private String myechourl;
@SerializedName("webaddress")
@Expose
private String webaddress;
@SerializedName("mqttaddress")
@Expose
private IpList mqttaddress;
@SerializedName("hassaddress")
@Expose
private IpList hassaddress;
@SerializedName("domoticzaddress")
@Expose
private IpList domoticzaddress;
@SerializedName("somfyaddress")
@Expose
private IpList somfyaddress;
@SerializedName("hubversion")
@Expose
private String hubversion;
@SerializedName("securityData")
@Expose
private String securityData;
private boolean settingsChanged;
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 nestconfigured;
private boolean halconfigured;
private Map<String, WhitelistEntry> whitelist;
private boolean settingsChanged;
private String myechourl;
private String webaddress;
private IpList mqttaddress;
private boolean mqttconfigured;
private IpList hassaddress;
private boolean hassconfigured;
private String hubversion;
private IpList domoticzaddress;
private boolean domoticzconfigured;
private IpList somfyaddress;
private boolean somfyconfigured;
private boolean lifxconfigured;
public BridgeSettingsDescriptor() {
@@ -226,8 +273,8 @@ public class BridgeSettingsDescriptor {
public Map<String, WhitelistEntry> getWhitelist() {
return whitelist;
}
public void setWhitelist(Map<String, WhitelistEntry> whitelist) {
this.whitelist = whitelist;
protected void removeWhitelist() {
whitelist = null;
}
public boolean isSettingsChanged() {
return settingsChanged;
@@ -295,6 +342,12 @@ public class BridgeSettingsDescriptor {
public void setLifxconfigured(boolean lifxconfigured) {
this.lifxconfigured = lifxconfigured;
}
public String getSecurityData() {
return securityData;
}
public void setSecurityData(String securityData) {
this.securityData = securityData;
}
public Boolean isValidVera() {
if(this.getVeraAddress() == null || this.getVeraAddress().getDevices().size() <= 0)
return false;
@@ -371,80 +424,4 @@ public class BridgeSettingsDescriptor {
public Boolean isValidLifx() {
return this.isLifxconfigured();
}
public HueError[] validateWhitelistUser(String aUser, String userDescription, boolean strict) {
String validUser = null;
boolean found = false;
if (aUser != null && !aUser.equalsIgnoreCase("undefined") && !aUser.equalsIgnoreCase("null")
&& !aUser.equalsIgnoreCase("")) {
if (whitelist != null) {
Set<String> theUserIds = whitelist.keySet();
Iterator<String> userIterator = theUserIds.iterator();
while (userIterator.hasNext()) {
validUser = userIterator.next();
if (validUser.equals(aUser))
found = true;
}
}
}
if(!found && !strict) {
newWhitelistUser(aUser, userDescription);
found = true;
}
if (!found) {
return HueErrorResponse.createResponse("1", "/api/" + aUser, "unauthorized user", null, null, null).getTheErrors();
}
return null;
}
public void newWhitelistUser(String aUser, String userDescription) {
if (whitelist == null) {
whitelist = new HashMap<>();
}
if(userDescription == null)
userDescription = "auto insert user";
whitelist.put(aUser, WhitelistEntry.createEntry(userDescription));
setSettingsChanged(true);
}
public String createWhitelistUser(String userDescription) {
String aUser = getNewUserID();
newWhitelistUser(aUser, userDescription);
return aUser;
}
private String getNewUserID() {
UUID uid = UUID.randomUUID();
StringTokenizer st = new StringTokenizer(uid.toString(), "-");
String newUser = "";
while (st.hasMoreTokens()) {
newUser = newUser + st.nextToken();
}
return newUser;
}
public String getInternalTestUser() {
return DEFAULT_INTERNAL_USER;
}
public void setupInternalTestUser() {
boolean found = false;
if(whitelist != null) {
for (String key : whitelist.keySet()) {
if(key.equals(DEFAULT_INTERNAL_USER)) {
found = true;
break;
}
}
}
if(!found) {
newWhitelistUser(DEFAULT_INTERNAL_USER, DEFAULT_USER_DESCRIPTION);
}
}
}

View File

@@ -45,6 +45,8 @@ public class HABridge {
log.info("HA Bridge (v" + theVersion.getVersion() + ") starting....");
bridgeSettings = new BridgeSettings();
// sparkjava config directive to set html static file location for Jetty
staticFileLocation("/public");
while(!bridgeSettings.getBridgeControl().isStop()) {
bridgeSettings.buildSettings();
log.info("HA Bridge initializing....");
@@ -52,8 +54,9 @@ public class HABridge {
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");
if(!bridgeSettings.getBridgeControl().isReinit())
init();
bridgeSettings.getBridgeControl().setReinit(false);
// setup system control api first
theSystem = new SystemControl(bridgeSettings, theVersion);
theSystem.setupServer();
@@ -65,9 +68,9 @@ public class HABridge {
else {
//Setup the device connection homes through the manager
homeManager = new HomeManager();
homeManager.buildHomes(bridgeSettings.getBridgeSettingsDescriptor(), udpSender);
homeManager.buildHomes(bridgeSettings, udpSender);
// setup the class to handle the resource setup rest api
theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), homeManager);
theResources = new DeviceResource(bridgeSettings, homeManager);
// setup the class to handle the upnp response rest api
theSettingResponder = new UpnpSettingsResource(bridgeSettings.getBridgeSettingsDescriptor());
theSettingResponder.setupServer();
@@ -89,9 +92,19 @@ public class HABridge {
udpSender.closeResponseSocket();
udpSender = null;
}
bridgeSettings.getBridgeControl().setReinit(false);
stop();
if(!bridgeSettings.getBridgeControl().isStop()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
bridgeSettings.getBridgeSecurity().removeTestUsers();
if(bridgeSettings.getBridgeSecurity().isSettingsChanged())
bridgeSettings.updateConfigFile();
log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting....");
System.exit(0);
}

View File

@@ -4,6 +4,6 @@ import com.bwssystems.HABridge.devicemanagmeent.ResourceHandler;
import com.bwssystems.HABridge.hue.HueMulatorHandler;
public interface Home extends HueMulatorHandler, ResourceHandler {
public Home createHome(BridgeSettingsDescriptor bridgeSettings);
public Home createHome(BridgeSettings bridgeSettings);
public void closeHome();
}

View File

@@ -31,7 +31,7 @@ public class HomeManager {
}
// factory method
public void buildHomes(BridgeSettingsDescriptor bridgeSettings, UDPDatagramSender aUdpDatagramSender) {
public void buildHomes(BridgeSettings bridgeSettings, UDPDatagramSender aUdpDatagramSender) {
Home aHome = null;
//setup the harmony connection if available
aHome = new HarmonyHome(bridgeSettings);

View File

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

View File

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

View File

@@ -0,0 +1,26 @@
package com.bwssystems.HABridge;
public class SecurityInfo {
private boolean useLinkButton;
private boolean secureHueApi;
private boolean isSecure;
public boolean isUseLinkButton() {
return useLinkButton;
}
public void setUseLinkButton(boolean useLinkButton) {
this.useLinkButton = useLinkButton;
}
public boolean isSecureHueApi() {
return secureHueApi;
}
public void setSecureHueApi(boolean secureHueApi) {
this.secureHueApi = secureHueApi;
}
public boolean isSecure() {
return isSecure;
}
public void setSecure(boolean isSecure) {
this.isSecure = isSecure;
}
}

View File

@@ -4,6 +4,8 @@ import static spark.Spark.get;
import static spark.Spark.options;
import static spark.Spark.post;
import static spark.Spark.put;
import static spark.Spark.before;
import static spark.Spark.halt;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -12,6 +14,8 @@ import java.net.MulticastSocket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Timer;
import java.util.Base64;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
@@ -55,24 +59,28 @@ public class SystemControl {
// This function sets up the sparkjava rest calls for the hue api
public void setupServer() {
log.info("System control service started....");
before(SYSTEM_CONTEXT + "/*", (request, response) -> {
if(bridgeSettings.getBridgeSecurity().isSecure()) {
String pathInfo = request.pathInfo();
if(pathInfo == null || (!pathInfo.equals(SYSTEM_CONTEXT + "/login") && !pathInfo.equals(SYSTEM_CONTEXT + "/habridge/version"))) {
User authUser = bridgeSettings.getBridgeSecurity().getAuthenticatedUser(request);
if(authUser == null) {
halt(401, "{\"message\":\"User not authenticated\"}");
}
}
}
});
// http://ip_address:port/system/habridge/version gets the version of this bridge instance
get (SYSTEM_CONTEXT + "/habridge/version", "application/json", (request, response) -> {
get (SYSTEM_CONTEXT + "/habridge/version", (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/habridge/testuser gets the valid test user for calling the api
get (SYSTEM_CONTEXT + "/habridge/testuser", "application/json", (request, response) -> {
log.debug("Get HA Bridge testuser: " + bridgeSettings.getBridgeSettingsDescriptor().getInternalTestUser());
response.status(HttpStatus.SC_OK);
return "{\"user\":\"" + bridgeSettings.getBridgeSettingsDescriptor().getInternalTestUser() + "\"}";
response.type("application/json");
return "{\"version\":\"" + version.getVersion() + "\",\"isSecure\":" + bridgeSettings.getBridgeSecurity().isSecure() + "}";
});
// http://ip_address:port/system/logmsgs gets the log messages for the bridge
get (SYSTEM_CONTEXT + "/logmsgs", "application/json", (request, response) -> {
get (SYSTEM_CONTEXT + "/logmsgs", (request, response) -> {
log.debug("Get logmsgs.");
response.status(HttpStatus.SC_OK);
String logMsgs;
int count = -1;
if(cyclicBufferAppender == null)
@@ -93,24 +101,190 @@ public class SystemControl {
}
}
logMsgs = logMsgs + "]";
response.status(200);
response.status(HttpStatus.SC_OK);
response.type("application/json");
return logMsgs;
});
// http://ip_address:port/system/logmgmt/loggers gets the logger info for the bridge
get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", "application/json", (request, response) -> {
get (SYSTEM_CONTEXT + "/logmgmt/loggers/:all", (request, response) -> {
log.debug("Get loggers info with showAll argument: " + request.params(":all"));
Boolean showAll = false;
if(request.params(":all").equals("true"))
showAll = true;
theLogServiceMgr.setShowAll(showAll);
theLogServiceMgr.init();
response.status(200);
response.status(HttpStatus.SC_OK);
response.type("application/json");
return theLogServiceMgr.getConfiguredLoggers();
}, new JsonTransformer());
// http://ip_address:port/system/setpassword CORS request
options(SYSTEM_CONTEXT + "/setpassword", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/setpassword which sets a password for a given user
post(SYSTEM_CONTEXT + "/setpassword", (request, response) -> {
log.debug("setpassword....");
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
String errorMessage = bridgeSettings.getBridgeSecurity().setPassword(theUser);
if(errorMessage != null) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
} else {
response.status(HttpStatus.SC_OK);
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
}
if(errorMessage == null)
errorMessage = "{}";
response.type("application/json");
return errorMessage;
});
// http://ip_address:port/system/adduser CORS request
options(SYSTEM_CONTEXT + "/adduser", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/adduser which adds a new user
put(SYSTEM_CONTEXT + "/adduser", (request, response) -> {
log.debug("adduser....");
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
String errorMessage = theUser.validate();
if(errorMessage != null) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
} else {
errorMessage = bridgeSettings.getBridgeSecurity().addUser(theUser);
if(errorMessage == null) {
response.status(HttpStatus.SC_OK);
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
} else {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
}
}
if(errorMessage == null)
errorMessage = "{}";
response.type("application/json");
return errorMessage;
});
// http://ip_address:port/system/deluser CORS request
options(SYSTEM_CONTEXT + "/deluser", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/deluser which dels a user
put(SYSTEM_CONTEXT + "/deluser", (request, response) -> {
log.debug("deluser....");
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
String errorMessage = bridgeSettings.getBridgeSecurity().delUser(theUser);
if(errorMessage != null) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
} else {
response.status(HttpStatus.SC_OK);
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
}
if(errorMessage == null)
errorMessage = "{}";
response.type("application/json");
return errorMessage;
});
// http://ip_address:port/system/login CORS request
options(SYSTEM_CONTEXT + "/login", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/login validates the login
post(SYSTEM_CONTEXT + "/login", (request, response) -> {
log.debug("login....");
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
LoginResult result = bridgeSettings.getBridgeSecurity().validatePassword(theUser);
if(result.getUser() != null)
bridgeSettings.getBridgeSecurity().addAuthenticatedUser(request, theUser);
response.status(HttpStatus.SC_OK);
response.type("application/json");
return result;
}, new JsonTransformer());
// http://ip_address:port/system/presslinkbutton CORS request
options(SYSTEM_CONTEXT + "/presslinkbutton", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/presslinkbutton which sets the link button for device registration
put(SYSTEM_CONTEXT + "/presslinkbutton", (request, response) -> {
log.info("Link button pressed....");
bridgeSettings.getBridgeControl().setLinkButton(true);
Timer theTimer = new Timer();
theTimer.schedule(new LinkButtonPressed(bridgeSettings.getBridgeControl(), theTimer), 30000);
response.status(HttpStatus.SC_OK);
response.type("application/json");
return "";
}, new JsonTransformer());
// http://ip_address:port/system/securityinfo gets the security info for the bridge
get (SYSTEM_CONTEXT + "/securityinfo", (request, response) -> {
log.debug("Get security info");
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSecurity().getSecurityInfo();
}, new JsonTransformer());
// http://ip_address:port/system/changesecurityinfo CORS request
options(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
// http://ip_address:port/system/changesecurityinfo which sets the security settings other than passwords and users
post(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
log.debug("changesecurityinfo....");
SecurityInfo theInfo = new Gson().fromJson(request.body(), SecurityInfo.class);
bridgeSettings.getBridgeSecurity().setUseLinkButton(theInfo.isUseLinkButton());
bridgeSettings.getBridgeSecurity().setSecureHueApi(theInfo.isSecureHueApi());
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSecurity().getSecurityInfo();
}, new JsonTransformer());
// http://ip_address:port/system/logmgmt/update CORS request
options(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
options(SYSTEM_CONTEXT + "/logmgmt/update", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
@@ -119,28 +293,28 @@ public class SystemControl {
return "";
});
// http://ip_address:port/system/logmgmt/update which changes logging parameters for the process
put(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
put(SYSTEM_CONTEXT + "/logmgmt/update", (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();
response.status(HttpStatus.SC_OK);
response.type("application/json");
return theLogServiceMgr.getConfiguredLoggers();
}, new JsonTransformer());
// http://ip_address:port/system/settings which returns the bridge configuration settings
get(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
get(SYSTEM_CONTEXT + "/settings", (request, response) -> {
log.debug("bridge settings requested from " + request.ip());
response.status(200);
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSettingsDescriptor();
}, new JsonTransformer());
// http://ip_address:port/system/settings CORS request
options(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
options(SYSTEM_CONTEXT + "/settings", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
@@ -149,17 +323,17 @@ public class SystemControl {
return "";
});
// http://ip_address:port/system/settings which returns the bridge configuration settings
put(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
put(SYSTEM_CONTEXT + "/settings", (request, response) -> {
log.debug("save bridge settings requested from " + request.ip() + " with body: " + request.body());
BridgeSettingsDescriptor newBridgeSettings = new Gson().fromJson(request.body(), BridgeSettingsDescriptor.class);
bridgeSettings.save(newBridgeSettings);
response.status(200);
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSettingsDescriptor();
}, new JsonTransformer());
// http://ip_address:port/system/control/reinit CORS request
options(SYSTEM_CONTEXT + "/control/reinit", "application/json", (request, response) -> {
options(SYSTEM_CONTEXT + "/control/reinit", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
@@ -168,12 +342,14 @@ public class SystemControl {
return "";
});
// http://ip_address:port/system/control/reinit sets the parameter reinit the server
put(SYSTEM_CONTEXT + "/control/reinit", "application/json", (request, response) -> {
put(SYSTEM_CONTEXT + "/control/reinit", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.type("application/json");
return reinit();
});
// http://ip_address:port/system/control/stop CORS request
options(SYSTEM_CONTEXT + "/control/stop", "application/json", (request, response) -> {
options(SYSTEM_CONTEXT + "/control/stop", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
@@ -182,19 +358,22 @@ public class SystemControl {
return "";
});
// http://ip_address:port/system/control/stop sets the parameter stop the server
put(SYSTEM_CONTEXT + "/control/stop", "application/json", (request, response) -> {
put(SYSTEM_CONTEXT + "/control/stop", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.type("application/json");
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) -> {
get (SYSTEM_CONTEXT + "/backup/available", (request, response) -> {
log.debug("Get backup filenames");
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBackups();
}, new JsonTransformer());
// http://ip_address:port/system/backup/create CORS request
options(SYSTEM_CONTEXT + "/backup/create", "application/json", (request, response) -> {
options(SYSTEM_CONTEXT + "/backup/create", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "PUT");
@@ -202,16 +381,18 @@ public class SystemControl {
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
put (SYSTEM_CONTEXT + "/backup/create", "application/json", (request, response) -> {
put (SYSTEM_CONTEXT + "/backup/create", (request, response) -> {
log.debug("Create backup: " + request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
BackupFilename returnFilename = new BackupFilename();
returnFilename.setFilename(bridgeSettings.backup(aFilename.getFilename()));
response.status(HttpStatus.SC_OK);
response.type("application/json");
return returnFilename;
}, new JsonTransformer());
// http://ip_address:port/system/backup/delete CORS request
options(SYSTEM_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
options(SYSTEM_CONTEXT + "/backup/delete", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "POST");
@@ -219,18 +400,20 @@ public class SystemControl {
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post (SYSTEM_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
post (SYSTEM_CONTEXT + "/backup/delete", (request, response) -> {
log.debug("Delete backup: " + request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
if(aFilename != null)
bridgeSettings.deleteBackup(aFilename.getFilename());
else
log.warn("No filename given for delete backup.");
return null;
response.status(HttpStatus.SC_OK);
response.type("application/json");
return "";
}, new JsonTransformer());
// http://ip_address:port/system/backup/restore CORS request
options(SYSTEM_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
options(SYSTEM_CONTEXT + "/backup/restore", (request, response) -> {
response.status(HttpStatus.SC_OK);
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
response.header("Access-Control-Allow-Methods", "POST");
@@ -238,7 +421,7 @@ public class SystemControl {
response.header("Content-Type", "text/html; charset=utf-8");
return "";
});
post (SYSTEM_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
post (SYSTEM_CONTEXT + "/backup/restore", (request, response) -> {
log.debug("Restore backup: " + request.body());
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
if(aFilename != null) {
@@ -247,6 +430,8 @@ public class SystemControl {
}
else
log.warn("No filename given for restore backup.");
response.status(HttpStatus.SC_OK);
response.type("application/json");
return bridgeSettings.getBridgeSettingsDescriptor();
}, new JsonTransformer());
}

View File

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

View File

@@ -1,9 +1,11 @@
package com.bwssystems.HABridge.devicemanagmeent;
import static spark.Spark.get;
import static spark.Spark.halt;
import static spark.Spark.options;
import static spark.Spark.post;
import static spark.Spark.put;
import static spark.Spark.before;
import static spark.Spark.delete;
import java.util.Arrays;
@@ -15,9 +17,10 @@ import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.HomeManager;
import com.bwssystems.HABridge.User;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.BackupFilename;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
@@ -36,11 +39,13 @@ public class DeviceResource {
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
private DeviceRepository deviceRepository;
private HomeManager homeManager;
private BridgeSettings bridgeSettings;
private Gson aGsonHandler;
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
public DeviceResource(BridgeSettingsDescriptor theSettings, HomeManager aHomeManager) {
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
public DeviceResource(BridgeSettings theSettings, HomeManager aHomeManager) {
bridgeSettings = theSettings;
this.deviceRepository = new DeviceRepository(bridgeSettings.getBridgeSettingsDescriptor().getUpnpDeviceDb());
homeManager = aHomeManager;
aGsonHandler = new GsonBuilder().create();
setupEndpoints();
@@ -52,6 +57,15 @@ public class DeviceResource {
private void setupEndpoints() {
log.info("HABridge device management service started.... ");
before(API_CONTEXT + "/*", (request, response) -> {
// This never gets called as the HueMulator class covers this path. This is here for backup
if(bridgeSettings.getBridgeSecurity().isSecure()) {
User authUser = bridgeSettings.getBridgeSecurity().getAuthenticatedUser(request);
if(authUser == null) {
halt(401, "{\"message\":\"User not authenticated\"}");
}
}
});
// http://ip_address:port/api/devices CORS request
options(API_CONTEXT, "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);

View File

@@ -4,6 +4,7 @@ import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.HomeManager;
import com.bwssystems.HABridge.User;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.api.UserCreateRequest;
import com.bwssystems.HABridge.api.hue.DeviceResponse;
@@ -22,7 +23,9 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import static spark.Spark.before;
import static spark.Spark.get;
import static spark.Spark.halt;
import static spark.Spark.options;
import static spark.Spark.post;
import static spark.Spark.put;
@@ -65,6 +68,22 @@ public class HueMulator {
// This function sets up the sparkjava rest calls for the hue api
public void setupServer() {
log.info("Hue emulator service started....");
before(HUE_CONTEXT + "/*", (request, response) -> {
if(bridgeSettingMaster.getBridgeSecurity().isSecure()) {
String pathInfo = request.pathInfo();
if(pathInfo != null && pathInfo.contains(HUE_CONTEXT + "/devices")) {
User authUser = bridgeSettingMaster.getBridgeSecurity().getAuthenticatedUser(request);
if(authUser == null) {
halt(401, "{\"message\":\"User not authenticated\"}");
}
} else if (bridgeSettingMaster.getBridgeSecurity().isSecureHueApi()) {
User authUser = bridgeSettingMaster.getBridgeSecurity().getAuthenticatedUser(request);
if(authUser == null) {
halt(401, "{\"message\":\"User not authenticated\"}");
}
}
}
});
// http://ip_address:port/api/{userId}/groups returns json objects of
// all groups configured
get(HUE_CONTEXT + "/:userid/groups", "application/json", (request, response) -> {
@@ -576,9 +595,9 @@ public class HueMulator {
private String basicListHandler(String type, String userId, String requestIp) {
log.debug("hue " + type + " list requested: " + userId + " from " + requestIp);
HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, false);
HueError[] theErrors = bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
if (theErrors != null) {
if(bridgeSettings.isSettingsChanged())
if(bridgeSettingMaster.getBridgeSecurity().isSettingsChanged())
bridgeSettingMaster.updateConfigFile();
return aGsonHandler.toJson(theErrors);
@@ -590,9 +609,9 @@ public class HueMulator {
log.debug("hue group list requested: " + userId + " from " + requestIp);
HueError[] theErrors = null;
Map<String, GroupResponse> groupResponseMap = null;
theErrors = bridgeSettings.validateWhitelistUser(userId, null, false);
theErrors = bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
if (theErrors == null) {
if(bridgeSettings.isSettingsChanged())
if(bridgeSettingMaster.getBridgeSecurity().isSettingsChanged())
bridgeSettingMaster.updateConfigFile();
groupResponseMap = new HashMap<String, GroupResponse>();
@@ -607,9 +626,9 @@ public class HueMulator {
private Object groupsIdHandler(String groupId, String userId, String requestIp) {
log.debug("hue group id: <" + groupId + "> requested: " + userId + " from " + requestIp);
HueError[] theErrors = null;
theErrors = bridgeSettings.validateWhitelistUser(userId, null, false);
theErrors = bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
if (theErrors == null) {
if(bridgeSettings.isSettingsChanged())
if(bridgeSettingMaster.getBridgeSecurity().isSettingsChanged())
bridgeSettingMaster.updateConfigFile();
if (groupId.equalsIgnoreCase("0")) {
@@ -632,9 +651,9 @@ public class HueMulator {
if (bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue lights list requested: " + userId + " from " + requestIp);
log.debug("hue lights list requested: " + userId + " from " + requestIp);
theErrors = bridgeSettings.validateWhitelistUser(userId, null, false);
theErrors = bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
if (theErrors == null) {
if(bridgeSettings.isSettingsChanged())
if(bridgeSettingMaster.getBridgeSecurity().isSettingsChanged())
bridgeSettingMaster.updateConfigFile();
List<DeviceDescriptor> deviceList = repository.findAllByRequester(requestIp);
@@ -642,28 +661,30 @@ public class HueMulator {
deviceResponseMap = new HashMap<String, DeviceResponse>();
for (DeviceDescriptor device : deviceList) {
DeviceResponse deviceResponse = null;
if (device.containsType(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
CallItem[] callItems = null;
try {
if(device.getOnUrl() != null)
callItems = aGsonHandler.fromJson(device.getOnUrl(), CallItem[].class);
} catch(JsonSyntaxException e) {
log.warn("Could not decode Json for url items to get Hue state for device: " + device.getName());
callItems = null;
}
for (int i = 0; callItems != null && i < callItems.length; i++) {
if((callItems[i].getType() != null && callItems[i].getType().equals(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) ||
(callItems[i].getItem().getAsString().contains("hueName"))) {
deviceResponse = myHueHome.getHueDeviceInfo(callItems[i], device);
i = callItems.length;
if(!device.isInactive()) {
if (device.containsType(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) {
CallItem[] callItems = null;
try {
if(device.getOnUrl() != null)
callItems = aGsonHandler.fromJson(device.getOnUrl(), CallItem[].class);
} catch(JsonSyntaxException e) {
log.warn("Could not decode Json for url items to get Hue state for device: " + device.getName());
callItems = null;
}
for (int i = 0; callItems != null && i < callItems.length; i++) {
if((callItems[i].getType() != null && callItems[i].getType().equals(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex])) ||
(callItems[i].getItem().getAsString().contains("hueName"))) {
deviceResponse = myHueHome.getHueDeviceInfo(callItems[i], device);
i = callItems.length;
}
}
}
if (deviceResponse == null)
deviceResponse = DeviceResponse.createResponse(device);
deviceResponseMap.put(device.getId(), deviceResponse);
}
if (deviceResponse == null)
deviceResponse = DeviceResponse.createResponse(device);
deviceResponseMap.put(device.getId(), deviceResponse);
}
}
@@ -677,50 +698,58 @@ public class HueMulator {
UserCreateRequest aNewUser = null;
String newUser = null;
String aDeviceType = null;
boolean toContinue = false;
if (bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue api user create requested: " + body + " from " + ipAddress);
log.debug("hue api user create requested: " + body + " from " + ipAddress);
if (body != null && !body.isEmpty()) {
try {
aNewUser = aGsonHandler.fromJson(body, UserCreateRequest.class);
} catch (Exception e) {
log.warn("Could not add user. Request garbled: " + body);
return aGsonHandler.toJson(HueErrorResponse.createResponse("2", "/",
"Could not add user.", null, null, null).getTheErrors(), HueError[].class);
}
newUser = aNewUser.getUsername();
aDeviceType = aNewUser.getDevicetype();
}
if (aDeviceType == null)
aDeviceType = "<not given>";
if (newUser == null) {
newUser = bridgeSettings.createWhitelistUser(aDeviceType);
}
else {
bridgeSettings.validateWhitelistUser(newUser, aDeviceType, false);
}
if(bridgeSettings.isSettingsChanged())
bridgeSettingMaster.updateConfigFile();
if (bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue api user create requested for device type: " + aDeviceType + " and username: "
+ newUser + (followingSlash ? " /api/ called" : ""));
log.debug("hue api user create requested for device type: " + aDeviceType + " and username: " + newUser + (followingSlash ? " /api/ called" : ""));
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
if(bridgeSettingMaster.getBridgeSecurity().isUseLinkButton() && bridgeSettingMaster.getBridgeControl().isLinkButton())
toContinue = true;
else if(!bridgeSettingMaster.getBridgeSecurity().isUseLinkButton())
toContinue = true;
if(toContinue) {
log.debug("hue api user create requested: " + body + " from " + ipAddress);
if (body != null && !body.isEmpty()) {
try {
aNewUser = aGsonHandler.fromJson(body, UserCreateRequest.class);
} catch (Exception e) {
log.warn("Could not add user. Request garbled: " + body);
return aGsonHandler.toJson(HueErrorResponse.createResponse("2", "/",
"Could not add user.", null, null, null).getTheErrors(), HueError[].class);
}
newUser = aNewUser.getUsername();
aDeviceType = aNewUser.getDevicetype();
}
if (aDeviceType == null)
aDeviceType = "<not given>";
if (newUser == null) {
newUser = bridgeSettingMaster.getBridgeSecurity().createWhitelistUser(aDeviceType);
}
else {
bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(newUser, aDeviceType, false);
}
if(bridgeSettingMaster.getBridgeSecurity().isSettingsChanged())
bridgeSettingMaster.updateConfigFile();
if (bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue api user create requested for device type: " + aDeviceType + " and username: "
+ newUser + (followingSlash ? " /api/ called" : ""));
log.debug("hue api user create requested for device type: " + aDeviceType + " and username: " + newUser + (followingSlash ? " /api/ called" : ""));
return "[{\"success\":{\"username\":\"" + newUser + "\"}}]";
}
return aGsonHandler.toJson(HueErrorResponse.createResponse("1", "/api/", "unauthorized user", null, null, null).getTheErrors());
}
private Object getConfig(String userId, String ipAddress) {
if (bridgeSettings.isTraceupnp())
log.info("Traceupnp: hue api/:userid/config config requested: " + userId + " from " + ipAddress);
log.debug("hue api config requested: " + userId + " from " + ipAddress);
if (bridgeSettings.validateWhitelistUser(userId, null, true) != null) {
if (bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton()) != null) {
log.debug("hue api config requested, No User supplied, returning public config");
HuePublicConfig apiResponse = HuePublicConfig.createConfig("Philips hue",
bridgeSettings.getUpnpConfigAddress(), bridgeSettings.getHubversion());
@@ -736,7 +765,7 @@ public class HueMulator {
@SuppressWarnings("unchecked")
private Object getFullState(String userId, String ipAddress) {
log.debug("hue api full state requested: " + userId + " from " + ipAddress);
HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, true);
HueError[] theErrors = bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
if (theErrors != null)
return theErrors;
@@ -750,7 +779,7 @@ public class HueMulator {
private Object getLight(String userId, String lightId, String ipAddress) {
log.debug("hue light requested: " + lightId + " for user: " + userId + " from " + ipAddress);
HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, true);
HueError[] theErrors = bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
if (theErrors != null)
return theErrors;
@@ -794,7 +823,7 @@ public class HueMulator {
Integer targetBri = null;
Integer targetBriInc = null;
log.debug("Update state requested: " + userId + " from " + ipAddress + " body: " + body);
HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, true);
HueError[] theErrors = bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
if (theErrors != null)
return aGsonHandler.toJson(theErrors);
try {
@@ -844,7 +873,7 @@ public class HueMulator {
aMultiUtil.setDelayDefault(bridgeSettings.getButtonsleep());
aMultiUtil.setSetCount(1);
log.debug("hue state change requested: " + userId + " from " + ipAddress + " body: " + body);
HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, true);
HueError[] theErrors = bridgeSettingMaster.getBridgeSecurity().validateWhitelistUser(userId, null, bridgeSettingMaster.getBridgeSecurity().isUseLinkButton());
if (theErrors != null)
return aGsonHandler.toJson(theErrors);
try {
@@ -954,6 +983,9 @@ public class HueMulator {
else
aMultiUtil.setTheDelay(aMultiUtil.getDelayDefault());
responseString = homeManager.findHome(callItems[i].getType().trim()).deviceHandler(callItems[i], aMultiUtil, lightId, state.getBri(), targetBri, targetBriInc, device, body);
if(responseString != null && responseString.contains("{\"error\":")) {
x = aMultiUtil.getSetCount();
}
}
}
}

View File

@@ -7,7 +7,7 @@ import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
@@ -32,7 +32,7 @@ public class NestHome implements com.bwssystems.HABridge.Home {
private Boolean isFarenheit;
private Boolean validNest;
public NestHome(BridgeSettingsDescriptor bridgeSettings) {
public NestHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@@ -164,20 +164,20 @@ public class NestHome implements com.bwssystems.HABridge.Home {
}
@Override
public com.bwssystems.HABridge.Home createHome(BridgeSettingsDescriptor bridgeSettings) {
public com.bwssystems.HABridge.Home createHome(BridgeSettings bridgeSettings) {
theSession = null;
theNest = null;
nestItems = null;
validNest = bridgeSettings.isValidNest();
validNest = bridgeSettings.getBridgeSettingsDescriptor().isValidNest();
aGsonHandler = null;
log.info("Nest Home created." + (validNest ? "" : " No Nest configured."));
if(validNest) {
aGsonHandler = new GsonBuilder().create();
isFarenheit = bridgeSettings.isFarenheit();
isFarenheit = bridgeSettings.getBridgeSettingsDescriptor().isFarenheit();
try {
theSession = new NestSession(bridgeSettings.getNestuser(), bridgeSettings.getNestpwd());
theSession = new NestSession(bridgeSettings.getBridgeSettingsDescriptor().getNestuser(), bridgeSettings.getBridgeSettingsDescriptor().getNestpwd());
theNest = new Nest(theSession);
} catch (LoginException e) {
log.error("Caught Login Exception, setting Nest to invalid....");

View File

@@ -9,7 +9,7 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
@@ -27,7 +27,7 @@ public class DomoticzHome implements Home {
private Boolean validDomoticz;
private HTTPHandler httpClient;
public DomoticzHome(BridgeSettingsDescriptor bridgeSettings) {
public DomoticzHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@@ -123,14 +123,14 @@ public class DomoticzHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
validDomoticz = bridgeSettings.isValidDomoticz();
public Home createHome(BridgeSettings bridgeSettings) {
validDomoticz = bridgeSettings.getBridgeSettingsDescriptor().isValidDomoticz();
log.info("Domoticz Home created." + (validDomoticz ? "" : " No Domoticz devices configured."));
if(!validDomoticz)
return null;
httpClient = new HTTPHandler();
domoticzs = new HashMap<String, DomoticzHandler>();
Iterator<NamedIP> theList = bridgeSettings.getDomoticzaddress().getDevices().iterator();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getDomoticzaddress().getDevices().iterator();
while(theList.hasNext()) {
NamedIP aDomoticz = theList.next();
try {

View File

@@ -5,7 +5,7 @@ import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
@@ -16,15 +16,16 @@ import com.bwssystems.HABridge.hue.TimeDecode;
public class CommandHome implements Home {
private static final Logger log = LoggerFactory.getLogger(CommandHome.class);
private BridgeSettings theSettings;
public CommandHome(BridgeSettingsDescriptor bridgeSettings) {
public CommandHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int itensity, Integer targetBri, Integer targetBriInc, DeviceDescriptor device, String body) {
log.debug("Exec Request called with url: " + anItem.getItem().getAsString());
log.debug("Exec Request called with url: " + anItem.getItem().getAsString() + " and exec Garden: " + (theSettings.getBridgeSecurity().getExecGarden() == null ? "not given" : theSettings.getBridgeSecurity().getExecGarden()));
String responseString = null;
String intermediate;
if (anItem.getItem().getAsString().contains("exec://"))
@@ -34,6 +35,14 @@ public class CommandHome implements Home {
intermediate = BrightnessDecode.calculateReplaceIntensityValue(intermediate, itensity, targetBri, targetBriInc, false);
intermediate = DeviceDataDecode.replaceDeviceData(intermediate, device);
intermediate = TimeDecode.replaceTimeValue(intermediate);
String execGarden = theSettings.getBridgeSecurity().getExecGarden();
if(execGarden != null && !execGarden.trim().isEmpty()) {
if(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
intermediate = execGarden + "\\" + intermediate;
else
intermediate = execGarden + "/" + intermediate;
}
String anError = doExecRequest(intermediate, lightId);
if (anError != null) {
responseString = anError;
@@ -49,7 +58,7 @@ public class CommandHome implements Home {
Process p = Runtime.getRuntime().exec(anItem);
log.debug("Process running: " + p.isAlive());
} catch (IOException e) {
log.warn("Could not execute request: " + anItem, e);
log.warn("Could not execute request: " + anItem + " with message: " + e.getMessage());
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId
+ "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId
+ "state\"}}]";
@@ -65,8 +74,9 @@ public class CommandHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
public Home createHome(BridgeSettings bridgeSettings) {
log.info("Command Home for system program execution created.");
this.theSettings = bridgeSettings;
return this;
}

View File

@@ -9,7 +9,7 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
@@ -21,7 +21,7 @@ public class HalHome implements Home {
private Map<String, HalInfo> hals;
private Boolean validHal;
public HalHome(BridgeSettingsDescriptor bridgeSettings) {
public HalHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@@ -111,17 +111,17 @@ public class HalHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
validHal = bridgeSettings.isValidHal();
public Home createHome(BridgeSettings bridgeSettings) {
validHal = bridgeSettings.getBridgeSettingsDescriptor().isValidHal();
log.info("HAL Home created." + (validHal ? "" : " No HAL devices configured."));
if(!validHal)
return null;
hals = new HashMap<String, HalInfo>();
Iterator<NamedIP> theList = bridgeSettings.getHaladdress().getDevices().iterator();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHaladdress().getDevices().iterator();
while(theList.hasNext()) {
NamedIP aHal = theList.next();
try {
hals.put(aHal.getName(), new HalInfo(aHal, bridgeSettings.getHaltoken()));
hals.put(aHal.getName(), new HalInfo(aHal, bridgeSettings.getBridgeSettingsDescriptor().getHaltoken()));
} catch (Exception e) {
log.error("Cannot get hal client (" + aHal.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
return null;

View File

@@ -10,7 +10,7 @@ import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.IpList;
@@ -32,7 +32,7 @@ public class HarmonyHome implements Home {
private Boolean validHarmony;
private Gson aGsonHandler;
public HarmonyHome(BridgeSettingsDescriptor bridgeSettings) {
public HarmonyHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@@ -197,9 +197,9 @@ public class HarmonyHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
public Home createHome(BridgeSettings bridgeSettings) {
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
validHarmony = bridgeSettings.isValidHarmony();
validHarmony = bridgeSettings.getBridgeSettingsDescriptor().isValidHarmony();
log.info("Harmony Home created." + (validHarmony ? "" : " No Harmony devices configured.") + (isDevMode ? " DevMode is set." : ""));
if(validHarmony || isDevMode) {
hubs = new HashMap<String, HarmonyServer>();
@@ -214,16 +214,16 @@ public class HarmonyHome implements Home {
theList.add(devModeIp);
IpList thedevList = new IpList();
thedevList.setDevices(theList);
bridgeSettings.setHarmonyAddress(thedevList);
bridgeSettings.getBridgeSettingsDescriptor().setHarmonyAddress(thedevList);
}
Iterator<NamedIP> theList = bridgeSettings.getHarmonyAddress().getDevices().iterator();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHarmonyAddress().getDevices().iterator();
while(theList.hasNext() && validHarmony) {
NamedIP aHub = theList.next();
boolean loopControl = true;
int retryCount = 0;
while(loopControl) {
try {
hubs.put(aHub.getName(), HarmonyServer.setup(bridgeSettings, isDevMode, aHub));
hubs.put(aHub.getName(), HarmonyServer.setup(bridgeSettings.getBridgeSettingsDescriptor(), isDevMode, aHub));
loopControl = false;
} catch (Exception e) {
if(retryCount > 3) {

View File

@@ -9,7 +9,7 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
@@ -26,23 +26,23 @@ public class HassHome implements Home {
private Boolean validHass;
private Gson aGsonHandler;
public HassHome(BridgeSettingsDescriptor bridgeSettings) {
public HassHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
public Home createHome(BridgeSettings bridgeSettings) {
hassMap = null;
aGsonHandler = null;
validHass = bridgeSettings.isValidHass();
validHass = bridgeSettings.getBridgeSettingsDescriptor().isValidHass();
log.info("HomeAssistant Home created." + (validHass ? "" : " No HomeAssistants configured."));
if(validHass) {
hassMap = new HashMap<String,HomeAssistant>();
aGsonHandler =
new GsonBuilder()
.create();
Iterator<NamedIP> theList = bridgeSettings.getHassaddress().getDevices().iterator();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHassaddress().getDevices().iterator();
while(theList.hasNext() && validHass) {
NamedIP aHass = theList.next();
try {

View File

@@ -3,7 +3,7 @@ package com.bwssystems.HABridge.plugins.http;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.api.NameValue;
@@ -20,7 +20,7 @@ public class HTTPHome implements Home {
private static final Logger log = LoggerFactory.getLogger(HTTPHome.class);
private HTTPHandler anHttpHandler;
public HTTPHome(BridgeSettingsDescriptor bridgeSettings) {
public HTTPHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@@ -79,7 +79,7 @@ public class HTTPHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
public Home createHome(BridgeSettings bridgeSettings) {
anHttpHandler = new HTTPHandler();
log.info("Http Home created.");
return this;

View File

@@ -8,7 +8,7 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
@@ -25,7 +25,7 @@ public class HueHome implements Home {
private Boolean validHue;
private Gson aGsonHandler;
public HueHome(BridgeSettingsDescriptor bridgeSettings) {
public HueHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@@ -105,12 +105,12 @@ public class HueHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
validHue = bridgeSettings.isValidHue();
public Home createHome(BridgeSettings bridgeSettings) {
validHue = bridgeSettings.getBridgeSettingsDescriptor().isValidHue();
log.info("Hue passthru Home created." + (validHue ? "" : " No Hue passtrhu systems configured."));
if(validHue) {
hues = new HashMap<String, HueInfo>();
Iterator<NamedIP> theList = bridgeSettings.getHueaddress().getDevices().iterator();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getHueaddress().getDevices().iterator();
while(theList.hasNext()) {
NamedIP aHue = theList.next();
hues.put(aHue.getName(), new HueInfo(aHue));

View File

@@ -14,7 +14,7 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
@@ -38,21 +38,21 @@ public class LifxHome implements Home {
private Boolean validLifx;
private Gson aGsonHandler;
public LifxHome(BridgeSettingsDescriptor bridgeSettings) {
public LifxHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
public Home createHome(BridgeSettings bridgeSettings) {
lifxMap = null;
aGsonHandler = null;
validLifx = bridgeSettings.isValidLifx();
validLifx = bridgeSettings.getBridgeSettingsDescriptor().isValidLifx();
log.info("LifxDevice Home created." + (validLifx ? "" : " No LifxDevices configured."));
if(validLifx) {
try {
log.info("Open Lifx client....");
InetAddress configuredAddress = InetAddress.getByName(bridgeSettings.getUpnpConfigAddress());
InetAddress configuredAddress = InetAddress.getByName(bridgeSettings.getBridgeSettingsDescriptor().getUpnpConfigAddress());
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(configuredAddress);
InetAddress bcastInetAddr = null;
if (networkInterface != null) {

View File

@@ -8,7 +8,7 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
@@ -26,7 +26,7 @@ public class MQTTHome implements Home {
private Boolean validMqtt;
private Gson aGsonHandler;
public MQTTHome(BridgeSettingsDescriptor bridgeSettings) {
public MQTTHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@@ -123,15 +123,15 @@ public class MQTTHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
validMqtt = bridgeSettings.isValidMQTT();
public Home createHome(BridgeSettings bridgeSettings) {
validMqtt = bridgeSettings.getBridgeSettingsDescriptor().isValidMQTT();
log.info("MQTT Home created." + (validMqtt ? "" : " No MQTT Clients configured."));
if(validMqtt) {
aGsonHandler =
new GsonBuilder()
.create();
handlers = new HashMap<String, MQTTHandler>();
Iterator<NamedIP> theList = bridgeSettings.getMqttaddress().getDevices().iterator();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getMqttaddress().getDevices().iterator();
while(theList.hasNext()) {
NamedIP aClientConfig = theList.next();
MQTTHandler aHandler = new MQTTHandler(aClientConfig);

View File

@@ -1,6 +1,6 @@
package com.bwssystems.HABridge.plugins.somfy;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
@@ -31,7 +31,7 @@ public class SomfyHome implements Home {
private Map<String, SomfyInfo> somfys;
private Boolean validSomfy;
public SomfyHome(BridgeSettingsDescriptor bridgeSettings) {
public SomfyHome(BridgeSettings bridgeSettings) {
createHome(bridgeSettings);
}
@@ -97,12 +97,12 @@ public class SomfyHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
validSomfy = bridgeSettings.isValidSomfy();
public Home createHome(BridgeSettings bridgeSettings) {
validSomfy = bridgeSettings.getBridgeSettingsDescriptor().isValidSomfy();
log.info("Somfy Home created." + (validSomfy ? "" : " No Somfys configured."));
if(validSomfy) {
somfys = new HashMap<>();
Iterator<NamedIP> theList = bridgeSettings.getSomfyAddress().getDevices().iterator();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getSomfyAddress().getDevices().iterator();
while (theList.hasNext()) {
NamedIP aSomfy = theList.next();
somfys.put(aSomfy.getName(), new SomfyInfo(aSomfy, aSomfy.getName()));

View File

@@ -15,22 +15,26 @@ import org.apache.commons.lang3.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.DeviceDataDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class TCPHome implements Home {
private static final Logger log = LoggerFactory.getLogger(TCPHome.class);
private byte[] sendData;
private Map<String, Socket> theSockets;
private Gson aGsonHandler;
public TCPHome(BridgeSettingsDescriptor bridgeSettings) {
public TCPHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@@ -41,8 +45,12 @@ public class TCPHome implements Home {
Socket dataSendSocket = null;
log.debug("executing HUE api request to TCP: " + anItem.getItem().getAsString());
String theUrl = anItem.getItem().getAsString();
if(theUrl != null && !theUrl.isEmpty () && theUrl.startsWith("tcp://")) {
String intermediate = theUrl.substring(theUrl.indexOf("://") + 3);
if(theUrl != null && !theUrl.isEmpty () && theUrl.contains("tcp://")) {
if(!theUrl.startsWith("{\"tcpDevice\""))
theUrl = "{\"tcpDevice\":\"" + theUrl + "\"}";
TcpDevice theDevice = aGsonHandler.fromJson(theUrl, TcpDevice.class);
String intermediate = theDevice.getTcpDevice().substring(theDevice.getTcpDevice().indexOf("://") + 3);
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
String hostAddr = null;
@@ -58,14 +66,15 @@ public class TCPHome implements Home {
try {
IPAddress = InetAddress.getByName(hostAddr);
} catch (UnknownHostException e) {
// noop
return aGsonHandler.toJson(HueErrorResponse.createResponse("901", null, "Cannot connect, Unknown Host", null, "/lights/" + device.getId(), null).getTheErrors());
}
try {
dataSendSocket = new Socket(IPAddress, Integer.parseInt(port));
theSockets.put(hostPortion, dataSendSocket);
if(theDevice.isPersistent())
theSockets.put(hostPortion, dataSendSocket);
} catch (Exception e) {
// noop
return aGsonHandler.toJson(HueErrorResponse.createResponse("901", null, "Cannot connect, Socket Creation issue", null, "/lights/" + device.getId(), null).getTheErrors());
}
}
@@ -80,18 +89,32 @@ public class TCPHome implements Home {
theUrlBody = StringEscapeUtils.unescapeJava(theUrlBody);
sendData = theUrlBody.getBytes();
}
try {
DataOutputStream outToClient = new DataOutputStream(dataSendSocket.getOutputStream());
outToClient.write(sendData);
outToClient.flush();
} catch (Exception e) {
DataOutputStream outToClient = new DataOutputStream(dataSendSocket.getOutputStream());
outToClient.write(sendData);
outToClient.flush();
} catch (IOException e) {
log.warn("Could not send data to TCP socket <<<" + e.getMessage() + ">>>, closing socket: " + theUrl);
try {
dataSendSocket.close();
} catch (IOException e1) {
// noop
}
theSockets.remove(hostPortion);
dataSendSocket = null;
if(theDevice.isPersistent())
theSockets.remove(hostPortion);
return aGsonHandler.toJson(HueErrorResponse.createResponse("901", null, "Cannot send data", null, "/lights/" + device.getId(), null).getTheErrors());
}
if(!theDevice.isPersistent()) {
try {
if(dataSendSocket != null)
dataSendSocket.close();
} catch (IOException e1) {
// noop
}
dataSendSocket = null;
}
} else
log.warn("Tcp Call to be presented as tcp://<ip_address>:<port>/payload, format of request unknown: " + theUrl);
@@ -99,9 +122,10 @@ public class TCPHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
public Home createHome(BridgeSettings bridgeSettings) {
log.info("TCP Home created.");
theSockets = new HashMap<String, Socket>();
aGsonHandler = new GsonBuilder().create();
return this;
}

View File

@@ -0,0 +1,18 @@
package com.bwssystems.HABridge.plugins.tcp;
public class TcpDevice {
private String tcpDevice;
private boolean persistent;
public String getTcpDevice() {
return tcpDevice;
}
public void setTcpDevice(String tcpDevice) {
this.tcpDevice = tcpDevice;
}
public boolean isPersistent() {
return persistent;
}
public void setPersistent(boolean persistent) {
this.persistent = persistent;
}
}

View File

@@ -10,7 +10,7 @@ import org.apache.commons.lang3.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
@@ -25,7 +25,7 @@ public class UDPHome implements Home {
private UDPDatagramSender theUDPDatagramSender;
private byte[] sendData;
public UDPHome(BridgeSettingsDescriptor bridgeSettings, UDPDatagramSender aUDPDatagramSender) {
public UDPHome(BridgeSettings bridgeSettings, UDPDatagramSender aUDPDatagramSender) {
super();
theUDPDatagramSender = aUDPDatagramSender;
createHome(bridgeSettings);
@@ -80,7 +80,7 @@ public class UDPHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
public Home createHome(BridgeSettings bridgeSettings) {
log.info("UDP Home created.");
return this;
}

View File

@@ -9,7 +9,7 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.DeviceMapTypes;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
@@ -25,7 +25,7 @@ public class VeraHome implements Home {
private Map<String, VeraInfo> veras;
private Boolean validVera;
public VeraHome(BridgeSettingsDescriptor bridgeSettings) {
public VeraHome(BridgeSettings bridgeSettings) {
super();
createHome(bridgeSettings);
}
@@ -90,12 +90,12 @@ public class VeraHome implements Home {
}
@Override
public Home createHome(BridgeSettingsDescriptor bridgeSettings) {
validVera = bridgeSettings.isValidVera();
public Home createHome(BridgeSettings bridgeSettings) {
validVera = bridgeSettings.getBridgeSettingsDescriptor().isValidVera();
log.info("Vera Home created." + (validVera ? "" : " No Veras configured."));
if(validVera) {
veras = new HashMap<String, VeraInfo>();
Iterator<NamedIP> theList = bridgeSettings.getVeraAddress().getDevices().iterator();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getVeraAddress().getDevices().iterator();
while(theList.hasNext()) {
NamedIP aVera = theList.next();
veras.put(aVera.getName(), new VeraInfo(aVera));

View File

@@ -1,3 +1,11 @@
[ng\:cloak],
[ng-cloak],
[data-ng-cloak],
[x-ng-cloak],
.ng-cloak,
.x-ng-cloak {
display: none !important;
}
body {
padding-top: 60px;
padding-bottom: 20px;
@@ -8,4 +16,48 @@ body {
}
.sortorder.reverse:after {
content: '\25bc';
}
}
.form-container {
position: relative;
bottom: 0;
display: table-cell;
vertical-align: middle;
}
.form-container form > div {
padding: 0 15px;
}
.form-container form > button {
margin-left: 15px;
}
legend.form-label {
font-size: 24pt;
padding: 0 15px;
}
.form-control.error {
border-color: red;
}
.form-hint {
font-size: 10pt;
line-height: 12pt;
margin: -5px auto 5px;
color: #999;
}
.form-hint.error {
color: #C00;
font-weight: bold;
font-size: 8pt;
}
.msg-block {
margin-top:5px;
}
.msg-error {
color:#F00;
font-size:14px;
}

View File

@@ -0,0 +1,11 @@
/*!
* @copyright Copyright &copy; Kartik Visweswaran, Krajee.com, 2015
* @package yii2-password
* @version 1.1.3
*
* Password Strength Meter
* Modified and built for Yii Framework 2.0
* Author: Kartik Visweswaran
* Year: 2015
* For more Yii related demos visit http://demos.krajee.com
*/.kv-strength-container{width:100%;margin:0;padding:0;border:0}.kv-strength-container td{vertical-align:middle}.kv-meter-container{width:130px}.kv-meter{text-align:center}.kv-disabled{opacity:.65;cursor:not-allowed}.kv-scorebar-border{background:none repeat scroll 0 0 #333;border:1px solid #333;height:16px;width:100px;margin:0 auto;border-radius:4px}.kv-scorebar{background-image:url(../img/bg_strength_gradient.jpg);background-repeat:no-repeat;background-position:0 0;position:absolute;width:98px;height:14px;z-index:0;border-radius:2px}.kv-score{font-weight:700;font-size:75%;position:absolute;width:98px;z-index:10;border-radius:2px}.kv-score-0,.kv-score-1,.kv-score-5{color:#fff}.kv-score-2,.kv-score-3,.kv-score-4{color:#333}.kv-verdict{width:100%}

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

View File

@@ -13,6 +13,7 @@
<link href="css/ngDialog.min.css" rel="stylesheet">
<link href="css/ngDialog-theme-default.min.css" rel="stylesheet">
<link href="css/scrollable-table.css" rel="stylesheet">
<link href="css/strength-meter.min.css" rel="stylesheet">
<!--[if lt IE 9]>
<script type="text/javascript" src="js/html5shiv.min.js"></script>
@@ -49,7 +50,7 @@
<ul class="dropdown-menu" aria-labelledby="dLabel">
<li><a href="http://www.bwssystems.com" target="_blank">Developed by BWS Systems</a></li>
<li><a href="http://www.amazon.com/echo" target="_blank">Amazon Echo</a></li>
<li><a href="">HA Bridge Version {{bridge.habridgeversion}}</a></li>
<li><a href="">HA Bridge Version {{bridge.habridgeversion.version}}</a></li>
<li><center>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
@@ -62,12 +63,13 @@
</li>
</ul>
</li>
<li><a href="#!/login">Login/Logout</a></li>
</ul>
</div>
</div>
</nav>
<div ng-view>
<div ng-view prerender-action="authorized()">
</div>
@@ -81,6 +83,10 @@
<script src="js/rzslider.min.js"></script>
<script src="js/ngDialog.min.js"></script>
<script src="js/angular-scrollable-table.min.js"></script>
<script src="js/strength-meter.min.js"></script>
<script src="js/angular-base64.min.js"></script>
<script src="js/angular-resource.min.js"></script>
<script src="js/ngStorage.min.js"></script>
<script src="scripts/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1 @@
!function(){"use strict";angular.module("base64",[]).constant("$base64",function(){function a(a,b){var c=f.indexOf(a.charAt(b));if(-1==c)throw"Cannot decode base64";return c}function b(b){b=""+b;var c,d,f,g=b.length;if(0==g)return b;if(0!=g%4)throw"Cannot decode base64";c=0,b.charAt(g-1)==e&&(c=1,b.charAt(g-2)==e&&(c=2),g-=4);var h=[];for(d=0;g>d;d+=4)f=a(b,d)<<18|a(b,d+1)<<12|a(b,d+2)<<6|a(b,d+3),h.push(String.fromCharCode(f>>16,255&f>>8,255&f));switch(c){case 1:f=a(b,d)<<18|a(b,d+1)<<12|a(b,d+2)<<6,h.push(String.fromCharCode(f>>16,255&f>>8));break;case 2:f=a(b,d)<<18|a(b,d+1)<<12,h.push(String.fromCharCode(f>>16))}return h.join("")}function c(a,b){var c=a.charCodeAt(b);if(c>255)throw"INVALID_CHARACTER_ERR: DOM Exception 5";return c}function d(a){if(1!=arguments.length)throw"SyntaxError: Not enough arguments";var b,d,g=[];a=""+a;var h=a.length-a.length%3;if(0==a.length)return a;for(b=0;h>b;b+=3)d=c(a,b)<<16|c(a,b+1)<<8|c(a,b+2),g.push(f.charAt(d>>18)),g.push(f.charAt(63&d>>12)),g.push(f.charAt(63&d>>6)),g.push(f.charAt(63&d));switch(a.length-h){case 1:d=c(a,b)<<16,g.push(f.charAt(d>>18)+f.charAt(63&d>>12)+e+e);break;case 2:d=c(a,b)<<16|c(a,b+1)<<8,g.push(f.charAt(d>>18)+f.charAt(63&d>>12)+f.charAt(63&d>>6)+e)}return g.join("")}var e="=",f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";return{encode:d,decode:b}}())}();

View File

@@ -0,0 +1,15 @@
/*
AngularJS v1.6.1
(c) 2010-2016 Google, Inc. http://angularjs.org
License: MIT
*/
(function(W,b){'use strict';function K(q,g){g=g||{};b.forEach(g,function(b,h){delete g[h]});for(var h in q)!q.hasOwnProperty(h)||"$"===h.charAt(0)&&"$"===h.charAt(1)||(g[h]=q[h]);return g}var B=b.$$minErr("$resource"),Q=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;b.module("ngResource",["ng"]).provider("$resource",function(){var q=/^https?:\/\/\[[^\]]*][^/]*/,g=this;this.defaults={stripTrailingSlashes:!0,cancellable:!1,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},
"delete":{method:"DELETE"}}};this.$get=["$http","$log","$q","$timeout",function(h,P,L,M){function C(b,e){this.template=b;this.defaults=p({},g.defaults,e);this.urlParams={}}function x(D,e,u,m){function c(a,d){var c={};d=p({},e,d);t(d,function(d,l){y(d)&&(d=d(a));var f;if(d&&d.charAt&&"@"===d.charAt(0)){f=a;var k=d.substr(1);if(null==k||""===k||"hasOwnProperty"===k||!Q.test("."+k))throw B("badmember",k);for(var k=k.split("."),e=0,g=k.length;e<g&&b.isDefined(f);e++){var h=k[e];f=null!==f?f[h]:void 0}}else f=
d;c[l]=f});return c}function R(a){return a.resource}function l(a){K(a||{},this)}var q=new C(D,m);u=p({},g.defaults.actions,u);l.prototype.toJSON=function(){var a=p({},this);delete a.$promise;delete a.$resolved;return a};t(u,function(a,d){var b=/^(POST|PUT|PATCH)$/i.test(a.method),e=a.timeout,g=N(a.cancellable)?a.cancellable:q.defaults.cancellable;e&&!S(e)&&(P.debug("ngResource:\n Only numeric values are allowed as `timeout`.\n Promises are not supported in $resource, because the same value would be used for multiple requests. If you are looking for a way to cancel requests, you should use the `cancellable` option."),
delete a.timeout,e=null);l[d]=function(f,k,m,D){function u(a){r.catch(E);z.resolve(a)}var G={},v,w,A;switch(arguments.length){case 4:A=D,w=m;case 3:case 2:if(y(k)){if(y(f)){w=f;A=k;break}w=k;A=m}else{G=f;v=k;w=m;break}case 1:y(f)?w=f:b?v=f:G=f;break;case 0:break;default:throw B("badargs",arguments.length);}var F=this instanceof l,n=F?v:a.isArray?[]:new l(v),s={},C=a.interceptor&&a.interceptor.response||R,x=a.interceptor&&a.interceptor.responseError||void 0,H=!!A,I=!!x,z,J;t(a,function(a,d){switch(d){default:s[d]=
T(a);case "params":case "isArray":case "interceptor":case "cancellable":}});!F&&g&&(z=L.defer(),s.timeout=z.promise,e&&(J=M(z.resolve,e)));b&&(s.data=v);q.setUrlParams(s,p({},c(v,a.params||{}),G),a.url);var r=h(s).then(function(f){var c=f.data;if(c){if(O(c)!==!!a.isArray)throw B("badcfg",d,a.isArray?"array":"object",O(c)?"array":"object",s.method,s.url);if(a.isArray)n.length=0,t(c,function(a){"object"===typeof a?n.push(new l(a)):n.push(a)});else{var b=n.$promise;K(c,n);n.$promise=b}}f.resource=n;
return f}),r=r["finally"](function(){n.$resolved=!0;!F&&g&&(n.$cancelRequest=E,M.cancel(J),z=J=s.timeout=null)}),r=r.then(function(a){var d=C(a);(w||E)(d,a.headers,a.status,a.statusText);return d},H||I?function(a){H&&A(a);return I?x(a):L.reject(a)}:void 0);H&&!I&&r.catch(E);return F?r:(n.$promise=r,n.$resolved=!1,g&&(n.$cancelRequest=u),n)};l.prototype["$"+d]=function(a,c,b){y(a)&&(b=c,c=a,a={});a=l[d].call(this,a,this,c,b);return a.$promise||a}});l.bind=function(a){a=p({},e,a);return x(D,a,u,m)};
return l}var E=b.noop,t=b.forEach,p=b.extend,T=b.copy,O=b.isArray,N=b.isDefined,y=b.isFunction,S=b.isNumber,U=b.$$encodeUriQuery,V=b.$$encodeUriSegment;C.prototype={setUrlParams:function(b,e,g){var m=this,c=g||m.template,h,l,p="",a=m.urlParams=Object.create(null);t(c.split(/\W/),function(d){if("hasOwnProperty"===d)throw B("badname");!/^\d+$/.test(d)&&d&&(new RegExp("(^|[^\\\\]):"+d+"(\\W|$)")).test(c)&&(a[d]={isQueryParamValue:(new RegExp("\\?.*=:"+d+"(?:\\W|$)")).test(c)})});c=c.replace(/\\:/g,":");
c=c.replace(q,function(a){p=a;return""});e=e||{};t(m.urlParams,function(a,b){h=e.hasOwnProperty(b)?e[b]:m.defaults[b];N(h)&&null!==h?(l=a.isQueryParamValue?U(h,!0):V(h),c=c.replace(new RegExp(":"+b+"(\\W|$)","g"),function(a,b){return l+b})):c=c.replace(new RegExp("(/?):"+b+"(\\W|$)","g"),function(a,b,d){return"/"===d.charAt(0)?d:b+d})});m.defaults.stripTrailingSlashes&&(c=c.replace(/\/+$/,"")||"/");c=c.replace(/\/\.(?=\w+($|\?))/,".");b.url=p+c.replace(/\/\\\./,"/.");t(e,function(a,c){m.urlParams[c]||
(b.params=b.params||{},b.params[c]=a)})}};return x}]})})(window,window.angular);
//# sourceMappingURL=angular-resource.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
/*! ngstorage 0.3.10 | Copyright (c) 2016 Gias Kay Lee | MIT License */!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):a.hasOwnProperty("angular")?b(a.angular):"object"==typeof exports&&(module.exports=b(require("angular")))}(this,function(a){"use strict";function b(a,b){var c;try{c=a[b]}catch(d){c=!1}if(c){var e="__"+Math.round(1e7*Math.random());try{a[b].setItem(e,e),a[b].removeItem(e,e)}catch(d){c=!1}}return c}function c(c){var d=b(window,c);return function(){var e="ngStorage-";this.setKeyPrefix=function(a){if("string"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setKeyPrefix() expects a String.");e=a};var f=a.toJson,g=a.fromJson;this.setSerializer=function(a){if("function"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setSerializer expects a function.");f=a},this.setDeserializer=function(a){if("function"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setDeserializer expects a function.");g=a},this.supported=function(){return!!d},this.get=function(a){return d&&g(d.getItem(e+a))},this.set=function(a,b){return d&&d.setItem(e+a,f(b))},this.remove=function(a){d&&d.removeItem(e+a)},this.$get=["$rootScope","$window","$log","$timeout","$document",function(d,h,i,j,k){var l,m,n=e.length,o=b(h,c),p=o||(i.warn("This browser does not support Web Storage!"),{setItem:a.noop,getItem:a.noop,removeItem:a.noop}),q={$default:function(b){for(var c in b)a.isDefined(q[c])||(q[c]=a.copy(b[c]));return q.$sync(),q},$reset:function(a){for(var b in q)"$"===b[0]||delete q[b]&&p.removeItem(e+b);return q.$default(a)},$sync:function(){for(var a,b=0,c=p.length;c>b;b++)(a=p.key(b))&&e===a.slice(0,n)&&(q[a.slice(n)]=g(p.getItem(a)))},$apply:function(){var b;if(m=null,!a.equals(q,l)){b=a.copy(l),a.forEach(q,function(c,d){a.isDefined(c)&&"$"!==d[0]&&(p.setItem(e+d,f(c)),delete b[d])});for(var c in b)p.removeItem(e+c);l=a.copy(q)}},$supported:function(){return!!o}};return q.$sync(),l=a.copy(q),d.$watch(function(){m||(m=j(q.$apply,100,!1))}),h.addEventListener&&h.addEventListener("storage",function(b){if(b.key){var c=k[0];c.hasFocus&&c.hasFocus()||e!==b.key.slice(0,n)||(b.newValue?q[b.key.slice(n)]=g(b.newValue):delete q[b.key.slice(n)],l=a.copy(q),d.$apply())}}),h.addEventListener&&h.addEventListener("beforeunload",function(){q.$apply()}),q}]}}return a=a&&a.module?a:window.angular,a.module("ngStorage",[]).provider("$localStorage",c("localStorage")).provider("$sessionStorage",c("sessionStorage"))});

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,7 @@
<p>
<button class="btn btn-primary" type="submit" ng-click="renumberDevices()">Renumber Devices</button>
<button ng-if="bridge.securityInfo.useLinkButton" class="btn btn-primary" type="submit" ng-click="pushLinkButton()">Link</button>
</p>
<scrollable-table watch="bridge.devices">
<table class="table table-bordered table-striped table-hover">

View File

@@ -0,0 +1,29 @@
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">Login</h2>
</div>
<div class="panel-body">
<div ng-if="!loggedIn" class="form-container">
<form name="loginForm" role="form" ng-submit="login(username, password)">
<legend class="form-label">Enter Credentials</legend>
<div class="form-group">
<label>User</label> <input id="username" name="username"
class="form-control" type="text" ng-model="username"
placeholder="someone" autofocus/>
</div>
<div class="form-group">
<label>Password</label> <input id="password" name="password"
class="form-control" type="password" ng-model="password" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-success" value="Submit">Submit</button>
</div>
</form>
</div>
<div ng-if="loggedIn">
<button type="button" class="btn btn-danger" ng-click="logout()">Logout</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,51 @@
<div class="form-container ngdialog-message" ng-controller="SecurityDialogCtrl">
<form name="securityForm" role="form">
<legend class="form-label">Update Security Settings</legend>
<div class="form-group">
<label>Use Link Button</label>
<input type="checkbox"
ng-model="useLinkButton" ng-true-value=true
ng-false-value=false /> {{useLinkButton}}
</div>
<div class="form-group">
<label>Use username/password for HUE Api</label>
<input type="checkbox"
ng-model="secureHueApi" ng-true-value=true
ng-false-value=false /> {{secureHueApi}}
</div>
<div class="form-group">
<button type="button" class="btn btn-primary" ng-click="setSecurityInfo()">Update</button>
</div>
<div class="form-group">
<label>Add/Delete User</label>
<input id="new-user" name="new-user" class="form-control"
type="text" ng-model="newUser"
placeholder="someone" nu-check="new-user" />
</div>
<div ng-if="isSecure" class="form-group">
<button type="button" class="btn btn-danger" ng-click="delUser(newUser)">Delete</button>
</div>
<div ng-if="showPassword" postrender-action="setBlankPassword('password-1')">
<div class="form-group">
<label>Change Password for {{username}}</label>
<input id="password-1" name="password-1" type="password" class="form-control strength" ng-model="password" data-toggle-title="Display Password" />
</div>
<div class="form-group">
<label>Confirm Password</label>
<input id="password-2" name="password-2" class="form-control" type="password" ng-model="password2" pw-check="password-1" />
<div class="msg-block" ng-show="securityForm.$error">
<span class="msg-error" ng-show="securityForm.$error.pwmatch">Passwords don't match.</span>
</div>
</div>
<div ng-if="matched" class="form-group">
<button ng-if="!addingUser" class="btn btn-warning" ng-click="changePassword(password, password2)">Change Password</button>
<button ng-if="addingUser" class="btn btn-success" ng-click="addUser(newUser, password, password2)">Add User</button>
</div>
</div>
<div class="form-group">
<button type="button" class="btn btn-success" ng-click="dismissDialog()" autofocus>Dismiss</button>
</div>
</form>
</div>

View File

@@ -48,16 +48,11 @@
<button ng-disabled="bridge.isInControl"
class="btn btn-success" type="submit" ng-click="saveSettings()">Save</button>
<button ng-disabled="bridge.isInControl" class="btn btn-warning"
type="submit" ng-click="bridgeReinit()">Bridge
Reinitialize</button>
type="submit" ng-click="bridgeReinit()">Bridge Reinitialize</button>
<button ng-disabled="bridge.isInControl" class="btn btn-danger"
type="submit" ng-click="bridgeStop()">Bridge Stop</button>
<button class="btn btn-primary" type="submit" onclick="myRefresh()">Refresh</button>
<script>
function myRefresh() {
location.reload();
}
</script>
<button class="btn btn-warning"
type="submit" ng-click="changeSeuritySettings()">Update Security Settings</button>
</p>
<table class="table table-bordered table-striped table-hover">
<thead>

View File

@@ -1,4 +1,4 @@
package com.bwssystems.hass.test;
package com.bwssystems.domoticz.test;
import java.util.Iterator;

View File

@@ -1,4 +1,4 @@
package com.bwssystems.hass.test;
package com.bwssystems.domoticz.test;
import org.junit.Assert;
import org.junit.Test;