mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-18 16:17:30 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77d3084b01 | ||
|
|
922bb54143 | ||
|
|
8586bbd965 | ||
|
|
d64a028f30 | ||
|
|
fff30d17d6 | ||
|
|
13fa5dea73 | ||
|
|
1d6a4c1432 | ||
|
|
9cb275230e | ||
|
|
c97ab2cd38 | ||
|
|
7b45ca9438 | ||
|
|
e6da9950d6 | ||
|
|
20328b15d8 | ||
|
|
2ff73e5672 | ||
|
|
cca9a6be78 | ||
|
|
5d5b68209a | ||
|
|
d2e906caa3 | ||
|
|
a1708f2a88 | ||
|
|
49c3b85894 | ||
|
|
f9f5a3a878 | ||
|
|
2565183ee9 | ||
|
|
a6bb1ae3aa | ||
|
|
4bc91be88b | ||
|
|
315fd31270 | ||
|
|
09787fe08d | ||
|
|
37d346f558 | ||
|
|
ac59398aa0 | ||
|
|
87073435fc | ||
|
|
8687f3482a | ||
|
|
32a5f26ddd | ||
|
|
c28f07d628 | ||
|
|
d3cc961dfb | ||
|
|
1b3d826f28 | ||
|
|
c8f4d89a45 | ||
|
|
2b335d6b9b | ||
|
|
b27bb5eef8 | ||
|
|
3c54ccd56d | ||
|
|
cf772334c4 | ||
|
|
5a843f7569 | ||
|
|
2a52783bb1 | ||
|
|
195f1854ec | ||
|
|
2e5596a6e4 | ||
|
|
9fc13c6c45 | ||
|
|
4b4d4e36c7 | ||
|
|
1e7bdc560b | ||
|
|
aff0f8d64c | ||
|
|
7a812d6e6b | ||
|
|
26f2105801 | ||
|
|
feef345a3b |
18
pom.xml
18
pom.xml
@@ -5,11 +5,11 @@
|
||||
|
||||
<groupId>com.bwssystems.HABridge</groupId>
|
||||
<artifactId>ha-bridge</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<version>1.4.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HA Bridge</name>
|
||||
<description>Emulates a Philips Hue bridge to allow the Amazon Echo to hook up to other HA systems, i.e. Vera or Harmony Hub, using lightweight frameworks</description>
|
||||
<description>Emulates a Philips Hue bridge to allow the Amazon Echo to hook up to other HA systems, i.e. Vera or Harmony Hub or Nest, using lightweight frameworks</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
@@ -30,16 +30,26 @@
|
||||
<artifactId>harmony-java-client</artifactId>
|
||||
<version>1.0.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.bwssytems</groupId>
|
||||
<artifactId>nest-controller</artifactId>
|
||||
<version>1.0.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sparkjava</groupId>
|
||||
<artifactId>spark-core</artifactId>
|
||||
<version>2.2</version>
|
||||
<version>2.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.3.6</version>
|
||||
<version>4.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
<version>4.4.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
|
||||
95
src/main/java/com/bwssystems/HABridge/BackupHandler.java
Normal file
95
src/main/java/com/bwssystems/HABridge/BackupHandler.java
Normal file
@@ -0,0 +1,95 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class BackupHandler {
|
||||
final Logger log = LoggerFactory.getLogger(BackupHandler.class);
|
||||
private Path repositoryPath;
|
||||
private String fileExtension;
|
||||
private String defaultName;
|
||||
|
||||
protected void setupParams(Path aPath, String anExtension, String adefaultName) {
|
||||
repositoryPath = aPath;
|
||||
if(anExtension.substring(0, 1).equalsIgnoreCase("."))
|
||||
fileExtension = anExtension;
|
||||
else
|
||||
fileExtension = "." + anExtension;
|
||||
|
||||
defaultName = adefaultName;
|
||||
|
||||
log.debug("setupParams has defaultName: " + defaultName + " and file extension as: " + fileExtension);
|
||||
}
|
||||
|
||||
public String backup(String aFilename) {
|
||||
if(aFilename == null || aFilename.equalsIgnoreCase("")) {
|
||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||
aFilename = defaultName + dateFormat.format(Calendar.getInstance().getTime()) + fileExtension;
|
||||
}
|
||||
else
|
||||
aFilename = aFilename + fileExtension;
|
||||
try {
|
||||
Files.copy(repositoryPath, FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename), StandardCopyOption.COPY_ATTRIBUTES);
|
||||
} catch (IOException e) {
|
||||
log.error("Could not backup to file: " + aFilename + " message: " + e.getMessage(), e);
|
||||
}
|
||||
log.debug("Backup repository: " + aFilename);
|
||||
return aFilename;
|
||||
}
|
||||
|
||||
public String deleteBackup(String aFilename) {
|
||||
log.debug("Delete backup repository: " + aFilename);
|
||||
try {
|
||||
Files.delete(FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename));
|
||||
} catch (IOException e) {
|
||||
log.error("Could not delete file: " + aFilename + " message: " + e.getMessage(), e);
|
||||
}
|
||||
return aFilename;
|
||||
}
|
||||
|
||||
public String restoreBackup(String aFilename) {
|
||||
log.debug("Restore backup repository: " + aFilename);
|
||||
try {
|
||||
Path target = null;
|
||||
if(Files.exists(repositoryPath)) {
|
||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||
target = FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), defaultName + dateFormat.format(Calendar.getInstance().getTime()) + fileExtension);
|
||||
Files.move(repositoryPath, target);
|
||||
}
|
||||
Files.copy(FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename), repositoryPath, StandardCopyOption.COPY_ATTRIBUTES);
|
||||
} catch (IOException e) {
|
||||
log.error("Error restoring the file: " + aFilename + " message: " + e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
return aFilename;
|
||||
}
|
||||
|
||||
public List<String> getBackups() {
|
||||
List<String> theFilenames = new ArrayList<String>();
|
||||
Path dir = repositoryPath.getParent();
|
||||
try (DirectoryStream<Path> stream =
|
||||
Files.newDirectoryStream(dir, "*.{"+ fileExtension.substring(1) + "}")) {
|
||||
for (Path entry: stream) {
|
||||
theFilenames.add(entry.getFileName().toString());
|
||||
}
|
||||
} catch (IOException x) {
|
||||
// IOException can never be thrown by the iteration.
|
||||
// In this snippet, it can // only be thrown by newDirectoryStream.
|
||||
log.warn("Issue getting directory listing for backups in directory: " + x.getMessage());
|
||||
}
|
||||
return theFilenames;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class BridgeControlDescriptor {
|
||||
private boolean reinit;
|
||||
private boolean stop;
|
||||
|
||||
public BridgeControlDescriptor() {
|
||||
super();
|
||||
this.reinit = false;
|
||||
this.stop = false;
|
||||
}
|
||||
|
||||
public boolean isReinit() {
|
||||
return reinit;
|
||||
}
|
||||
public void setReinit(boolean reinit) {
|
||||
this.reinit = reinit;
|
||||
}
|
||||
public boolean isStop() {
|
||||
return stop;
|
||||
}
|
||||
public void setStop(boolean stop) {
|
||||
this.stop = stop;
|
||||
}
|
||||
}
|
||||
@@ -1,96 +1,239 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class BridgeSettings {
|
||||
private String upnpconfigaddress;
|
||||
private String serverport;
|
||||
private String upnpresponseport;
|
||||
private String upnpdevicedb;
|
||||
private String veraaddress;
|
||||
private String harmonyaddress;
|
||||
private String harmonyuser;
|
||||
private String harmonypwd;
|
||||
private boolean upnpstrict;
|
||||
private boolean traceupnp;
|
||||
private boolean devmode;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class BridgeSettings extends BackupHandler {
|
||||
private BridgeSettingsDescriptor theBridgeSettings;
|
||||
private BridgeControlDescriptor bridgeControl;
|
||||
|
||||
public String getUpnpConfigAddress() {
|
||||
return upnpconfigaddress;
|
||||
public BridgeSettings() {
|
||||
super();
|
||||
bridgeControl = new BridgeControlDescriptor();
|
||||
theBridgeSettings = new BridgeSettingsDescriptor();
|
||||
}
|
||||
public void setUpnpConfigAddress(String upnpConfigAddress) {
|
||||
this.upnpconfigaddress = upnpConfigAddress;
|
||||
public BridgeControlDescriptor getBridgeControl() {
|
||||
return bridgeControl;
|
||||
}
|
||||
public String getServerPort() {
|
||||
return serverport;
|
||||
public BridgeSettingsDescriptor getBridgeSettingsDescriptor() {
|
||||
return theBridgeSettings;
|
||||
}
|
||||
public void setServerPort(String serverPort) {
|
||||
this.serverport = serverPort;
|
||||
public void buildSettings() {
|
||||
Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
InetAddress address = null;
|
||||
String addressString = null;
|
||||
String theVeraAddress = null;
|
||||
String theHarmonyAddress = null;
|
||||
String configFileProperty = System.getProperty("config.file");
|
||||
if(configFileProperty == null) {
|
||||
Path filePath = Paths.get(Configuration.CONFIG_FILE);
|
||||
if(Files.exists(filePath) && Files.isReadable(filePath))
|
||||
configFileProperty = Configuration.CONFIG_FILE;
|
||||
}
|
||||
if(configFileProperty != null)
|
||||
{
|
||||
log.info("reading from config file: " + configFileProperty);
|
||||
theBridgeSettings.setConfigfile(configFileProperty);
|
||||
_loadConfig();
|
||||
}
|
||||
else
|
||||
{
|
||||
log.info("reading from system properties");
|
||||
theBridgeSettings.setConfigfile(Configuration.CONFIG_FILE);
|
||||
theBridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DEFAULT_WEB_PORT));
|
||||
theBridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address"));
|
||||
theBridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db"));
|
||||
theBridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT));
|
||||
|
||||
theVeraAddress = System.getProperty("vera.address");
|
||||
IpList theVeraList = null;
|
||||
if(theVeraAddress != null) {
|
||||
try {
|
||||
theVeraList = new Gson().fromJson(theVeraAddress, IpList.class);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
theVeraList = new Gson().fromJson("{devices:[{name:default,ip:" + theVeraAddress + "}]}", IpList.class);
|
||||
} catch (Exception et) {
|
||||
log.error("Cannot parse vera.address, not set with message: " + e.getMessage(), e);
|
||||
theVeraList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
theBridgeSettings.setVeraAddress(theVeraList);
|
||||
|
||||
theHarmonyAddress = System.getProperty("harmony.address");
|
||||
IpList theHarmonyList = null;
|
||||
if(theHarmonyAddress != null) {
|
||||
try {
|
||||
theHarmonyList = new Gson().fromJson(theHarmonyAddress, IpList.class);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
theHarmonyList = new Gson().fromJson("{devices:[{name:default,ip:" + theHarmonyAddress + "}]}", IpList.class);
|
||||
} catch (Exception et) {
|
||||
log.error("Cannot parse harmony.address, not set with message: " + e.getMessage(), e);
|
||||
theHarmonyList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
theBridgeSettings.setHarmonyAddress(theHarmonyList);
|
||||
theBridgeSettings.setHarmonyUser(System.getProperty("harmony.user"));
|
||||
theBridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd"));
|
||||
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
|
||||
theBridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
|
||||
theBridgeSettings.setButtonsleep(Integer.parseInt(System.getProperty("button.sleep", Configuration.DEFAULT_BUTTON_SLEEP)));
|
||||
theBridgeSettings.setNestuser(System.getProperty("nest.user"));
|
||||
theBridgeSettings.setNestpwd(System.getProperty("nest.pwd"));
|
||||
}
|
||||
|
||||
if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().equals("")) {
|
||||
try {
|
||||
log.info("Getting an IP address for this host....");
|
||||
Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();
|
||||
|
||||
while (ifs.hasMoreElements() && addressString == null) {
|
||||
NetworkInterface xface = ifs.nextElement();
|
||||
Enumeration<InetAddress> addrs = xface.getInetAddresses();
|
||||
String name = xface.getName();
|
||||
int IPsPerNic = 0;
|
||||
|
||||
while (addrs.hasMoreElements() && IPsPerNic == 0) {
|
||||
address = addrs.nextElement();
|
||||
if (InetAddressUtils.isIPv4Address(address.getHostAddress())) {
|
||||
log.debug(name + " ... has IPV4 addr " + address);
|
||||
if(!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE)|| !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS)) {
|
||||
IPsPerNic++;
|
||||
addressString = address.getHostAddress();
|
||||
log.info("Adding " + addressString + " from interface " + name + " as our default upnp config address.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
log.error("Cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
theBridgeSettings.setUpnpConfigAddress(addressString);
|
||||
}
|
||||
|
||||
if(theBridgeSettings.getUpnpResponsePort() == null)
|
||||
theBridgeSettings.setUpnpResponsePort(Configuration.UPNP_RESPONSE_PORT);
|
||||
|
||||
if(theBridgeSettings.getServerPort() == null)
|
||||
theBridgeSettings.setServerPort(Configuration.DEFAULT_WEB_PORT);
|
||||
|
||||
if(theBridgeSettings.getUpnpDeviceDb() == null)
|
||||
theBridgeSettings.setUpnpDeviceDb(Configuration.DEVICE_DB_DIRECTORY);
|
||||
|
||||
if(theBridgeSettings.getButtonsleep() <= 0)
|
||||
theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP));
|
||||
|
||||
theBridgeSettings.setVeraconfigured(theBridgeSettings.isValidVera());
|
||||
theBridgeSettings.setHarmonyconfigured(theBridgeSettings.isValidHarmony());
|
||||
theBridgeSettings.setNestConfigured(theBridgeSettings.isValidNest());
|
||||
setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-");
|
||||
}
|
||||
public String getUpnpResponsePort() {
|
||||
return upnpresponseport;
|
||||
|
||||
public void loadConfig() {
|
||||
if(theBridgeSettings.getConfigfile() != null)
|
||||
_loadConfig();
|
||||
}
|
||||
public void setUpnpResponsePort(String upnpResponsePort) {
|
||||
this.upnpresponseport = upnpResponsePort;
|
||||
private void _loadConfig() {
|
||||
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
|
||||
_loadConfig(configPath);
|
||||
}
|
||||
|
||||
private void _loadConfig(Path aPath) {
|
||||
String jsonContent = configReader(aPath);
|
||||
BridgeSettingsDescriptor aBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettingsDescriptor.class);
|
||||
theBridgeSettings.setButtonsleep(aBridgeSettings.getButtonsleep());
|
||||
theBridgeSettings.setUpnpConfigAddress(aBridgeSettings.getUpnpConfigAddress());
|
||||
theBridgeSettings.setServerPort(aBridgeSettings.getServerPort());
|
||||
theBridgeSettings.setUpnpResponsePort(aBridgeSettings.getUpnpResponsePort());
|
||||
theBridgeSettings.setUpnpDeviceDb(aBridgeSettings.getUpnpDeviceDb());
|
||||
theBridgeSettings.setVeraAddress(aBridgeSettings.getVeraAddress());
|
||||
theBridgeSettings.setHarmonyAddress(aBridgeSettings.getHarmonyAddress());
|
||||
theBridgeSettings.setHarmonyUser(aBridgeSettings.getHarmonyUser());
|
||||
theBridgeSettings.setHarmonyPwd(aBridgeSettings.getHarmonyPwd());
|
||||
theBridgeSettings.setUpnpStrict(aBridgeSettings.isUpnpStrict());
|
||||
theBridgeSettings.setTraceupnp(aBridgeSettings.isTraceupnp());
|
||||
theBridgeSettings.setNestuser(aBridgeSettings.getNestuser());
|
||||
theBridgeSettings.setNestpwd(aBridgeSettings.getNestpwd());
|
||||
theBridgeSettings.setVeraconfigured(aBridgeSettings.isValidVera());
|
||||
theBridgeSettings.setHarmonyconfigured(aBridgeSettings.isValidHarmony());
|
||||
theBridgeSettings.setNestConfigured(aBridgeSettings.isValidNest());
|
||||
}
|
||||
|
||||
public void save(BridgeSettingsDescriptor newBridgeSettings) {
|
||||
Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
log.debug("Save HA Bridge settings.");
|
||||
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
String jsonValue = aRenderer.render(newBridgeSettings);
|
||||
configWriter(jsonValue, configPath);
|
||||
_loadConfig(configPath);
|
||||
}
|
||||
|
||||
|
||||
private void configWriter(String content, Path filePath) {
|
||||
Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
if(Files.exists(filePath) && !Files.isWritable(filePath)){
|
||||
log.error("Error file is not writable: " + filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Files.notExists(filePath.getParent())) {
|
||||
try {
|
||||
Files.createDirectories(filePath.getParent());
|
||||
} catch (IOException e) {
|
||||
log.error("Error creating the directory: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Path target = null;
|
||||
if(Files.exists(filePath)) {
|
||||
target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "habridge.config.old");
|
||||
Files.move(filePath, target);
|
||||
}
|
||||
Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE);
|
||||
if(target != null)
|
||||
Files.delete(target);
|
||||
} catch (IOException e) {
|
||||
log.error("Error writing the file: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
public String getUpnpDeviceDb() {
|
||||
return upnpdevicedb;
|
||||
}
|
||||
public void setUpnpDeviceDb(String upnpDeviceDb) {
|
||||
this.upnpdevicedb = upnpDeviceDb;
|
||||
}
|
||||
public String getVeraAddress() {
|
||||
return veraaddress;
|
||||
}
|
||||
public void setVeraAddress(String veraAddress) {
|
||||
this.veraaddress = veraAddress;
|
||||
}
|
||||
public String getHarmonyAddress() {
|
||||
return harmonyaddress;
|
||||
}
|
||||
public void setHarmonyAddress(String harmonyaddress) {
|
||||
this.harmonyaddress = harmonyaddress;
|
||||
}
|
||||
public String getHarmonyUser() {
|
||||
return harmonyuser;
|
||||
}
|
||||
public void setHarmonyUser(String harmonyuser) {
|
||||
this.harmonyuser = harmonyuser;
|
||||
}
|
||||
public String getHarmonyPwd() {
|
||||
return harmonypwd;
|
||||
}
|
||||
public void setHarmonyPwd(String harmonypwd) {
|
||||
this.harmonypwd = harmonypwd;
|
||||
}
|
||||
public boolean isUpnpStrict() {
|
||||
return upnpstrict;
|
||||
}
|
||||
public void setUpnpStrict(boolean upnpStrict) {
|
||||
this.upnpstrict = upnpStrict;
|
||||
}
|
||||
public boolean isTraceupnp() {
|
||||
return traceupnp;
|
||||
}
|
||||
public void setTraceupnp(boolean traceupnp) {
|
||||
this.traceupnp = traceupnp;
|
||||
}
|
||||
public boolean isDevMode() {
|
||||
return devmode;
|
||||
}
|
||||
public void setDevMode(boolean devmode) {
|
||||
this.devmode = devmode;
|
||||
}
|
||||
public Boolean isValidVera() {
|
||||
if(this.veraaddress.contains(Configuration.DEFAULT_VERA_ADDRESS))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidHarmony() {
|
||||
if(this.harmonyaddress.contains(Configuration.DEFAULT_HARMONY_ADDRESS))
|
||||
return false;
|
||||
if(this.harmonypwd == null || this.harmonypwd == "")
|
||||
return false;
|
||||
if(this.harmonyuser == null || this.harmonyuser == "")
|
||||
return false;
|
||||
return true;
|
||||
|
||||
private String configReader(Path filePath) {
|
||||
Logger log = LoggerFactory.getLogger(BridgeSettings.class);
|
||||
|
||||
String content = null;
|
||||
if(Files.notExists(filePath) || !Files.isReadable(filePath)){
|
||||
log.warn("Error reading the file: " + filePath + " - Does not exist or is not readable. continuing...");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
content = new String(Files.readAllBytes(filePath));
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BridgeSettingsDescriptor {
|
||||
private String upnpconfigaddress;
|
||||
private Integer serverport;
|
||||
private Integer upnpresponseport;
|
||||
private String upnpdevicedb;
|
||||
private IpList veraaddress;
|
||||
private IpList harmonyaddress;
|
||||
private String harmonyuser;
|
||||
private String harmonypwd;
|
||||
private Integer buttonsleep;
|
||||
private boolean upnpstrict;
|
||||
private boolean traceupnp;
|
||||
private String nestuser;
|
||||
private String nestpwd;
|
||||
private boolean veraconfigured;
|
||||
private boolean harmonyconfigured;
|
||||
private boolean nestconfigured;
|
||||
private String configfile;
|
||||
|
||||
public BridgeSettingsDescriptor() {
|
||||
super();
|
||||
this.upnpstrict = true;
|
||||
this.traceupnp = false;
|
||||
this.nestconfigured = false;
|
||||
this.veraconfigured = false;
|
||||
this.harmonyconfigured = false;
|
||||
}
|
||||
public String getUpnpConfigAddress() {
|
||||
return upnpconfigaddress;
|
||||
}
|
||||
public void setUpnpConfigAddress(String upnpConfigAddress) {
|
||||
this.upnpconfigaddress = upnpConfigAddress;
|
||||
}
|
||||
public Integer getServerPort() {
|
||||
return serverport;
|
||||
}
|
||||
public void setServerPort(Integer serverPort) {
|
||||
this.serverport = serverPort;
|
||||
}
|
||||
public void setServerPort(String serverPort) {
|
||||
this.serverport = Integer.valueOf(serverPort);
|
||||
}
|
||||
public Integer getUpnpResponsePort() {
|
||||
return upnpresponseport;
|
||||
}
|
||||
public void setUpnpResponsePort(Integer upnpResponsePort) {
|
||||
this.upnpresponseport = upnpResponsePort;
|
||||
}
|
||||
public void setUpnpResponsePort(String upnpResponsePort) {
|
||||
this.upnpresponseport = Integer.valueOf(upnpResponsePort);
|
||||
}
|
||||
public String getUpnpDeviceDb() {
|
||||
return upnpdevicedb;
|
||||
}
|
||||
public void setUpnpDeviceDb(String upnpDeviceDb) {
|
||||
this.upnpdevicedb = upnpDeviceDb;
|
||||
}
|
||||
public IpList getVeraAddress() {
|
||||
return veraaddress;
|
||||
}
|
||||
public void setVeraAddress(IpList veraAddress) {
|
||||
this.veraaddress = veraAddress;
|
||||
}
|
||||
public IpList getHarmonyAddress() {
|
||||
return harmonyaddress;
|
||||
}
|
||||
public void setHarmonyAddress(IpList harmonyaddress) {
|
||||
this.harmonyaddress = harmonyaddress;
|
||||
}
|
||||
public String getHarmonyUser() {
|
||||
return harmonyuser;
|
||||
}
|
||||
public void setHarmonyUser(String harmonyuser) {
|
||||
this.harmonyuser = harmonyuser;
|
||||
}
|
||||
public String getHarmonyPwd() {
|
||||
return harmonypwd;
|
||||
}
|
||||
public void setHarmonyPwd(String harmonypwd) {
|
||||
this.harmonypwd = harmonypwd;
|
||||
}
|
||||
public boolean isUpnpStrict() {
|
||||
return upnpstrict;
|
||||
}
|
||||
public void setUpnpStrict(boolean upnpStrict) {
|
||||
this.upnpstrict = upnpStrict;
|
||||
}
|
||||
public boolean isTraceupnp() {
|
||||
return traceupnp;
|
||||
}
|
||||
public void setTraceupnp(boolean traceupnp) {
|
||||
this.traceupnp = traceupnp;
|
||||
}
|
||||
public String getNestuser() {
|
||||
return nestuser;
|
||||
}
|
||||
public void setNestuser(String nestuser) {
|
||||
this.nestuser = nestuser;
|
||||
}
|
||||
public String getNestpwd() {
|
||||
return nestpwd;
|
||||
}
|
||||
public void setNestpwd(String nestpwd) {
|
||||
this.nestpwd = nestpwd;
|
||||
}
|
||||
public boolean isVeraconfigured() {
|
||||
return veraconfigured;
|
||||
}
|
||||
public void setVeraconfigured(boolean veraconfigured) {
|
||||
this.veraconfigured = veraconfigured;
|
||||
}
|
||||
public boolean isHarmonyconfigured() {
|
||||
return harmonyconfigured;
|
||||
}
|
||||
public void setHarmonyconfigured(boolean harmonyconfigured) {
|
||||
this.harmonyconfigured = harmonyconfigured;
|
||||
}
|
||||
public boolean isNestConfigured() {
|
||||
return nestconfigured;
|
||||
}
|
||||
public void setNestConfigured(boolean isNestConfigured) {
|
||||
this.nestconfigured = isNestConfigured;
|
||||
}
|
||||
public Integer getButtonsleep() {
|
||||
return buttonsleep;
|
||||
}
|
||||
public void setButtonsleep(Integer buttonsleep) {
|
||||
this.buttonsleep = buttonsleep;
|
||||
}
|
||||
public String getConfigfile() {
|
||||
return configfile;
|
||||
}
|
||||
public void setConfigfile(String configfile) {
|
||||
this.configfile = configfile;
|
||||
}
|
||||
public Boolean isValidVera() {
|
||||
if(this.getVeraAddress() == null)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getVeraAddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidHarmony() {
|
||||
if(this.getHarmonyAddress() == null || this.getHarmonyAddress().getDevices().size() <= 0)
|
||||
return false;
|
||||
List<NamedIP> devicesList = this.getHarmonyAddress().getDevices();
|
||||
if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
|
||||
return false;
|
||||
if(this.getHarmonyPwd() == null || this.getHarmonyPwd().equals(""))
|
||||
return false;
|
||||
if(this.getHarmonyUser() == null || this.getHarmonyUser().equals(""))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public Boolean isValidNest() {
|
||||
if(this.getNestpwd() == null || this.getNestpwd().equals(""))
|
||||
return false;
|
||||
if(this.getNestuser() == null || this.getNestuser().equals(""))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,9 +3,12 @@ package com.bwssystems.HABridge;
|
||||
public class Configuration {
|
||||
public final static String DEVICE_DB_DIRECTORY = "data/device.db";
|
||||
public final static String UPNP_RESPONSE_PORT = "50000";
|
||||
public final static String DEFAULT_VERA_ADDRESS = "1.1.1.1";
|
||||
public final static String DEFAULT_HARMONY_ADDRESS = "1.1.1.1";
|
||||
public final static String DEFAULT_HARMONY_USER = "";
|
||||
public final static String DEFAULT_HARMONY_PWD = "";
|
||||
public final static String DFAULT_WEB_PORT = "8080";
|
||||
public final static String DEFAULT_ADDRESS = "1.1.1.1";
|
||||
public final static String LOOP_BACK_ADDRESS = "127.0.0.1";
|
||||
public final static String LOOP_BACK_INTERFACE = "lo";
|
||||
public final static String DEFAULT_WEB_PORT = "8080";
|
||||
public final static String DEFAULT_BUTTON_SLEEP = "100";
|
||||
public static final int UPNP_DISCOVERY_PORT = 1900;
|
||||
public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250";
|
||||
public static final String CONFIG_FILE = "data/habridge.config";
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@ package com.bwssystems.HABridge;
|
||||
|
||||
import static spark.Spark.*;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -12,7 +9,8 @@ import com.bwssystems.HABridge.devicemanagmeent.*;
|
||||
import com.bwssystems.HABridge.hue.HueMulator;
|
||||
import com.bwssystems.HABridge.upnp.UpnpListener;
|
||||
import com.bwssystems.HABridge.upnp.UpnpSettingsResource;
|
||||
import com.bwssystems.harmony.HarmonyServer;
|
||||
import com.bwssystems.NestBridge.NestHome;
|
||||
import com.bwssystems.harmony.HarmonyHome;
|
||||
|
||||
public class HABridge {
|
||||
|
||||
@@ -34,66 +32,60 @@ public class HABridge {
|
||||
public static void main(String[] args) {
|
||||
Logger log = LoggerFactory.getLogger(HABridge.class);
|
||||
DeviceResource theResources;
|
||||
HarmonyServer myHarmonyServer;
|
||||
HarmonyHome harmonyHome;
|
||||
NestHome nestHome;
|
||||
HueMulator theHueMulator;
|
||||
UpnpSettingsResource theSettingResponder;
|
||||
UpnpListener theUpnpListener;
|
||||
InetAddress address;
|
||||
String addressString;
|
||||
SystemControl theSystem;
|
||||
BridgeSettings bridgeSettings;
|
||||
Version theVersion;
|
||||
|
||||
theVersion = new Version();
|
||||
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") starting setup....");
|
||||
//get ip address for upnp requests
|
||||
try {
|
||||
address = InetAddress.getLocalHost();
|
||||
addressString = address.getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
log.error("Cannot get ip address of this host, Exiting with message: " + e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") starting....");
|
||||
|
||||
bridgeSettings = new BridgeSettings();
|
||||
bridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DFAULT_WEB_PORT));
|
||||
bridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address", addressString));
|
||||
bridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db", Configuration.DEVICE_DB_DIRECTORY));
|
||||
bridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT));
|
||||
bridgeSettings.setVeraAddress(System.getProperty("vera.address", Configuration.DEFAULT_VERA_ADDRESS));
|
||||
bridgeSettings.setHarmonyAddress(System.getProperty("harmony.address", Configuration.DEFAULT_HARMONY_ADDRESS));
|
||||
bridgeSettings.setHarmonyUser(System.getProperty("harmony.user", Configuration.DEFAULT_HARMONY_USER));
|
||||
bridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd", Configuration.DEFAULT_HARMONY_PWD));
|
||||
bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
|
||||
bridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
|
||||
bridgeSettings.setDevMode(Boolean.parseBoolean(System.getProperty("dev.mode", "false")));
|
||||
while(!bridgeSettings.getBridgeControl().isStop()) {
|
||||
bridgeSettings.buildSettings();
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") initializing....");
|
||||
// sparkjava config directive to set ip address for the web server to listen on
|
||||
// ipAddress("0.0.0.0"); // not used
|
||||
// sparkjava config directive to set port for the web server to listen on
|
||||
port(bridgeSettings.getBridgeSettingsDescriptor().getServerPort());
|
||||
// sparkjava config directive to set html static file location for Jetty
|
||||
staticFileLocation("/public");
|
||||
// setup system control api first
|
||||
theSystem = new SystemControl(bridgeSettings, theVersion);
|
||||
theSystem.setupServer();
|
||||
//setup the harmony connection if available
|
||||
harmonyHome = new HarmonyHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
//setup the nest connection if available
|
||||
nestHome = new NestHome(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
// setup the class to handle the resource setup rest api
|
||||
theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), harmonyHome, nestHome);
|
||||
// setup the class to handle the hue emulator rest api
|
||||
theHueMulator = new HueMulator(bridgeSettings.getBridgeSettingsDescriptor(), theResources.getDeviceRepository(), harmonyHome, nestHome);
|
||||
theHueMulator.setupServer();
|
||||
// setup the class to handle the upnp response rest api
|
||||
theSettingResponder = new UpnpSettingsResource(bridgeSettings.getBridgeSettingsDescriptor());
|
||||
theSettingResponder.setupServer();
|
||||
// wait for the sparkjava initialization of the rest api classes to be complete
|
||||
awaitInitialization();
|
||||
|
||||
// start the upnp ssdp discovery listener
|
||||
theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl());
|
||||
if(theUpnpListener.startListening())
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") reinitialization requessted....");
|
||||
|
||||
// sparkjava config directive to set ip address for the web server to listen on
|
||||
// ipAddress("0.0.0.0"); // not used
|
||||
// sparkjava config directive to set port for the web server to listen on
|
||||
port(Integer.valueOf(bridgeSettings.getServerPort()));
|
||||
// sparkjava config directive to set html static file location for Jetty
|
||||
staticFileLocation("/public");
|
||||
//setup the harmony connection if available
|
||||
try {
|
||||
myHarmonyServer = HarmonyServer.setup(bridgeSettings);
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot get harmony client setup, Exiting with message: " + e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
// setup the class to handle the resource setup rest api
|
||||
theResources = new DeviceResource(bridgeSettings, theVersion, myHarmonyServer.getMyHarmony());
|
||||
// setup the class to handle the hue emulator rest api
|
||||
theHueMulator = new HueMulator(theResources.getDeviceRepository(), myHarmonyServer.getMyHarmony());
|
||||
theHueMulator.setupServer();
|
||||
// setup the class to handle the upnp response rest api
|
||||
theSettingResponder = new UpnpSettingsResource(bridgeSettings);
|
||||
theSettingResponder.setupServer();
|
||||
// wait for the sparkjava initialization of the rest api classes to be complete
|
||||
awaitInitialization();
|
||||
|
||||
// start the upnp ssdp discovery listener
|
||||
theUpnpListener = new UpnpListener(bridgeSettings);
|
||||
theUpnpListener.startListening();
|
||||
bridgeSettings.getBridgeControl().setReinit(false);
|
||||
stop();
|
||||
nestHome.closeTheNest();
|
||||
nestHome = null;
|
||||
harmonyHome.shutdownHarmonyHubs();
|
||||
harmonyHome = null;
|
||||
}
|
||||
log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting....");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
16
src/main/java/com/bwssystems/HABridge/IpList.java
Normal file
16
src/main/java/com/bwssystems/HABridge/IpList.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IpList {
|
||||
private List<NamedIP> devices;
|
||||
|
||||
public List<NamedIP> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
public void setDevices(List<NamedIP> devices) {
|
||||
this.devices = devices;
|
||||
}
|
||||
|
||||
}
|
||||
25
src/main/java/com/bwssystems/HABridge/NamedIP.java
Normal file
25
src/main/java/com/bwssystems/HABridge/NamedIP.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
public class NamedIP {
|
||||
private String name;
|
||||
private String ip;
|
||||
private String port;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
public String getPort() {
|
||||
return port;
|
||||
}
|
||||
public void setPort(String port) {
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
192
src/main/java/com/bwssystems/HABridge/SystemControl.java
Normal file
192
src/main/java/com/bwssystems/HABridge/SystemControl.java
Normal file
@@ -0,0 +1,192 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import static spark.Spark.get;
|
||||
import static spark.Spark.options;
|
||||
import static spark.Spark.post;
|
||||
import static spark.Spark.put;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MulticastSocket;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.dao.BackupFilename;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class SystemControl {
|
||||
private static final Logger log = LoggerFactory.getLogger(SystemControl.class);
|
||||
private static final String SYSTEM_CONTEXT = "/system";
|
||||
private BridgeSettings bridgeSettings;
|
||||
private Version version;
|
||||
|
||||
public SystemControl(BridgeSettings theBridgeSettings, Version theVersion) {
|
||||
this.bridgeSettings = theBridgeSettings;
|
||||
this.version = theVersion;
|
||||
}
|
||||
|
||||
// This function sets up the sparkjava rest calls for the hue api
|
||||
public void setupServer() {
|
||||
log.info("Hue emulator service started....");
|
||||
// http://ip_address:port/system/habridge/version gets the version of this bridge instance
|
||||
get (SYSTEM_CONTEXT + "/habridge/version", "application/json", (request, response) -> {
|
||||
log.debug("Get HA Bridge version: v" + version.getVersion());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return "{\"version\":\"" + version.getVersion() + "\"}";
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/settings which returns the bridge configuration settings
|
||||
get(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
log.debug("bridge settings requested from " + request.ip());
|
||||
|
||||
response.status(200);
|
||||
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/settings CORS request
|
||||
options(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/settings which returns the bridge configuration settings
|
||||
put(SYSTEM_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
log.debug("save bridge settings requested from " + request.ip() + " with body: " + request.body());
|
||||
BridgeSettingsDescriptor newBridgeSettings = new Gson().fromJson(request.body(), BridgeSettingsDescriptor.class);
|
||||
bridgeSettings.save(newBridgeSettings);
|
||||
response.status(200);
|
||||
|
||||
return bridgeSettings.getBridgeSettingsDescriptor();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/control/reinit CORS request
|
||||
options(SYSTEM_CONTEXT + "/control/reinit", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/control/reinit sets the parameter reinit the server
|
||||
put(SYSTEM_CONTEXT + "/control/reinit", "application/json", (request, response) -> {
|
||||
return reinit();
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/control/stop CORS request
|
||||
options(SYSTEM_CONTEXT + "/control/stop", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
// http://ip_address:port/system/control/stop sets the parameter stop the server
|
||||
put(SYSTEM_CONTEXT + "/control/stop", "application/json", (request, response) -> {
|
||||
return stop();
|
||||
});
|
||||
|
||||
// http://ip_address:port/system/backup/available returns a list of config backup filenames
|
||||
get (SYSTEM_CONTEXT + "/backup/available", "application/json", (request, response) -> {
|
||||
log.debug("Get backup filenames");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return bridgeSettings.getBackups();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/create CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
put (SYSTEM_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
log.debug("Create backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
BackupFilename returnFilename = new BackupFilename();
|
||||
returnFilename.setFilename(bridgeSettings.backup(aFilename.getFilename()));
|
||||
return returnFilename;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/delete CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (SYSTEM_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
log.debug("Delete backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null)
|
||||
bridgeSettings.deleteBackup(aFilename.getFilename());
|
||||
else
|
||||
log.warn("No filename given for delete backup.");
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/system/backup/restore CORS request
|
||||
options(SYSTEM_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (SYSTEM_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
log.debug("Restore backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null) {
|
||||
bridgeSettings.restoreBackup(aFilename.getFilename());
|
||||
bridgeSettings.loadConfig();
|
||||
}
|
||||
else
|
||||
log.warn("No filename given for restore backup.");
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
}
|
||||
|
||||
protected void pingListener() {
|
||||
try {
|
||||
byte[] buf = new byte[256];
|
||||
String testData = "M-SEARCH * HTTP/1.1\nHOST: " + Configuration.UPNP_MULTICAST_ADDRESS + ":" + Configuration.UPNP_DISCOVERY_PORT + "ST: urn:schemas-upnp-org:device:CloudProxy:1\nMAN: \"ssdp:discover\"\nMX: 3";
|
||||
buf = testData.getBytes();
|
||||
MulticastSocket socket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
|
||||
|
||||
InetAddress group = InetAddress.getByName(Configuration.UPNP_MULTICAST_ADDRESS);
|
||||
DatagramPacket packet;
|
||||
packet = new DatagramPacket(buf, buf.length, group, Configuration.UPNP_DISCOVERY_PORT);
|
||||
socket.send(packet);
|
||||
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.warn("Error pinging listener. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String reinit() {
|
||||
bridgeSettings.getBridgeControl().setReinit(true);
|
||||
pingListener();
|
||||
return "{\"control\":\"reiniting\"}";
|
||||
}
|
||||
|
||||
public String stop() {
|
||||
bridgeSettings.getBridgeControl().setStop(true);
|
||||
pingListener();
|
||||
return "{\"control\":\"stopping\"}";
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,17 @@ public class HueApiResponse {
|
||||
private Map<String, DeviceResponse> lights;
|
||||
private Map<String, String> scenes;
|
||||
private Map<String, String> groups;
|
||||
private Map<String, String> schedules;
|
||||
private Map<String, String> sensors;
|
||||
private Map<String, String> rules;
|
||||
private HueConfig config;
|
||||
|
||||
public HueApiResponse(String name, String ipaddress, String devicetype, String userid) {
|
||||
super();
|
||||
this.setConfig(HueConfig.createConfig(name, ipaddress, devicetype, userid));
|
||||
this.setRules(new HashMap<>());
|
||||
this.setSensors(new HashMap<>());
|
||||
this.setSchedules(new HashMap<>());
|
||||
this.setGroups(new HashMap<>());
|
||||
this.setScenes(new HashMap<>());
|
||||
}
|
||||
@@ -45,6 +51,30 @@ public class HueApiResponse {
|
||||
this.groups = groups;
|
||||
}
|
||||
|
||||
public Map<String, String> getSchedules() {
|
||||
return schedules;
|
||||
}
|
||||
|
||||
public void setSchedules(Map<String, String> schedules) {
|
||||
this.schedules = schedules;
|
||||
}
|
||||
|
||||
public Map<String, String> getSensors() {
|
||||
return sensors;
|
||||
}
|
||||
|
||||
public void setSensors(Map<String, String> sensors) {
|
||||
this.sensors = sensors;
|
||||
}
|
||||
|
||||
public Map<String, String> getRules() {
|
||||
return rules;
|
||||
}
|
||||
|
||||
public void setRules(Map<String, String> rules) {
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
public HueConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -84,6 +84,10 @@ public class HueConfig
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
} catch (Exception e){
|
||||
|
||||
sb.append("00:00:88:00:bb:ee");
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.bwssystems.HABridge.dao;
|
||||
|
||||
public class BackupFilename {
|
||||
private String filename;
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public void setFilename(String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ public class DeviceDescriptor{
|
||||
private String mapId;
|
||||
private String mapType;
|
||||
private String deviceType;
|
||||
private String targetDevice;
|
||||
private String offUrl;
|
||||
private String onUrl;
|
||||
private String httpVerb;
|
||||
@@ -47,7 +48,15 @@ public class DeviceDescriptor{
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public String getOffUrl() {
|
||||
public String getTargetDevice() {
|
||||
return targetDevice;
|
||||
}
|
||||
|
||||
public void setTargetDevice(String targetDevice) {
|
||||
this.targetDevice = targetDevice;
|
||||
}
|
||||
|
||||
public String getOffUrl() {
|
||||
return offUrl;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.Random;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BackupHandler;
|
||||
import com.bwssystems.HABridge.JsonTransformer;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
@@ -25,7 +26,7 @@ import java.util.ListIterator;
|
||||
* This is an in memory list to manage the configured devices and saves the list as a JSON string to a file for later
|
||||
* loading.
|
||||
*/
|
||||
public class DeviceRepository {
|
||||
public class DeviceRepository extends BackupHandler {
|
||||
Map<String, DeviceDescriptor> devices;
|
||||
Path repositoryPath;
|
||||
final Random random = new Random();
|
||||
@@ -33,8 +34,18 @@ public class DeviceRepository {
|
||||
|
||||
public DeviceRepository(String deviceDb) {
|
||||
super();
|
||||
repositoryPath = null;
|
||||
repositoryPath = Paths.get(deviceDb);
|
||||
String jsonContent = repositoryReader(repositoryPath);
|
||||
setupParams(repositoryPath, ".bk", "device.db-");
|
||||
_loadRepository(repositoryPath);
|
||||
}
|
||||
|
||||
public void loadRepository() {
|
||||
if(repositoryPath != null)
|
||||
_loadRepository(repositoryPath);
|
||||
}
|
||||
private void _loadRepository(Path aPath){
|
||||
String jsonContent = repositoryReader(aPath);
|
||||
devices = new HashMap<String, DeviceDescriptor>();
|
||||
|
||||
if(jsonContent != null)
|
||||
@@ -47,7 +58,8 @@ public class DeviceRepository {
|
||||
put(theDevice.getId(), theDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<DeviceDescriptor> findAll() {
|
||||
List<DeviceDescriptor> list = new ArrayList<DeviceDescriptor>(devices.values());
|
||||
@@ -67,16 +79,20 @@ public class DeviceRepository {
|
||||
devices.put(id, aDescriptor);
|
||||
}
|
||||
|
||||
public void save(DeviceDescriptor aDescriptor) {
|
||||
if(aDescriptor.getId() != null)
|
||||
devices.remove(aDescriptor.getId());
|
||||
else
|
||||
aDescriptor.setId(String.valueOf(random.nextInt(Integer.MAX_VALUE)));
|
||||
put(aDescriptor.getId(), aDescriptor);
|
||||
public void save(DeviceDescriptor[] descriptors) {
|
||||
String theNames = "";
|
||||
for(int i = 0; i < descriptors.length; i++) {
|
||||
if(descriptors[i].getId() != null)
|
||||
devices.remove(descriptors[i].getId());
|
||||
else
|
||||
descriptors[i].setId(String.valueOf(random.nextInt(Integer.MAX_VALUE)));
|
||||
put(descriptors[i].getId(), descriptors[i]);
|
||||
theNames = theNames + " " + descriptors[i].getName() + ", ";
|
||||
}
|
||||
JsonTransformer aRenderer = new JsonTransformer();
|
||||
String jsonValue = aRenderer.render(findAll());
|
||||
repositoryWriter(jsonValue, repositoryPath);
|
||||
log.debug("Save device: " + aDescriptor.getName());
|
||||
log.debug("Save device(s): " + theNames);
|
||||
}
|
||||
|
||||
public String delete(DeviceDescriptor aDescriptor) {
|
||||
@@ -187,6 +203,9 @@ public class DeviceRepository {
|
||||
} else if (name.equals("deviceType")) {
|
||||
deviceEntry.setDeviceType(reader.nextString());
|
||||
log.debug("Read a Device - device json type:" + deviceEntry.getDeviceType());
|
||||
} else if (name.equals("targetDevice")) {
|
||||
deviceEntry.setTargetDevice(reader.nextString());
|
||||
log.debug("Read a Device - device json type:" + deviceEntry.getTargetDevice());
|
||||
} else if (name.equals("offUrl")) {
|
||||
deviceEntry.setOffUrl(reader.nextString());
|
||||
log.debug("Read a Device - device json off URL:" + deviceEntry.getOffUrl());
|
||||
|
||||
18
src/main/java/com/bwssystems/HABridge/dao/ErrorMessage.java
Normal file
18
src/main/java/com/bwssystems/HABridge/dao/ErrorMessage.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.bwssystems.HABridge.dao;
|
||||
|
||||
public class ErrorMessage {
|
||||
private String message;
|
||||
|
||||
public ErrorMessage(String message) {
|
||||
super();
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,17 @@ import org.apache.http.HttpStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.JsonTransformer;
|
||||
import com.bwssystems.HABridge.Version;
|
||||
import com.bwssystems.HABridge.dao.BackupFilename;
|
||||
import com.bwssystems.HABridge.dao.DeviceDescriptor;
|
||||
import com.bwssystems.HABridge.dao.DeviceRepository;
|
||||
import com.bwssystems.harmony.HarmonyHandler;
|
||||
import com.bwssystems.luupRequests.Sdata;
|
||||
import com.bwssystems.vera.VeraInfo;
|
||||
import com.bwssystems.HABridge.dao.ErrorMessage;
|
||||
import com.bwssystems.NestBridge.NestHome;
|
||||
import com.bwssystems.harmony.HarmonyHome;
|
||||
import com.bwssystems.luupRequests.Device;
|
||||
import com.bwssystems.luupRequests.Scene;
|
||||
import com.bwssystems.vera.VeraHome;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
@@ -33,17 +36,29 @@ public class DeviceResource {
|
||||
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
|
||||
|
||||
private DeviceRepository deviceRepository;
|
||||
private VeraInfo veraInfo;
|
||||
private Version version;
|
||||
private HarmonyHandler myHarmonyHandler;
|
||||
private VeraHome veraHome;
|
||||
private HarmonyHome myHarmonyHome;
|
||||
private NestHome nestHome;
|
||||
private static final Set<String> supportedVerbs = new HashSet<>(Arrays.asList("get", "put", "post"));
|
||||
|
||||
public DeviceResource(BridgeSettings theSettings, Version theVersion, HarmonyHandler myHarmony) {
|
||||
super();
|
||||
public DeviceResource(BridgeSettingsDescriptor theSettings, HarmonyHome theHarmonyHome, NestHome aNestHome) {
|
||||
this.deviceRepository = new DeviceRepository(theSettings.getUpnpDeviceDb());
|
||||
this.veraInfo = new VeraInfo(theSettings.getVeraAddress(), theSettings.isValidVera());
|
||||
this.myHarmonyHandler = myHarmony;
|
||||
this.version = theVersion;
|
||||
|
||||
if(theSettings.isValidVera())
|
||||
this.veraHome = new VeraHome(theSettings);
|
||||
else
|
||||
this.veraHome = null;
|
||||
|
||||
if(theSettings.isValidHarmony())
|
||||
this.myHarmonyHome = theHarmonyHome;
|
||||
else
|
||||
this.myHarmonyHome = null;
|
||||
|
||||
if(theSettings.isValidNest())
|
||||
this.nestHome = aNestHome;
|
||||
else
|
||||
this.nestHome = null;
|
||||
|
||||
setupEndpoints();
|
||||
}
|
||||
|
||||
@@ -63,24 +78,31 @@ public class DeviceResource {
|
||||
return "";
|
||||
});
|
||||
post(API_CONTEXT, "application/json", (request, response) -> {
|
||||
log.debug("Create a Device - request body: " + request.body());
|
||||
DeviceDescriptor device = new Gson().fromJson(request.body(), DeviceDescriptor.class);
|
||||
if(device.getContentBody() != null ) {
|
||||
if (device.getContentType() == null || device.getHttpVerb() == null || !supportedVerbs.contains(device.getHttpVerb().toLowerCase())) {
|
||||
device = null;
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
log.debug("Bad http verb in create a Device: " + request.body());
|
||||
return device;
|
||||
}
|
||||
}
|
||||
log.debug("Create a Device(s) - request body: " + request.body());
|
||||
DeviceDescriptor devices[];
|
||||
if(request.body().substring(0,1).equalsIgnoreCase("[") == true) {
|
||||
devices = new Gson().fromJson(request.body(), DeviceDescriptor[].class);
|
||||
}
|
||||
else {
|
||||
devices = new Gson().fromJson("[" + request.body() + "]", DeviceDescriptor[].class);
|
||||
}
|
||||
for(int i = 0; i < devices.length; i++) {
|
||||
if(devices[i].getContentBody() != null ) {
|
||||
if (devices[i].getContentType() == null || devices[i].getHttpVerb() == null || !supportedVerbs.contains(devices[i].getHttpVerb().toLowerCase())) {
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
log.debug("Bad http verb in create a Device(s): " + request.body());
|
||||
return new ErrorMessage("Bad http verb in create a Device(s): " + request.body() + " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviceRepository.save(device);
|
||||
log.debug("Created a Device: " + request.body());
|
||||
deviceRepository.save(devices);
|
||||
log.debug("Created a Device(s): " + request.body());
|
||||
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.status(HttpStatus.SC_CREATED);
|
||||
|
||||
return device;
|
||||
return devices;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/devices/:id CORS request
|
||||
@@ -99,6 +121,7 @@ public class DeviceResource {
|
||||
if(deviceEntry == null){
|
||||
log.debug("Could not save an edited Device Id: " + request.params(":id"));
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
return new ErrorMessage("Could not save an edited Device Id: " + request.params(":id") + " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -109,6 +132,7 @@ public class DeviceResource {
|
||||
deviceEntry.setDeviceType(device.getDeviceType());
|
||||
deviceEntry.setMapId(device.getMapId());
|
||||
deviceEntry.setMapType(device.getMapType());
|
||||
deviceEntry.setTargetDevice(device.getTargetDevice());
|
||||
deviceEntry.setOnUrl(device.getOnUrl());
|
||||
deviceEntry.setOffUrl(device.getOffUrl());
|
||||
deviceEntry.setHttpVerb(device.getHttpVerb());
|
||||
@@ -116,7 +140,9 @@ public class DeviceResource {
|
||||
deviceEntry.setContentBody(device.getContentBody());
|
||||
deviceEntry.setContentBodyOff(device.getContentBodyOff());
|
||||
|
||||
deviceRepository.save(deviceEntry);
|
||||
DeviceDescriptor[] theDevices = new DeviceDescriptor[1];
|
||||
theDevices[0] = deviceEntry;
|
||||
deviceRepository.save(theDevices);
|
||||
response.status(HttpStatus.SC_OK);
|
||||
}
|
||||
return deviceEntry;
|
||||
@@ -135,8 +161,10 @@ public class DeviceResource {
|
||||
get (API_CONTEXT + "/:id", "application/json", (request, response) -> {
|
||||
log.debug("Get a device");
|
||||
DeviceDescriptor descriptor = deviceRepository.findOne(request.params(":id"));
|
||||
if(descriptor == null)
|
||||
if(descriptor == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("Could not find, id: " + request.params(":id") + " ");
|
||||
}
|
||||
else
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return descriptor;
|
||||
@@ -146,8 +174,10 @@ public class DeviceResource {
|
||||
String anId = request.params(":id");
|
||||
log.debug("Delete a device: " + anId);
|
||||
DeviceDescriptor deleted = deviceRepository.findOne(anId);
|
||||
if(deleted == null)
|
||||
if(deleted == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("Could not delete, id: " + anId + " not found. ");
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceRepository.delete(deleted);
|
||||
@@ -156,64 +186,139 @@ public class DeviceResource {
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/habridge/version", "application/json", (request, response) -> {
|
||||
log.debug("Get HA Bridge version: v" + version.getVersion());
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return "{\"version\":\"" + version.getVersion() + "\"}";
|
||||
});
|
||||
|
||||
get (API_CONTEXT + "/vera/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get vera devices");
|
||||
Sdata sData = veraInfo.getSdata();
|
||||
if(sData == null){
|
||||
if(veraHome == null){
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return null;
|
||||
return new ErrorMessage("A Vera is not available.");
|
||||
}
|
||||
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return sData.getDevices();
|
||||
List<Device> theDevices = veraHome.getDevices();
|
||||
if(theDevices == null) {
|
||||
response.status(HttpStatus.SC_SERVICE_UNAVAILABLE);
|
||||
return new ErrorMessage("A Vera request failed to get devices. Check your Vera IP addresses.");
|
||||
}
|
||||
else
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return theDevices;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/vera/scenes", "application/json", (request, response) -> {
|
||||
log.debug("Get vera scenes");
|
||||
Sdata sData = veraInfo.getSdata();
|
||||
if(sData == null){
|
||||
if(veraHome == null){
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return null;
|
||||
return new ErrorMessage("A Vera is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return sData.getScenes();
|
||||
List<Scene> theScenes = veraHome.getScenes();
|
||||
if(theScenes == null) {
|
||||
response.status(HttpStatus.SC_SERVICE_UNAVAILABLE);
|
||||
return new ErrorMessage("A Vera is not available and failed to get scenes. Check your Vera IP addresses.");
|
||||
}
|
||||
else
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return theScenes;
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/harmony/activities", "application/json", (request, response) -> {
|
||||
log.debug("Get harmony activities");
|
||||
if(myHarmonyHandler == null) {
|
||||
if(myHarmonyHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return null;
|
||||
return new ErrorMessage("A Harmony is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return myHarmonyHandler.getActivities();
|
||||
return myHarmonyHome.getActivities();
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/harmony/show", "application/json", (request, response) -> {
|
||||
log.debug("Get harmony current activity");
|
||||
if(myHarmonyHandler == null) {
|
||||
if(myHarmonyHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return null;
|
||||
return new ErrorMessage("A Harmony is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return myHarmonyHandler.getCurrentActivity();
|
||||
return myHarmonyHome.getCurrentActivities();
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/harmony/devices", "application/json", (request, response) -> {
|
||||
log.debug("Get harmony devices");
|
||||
if(myHarmonyHandler == null) {
|
||||
if(myHarmonyHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return null;
|
||||
return new ErrorMessage("A Harmony is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return myHarmonyHandler.getDevices();
|
||||
return myHarmonyHome.getDevices();
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/nest/items", "application/json", (request, response) -> {
|
||||
log.debug("Get nest items");
|
||||
if(nestHome == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return new ErrorMessage("A Nest is not available.");
|
||||
}
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return nestHome.getItems();
|
||||
}, new JsonTransformer());
|
||||
|
||||
get (API_CONTEXT + "/backup/available", "application/json", (request, response) -> {
|
||||
log.debug("Get backup filenames");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return deviceRepository.getBackups();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/devices/backup/create CORS request
|
||||
options(API_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "PUT");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
put (API_CONTEXT + "/backup/create", "application/json", (request, response) -> {
|
||||
log.debug("Create backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
BackupFilename returnFilename = new BackupFilename();
|
||||
returnFilename.setFilename(deviceRepository.backup(aFilename.getFilename()));
|
||||
return returnFilename;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/devices/backup/delete CORS request
|
||||
options(API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
|
||||
log.debug("Delete backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null)
|
||||
deviceRepository.deleteBackup(aFilename.getFilename());
|
||||
else
|
||||
log.warn("No filename given for delete backup.");
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/devices/backup/restore CORS request
|
||||
options(API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
response.status(HttpStatus.SC_OK);
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
|
||||
response.header("Content-Type", "text/html; charset=utf-8");
|
||||
return "";
|
||||
});
|
||||
post (API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
|
||||
log.debug("Restore backup: " + request.body());
|
||||
BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
|
||||
if(aFilename != null) {
|
||||
deviceRepository.restoreBackup(aFilename.getFilename());
|
||||
deviceRepository.loadRepository();
|
||||
}
|
||||
else
|
||||
log.warn("No filename given for restore backup.");
|
||||
return null;
|
||||
}, new JsonTransformer());
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.JsonTransformer;
|
||||
import com.bwssystems.HABridge.api.UserCreateRequest;
|
||||
import com.bwssystems.HABridge.api.hue.DeviceResponse;
|
||||
import com.bwssystems.HABridge.api.hue.DeviceState;
|
||||
import com.bwssystems.HABridge.api.hue.HueApiResponse;
|
||||
import com.bwssystems.HABridge.dao.*;
|
||||
import com.bwssystems.NestBridge.NestInstruction;
|
||||
import com.bwssystems.NestBridge.NestHome;
|
||||
import com.bwssystems.harmony.ButtonPress;
|
||||
import com.bwssystems.harmony.HarmonyHandler;
|
||||
import com.bwssystems.harmony.HarmonyHome;
|
||||
import com.bwssystems.harmony.RunActivity;
|
||||
import com.bwssystems.nest.controller.Nest;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
@@ -37,10 +42,15 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
/**
|
||||
* Based on Armzilla's HueMulator - a Philips Hue emulator using sparkjava rest server
|
||||
*/
|
||||
@@ -55,17 +65,28 @@ public class HueMulator {
|
||||
private static final String HUE_CONTEXT = "/api";
|
||||
|
||||
private DeviceRepository repository;
|
||||
private HarmonyHandler myHarmony;
|
||||
private HarmonyHome myHarmonyHome;
|
||||
private Nest theNest;
|
||||
private HttpClient httpClient;
|
||||
private ObjectMapper mapper;
|
||||
private BridgeSettingsDescriptor bridgeSettings;
|
||||
private byte[] sendData;
|
||||
|
||||
|
||||
public HueMulator(DeviceRepository aDeviceRepository, HarmonyHandler theHandler){
|
||||
public HueMulator(BridgeSettingsDescriptor theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome, NestHome aNestHome){
|
||||
httpClient = HttpClients.createDefault();
|
||||
mapper = new ObjectMapper(); //armzilla: work around Echo incorrect content type and breaking mapping. Map manually
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
repository = aDeviceRepository;
|
||||
myHarmony = theHandler;
|
||||
if(theBridgeSettings.isValidHarmony())
|
||||
this.myHarmonyHome = theHarmonyHome;
|
||||
else
|
||||
this.myHarmonyHome = null;
|
||||
if(theBridgeSettings.isValidNest())
|
||||
this.theNest = aNestHome.getTheNest();
|
||||
else
|
||||
this.theNest = null;
|
||||
bridgeSettings = theBridgeSettings;
|
||||
}
|
||||
|
||||
// This function sets up the sparkjava rest calls for the hue api
|
||||
@@ -74,6 +95,8 @@ public class HueMulator {
|
||||
// http://ip_address:port/api/{userId}/lights returns json objects of all lights configured
|
||||
get(HUE_CONTEXT + "/:userid/lights", "application/json", (request, response) -> {
|
||||
String userId = request.params(":userid");
|
||||
if(bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue lights list requested: " + userId + " from " + request.ip());
|
||||
log.debug("hue lights list requested: " + userId + " from " + request.ip());
|
||||
List<DeviceDescriptor> deviceList = repository.findAll();
|
||||
Map<String, DeviceResponse> deviceResponseMap = new HashMap<>();
|
||||
@@ -101,6 +124,8 @@ public class HueMulator {
|
||||
String newUser = null;
|
||||
String aDeviceType = null;
|
||||
|
||||
if(bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue api/ user create requested: " + request.body() + " from " + request.ip());
|
||||
log.debug("hue api user create requested: " + request.body() + " from " + request.ip());
|
||||
|
||||
if(request.body() != null && !request.body().isEmpty()) {
|
||||
@@ -113,6 +138,8 @@ public class HueMulator {
|
||||
|
||||
if(aDeviceType == null)
|
||||
aDeviceType = "<not given>";
|
||||
if(bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
|
||||
log.debug("hue api user create requested for device type: " + aDeviceType + " and username: " + newUser);
|
||||
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
@@ -136,7 +163,8 @@ public class HueMulator {
|
||||
String newUser = null;
|
||||
String aDeviceType = null;
|
||||
|
||||
log.info("HH trace: hue api user create requested: " + request.body() + " from " + request.ip());
|
||||
if(bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue api/* user create requested: " + request.body() + " from " + request.ip());
|
||||
|
||||
if(request.body() != null && !request.body().isEmpty()) {
|
||||
aNewUser = new Gson().fromJson(request.body(), UserCreateRequest.class);
|
||||
@@ -158,22 +186,26 @@ public class HueMulator {
|
||||
|
||||
// http://ip_address:port/api/config returns json objects for the config when no user is given
|
||||
get(HUE_CONTEXT + "/config", "application/json", (request, response) -> {
|
||||
String userId = request.params(":userid");
|
||||
log.debug("hue api config requested: " + userId + " from " + request.ip());
|
||||
HueApiResponse apiResponse = new HueApiResponse("Philips hue", request.ip(), "My App", userId);
|
||||
if(bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue api/config config requested: <no_user> from " + request.ip());
|
||||
log.debug("hue api config requested: <no_user> from " + request.ip());
|
||||
HueApiResponse apiResponse = new HueApiResponse("Philips hue", bridgeSettings.getUpnpConfigAddress(), "My App", "none");
|
||||
|
||||
response.type("application/json; charset=utf-8");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
String responseString = null;
|
||||
responseString = "[{\"swversion\":\"" + apiResponse.getConfig().getSwversion() + "\",\"apiversion\":\"" + apiResponse.getConfig().getApiversion() + "\",\"name\":\"" + apiResponse.getConfig().getName() + "\",\"mac\":\"" + apiResponse.getConfig().getMac() + "\"}]";
|
||||
return responseString;
|
||||
});
|
||||
// String responseString = null;
|
||||
// responseString = "[{\"swversion\":\"" + apiResponse.getConfig().getSwversion() + "\",\"apiversion\":\"" + apiResponse.getConfig().getApiversion() + "\",\"name\":\"" + apiResponse.getConfig().getName() + "\",\"mac\":\"" + apiResponse.getConfig().getMac() + "\"}]";
|
||||
// return responseString;
|
||||
return apiResponse.getConfig();
|
||||
}, new JsonTransformer());
|
||||
|
||||
// http://ip_address:port/api/{userId}/config returns json objects for the config
|
||||
get(HUE_CONTEXT + "/:userid/config", "application/json", (request, response) -> {
|
||||
String userId = request.params(":userid");
|
||||
if(bridgeSettings.isTraceupnp())
|
||||
log.info("Traceupnp: hue api/:userid/config config requested: " + userId + " from " + request.ip());
|
||||
log.debug("hue api config requested: " + userId + " from " + request.ip());
|
||||
HueApiResponse apiResponse = new HueApiResponse("Philips hue", request.ip(), "My App", userId);
|
||||
HueApiResponse apiResponse = new HueApiResponse("Philips hue", bridgeSettings.getUpnpConfigAddress(), "My App", userId);
|
||||
|
||||
response.type("application/json; charset=utf-8");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
@@ -197,7 +229,7 @@ public class HueMulator {
|
||||
deviceList.put(descriptor.getId(), deviceResponse);
|
||||
}
|
||||
);
|
||||
HueApiResponse apiResponse = new HueApiResponse("Philips hue", request.ip(), "My App", userId);
|
||||
HueApiResponse apiResponse = new HueApiResponse("Philips hue", bridgeSettings.getUpnpConfigAddress(), "My App", userId);
|
||||
apiResponse.setLights(deviceList);
|
||||
|
||||
response.type("application/json; charset=utf-8");
|
||||
@@ -213,7 +245,7 @@ public class HueMulator {
|
||||
DeviceDescriptor device = repository.findOne(lightId);
|
||||
if (device == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
return null;
|
||||
return "[{\"error\":{\"type\": 3, \"address\": \"/lights/" + lightId + "\",\"description\": \"Object not found\"}}]";
|
||||
} else {
|
||||
log.debug("found device named: " + device.getName());
|
||||
}
|
||||
@@ -241,26 +273,29 @@ public class HueMulator {
|
||||
*/
|
||||
String userId = request.params(":userid");
|
||||
String lightId = request.params(":id");
|
||||
log.debug("hue state change requested: " + userId + " from " + request.ip() + " body: " + request.body());
|
||||
|
||||
String responseString = null;
|
||||
String url = null;
|
||||
DeviceState state = null;
|
||||
log.debug("hue state change requested: " + userId + " from " + request.ip() + " body: " + request.body());
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.type("application/json; charset=utf-8");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
|
||||
try {
|
||||
state = mapper.readValue(request.body(), DeviceState.class);
|
||||
} catch (IOException e) {
|
||||
log.error("Object mapper barfed on input of body.", e);
|
||||
response.status(HttpStatus.SC_BAD_REQUEST);
|
||||
return null;
|
||||
log.warn("Object mapper barfed on input of body.", e);
|
||||
responseString = "[{\"error\":{\"type\": 2, \"address\": \"/lights/" + lightId + "\",\"description\": \"Object mapper barfed on input of body.\"}}]";
|
||||
return responseString;
|
||||
}
|
||||
|
||||
DeviceDescriptor device = repository.findOne(lightId);
|
||||
if (device == null) {
|
||||
response.status(HttpStatus.SC_NOT_FOUND);
|
||||
log.error("Could not find devcie: " + lightId + " for hue state change request: " + userId + " from " + request.ip() + " body: " + request.body());
|
||||
return null;
|
||||
log.warn("Could not find device: " + lightId + " for hue state change request: " + userId + " from " + request.ip() + " body: " + request.body());
|
||||
responseString = "[{\"error\":{\"type\": 3, \"address\": \"/lights/" + lightId + "\",\"description\": \"Could not find device\", \"resource\": \"/lights/" + lightId + "\"}}]";
|
||||
return responseString;
|
||||
}
|
||||
|
||||
String responseString =null;
|
||||
String url = null;
|
||||
if (state.isOn()) {
|
||||
responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":true}}";
|
||||
url = device.getOnUrl();
|
||||
@@ -283,23 +318,128 @@ public class HueMulator {
|
||||
}
|
||||
else
|
||||
responseString = responseString + "]";
|
||||
|
||||
|
||||
if(device.getDeviceType().toLowerCase().contains("activity"))
|
||||
|
||||
if(device.getDeviceType().toLowerCase().contains("activity") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyActivity")))
|
||||
{
|
||||
log.debug("executing activity to Harmony: " + url);
|
||||
RunActivity anActivity = new Gson().fromJson(url, RunActivity.class);
|
||||
myHarmony.startActivity(anActivity);
|
||||
log.debug("executing HUE api request to change activity to Harmony: " + url);
|
||||
if(myHarmonyHome != null)
|
||||
{
|
||||
RunActivity anActivity = new Gson().fromJson(url, RunActivity.class);
|
||||
HarmonyHandler myHarmony = myHarmonyHome.getHarmonyHandler(device.getTargetDevice());
|
||||
if(myHarmony == null)
|
||||
{
|
||||
log.warn("Should not get here, no harmony hub available");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Should not get here, no harmony hub available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
else
|
||||
myHarmony.startActivity(anActivity);
|
||||
}
|
||||
else {
|
||||
log.warn("Should not get here, no harmony configured");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Should not get here, no harmony configured\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
|
||||
}
|
||||
}
|
||||
else if(device.getDeviceType().toLowerCase().contains("button"))
|
||||
else if(device.getDeviceType().toLowerCase().contains("button") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("harmonyButton")))
|
||||
{
|
||||
log.debug("executing button press to Harmony: " + url);
|
||||
ButtonPress aDeviceButton = new Gson().fromJson(url, ButtonPress.class);
|
||||
myHarmony.pressButton(aDeviceButton);
|
||||
log.debug("executing HUE api request to button press(es) to Harmony: " + url);
|
||||
if(myHarmonyHome != null)
|
||||
{
|
||||
if(url.substring(0, 1).equalsIgnoreCase("{")) {
|
||||
url = "[" + url +"]";
|
||||
}
|
||||
ButtonPress[] deviceButtons = new Gson().fromJson(url, ButtonPress[].class);
|
||||
HarmonyHandler myHarmony = myHarmonyHome.getHarmonyHandler(device.getTargetDevice());
|
||||
if(myHarmony == null)
|
||||
{
|
||||
log.warn("Should not get here, no harmony hub available");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Should not get here, no harmony hub available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
else {
|
||||
for(int i = 0; i < deviceButtons.length; i++) {
|
||||
if( i > 0)
|
||||
Thread.sleep(bridgeSettings.getButtonsleep());
|
||||
log.debug("pressing button: " + deviceButtons[i].getDevice() + " - " + deviceButtons[i].getButton() + " - iteration: " + String.valueOf(i));
|
||||
myHarmony.pressButton(deviceButtons[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn("Should not get here, no harmony configured");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Should not get here, no harmony configured\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
|
||||
}
|
||||
}
|
||||
else if(device.getDeviceType().toLowerCase().contains("home") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("nestHomeAway")))
|
||||
{
|
||||
log.debug("executing HUE api request to set away for nest home: " + url);
|
||||
if(theNest == null)
|
||||
{
|
||||
log.warn("Should not get here, no Nest available");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Should not get here, no Nest available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
else {
|
||||
NestInstruction homeAway = new Gson().fromJson(url, NestInstruction.class);
|
||||
theNest.getHome(homeAway.getName()).setAway(homeAway.getAway());
|
||||
}
|
||||
}
|
||||
else if(device.getDeviceType().toLowerCase().contains("thermo") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("nestThermoSet")))
|
||||
{
|
||||
log.debug("executing HUE api request to set thermostat for nest: " + url);
|
||||
if(theNest == null)
|
||||
{
|
||||
log.warn("Should not get here, no Nest available");
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Should not get here, no Nest available\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
else {
|
||||
NestInstruction thermoSetting = new Gson().fromJson(url, NestInstruction.class);
|
||||
if(thermoSetting.getControl().equalsIgnoreCase("temp")) {
|
||||
if(request.body().contains("bri")) {
|
||||
thermoSetting.setTemp(String.valueOf((Double.parseDouble(replaceIntensityValue(thermoSetting.getTemp(), state.getBri())) - 32.0)/1.8));
|
||||
log.debug("Setting thermostat: " + thermoSetting.getName() + " to " + thermoSetting.getTemp() + "C");
|
||||
theNest.getThermostat(thermoSetting.getName()).setTargetTemperature(Float.parseFloat(thermoSetting.getTemp()));
|
||||
}
|
||||
}
|
||||
else if (thermoSetting.getControl().contains("range") ||thermoSetting.getControl().contains("heat") ||thermoSetting.getControl().contains("cool") ||thermoSetting.getControl().contains("off")) {
|
||||
log.debug("Setting thermostat target type: " + thermoSetting.getName() + " to " + thermoSetting.getControl());
|
||||
theNest.getThermostat(thermoSetting.getName()).setTargetType(thermoSetting.getControl());
|
||||
}
|
||||
else if(thermoSetting.getControl().contains("fan")) {
|
||||
log.debug("Setting thermostat fan mode: " + thermoSetting.getName() + " to " + thermoSetting.getControl().substring(4));
|
||||
theNest.getThermostat(thermoSetting.getName()).setFanMode(thermoSetting.getControl().substring(4));
|
||||
}
|
||||
else {
|
||||
log.warn("no valid Nest control info: " + thermoSetting.getControl());
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"no valid Nest control info\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(url.startsWith("udp://"))
|
||||
{
|
||||
log.debug("executing HUE api request to UDP: " + url);
|
||||
try {
|
||||
String intermediate = url.substring(6);
|
||||
String ipAddr = intermediate.substring(0, intermediate.indexOf(':'));
|
||||
String port = intermediate.substring(intermediate.indexOf(':') + 1, intermediate.indexOf('/'));
|
||||
String theBody = intermediate.substring(intermediate.indexOf('/')+1);
|
||||
DatagramSocket responseSocket = new DatagramSocket(Integer.parseInt(port));
|
||||
if(theBody.startsWith("0x")) {
|
||||
sendData = DatatypeConverter.parseHexBinary(theBody.substring(2));
|
||||
}
|
||||
else
|
||||
sendData = theBody.getBytes();
|
||||
InetAddress IPAddress = InetAddress.getByName(ipAddr);
|
||||
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, Integer.parseInt(port));
|
||||
responseSocket.send(sendPacket);
|
||||
responseSocket.close();
|
||||
} catch (IOException e) {
|
||||
log.warn("Could not send UDP Datagram packet for request.", e);
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log.debug("executing activity to Http " + (device.getHttpVerb() == null?"GET":device.getHttpVerb()) + ": " + url);
|
||||
log.debug("executing HUE api request to Http " + (device.getHttpVerb() == null?"GET":device.getHttpVerb()) + ": " + url);
|
||||
// quick template
|
||||
String body;
|
||||
url = replaceIntensityValue(url, state.getBri());
|
||||
@@ -309,15 +449,11 @@ public class HueMulator {
|
||||
body = replaceIntensityValue(device.getContentBodyOff(), state.getBri());
|
||||
// make call
|
||||
if (!doHttpRequest(url, device.getHttpVerb(), device.getContentType(), body)) {
|
||||
response.status(HttpStatus.SC_SERVICE_UNAVAILABLE);
|
||||
log.error("Error on calling url to change device state: " + url);
|
||||
return null;
|
||||
log.warn("Error on calling url to change device state: " + url);
|
||||
responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling url to change device state\", \"parameter\": \"/lights/" + lightId + "state\"}}]";
|
||||
}
|
||||
}
|
||||
|
||||
response.header("Access-Control-Allow-Origin", request.headers("Origin"));
|
||||
response.type("application/json; charset=utf-8");
|
||||
response.status(HttpStatus.SC_OK);
|
||||
return responseString;
|
||||
});
|
||||
}
|
||||
@@ -353,8 +489,9 @@ public class HueMulator {
|
||||
Integer endResult = Math.round(result.floatValue());
|
||||
request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE, endResult.toString());
|
||||
} catch (Exception e) {
|
||||
log.error("Could not execute Math: " + mathDescriptor, e);
|
||||
} }
|
||||
log.warn("Could not execute Math: " + mathDescriptor, e);
|
||||
}
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -386,7 +523,7 @@ public class HueMulator {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error calling out to HA gateway", e);
|
||||
log.warn("Error calling out to HA gateway", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.bwssystems.HABridge.upnp;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.BridgeControlDescriptor;
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.Configuration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
@@ -14,34 +16,29 @@ import org.apache.http.conn.util.*;
|
||||
|
||||
public class UpnpListener {
|
||||
private Logger log = LoggerFactory.getLogger(UpnpListener.class);
|
||||
private static final int UPNP_DISCOVERY_PORT = 1900;
|
||||
private static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250";
|
||||
|
||||
private int upnpResponsePort;
|
||||
|
||||
private int httpServerPort;
|
||||
|
||||
private String responseAddress;
|
||||
|
||||
private boolean strict;
|
||||
|
||||
private boolean traceupnp;
|
||||
private BridgeControlDescriptor bridgeControl;
|
||||
|
||||
public UpnpListener(BridgeSettings theSettings) {
|
||||
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl) {
|
||||
super();
|
||||
upnpResponsePort = Integer.valueOf(theSettings.getUpnpResponsePort());
|
||||
upnpResponsePort = theSettings.getUpnpResponsePort();
|
||||
httpServerPort = Integer.valueOf(theSettings.getServerPort());
|
||||
responseAddress = theSettings.getUpnpConfigAddress();
|
||||
strict = theSettings.isUpnpStrict();
|
||||
traceupnp = theSettings.isTraceupnp();
|
||||
bridgeControl = theControl;
|
||||
}
|
||||
|
||||
public void startListening(){
|
||||
public boolean startListening(){
|
||||
log.info("UPNP Discovery Listener starting....");
|
||||
|
||||
try (DatagramSocket responseSocket = new DatagramSocket(upnpResponsePort);
|
||||
MulticastSocket upnpMulticastSocket = new MulticastSocket(UPNP_DISCOVERY_PORT);) {
|
||||
InetSocketAddress socketAddress = new InetSocketAddress(UPNP_MULTICAST_ADDRESS, UPNP_DISCOVERY_PORT);
|
||||
MulticastSocket upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);) {
|
||||
InetSocketAddress socketAddress = new InetSocketAddress(Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT);
|
||||
Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();
|
||||
|
||||
while (ifs.hasMoreElements()) {
|
||||
@@ -71,22 +68,35 @@ public class UpnpListener {
|
||||
}
|
||||
|
||||
log.info("UPNP Discovery Listener running and ready....");
|
||||
|
||||
while(true){ //trigger shutdown here
|
||||
boolean loopControl = true;
|
||||
while(loopControl){ //trigger shutdown here
|
||||
byte[] buf = new byte[1024];
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||
upnpMulticastSocket.receive(packet);
|
||||
if(isSSDPDiscovery(packet)){
|
||||
sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort());
|
||||
}
|
||||
if(bridgeControl.isReinit() || bridgeControl.isStop()) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// noop
|
||||
}
|
||||
loopControl = false;
|
||||
}
|
||||
}
|
||||
|
||||
upnpMulticastSocket.close();
|
||||
responseSocket.close();
|
||||
} catch (IOException e) {
|
||||
log.error("UpnpListener encountered an error opening sockets. Shutting down", e);
|
||||
|
||||
}
|
||||
log.info("UPNP Discovery Listener Stopped");
|
||||
|
||||
if(bridgeControl.isReinit())
|
||||
log.info("UPNP Discovery Listener - ended, restart found");
|
||||
if(bridgeControl.isStop())
|
||||
log.info("UPNP Discovery Listener - ended, stop found");
|
||||
if(!bridgeControl.isStop()&& !bridgeControl.isReinit())
|
||||
log.info("UPNP Discovery Listener - ended, error found");
|
||||
return bridgeControl.isReinit();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,30 +106,34 @@ public class UpnpListener {
|
||||
//Only respond to discover request for strict upnp form
|
||||
String packetString = new String(packet.getData());
|
||||
if(packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1") && packetString.contains("\"ssdp:discover\"")){
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to be an M-SEARCH message.");
|
||||
}
|
||||
else {
|
||||
log.debug("Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
log.debug("Found message to be an M-SEARCH message.");
|
||||
}
|
||||
log.debug("isSSDPDiscovery Found message to be an M-SEARCH message.");
|
||||
log.debug("isSSDPDiscovery Got SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
|
||||
if(strict && (packetString.contains("ST: urn:schemas-upnp-org:device:basic:1") || packetString.contains("ST: upnp:rootdevice") || packetString.contains("ST: ssdp:all")))
|
||||
{
|
||||
if(traceupnp)
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to be an M-SEARCH message.");
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
||||
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
}
|
||||
log.debug("isSSDPDiscovery found message to be valid under strict rules - strict: " + strict);
|
||||
return true;
|
||||
}
|
||||
else if (!strict)
|
||||
{
|
||||
if(traceupnp)
|
||||
if(traceupnp) {
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to be an M-SEARCH message.");
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
||||
log.info("Traceupnp: SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
}
|
||||
log.debug("isSSDPDiscovery found message to be valid under loose rules - strict: " + strict);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(traceupnp)
|
||||
log.info("Traceupnp: isSSDPDiscovery found message to not be valid - strict: " + strict);
|
||||
else {
|
||||
// log.debug("isSSDPDiscovery found message to not be valid - strict: " + strict);
|
||||
// log.debug("SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ package com.bwssystems.HABridge.upnp;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.JsonTransformer;
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
|
||||
import static spark.Spark.get;
|
||||
|
||||
@@ -12,11 +11,9 @@ import static spark.Spark.get;
|
||||
*
|
||||
*/
|
||||
public class UpnpSettingsResource {
|
||||
private static final String UPNP_CONTEXT = "/upnp";
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(UpnpSettingsResource.class);
|
||||
|
||||
private BridgeSettings theSettings;
|
||||
private BridgeSettingsDescriptor theSettings;
|
||||
|
||||
private String hueTemplate = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
|
||||
+ "<specVersion>\n" + "<major>1</major>\n" + "<minor>0</minor>\n" + "</specVersion>\n"
|
||||
@@ -40,9 +37,9 @@ public class UpnpSettingsResource {
|
||||
+ "<depth>24</depth>\n" + "<url>hue_logo_3.png</url>\n" + "</icon>\n" + "</iconList>\n" + "</device>\n"
|
||||
+ "</root>\n";
|
||||
|
||||
public UpnpSettingsResource(BridgeSettings theSettings) {
|
||||
public UpnpSettingsResource(BridgeSettingsDescriptor theBridgeSettings) {
|
||||
super();
|
||||
this.theSettings = theSettings;
|
||||
this.theSettings = theBridgeSettings;
|
||||
}
|
||||
|
||||
public void setupServer() {
|
||||
@@ -75,14 +72,5 @@ public class UpnpSettingsResource {
|
||||
|
||||
return filledTemplate;
|
||||
} );
|
||||
|
||||
// http://ip_address:port/upnp/settings which returns the bridge configuration settings
|
||||
get(UPNP_CONTEXT + "/settings", "application/json", (request, response) -> {
|
||||
log.debug("bridge settings requested from " + request.ip());
|
||||
|
||||
response.status(200);
|
||||
|
||||
return theSettings;
|
||||
}, new JsonTransformer());
|
||||
}
|
||||
}
|
||||
|
||||
110
src/main/java/com/bwssystems/NestBridge/NestHome.java
Normal file
110
src/main/java/com/bwssystems/NestBridge/NestHome.java
Normal file
@@ -0,0 +1,110 @@
|
||||
package com.bwssystems.NestBridge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.nest.controller.Home;
|
||||
import com.bwssystems.nest.controller.Nest;
|
||||
import com.bwssystems.nest.controller.NestSession;
|
||||
import com.bwssystems.nest.controller.Thermostat;
|
||||
import com.bwssystems.nest.protocol.error.LoginException;
|
||||
import com.bwssystems.nest.protocol.status.WhereDetail;
|
||||
import com.bwssystems.nest.protocol.status.WhereItem;
|
||||
|
||||
public class NestHome {
|
||||
private static final Logger log = LoggerFactory.getLogger(NestHome.class);
|
||||
private NestSession theSession;
|
||||
private Nest theNest;
|
||||
private ArrayList<NestItem> nestItems;
|
||||
|
||||
public NestHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
theSession = null;
|
||||
theNest = null;
|
||||
nestItems = null;
|
||||
|
||||
if(!bridgeSettings.isValidNest()) {
|
||||
log.debug("not a valid nest");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
theSession = new NestSession(bridgeSettings.getNestuser(), bridgeSettings.getNestpwd());
|
||||
theNest = new Nest(theSession);
|
||||
} catch (LoginException e) {
|
||||
log.error("Caught Login Exception, exiting....");
|
||||
theSession = null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<NestItem> getItems() {
|
||||
if(theNest == null)
|
||||
return null;
|
||||
|
||||
if(nestItems == null) {
|
||||
nestItems = new ArrayList<NestItem>();
|
||||
Set<String> homeNames = theNest.getHomeNames();
|
||||
Home aHome = null;
|
||||
NestItem anItem = null;
|
||||
for(String name : homeNames) {
|
||||
aHome = theNest.getHome(name);
|
||||
anItem = new NestItem();
|
||||
anItem.setId(name);
|
||||
anItem.setName(aHome.getDetail().getName());
|
||||
anItem.setType("Home");
|
||||
anItem.setLocation(aHome.getDetail().getLocation());
|
||||
nestItems.add(anItem);
|
||||
}
|
||||
Thermostat thermo = null;
|
||||
Set<String> thermoNames = theNest.getThermostatNames();
|
||||
for(String name : thermoNames) {
|
||||
thermo = theNest.getThermostat(name);
|
||||
anItem = new NestItem();
|
||||
anItem.setId(name);
|
||||
anItem.setType("Thermostat");
|
||||
String where = null;
|
||||
String homeName= null;
|
||||
Boolean found = false;
|
||||
for(String aHomeName : homeNames) {
|
||||
WhereDetail aDetail = theNest.getWhere(aHomeName);
|
||||
ListIterator<WhereItem> anIterator = aDetail.getWheres().listIterator();
|
||||
while(anIterator.hasNext()) {
|
||||
WhereItem aWhereItem = (WhereItem) anIterator.next();
|
||||
if(aWhereItem.getWhereId().equals(thermo.getDeviceDetail().getWhereId())) {
|
||||
where = aWhereItem.getName();
|
||||
homeName = theNest.getHome(aHomeName).getDetail().getName();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found)
|
||||
break;
|
||||
}
|
||||
anItem.setName(where + "(" + name.substring(name.length() - 4) + ")");
|
||||
anItem.setLocation(where + " - " + homeName);
|
||||
nestItems.add(anItem);
|
||||
}
|
||||
}
|
||||
|
||||
return nestItems;
|
||||
}
|
||||
|
||||
public Nest getTheNest() {
|
||||
return theNest;
|
||||
}
|
||||
|
||||
public void closeTheNest() {
|
||||
if(theSession != null) {
|
||||
theNest.endNestSession();
|
||||
theNest = null;
|
||||
theSession = null;
|
||||
nestItems = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
src/main/java/com/bwssystems/NestBridge/NestInstruction.java
Normal file
33
src/main/java/com/bwssystems/NestBridge/NestInstruction.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.bwssystems.NestBridge;
|
||||
|
||||
public class NestInstruction {
|
||||
private String name;
|
||||
private Boolean away;
|
||||
private String control;
|
||||
private String temp;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public Boolean getAway() {
|
||||
return away;
|
||||
}
|
||||
public void setAway(Boolean away) {
|
||||
this.away = away;
|
||||
}
|
||||
public String getControl() {
|
||||
return control;
|
||||
}
|
||||
public void setControl(String control) {
|
||||
this.control = control;
|
||||
}
|
||||
public String getTemp() {
|
||||
return temp;
|
||||
}
|
||||
public void setTemp(String temp) {
|
||||
this.temp = temp;
|
||||
}
|
||||
}
|
||||
32
src/main/java/com/bwssystems/NestBridge/NestItem.java
Normal file
32
src/main/java/com/bwssystems/NestBridge/NestItem.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.bwssystems.NestBridge;
|
||||
|
||||
public class NestItem {
|
||||
private String name;
|
||||
private String id;
|
||||
private String type;
|
||||
private String location;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String anid) {
|
||||
id = anid;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
}
|
||||
21
src/main/java/com/bwssystems/harmony/HarmonyActivity.java
Normal file
21
src/main/java/com/bwssystems/harmony/HarmonyActivity.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
import net.whistlingfish.harmony.config.Activity;
|
||||
|
||||
public class HarmonyActivity {
|
||||
private String hub;
|
||||
private Activity activity;
|
||||
public String getHub() {
|
||||
return hub;
|
||||
}
|
||||
public void setHub(String hub) {
|
||||
this.hub = hub;
|
||||
}
|
||||
public Activity getActivity() {
|
||||
return activity;
|
||||
}
|
||||
public void setActivity(Activity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
}
|
||||
20
src/main/java/com/bwssystems/harmony/HarmonyDevice.java
Normal file
20
src/main/java/com/bwssystems/harmony/HarmonyDevice.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
import net.whistlingfish.harmony.config.Device;
|
||||
|
||||
public class HarmonyDevice {
|
||||
private Device device;
|
||||
private String hub;
|
||||
public Device getDevice() {
|
||||
return device;
|
||||
}
|
||||
public void setDevice(Device device) {
|
||||
this.device = device;
|
||||
}
|
||||
public String getHub() {
|
||||
return hub;
|
||||
}
|
||||
public void setHub(String hub) {
|
||||
this.hub = hub;
|
||||
}
|
||||
}
|
||||
@@ -119,4 +119,14 @@ public class HarmonyHandler {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
log.debug("Harmony api shutdown requested.");
|
||||
if(devMode)
|
||||
return;
|
||||
|
||||
harmonyClient.disconnect();
|
||||
harmonyClient = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
127
src/main/java/com/bwssystems/harmony/HarmonyHome.java
Normal file
127
src/main/java/com/bwssystems/harmony/HarmonyHome.java
Normal file
@@ -0,0 +1,127 @@
|
||||
package com.bwssystems.harmony;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.IpList;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
|
||||
import net.whistlingfish.harmony.config.Activity;
|
||||
import net.whistlingfish.harmony.config.Device;
|
||||
|
||||
public class HarmonyHome {
|
||||
private static final Logger log = LoggerFactory.getLogger(HarmonyHome.class);
|
||||
private Map<String, HarmonyServer> hubs;
|
||||
private Boolean isDevMode;
|
||||
|
||||
public HarmonyHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
super();
|
||||
isDevMode = Boolean.parseBoolean(System.getProperty("dev.mode", "false"));
|
||||
hubs = new HashMap<String, HarmonyServer>();
|
||||
if(!bridgeSettings.isValidHarmony() && !isDevMode)
|
||||
return;
|
||||
if(isDevMode) {
|
||||
NamedIP devModeIp = new NamedIP();
|
||||
devModeIp.setIp("10.10.10.10");
|
||||
devModeIp.setName("devMode");
|
||||
List<NamedIP> theList = new ArrayList<NamedIP>();
|
||||
theList.add(devModeIp);
|
||||
IpList thedevList = new IpList();
|
||||
thedevList.setDevices(theList);
|
||||
bridgeSettings.setHarmonyAddress(thedevList);
|
||||
}
|
||||
Iterator<NamedIP> theList = bridgeSettings.getHarmonyAddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aHub = theList.next();
|
||||
try {
|
||||
hubs.put(aHub.getName(), HarmonyServer.setup(bridgeSettings, isDevMode, aHub));
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot get harmony client (" + aHub.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdownHarmonyHubs() {
|
||||
if(isDevMode)
|
||||
return;
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
hubs.get(key).getMyHarmony().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public HarmonyHandler getHarmonyHandler(String aName) {
|
||||
HarmonyHandler aHandler = null;
|
||||
if(aName == null || aName.equals("")) {
|
||||
aName = "default";
|
||||
}
|
||||
|
||||
if(hubs.get(aName) == null) {
|
||||
Set<String> keys = hubs.keySet();
|
||||
if(!keys.isEmpty()) {
|
||||
aHandler = hubs.get(keys.toArray()[0]).getMyHarmony();
|
||||
}
|
||||
else
|
||||
aHandler = null;
|
||||
}
|
||||
else
|
||||
aHandler = hubs.get(aName).getMyHarmony();
|
||||
return aHandler;
|
||||
}
|
||||
|
||||
public List<HarmonyActivity> getActivities() {
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
ArrayList<HarmonyActivity> activityList = new ArrayList<HarmonyActivity>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Iterator<Activity> activities = hubs.get(key).getMyHarmony().getActivities().iterator();
|
||||
while(activities.hasNext()) {
|
||||
HarmonyActivity anActivity = new HarmonyActivity();
|
||||
anActivity.setActivity(activities.next());
|
||||
anActivity.setHub(key);
|
||||
activityList.add(anActivity);
|
||||
}
|
||||
}
|
||||
return activityList;
|
||||
}
|
||||
public List<HarmonyActivity> getCurrentActivities() {
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
ArrayList<HarmonyActivity> activityList = new ArrayList<HarmonyActivity>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Iterator<Activity> activities = hubs.get(key).getMyHarmony().getActivities().iterator();
|
||||
while(activities.hasNext()) {
|
||||
HarmonyActivity anActivity = new HarmonyActivity();
|
||||
anActivity.setActivity(activities.next());
|
||||
anActivity.setHub(key);
|
||||
activityList.add(anActivity);
|
||||
}
|
||||
}
|
||||
return activityList;
|
||||
}
|
||||
public List<HarmonyDevice> getDevices() {
|
||||
Iterator<String> keys = hubs.keySet().iterator();
|
||||
ArrayList<HarmonyDevice> deviceList = new ArrayList<HarmonyDevice>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Iterator<Device> devices = hubs.get(key).getMyHarmony().getDevices().iterator();
|
||||
while(devices.hasNext()) {
|
||||
HarmonyDevice aDevice = new HarmonyDevice();
|
||||
aDevice.setDevice(devices.next());
|
||||
aDevice.setHub(key);
|
||||
deviceList.add(aDevice);
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,8 @@ import javax.inject.Inject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettings;
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
@@ -24,40 +25,44 @@ public class HarmonyServer {
|
||||
private HarmonyHandler myHarmony;
|
||||
private DevModeResponse devResponse;
|
||||
private OAReplyProvider dummyProvider;
|
||||
|
||||
private NamedIP myNameAndIP;
|
||||
private Boolean isDevMode;
|
||||
private Logger log = LoggerFactory.getLogger(HarmonyServer.class);
|
||||
|
||||
public HarmonyServer() {
|
||||
public HarmonyServer(NamedIP theHarmonyAddress) {
|
||||
super();
|
||||
myHarmony = null;
|
||||
dummyProvider = null;
|
||||
myNameAndIP = theHarmonyAddress;
|
||||
isDevMode = false;
|
||||
}
|
||||
|
||||
public static HarmonyServer setup(BridgeSettings bridgeSettings) throws Exception {
|
||||
if(!bridgeSettings.isValidHarmony()) {
|
||||
return new HarmonyServer();
|
||||
public static HarmonyServer setup(BridgeSettingsDescriptor bridgeSettings, Boolean harmonyDevMode, NamedIP theHarmonyAddress) throws Exception {
|
||||
if(!bridgeSettings.isValidHarmony() && harmonyDevMode) {
|
||||
return new HarmonyServer(theHarmonyAddress);
|
||||
}
|
||||
Injector injector = null;
|
||||
if(!bridgeSettings.isDevMode())
|
||||
if(!harmonyDevMode)
|
||||
injector = Guice.createInjector(new HarmonyClientModule());
|
||||
HarmonyServer mainObject = new HarmonyServer();
|
||||
if(!bridgeSettings.isDevMode())
|
||||
HarmonyServer mainObject = new HarmonyServer(theHarmonyAddress);
|
||||
if(!harmonyDevMode)
|
||||
injector.injectMembers(mainObject);
|
||||
mainObject.execute(bridgeSettings);
|
||||
mainObject.execute(bridgeSettings, harmonyDevMode);
|
||||
return mainObject;
|
||||
}
|
||||
|
||||
private void execute(BridgeSettings mySettings) throws Exception {
|
||||
private void execute(BridgeSettingsDescriptor mySettings, Boolean harmonyDevMode) throws Exception {
|
||||
Boolean noopCalls = Boolean.parseBoolean(System.getProperty("noop.calls", "false"));
|
||||
String modeString = "";
|
||||
isDevMode = harmonyDevMode;
|
||||
String modeString = "";
|
||||
if(dummyProvider != null)
|
||||
log.debug("something is very wrong as dummyProvider is not null...");
|
||||
if(mySettings.isDevMode())
|
||||
if(isDevMode)
|
||||
modeString = " (development mode)";
|
||||
else if(noopCalls)
|
||||
modeString = " (no op calls to harmony)";
|
||||
log.info("setup initiated " + modeString + "....");
|
||||
if(mySettings.isDevMode())
|
||||
if(isDevMode)
|
||||
{
|
||||
harmonyClient = null;
|
||||
devResponse = new DevModeResponse();
|
||||
@@ -70,7 +75,7 @@ public class HarmonyServer {
|
||||
log.info(format("activity changed: [%d] %s", activity.getId(), activity.getLabel()));
|
||||
}
|
||||
});
|
||||
harmonyClient.connect(mySettings.getHarmonyAddress(), mySettings.getHarmonyUser(), mySettings.getHarmonyPwd());
|
||||
harmonyClient.connect(myNameAndIP.getIp(), mySettings.getHarmonyUser(), mySettings.getHarmonyPwd());
|
||||
}
|
||||
myHarmony = new HarmonyHandler(harmonyClient, noopCalls, devResponse);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ public class Device {
|
||||
private String level;
|
||||
private String state;
|
||||
private String comment;
|
||||
private String veraname;
|
||||
private String veraaddress;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -79,5 +81,17 @@ public class Device {
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
public String getVeraname() {
|
||||
return veraname;
|
||||
}
|
||||
public void setVeraname(String veraname) {
|
||||
this.veraname = veraname;
|
||||
}
|
||||
public String getVeraaddress() {
|
||||
return veraaddress;
|
||||
}
|
||||
public void setVeraaddress(String veraaddress) {
|
||||
this.veraaddress = veraaddress;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ public class Scene {
|
||||
private String name;
|
||||
private String id;
|
||||
private String room;
|
||||
private String veraname;
|
||||
private String veraaddress;
|
||||
public String getActive() {
|
||||
return active;
|
||||
}
|
||||
@@ -29,5 +31,17 @@ public class Scene {
|
||||
public void setRoom(String room) {
|
||||
this.room = room;
|
||||
}
|
||||
public String getVeraname() {
|
||||
return veraname;
|
||||
}
|
||||
public void setVeraname(String veraname) {
|
||||
this.veraname = veraname;
|
||||
}
|
||||
public String getVeraaddress() {
|
||||
return veraaddress;
|
||||
}
|
||||
public void setVeraaddress(String veraaddress) {
|
||||
this.veraaddress = veraaddress;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
73
src/main/java/com/bwssystems/vera/VeraHome.java
Normal file
73
src/main/java/com/bwssystems/vera/VeraHome.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package com.bwssystems.vera;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.luupRequests.Device;
|
||||
import com.bwssystems.luupRequests.Scene;
|
||||
import com.bwssystems.luupRequests.Sdata;
|
||||
|
||||
public class VeraHome {
|
||||
private static final Logger log = LoggerFactory.getLogger(VeraHome.class);
|
||||
private Map<String, VeraInfo> veras;
|
||||
|
||||
public VeraHome(BridgeSettingsDescriptor bridgeSettings) {
|
||||
veras = new HashMap<String, VeraInfo>();
|
||||
if(!bridgeSettings.isValidVera())
|
||||
return;
|
||||
Iterator<NamedIP> theList = bridgeSettings.getVeraAddress().getDevices().iterator();
|
||||
while(theList.hasNext()) {
|
||||
NamedIP aVera = theList.next();
|
||||
veras.put(aVera.getName(), new VeraInfo(aVera, bridgeSettings.isValidVera()));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Device> getDevices() {
|
||||
log.debug("consolidating devices for veras");
|
||||
Iterator<String> keys = veras.keySet().iterator();
|
||||
ArrayList<Device> deviceList = new ArrayList<Device>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Sdata theSdata = veras.get(key).getSdata();
|
||||
if(theSdata != null) {
|
||||
Iterator<Device> devices = theSdata.getDevices().iterator();
|
||||
while(devices.hasNext()) {
|
||||
deviceList.add(devices.next());
|
||||
}
|
||||
}
|
||||
else {
|
||||
deviceList = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
public List<Scene> getScenes() {
|
||||
log.debug("consolidating scenes for veras");
|
||||
Iterator<String> keys = veras.keySet().iterator();
|
||||
ArrayList<Scene> sceneList = new ArrayList<Scene>();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Sdata theSdata = veras.get(key).getSdata();
|
||||
if(theSdata != null) {
|
||||
Iterator<Scene> scenes = theSdata.getScenes().iterator();
|
||||
while(scenes.hasNext()) {
|
||||
sceneList.add(scenes.next());
|
||||
}
|
||||
}
|
||||
else {
|
||||
sceneList = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sceneList;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.bwssystems.HABridge.NamedIP;
|
||||
import com.bwssystems.luupRequests.Categorie;
|
||||
import com.bwssystems.luupRequests.Device;
|
||||
import com.bwssystems.luupRequests.Room;
|
||||
@@ -25,27 +26,30 @@ public class VeraInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(VeraInfo.class);
|
||||
private HttpClient httpClient;
|
||||
private static final String SDATA_REQUEST = ":3480/data_request?id=sdata&output_format=json";
|
||||
private String veraAddressString;
|
||||
private NamedIP veraAddress;
|
||||
private Boolean validVera;
|
||||
|
||||
public VeraInfo(String addressString, Boolean isValidVera) {
|
||||
public VeraInfo(NamedIP addressName, Boolean isValidVera) {
|
||||
super();
|
||||
httpClient = HttpClients.createMinimal();
|
||||
veraAddressString = addressString;
|
||||
httpClient = HttpClients.createDefault();
|
||||
veraAddress = addressName;
|
||||
validVera = isValidVera;
|
||||
}
|
||||
|
||||
public Sdata getSdata() {
|
||||
Sdata theSdata = null;
|
||||
if(!validVera)
|
||||
return new Sdata();
|
||||
return theSdata;
|
||||
|
||||
String theUrl = "http://" + veraAddressString + SDATA_REQUEST;
|
||||
String theUrl = "http://" + veraAddress.getIp() + SDATA_REQUEST;
|
||||
String theData;
|
||||
|
||||
theData = doHttpGETRequest(theUrl);
|
||||
Sdata theSdata = new Gson().fromJson(theData, Sdata.class);
|
||||
log.debug("GET sdata - full: " + theSdata.getFull() + ", version: " + theSdata.getVersion());
|
||||
denormalizeSdata(theSdata);
|
||||
if(theData != null) {
|
||||
theSdata = new Gson().fromJson(theData, Sdata.class);
|
||||
log.debug("GET sdata - full: " + theSdata.getFull() + ", version: " + theSdata.getVersion());
|
||||
denormalizeSdata(theSdata);
|
||||
}
|
||||
return theSdata;
|
||||
}
|
||||
|
||||
@@ -71,6 +75,8 @@ public class VeraInfo {
|
||||
theDevice.setCategory(categoryMap.get(theDevice.getCategory()).getName());
|
||||
else
|
||||
theDevice.setCategory("<unknown>");
|
||||
theDevice.setVeraaddress(veraAddress.getIp());
|
||||
theDevice.setVeraname(veraAddress.getName());
|
||||
}
|
||||
|
||||
ListIterator<Scene> theSecneIter = theSdata.getScenes().listIterator();
|
||||
@@ -81,24 +87,26 @@ public class VeraInfo {
|
||||
theScene.setRoom(roomMap.get(theScene.getRoom()).getName());
|
||||
else
|
||||
theScene.setRoom("no room");
|
||||
theScene.setVeraaddress(veraAddress.getIp());
|
||||
theScene.setVeraname(veraAddress.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// This function executes the url against the vera
|
||||
protected String doHttpGETRequest(String url) {
|
||||
String theContent = null;
|
||||
log.debug("calling GET on URL: " + url);
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
try {
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
String theContent = EntityUtils.toString(response.getEntity()); //read content for data
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
log.debug("GET on URL responded: " + response.getStatusLine().getStatusCode());
|
||||
if(response.getStatusLine().getStatusCode() == 200){
|
||||
return theContent;
|
||||
theContent = EntityUtils.toString(response.getEntity()); //read content for data
|
||||
EntityUtils.consume(response.getEntity()); //close out inputstream ignore content
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error calling out to HA gateway", e);
|
||||
log.error("doHttpGETRequest: Error calling out to HA gateway: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
return theContent;
|
||||
}
|
||||
}
|
||||
|
||||
6
src/main/resources/public/css/bootstrap-theme.min.css
vendored
Normal file
6
src/main/resources/public/css/bootstrap-theme.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/main/resources/public/css/ngDialog-theme-default.min.css
vendored
Normal file
1
src/main/resources/public/css/ngDialog-theme-default.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
@-webkit-keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}.ngdialog.ngdialog-theme-default{padding-bottom:160px;padding-top:160px}.ngdialog.ngdialog-theme-default.ngdialog-closing .ngdialog-content{-webkit-animation:ngdialog-flyout .5s;animation:ngdialog-flyout .5s}.ngdialog.ngdialog-theme-default .ngdialog-content{-webkit-animation:ngdialog-flyin .5s;animation:ngdialog-flyin .5s;background:#f0f0f0;border-radius:5px;color:#444;font-family:Helvetica,sans-serif;font-size:1.1em;line-height:1.5em;margin:0 auto;max-width:100%;padding:1em;position:relative;width:450px}.ngdialog.ngdialog-theme-default .ngdialog-close{border-radius:5px;cursor:pointer;position:absolute;right:0;top:0}.ngdialog.ngdialog-theme-default .ngdialog-close:before{background:0 0;border-radius:3px;color:#bbb;content:'\00D7';font-size:26px;font-weight:400;height:30px;line-height:26px;position:absolute;right:3px;text-align:center;top:3px;width:30px}.ngdialog.ngdialog-theme-default .ngdialog-close:active:before,.ngdialog.ngdialog-theme-default .ngdialog-close:hover:before{color:#777}.ngdialog.ngdialog-theme-default .ngdialog-message{margin-bottom:.5em}.ngdialog.ngdialog-theme-default .ngdialog-input{margin-bottom:1em}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url],.ngdialog.ngdialog-theme-default .ngdialog-input textarea{background:#fff;border:0;border-radius:3px;font-family:inherit;font-size:inherit;font-weight:inherit;margin:0 0 .25em;min-height:2.5em;padding:.25em .67em;width:100%}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input textarea:focus{box-shadow:inset 0 0 0 2px #8dbdf1;outline:0}.ngdialog.ngdialog-theme-default .ngdialog-buttons:after{content:'';display:table;clear:both}.ngdialog.ngdialog-theme-default .ngdialog-button{border:0;border-radius:3px;cursor:pointer;float:right;font-family:inherit;font-size:.8em;letter-spacing:.1em;line-height:1em;margin:0 0 0 .5em;padding:.75em 2em;text-transform:uppercase}.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:ngdialog-pulse 1.1s infinite;animation:ngdialog-pulse 1.1s infinite;outline:0}@media (max-width:568px){.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:none;animation:none}}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-primary{background:#3288e6;color:#fff}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-secondary{background:#e0e0e0;color:#777}
|
||||
1
src/main/resources/public/css/ngDialog.min.css
vendored
Normal file
1
src/main/resources/public/css/ngDialog.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.ngdialog,.ngdialog-overlay{position:fixed;top:0;right:0;bottom:0;left:0}@-webkit-keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}@keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}.ngdialog{box-sizing:border-box;overflow:auto;-webkit-overflow-scrolling:touch;z-index:10000}.ngdialog *,.ngdialog :after,.ngdialog :before{box-sizing:inherit}.ngdialog.ngdialog-disabled-animation,.ngdialog.ngdialog-disabled-animation .ngdialog-content,.ngdialog.ngdialog-disabled-animation .ngdialog-overlay{-webkit-animation:none!important;animation:none!important}.ngdialog-overlay{background:rgba(0,0,0,.4);-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s}.ngdialog-no-overlay{pointer-events:none}.ngdialog.ngdialog-closing .ngdialog-overlay{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-content{background:#fff;-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s;pointer-events:all}.ngdialog.ngdialog-closing .ngdialog-content{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-close:before{font-family:Helvetica,Arial,sans-serif;content:'\00D7';cursor:pointer}body.ngdialog-open,html.ngdialog-open{overflow:hidden}
|
||||
7
src/main/resources/public/css/ngToast.min.css
vendored
Normal file
7
src/main/resources/public/css/ngToast.min.css
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* ngToast v1.5.6 (http://tameraydin.github.io/ngToast)
|
||||
* Copyright 2015 Tamer Aydin (http://tamerayd.in)
|
||||
* Licensed under MIT (http://tameraydin.mit-license.org/)
|
||||
*/
|
||||
|
||||
.ng-toast{position:fixed;z-index:1080;width:100%;height:0;margin-top:20px;text-align:center}.ng-toast.ng-toast--top,.ng-toast.ng-toast--top .ng-toast__list{top:0;bottom:auto}.ng-toast.ng-toast--top.ng-toast--center .ng-toast__list{position:static}.ng-toast.ng-toast--bottom,.ng-toast.ng-toast--bottom .ng-toast__list{top:auto;bottom:0}.ng-toast.ng-toast--bottom.ng-toast--center .ng-toast__list{pointer-events:none}.ng-toast.ng-toast--bottom.ng-toast--center .ng-toast__message .alert{pointer-events:auto}.ng-toast.ng-toast--right .ng-toast__list{left:auto;right:0;margin-right:20px}.ng-toast.ng-toast--right .ng-toast__message{text-align:right}.ng-toast.ng-toast--left .ng-toast__list{right:auto;left:0;margin-left:20px}.ng-toast.ng-toast--left .ng-toast__message{text-align:left}.ng-toast .ng-toast__list{display:inline-block;position:absolute;right:0;left:0;margin:0 auto;padding:0;list-style:none}.ng-toast .ng-toast__message{display:block;width:100%;text-align:center}.ng-toast .ng-toast__message .alert{display:inline-block}.ng-toast .ng-toast__message__count{display:inline-block;margin:0 15px 0 5px}
|
||||
2
src/main/resources/public/css/rzslider.min.css
vendored
Normal file
2
src/main/resources/public/css/rzslider.min.css
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/*! angularjs-slider - v2.8.0 - (c) Rafal Zajac <rzajac@gmail.com>, Valentin Hervieu <valentin@hervieu.me>, Jussi Saarivirta <jusasi@gmail.com>, Angelin Sirbu <angelin.sirbu@gmail.com> - https://github.com/angular-slider/angularjs-slider - 2016-02-08 */
|
||||
rzslider{position:relative;display:inline-block;width:100%;height:4px;margin:35px 0 15px 0;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}rzslider[disabled]{cursor:not-allowed}rzslider[disabled] .rz-pointer{cursor:not-allowed;background-color:#d8e0f3}rzslider span{position:absolute;display:inline-block;white-space:nowrap}rzslider .rz-base{width:100%;height:100%;padding:0}rzslider .rz-bar-wrapper{left:0;z-index:1;width:100%;height:32px;padding-top:16px;margin-top:-16px;box-sizing:border-box}rzslider .rz-bar-wrapper.rz-draggable{cursor:move}rzslider .rz-bar{left:0;z-index:1;width:100%;height:4px;background:#d8e0f3;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-bar.rz-selection{z-index:2;background:#0db9f0;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-pointer{top:-14px;z-index:3;width:32px;height:32px;cursor:pointer;background-color:#0db9f0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}rzslider .rz-pointer:after{position:absolute;top:12px;left:12px;width:8px;height:8px;background:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;content:''}rzslider .rz-pointer:hover:after{background-color:#fff}rzslider .rz-pointer.rz-active{z-index:4}rzslider .rz-pointer.rz-active:after{background-color:#451aff}rzslider .rz-bubble{bottom:16px;padding:1px 3px;color:#55637d;cursor:default}rzslider .rz-bubble.rz-selection{top:16px}rzslider .rz-bubble.rz-limit{color:#55637d}rzslider .rz-ticks{position:absolute;top:-3px;left:0;z-index:1;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;height:0;padding:0 11px;margin:0;list-style:none;box-sizing:border-box;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}rzslider .rz-ticks .tick{width:10px;height:10px;text-align:center;cursor:pointer;background:#d8e0f3;border-radius:50%}rzslider .rz-ticks .tick.selected{background:#0db9f0}rzslider .rz-ticks .tick .tick-value{position:absolute;top:-30px;transform:translate(-50%,0)}rzslider.vertical{position:relative;width:4px;height:100%;padding:0;margin:0 20px;vertical-align:baseline}rzslider.vertical .rz-base{width:100%;height:100%;padding:0}rzslider.vertical .rz-bar-wrapper{top:auto;left:0;width:32px;height:100%;padding:0 0 0 16px;margin:0 0 0 -16px}rzslider.vertical .rz-bar{bottom:0;left:auto;width:4px;height:100%}rzslider.vertical .rz-pointer{top:auto;bottom:0;left:-14px!important}rzslider.vertical .rz-bubble{bottom:0;left:16px!important;margin-left:3px}rzslider.vertical .rz-bubble.rz-selection{top:auto;left:16px!important}rzslider.vertical .rz-ticks{top:0;left:-3px;z-index:1;width:0;height:100%;padding:11px 0;-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}rzslider.vertical .rz-ticks .tick{vertical-align:middle}rzslider.vertical .rz-ticks .tick .tick-value{top:auto;right:-30px;transform:translate(0,-28%)}
|
||||
@@ -7,6 +7,11 @@
|
||||
<title>HA Bridge</title>
|
||||
<link href="css/main.css" rel="stylesheet">
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="css/bootstrap-theme.min.css" rel="stylesheet">
|
||||
<link href="css/ngToast.min.css" rel="stylesheet">
|
||||
<link href="css/rzslider.min.css" rel="stylesheet">
|
||||
<link href="css/ngDialog.min.css" rel="stylesheet">
|
||||
<link href="css/ngDialog-theme-default.min.css" rel="stylesheet">
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script type="text/javascript" src="js/html5shiv.min.js"></script>
|
||||
@@ -14,14 +19,16 @@
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<toast></toast>
|
||||
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container" ng-controller="VersionController">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">HA Bridge</a>
|
||||
</div>
|
||||
@@ -29,11 +36,10 @@
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="active"><a href="#">Home</a></li>
|
||||
<li><a href="http://echo.amazon.com/#cards" target="_blank">My Echo</a></li>
|
||||
<li><a href="https://github.com/bwssytems/ha-bridge/blob/master/README.md" target="_blank">Help</a></li>
|
||||
<li class="dropdown">
|
||||
<a id="dropdownMenu1" href="" class="dropdown-toggle"
|
||||
data-toggle="dropdown" role="button" aria-haspopup="true"
|
||||
aria-expanded="false">About <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<a id="dLabel" href="" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">About <span class="caret"></span></a>
|
||||
<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>
|
||||
@@ -49,11 +55,14 @@
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script src="js/jquery-1.11.3.min.js"></script>
|
||||
<script src="js/angular.min.js"></script>
|
||||
<script src="js/angular-route.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script src="js/angular-sanitize.min.js"></script>
|
||||
<script src="js/ngToast.min.js"></script>
|
||||
<script src="js/rzslider.min.js"></script>
|
||||
<script src="js/rzslider.min.js"></script>
|
||||
<script src="js/ngDialog.min.js"></script>
|
||||
<script src="scripts/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
16
src/main/resources/public/js/angular-sanitize.min.js
vendored
Normal file
16
src/main/resources/public/js/angular-sanitize.min.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
AngularJS v1.4.3
|
||||
(c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(n,h,p){'use strict';function E(a){var f=[];r(f,h.noop).chars(a);return f.join("")}function g(a,f){var d={},c=a.split(","),b;for(b=0;b<c.length;b++)d[f?h.lowercase(c[b]):c[b]]=!0;return d}function F(a,f){function d(a,b,d,l){b=h.lowercase(b);if(s[b])for(;e.last()&&t[e.last()];)c("",e.last());u[b]&&e.last()==b&&c("",b);(l=v[b]||!!l)||e.push(b);var m={};d.replace(G,function(b,a,f,c,d){m[a]=q(f||c||d||"")});f.start&&f.start(b,m,l)}function c(b,a){var c=0,d;if(a=h.lowercase(a))for(c=e.length-
|
||||
1;0<=c&&e[c]!=a;c--);if(0<=c){for(d=e.length-1;d>=c;d--)f.end&&f.end(e[d]);e.length=c}}"string"!==typeof a&&(a=null===a||"undefined"===typeof a?"":""+a);var b,k,e=[],m=a,l;for(e.last=function(){return e[e.length-1]};a;){l="";k=!0;if(e.last()&&w[e.last()])a=a.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*"+e.last()+"[^>]*>","i"),function(a,b){b=b.replace(H,"$1").replace(I,"$1");f.chars&&f.chars(q(b));return""}),c("",e.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",
|
||||
b)===b&&(f.comment&&f.comment(a.substring(4,b)),a=a.substring(b+3),k=!1);else if(x.test(a)){if(b=a.match(x))a=a.replace(b[0],""),k=!1}else if(J.test(a)){if(b=a.match(y))a=a.substring(b[0].length),b[0].replace(y,c),k=!1}else K.test(a)&&((b=a.match(z))?(b[4]&&(a=a.substring(b[0].length),b[0].replace(z,d)),k=!1):(l+="<",a=a.substring(1)));k&&(b=a.indexOf("<"),l+=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),f.chars&&f.chars(q(l)))}if(a==m)throw L("badparse",a);m=a}c()}function q(a){if(!a)return"";A.innerHTML=
|
||||
a.replace(/</g,"<");return A.textContent}function B(a){return a.replace(/&/g,"&").replace(M,function(a){var d=a.charCodeAt(0);a=a.charCodeAt(1);return"&#"+(1024*(d-55296)+(a-56320)+65536)+";"}).replace(N,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"<").replace(/>/g,">")}function r(a,f){var d=!1,c=h.bind(a,a.push);return{start:function(a,k,e){a=h.lowercase(a);!d&&w[a]&&(d=a);d||!0!==C[a]||(c("<"),c(a),h.forEach(k,function(d,e){var k=h.lowercase(e),g="img"===a&&"src"===k||
|
||||
"background"===k;!0!==O[k]||!0===D[k]&&!f(d,g)||(c(" "),c(e),c('="'),c(B(d)),c('"'))}),c(e?"/>":">"))},end:function(a){a=h.lowercase(a);d||!0!==C[a]||(c("</"),c(a),c(">"));a==d&&(d=!1)},chars:function(a){d||c(B(a))}}}var L=h.$$minErr("$sanitize"),z=/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,y=/^<\/\s*([\w:-]+)[^>]*>/,G=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,K=/^</,J=/^<\//,H=/\x3c!--(.*?)--\x3e/g,x=/<!DOCTYPE([^>]*?)>/i,
|
||||
I=/<!\[CDATA\[(.*?)]]\x3e/g,M=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,N=/([^\#-~| |!])/g,v=g("area,br,col,hr,img,wbr");n=g("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr");p=g("rp,rt");var u=h.extend({},p,n),s=h.extend({},n,g("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),t=h.extend({},p,g("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
|
||||
n=g("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan,use");var w=g("script,style"),C=h.extend({},v,s,t,u,n),D=g("background,cite,href,longdesc,src,usemap,xlink:href");n=g("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,valign,value,vspace,width");
|
||||
p=g("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan",
|
||||
!0);var O=h.extend({},D,p,n),A=document.createElement("pre");h.module("ngSanitize",[]).provider("$sanitize",function(){this.$get=["$$sanitizeUri",function(a){return function(f){var d=[];F(f,r(d,function(c,b){return!/^unsafe/.test(a(c,b))}));return d.join("")}}]});h.module("ngSanitize").filter("linky",["$sanitize",function(a){var f=/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,d=/^mailto:/i;return function(c,b){function k(a){a&&g.push(E(a))}function e(a,
|
||||
c){g.push("<a ");h.isDefined(b)&&g.push('target="',b,'" ');g.push('href="',a.replace(/"/g,"""),'">');k(c);g.push("</a>")}if(!c)return c;for(var m,l=c,g=[],n,p;m=l.match(f);)n=m[0],m[2]||m[4]||(n=(m[3]?"http://":"mailto:")+n),p=m.index,k(l.substr(0,p)),e(n,m[0].replace(d,"")),l=l.substring(p+m[0].length);k(l);return a(g.join(""))}}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-sanitize.min.js.map
|
||||
2
src/main/resources/public/js/ngDialog.min.js
vendored
Normal file
2
src/main/resources/public/js/ngDialog.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
src/main/resources/public/js/ngToast.min.js
vendored
Normal file
6
src/main/resources/public/js/ngToast.min.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* ngToast v1.5.6 (http://tameraydin.github.io/ngToast)
|
||||
* Copyright 2015 Tamer Aydin (http://tamerayd.in)
|
||||
* Licensed under MIT (http://tameraydin.mit-license.org/)
|
||||
*/
|
||||
!function(a,b,c){"use strict";b.module("ngToast.provider",[]).provider("ngToast",[function(){function a(a){for(var d=Math.floor(1e3*Math.random());c.indexOf(d)>-1;)d=Math.floor(1e3*Math.random());this.id=d,this.count=0,this.animation=e.animation,this.className=e.className,this.additionalClasses=e.additionalClasses,this.dismissOnTimeout=e.dismissOnTimeout,this.timeout=e.timeout,this.dismissButton=e.dismissButton,this.dismissButtonHtml=e.dismissButtonHtml,this.dismissOnClick=e.dismissOnClick,this.compileContent=e.compileContent,b.extend(this,a)}var c=[],d=[],e={animation:!1,className:"success",additionalClasses:null,dismissOnTimeout:!0,timeout:4e3,dismissButton:!1,dismissButtonHtml:"×",dismissOnClick:!0,compileContent:!1,combineDuplications:!1,horizontalPosition:"right",verticalPosition:"top",maxNumber:0};this.configure=function(a){b.extend(e,a)},this.$get=[function(){var b=function(a,b){return b="object"==typeof b?b:{content:b},b.className=a,this.create(b)};return{settings:e,messages:c,dismiss:function(a){if(a){for(var b=c.length-1;b>=0;b--)if(c[b].id===a)return c.splice(b,1),void d.splice(d.indexOf(a),1)}else{for(;c.length>0;)c.pop();d=[]}},create:function(b){if(b="object"==typeof b?b:{content:b},e.combineDuplications)for(var f=d.length-1;f>=0;f--){var g=c[f],h=b.className||"success";if(g.content===b.content&&g.className===h)return void c[f].count++}e.maxNumber>0&&d.length>=e.maxNumber&&this.dismiss(d[0]);var i=new a(b);return"bottom"===e.verticalPosition?c.unshift(i):c.push(i),d.push(i.id),i.id},success:function(a){return b.call(this,"success",a)},info:function(a){return b.call(this,"info",a)},warning:function(a){return b.call(this,"warning",a)},danger:function(a){return b.call(this,"danger",a)}}}]}])}(window,window.angular),function(a,b){"use strict";b.module("ngToast.directives",["ngToast.provider"]).run(["$templateCache",function(a){a.put("ngToast/toast.html",'<div class="ng-toast ng-toast--{{hPos}} ng-toast--{{vPos}} {{animation ? \'ng-toast--animate-\' + animation : \'\'}}"><ul class="ng-toast__list"><toast-message ng-repeat="message in messages" message="message" count="message.count"><span ng-bind-html="message.content"></span></toast-message></ul></div>'),a.put("ngToast/toastMessage.html",'<li class="ng-toast__message {{message.additionalClasses}}"ng-mouseenter="onMouseEnter()"ng-mouseleave="onMouseLeave()"><div class="alert alert-{{message.className}}" ng-class="{\'alert-dismissible\': message.dismissButton}"><button type="button" class="close" ng-if="message.dismissButton" ng-bind-html="message.dismissButtonHtml" ng-click="!message.dismissOnClick && dismiss()"></button><span ng-if="count" class="ng-toast__message__count">{{count + 1}}</span><span ng-if="!message.compileContent" ng-transclude></span></div></li>')}]).directive("toast",["ngToast","$templateCache","$log",function(a,b,c){return{replace:!0,restrict:"EA",templateUrl:"ngToast/toast.html",compile:function(d,e){if(e.template){var f=b.get(e.template);f?d.replaceWith(f):c.warn("ngToast: Provided template could not be loaded. Please be sure that it is populated before the <toast> element is represented.")}return function(b){b.hPos=a.settings.horizontalPosition,b.vPos=a.settings.verticalPosition,b.animation=a.settings.animation,b.messages=a.messages}}}}]).directive("toastMessage",["$timeout","$compile","ngToast",function(a,b,c){return{replace:!0,transclude:!0,restrict:"EA",scope:{message:"=",count:"="},controller:["$scope","ngToast",function(a,b){a.dismiss=function(){b.dismiss(a.message.id)}}],templateUrl:"ngToast/toastMessage.html",link:function(d,e,f,g,h){e.attr("data-message-id",d.message.id);var i,j=d.message.compileContent;if(d.cancelTimeout=function(){a.cancel(i)},d.startTimeout=function(){d.message.dismissOnTimeout&&(i=a(function(){c.dismiss(d.message.id)},d.message.timeout))},d.onMouseEnter=function(){d.cancelTimeout()},d.onMouseLeave=function(){d.startTimeout()},j){var k;h(d,function(a){k=a,e.children().append(k)}),a(function(){b(k.contents())("boolean"==typeof j?d.$parent:j,function(a){k.replaceWith(a)})},0)}d.startTimeout(),d.message.dismissOnClick&&e.bind("click",function(){c.dismiss(d.message.id),d.$apply()})}}}])}(window,window.angular),function(a,b){"use strict";b.module("ngToast",["ngSanitize","ngToast.directives","ngToast.provider"])}(window,window.angular);
|
||||
2
src/main/resources/public/js/rzslider.min.js
vendored
Normal file
2
src/main/resources/public/js/rzslider.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1,33 +1,22 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#">Configuration</a></li>
|
||||
<li role="presentation" class="active"><a href="#">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#/system">Bridge Control</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<div ng-controller="ErrorsController">
|
||||
<div ng-if="bridge.error"
|
||||
class="alert alert-warning alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert"
|
||||
aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
|
||||
<h2 ng-show='bridge.error != ""'>ERROR</h2>
|
||||
|
||||
<div ng-show='bridge.error != ""'>{{bridge.error}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Current devices</h2>
|
||||
<h2 class="panel-title">Current devices ({{bridge.devices.length}}) </h2>
|
||||
</div>
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('id')">ID</a>
|
||||
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span></th>
|
||||
@@ -37,91 +26,83 @@
|
||||
<th>
|
||||
<a href="" ng-click="order('deviceType')">Type</a>
|
||||
<span class="sortorder" ng-show="predicate === 'deviceType'" ng-class="{reverse:reverse}"></span></th>
|
||||
<th>
|
||||
<a href="" ng-click="order('targetDevice')">Target</a>
|
||||
<span class="sortorder" ng-show="predicate === 'targetDevice'" ng-class="{reverse:reverse}"></span></th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="device in bridge.devices | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.id}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.deviceType}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>
|
||||
<p>
|
||||
<button class="btn btn-info" type="submit"
|
||||
ng-click="testUrl(device, 'on')">Test ON</button>
|
||||
<button class="btn btn-info" type="submit"
|
||||
ng-click="testUrl(device, 'off')">Test OFF</button>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="editDevice(device)">Edit</button>
|
||||
ng-click="editDevice(device)">Edit/Copy</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDevice(device)">Delete</button>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel panel-default backup">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">Bridge settings <a ng-click="toggle()"><span class={{imgUrl}} aria-hidden="true"></a></h1>
|
||||
<h1 class="panel-title">Bridge Device DB Backup <a ng-click="toggleBk()"><span class={{imgBkUrl}} aria-hidden="true"></a></h1>
|
||||
</div>
|
||||
<div ng-if="visible" class="animate-if" class="panel-body">
|
||||
|
||||
<div ng-if="visibleBk" class="animate-if" class="panel-body">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-3 control-label" for="bridge-base">Bridge
|
||||
server</label>
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="backup-name">Backup File Name</label>
|
||||
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<input id="bridge-base" class="form-control" type="text"
|
||||
ng-model="bridge.base" placeholder="URL to bridge">
|
||||
<input id="backup-name" class="form-control" type="text"
|
||||
ng-model="optionalbackupname" placeholder="Optional">
|
||||
</div>
|
||||
<button type="submit" class="col-xs-2 col-sm-1 btn btn-primary"
|
||||
ng-click="setBridgeUrl(bridge.base)">Load</button>
|
||||
<button type="submit" class="col-xs-2 col-sm-1 btn btn-primary"
|
||||
ng-click="testUrl(bridge.base)">Go</button>
|
||||
<button type="submit" class="btn btn-primary"
|
||||
ng-click="backupDeviceDb(optionalbackupname)">Backup Device DB</button>
|
||||
</div>
|
||||
</form>
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Setting</th>
|
||||
<th>Value</th>
|
||||
<th>Filename</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>upnp.config.address</td>
|
||||
<td>{{BridgeSettings.upnpconfigaddress}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>server.port</td>
|
||||
<td>{{BridgeSettings.serverport}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>upnp.devices.db</td>
|
||||
<td>{{BridgeSettings.upnpdevicedb}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>upnp.response.port</td>
|
||||
<td>{{BridgeSettings.upnpresponseport}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>vera.address</td>
|
||||
<td>{{BridgeSettings.veraaddress}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>harmony.address</td>
|
||||
<td>{{BridgeSettings.harmonyaddress}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>upnp.strict</td>
|
||||
<td>{{BridgeSettings.upnpstrict}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>trace.upnp</td>
|
||||
<td>{{BridgeSettings.traceupnp}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>dev.mode</td>
|
||||
<td>{{BridgeSettings.devmode}}</td>
|
||||
<tr ng-repeat="backup in bridge.backups">
|
||||
<td>{{backup}}</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="restoreBackup(backup)">Restore</button>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="deleteBackup(backup)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/ng-template" id="valueDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Select value</h2>
|
||||
<p>
|
||||
<input type="radio" ng-model="valueType" value="percentage" ng-change="changeScale()"> Percentage
|
||||
<input type="radio" ng-model="valueType" value="raw" ng-change="changeScale()"> Raw
|
||||
</p>
|
||||
<p>
|
||||
<rzslider rz-slider-model="slider.value" rz-slider-options="slider.options"></rzslider>
|
||||
</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-primary" ng-click="setValue()">Set</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#">Configuration</a></li>
|
||||
<li role="presentation"><a href="#">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#/system">Bridge Control</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
<li role="presentation" class="active"><a href="#/editdevice">Edit Device</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Edit a device</h2>
|
||||
<h2 class="panel-title">Edit/Copy a device</h2>
|
||||
</div>
|
||||
<p class="text-muted">This screen allows the modification of many fields the bridge uses. Please use care when
|
||||
updating these fields as you may break the settings used by the bridge to call a specific end point device.</p>
|
||||
<p>When copying, update the name and select the "Add Bridge Device" Button.</p>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<form class="form-horizontal" ng-submit="addDevice()">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
|
||||
</label>
|
||||
@@ -23,8 +28,19 @@
|
||||
<input type="text" class="form-control" id="device-name"
|
||||
ng-model="device.name" placeholder="Device Name">
|
||||
</div>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
|
||||
Update Device</button>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-success" ng-click="addDevice()">
|
||||
Update Bridge Device</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="device-target">Target
|
||||
</label>
|
||||
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<input type="text" class="form-control" id="device-target"
|
||||
ng-model="device.targetDevice" placeholder="default">
|
||||
</div>
|
||||
<button class="btn btn-primary" ng-click="copyDevice()">
|
||||
Add Bridge Device</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
@@ -38,8 +54,12 @@
|
||||
<option value="veraScene">Vera Scene</option>
|
||||
<option value="harmonyActivity">Harmony Activity</option>
|
||||
<option value="harmonyButton">Harmony Button</option>
|
||||
<option value="nestHomeAway">Nest Home Status</option>
|
||||
<option value="nestThermoSet">Nest Thermostat</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-danger" ng-click="clearDevice()">
|
||||
Clear Device</button>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="device.mapType" class="form-group">
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#">Configuration</a></li>
|
||||
<li role="presentation"><a href="#">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#/system">Bridge Control</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
|
||||
<li role="presentation" class="active"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -73,9 +75,11 @@
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add a new device</h2>
|
||||
</div>
|
||||
<p class="text-muted">This area allows you to create any http or udp call to an endpoint. You can use the default GET or select
|
||||
the http verb type below and configure a payload for either on or off methods. Currently, https is not supported.</p>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<form class="form-horizontal" ng-submit="addDevice()">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
|
||||
@@ -85,8 +89,8 @@
|
||||
<input type="text" class="form-control" id="device-name"
|
||||
ng-model="device.name" placeholder="Device Name">
|
||||
</div>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
|
||||
Add Device</button>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
|
||||
Add Bridge Device</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -98,6 +102,8 @@
|
||||
<textarea rows="3" class="form-control" id="device-on-url"
|
||||
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
|
||||
</div>
|
||||
<button class="btn btn-danger" ng-click="clearDevice()">
|
||||
Clear Device</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#">Configuration</a></li>
|
||||
<li role="presentation"><a href="#">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#/system">Bridge Control</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
|
||||
<li role="presentation" class="active"><a href="#/harmonyactivities">Harmony Activities</a></li>
|
||||
<li role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -13,12 +15,14 @@
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p class="text-muted">You can select a Harmony Activity and generate
|
||||
the add activity box selections automatically.</p>
|
||||
<p class="text-muted">For any Harmony Activity, use the action buttons to generate the device addition information below automatically.
|
||||
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
|
||||
The 'Already Configured Activities' list below will show what is already setup for your Harmony Hubs.</p>
|
||||
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('label')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
@@ -26,13 +30,19 @@
|
||||
<th>
|
||||
<a href="" ng-click="order('id')">Id</a>
|
||||
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('hub')">Hub</a>
|
||||
<span class="sortorder" ng-show="predicate === 'hub'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | availableId | orderBy:predicate:reverse">
|
||||
<td>{{harmonyactivity.label}}</td>
|
||||
<td>{{harmonyactivity.id}}</td>
|
||||
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | availableHarmonyActivityId | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{harmonyactivity.activity.label}}</td>
|
||||
<td>{{harmonyactivity.activity.id}}</td>
|
||||
<td>{{harmonyactivity.hub}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildActivityUrls(harmonyactivity)">Generate
|
||||
@@ -43,13 +53,14 @@
|
||||
</li>
|
||||
</ul>
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Already Configured Activities <a ng-click="toggleActivities()"><span class={{imgActivitiesUrl}} aria-hidden="true"></a></h2>
|
||||
<h2 class="panel-title">Already Configured Activities <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}} aria-hidden="true"></span></a></h2>
|
||||
</div>
|
||||
<ul ng-if="activitiesVisible" class="list-group">
|
||||
<ul ng-if="buttonsVisible" class="list-group">
|
||||
<li class="list-group-item">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('label')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
@@ -57,15 +68,21 @@
|
||||
<th>
|
||||
<a href="" ng-click="order('id')">Id</a>
|
||||
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('hub')">Hub</a>
|
||||
<span class="sortorder" ng-show="predicate === 'hub'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | unavailableId | orderBy:predicate:reverse">
|
||||
<td>{{harmonyactivity.label}}</td>
|
||||
<td>{{harmonyactivity.id}}</td>
|
||||
<tr ng-repeat="harmonyactivity in bridge.harmonyactivities | unavailableHarmonyActivityId | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{harmonyactivity.activity.label}}</td>
|
||||
<td>{{harmonyactivity.activity.id}}</td>
|
||||
<td>{{harmonyactivity.hub}}</td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDeviceByMapId(harmonyactivity.id)">Delete</button></td>
|
||||
ng-click="deleteDeviceByMapId(harmonyactivity.activity.id, 'harmonyActivity')">Delete</button></td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
@@ -73,11 +90,11 @@
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add a Harmony Activity</h2>
|
||||
<h2 class="panel-title">Add a Bridge Device for a Harmony Activity</h2>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<form class="form-horizontal" ng-submit="addDevice()">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
|
||||
</label>
|
||||
@@ -86,8 +103,8 @@
|
||||
<input type="text" class="form-control" id="device-name"
|
||||
ng-model="device.name" placeholder="Device Name">
|
||||
</div>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
|
||||
Add Activity</button>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
|
||||
Add Bridge Device</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
@@ -98,6 +115,8 @@
|
||||
<textarea rows="3" class="form-control" id="device-on-url"
|
||||
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
|
||||
</div>
|
||||
<button class="btn btn-danger" ng-click="clearDevice()">
|
||||
Clear Device</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#">Configuration</a></li>
|
||||
<li role="presentation"><a href="#">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#/system">Bridge Control</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
|
||||
<li role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
|
||||
<li role="presentation" class="active"><a href="#/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -13,12 +15,15 @@
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p class="text-muted">You can select a Harmony Device and Button and generate
|
||||
the add button box selections automatically.</p>
|
||||
<p class="text-muted">For any Harmony Device and Buttons, use the build button to generate the configuration for this bridge device. You can add button presses by
|
||||
selecting yoru devic and buttons and then selecting the Build Button. This will allow multiple button presses to be built for a given device.
|
||||
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
|
||||
The 'Already Configured Harmony Buttons' list below will show what is already setup for your Harmony Hubs.</p>
|
||||
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('label')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
@@ -26,6 +31,10 @@
|
||||
<th>
|
||||
<a href="" ng-click="order('id')">Id</a>
|
||||
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('hub')">Hub</a>
|
||||
<span class="sortorder" ng-show="predicate === 'hub'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>On Button</th>
|
||||
<th>Off Button</th>
|
||||
@@ -33,26 +42,27 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="harmonydevice in bridge.harmonydevices | orderBy:predicate:reverse">
|
||||
<td>{{harmonydevice.label}}</td>
|
||||
<td>{{harmonydevice.id}}</td>
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{harmonydevice.device.label}}</td>
|
||||
<td>{{harmonydevice.device.id}}</td>
|
||||
<td>{{harmonydevice.hub}}</td>
|
||||
<td>
|
||||
<select name="device-ctrlon" id="device-ctrlon" ng-model="devicectrlon">
|
||||
<optgroup ng-repeat="ctrlon in harmonydevice.controlGroup" label="{{ctrlon.name}}">
|
||||
<option ng-repeat="funcon in ctrlon.function">{{funcon.name}}</option>
|
||||
<optgroup ng-repeat="ctrlon in harmonydevice.device.controlGroup" label="{{ctrlon.name}}">
|
||||
<option ng-repeat="funcon in ctrlon.function" value="{{funcon.action}}">{{funcon.label}}</option>
|
||||
</optgroup >
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select name="device-ctrloff" id="device-ctrloff" ng-model="devicectrloff">
|
||||
<optgroup ng-repeat="ctrloff in harmonydevice.controlGroup" label="{{ctrloff.name}}">
|
||||
<option ng-repeat="funcoff in ctrloff.function">{{funcoff.name}}</option>
|
||||
<optgroup ng-repeat="ctrloff in harmonydevice.device.controlGroup" label="{{ctrloff.name}}">
|
||||
<option ng-repeat="funcoff in ctrloff.function" value="{{funcoff.action}}">{{funcoff.label}}</option>
|
||||
</optgroup >
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildButtonUrls(harmonydevice, devicectrlon, devicectrloff)">Generate
|
||||
Button URLs</button>
|
||||
ng-click="buildButtonUrls(harmonydevice, devicectrlon, devicectrloff)">Build A Button</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -66,6 +76,7 @@
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('name')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
@@ -73,6 +84,10 @@
|
||||
<th>
|
||||
<a href="" ng-click="order('id')">Device Id</a>
|
||||
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('targetDevice')">Hub</a>
|
||||
<span class="sortorder" ng-show="predicate === 'targetDevice'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('mapId')">Harmony Device-Button On-Button Off</a>
|
||||
@@ -82,12 +97,14 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="device in bridge.devices | configuredButtons | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.id}}</td>
|
||||
<td>{{device.targetDevice}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDeviceByMapId(device.mapId)">Delete</button>
|
||||
ng-click="deleteDeviceByMapId(device.mapId, device.mapType)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -96,11 +113,11 @@
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add a Harmony Button</h2>
|
||||
<h2 class="panel-title">Add a Bridge Device for Harmony Buttons</h2>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<form class="form-horizontal" ng-submit="addDevice()">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
|
||||
</label>
|
||||
@@ -109,8 +126,8 @@
|
||||
<input type="text" class="form-control" id="device-name"
|
||||
ng-model="device.name" placeholder="Device Name">
|
||||
</div>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
|
||||
Add Button</button>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
|
||||
Add Bridge Device</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
@@ -121,6 +138,8 @@
|
||||
<textarea rows="3" class="form-control" id="device-on-url"
|
||||
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
|
||||
</div>
|
||||
<button class="btn btn-danger" ng-click="clearDevice()">
|
||||
Clear Device</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
157
src/main/resources/public/views/nestactions.html
Normal file
157
src/main/resources/public/views/nestactions.html
Normal file
@@ -0,0 +1,157 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#/system">Bridge Control</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#/nest">Nest</a></li>
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Nest Items List</h2>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p class="text-muted">For any Nest Item, use the action buttons to generate the device addition information below automatically.
|
||||
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
|
||||
The 'Already Configured Nest Items' list below will show what is already setup for your Nest.</p>
|
||||
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('name')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('type')">Type</a>
|
||||
<span class="sortorder" ng-show="predicate === 'type'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('location')">Location</a>
|
||||
<span class="sortorder" ng-show="predicate === 'location'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="nestitem in bridge.nestitems | availableNestItemId | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{nestitem.name}}</td>
|
||||
<td>{{nestitem.type}}</td>
|
||||
<td>{{nestitem.location}}</td>
|
||||
<td>
|
||||
<ul class="list-group">
|
||||
<li ng-if="nestitem.type ==='Home' " class="list-group-item">
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestHomeUrls(nestitem)">Home/Away</button>
|
||||
</li>
|
||||
<li ng-if="nestitem.type ==='Thermostat' " class="list-group-item">
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestTempUrls(nestitem)">Temp</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestHeatUrls(nestitem)">Heat</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestCoolUrls(nestitem)">Cool</button>
|
||||
</p>
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestRangeUrls(nestitem)">Range</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestOffUrls(nestitem)">Off</button>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildNestFanUrls(nestitem)">Fan</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Already Configured Nest Items <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}} aria-hidden="true"></span></a></h2>
|
||||
</div>
|
||||
<ul ng-if="buttonsVisible" class="list-group">
|
||||
<li class="list-group-item">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('name')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('id')">Id</a>
|
||||
<span class="sortorder" ng-show="predicate === 'id'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('mapId')">mapId</a>
|
||||
<span class="sortorder" ng-show="predicate === 'mapId'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="device in bridge.devices | unavailableNestItemId | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{device.name}}</td>
|
||||
<td>{{device.id}}</td>
|
||||
<td>{{device.mapId}}</td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDeviceByMapId(device.mapId, 'nest')">Delete</button></td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add a Bridge Device for a Nest Item</h2>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
|
||||
</label>
|
||||
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<input type="text" class="form-control" id="device-name"
|
||||
ng-model="device.name" placeholder="Device Name">
|
||||
</div>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
|
||||
Add Bridge Device</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="device-on-url">On
|
||||
URL </label>
|
||||
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<textarea rows="3" class="form-control" id="device-on-url"
|
||||
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
|
||||
</div>
|
||||
<button class="btn btn-danger" ng-click="clearDevice()">
|
||||
Clear Device</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<label class="col-xs-12 col-sm-2 control-label"
|
||||
for="device-off-url">Off URL </label>
|
||||
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<textarea rows="3" class="form-control" id="device-off-url"
|
||||
ng-model="device.offUrl" placeholder="URL to turn device off"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
210
src/main/resources/public/views/system.html
Normal file
210
src/main/resources/public/views/system.html
Normal file
@@ -0,0 +1,210 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#">Bridge Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#/system">Bridge Control</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/veradevices">Vera Devices</a></li>
|
||||
<li ng-if="bridge.showVera" role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default bridgeServer">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">Bridge Settings</h1>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="bridge-base">Bridge
|
||||
server</label>
|
||||
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<input id="bridge-base" class="form-control" type="text"
|
||||
ng-model="bridge.base" placeholder="URL to bridge">
|
||||
</div>
|
||||
<button type="submit" class="col-xs-2 col-sm-1 btn btn-primary"
|
||||
ng-click="setBridgeUrl(bridge.base)">Load</button>
|
||||
<button type="submit" class="col-xs-2 col-sm-1 btn btn-primary"
|
||||
ng-click="goBridgeUrl(bridge.base)">Go</button>
|
||||
</div>
|
||||
</form>
|
||||
<form name="form">
|
||||
<p>
|
||||
<button ng-disabled="form.$pristine || 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>
|
||||
<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>
|
||||
</p>
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Setting</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>Configuration Path and File</td>
|
||||
<td><input id="bridge-settings-configfile" class="form-control" type="text"
|
||||
ng-model="bridge.settings.configfile" placeholder="data/ha-bridge.config"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Device DB Path and File</td>
|
||||
<td><input id="bridge-settings-upnpdevicedb" class="form-control" type="text"
|
||||
ng-model="bridge.settings.upnpdevicedb" placeholder="data/device.db"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UPNP IP Address</td>
|
||||
<td><input id="bridge-settings-upnpconfigaddress" class="form-control" type="text"
|
||||
ng-model="bridge.settings.upnpconfigaddress" placeholder="192.168.1.1"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Web Server Port</td>
|
||||
<td><input id="bridge-settings-serverport" class="form-control" type="number"
|
||||
ng-model="bridge.settings.serverport" min="1" max="65535"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UPNP Response Port</td>
|
||||
<td><input id="bridge-settings-upnpresponseport" class="form-control" type="number"
|
||||
ng-model="bridge.settings.upnpresponseport" min="1" max="65535"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Vera Names and IP Addresses</td>
|
||||
<td><table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>IP</th>
|
||||
<th>Manage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="vera in bridge.settings.veraaddress.devices">
|
||||
<td>{{vera.name}}</td>
|
||||
<td>{{vera.ip}}</td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="removeVeratoSettings(vera.name, vera.ip)">Del</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input id="bridge-settings-next-vera-name" class="form-control" type="text"
|
||||
ng-model="newveraname" placeholder="A Vera"></td>
|
||||
<td><input id="bridge-settings-next-vera-ip" class="form-control" type="text"
|
||||
ng-model="newveraip" placeholder="192.168.1.2"></td>
|
||||
<td><button class="btn btn-success" type="submit"
|
||||
ng-click="addVeratoSettings(newveraname, newveraip)">Add</button></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Harmony Names and IP Addresses</td>
|
||||
<td><table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>IP</th>
|
||||
<th>Manage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="harmony in bridge.settings.harmonyaddress.devices">
|
||||
<td>{{harmony.name}}</td>
|
||||
<td>{{harmony.ip}}</td>
|
||||
<td><button class="btn btn-danger" type="submit"
|
||||
ng-click="removeHarmonytoSettings(harmony.name, harmony.ip)">Del</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input id="bridge-settings-next-harmony-name" class="form-control" type="text"
|
||||
ng-model="newharmonyname" placeholder="A Harmony"></td>
|
||||
<td><input id="bridge-settings-next-harmony-ip" class="form-control" type="text"
|
||||
ng-model="newharmonyip" placeholder="192.168.1.3"></td>
|
||||
<td><button class="btn btn-success" type="submit"
|
||||
ng-click="addHarmonytoSettings(newharmonyname, newharmonyip)">Add</button></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Harmony Username</td>
|
||||
<td><input id="bridge-settings-harmonyuser" class="form-control" type="text"
|
||||
ng-model="bridge.settings.harmonyuser" placeholder="someone@gmail.com"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Harmony Password</td>
|
||||
<td><input id="bridge-settings-harmonypwd" class="form-control" type="password"
|
||||
ng-model="bridge.settings.harmonypwd" placeholder="thepassword"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Button Press Sleep Interval (ms)</td>
|
||||
<td><input id="bridge-settings-buttonsleep" class="form-control" type="number" name="input"
|
||||
ng-model="bridge.settings.buttonsleep" min="100" max="9999"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nest Username</td>
|
||||
<td><input id="bridge-settings-nestuser" class="form-control" type="text"
|
||||
ng-model="bridge.settings.nestuser" placeholder="someone@gmail.com"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nest Password</td>
|
||||
<td><input id="bridge-settings-nestpwd" class="form-control" type="password"
|
||||
ng-model="bridge.settings.nestpwd" placeholder="thepassword"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UPNP Strict Handling</td>
|
||||
<td><input type="checkbox" ng-model="bridge.settings.upnpstrict"
|
||||
ng-true-value=true ng-false-value=false> {{bridge.settings.upnpstrict}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Trace UPNP Calls</td>
|
||||
<td><input type="checkbox" ng-model="bridge.settings.traceupnp"
|
||||
ng-true-value=true ng-false-value=false> {{bridge.settings.traceupnp}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default backup">
|
||||
<div class="panel-heading">
|
||||
<h1 class="panel-title">Bridge Settings Backup <a ng-click="toggleBk()"><span class={{imgBkUrl}} aria-hidden="true"></a></h1>
|
||||
</div>
|
||||
<div ng-if="visibleBk" class="animate-if" class="panel-body">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="backup-name">Backup Settings File Name</label>
|
||||
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<input id="backup-name" class="form-control" type="text"
|
||||
ng-model="optionalbackupname" placeholder="Optional">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"
|
||||
ng-click="backupSettings(optionalbackupname)">Backup Settings</button>
|
||||
</div>
|
||||
</form>
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Filename</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="backup in bridge.configs">
|
||||
<td>{{backup}}</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="restoreSettings(backup)">Restore</button>
|
||||
<button class="btn btn-warning" type="submit"
|
||||
ng-click="deleteSettingsBackup(backup)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#">Configuration</a></li>
|
||||
<li role="presentation"><a href="#">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#/system">Bridge Control</a></li>
|
||||
<li role="presentation" class="active"><a href="#/veradevices">Vera Devices</a></li>
|
||||
<li role="presentation"><a href="#/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Vera Device List</h2>
|
||||
<h2 class="panel-title">Vera Device List ({{bridge.veradevices.length}})</h2>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p class="text-muted">You can select a Vera device and generate
|
||||
the add device box selections automatically.</p><p>Also, use this select menu for which type of dim
|
||||
<p class="text-muted">For any Vera Device, use the action buttons to generate the device addition information below automatically.
|
||||
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
|
||||
The 'Already Configured Vera Devices' list below will show what is already setup for your Vera.</p>
|
||||
<p>Also, use this select menu for which type of dim
|
||||
control you would like to be generated:
|
||||
<select name="device-dim-control" id="device-dim-control" ng-model="device_dim_control">
|
||||
<option value="">none</option>
|
||||
@@ -23,9 +27,13 @@
|
||||
<option value="${intensity.math(X*1)}">Custom Math</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>Use the check boxes by the names to use the bulk addition feature. Select your items and dim control type if wanted, then click bulk add below.
|
||||
Your items will be added with on and off or dim and off if selected with the name of the device from the Vera.
|
||||
</p>
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('name')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
@@ -41,15 +49,21 @@
|
||||
<th>
|
||||
<a href="" ng-click="order('room')">Room</a>
|
||||
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('vera')">Vera</a>
|
||||
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="veradevice in bridge.veradevices | availableId | orderBy:predicate:reverse">
|
||||
<td>{{veradevice.name}}</td>
|
||||
<tr ng-repeat="veradevice in bridge.veradevices | availableVeraDeviceId | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td><input type="checkbox" name="bulk.devices[]" value="{{veradevice.id}}" ng-checked="bulk.devices.indexOf(veradevice.id) > -1" ng-click="toggleSelection(veradevice.id)"> {{veradevice.name}}</td>
|
||||
<td>{{veradevice.id}}</td>
|
||||
<td>{{veradevice.category}}</td>
|
||||
<td>{{veradevice.room}}</td>
|
||||
<td>{{veradevice.veraname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildDeviceUrls(veradevice, device_dim_control)">Generate
|
||||
@@ -57,16 +71,21 @@
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="bulkAddDevices(device_dim_control)">Bulk Add ({{bulk.devices.length}})</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Already Configured Vera Devices <a ng-click="toggleDevices()"><span class={{imgDevicesUrl}} aria-hidden="true"></a></h2>
|
||||
<h2 class="panel-title">Already Configured Vera Devices <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}} aria-hidden="true"></span></a></a></h2>
|
||||
</div>
|
||||
<ul ng-if="devicesVisible" class="list-group">
|
||||
<ul ng-if="buttonsVisible" class="list-group">
|
||||
<li class="list-group-item">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('name')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
@@ -82,18 +101,24 @@
|
||||
<th>
|
||||
<a href="" ng-click="order('room')">Room</a>
|
||||
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('vera')">Vera</a>
|
||||
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="veradevice in bridge.veradevices | unavailableId | orderBy:predicate:reverse">
|
||||
<tr ng-repeat="veradevice in bridge.veradevices | unavailableVeraDeviceId | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{veradevice.name}}</td>
|
||||
<td>{{veradevice.id}}</td>
|
||||
<td>{{veradevice.category}}</td>
|
||||
<td>{{veradevice.room}}</td>
|
||||
<td>{{veradevice.veraname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDeviceByMapId(harmonyactivity.id)">Delete</button>
|
||||
ng-click="deleteDeviceByMapId(veradevice.id, 'veraDevice')">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -102,11 +127,11 @@
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add a Vera device</h2>
|
||||
<h2 class="panel-title">Add Bridge Device for a Vera Device</h2>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<form class="form-horizontal" ng-submit="addDevice()">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
|
||||
</label>
|
||||
@@ -115,8 +140,8 @@
|
||||
<input type="text" class="form-control" id="device-name"
|
||||
ng-model="device.name" placeholder="Device Name">
|
||||
</div>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
|
||||
Add Device</button>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
|
||||
Add Bridge Device</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
@@ -127,6 +152,8 @@
|
||||
<textarea rows="3" class="form-control" id="device-on-url"
|
||||
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
|
||||
</div>
|
||||
<button class="btn btn-danger" ng-click="clearDevice()">
|
||||
Clear Device</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation"><a href="#">Configuration</a></li>
|
||||
<li role="presentation"><a href="#">Bridge Devices</a></li>
|
||||
<li role="presentation"><a href="#/system">Bridge Control</a></li>
|
||||
<li role="presentation"><a href="#/veradevices">Vera Devices</a></li>
|
||||
<li role="presentation" class="active"><a href="#/verascenes">Vera Scenes</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonyactivities">Harmony Activities</a></li>
|
||||
<li ng-if="bridge.showHarmony" role="presentation"><a href="#/harmonydevices">Harmony Devices</a></li>
|
||||
<li ng-if="bridge.showNest" role="presentation"><a href="#/nest">Nest</a></li>
|
||||
<li role="presentation"><a href="#/editor">Manual Add</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -13,12 +15,14 @@
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p class="text-muted">You can select a Vera scene and generate
|
||||
the add scene box selections automatically.</p>
|
||||
<p class="text-muted">For any Vera Scene, use the action buttons to generate the device addition information below automatically.
|
||||
Then you can modify the name to anything you want that will be the keyword for Alexa. Click the 'Add Bridge Device' to finish that selection setup.
|
||||
The 'Already Configured Vera Scenes' list below will show what is already setup for your Vera.</p>
|
||||
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('name')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
@@ -30,14 +34,20 @@
|
||||
<th>
|
||||
<a href="" ng-click="order('room')">Room</a>
|
||||
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('vera')">Vera</a>
|
||||
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="verascene in bridge.verascenes | availableId | orderBy:predicate:reverse">
|
||||
<tr ng-repeat="verascene in bridge.verascenes | availableVeraSceneId | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{verascene.name}}</td>
|
||||
<td>{{verascene.id}}</td>
|
||||
<td>{{verascene.room}}</td>
|
||||
<td>{{verascene.veraname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-success" type="submit"
|
||||
ng-click="buildSceneUrls(verascene)">Generate
|
||||
@@ -48,13 +58,14 @@
|
||||
</li>
|
||||
</ul>
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Already Configured Vera Scenes <a ng-click="toggleScenes()"><span class={{imgScenesUrl}} aria-hidden="true"></a></h2>
|
||||
<h2 class="panel-title">Already Configured Vera Scenes <a ng-click="toggleButtons()"><span class={{imgButtonsUrl}} aria-hidden="true"></span></a></h2>
|
||||
</div>
|
||||
<ul ng-if="scenesVisible" class="list-group">
|
||||
<ul ng-if="buttonsVisible" class="list-group">
|
||||
<li class="list-group-item">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Row</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('name')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
@@ -66,17 +77,23 @@
|
||||
<th>
|
||||
<a href="" ng-click="order('room')">Room</a>
|
||||
<span class="sortorder" ng-show="predicate === 'room'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('vera')">Vera</a>
|
||||
<span class="sortorder" ng-show="predicate === 'vera'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="verascene in bridge.verascenes | unavailableId | orderBy:predicate:reverse">
|
||||
<tr ng-repeat="verascene in bridge.verascenes | unavailableVeraSceneId | orderBy:predicate:reverse">
|
||||
<td>{{$index+1}}</td>
|
||||
<td>{{verascene.name}}</td>
|
||||
<td>{{verascene.id}}</td>
|
||||
<td>{{verascene.room}}</td>
|
||||
<td>{{verascene.veraname}}</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
ng-click="deleteDeviceByMapId(harmonyactivity.id)">Delete</button>
|
||||
ng-click="deleteDeviceByMapId(verascene.id, 'veraScene')">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -85,11 +102,11 @@
|
||||
</div>
|
||||
<div class="panel panel-default bridgeServer" ng-if="!bridge.error">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">Add a Vera scene</h2>
|
||||
<h2 class="panel-title">Add a Bridge Device for a Vera scene</h2>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<form class="form-horizontal" ng-submit="addDevice()">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-12 col-sm-2 control-label" for="device-name">Name
|
||||
</label>
|
||||
@@ -98,8 +115,8 @@
|
||||
<input type="text" class="form-control" id="device-name"
|
||||
ng-model="device.name" placeholder="Device Name">
|
||||
</div>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary">
|
||||
Add Scene</button>
|
||||
<button type="submit" class="col-xs-4 col-sm-2 btn btn-primary" ng-click="addDevice()">
|
||||
Add Bridge Device</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
@@ -110,6 +127,8 @@
|
||||
<textarea rows="3" class="form-control" id="device-on-url"
|
||||
ng-model="device.onUrl" placeholder="URL to turn device on"></textarea>
|
||||
</div>
|
||||
<button class="btn btn-danger" ng-click="clearDevice()">
|
||||
Clear Device</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
|
||||
Reference in New Issue
Block a user