Compare commits

...

10 Commits

Author SHA1 Message Date
BWS Systems
ff2973e473 Update Readme for https 2019-07-12 09:11:53 -05:00
BWS Systems
743656cab3 Updated mDNS host and naming 2019-07-11 13:41:34 -05:00
BWS Systems
53be3ba213 Added https to web and api that will use java keytool 2019-07-10 16:07:22 -05:00
BWS Systems
a5ee0aafc8 Update upnp original to be like v3.5.1 2019-07-02 10:57:46 -05:00
BWS Systems
aed8ffa8d3 Remove extra SEARCH response, use upnp original for udp send change 2019-07-01 15:47:23 -05:00
BWS Systems
369e5a25e6 Fixed bulk add and sort on moziot page 2019-06-28 15:05:57 -05:00
BWS Systems
3fe19f5d4e Updated a few issues
Updated the hue uniqueid generation. Updated the moziot login handling. updated upnp response message to have hue-bridgeId in capital letters - HUE-BRIDGEID
2019-06-28 15:02:28 -05:00
BWS Systems
5736bb92db Issue with tabs in moziot page 2019-06-28 10:11:32 -05:00
BWS Systems
b61f334826 updated tab issues and buildUrls call in app.js 2019-06-28 10:05:19 -05:00
BWS Systems
556a5fef1c Mozilla IOT changed Thing JSON object already for name to title 2019-06-27 16:00:05 -05:00
22 changed files with 465 additions and 166 deletions

View File

@@ -290,7 +290,31 @@ This field is used to test the bridge server with the UPNP IP Address and to mak
#### Bridge Control Buttons
These buttons are for managing the bridge. The Save button is enabled when there is a change to the configuration. The Bridge Reinitialize button will recycle the internal running of the bridge in the java process. The Stop button will stop the java process. The Refresh button will refresh the page and settings.
#### The Security Dialog
This is where you can set the different security settings for the ha-bridge. There are two settings, one for enabling Hue like operation to secure the Hue API with the internally generated user for the calls that are done after the link button. The other is to secure the hue API with a username/password that is created as well. The other fields are to add and delete users and to set and change passwords for those users. If there are no users in the system, the system will not require a username/password to operate.
This is where you can set the different security settings for the ha-bridge. The settings are the https enablement with the Keyfile path and password and described below, enabling Hue like operation to secure the Hue API with the internally generated user for the calls that are done after the link button. Another is to secure the hue API with a username/password that is created as well. The last one is the path to the exec garden if used for only allowing scripts and programs to be executed from this location only. The other fields are to add and delete users and to set and change passwords for those users. If there are no users in the system, the system will not require a username/password to operate.
The use https selection will require you to generate a java keytool keystore file for the ha-bridge to use. This will require the path to the keyfile and the password that was used to secure the keyfile. There are mulitple ways to add the key file. The basic way is to generate a self signed keystore using keytool as follows:
Step 1. Open the command console
Step 2. Run this command (Where indicate the number of days for which the certificate will be valid)
keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass ```<password>``` -validity 365 -keysize 2048
Step 3. Enter a password for the keystore. Note this ```<password>``` as you require this for configuring the server
Step 4. When prompted for first name and last name, enter the domain name of the server. For example, myserver or myserver.mycompany.com.
Step 5. Enter the other details, such as Organizational Unit, Organization, City, State, and Country.
Step 6. When prompted with Enter key ```<password>``` for , press Enter to use the same ```<password>``` as the keystore ```<password>```
Step 7. Run this command to verify the contents of the keystore
keytool -list -v -keystore selfsigned.jks
Step 8. When prompted, enter the keystore password note in Step 3. The basic information about the generated certificate is displayed. Verify that the Owner and Issuer are the same. Also, you should see the information you provided in Step 4 and 5.
The second way is to acquire a certified certificate to use in the keyfile. Such way to get one is use letsencrypt. Once you have the certifcate files you can follow this to create the keystore: https://community.letsencrypt.org/t/tutorial-java-keystores-jks-with-lets-encrypt/34754.
The easiest place to keep the keystore files is in your ha bridge data directory.
#### Configuration Path and File
The default location for the configuration file to contain the settings for the bridge is the relative path from where the bridge is started in "data/habridge.config". If you would like a different filename or directory, specify `<directory>/<filename>` explicitly.
#### Device DB Path and File
@@ -336,7 +360,7 @@ Provide IP Addresses of your FHEM Systems that you want to utilize with the brid
#### Mozilla IOT Names and IP Addresses
Provide IP Addresses of your Mozilla IOT Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices by the call it receives and send it to the target Mozilla IOT device you configure.
#### HomeGenie Names and IP Addresses
Provide IP Addresses of your HomeGenie Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices by the call it receives and send it to the target HomeGenie device you configure.
Provide IP Addresses of your HomeGenie Systems that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices by the call it receives and send it to the target HomeGenie device you configure. There is an extra Other Types field that you can add types that are not within the defualt of "light", "Switch" and "Dimmer" that you may want to control.
#### Nest Username
Provide the username and password of the home.nest.com account for the Nest user. This needs to be given if you are using the Nest features. There is no need to give any ip address or host information as this contacts your cloud account. Also, you can set Nest Temp Fahrenheit that allows the value being sent into the bridge to be interpreted as Fahrenheit or Celsius. The default is to have Fahrenheit.
#### LIFX Support

View File

@@ -5,7 +5,7 @@
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>5.3.0RC7</version>
<version>5.3.0RC10</version>
<packaging>jar</packaging>
<name>HA Bridge</name>
@@ -134,6 +134,11 @@
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<dependency>
<groupId>org.jmdns</groupId>
<artifactId>jmdns</artifactId>
<version>3.5.5</version>
</dependency>
</dependencies>
<build>

View File

@@ -38,7 +38,7 @@ public class BridgeSecurity {
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
};
private char[] habridgeKey;
private static char[] habridgeKey;
private String execGarden;
private BridgeSecurityDescriptor securityDescriptor;
private boolean settingsChanged;
@@ -146,7 +146,16 @@ public class BridgeSecurity {
public String getExecGarden() {
return execGarden;
}
public void setUseLinkButton(boolean useThis) {
String getKeyfilePath() {
return securityDescriptor.getKeyfilePath();
}
String getKeyfilePassword() {
return securityDescriptor.getKeyfilePassword();
}
private void setUseLinkButton(boolean useThis) {
securityDescriptor.setUseLinkButton(useThis);
settingsChanged = true;
}
@@ -155,16 +164,77 @@ public class BridgeSecurity {
return securityDescriptor.isSecureHueApi();
}
public void setSecureHueApi(boolean theState) {
securityDescriptor.setSecureHueApi(theState);
public boolean isUseHttps() {
return securityDescriptor.isUseHttps();
}
public boolean isKeyfilePW() {
if(securityDescriptor.getKeyfilePassword() != null && !securityDescriptor.getKeyfilePassword().trim().isEmpty()) {
return true;
}
return false;
}
private void setSecureHueApi(boolean theState) {
securityDescriptor.setSecureHueApi(theState);
settingsChanged = true;
}
private void setUseHttps(boolean usehttps, String keyfilepath, String keyfilepassword) {
if(usehttps) {
if(!isUseHttps()) {
securityDescriptor.setKeyfilePath(keyfilepath);
securityDescriptor.setKeyfilePassword(keyfilepassword);
securityDescriptor.setUseHttps(usehttps);
settingsChanged = true;
} else {
if(!keyfilepassword.equals("########")) {
securityDescriptor.setKeyfilePassword(keyfilepassword);
settingsChanged = true;
}
if(!securityDescriptor.getKeyfilePath().equals(keyfilepath)) {
securityDescriptor.setKeyfilePath(keyfilepath);
settingsChanged = true;
}
}
} else {
if(isUseHttps()) {
securityDescriptor.setKeyfilePassword("");
securityDescriptor.setKeyfilePath("");
securityDescriptor.setUseHttps(usehttps);
settingsChanged = true;
}
}
}
public SecurityInfo getSecurityInfo() {
SecurityInfo theInfo = new SecurityInfo();
theInfo.setUseLinkButton(isUseLinkButton());
theInfo.setSecureHueApi(isSecureHueApi());
theInfo.setSecure(isSecure());
theInfo.setUseHttps(isUseHttps());
theInfo.setKeyfilePath(securityDescriptor.getKeyfilePath());
if(isKeyfilePW()) {
theInfo.setKeyfilePassword("########");
}
else {
theInfo.setKeyfilePassword("");
}
if(isSecure()) {
theInfo.setExecGarden(execGarden);
}
return theInfo;
}
public void setSecurityDataByInfo(SecurityInfo theInfo) {
setUseLinkButton(theInfo.isUseLinkButton());
setSecureHueApi(theInfo.isSecureHueApi());
setUseHttps(theInfo.isUseHttps(), theInfo.getKeyfilePath(), theInfo.getKeyfilePassword());
}
public LoginResult validatePassword(User targetUser) throws IOException {
LoginResult result = new LoginResult();
if(targetUser != null && targetUser.getUsername() != null) {
@@ -324,7 +394,7 @@ public class BridgeSecurity {
}
}
private String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
static String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(habridgeKey));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
@@ -336,7 +406,7 @@ public class BridgeSecurity {
return Base64.getEncoder().encodeToString(bytes);
}
private String decrypt(String property) throws GeneralSecurityException, IOException {
static String decrypt(String property) throws GeneralSecurityException, IOException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(habridgeKey));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");

View File

@@ -9,6 +9,9 @@ public class BridgeSecurityDescriptor {
private String execGarden;
private boolean secureHueApi;
private Map<String, WhitelistEntry> whitelist;
private boolean useHttps;
private String keyfilePassword;
private String keyfilePath;
public BridgeSecurityDescriptor() {
super();
@@ -67,4 +70,28 @@ public class BridgeSecurityDescriptor {
return secureFlag;
}
public boolean isUseHttps() {
return useHttps;
}
public void setUseHttps(boolean useHttps) {
this.useHttps = useHttps;
}
public String getKeyfilePassword() {
return keyfilePassword;
}
public void setKeyfilePassword(String keyfilePassword) {
this.keyfilePassword = keyfilePassword;
}
public String getKeyfilePath() {
return keyfilePath;
}
public void setKeyfilePath(String keyfilePath) {
this.keyfilePath = keyfilePath;
}
}

View File

@@ -129,6 +129,9 @@ public class BridgeSettingsDescriptor {
@SerializedName("seedid")
@Expose
private Integer seedid;
@SerializedName("haaddressessecured")
@Expose
private boolean haaddressessecured;
// @SerializedName("activeloggers")
// @Expose
// private List<NameValue> activeloggers;
@@ -188,6 +191,7 @@ public class BridgeSettingsDescriptor {
this.tracestate = false;
this.upnporiginal = false;
this.seedid = 100;
this.haaddressessecured = false;
}
public String getUpnpConfigAddress() {
@@ -255,6 +259,7 @@ public class BridgeSettingsDescriptor {
}
public IpList getVeraAddress() {
return veraaddress;
}
@@ -835,4 +840,11 @@ public class BridgeSettingsDescriptor {
return true;
}
public boolean isHaaddressessecured() {
return haaddressessecured;
}
public void setHaaddressessecured(boolean haaddressessecured) {
this.haaddressessecured = haaddressessecured;
}
}

View File

@@ -54,9 +54,13 @@ public class HABridge {
bridgeSettings = new BridgeSettings();
// sparkjava config directive to set html static file location for Jetty
while(!bridgeSettings.getBridgeControl().isStop()) {
bridgeSettings.buildSettings();
bridgeSettings.getBridgeSecurity().removeTestUsers();
log.info("HA Bridge (v{}) initializing....", theVersion.getVersion() );
bridgeSettings.buildSettings();
if(bridgeSettings.getBridgeSecurity().isUseHttps()) {
secure(bridgeSettings.getBridgeSecurity().getKeyfilePath(), bridgeSettings.getBridgeSecurity().getKeyfilePassword(), null, null);
log.info("Using https for web and api calls");
}
bridgeSettings.getBridgeSecurity().removeTestUsers();
// sparkjava config directive to set ip address for the web server to listen on
ipAddress(bridgeSettings.getBridgeSettingsDescriptor().getWebaddress());
// sparkjava config directive to set port for the web server to listen on

View File

@@ -4,6 +4,10 @@ public class SecurityInfo {
private boolean useLinkButton;
private boolean secureHueApi;
private boolean isSecure;
private String execGarden;
private boolean useHttps;
private String keyfilePath;
private String keyfilePassword;
public boolean isUseLinkButton() {
return useLinkButton;
@@ -23,4 +27,36 @@ public class SecurityInfo {
public void setSecure(boolean isSecure) {
this.isSecure = isSecure;
}
public boolean isUseHttps() {
return useHttps;
}
public void setUseHttps(boolean useHttps) {
this.useHttps = useHttps;
}
public String getKeyfilePath() {
return keyfilePath;
}
public void setKeyfilePath(String keyfilePath) {
this.keyfilePath = keyfilePath;
}
public String getExecGarden() {
return execGarden;
}
public void setExecGarden(String execGarden) {
this.execGarden = execGarden;
}
public String getKeyfilePassword() {
return keyfilePassword;
}
public void setKeyfilePassword(String keyfilePassword) {
this.keyfilePassword = keyfilePassword;
}
}

View File

@@ -314,8 +314,7 @@ public class SystemControl {
post(SYSTEM_CONTEXT + "/changesecurityinfo", (request, response) -> {
log.debug("changesecurityinfo....");
SecurityInfo theInfo = new Gson().fromJson(request.body(), SecurityInfo.class);
bridgeSettings.getBridgeSecurity().setUseLinkButton(theInfo.isUseLinkButton());
bridgeSettings.getBridgeSecurity().setSecureHueApi(theInfo.isSecureHueApi());
bridgeSettings.getBridgeSecurity().setSecurityDataByInfo(theInfo);
bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor());
response.status(HttpStatus.SC_OK);
response.type("application/json");

View File

@@ -186,9 +186,7 @@ public class DeviceRepository extends BackupHandler {
nextId++;
}
if (descriptors[i].getUniqueid() == null || descriptors[i].getUniqueid().length() == 0) {
String hexValue = HexLibrary.encodeUsingBigIntegerToString(descriptors[i].getId());
descriptors[i].setUniqueid("00:17:88:5E:D3:" + hexValue + "-" + hexValue);
descriptors[i].setUniqueid("00:17:88:5E:D3:" + hueUniqueId(Integer.valueOf(descriptors[i].getId())));
}
put(descriptors[i].getId(), descriptors[i]);
theNames = theNames + " " + descriptors[i].getName() + ", ";
@@ -203,8 +201,6 @@ public class DeviceRepository extends BackupHandler {
Iterator<DeviceDescriptor> deviceIterator = list.iterator();
Map<String, DeviceDescriptor> newdevices = new HashMap<String, DeviceDescriptor>();
List<String> lockedIds = new ArrayList<String>();
String hexValue;
Integer newValue;
DeviceDescriptor theDevice;
boolean findNext = true;
@@ -230,14 +226,7 @@ public class DeviceRepository extends BackupHandler {
}
}
theDevice.setId(String.valueOf(nextId));
newValue = nextId % 256;
if (newValue <= 0)
newValue = 1;
else if (newValue > 255)
newValue = 255;
hexValue = HexLibrary.encodeUsingBigIntegerToString(newValue.toString());
theDevice.setUniqueid("00:17:88:5E:D3:" + hexValue + "-" + hexValue);
theDevice.setUniqueid("00:17:88:5E:D3:" + hueUniqueId(nextId));
nextId++;
}
newdevices.put(theDevice.getId(), theDevice);
@@ -304,4 +293,29 @@ public class DeviceRepository extends BackupHandler {
return content;
}
private String hueUniqueId(Integer anId) {
String theUniqueId;
Integer newValue;
String hexValueLeft;
String hexValueRight;
newValue = anId % 256;
if (newValue <= 0)
newValue = 1;
else if (newValue > 255)
newValue = 255;
hexValueLeft = HexLibrary.byteToHex(newValue.byteValue());
newValue = anId / 256;
newValue = newValue % 256;
if (newValue < 0)
newValue = 0;
else if (newValue > 255)
newValue = 255;
hexValueRight = HexLibrary.byteToHex(newValue.byteValue());
theUniqueId = String.format("%s-%s", hexValueLeft, hexValueRight).toUpperCase();
return theUniqueId;
}
}

View File

@@ -19,15 +19,20 @@ public class MozIotInstance {
private JWT moziotToken;
private NamedIP mozIotIP;
private NameValue[] headers;
private HTTPHandler anHttpClient;
public MozIotInstance(NamedIP theNamedIp, HTTPHandler httpClient) {
mozIotIP = theNamedIp;
headers = null;
gatewayLogin(httpClient);
moziotToken = null;
anHttpClient = httpClient;
gatewayLogin();
}
public Boolean callCommand(String deviceId, String aCommand, MozIotCommandDetail commandData, HTTPHandler httpClient) {
log.debug("calling Mozilla IOT: {}:{}{}{}", mozIotIP.getIp(), mozIotIP.getPort(), aCommand, commandData.getBody());
public Boolean callCommand(String deviceId, String aCommand, MozIotCommandDetail commandData,
HTTPHandler httpClient) {
log.debug("calling Mozilla IOT: {}:{}{}{}", mozIotIP.getIp(), mozIotIP.getPort(), aCommand,
commandData.getBody());
String aUrl = null;
if (mozIotIP.getSecure() != null && mozIotIP.getSecure())
@@ -37,7 +42,8 @@ public class MozIotInstance {
headers = getAuthHeader();
aUrl = aUrl + mozIotIP.getIp() + ":" + mozIotIP.getPort() + "/things/" + deviceId + "/" + aCommand;
String theData = httpClient.doHttpRequest(aUrl, HttpPut.METHOD_NAME, "application/json", commandData.getBody(), headers);
String theData = httpClient.doHttpRequest(aUrl, HttpPut.METHOD_NAME, "application/json", commandData.getBody(),
headers);
log.debug("call Command return is: <<<{}>>>", theData);
if (theData.contains("error") || theData.contains("ERROR") || theData.contains("Error"))
return false;
@@ -77,7 +83,10 @@ public class MozIotInstance {
}
private NameValue[] getAuthHeader() {
if (headers == null) {
if (moziotToken == null)
gatewayLogin();
if (headers == null && moziotToken != null) {
headers = new NameValue[3];
headers[0] = new NameValue();
headers[0].setName("Authorization");
@@ -92,9 +101,10 @@ public class MozIotInstance {
return headers;
}
private void gatewayLogin(HTTPHandler httpClient) {
private void gatewayLogin() {
String aUrl = null;
moziotToken = null;
if (mozIotIP.getSecure() != null && mozIotIP.getSecure())
aUrl = "https://";
else
@@ -112,13 +122,15 @@ public class MozIotInstance {
String commandData = "{\"email\": \"" + mozIotIP.getUsername() + "\", \"password\":\"" + mozIotIP.getPassword()
+ "\"}";
log.debug("The login body: {}", commandData);
String theData = httpClient.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "application/json", commandData, headers);
String theData = anHttpClient.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "application/json", commandData,
headers);
if (theData != null) {
log.debug("GET Mozilla login - data: {}", theData);
try {
moziotToken = new Gson().fromJson(theData, JWT.class);
} catch (Exception e) {
log.warn("Cannot get login for Mozilla IOT {} Gson Parse Error.", mozIotIP.getName());
moziotToken = null;
}
} else {
log.warn("Could not login {} error: <<<{}>>>", mozIotIP.getName(), theData);

View File

@@ -7,9 +7,9 @@ import com.google.gson.annotations.SerializedName;
public class MozillaThing {
@SerializedName("name")
@SerializedName("title")
@Expose
private String name;
private String title;
@SerializedName("type")
@Expose
private String type;
@@ -47,14 +47,6 @@ public class MozillaThing {
@Expose
private Object iconHref;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
@@ -151,4 +143,12 @@ public class MozillaThing {
this.iconHref = iconHref;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

View File

@@ -12,13 +12,18 @@ import com.bwssystems.HABridge.util.UDPDatagramSender;
import java.io.IOException;
import java.net.*;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
// import java.time.Instant;
// import java.time.temporal.ChronoUnit;
import java.util.Enumeration;
import java.util.HashMap;
import org.apache.http.conn.util.*;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;
public class UpnpListener {
private Logger log = LoggerFactory.getLogger(UpnpListener.class);
private UDPDatagramSender theUDPDatagramSender;
private MulticastSocket upnpMulticastSocket;
private int httpServerPort;
private String upnpConfigIP;
@@ -31,41 +36,67 @@ public class UpnpListener {
private String bridgeSNUUID;
private HuePublicConfig aHueConfig;
private Integer theUpnpSendDelay;
private String responseTemplateOriginal = "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + "EXT:\r\n"
+ "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:"
+ HueConstants.UUID_PREFIX + "%s::urn:schemas-upnp-org:device:basic:1\r\n\r\n";
private String responseTemplate1 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "EXT:\r\n" + "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: upnp:rootdevice\r\n" + "USN: uuid:"
+ HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "EXT:\r\n" + "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: uuid:" + HueConstants.UUID_PREFIX
+ "%s\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String responseTemplate3 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "EXT:\r\n" + "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: urn:schemas-upnp-org:device:basic:1\r\n"
+ "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String responseTemplate1 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"HUE-BRIDGEID: %s\r\n" +
"ST: upnp:rootdevice\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"HUE-BRIDGEID: %s\r\n" +
"ST: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String responseTemplate3 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"HUE-BRIDGEID: %s\r\n" +
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n" + "NT: uuid:"
+ HueConstants.UUID_PREFIX + "%s\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate1 = "NOTIFY * HTTP/1.1\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"NTS: ssdp:alive\r\n" +
"HUE-BRIDGEID: %s\r\n" +
"NT: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate2 = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n"
+ "NT: upnp:rootdevice\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String notifyTemplate2 = "NOTIFY * HTTP/1.1\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"NTS: ssdp:alive\r\n" + "HUE-BRIDGEID: %s\r\n" +
"NT: upnp:rootdevice\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String notifyTemplate3 = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n"
+ "NT: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate3 = "NOTIFY * HTTP/1.1\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"NTS: ssdp:alive\r\n" +
"HUE-BRIDGEID: %s\r\n" +
"NT: urn:schemas-upnp-org:device:basic:1\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl,
UDPDatagramSender aUdpDatagramSender) throws IOException {
super();
theUDPDatagramSender = aUdpDatagramSender;
upnpMulticastSocket = null;
httpServerPort = Integer.valueOf(theSettings.getServerPort());
upnpConfigIP = theSettings.getUpnpConfigAddress();
@@ -106,6 +137,7 @@ public class UpnpListener {
return false;
}
InetAddress theUpnpAddress = null;
while (ifs.hasMoreElements()) {
NetworkInterface xface = ifs.nextElement();
Enumeration<InetAddress> addrs = xface.getInetAddresses();
@@ -126,6 +158,10 @@ public class UpnpListener {
+ addr);
IPsPerNic++;
}
if (addr.getHostAddress().equals(upnpConfigIP)) {
theUpnpAddress = addr;
}
}
}
log.debug("Checking " + name + " to our interface set");
@@ -143,6 +179,25 @@ public class UpnpListener {
}
}
JmDNS jmdns = null;
if (theUpnpAddress != null) {
log.info("Create and run mDNS service.");
try {
// Create a JmDNS instance
jmdns = JmDNS.create(theUpnpAddress, "Philips-hue");
// Register a service - Defined TXT keys: bridgeid, modelid
final HashMap<String, String> values = new HashMap<String, String>();
values.put("modelid", HueConstants.MODEL_ID);
values.put("bridgeid", bridgeId);
ServiceInfo serviceInfo = ServiceInfo.create("_hue._tcp.local.", "Philips Hue - " + bridgeId.substring(bridgeId.length() - 6), httpServerPort, 0, 0, values);
jmdns.registerService(serviceInfo);
} catch (IOException e) {
log.warn("Could not start mDNS service for hue api. {}", e.getMessage());
}
}
log.info("UPNP Discovery Listener running and ready....");
boolean loopControl = true;
boolean error = false;
@@ -151,8 +206,8 @@ public class UpnpListener {
} catch (SocketException e1) {
log.warn("Could not sent soTimeout on multi-cast socket");
}
Instant current, previous;
previous = Instant.now();
// Instant current, previous;
// previous = Instant.now();
while (loopControl) { // trigger shutdown here
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
@@ -167,15 +222,28 @@ public class UpnpListener {
log.debug("UpnpListener send upnp exception: ", e);
}
}
/*
current = Instant.now();
if (ChronoUnit.MILLIS.between(previous, current) > Configuration.UPNP_NOTIFY_TIMEOUT) {
try {
sendUpnpNotify(socketAddress.getAddress());
previous = Instant.now();
} catch (IOException e) {
log.warn("UpnpListener encountered an error sending upnp notify packets. IP: "
+ packet.getAddress().getHostAddress() + " with message: " + e.getMessage());
log.debug("UpnpListener send upnp notify exception: ", e);
}
previous = Instant.now();
}
*/
} catch (SocketTimeoutException e) {
try {
sendUpnpNotify(socketAddress.getAddress());
} catch (IOException en) {
log.warn("UpnpListener encountered an error sending upnp notify packets. IP: "
+ packet.getAddress().getHostAddress() + " with message: " + en.getMessage());
log.debug("UpnpListener send upnp notify exception: ", en);
}
} catch (IOException e) {
log.error("UpnpListener encountered an error reading socket. Shutting down", e);
error = true;
@@ -190,6 +258,10 @@ public class UpnpListener {
}
}
upnpMulticastSocket.close();
if(jmdns != null) {
// Unregister all services
jmdns.unregisterAllServices();
}
if (bridgeControl.isReinit())
log.info("UPNP Discovery Listener - ended, restart found");
if (bridgeControl.isStop())
@@ -209,8 +281,9 @@ public class UpnpListener {
String packetString = new String(packet.getData(), 0, packet.getLength());
if (packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1")
&& packetString.contains("\"ssdp:discover\"")) {
if ((packetString.contains("ST: urn:schemas-upnp-org:device:basic:1")
|| packetString.contains("ST: upnp:rootdevice") || packetString.contains("ST: ssdp:all"))) {
if ((packetString.contains("ST: urn:schemas-upnp-org:device:basic:1") ||
packetString.contains("ST: upnp:rootdevice") ||
packetString.contains("ST: ssdp:all"))) {
if (traceupnp) {
log.info("Traceupnp: SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":"
+ packet.getPort());
@@ -248,31 +321,6 @@ public class UpnpListener {
// noop
}
if (upnpOriginal) {
discoveryResponse = String.format(responseTemplateOriginal, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: send upnp discovery template Original with response address: "
+ httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " with discovery responseTemplateOriginal is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
} else {
discoveryResponse = String.format(responseTemplateOriginal, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: send upnp discovery template Original with response address: "
+ httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " with discovery responseTemplateOriginal is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) {
@@ -314,17 +362,20 @@ public class UpnpListener {
+ " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
}
}
private void sendUDPResponse(byte[] udpMessage, InetAddress requester, int sourcePort) throws IOException {
log.debug("Sending response string: <<<" + new String(udpMessage) + ">>>");
if(upnpOriginal) {
theUDPDatagramSender.sendUDPResponse(udpMessage, requester, sourcePort);
} else {
if (upnpMulticastSocket == null)
throw new IOException("Socket not initialized");
DatagramPacket response = new DatagramPacket(udpMessage, udpMessage.length, requester, sourcePort);
upnpMulticastSocket.send(response);
}
}
protected void sendUpnpNotify(InetAddress aSocketAddress) {
protected void sendUpnpNotify(InetAddress aSocketAddress) throws IOException {
String notifyData = null;
try {
Thread.sleep(theUpnpSendDelay);
@@ -332,9 +383,13 @@ public class UpnpListener {
// noop
}
notifyData = String.format(notifyTemplate, Configuration.UPNP_MULTICAST_ADDRESS,
notifyData = String.format(notifyTemplate1, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
sendNotifyDatagram(notifyData, aSocketAddress, "notifyTemplate1");
if (traceupnp) {
log.info("Traceupnp: sendUpnpNotify notifyTemplate1");
}
log.debug("sendUpnpNotify notifyTemplate1 is <<<{}>>>", notifyData);
sendUDPResponse(notifyData.getBytes(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
try {
Thread.sleep(theUpnpSendDelay);
@@ -344,7 +399,11 @@ public class UpnpListener {
notifyData = String.format(notifyTemplate2, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID);
sendNotifyDatagram(notifyData, aSocketAddress, "notifyTemplate2");
if (traceupnp) {
log.info("Traceupnp: sendUpnpNotify notifyTemplate2");
}
log.debug("sendUpnpNotify notifyTemplate2 is <<<{}>>>", notifyData);
sendUDPResponse(notifyData.getBytes(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
try {
Thread.sleep(theUpnpSendDelay);
@@ -354,23 +413,11 @@ public class UpnpListener {
notifyData = String.format(notifyTemplate3, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID);
sendNotifyDatagram(notifyData, aSocketAddress, "notifyTemplate3");
}
public void sendNotifyDatagram(String notifyData, InetAddress aSocketAddress, String templateNumber) {
if (traceupnp) {
log.info("Traceupnp: sendUpnpNotify {}", templateNumber);
}
log.debug("sendUpnpNotify {} is <<<{}>>>", templateNumber, notifyData);
DatagramPacket notifyPacket = new DatagramPacket(notifyData.getBytes(), notifyData.length(), aSocketAddress,
Configuration.UPNP_DISCOVERY_PORT);
try {
upnpMulticastSocket.send(notifyPacket);
} catch (IOException e1) {
log.warn("UpnpListener encountered an error sending upnp {}. IP: {} with message: {}", templateNumber,
notifyPacket.getAddress().getHostAddress(), e1.getMessage());
log.debug("UpnpListener send {} exception: ", templateNumber, e1);
log.info("Traceupnp: sendUpnpNotify notifyTemplate3");
}
log.debug("sendUpnpNotify notifyTemplate3 is <<<{}>>>", notifyData);
sendUDPResponse(notifyData.getBytes(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
}
// added by https://github.com/pvint

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -366,12 +366,15 @@ app.service('bridgeService', function ($rootScope, $http, $base64, $location, ng
);
};
this.changeSecuritySettings = function (useLinkButton, secureHueApi, execGarden) {
this.changeSecuritySettings = function (useLinkButton, secureHueApi, execGarden, useHttps, keyfilePath, keyfilePassword) {
var newSecurityInfo = {};
newSecurityInfo = {
useLinkButton: useLinkButton,
secureHueApi: secureHueApi,
execGarden: execGarden
execGarden: execGarden,
useHttps: useHttps,
keyfilePath: keyfilePath,
keyfilePassword: keyfilePassword
};
return $http.post(this.state.systemsbase + "/changesecurityinfo", newSecurityInfo).then(
function (response) {
@@ -2287,13 +2290,17 @@ app.controller('SecurityDialogCtrl', function ($scope, bridgeService, ngDialog)
$scope.useLinkButton = bridgeService.state.securityInfo.useLinkButton;
$scope.execGarden = bridgeService.state.securityInfo.execGarden;
$scope.isSecure = bridgeService.state.securityInfo.isSecure;
$scope.useHttps = bridgeService.state.securityInfo.useHttps;
$scope.keyfilePath = bridgeService.state.securityInfo.keyfilePath;
$scope.keyfilePassword = bridgeService.state.securityInfo.keyfilePassword;
$scope.matched = false;
$scope.addingUser = false;
$scope.showPassword = $scope.isSecure;
$scope.firstTime = true;
$scope.setSecurityInfo = function () {
bridgeService.changeSecuritySettings($scope.useLinkButton, $scope.secureHueApi, $scope.execGarden);
bridgeService.changeSecuritySettings($scope.useLinkButton, $scope.secureHueApi, $scope.execGarden, $scope.useHttps, $scope.keyfilePath, $scope.keyfilePassword);
};
$scope.changePassword = function (password, password2) {
@@ -3667,7 +3674,7 @@ app.controller('HassController', function ($scope, $location, bridgeService, ngD
dimpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"on\"}";
offpayload = "{\"entityId\":\"" + hassdevice.deviceState.entity_id + "\",\"hassName\":\"" + hassdevice.hassname + "\",\"state\":\"off\"}";
bridgeService.buildUrls(onpayload, dimpayload, offpayload, null, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, hassdevice.domain, "hassDevice", null, null);
bridgeService.buildUrls(onpayload, dimpayload, offpayload, null, true, hassdevice.hassname + "-" + hassdevice.deviceState.entity_id, hassdevice.deviceState.entity_id, hassdevice.hassname, "switch", "hassDevice", null, null);
$scope.device = bridgeService.state.device;
if (!buildonly) {
bridgeService.editNewDevice($scope.device);
@@ -3797,7 +3804,7 @@ app.controller('HomeWizardController', function ($scope, $location, bridgeServic
onpayload = "{\"deviceid\":\"" + homewizarddevice.id + "\",\"action\":\"on\"}";
offpayload = "{\"deviceid\":\"" + homewizarddevice.id + "\",\"action\":\"off\"}";
bridgeService.buildUrls(onpayload, dimpayload, offpayload, null, true, homewizarddevice.id, homewizarddevice.name, homewizarddevice.gateway, null, "homewizardDevice", null, null);
bridgeService.buildUrls(onpayload, dimpayload, offpayload, null, true, homewizarddevice.id, homewizarddevice.name, homewizarddevice.gateway, "switch", "homewizardDevice", null, null);
$scope.device = bridgeService.state.device;
if (!buildonly) {
@@ -4091,7 +4098,7 @@ app.controller('LifxController', function ($scope, $location, bridgeService, ngD
dimpayload = angular.toJson(lifxdevice);
onpayload = angular.toJson(lifxdevice);
offpayload = angular.toJson(lifxdevice);
bridgeService.buildUrls(onpayload, dimpayload, offpayload, null, true, lifxdevice.name, lifxdevice.name, lifxdevice.name, null, "lifxDevice", null, null);
bridgeService.buildUrls(onpayload, dimpayload, offpayload, null, true, lifxdevice.name, lifxdevice.name, lifxdevice.name, "switch", "lifxDevice", null, null);
$scope.device = bridgeService.state.device;
if (!buildonly) {
bridgeService.editNewDevice($scope.device);
@@ -4392,7 +4399,7 @@ app.controller('OpenHABController', function ($scope, $location, bridgeService,
else
colorpayload = null;
}
bridgeService.buildUrls(onpayload, dimpayload, offpayload, colorpayload, true, openhabdevice.item.name + "-" + openhabdevice.name, openhabdevice.item.name, openhabdevice.name, openhabdevice.item.type, "openhabDevice", null, null);
bridgeService.buildUrls(onpayload, dimpayload, offpayload, colorpayload, true, openhabdevice.item.name + "-" + openhabdevice.name, openhabdevice.item.name, openhabdevice.name, "switch", "openhabDevice", null, null);
$scope.device = bridgeService.state.device;
if (!buildonly) {
bridgeService.editNewDevice($scope.device);
@@ -4534,7 +4541,7 @@ app.controller('MozIotController', function ($scope, $location, bridgeService, n
else if (coloraction !== undefined && coloraction !== null && coloraction !== '')
colorpayload = "{\"url\":\"" + preCmd + "color\",\"command\":{\"color\":\"" + coloraction + "\"}}";
bridgeService.buildUrls(onpayload, dimpayload, offpayload, colorpayload, true, moziotdevice.deviceDetail.name + "-" + moziotdevice.deviceDetail.type, moziotdevice.deviceDetail.name, moziotdevice.gatewayName, "Switch", "moziotDevice", null, null);
bridgeService.buildUrls(onpayload, dimpayload, offpayload, colorpayload, true, moziotdevice.deviceDetail.title + "-" + moziotdevice.deviceDetail.type, moziotdevice.deviceDetail.title, moziotdevice.gatewayName, "switch", "moziotDevice", null, null);
$scope.device = bridgeService.state.device;
if (!buildonly) {
bridgeService.editNewDevice($scope.device);
@@ -4678,7 +4685,7 @@ app.controller('FhemController', function ($scope, $location, bridgeService, ngD
colorpayload = null;
onpayload = "{\"url\":\"http://" + fhemdevice.address + preCmd + "\",\"command\":\"on\"}";
offpayload = "{\"url\":\"http://" + fhemdevice.address + preCmd + "\",\"command\":\"off\"}";
bridgeService.buildUrls(onpayload, dimpayload, offpayload, colorpayload, true, fhemdevice.item.Name + "-" + fhemdevice.name, fhemdevice.item.Name, fhemdevice.name, fhemdevice.item.type, "fhemDevice", null, null);
bridgeService.buildUrls(onpayload, dimpayload, offpayload, colorpayload, true, fhemdevice.item.Name + "-" + fhemdevice.name, fhemdevice.item.Name, fhemdevice.name, "switch", "fhemDevice", null, null);
$scope.device = bridgeService.state.device;
if (!buildonly) {
bridgeService.editNewDevice($scope.device);
@@ -4837,7 +4844,7 @@ app.controller('BroadlinkController', function ($scope, $location, bridgeService
else
colorpayload = null;
}
bridgeService.buildUrls(onpayload, dimpayload, offpayload, colorpayload, true, broadlinkdevice.id, broadlinkdevice.name, broadlinkdevice.id, broadlinkdevice.desc, "broadlinkDevice", null, null);
bridgeService.buildUrls(onpayload, dimpayload, offpayload, colorpayload, true, broadlinkdevice.id, broadlinkdevice.name, broadlinkdevice.id, "switch", "broadlinkDevice", null, null);
$scope.device = bridgeService.state.device;
if (!buildonly) {
bridgeService.editNewDevice($scope.device);
@@ -4977,7 +4984,7 @@ app.controller('HomeGenieController', function ($scope, $location, bridgeService
// else if (coloraction !== undefined && coloraction !== null && coloraction !== '')
// colorpayload = "{\"url\":\"" + preCmd + "color\",\"command\":{\"color\":\"" + coloraction + "\"}}";
bridgeService.buildUrls(onpayload, dimpayload, offpayload, colorpayload, true, homegeniedevice.deviceDetail.Name + "-" + homegeniedevice.deviceDetail.DeviceType, homegeniedevice.deviceDetail.Name, homegeniedevice.gatewayName, homegeniedevice.deviceDetail.DeviceType, "homegenieDevice", null, null);
bridgeService.buildUrls(onpayload, dimpayload, offpayload, colorpayload, true, homegeniedevice.deviceDetail.Name + "-" + homegeniedevice.deviceDetail.DeviceType, homegeniedevice.deviceDetail.Name, homegeniedevice.gatewayName, "switch", "homegenieDevice", null, null);
$scope.device = bridgeService.state.device;
if (!buildonly) {
bridgeService.editNewDevice($scope.device);

View File

@@ -57,6 +57,7 @@
<option value="button">Button</option>
<option value="thermo">Thermo</option>
<option value="passthru">Pass Thru</option>
<option value="home">Home</option>
</select>
</div>

View File

@@ -144,6 +144,7 @@
<option value="button">Button</option>
<option value="thermo">Thermo</option>
<option value="passthru">Pass Thru</option>
<option value="home">Home</option>
</select></td>
</tr>
<tr>

View File

@@ -19,7 +19,7 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation" class="active"><a href="#!/homegeniedevices">HomeGenie
Devices</a></li>
@@ -28,7 +28,7 @@
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">HomeGenie show Device List
<h2 class="panel-title">HomeGenie show device List
({{bridge.homegeniedevices.length}})</h2>
</div>
<div class="panel-body">
@@ -96,7 +96,7 @@
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
Already Configured OpenHAB Devices <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}}
Already Configured HomeGenie Devices <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}}
aria-hidden="true"></span></a></a>
</h2>
</div>

View File

@@ -19,10 +19,9 @@
<li ng-if="bridge.showHomeWizard" role="presentation"><a href="#!/homewizarddevices">HomeWizard Devices</a></li>
<li ng-if="bridge.showOpenHAB" role="presentation"><a href="#!/openhabdevices">OpenHAB Devices</a></li>
<li ng-if="bridge.showFHEM" role="presentation"><a href="#!/fhemdevices">FHEM Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li ng-if="bridge.showMozIot" role="presentation" class="active"><a href="#!/moziotdevices">Mozilla IOT Devices</a>
</li>
<li ng-if="bridge.showMozIot" role="presentation" class="active"><a href="#!/moziotdevices">Mozilla IOT Devices</a></li>
<li ng-if="bridge.showBroadlink" role="presentation"><a href="#!/broadlinkdevices">Broadlink Devices</a></li>
<li ng-if="bridge.showHomeGenie" role="presentation"><a href="#!/homegeniedevices">HomeGenie Devices</a></li>
<li role="presentation"><a href="#!/editdevice">Add/Edit</a></li>
</ul>
@@ -72,10 +71,10 @@
</thead>
<tr ng-repeat="moziotdevice in bridge.moziotdevices">
<td>{{$index+1}}</td>
<td><input type="checkbox" name="bulk.devices[]" value="{{moziotdevice.deviceDetail.name}}"
ng-checked="bulk.devices.indexOf(moziotdevice.deviceDetail.name) > -1"
ng-click="toggleSelection(moziotdevice.deviceDetail.name)">
{{moziotdevice.deviceDetail.name}}</td>
<td><input type="checkbox" name="bulk.devices[]" value="{{moziotdevice.deviceDetail.title}}"
ng-checked="bulk.devices.indexOf(moziotdevice.deviceDetail.title) > -1"
ng-click="toggleSelection(moziotdevice.deviceDetail.title)">
{{moziotdevice.deviceDetail.title}}</td>
<td>{{moziotdevice.deviceDetail.type}}</td>
<td>{{moziotdevice.gatewayName}}</td>
<td>
@@ -104,7 +103,7 @@
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
Already Configured OpenHAB Devices <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}}
Already Configured Mozilla IOT Devices <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}}
aria-hidden="true"></span></a></a>
</h2>
</div>

View File

@@ -2,11 +2,31 @@
<form name="securityForm" role="form">
<legend class="form-label">Update Security Settings</legend>
<div class="form-group">
<label>Use HTTPS</label>
<input type="checkbox"
ng-model="useHttps" ng-true-value=true
ng-false-value=false /> {{useHttps}}
<div>
<label>Keyfile Path</label>
<input id="keyfilePath" name="keyfilePath" class="form-control"
type="text" ng-model="keyfilePath" placeholder="the keyfile path"/>
<label>Keyfile Password</label>
<input id="keyfilePassword" name="keyfilePassword" class="form-control" type="password" ng-model="keyfilePassword"/>
</div>
</div>
<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>Exec Garden</label>
<input id="execGarden" name="execGarden" class="form-control"
type="text" ng-model="execGarden" placeholder="execGarden path"/>
</div>
<div class="form-group">
<label>Use username/password for HUE Api</label>

View File

@@ -699,7 +699,7 @@
<td><input type="checkbox" ng-model="newmoziotsecure" ng-true-value=true
ng-false-value=false></td>
<td><button class="btn btn-success" type="submit"
ng-click="addMozIottoSettings(newmoziotname, newmoziotip, newmoziotport, newsmoziotusername, newmoziotpassword, newmoziotwebhook, newmoziotsecure)">Add</button>
ng-click="addMozIottoSettings(newmoziotname, newmoziotip, newmoziotport, newmoziotusername, newmoziotpassword, newmoziotwebhook, newmoziotsecure)">Add</button>
</td>
</tr>
</table>