mirror of
https://github.com/zyphlar/LanternPowerMonitor.git
synced 2024-03-08 14:07:47 +00:00
Password reset functionality, ZWave switch schedule improvement, support zwave controller on pi, support relay switches and security sensors.
This commit is contained in:
parent
6c2b567536
commit
de50645a2c
BIN
case/4B/LPM_Case_Flange_4B.blend
Normal file
BIN
case/4B/LPM_Case_Flange_4B.blend
Normal file
Binary file not shown.
BIN
case/4B/LPM_Case_Flange_4B.stl
Normal file
BIN
case/4B/LPM_Case_Flange_4B.stl
Normal file
Binary file not shown.
|
@ -3,7 +3,7 @@
|
|||
<groupId>com.lanternsoftware.currentmonitor</groupId>
|
||||
<artifactId>lantern-currentmonitor</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.4</version>
|
||||
<name>lantern-currentmonitor</name>
|
||||
|
||||
<properties>
|
||||
|
@ -25,7 +25,7 @@
|
|||
<dependency>
|
||||
<groupId>com.pi4j</groupId>
|
||||
<artifactId>pi4j-gpio-extension</artifactId>
|
||||
<version>1.2</version>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.hypfvieh</groupId>
|
||||
|
|
|
@ -178,14 +178,14 @@ public class MonitorApp {
|
|||
return new byte[]{NetworkMonitor.getNetworkStatus().toMask()};
|
||||
if (HubConfigCharacteristic.NetworkDetails == ch) {
|
||||
NetworkStatus status = NetworkMonitor.getNetworkStatus();
|
||||
DaoEntity meta = DaoSerializer.fromZipBson(pool.executeToByteArray(new HttpGet(host + "update/version")));
|
||||
DaoEntity meta = (host == null)?null:DaoSerializer.fromZipBson(pool.executeToByteArray(new HttpGet(host + "update/version")));
|
||||
status.setPingSuccessful(CollectionUtils.isNotEmpty(meta));
|
||||
return DaoSerializer.toZipBson(status);
|
||||
}
|
||||
if (HubConfigCharacteristic.Log == ch) {
|
||||
String[] log = NullUtils.cleanSplit(ResourceLoader.loadFileAsString(WORKING_DIR + "log/log.txt"), "\n");
|
||||
if (log.length > 10)
|
||||
log = Arrays.copyOfRange(log, log.length-10, log.length);
|
||||
if (log.length > 15)
|
||||
log = Arrays.copyOfRange(log, log.length-15, log.length);
|
||||
return ZipUtils.zip(NullUtils.toByteArray(CollectionUtils.delimit(Arrays.asList(log), "\n")));
|
||||
}
|
||||
return null;
|
||||
|
@ -341,7 +341,7 @@ public class MonitorApp {
|
|||
if (files != null) {
|
||||
for (File file : files) {
|
||||
payload = ResourceLoader.loadFile(file.getAbsolutePath());
|
||||
if (post(payload, file.getName().endsWith("dat") ? "power/batch" : "power/hub"))
|
||||
if (post(payload, "power/hub"))
|
||||
file.delete();
|
||||
else
|
||||
break;
|
||||
|
@ -401,7 +401,7 @@ public class MonitorApp {
|
|||
private static final class UpdateChecker implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if (NullUtils.isNotEmpty(host)) {
|
||||
if (NullUtils.isNotEmpty(host) && config.isAutoUpdate()) {
|
||||
DaoEntity meta = DaoSerializer.fromZipBson(pool.executeToByteArray(new HttpGet(host + "update/version")));
|
||||
String newVersion = DaoSerializer.getString(meta, "version");
|
||||
if (NullUtils.isNotEqual(newVersion, version)) {
|
||||
|
|
|
@ -17,6 +17,7 @@ public class MonitorConfig {
|
|||
private int connectTimeout;
|
||||
private int socketTimeout;
|
||||
private int updateInterval;
|
||||
private boolean autoUpdate;
|
||||
private float autoCalibrationVoltage;
|
||||
private boolean needsCalibration;
|
||||
private String mqttBrokerUrl;
|
||||
|
@ -107,6 +108,14 @@ public class MonitorConfig {
|
|||
updateInterval = _updateInterval;
|
||||
}
|
||||
|
||||
public boolean isAutoUpdate() {
|
||||
return autoUpdate;
|
||||
}
|
||||
|
||||
public void setAutoUpdate(boolean _autoUpdate) {
|
||||
autoUpdate = _autoUpdate;
|
||||
}
|
||||
|
||||
public float getAutoCalibrationVoltage() {
|
||||
return autoCalibrationVoltage;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ public class MonitorConfigSerializer extends AbstractDaoSerializer<MonitorConfig
|
|||
d.put("connect_timeout", _o.getConnectTimeout());
|
||||
d.put("socket_timeout", _o.getSocketTimeout());
|
||||
d.put("update_interval", _o.getUpdateInterval());
|
||||
d.put("auto_update", _o.isAutoUpdate());
|
||||
d.put("auto_calibration_voltage", _o.getAutoCalibrationVoltage());
|
||||
d.put("needs_calibration", _o.isNeedsCalibration());
|
||||
d.put("mqtt_broker_url", _o.getMqttBrokerUrl());
|
||||
|
@ -60,6 +61,7 @@ public class MonitorConfigSerializer extends AbstractDaoSerializer<MonitorConfig
|
|||
o.setConnectTimeout(DaoSerializer.getInteger(_d, "connect_timeout"));
|
||||
o.setSocketTimeout(DaoSerializer.getInteger(_d, "socket_timeout"));
|
||||
o.setUpdateInterval(DaoSerializer.getInteger(_d, "update_interval"));
|
||||
o.setAutoUpdate(DaoSerializer.getBoolean(_d, "auto_update"));
|
||||
o.setAutoCalibrationVoltage(DaoSerializer.getFloat(_d, "auto_calibration_voltage"));
|
||||
o.setNeedsCalibration(DaoSerializer.getBoolean(_d, "needs_calibration"));
|
||||
o.setMqttBrokerUrl(DaoSerializer.getString(_d, "mqtt_broker_url"));
|
||||
|
|
|
@ -32,6 +32,9 @@ public interface CurrentMonitorDao {
|
|||
|
||||
void updateSummaries(BreakerGroup _rootGroup, Set<Date> _daysToSummarize, TimeZone _tz);
|
||||
|
||||
String addPasswordResetKey(String _email);
|
||||
String getEmailForResetKey(String _key);
|
||||
boolean resetPassword(String _key, String _password);
|
||||
String authenticateAccount(String _username, String _password);
|
||||
String getAuthCodeForEmail(String _email, TimeZone _tz);
|
||||
Account authCodeToAccount(String _authCode);
|
||||
|
|
|
@ -296,6 +296,32 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
|
|||
return _account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String addPasswordResetKey(String _email) {
|
||||
String key = aes.encryptToBase64(_email);
|
||||
proxy.saveEntity("password_reset", new DaoEntity("_id", key));
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEmailForResetKey(String _key) {
|
||||
DaoEntity entity = proxy.queryForEntity("password_reset", new DaoQuery("_id", _key));
|
||||
if (entity == null)
|
||||
return null;
|
||||
return aes.decryptFromBase64ToString(_key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resetPassword(String _key, String _password) {
|
||||
DaoEntity entity = proxy.queryForEntity("password_reset", new DaoQuery("_id", _key));
|
||||
if (entity == null)
|
||||
return false;
|
||||
Account acct = getAccountByUsername(aes.decryptFromBase64ToString(_key));
|
||||
acct.setPassword(_password);
|
||||
putAccount(acct);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoProxy getProxy() {
|
||||
return proxy;
|
||||
|
|
|
@ -44,6 +44,11 @@
|
|||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.2.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sendgrid</groupId>
|
||||
<artifactId>sendgrid-java</artifactId>
|
||||
<version>4.7.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
|
@ -76,11 +81,25 @@
|
|||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
<configuration>
|
||||
<webResources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
<directory>src/main/webapp</directory>
|
||||
<includes>
|
||||
<include>versioninfo</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</webResources>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Build-Time>${maven.build.timestamp}</Build-Time>
|
||||
<Build-OS>${os.name}</Build-OS>
|
||||
<Implementation-Version>${project.version}</Implementation-Version>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package com.lanternsoftware.currentmonitor.servlet;
|
||||
|
||||
import com.lanternsoftware.currentmonitor.context.Globals;
|
||||
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -10,8 +13,13 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
@WebServlet("/config/*")
|
||||
public class ConfigServlet extends SecureServlet {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ConfigServlet.class);
|
||||
|
||||
@Override
|
||||
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
|
||||
if (_authCode.getAccountId() == 100) {
|
||||
logger.error("my ip: " + _req.getRemoteAddr());
|
||||
}
|
||||
if (isPath(_req, 0, "bin"))
|
||||
zipBsonResponse(_rep, Globals.dao.getMergedConfig(_authCode));
|
||||
else
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package com.lanternsoftware.currentmonitor.servlet;
|
||||
|
||||
import com.lanternsoftware.currentmonitor.context.Globals;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
import com.lanternsoftware.util.ResourceLoader;
|
||||
import com.lanternsoftware.util.dao.DaoEntity;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
import com.lanternsoftware.util.email.EmailValidator;
|
||||
import com.lanternsoftware.util.servlet.FreemarkerConfigUtil;
|
||||
import com.lanternsoftware.util.servlet.FreemarkerServlet;
|
||||
import com.sendgrid.Method;
|
||||
import com.sendgrid.Request;
|
||||
import com.sendgrid.Response;
|
||||
import com.sendgrid.SendGrid;
|
||||
import com.sendgrid.helpers.mail.Mail;
|
||||
import com.sendgrid.helpers.mail.objects.Content;
|
||||
import com.sendgrid.helpers.mail.objects.Email;
|
||||
import freemarker.template.Configuration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
|
||||
@WebServlet("/resetPassword/*")
|
||||
public class ResetPasswordServlet extends FreemarkerServlet {
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(ResetPasswordServlet.class);
|
||||
protected static final Configuration CONFIG = FreemarkerConfigUtil.createConfig(ResetPasswordServlet.class, "/templates", 100);
|
||||
protected static final String api_key = ResourceLoader.loadFileAsString(LanternFiles.OPS_PATH + "sendgrid.txt");
|
||||
|
||||
@Override
|
||||
protected Configuration getFreemarkerConfig() {
|
||||
return CONFIG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest _req, HttpServletResponse _resp) {
|
||||
String[] path = getPath(_req);
|
||||
String email = Globals.dao.getEmailForResetKey(path[1]);
|
||||
if (EmailValidator.getInstance().isValid(email)) {
|
||||
render(_resp, "passwordReset.ftl", model(_req, "key", path[1]));
|
||||
} else {
|
||||
render(_resp, "passwordResetMsg.ftl", model(_req, "msg", "This password reset code is no longer valid. Please try sending a new code from the Lantern Power Monitor application."));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest _req, HttpServletResponse _resp) {
|
||||
if (NullUtils.isEqual(_req.getContentType(), MediaType.APPLICATION_FORM_URLENCODED)) {
|
||||
String key = _req.getParameter("reset_key");
|
||||
String password = _req.getParameter("password");
|
||||
if (NullUtils.length(password) < 8) {
|
||||
render(_resp, "passwordReset.ftl", model(_req, "key", key).and("error", "Your password must be at least 8 characters."));
|
||||
return;
|
||||
}
|
||||
Globals.dao.resetPassword(key, password);
|
||||
render(_resp, "passwordResetMsg.ftl", model(_req, "msg", "Your password has been changed."));
|
||||
} else {
|
||||
DaoEntity payload = getRequestZipBson(_req);
|
||||
String email = DaoSerializer.getString(payload, "email");
|
||||
if (NullUtils.isNotEmpty(email)) {
|
||||
String key = Globals.dao.addPasswordResetKey(email);
|
||||
Email from = new Email("info@lanternsoftware.com");
|
||||
String subject = "Password Reset - Lantern Power Monitor";
|
||||
Email to = new Email(email);
|
||||
Content content = new Content("text/plain", "Reset your password using this link:\nhttps://lanternsoftware.com/currentmonitor/resetPassword/" + key);
|
||||
Mail mail = new Mail(from, subject, to, content);
|
||||
SendGrid sg = new SendGrid(api_key);
|
||||
Request request = new Request();
|
||||
try {
|
||||
request.setMethod(Method.POST);
|
||||
request.setEndpoint("mail/send");
|
||||
request.setBody(mail.build());
|
||||
Response response = sg.api(request);
|
||||
zipBsonResponse(_resp, new DaoEntity("success", response.getStatusCode() == 200));
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Failed to send password reset email", ex);
|
||||
zipBsonResponse(_resp, new DaoEntity("success", false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Lantern | Reset Password</title>
|
||||
<link href="${context}/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section id="reset" class="container features" style="margin-top:40px;margin-bottom:40px;">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="navy-line"></div>
|
||||
<h4><img class="mr-1" src="${context}/img/logo_40.png">Reset Password</h4>
|
||||
<form class="ml-2" method="POST">
|
||||
<input type="hidden" name="reset_key" value="${key}"/>
|
||||
<div>New Password:</div>
|
||||
<input type="password" name="password"/>
|
||||
<input type="submit" class="btn-primary" value="Submit"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Lantern | Reset Password</title>
|
||||
<link href="${context}/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section id="reset" class="container features" style="margin-top:40px;margin-bottom:40px;">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="navy-line"></div>
|
||||
<h4><img class="mr-1" src="${context}/img/logo_40.png">Reset Password</h4>
|
||||
<div class="ml-2">${msg}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>
|
3904
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/bootstrap-grid.css
vendored
Normal file
3904
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/bootstrap-grid.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
325
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/bootstrap-reboot.css
vendored
Normal file
325
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/bootstrap-reboot.css
vendored
Normal file
|
@ -0,0 +1,325 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v4.5.0 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2020 The Bootstrap Authors
|
||||
* Copyright 2011-2020 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus:not(:focus-visible) {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
button:not(:disabled),
|
||||
[type="button"]:not(:disabled),
|
||||
[type="reset"]:not(:disabled),
|
||||
[type="submit"]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,8 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v4.5.0 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2020 The Bootstrap Authors
|
||||
* Copyright 2011-2020 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
|
||||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
File diff suppressed because one or more lines are too long
10278
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/bootstrap.css
vendored
Normal file
10278
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7033
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/js/bootstrap.bundle.js
vendored
Normal file
7033
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/js/bootstrap.bundle.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4420
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/js/bootstrap.js
vendored
Normal file
4420
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/js/bootstrap.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
7
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/js/bootstrap.min.js
vendored
Normal file
7
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -80,6 +80,8 @@ public class HttpPool {
|
|||
CloseableHttpResponse resp = null;
|
||||
try {
|
||||
resp = execute(_request);
|
||||
if (resp == null)
|
||||
return null;
|
||||
if ((resp.getStatusLine().getStatusCode() < 200) || (resp.getStatusLine().getStatusCode() >= 300)) {
|
||||
LOG.error("Failed to make http request to " + _request.getURI().toString() + ". Status code: " + resp.getStatusLine().getStatusCode());
|
||||
return null;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>lantern-util-common</artifactId>
|
||||
<artifactId>lantern-util-dao</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -1,27 +1,24 @@
|
|||
package com.lanternsoftware.util.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
import com.lanternsoftware.util.dao.DaoEntity;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
import freemarker.template.Configuration;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
|
||||
import freemarker.template.Configuration;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class FreemarkerServlet extends HttpServlet {
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(FreemarkerServlet.class);
|
||||
|
||||
protected abstract Configuration getFreemarkerConfig();
|
||||
|
||||
public static String[] getPath(HttpServletRequest _request) {
|
||||
|
@ -29,7 +26,7 @@ public abstract class FreemarkerServlet extends HttpServlet {
|
|||
if (sPath.startsWith("/"))
|
||||
sPath = sPath.substring(1);
|
||||
String[] path = sPath.split("/");
|
||||
if ((path == null) || (path.length == 0) || (path[0].length() == 0))
|
||||
if ((path.length == 0) || (path[0].length() == 0))
|
||||
return new String[] { "index" };
|
||||
int iExtPos = CollectionUtils.last(path).lastIndexOf(".");
|
||||
if (iExtPos > -1) {
|
||||
|
@ -42,45 +39,6 @@ public abstract class FreemarkerServlet extends HttpServlet {
|
|||
_response.sendRedirect(_response.encodeRedirectURL(_sURL));
|
||||
}
|
||||
|
||||
public static void setResponseHtml(HttpServletResponse _response, String _sHtml) {
|
||||
setResponseEntity(_response, "text/html", _sHtml);
|
||||
}
|
||||
|
||||
public static void setResponseEntity(HttpServletResponse _response, String _sContentType, String _sEntity) {
|
||||
setResponseEntity(_response, 200, _sContentType, _sEntity);
|
||||
}
|
||||
|
||||
public static void setResponseEntity(HttpServletResponse _response, String _sContentType, byte[] _btData) {
|
||||
setResponseEntity(_response, 200, _sContentType, _btData);
|
||||
}
|
||||
|
||||
public static void setResponseEntity(HttpServletResponse _response, int _iStatus, String _sContentType, String _sEntity) {
|
||||
setResponseEntity(_response, _iStatus, _sContentType, NullUtils.toByteArray(_sEntity));
|
||||
}
|
||||
|
||||
public static void setResponseEntity(HttpServletResponse _response, int _iStatus, String _sContentType, byte[] _btData) {
|
||||
OutputStream os = null;
|
||||
try {
|
||||
_response.setStatus(_iStatus);
|
||||
_response.setCharacterEncoding("UTF-8");
|
||||
_response.setContentType(_sContentType);
|
||||
if ((_btData != null) && (_btData.length > 0)) {
|
||||
_response.setContentLength(_btData.length);
|
||||
os = _response.getOutputStream();
|
||||
os.write(_btData);
|
||||
}
|
||||
else
|
||||
_response.setContentLength(0);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (!e.getClass().getSimpleName().equals("ClientAbortException"))
|
||||
LOG.error("Failed to set response entity", e);
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
}
|
||||
|
||||
public void render(HttpServletResponse _rep, String _sHtmlResourceKey, Map<String, Object> _mapModel) {
|
||||
String html = FreemarkerUtil.render(getFreemarkerConfig(), _sHtmlResourceKey, _mapModel);
|
||||
if (html == null)
|
||||
|
@ -88,13 +46,19 @@ public abstract class FreemarkerServlet extends HttpServlet {
|
|||
else
|
||||
setResponseHtml(_rep, html);
|
||||
}
|
||||
|
||||
protected Map<String, Object> simpleModel(String _name, Object _value) {
|
||||
Map<String, Object> mapModel = new HashMap<String, Object>();
|
||||
mapModel.put(_name, _value);
|
||||
return mapModel;
|
||||
|
||||
public static DaoEntity model(HttpServletRequest _req, String _name, Object _value) {
|
||||
DaoEntity model = model(_req);
|
||||
model.put(_name, _value);
|
||||
return model;
|
||||
}
|
||||
|
||||
|
||||
protected static DaoEntity model(HttpServletRequest _req) {
|
||||
DaoEntity model = new DaoEntity("context", _req.getContextPath());
|
||||
model.put("css_version", "1.0.0");
|
||||
return model;
|
||||
}
|
||||
|
||||
public static <T> T getSessionVar(HttpServletRequest _req, String _name) {
|
||||
return (T) _req.getSession().getAttribute(_name);
|
||||
}
|
||||
|
@ -121,5 +85,90 @@ public abstract class FreemarkerServlet extends HttpServlet {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static void setResponseHtml(HttpServletResponse _response, String _sHtml) {
|
||||
setResponseEntity(_response, MediaType.TEXT_HTML, _sHtml);
|
||||
}
|
||||
|
||||
public static void setResponseEntity(HttpServletResponse _response, String _sContentType, String _sEntity) {
|
||||
setResponseEntity(_response, 200, _sContentType, _sEntity);
|
||||
}
|
||||
|
||||
public static void setResponseEntity(HttpServletResponse _response, String _sContentType, byte[] _btData) {
|
||||
setResponseEntity(_response, 200, _sContentType, _btData);
|
||||
}
|
||||
|
||||
public static void setResponseEntity(HttpServletResponse _response, int _iStatus, String _sContentType, String _sEntity) {
|
||||
setResponseEntity(_response, _iStatus, _sContentType, NullUtils.toByteArray(_sEntity));
|
||||
}
|
||||
|
||||
public static void setResponseEntity(HttpServletResponse _response, int _iStatus, String _sContentType, byte[] _btData) {
|
||||
OutputStream os = null;
|
||||
try {
|
||||
_response.setStatus(_iStatus);
|
||||
_response.setCharacterEncoding("UTF-8");
|
||||
_response.setContentType(_sContentType);
|
||||
if ((_btData != null) && (_btData.length > 0)) {
|
||||
_response.setContentLength(_btData.length);
|
||||
os = _response.getOutputStream();
|
||||
os.write(_btData);
|
||||
} else
|
||||
_response.setContentLength(0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
}
|
||||
|
||||
protected void zipBsonResponse(HttpServletResponse _response, Object _object)
|
||||
{
|
||||
setResponseEntity(_response, 200, MediaType.APPLICATION_OCTET_STREAM, DaoSerializer.toZipBson(_object));
|
||||
}
|
||||
|
||||
protected void jsonResponse(HttpServletResponse _response, Object _object)
|
||||
{
|
||||
setResponseEntity(_response, 200, MediaType.APPLICATION_JSON, DaoSerializer.toJson(_object));
|
||||
}
|
||||
|
||||
protected void jsonResponse(HttpServletResponse _response, String _json)
|
||||
{
|
||||
setResponseEntity(_response, 200, MediaType.APPLICATION_JSON, _json);
|
||||
}
|
||||
|
||||
protected String getRequestPayloadAsString(HttpServletRequest _req) {
|
||||
return NullUtils.toString(getRequestPayload(_req));
|
||||
}
|
||||
|
||||
protected byte[] getRequestPayload(HttpServletRequest _req) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = _req.getInputStream();
|
||||
return IOUtils.toByteArray(is);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
protected DaoEntity getRequestZipBson(HttpServletRequest _req) {
|
||||
return DaoSerializer.fromZipBson(getRequestPayload(_req));
|
||||
}
|
||||
|
||||
protected <T> T getRequestPayload(HttpServletRequest _req, Class<T> _retClass) {
|
||||
return DaoSerializer.fromZipBson(getRequestPayload(_req), _retClass);
|
||||
}
|
||||
|
||||
protected String[] path(HttpServletRequest _req) {
|
||||
return NullUtils.cleanSplit(NullUtils.makeNotNull(_req.getPathInfo()), "/");
|
||||
}
|
||||
|
||||
protected boolean isPath(HttpServletRequest _req, int _index, String _path) {
|
||||
return NullUtils.isEqual(_path, CollectionUtils.get(path(_req), _index));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
package com.lanternsoftware.datamodel.zwave;
|
||||
|
||||
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
import com.lanternsoftware.util.dao.annotations.DBSerializable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@DBSerializable
|
||||
public class Switch {
|
||||
private SwitchType type;
|
||||
private String room;
|
||||
private String name;
|
||||
private int nodeId;
|
||||
private int level;
|
||||
private int gpioPin;
|
||||
private boolean primary;
|
||||
private boolean multilevel;
|
||||
private boolean hold;
|
||||
private String thermostatSource;
|
||||
private boolean hidden;
|
||||
private String thermometerUrl;
|
||||
private String controllerUrl;
|
||||
private ThermostatMode thermostatMode;
|
||||
private int lowLevel;
|
||||
private List<SwitchSchedule> schedule;
|
||||
|
@ -23,23 +28,30 @@ public class Switch {
|
|||
public Switch() {
|
||||
}
|
||||
|
||||
public Switch(String _room, String _name, int _nodeId, boolean _primary, boolean _multilevel, String _thermostatSource, int _lowLevel) {
|
||||
this(_room, _name, _nodeId, 0, _primary, _multilevel, false, _thermostatSource, _lowLevel, null);
|
||||
public Switch(String _room, String _name, int _nodeId, boolean _primary, boolean _multilevel, String _thermometerUrl, int _lowLevel) {
|
||||
this(_room, _name, _nodeId, 0, _primary, false, _thermometerUrl, _lowLevel, null);
|
||||
}
|
||||
|
||||
public Switch(String _room, String _name, int _nodeId, int _level, boolean _primary, boolean _multilevel, boolean _hold, String _thermostatSource, int _lowLevel, List<SwitchSchedule> _schedule) {
|
||||
public Switch(String _room, String _name, int _nodeId, int _level, boolean _primary, boolean _hold, String _thermometerUrl, int _lowLevel, List<SwitchSchedule> _schedule) {
|
||||
room = _room;
|
||||
name = _name;
|
||||
nodeId = _nodeId;
|
||||
level = _level;
|
||||
primary = _primary;
|
||||
multilevel = _multilevel;
|
||||
hold = _hold;
|
||||
thermostatSource = _thermostatSource;
|
||||
thermometerUrl = _thermometerUrl;
|
||||
lowLevel = _lowLevel;
|
||||
schedule = _schedule;
|
||||
}
|
||||
|
||||
public SwitchType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(SwitchType _type) {
|
||||
type = _type;
|
||||
}
|
||||
|
||||
public String getRoom() {
|
||||
return room;
|
||||
}
|
||||
|
@ -56,6 +68,12 @@ public class Switch {
|
|||
name = _name;
|
||||
}
|
||||
|
||||
public String getFullDisplay() {
|
||||
if (NullUtils.isNotEmpty(room))
|
||||
return room + " - " + name;
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
@ -72,6 +90,14 @@ public class Switch {
|
|||
level = _level;
|
||||
}
|
||||
|
||||
public int getGpioPin() {
|
||||
return gpioPin;
|
||||
}
|
||||
|
||||
public void setGpioPin(int _gpioPin) {
|
||||
gpioPin = _gpioPin;
|
||||
}
|
||||
|
||||
public boolean isPrimary() {
|
||||
return primary;
|
||||
}
|
||||
|
@ -81,11 +107,7 @@ public class Switch {
|
|||
}
|
||||
|
||||
public boolean isMultilevel() {
|
||||
return multilevel;
|
||||
}
|
||||
|
||||
public void setMultilevel(boolean _multilevel) {
|
||||
multilevel = _multilevel;
|
||||
return type == SwitchType.DIMMER;
|
||||
}
|
||||
|
||||
public boolean isHold() {
|
||||
|
@ -96,28 +118,44 @@ public class Switch {
|
|||
hold = _hold;
|
||||
}
|
||||
|
||||
public String getThermostatSource() {
|
||||
return thermostatSource;
|
||||
public String getThermometerUrl() {
|
||||
return thermometerUrl;
|
||||
}
|
||||
|
||||
public void setThermostatSource(String _thermostatSource) {
|
||||
thermostatSource = _thermostatSource;
|
||||
public void setThermometerUrl(String _thermometerUrl) {
|
||||
thermometerUrl = _thermometerUrl;
|
||||
}
|
||||
|
||||
public String getControllerUrl() {
|
||||
return controllerUrl;
|
||||
}
|
||||
|
||||
public void setControllerUrl(String _controllerUrl) {
|
||||
controllerUrl = _controllerUrl;
|
||||
}
|
||||
|
||||
public boolean isThermostat() {
|
||||
return NullUtils.isNotEmpty(thermostatSource) && (nodeId < 100);
|
||||
return isSpaceHeaterThermostat() || isZWaveThermostat();
|
||||
}
|
||||
|
||||
public boolean isThermometer() {
|
||||
return isUrlThermostat() && (nodeId > 99);
|
||||
public boolean isSpaceHeaterThermostat() {
|
||||
return type == SwitchType.SPACE_HEATER_THERMOSTAT;
|
||||
}
|
||||
|
||||
public boolean isUrlThermostat() {
|
||||
return NullUtils.makeNotNull(thermostatSource).startsWith("http");
|
||||
public boolean isThermometerUrlValid() {
|
||||
return NullUtils.makeNotNull(thermometerUrl).startsWith("http");
|
||||
}
|
||||
|
||||
public boolean isZWaveThermostat() {
|
||||
return NullUtils.isEqual(thermostatSource, "ZWAVE");
|
||||
return type == SwitchType.THERMOSTAT;
|
||||
}
|
||||
|
||||
public boolean isRelay() {
|
||||
return type == SwitchType.RELAY;
|
||||
}
|
||||
|
||||
public boolean isControlledBy(String _controllerUrl) {
|
||||
return NullUtils.isEqual(_controllerUrl, controllerUrl);
|
||||
}
|
||||
|
||||
public ThermostatMode getThermostatMode() {
|
||||
|
@ -136,6 +174,14 @@ public class Switch {
|
|||
lowLevel = _lowLevel;
|
||||
}
|
||||
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
}
|
||||
|
||||
public void setHidden(boolean _hidden) {
|
||||
hidden = _hidden;
|
||||
}
|
||||
|
||||
public List<SwitchSchedule> getSchedule() {
|
||||
return schedule;
|
||||
}
|
||||
|
@ -143,4 +189,40 @@ public class Switch {
|
|||
public void setSchedule(List<SwitchSchedule> _schedule) {
|
||||
schedule = _schedule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object _o) {
|
||||
if (this == _o) return true;
|
||||
if (_o == null || getClass() != _o.getClass()) return false;
|
||||
Switch aSwitch = (Switch) _o;
|
||||
return nodeId == aSwitch.nodeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(nodeId);
|
||||
}
|
||||
|
||||
public boolean isModified(Switch _switch) {
|
||||
return (_switch == null) || (level != _switch.getLevel()) || (hold != _switch.isHold()) || (thermostatMode != _switch.getThermostatMode());
|
||||
}
|
||||
|
||||
public Switch duplicate() {
|
||||
Switch s = new Switch();
|
||||
s.setType(getType());
|
||||
s.setRoom(getRoom());
|
||||
s.setName(getName());
|
||||
s.setNodeId(getNodeId());
|
||||
s.setLevel(getLevel());
|
||||
s.setGpioPin(getGpioPin());
|
||||
s.setPrimary(isPrimary());
|
||||
s.setHold(isHold());
|
||||
s.setHidden(isHidden());
|
||||
s.setThermometerUrl(getThermometerUrl());
|
||||
s.setControllerUrl(getControllerUrl());
|
||||
s.setThermostatMode(getThermostatMode());
|
||||
s.setLowLevel(getLowLevel());
|
||||
s.setSchedule(CollectionUtils.transform(getSchedule(), SwitchSchedule::duplicate));
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,20 +7,24 @@ import com.lanternsoftware.util.dao.annotations.DBSerializable;
|
|||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@DBSerializable
|
||||
public class SwitchSchedule {
|
||||
private int dayOfWeek;
|
||||
private int timeOfDay;
|
||||
private int minutesPerHour;
|
||||
private int timeOfDayEnd;
|
||||
private int onDuration;
|
||||
private int offDuration;
|
||||
private int level;
|
||||
|
||||
public SwitchSchedule() {
|
||||
}
|
||||
|
||||
public SwitchSchedule(int _minutesPerHour) {
|
||||
minutesPerHour = _minutesPerHour;
|
||||
public SwitchSchedule(int _onDuration, int _offDuration) {
|
||||
onDuration = _onDuration;
|
||||
offDuration = _offDuration;
|
||||
}
|
||||
|
||||
public SwitchSchedule(int _dayOfWeek, int _timeOfDay, int _level) {
|
||||
|
@ -64,12 +68,34 @@ public class SwitchSchedule {
|
|||
timeOfDay = (_hour * 3600) + (_minute * 60) + _second;
|
||||
}
|
||||
|
||||
public int getMinutesPerHour() {
|
||||
return minutesPerHour;
|
||||
public int getTimeOfDayEnd() {
|
||||
return timeOfDayEnd;
|
||||
}
|
||||
|
||||
public void setMinutesPerHour(int _minutesPerHour) {
|
||||
minutesPerHour = _minutesPerHour;
|
||||
public void setTimeOfDayEnd(int _timeOfDayEnd) {
|
||||
timeOfDayEnd = _timeOfDayEnd;
|
||||
}
|
||||
public void setTimeOfDayEnd(int _hour, int _minute) {
|
||||
timeOfDayEnd = (_hour * 3600) + (_minute * 60);
|
||||
}
|
||||
public void setTimeOfDayEnd(int _hour, int _minute, int _second) {
|
||||
timeOfDayEnd = (_hour * 3600) + (_minute * 60) + _second;
|
||||
}
|
||||
|
||||
public int getOnDuration() {
|
||||
return onDuration;
|
||||
}
|
||||
|
||||
public void setOnDuration(int _onDuration) {
|
||||
onDuration = _onDuration;
|
||||
}
|
||||
|
||||
public int getOffDuration() {
|
||||
return offDuration;
|
||||
}
|
||||
|
||||
public void setOffDuration(int _offDuration) {
|
||||
offDuration = _offDuration;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
|
@ -81,38 +107,88 @@ public class SwitchSchedule {
|
|||
}
|
||||
|
||||
public int hour() {
|
||||
return timeOfDay/3600;
|
||||
return hour(timeOfDay);
|
||||
}
|
||||
|
||||
public int minute() {
|
||||
return (timeOfDay/60)%60;
|
||||
return minute(timeOfDay);
|
||||
}
|
||||
|
||||
public int second() {
|
||||
return timeOfDay%60;
|
||||
return second(timeOfDay);
|
||||
}
|
||||
|
||||
private boolean isOn() {
|
||||
return GregorianCalendar.getInstance().get(Calendar.MINUTE) < minutesPerHour;
|
||||
public int hour(int _timeOfDay) {
|
||||
return _timeOfDay/3600;
|
||||
}
|
||||
|
||||
public int minute(int _timeOfDay) {
|
||||
return (_timeOfDay/60)%60;
|
||||
}
|
||||
|
||||
public int second(int _timeOfDay) {
|
||||
return _timeOfDay%60;
|
||||
}
|
||||
|
||||
public Date startToday(TimeZone _tz) {
|
||||
return today(timeOfDay, _tz);
|
||||
}
|
||||
|
||||
public Date endToday(TimeZone _tz) {
|
||||
return today(timeOfDayEnd, _tz);
|
||||
}
|
||||
|
||||
public Date today(int _timeOfDay, TimeZone _tz) {
|
||||
Date now = new Date();
|
||||
Calendar cal = DateUtils.toCalendar(now, _tz);
|
||||
if (dayOfWeek > 0)
|
||||
cal.set(Calendar.DAY_OF_WEEK, dayOfWeek);
|
||||
cal.set(Calendar.HOUR_OF_DAY, hour(_timeOfDay));
|
||||
cal.set(Calendar.MINUTE, minute(_timeOfDay));
|
||||
cal.set(Calendar.SECOND, second(_timeOfDay));
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
public SwitchTransition getNextTransition(Switch _switch, TimeZone _tz) {
|
||||
if (minutesPerHour > 0) {
|
||||
Date dt = DateUtils.getStartOfHour(_tz);
|
||||
Date transition = DateUtils.addMinutes(dt, minutesPerHour);
|
||||
if (new Date().before(transition))
|
||||
return new SwitchTransition(_switch, transition, 0);
|
||||
return new SwitchTransition(_switch, DateUtils.getEndOfHour(_tz), level == 0?255:level);
|
||||
}
|
||||
Date startToday = startToday(_tz);
|
||||
Date now = new Date();
|
||||
Calendar cal = DateUtils.toCalendar(now, _tz);
|
||||
cal.set(Calendar.DAY_OF_WEEK, dayOfWeek);
|
||||
cal.set(Calendar.HOUR_OF_DAY, hour());
|
||||
cal.set(Calendar.MINUTE, minute());
|
||||
cal.set(Calendar.SECOND, second());
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
if (cal.getTimeInMillis() <= now.getTime())
|
||||
cal.add(Calendar.DAY_OF_MONTH, 7);
|
||||
return new SwitchTransition(_switch, cal.getTime(), level);
|
||||
if (onDuration > 0) {
|
||||
if ((timeOfDay > 0) && now.before(startToday))
|
||||
now = startToday;
|
||||
if (timeOfDayEnd > 0) {
|
||||
if (now.after(endToday(_tz)))
|
||||
now = DateUtils.addDays(startToday, (dayOfWeek > 0)?7:1, _tz);
|
||||
}
|
||||
long progress = now.getTime()%((onDuration+offDuration)*1000L);
|
||||
if (progress < onDuration*1000L)
|
||||
return new SwitchTransition(_switch, new Date(now.getTime() + (onDuration*1000L)-progress), 0);
|
||||
return new SwitchTransition(_switch, new Date(now.getTime()+((onDuration+offDuration)*1000L)-progress), level == 0 ? 255 : level);
|
||||
}
|
||||
return new SwitchTransition(_switch, startToday.after(now)?startToday:DateUtils.addDays(startToday, (dayOfWeek > 0)?7:1, _tz), level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object _o) {
|
||||
if (this == _o) return true;
|
||||
if (_o == null || getClass() != _o.getClass()) return false;
|
||||
SwitchSchedule that = (SwitchSchedule) _o;
|
||||
return dayOfWeek == that.dayOfWeek && timeOfDay == that.timeOfDay && timeOfDayEnd == that.timeOfDayEnd && onDuration == that.onDuration && offDuration == that.offDuration && level == that.level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(dayOfWeek, timeOfDay, timeOfDayEnd, onDuration, offDuration, level);
|
||||
}
|
||||
|
||||
public SwitchSchedule duplicate() {
|
||||
SwitchSchedule s = new SwitchSchedule();
|
||||
s.setDayOfWeek(getDayOfWeek());
|
||||
s.setTimeOfDay(getTimeOfDay());
|
||||
s.setTimeOfDayEnd(getTimeOfDayEnd());
|
||||
s.setOnDuration(getOnDuration());
|
||||
s.setOffDuration(getOffDuration());
|
||||
s.setLevel(getLevel());
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.lanternsoftware.datamodel.zwave;
|
||||
|
||||
public enum SwitchType {
|
||||
BINARY,
|
||||
DIMMER,
|
||||
THERMOSTAT,
|
||||
SPACE_HEATER_THERMOSTAT,
|
||||
THERMOMETER,
|
||||
RELAY,
|
||||
SECURITY
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package com.lanternsoftware.datamodel.zwave;
|
||||
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
import com.lanternsoftware.util.dao.annotations.DBSerializable;
|
||||
import com.lanternsoftware.util.dao.annotations.PrimaryKey;
|
||||
|
||||
|
@ -7,8 +9,10 @@ import java.util.List;
|
|||
|
||||
@DBSerializable(autogen = false)
|
||||
public class ZWaveConfig {
|
||||
@PrimaryKey
|
||||
private int accountId;
|
||||
@PrimaryKey private int accountId;
|
||||
private String commPort;
|
||||
private String url;
|
||||
private String masterUrl;
|
||||
private List<Switch> switches;
|
||||
|
||||
public int getAccountId() {
|
||||
|
@ -19,6 +23,30 @@ public class ZWaveConfig {
|
|||
accountId = _accountId;
|
||||
}
|
||||
|
||||
public String getCommPort() {
|
||||
return commPort;
|
||||
}
|
||||
|
||||
public void setCommPort(String _commPort) {
|
||||
commPort = _commPort;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String _url) {
|
||||
url = _url;
|
||||
}
|
||||
|
||||
public String getMasterUrl() {
|
||||
return masterUrl;
|
||||
}
|
||||
|
||||
public void setMasterUrl(String _masterUrl) {
|
||||
masterUrl = _masterUrl;
|
||||
}
|
||||
|
||||
public List<Switch> getSwitches() {
|
||||
return switches;
|
||||
}
|
||||
|
@ -26,4 +54,16 @@ public class ZWaveConfig {
|
|||
public void setSwitches(List<Switch> _switches) {
|
||||
switches = _switches;
|
||||
}
|
||||
|
||||
public boolean isMaster() {
|
||||
return NullUtils.isEqual(url, masterUrl);
|
||||
}
|
||||
|
||||
public List<Switch> getSwitchesForThisController() {
|
||||
return CollectionUtils.filter(switches, this::isMySwitch);
|
||||
}
|
||||
|
||||
public boolean isMySwitch(Switch _sw) {
|
||||
return (isMaster() && NullUtils.isEmpty(_sw.getControllerUrl())) || _sw.isControlledBy(getUrl());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import com.lanternsoftware.util.dao.AbstractDaoSerializer;
|
|||
import com.lanternsoftware.util.dao.DaoEntity;
|
||||
import com.lanternsoftware.util.dao.DaoProxyType;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -28,7 +27,9 @@ public class SwitchScheduleSerializer extends AbstractDaoSerializer<SwitchSchedu
|
|||
DaoEntity d = new DaoEntity();
|
||||
d.put("day_of_week", _o.getDayOfWeek());
|
||||
d.put("time_of_day", _o.getTimeOfDay());
|
||||
d.put("minutes_per_hour", _o.getMinutesPerHour());
|
||||
d.put("time_of_day_end", _o.getTimeOfDayEnd());
|
||||
d.put("on_duration", _o.getOnDuration());
|
||||
d.put("off_duration", _o.getOffDuration());
|
||||
d.put("level", _o.getLevel());
|
||||
return d;
|
||||
}
|
||||
|
@ -39,7 +40,9 @@ public class SwitchScheduleSerializer extends AbstractDaoSerializer<SwitchSchedu
|
|||
SwitchSchedule o = new SwitchSchedule();
|
||||
o.setDayOfWeek(DaoSerializer.getInteger(_d, "day_of_week"));
|
||||
o.setTimeOfDay(DaoSerializer.getInteger(_d, "time_of_day"));
|
||||
o.setMinutesPerHour(DaoSerializer.getInteger(_d, "minutes_per_hour"));
|
||||
o.setTimeOfDayEnd(DaoSerializer.getInteger(_d, "time_of_day_end"));
|
||||
o.setOnDuration(DaoSerializer.getInteger(_d, "on_duration"));
|
||||
o.setOffDuration(DaoSerializer.getInteger(_d, "off_duration"));
|
||||
o.setLevel(DaoSerializer.getInteger(_d, "level"));
|
||||
return o;
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ package com.lanternsoftware.datamodel.zwave.dao;
|
|||
|
||||
import com.lanternsoftware.datamodel.zwave.Switch;
|
||||
import com.lanternsoftware.datamodel.zwave.SwitchSchedule;
|
||||
import com.lanternsoftware.datamodel.zwave.SwitchType;
|
||||
import com.lanternsoftware.datamodel.zwave.ThermostatMode;
|
||||
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
|
||||
import com.lanternsoftware.util.dao.DaoEntity;
|
||||
import com.lanternsoftware.util.dao.DaoProxyType;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -28,16 +28,19 @@ public class SwitchSerializer extends AbstractDaoSerializer<Switch>
|
|||
public DaoEntity toDaoEntity(Switch _o)
|
||||
{
|
||||
DaoEntity d = new DaoEntity();
|
||||
d.put("type", DaoSerializer.toEnumName(_o.getType()));
|
||||
d.put("room", _o.getRoom());
|
||||
d.put("name", _o.getName());
|
||||
d.put("node_id", _o.getNodeId());
|
||||
d.put("level", _o.getLevel());
|
||||
d.put("gpio_pin", _o.getGpioPin());
|
||||
d.put("primary", _o.isPrimary());
|
||||
d.put("multilevel", _o.isMultilevel());
|
||||
d.put("hold", _o.isHold());
|
||||
d.put("thermostat_source", _o.getThermostatSource());
|
||||
d.put("thermometer_url", _o.getThermometerUrl());
|
||||
d.put("controller_url", _o.getControllerUrl());
|
||||
d.put("thermostat_mode", DaoSerializer.toEnumName(_o.getThermostatMode()));
|
||||
d.put("low_level", _o.getLowLevel());
|
||||
d.put("hidden", _o.isHidden());
|
||||
d.put("schedule", DaoSerializer.toDaoEntities(_o.getSchedule(), DaoProxyType.MONGO));
|
||||
return d;
|
||||
}
|
||||
|
@ -46,16 +49,19 @@ public class SwitchSerializer extends AbstractDaoSerializer<Switch>
|
|||
public Switch fromDaoEntity(DaoEntity _d)
|
||||
{
|
||||
Switch o = new Switch();
|
||||
o.setType(DaoSerializer.getEnum(_d, "type", SwitchType.class));
|
||||
o.setRoom(DaoSerializer.getString(_d, "room"));
|
||||
o.setName(DaoSerializer.getString(_d, "name"));
|
||||
o.setNodeId(DaoSerializer.getInteger(_d, "node_id"));
|
||||
o.setLevel(DaoSerializer.getInteger(_d, "level"));
|
||||
o.setGpioPin(DaoSerializer.getInteger(_d, "gpio_pin"));
|
||||
o.setPrimary(DaoSerializer.getBoolean(_d, "primary"));
|
||||
o.setMultilevel(DaoSerializer.getBoolean(_d, "multilevel"));
|
||||
o.setHold(DaoSerializer.getBoolean(_d, "hold"));
|
||||
o.setThermostatSource(DaoSerializer.getString(_d, "thermostat_source"));
|
||||
o.setThermometerUrl(DaoSerializer.getString(_d, "thermometer_url"));
|
||||
o.setControllerUrl(DaoSerializer.getString(_d, "controller_url"));
|
||||
o.setThermostatMode(DaoSerializer.getEnum(_d, "thermostat_mode", ThermostatMode.class));
|
||||
o.setLowLevel(DaoSerializer.getInteger(_d, "low_level"));
|
||||
o.setHidden(DaoSerializer.getBoolean(_d, "hidden"));
|
||||
o.setSchedule(DaoSerializer.getList(_d, "schedule", SwitchSchedule.class));
|
||||
return o;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ public class ZWaveConfigSerializer extends AbstractDaoSerializer<ZWaveConfig>
|
|||
{
|
||||
DaoEntity d = new DaoEntity();
|
||||
d.put("_id", String.valueOf(_o.getAccountId()));
|
||||
d.put("comm_port", _o.getCommPort());
|
||||
d.put("url", _o.getUrl());
|
||||
d.put("master_url", _o.getMasterUrl());
|
||||
d.put("switches", DaoSerializer.toDaoEntities(_o.getSwitches(), DaoProxyType.MONGO));
|
||||
return d;
|
||||
}
|
||||
|
@ -37,6 +40,9 @@ public class ZWaveConfigSerializer extends AbstractDaoSerializer<ZWaveConfig>
|
|||
{
|
||||
ZWaveConfig o = new ZWaveConfig();
|
||||
o.setAccountId(DaoSerializer.getInteger(_d, "_id"));
|
||||
o.setCommPort(DaoSerializer.getString(_d, "comm_port"));
|
||||
o.setUrl(DaoSerializer.getString(_d, "url"));
|
||||
o.setMasterUrl(DaoSerializer.getString(_d, "master_url"));
|
||||
o.setSwitches(DaoSerializer.getList(_d, "switches", Switch.class));
|
||||
return o;
|
||||
}
|
||||
|
|
|
@ -20,9 +20,14 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lanternsoftware.currentmonitor</groupId>
|
||||
<artifactId>lantern-dataaccess-currentmonitor</artifactId>
|
||||
<artifactId>lantern-datamodel-currentmonitor</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.pi4j</groupId>
|
||||
<artifactId>pi4j-gpio-extension</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lanternsoftware.zwave</groupId>
|
||||
<artifactId>lantern-zwave</artifactId>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.lanternsoftware.zwave;
|
||||
|
||||
import com.lanternsoftware.datamodel.zwave.Switch;
|
||||
import com.lanternsoftware.datamodel.zwave.SwitchType;
|
||||
import com.lanternsoftware.zwave.security.SecurityController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TestSecurity {
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(TestSecurity.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
SecurityController c = new SecurityController();
|
||||
Switch sw = new Switch("Garage", "Door 1", 1000, true, false, null, 0);
|
||||
sw.setGpioPin(7);
|
||||
sw.setType(SwitchType.SECURITY);
|
||||
c.listen(sw, (nodeId, _open) -> LOG.info("Door is " + (_open ? "OPEN" : "CLOSED")));
|
||||
}
|
||||
}
|
|
@ -1,19 +1,13 @@
|
|||
package com.lanternsoftware.zwave.context;
|
||||
|
||||
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
public class Globals implements ServletContextListener {
|
||||
public static ZWaveApp app;
|
||||
public static MongoCurrentMonitorDao cmDao;
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
cmDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
|
||||
app = new ZWaveApp();
|
||||
app.start();
|
||||
}
|
||||
|
@ -24,7 +18,5 @@ public class Globals implements ServletContextListener {
|
|||
app.stop();
|
||||
app = null;
|
||||
}
|
||||
if (cmDao != null)
|
||||
cmDao.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.lanternsoftware.zwave.context;
|
||||
|
||||
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
|
||||
import com.lanternsoftware.datamodel.zwave.Switch;
|
||||
import com.lanternsoftware.datamodel.zwave.SwitchSchedule;
|
||||
import com.lanternsoftware.datamodel.zwave.SwitchTransition;
|
||||
|
@ -9,7 +10,9 @@ import com.lanternsoftware.util.CollectionUtils;
|
|||
import com.lanternsoftware.util.DateUtils;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
import com.lanternsoftware.util.ResourceLoader;
|
||||
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
|
||||
import com.lanternsoftware.util.cryptography.AESTool;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||
import com.lanternsoftware.util.http.HttpPool;
|
||||
|
@ -19,6 +22,7 @@ import com.lanternsoftware.zwave.message.IMessageSubscriber;
|
|||
import com.lanternsoftware.zwave.message.MessageEngine;
|
||||
import com.lanternsoftware.zwave.message.impl.BinarySwitchReportRequest;
|
||||
import com.lanternsoftware.zwave.message.impl.BinarySwitchSetRequest;
|
||||
import com.lanternsoftware.zwave.message.impl.CRC16EncapRequest;
|
||||
import com.lanternsoftware.zwave.message.impl.MultilevelSensorGetRequest;
|
||||
import com.lanternsoftware.zwave.message.impl.MultilevelSensorReportRequest;
|
||||
import com.lanternsoftware.zwave.message.impl.MultilevelSwitchReportRequest;
|
||||
|
@ -27,7 +31,10 @@ import com.lanternsoftware.zwave.message.impl.ThermostatModeSetRequest;
|
|||
import com.lanternsoftware.zwave.message.impl.ThermostatSetPointReportRequest;
|
||||
import com.lanternsoftware.zwave.message.impl.ThermostatSetPointSetRequest;
|
||||
import com.lanternsoftware.zwave.message.thermostat.ThermostatSetPointIndex;
|
||||
import com.lanternsoftware.zwave.relay.RelayController;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -35,31 +42,57 @@ import java.util.Comparator;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class ZWaveApp {
|
||||
public static final AESTool aes = new AESTool(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "authKey.dat"));
|
||||
public static String authCode = aes.encryptToBase64(DaoSerializer.toZipBson(new AuthCode(100, null)));
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ZWaveApp.class);
|
||||
|
||||
private MongoZWaveDao dao;
|
||||
private ZWaveConfig config;
|
||||
private Controller controller;
|
||||
private RelayController relayController;
|
||||
private final Map<Integer, Switch> originalSwitches = new HashMap<>();
|
||||
private final Map<Integer, Switch> switches = new HashMap<>();
|
||||
private final Map<Integer, Switch> mySwitches = new HashMap<>();
|
||||
private final Map<Integer, List<Switch>> peers = new HashMap<>();
|
||||
private Timer timer;
|
||||
private HttpPool pool;
|
||||
private SwitchScheduleTask nextScheduleTask;
|
||||
private final Map<Integer, Double> temperatures = new HashMap<>();
|
||||
private final Map<Integer, Double> sensors = new HashMap<>();
|
||||
private final Object ZWAVE_MUTEX = new Object();
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
dao = new MongoZWaveDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
|
||||
controller = new Controller();
|
||||
controller.start("COM4");
|
||||
pool = new HttpPool(100, 20, 5000, 5000, 5000);
|
||||
config = DaoSerializer.parse(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "config.json"), ZWaveConfig.class);
|
||||
if (config == null) {
|
||||
dao = new MongoZWaveDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
|
||||
config = dao.getConfig(1);
|
||||
}
|
||||
if (NullUtils.isNotEmpty(config.getCommPort())) {
|
||||
controller = new Controller();
|
||||
controller.start(config.getCommPort());
|
||||
}
|
||||
if (!config.isMaster()) {
|
||||
HttpGet get = new HttpGet(config.getMasterUrl() + "/config");
|
||||
get.setHeader("auth_code", authCode);
|
||||
ZWaveConfig switchConfig = DaoSerializer.parse(pool.executeToString(get), ZWaveConfig.class);
|
||||
if (switchConfig != null) {
|
||||
config.setSwitches(switchConfig.getSwitches());
|
||||
}
|
||||
else {
|
||||
logger.error("Failed to retrieve switch config from master controller");
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
timer = new Timer("ZWaveApp Timer");
|
||||
pool = new HttpPool(10, 10, 30000, 10000, 10000);
|
||||
|
||||
//// for (int node = 3; node < 7; node++) {
|
||||
// session.doAction(new ConfigurationSetAction(node, (byte) 7, new byte[]{99}));
|
||||
|
@ -78,20 +111,26 @@ public class ZWaveApp {
|
|||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
config = dao.getConfig(1);
|
||||
Map<String, List<Switch>> groups = new HashMap<>();
|
||||
for (Switch sw : CollectionUtils.makeNotNull(config.getSwitches())) {
|
||||
for (Switch sw : config.getSwitches()) {
|
||||
switches.put(sw.getNodeId(), sw);
|
||||
originalSwitches.put(sw.getNodeId(), sw.duplicate());
|
||||
if (config.isMySwitch(sw))
|
||||
mySwitches.put(sw.getNodeId(), sw);
|
||||
CollectionUtils.addToMultiMap(sw.getRoom() + ":" + sw.getName(), sw, groups);
|
||||
}
|
||||
if (CollectionUtils.filterOne(config.getSwitches(), Switch::isUrlThermostat) != null) {
|
||||
if (CollectionUtils.anyQualify(mySwitches.values(), Switch::isThermometerUrlValid)) {
|
||||
timer.scheduleAtFixedRate(new ThermostatTask(), 0, 30000);
|
||||
}
|
||||
if (CollectionUtils.anyQualify(mySwitches.values(), Switch::isRelay)) {
|
||||
relayController = new RelayController();
|
||||
}
|
||||
for (List<Switch> group : groups.values()) {
|
||||
for (Switch sw : group) {
|
||||
peers.put(sw.getNodeId(), CollectionUtils.filter(group, _sw -> _sw.getNodeId() != sw.getNodeId()));
|
||||
}
|
||||
}
|
||||
System.out.println("My Switches:\n" + DaoSerializer.toJson(DaoSerializer.toDaoEntities(mySwitches.values())));
|
||||
scheduleNextTransition();
|
||||
|
||||
MessageEngine.subscribe(new IMessageSubscriber<MultilevelSensorReportRequest>() {
|
||||
|
@ -102,9 +141,9 @@ public class ZWaveApp {
|
|||
|
||||
@Override
|
||||
public void onMessage(MultilevelSensorReportRequest _message) {
|
||||
synchronized (temperatures) {
|
||||
temperatures.put((int) _message.getNodeId(), _message.getTemperatureCelsius());
|
||||
temperatures.notify();
|
||||
synchronized (sensors) {
|
||||
sensors.put((int) _message.getNodeId(), _message.getTemperatureCelsius());
|
||||
sensors.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -153,6 +192,18 @@ public class ZWaveApp {
|
|||
}
|
||||
});
|
||||
|
||||
MessageEngine.subscribe(new IMessageSubscriber<CRC16EncapRequest>() {
|
||||
@Override
|
||||
public Class<CRC16EncapRequest> getHandledMessageClass() {
|
||||
return CRC16EncapRequest.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(CRC16EncapRequest _message) {
|
||||
onSwitchLevelChange(_message.getNodeId(), _message.isOn()?0xFF:0);
|
||||
}
|
||||
});
|
||||
|
||||
// controller.send(new MultilevelSensorGetRequest((byte)11));
|
||||
// controller.send(new ThermostatSetPointGetRequest((byte)11, ThermostatSetPointIndex.HEATING));
|
||||
// controller.send(new ThermostatSetPointGetRequest((byte)11, ThermostatSetPointIndex.COOLING));
|
||||
|
@ -165,21 +216,22 @@ public class ZWaveApp {
|
|||
// controller.send(new ThermostatModeGetRequest((byte)11));
|
||||
}
|
||||
|
||||
private void onSwitchLevelChange(int _primaryNodeId, int _primaryLevel) {
|
||||
private void onSwitchLevelChange(int _secondaryNodeId, int _primaryLevel) {
|
||||
synchronized (switches) {
|
||||
Switch sw = switches.get(_primaryNodeId);
|
||||
if (sw != null) {
|
||||
Switch sw = switches.get(_secondaryNodeId);
|
||||
if ((sw != null) && !sw.isPrimary()) {
|
||||
int newLevel = sw.isMultilevel()?_primaryLevel:((_primaryLevel == 0)?0:99);
|
||||
sw.setLevel(newLevel);
|
||||
for (Switch peer : CollectionUtils.makeNotNull(peers.get(_primaryNodeId))) {
|
||||
logger.info("Mirror Event from node {} to node {}", _primaryNodeId, peer.getNodeId());
|
||||
if (peer.isMultilevel()) {
|
||||
peer.setLevel(newLevel);
|
||||
controller.send(new MultilevelSwitchSetRequest((byte)peer.getNodeId(), newLevel));
|
||||
}
|
||||
else {
|
||||
peer.setLevel(newLevel > 0?0xff:0);
|
||||
controller.send(new BinarySwitchSetRequest((byte)peer.getNodeId(), newLevel > 0));
|
||||
for (Switch peer : CollectionUtils.makeNotNull(peers.get(_secondaryNodeId))) {
|
||||
if (peer.isPrimary()) {
|
||||
logger.info("Mirror Event from node {} to node {}", _secondaryNodeId, peer.getNodeId());
|
||||
if (peer.isMultilevel()) {
|
||||
peer.setLevel(newLevel);
|
||||
controller.send(new MultilevelSwitchSetRequest((byte) peer.getNodeId(), newLevel));
|
||||
} else {
|
||||
peer.setLevel(newLevel > 0 ? 0xff : 0);
|
||||
controller.send(new BinarySwitchSetRequest((byte) peer.getNodeId(), newLevel > 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
persistConfig();
|
||||
|
@ -191,7 +243,7 @@ public class ZWaveApp {
|
|||
TimeZone tz = TimeZone.getTimeZone("America/Chicago");
|
||||
if (nextScheduleTask != null)
|
||||
nextScheduleTask.cancel();
|
||||
List<SwitchTransition> nextTransitions = CollectionUtils.getAllSmallest(CollectionUtils.aggregate(switches.values(), _s->CollectionUtils.transform(_s.getSchedule(), _t->_t.getNextTransition(_s, tz))), Comparator.comparing(SwitchTransition::getTransitionTime));
|
||||
List<SwitchTransition> nextTransitions = CollectionUtils.getAllSmallest(CollectionUtils.aggregate(mySwitches.values(), _s->CollectionUtils.transform(_s.getSchedule(), _t->_t.getNextTransition(_s, tz))), Comparator.comparing(SwitchTransition::getTransitionTime));
|
||||
if (!CollectionUtils.isEmpty(nextTransitions)) {
|
||||
for (SwitchTransition tr : nextTransitions) {
|
||||
logger.info("Next transition scheduled for node {} to level {} at {}", tr.getSwitch().getNodeId(), tr.getLevel(), DateUtils.format("hh:mm:ssa", tz, tr.getTransitionTime()));
|
||||
|
@ -203,24 +255,31 @@ public class ZWaveApp {
|
|||
}
|
||||
|
||||
public void setSwitchLevel(int _nodeId, int _level) {
|
||||
setSwitchLevel(_nodeId, _level, true);
|
||||
}
|
||||
|
||||
public void setSwitchLevel(int _nodeId, int _level, boolean _updatePeers) {
|
||||
Switch sw = switches.get(_nodeId);
|
||||
if ((sw == null) || !sw.isPrimary())
|
||||
return;
|
||||
sw.setLevel(_level);
|
||||
if (!sw.isThermostat()) {
|
||||
setGroupSwitchLevel(sw, _level);
|
||||
} else if (sw.isZWaveThermostat()) {
|
||||
controller.send(new ThermostatSetPointSetRequest((byte) sw.getNodeId(), sw.getThermostatMode() == ThermostatMode.COOL ? ThermostatSetPointIndex.COOLING : ThermostatSetPointIndex.HEATING, _level));
|
||||
} else {
|
||||
if (timer != null)
|
||||
timer.schedule(new ThermostatTask(), 0);
|
||||
persistConfig();
|
||||
if (config.isMySwitch(sw)) {
|
||||
if (sw.isSpaceHeaterThermostat()) {
|
||||
checkThermostat(sw);
|
||||
} else if (sw.isZWaveThermostat()) {
|
||||
controller.send(new ThermostatSetPointSetRequest((byte) sw.getNodeId(), sw.getThermostatMode() == ThermostatMode.COOL ? ThermostatSetPointIndex.COOLING : ThermostatSetPointIndex.HEATING, _level));
|
||||
} else if (sw.isRelay()) {
|
||||
relayController.setRelay(sw.getGpioPin(), sw.getLevel() > 0);
|
||||
} else {
|
||||
setGroupSwitchLevel(sw, _level);
|
||||
}
|
||||
}
|
||||
persistConfig(_updatePeers);
|
||||
}
|
||||
|
||||
public void setThermostatMode(int _nodeId, ThermostatMode _mode) {
|
||||
Switch sw = switches.get(_nodeId);
|
||||
if ((sw == null) || !sw.isPrimary() || !sw.isZWaveThermostat())
|
||||
if ((sw == null) || !sw.isPrimary() || !sw.isZWaveThermostat() || !config.isMySwitch(sw))
|
||||
return;
|
||||
controller.send(new ThermostatModeSetRequest((byte) sw.getNodeId(), com.lanternsoftware.zwave.message.thermostat.ThermostatMode.fromByte(_mode.data)));
|
||||
sw.setThermostatMode(_mode);
|
||||
|
@ -236,6 +295,12 @@ public class ZWaveApp {
|
|||
scheduleNextTransition();
|
||||
}
|
||||
|
||||
public void updateSwitch(Switch _sw) {
|
||||
switches.put(_sw.getNodeId(), _sw);
|
||||
mySwitches.put(_sw.getNodeId(), _sw);
|
||||
setSwitchLevel(_sw.getNodeId(), _sw.getLevel(), false);
|
||||
}
|
||||
|
||||
public void setSwitchHold(int _nodeId, boolean _hold) {
|
||||
Switch sw = switches.get(_nodeId);
|
||||
if ((sw == null) || !sw.isPrimary())
|
||||
|
@ -245,8 +310,37 @@ public class ZWaveApp {
|
|||
}
|
||||
|
||||
private void persistConfig() {
|
||||
persistConfig(true);
|
||||
}
|
||||
|
||||
private void persistConfig(boolean _updatePeers) {
|
||||
List<Switch> modified;
|
||||
synchronized (this) {
|
||||
dao.putConfig(config);
|
||||
modified = CollectionUtils.filter(switches.values(), _s->_s.isModified(originalSwitches.get(_s.getNodeId())));
|
||||
if (!modified.isEmpty()) {
|
||||
originalSwitches.clear();
|
||||
for (Switch s : switches.values()) {
|
||||
originalSwitches.put(s.getNodeId(), s.duplicate());
|
||||
}
|
||||
if (config.isMaster()) {
|
||||
if (dao != null)
|
||||
dao.putConfig(config);
|
||||
else
|
||||
ResourceLoader.writeFile(LanternFiles.OPS_PATH + "config.json", DaoSerializer.toJson(config));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_updatePeers) {
|
||||
Set<String> peers = CollectionUtils.transformToSet(modified, Switch::getControllerUrl);
|
||||
peers.remove(config.getUrl());
|
||||
for (String peer : peers) {
|
||||
for (Switch sw : modified) {
|
||||
HttpPost post = new HttpPost(peer + "/switch/" + sw.getNodeId());
|
||||
post.setHeader("auth_code", authCode);
|
||||
post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(sw)));
|
||||
pool.execute(post);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,6 +355,10 @@ public class ZWaveApp {
|
|||
|
||||
public void stop() {
|
||||
controller.stop();
|
||||
if (relayController != null) {
|
||||
relayController.shutdown();
|
||||
relayController = null;
|
||||
}
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
|
@ -276,11 +374,12 @@ public class ZWaveApp {
|
|||
}
|
||||
|
||||
private void setGroupSwitchLevel(Switch _primary, int _level) {
|
||||
if (_primary == null)
|
||||
if ((_primary == null) || !config.isMySwitch(_primary))
|
||||
return;
|
||||
List<Switch> nodes = CollectionUtils.asArrayList(_primary);
|
||||
nodes.addAll(CollectionUtils.makeNotNull(peers.get(_primary.getNodeId())));
|
||||
nodes.addAll(CollectionUtils.filter(peers.get(_primary.getNodeId()), _p->!_p.isPrimary()));
|
||||
for (Switch node : nodes) {
|
||||
logger.info("Setting {}, Node {} to {}", node.getName(), node.getNodeId(), _level);
|
||||
controller.send(node.isMultilevel() ? new MultilevelSwitchSetRequest((byte) node.getNodeId(), _level) : new BinarySwitchSetRequest((byte) node.getNodeId(), _level > 0));
|
||||
}
|
||||
}
|
||||
|
@ -288,24 +387,28 @@ public class ZWaveApp {
|
|||
private class ThermostatTask extends TimerTask {
|
||||
@Override
|
||||
public void run() {
|
||||
for (Switch sw : switches.values()) {
|
||||
try {
|
||||
if (sw.isUrlThermostat() && !sw.isThermometer()) {
|
||||
double tempF = getTemperatureCelsius(sw) * 1.8 + 32;
|
||||
if (tempF > sw.getLevel() + 0.4) {
|
||||
setGroupSwitchLevel(sw, 0);
|
||||
logger.info("Turning {} {} off, temp is: {} set to: {}", sw.getRoom(), sw.getName(), tempF + " set to: ", sw.getLevel());
|
||||
} else if (tempF < sw.getLevel() - 0.4) {
|
||||
setGroupSwitchLevel(sw, (byte) 0xf);
|
||||
logger.info("Turning {} {} on, temp is: {} set to: {}", sw.getRoom(), sw.getName(), tempF + " set to: ", sw.getLevel());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
logger.error("Failed to check temperature for thermostat {}", sw.getName());
|
||||
for (Switch sw : mySwitches.values()) {
|
||||
checkThermostat(sw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkThermostat(Switch _sw) {
|
||||
try {
|
||||
if (_sw.isSpaceHeaterThermostat()) {
|
||||
double tempF = getTemperatureCelsius(_sw) * 1.8 + 32;
|
||||
if (tempF > _sw.getLevel() + 0.4) {
|
||||
setGroupSwitchLevel(_sw, 0);
|
||||
logger.info("Turning {} {} off, temp is: {} set to: {}", _sw.getRoom(), _sw.getName(), tempF, _sw.getLevel());
|
||||
} else if (tempF < _sw.getLevel() - 0.4) {
|
||||
setGroupSwitchLevel(_sw, 255);
|
||||
logger.info("Turning {} {} on, temp is: {} set to: {}", _sw.getRoom(), _sw.getName(), tempF, _sw.getLevel());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
logger.error("Failed to check temperature for thermostat {}", _sw.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private class SwitchScheduleTask extends TimerTask {
|
||||
|
@ -318,12 +421,13 @@ public class ZWaveApp {
|
|||
@Override
|
||||
public void run() {
|
||||
for (SwitchTransition tr : transitions) {
|
||||
if (!tr.getSwitch().isHold()) {
|
||||
logger.info("Executing scheduled transition of node {} to level {}", tr.getSwitch().getNodeId(), tr.getLevel());
|
||||
Globals.app.setSwitchLevel(tr.getSwitch().getNodeId(), tr.getLevel());
|
||||
Switch sw = switches.get(tr.getSwitch().getNodeId());
|
||||
if (!sw.isHold()) {
|
||||
logger.info("Executing scheduled transition of node {} to level {}", sw.getNodeId(), tr.getLevel());
|
||||
Globals.app.setSwitchLevel(sw.getNodeId(), tr.getLevel());
|
||||
}
|
||||
else
|
||||
logger.info("Skipping scheduled transition of node {} to level {}, switch is on hold", tr.getSwitch().getNodeId(), tr.getLevel());
|
||||
logger.info("Skipping scheduled transition of node {} to level {}, switch is on hold", sw.getNodeId(), tr.getLevel());
|
||||
ConcurrencyUtils.sleep(100);
|
||||
}
|
||||
nextScheduleTask = null;
|
||||
|
@ -336,20 +440,20 @@ public class ZWaveApp {
|
|||
}
|
||||
|
||||
private double getTemperatureCelsius(Switch _sw) {
|
||||
if ((pool == null) || (_sw == null) || !(_sw.isThermometer() || _sw.isThermostat()))
|
||||
if ((pool == null) || (_sw == null))
|
||||
return 0.0;
|
||||
if (_sw.isUrlThermostat())
|
||||
return DaoSerializer.getDouble(DaoSerializer.parse(pool.executeToString(new HttpGet(_sw.getThermostatSource()))), "temp");
|
||||
else if (_sw.isZWaveThermostat()) {
|
||||
if (_sw.isThermometerUrlValid())
|
||||
return DaoSerializer.getDouble(DaoSerializer.parse(pool.executeToString(new HttpGet(_sw.getThermometerUrl()))), "temp");
|
||||
else if (_sw.isZWaveThermostat() && config.isMySwitch(_sw)) {
|
||||
synchronized (ZWAVE_MUTEX) {
|
||||
synchronized (temperatures) {
|
||||
synchronized (sensors) {
|
||||
controller.send(new MultilevelSensorGetRequest((byte) _sw.getNodeId()));
|
||||
try {
|
||||
temperatures.wait(5000);
|
||||
sensors.wait(3000);
|
||||
} catch (InterruptedException _e) {
|
||||
_e.printStackTrace();
|
||||
}
|
||||
Double temp = temperatures.get(_sw.getNodeId());
|
||||
Double temp = sensors.get(_sw.getNodeId());
|
||||
return (temp == null) ? 0.0 : temp;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,230 +0,0 @@
|
|||
package com.lanternsoftware.zwave.context;
|
||||
|
||||
public class ZWaveSpring {
|
||||
/* private ZWaveConfig config;
|
||||
private static ZWaveSession session;
|
||||
private static Map<Integer, Switch> switches = new HashMap<>();
|
||||
private static Map<Integer, List<Integer>> peers = new HashMap<>();
|
||||
private static Timer timer;
|
||||
private static HttpPool pool;
|
||||
private static SwitchScheduleTask nextScheduleTask;
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
// controller = new Controller();
|
||||
// controller.start("COM4");
|
||||
timer = new Timer("ZWaveApp Timer");
|
||||
pool = new HttpPool(10, 10, 30000, 10000, 10000);
|
||||
session = new LocalZwaveSession();
|
||||
session.connect();
|
||||
while (!session.isNetworkReady()) {
|
||||
System.out.println("Network not ready yet, sleeping");
|
||||
ConcurrencyUtils.sleep(1000);
|
||||
}
|
||||
// session.subscribe(new ZWaveEventListener());
|
||||
|
||||
// for (ZWaveNode node : session.getDeviceManager().getNodes()) {
|
||||
// for (CommandClass cc : node.getCommandClasses()) {
|
||||
// System.out.println(node.getNodeId() + " " + cc.getClassCode() + " " + cc.getLabel());
|
||||
// }
|
||||
// }
|
||||
|
||||
//// for (int node = 3; node < 7; node++) {
|
||||
// session.doAction(new ConfigurationSetAction(node, (byte) 7, new byte[]{99}));
|
||||
// ConcurrencyUtils.sleep(100);
|
||||
// session.doAction(new ConfigurationSetAction(node, (byte) 8, new byte[]{0, (byte) 1}));
|
||||
// ConcurrencyUtils.sleep(100);
|
||||
// session.doAction(new ConfigurationSetAction(node, (byte) 9, new byte[]{99}));
|
||||
// ConcurrencyUtils.sleep(100);
|
||||
// session.doAction(new ConfigurationSetAction(node, (byte) 10, new byte[]{0, (byte) 1}));
|
||||
// ConcurrencyUtils.sleep(100);
|
||||
// session.doAction(new ConfigurationSetAction(node, (byte) 11, new byte[]{99}));
|
||||
// ConcurrencyUtils.sleep(100);
|
||||
// session.doAction(new ConfigurationSetAction(node, (byte) 12, new byte[]{0, (byte) 1}));
|
||||
// ConcurrencyUtils.sleep(100);
|
||||
// }
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
config = SerializationEngine.deserialize(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "config.dat"), ZWaveConfig.class, SerializationEngine.SerializationType.JSON);
|
||||
Map<String, List<Integer>> groups = new HashMap<>();
|
||||
for (Switch sw : CollectionUtils.makeNotNull(config.getSwitches())) {
|
||||
switches.put(sw.getNodeId(), sw);
|
||||
CollectionUtils.addToMultiMap(sw.getRoom() + ":" + sw.getName(), sw.getNodeId(), groups);
|
||||
}
|
||||
if (CollectionUtils.filterOne(config.getSwitches(), _sw -> NullUtils.isNotEmpty(_sw.getThermostatSource())) != null) {
|
||||
timer.scheduleAtFixedRate(new ThermostatTask(), 0, 30000);
|
||||
}
|
||||
for (List<Integer> group : groups.values()) {
|
||||
for (Integer node : group) {
|
||||
peers.put(node, CollectionUtils.filter(group, _i -> !_i.equals(node)));
|
||||
}
|
||||
}
|
||||
scheduleNextTransition();
|
||||
}
|
||||
|
||||
public void scheduleNextTransition() {
|
||||
TimeZone tz = TimeZone.getTimeZone("America/Chicago");
|
||||
if (nextScheduleTask != null)
|
||||
nextScheduleTask.cancel();
|
||||
Switch next = null;
|
||||
SwitchTransition transition = null;
|
||||
Date transitionDate = null;
|
||||
for (Switch sw : switches.values()) {
|
||||
for (SwitchTransition t : CollectionUtils.makeNotNull(sw.getSchedule())) {
|
||||
Date nextTransition = t.getNextTransition(tz);
|
||||
if ((transitionDate == null) || nextTransition.before(transitionDate)) {
|
||||
transitionDate = nextTransition;
|
||||
transition = t;
|
||||
next = sw;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (transitionDate != null) {
|
||||
System.out.println("Next transition scheduled for node " + next.getNodeId() + " to level " + transition.getLevel() + " at " + DateUtils.format(tz, transitionDate, "hh:mm:ssa"));
|
||||
nextScheduleTask = new SwitchScheduleTask(next, transition);
|
||||
timer.schedule(nextScheduleTask, transitionDate);
|
||||
} else
|
||||
nextScheduleTask = null;
|
||||
}
|
||||
|
||||
public void setSwitchLevel(int _nodeId, int _level) {
|
||||
Switch sw = switches.get(_nodeId);
|
||||
if ((sw == null) || !sw.isPrimary())
|
||||
return;
|
||||
sw.setLevel(_level);
|
||||
if (NullUtils.isEmpty(sw.getThermostatSource())) {
|
||||
doGroupSwitchAction(_nodeId, _level, sw.isMultilevel());
|
||||
} else {
|
||||
if (timer != null)
|
||||
timer.schedule(new ThermostatTask(), 0);
|
||||
persistConfig();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setSwitchSchedule(int _nodeId, List<SwitchTransition> _transitions) {
|
||||
Switch sw = switches.get(_nodeId);
|
||||
if ((sw == null) || !sw.isPrimary())
|
||||
return;
|
||||
sw.setSchedule(_transitions);
|
||||
persistConfig();
|
||||
scheduleNextTransition();
|
||||
}
|
||||
|
||||
public void setSwitchHold(int _nodeId, boolean _hold) {
|
||||
Switch sw = switches.get(_nodeId);
|
||||
if ((sw == null) || !sw.isPrimary())
|
||||
return;
|
||||
sw.setHold(_hold);
|
||||
persistConfig();
|
||||
}
|
||||
|
||||
private void persistConfig() {
|
||||
synchronized (this) {
|
||||
ResourceLoader.writeFile(LanternFiles.OPS_PATH + "config.dat", SerializationEngine.serialize(config, SerializationEngine.SerializationType.JSON));
|
||||
}
|
||||
}
|
||||
|
||||
public int getSwitchLevel(int _nodeId) {
|
||||
Switch sw = switches.get(_nodeId);
|
||||
return (sw != null) ? sw.getLevel() : 0;
|
||||
}
|
||||
|
||||
public ZWaveConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
session.shutdown();
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
}
|
||||
if (pool != null) {
|
||||
pool.shutdown();
|
||||
pool = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public static class ZWaveEventListener implements EventHandler {
|
||||
@EventSubscribe
|
||||
public void receive(ZWaveEvent event) throws Exception {
|
||||
if (event instanceof ApplicationCommandEvent) {
|
||||
ApplicationCommandEvent ace = (ApplicationCommandEvent) event;
|
||||
if (ace.getCommandClass() == CommandClass.SWITCH_MULTILEVEL) {
|
||||
for (Integer node : CollectionUtils.makeNotNull(peers.get(ace.getNodeId()))) {
|
||||
Switch sw = switches.get(node);
|
||||
System.out.println("Mirror Event from node " + ((ApplicationCommandEvent) event).getNodeId() + " to node " + node);
|
||||
// session.doAction(new SwitchAction(node, ace.getPayload()[1], sw == null || sw.isMultilevel()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventSubscribe
|
||||
public void handleSensorEvent(DeviceSensorEvent sensorEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
private void doGroupSwitchAction(int _primary, int _level, boolean _multilevel) {
|
||||
List<Integer> nodes = CollectionUtils.asArrayList(_primary);
|
||||
nodes.addAll(CollectionUtils.makeNotNull(peers.get(_primary)));
|
||||
for (int node : nodes) {
|
||||
try {
|
||||
session.doAction(new SwitchAction(node, _level, _multilevel));
|
||||
} catch (HomeAutomationException _e) {
|
||||
_e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ThermostatTask extends TimerTask {
|
||||
@Override
|
||||
public void run() {
|
||||
for (Switch sw : switches.values()) {
|
||||
if (NullUtils.isNotEmpty(sw.getThermostatSource())) {
|
||||
double tempF = getTemperatureCelsius(sw) * 1.8 + 32;
|
||||
if (tempF > sw.getLevel() + 0.4) {
|
||||
doGroupSwitchAction(sw.getNodeId(), 0, false);
|
||||
System.out.println("Turning " + sw.getRoom() + " " + sw.getName() + " off, temp is: " + tempF + " set to: " + sw.getLevel());
|
||||
} else if (tempF < sw.getLevel() - 0.4) {
|
||||
doGroupSwitchAction(sw.getNodeId(), (byte) 0xf, false);
|
||||
System.out.println("Turning " + sw.getRoom() + " " + sw.getName() + " on, temp is: " + tempF + " set to: " + sw.getLevel());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SwitchScheduleTask extends TimerTask {
|
||||
private final Switch sw;
|
||||
private final SwitchTransition transition;
|
||||
|
||||
public SwitchScheduleTask(Switch _sw, SwitchTransition _transition) {
|
||||
sw = _sw;
|
||||
transition = _transition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("Executing scheduled transition of node " + sw.getNodeId() + " to level " + transition.getLevel());
|
||||
if (!sw.isHold()) {
|
||||
Globals.app.setSwitchLevel(sw.getNodeId(), transition.getLevel());
|
||||
}
|
||||
nextScheduleTask = null;
|
||||
Globals.app.scheduleNextTransition();
|
||||
}
|
||||
}
|
||||
|
||||
public double getTemperatureCelsius(int _nodeId) {
|
||||
return getTemperatureCelsius(switches.get(_nodeId));
|
||||
}
|
||||
|
||||
private static double getTemperatureCelsius(Switch _sw) {
|
||||
if ((pool == null) || (_sw == null) || NullUtils.isEmpty(_sw.getThermostatSource()))
|
||||
return 0.0;
|
||||
return BsonUtils.getDouble(BsonUtils.parse(pool.executeToString(new HttpGet(_sw.getThermostatSource()))), "temp");
|
||||
}*/
|
||||
}
|
|
@ -12,6 +12,7 @@ public class MongoZWaveDao implements ZWaveDao {
|
|||
proxy = new MongoProxy(_config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
proxy.shutdown();
|
||||
}
|
||||
|
|
|
@ -5,4 +5,5 @@ import com.lanternsoftware.datamodel.zwave.ZWaveConfig;
|
|||
public interface ZWaveDao {
|
||||
void putConfig(ZWaveConfig _config);
|
||||
ZWaveConfig getConfig(int _accountId);
|
||||
void shutdown();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package com.lanternsoftware.zwave.relay;
|
||||
|
||||
import com.pi4j.io.gpio.GpioFactory;
|
||||
import com.pi4j.io.gpio.GpioPinDigitalOutput;
|
||||
import com.pi4j.io.gpio.PinState;
|
||||
import com.pi4j.io.gpio.RaspiPin;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class RelayController {
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(RelayController.class);
|
||||
|
||||
private final Map<Integer, GpioPinDigitalOutput> pins = new HashMap<>();
|
||||
|
||||
public void setRelay(int _pin, boolean _on) {
|
||||
GpioPinDigitalOutput pin = pins.get(_pin);
|
||||
if (pin == null) {
|
||||
pin = GpioFactory.getInstance().provisionDigitalOutputPin(RaspiPin.getPinByAddress(_pin), "Relay", PinState.LOW);
|
||||
if (pin != null)
|
||||
pins.put(_pin, pin);
|
||||
else {
|
||||
LOG.error("Failed to get pin {}", _pin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_on)
|
||||
pin.high();
|
||||
else
|
||||
pin.low();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
GpioFactory.getInstance().shutdown();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.lanternsoftware.zwave.security;
|
||||
|
||||
import com.lanternsoftware.datamodel.zwave.Switch;
|
||||
import com.pi4j.io.gpio.GpioFactory;
|
||||
import com.pi4j.io.gpio.GpioPinDigitalInput;
|
||||
import com.pi4j.io.gpio.PinPullResistance;
|
||||
import com.pi4j.io.gpio.RaspiPin;
|
||||
import com.pi4j.io.gpio.event.GpioPinListenerDigital;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SecurityController {
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(SecurityController.class);
|
||||
|
||||
private final Map<Integer, GpioPinDigitalInput> pins = new HashMap<>();
|
||||
|
||||
public boolean isOpen(int _pin) {
|
||||
GpioPinDigitalInput pin = getPin(_pin);
|
||||
return (pin == null) || pin.getState().isHigh();
|
||||
}
|
||||
|
||||
public void listen(Switch _sw, SecurityListener _listener) {
|
||||
GpioPinDigitalInput pin = getPin(_sw.getGpioPin());
|
||||
if (pin != null)
|
||||
pin.addListener((GpioPinListenerDigital) _event -> _listener.onStateChanged(_sw.getNodeId(), _event.getState().isHigh()));
|
||||
}
|
||||
|
||||
private GpioPinDigitalInput getPin(int _pin) {
|
||||
GpioPinDigitalInput pin = pins.get(_pin);
|
||||
if (pin == null) {
|
||||
pin = GpioFactory.getInstance().provisionDigitalInputPin(RaspiPin.getPinByAddress(_pin), "SecuritySensor", PinPullResistance.PULL_UP);
|
||||
if (pin != null)
|
||||
pins.put(_pin, pin);
|
||||
else {
|
||||
LOG.error("Failed to get pin {}", _pin);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return pin;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
for (GpioPinDigitalInput pin : pins.values()) {
|
||||
pin.removeAllListeners();
|
||||
}
|
||||
pins.clear();
|
||||
GpioFactory.getInstance().shutdown();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.lanternsoftware.zwave.security;
|
||||
|
||||
public interface SecurityListener {
|
||||
void onStateChanged(int nodeId, boolean _open);
|
||||
}
|
|
@ -1,16 +1,22 @@
|
|||
package com.lanternsoftware.zwave.servlet;
|
||||
|
||||
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.ResourceLoader;
|
||||
import com.lanternsoftware.util.cryptography.AESTool;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
import com.lanternsoftware.zwave.context.Globals;
|
||||
import com.lanternsoftware.zwave.context.ZWaveApp;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public abstract class SecureServlet extends ZWaveServlet {
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) {
|
||||
AuthCode authCode = Globals.cmDao.decryptAuthCode(_req.getHeader("auth_code"));
|
||||
if ((authCode == null) || (authCode.getAccountId() != 1)) {
|
||||
AuthCode authCode = DaoSerializer.fromZipBson(ZWaveApp.aes.decryptFromBase64(_req.getHeader("auth_code")), AuthCode.class);
|
||||
if ((authCode == null) || (authCode.getAccountId() != 100)) {
|
||||
_rep.setStatus(401);
|
||||
return;
|
||||
}
|
||||
|
@ -22,8 +28,8 @@ public abstract class SecureServlet extends ZWaveServlet {
|
|||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) {
|
||||
AuthCode authCode = Globals.cmDao.decryptAuthCode(_req.getHeader("auth_code"));
|
||||
if ((authCode == null) || (authCode.getAccountId() != 1)) {
|
||||
AuthCode authCode = DaoSerializer.fromZipBson(ZWaveApp.aes.decryptFromBase64(_req.getHeader("auth_code")), AuthCode.class);
|
||||
if ((authCode == null) || (authCode.getAccountId() != 100)) {
|
||||
_rep.setStatus(401);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.lanternsoftware.zwave.servlet;
|
||||
|
||||
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
|
||||
import com.lanternsoftware.datamodel.zwave.Switch;
|
||||
import com.lanternsoftware.datamodel.zwave.SwitchSchedule;
|
||||
import com.lanternsoftware.datamodel.zwave.ThermostatMode;
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
|
@ -49,5 +50,8 @@ public class SwitchServlet extends SecureServlet {
|
|||
Globals.app.setSwitchSchedule(nodeId, transitions);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Globals.app.updateSwitch(getRequestPayload(_req, Switch.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,9 @@ public abstract class ZWaveServlet extends HttpServlet {
|
|||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
protected <T> T getRequestPayload(HttpServletRequest _req, Class<T> _retClass) {
|
||||
return DaoSerializer.fromZipBson(getRequestPayload(_req), _retClass);
|
||||
}
|
||||
|
||||
protected String[] path(HttpServletRequest _req) {
|
||||
return NullUtils.cleanSplit(NullUtils.makeNotNull(_req.getPathInfo()), "/");
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<dependency>
|
||||
<groupId>com.neuronrobotics</groupId>
|
||||
<artifactId>nrjavaserial</artifactId>
|
||||
<version>3.15.0</version>
|
||||
<version>5.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
|
@ -60,6 +60,46 @@
|
|||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<finalName>lantern-zwave</finalName>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<manifestEntries>
|
||||
<Main-Class>com.lanternsoftware.zwave.PortEnum</Main-Class>
|
||||
<Specification-Title>Lantern ZWave</Specification-Title>
|
||||
<Specification-Version>${project.version}</Specification-Version>
|
||||
<Specification-Vendor>Lantern Software, Inc.</Specification-Vendor>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package com.lanternsoftware.zwave;
|
||||
|
||||
import gnu.io.CommPortIdentifier;
|
||||
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class PortEnum {
|
||||
public static void main(String[] args) {
|
||||
Enumeration<CommPortIdentifier> e = CommPortIdentifier.getPortIdentifiers();
|
||||
while (e.hasMoreElements()) {
|
||||
CommPortIdentifier id = e.nextElement();
|
||||
if (id != null) {
|
||||
System.out.println(id.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -70,7 +70,7 @@ public enum CommandClass {
|
|||
TIME_PARAMETERS((byte)0x8B, "TIME_PARAMETERS"),
|
||||
GEOGRAPHIC_LOCATION((byte)0x8C, "GEOGRAPHIC_LOCATION"),
|
||||
COMPOSITE((byte)0x8D, "COMPOSITE"),
|
||||
MULTI_INSTANCE_ASSOCIATION((byte)0x8E, "MULTI_INSTANCE_ASSOCIATION"),
|
||||
MULTI_CHANNEL_ASSOCIATION((byte)0x8E, "MULTI_CHANNEL_ASSOCIATION"),
|
||||
MULTI_CMD((byte)0x8F, "MULTI_CMD"),
|
||||
ENERGY_PRODUCTION((byte)0x90, "ENERGY_PRODUCTION"),
|
||||
MANUFACTURER_PROPRIETARY((byte)0x91, "MANUFACTURER_PROPRIETARY"),
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
|
||||
public class AssociationGetRequest extends RequestMessage {
|
||||
public AssociationGetRequest() {
|
||||
this((byte)0);
|
||||
}
|
||||
|
||||
public AssociationGetRequest(byte _nodeId) {
|
||||
super(_nodeId, ControllerMessageType.SendData, CommandClass.ASSOCIATION, (byte)0x02);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
|
||||
public class AssociationReportRequest extends RequestMessage {
|
||||
private byte groupIdx;
|
||||
private byte maxAssociations;
|
||||
private byte numReportsToFollow;
|
||||
|
||||
public AssociationReportRequest() {
|
||||
this((byte) 0);
|
||||
}
|
||||
|
||||
public AssociationReportRequest(byte _nodeId) {
|
||||
super(_nodeId, ControllerMessageType.ApplicationCommandHandler, CommandClass.ASSOCIATION, (byte) 0x03);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
nodeId = _payload[5];
|
||||
groupIdx = _payload[8];
|
||||
maxAssociations = _payload[9];
|
||||
numReportsToFollow = _payload[10];
|
||||
}
|
||||
|
||||
public byte getGroupIdx() {
|
||||
return groupIdx;
|
||||
}
|
||||
|
||||
public void setGroupIdx(byte _groupIdx) {
|
||||
groupIdx = _groupIdx;
|
||||
}
|
||||
|
||||
public byte getMaxAssociations() {
|
||||
return maxAssociations;
|
||||
}
|
||||
|
||||
public void setMaxAssociations(byte _maxAssociations) {
|
||||
maxAssociations = _maxAssociations;
|
||||
}
|
||||
|
||||
public byte getNumReportsToFollow() {
|
||||
return numReportsToFollow;
|
||||
}
|
||||
|
||||
public void setNumReportsToFollow(byte _numReportsToFollow) {
|
||||
numReportsToFollow = _numReportsToFollow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.SendDataRequestMessage;
|
||||
|
||||
public class AssociationSetRequest extends SendDataRequestMessage {
|
||||
private byte groupIdx;
|
||||
private byte targetNodeId;
|
||||
|
||||
public AssociationSetRequest() {
|
||||
this((byte)0, (byte)0, (byte)0);
|
||||
}
|
||||
|
||||
public AssociationSetRequest(byte _nodeId, byte _groupIdx, byte _targetNodeId) {
|
||||
super(_nodeId, CommandClass.ASSOCIATION, (byte) 0x01);
|
||||
groupIdx = _groupIdx;
|
||||
targetNodeId = _targetNodeId;
|
||||
}
|
||||
|
||||
public byte getGroupIdx() {
|
||||
return groupIdx;
|
||||
}
|
||||
|
||||
public void setGroupIdx(byte _groupIdx) {
|
||||
groupIdx = _groupIdx;
|
||||
}
|
||||
|
||||
public byte getTargetNodeId() {
|
||||
return targetNodeId;
|
||||
}
|
||||
|
||||
public void setTargetNodeId(byte _targetNodeId) {
|
||||
targetNodeId = _targetNodeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
return asByteArray(groupIdx, targetNodeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId + " groupIdx: " + groupIdx + " targetNodeIdx: " + targetNodeId;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
com.lanternsoftware.zwave.message.impl.ApplicationUpdateRequest
|
||||
com.lanternsoftware.zwave.message.impl.AssociationGetRequest
|
||||
com.lanternsoftware.zwave.message.impl.AssociationReportRequest
|
||||
com.lanternsoftware.zwave.message.impl.BinarySwitchSetRequest
|
||||
com.lanternsoftware.zwave.message.impl.BinarySwitchReportRequest
|
||||
com.lanternsoftware.zwave.message.impl.ByteMessage
|
||||
|
|
Loading…
Reference in New Issue
Block a user