Add support for adding and removing zwave nodes via software. Support secondary z-wave controllers.

This commit is contained in:
MarkBryanMilligan 2021-10-28 21:45:56 -05:00
parent 88933a2286
commit f5066c541f
28 changed files with 346 additions and 87 deletions

View File

@ -1,15 +1,28 @@
package com.lanternsoftware.currentmonitor; package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Breaker;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.LanternFiles; import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.ResourceLoader; import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.dao.DaoSerializer;
public class CreateConfig { public class CreateConfig {
public static void main(String[] args) { public static void main(String[] args) {
// MonitorConfig c = new MonitorConfig(0, "https://mark.lanternsoftware.com/currentmonitor"); MonitorConfig c = new MonitorConfig(1, "https://lanternsoftware.com/currentmonitor");
MonitorConfig c = new MonitorConfig(1, "https://mark.lanternsoftware.com/currentmonitor"); c.setHost("");
c.setDebug(true); c.setDebug(false);
ResourceLoader.writeFile(LanternFiles.OPS_PATH + "hub1.json", DaoSerializer.toJson(c)); c.setMqttBrokerUrl("http://192.168.1.80:1883");
c.setMqttFrequency(60);
c.setMqttPortCalibrationFactor(1.0);
c.setMqttVoltageCalibrationFactor(1.0);
Breaker b1 = new Breaker();
b1.setPanel(0);
b1.setSpace(1);
b1.setHub(0);
b1.setPort(1);
b1.setSizeAmps(20);
c.setMqttBreakers(CollectionUtils.asArrayList(b1));
ResourceLoader.writeFile(LanternFiles.OPS_PATH + "mqtt1.json", DaoSerializer.toJson(c));
} }
} }

View File

@ -12,13 +12,14 @@ import com.lanternsoftware.util.servlet.LanternServlet;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
@WebServlet("/signup") @WebServlet("/signup")
public class SignupServlet extends LanternServlet { public class SignupServlet extends LanternServlet {
@Override @Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) {
BasicAuth auth = new BasicAuth(_req); BasicAuth auth = new BasicAuth(_req);
Account acct = Globals.dao.getAccountByUsername(auth.getUsername()); Account acct = Globals.dao.getAccountByUsername(auth.getUsername().toLowerCase().trim());
if (acct != null) { if (acct != null) {
jsonResponse(_rep, SignupResponse.error("An account for " + auth.getUsername() + " already exists")); jsonResponse(_rep, SignupResponse.error("An account for " + auth.getUsername() + " already exists"));
return; return;

View File

@ -21,7 +21,7 @@ public class Switch {
private boolean hold; private boolean hold;
private boolean hidden; private boolean hidden;
private boolean suppressEvents; private boolean suppressEvents;
private String thermometerUrl; private String sourceUrl;
private String controllerUrl; private String controllerUrl;
private ThermostatMode thermostatMode; private ThermostatMode thermostatMode;
private int lowLevel; private int lowLevel;
@ -30,18 +30,18 @@ public class Switch {
public Switch() { public Switch() {
} }
public Switch(String _room, String _name, int _nodeId, boolean _primary, boolean _multilevel, String _thermometerUrl, int _lowLevel) { public Switch(String _room, String _name, int _nodeId, boolean _primary, boolean _multilevel, String _sourceUrl, int _lowLevel) {
this(_room, _name, _nodeId, 0, _primary, false, _thermometerUrl, _lowLevel, null); this(_room, _name, _nodeId, 0, _primary, false, _sourceUrl, _lowLevel, null);
} }
public Switch(String _room, String _name, int _nodeId, int _level, boolean _primary, boolean _hold, String _thermometerUrl, int _lowLevel, List<SwitchSchedule> _schedule) { public Switch(String _room, String _name, int _nodeId, int _level, boolean _primary, boolean _hold, String _sourceUrl, int _lowLevel, List<SwitchSchedule> _schedule) {
room = _room; room = _room;
name = _name; name = _name;
nodeId = _nodeId; nodeId = _nodeId;
level = _level; level = _level;
primary = _primary; primary = _primary;
hold = _hold; hold = _hold;
thermometerUrl = _thermometerUrl; sourceUrl = _sourceUrl;
lowLevel = _lowLevel; lowLevel = _lowLevel;
schedule = _schedule; schedule = _schedule;
} }
@ -128,12 +128,12 @@ public class Switch {
hold = _hold; hold = _hold;
} }
public String getThermometerUrl() { public String getSourceUrl() {
return thermometerUrl; return sourceUrl;
} }
public void setThermometerUrl(String _thermometerUrl) { public void setSourceUrl(String _sourceUrl) {
thermometerUrl = _thermometerUrl; sourceUrl = _sourceUrl;
} }
public String getControllerUrl() { public String getControllerUrl() {
@ -152,8 +152,8 @@ public class Switch {
return type == SwitchType.SPACE_HEATER_THERMOSTAT; return type == SwitchType.SPACE_HEATER_THERMOSTAT;
} }
public boolean isThermometerUrlValid() { public boolean isSourceUrlValid() {
return NullUtils.makeNotNull(thermometerUrl).startsWith("http"); return NullUtils.makeNotNull(sourceUrl).startsWith("http");
} }
public boolean isZWaveThermostat() { public boolean isZWaveThermostat() {
@ -244,7 +244,7 @@ public class Switch {
s.setPrimary(isPrimary()); s.setPrimary(isPrimary());
s.setHold(isHold()); s.setHold(isHold());
s.setHidden(isHidden()); s.setHidden(isHidden());
s.setThermometerUrl(getThermometerUrl()); s.setSourceUrl(getSourceUrl());
s.setControllerUrl(getControllerUrl()); s.setControllerUrl(getControllerUrl());
s.setThermostatMode(getThermostatMode()); s.setThermostatMode(getThermostatMode());
s.setLowLevel(getLowLevel()); s.setLowLevel(getLowLevel());

View File

@ -8,5 +8,6 @@ public enum SwitchType {
THERMOMETER, THERMOMETER,
RELAY, RELAY,
SECURITY, SECURITY,
RELAY_BUTTON RELAY_BUTTON,
CO2_SENSOR
} }

View File

@ -5,7 +5,9 @@ import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.annotations.DBSerializable; import com.lanternsoftware.util.dao.annotations.DBSerializable;
import com.lanternsoftware.util.dao.annotations.PrimaryKey; import com.lanternsoftware.util.dao.annotations.PrimaryKey;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.TreeSet;
@DBSerializable(autogen = false) @DBSerializable(autogen = false)
public class ZWaveConfig { public class ZWaveConfig {
@ -75,4 +77,10 @@ public class ZWaveConfig {
public boolean isMySwitch(Switch _sw) { public boolean isMySwitch(Switch _sw) {
return (isMaster() && NullUtils.isEmpty(_sw.getControllerUrl())) || _sw.isControlledBy(getUrl()); return (isMaster() && NullUtils.isEmpty(_sw.getControllerUrl())) || _sw.isControlledBy(getUrl());
} }
public List<String> getControllers() {
TreeSet<String> controllers = new TreeSet<>(CollectionUtils.filter(CollectionUtils.transform(switches, Switch::getControllerUrl), NullUtils::isNotEmpty));
controllers.add(masterUrl);
return new ArrayList<>(controllers);
}
} }

View File

@ -39,7 +39,7 @@ public class SwitchSerializer extends AbstractDaoSerializer<Switch>
d.put("hold", _o.isHold()); d.put("hold", _o.isHold());
d.put("hidden", _o.isHidden()); d.put("hidden", _o.isHidden());
d.put("suppress_events", _o.isSuppressEvents()); d.put("suppress_events", _o.isSuppressEvents());
d.put("thermometer_url", _o.getThermometerUrl()); d.put("source_url", _o.getSourceUrl());
d.put("controller_url", _o.getControllerUrl()); d.put("controller_url", _o.getControllerUrl());
d.put("thermostat_mode", DaoSerializer.toEnumName(_o.getThermostatMode())); d.put("thermostat_mode", DaoSerializer.toEnumName(_o.getThermostatMode()));
d.put("low_level", _o.getLowLevel()); d.put("low_level", _o.getLowLevel());
@ -62,7 +62,7 @@ public class SwitchSerializer extends AbstractDaoSerializer<Switch>
o.setHold(DaoSerializer.getBoolean(_d, "hold")); o.setHold(DaoSerializer.getBoolean(_d, "hold"));
o.setHidden(DaoSerializer.getBoolean(_d, "hidden")); o.setHidden(DaoSerializer.getBoolean(_d, "hidden"));
o.setSuppressEvents(DaoSerializer.getBoolean(_d, "suppress_events")); o.setSuppressEvents(DaoSerializer.getBoolean(_d, "suppress_events"));
o.setThermometerUrl(DaoSerializer.getString(_d, "thermometer_url")); o.setSourceUrl(DaoSerializer.getString(_d, "source_url"));
o.setControllerUrl(DaoSerializer.getString(_d, "controller_url")); o.setControllerUrl(DaoSerializer.getString(_d, "controller_url"));
o.setThermostatMode(DaoSerializer.getEnum(_d, "thermostat_mode", ThermostatMode.class)); o.setThermostatMode(DaoSerializer.getEnum(_d, "thermostat_mode", ThermostatMode.class));
o.setLowLevel(DaoSerializer.getInteger(_d, "low_level")); o.setLowLevel(DaoSerializer.getInteger(_d, "low_level"));

View File

@ -33,11 +33,7 @@
<groupId>com.neuronrobotics</groupId> <groupId>com.neuronrobotics</groupId>
<artifactId>nrjavaserial</artifactId> <artifactId>nrjavaserial</artifactId>
<version>5.2.1</version> <version>5.2.1</version>
</dependency> <scope>provided</scope>
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.5.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.hid4java</groupId> <groupId>org.hid4java</groupId>

View File

@ -28,11 +28,11 @@ public class MHZ19BCO2Sensor implements ICO2Sensor {
private InputStream is; private InputStream is;
private OutputStream os; private OutputStream os;
private MHZ19BCO2Sensor(String _port) { public MHZ19BCO2Sensor(String _port) {
this(_port, DEFAULT_TIMEOUT); this(_port, DEFAULT_TIMEOUT);
} }
private MHZ19BCO2Sensor(String _port, int _timeout) { public MHZ19BCO2Sensor(String _port, int _timeout) {
try { try {
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(_port); CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(_port);
serialPort = portIdentifier.open("co2port", 2000); serialPort = portIdentifier.open("co2port", 2000);

View File

@ -0,0 +1,16 @@
package com.lanternsoftware.thermometer.config;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
@DBSerializable
public class EnvironmentConfig {
private String co2serialPort;
public String getCo2serialPort() {
return co2serialPort;
}
public void setCo2serialPort(String _co2serialPort) {
co2serialPort = _co2serialPort;
}
}

View File

@ -0,0 +1,39 @@
package com.lanternsoftware.thermometer.config.dao;
import com.lanternsoftware.thermometer.config.EnvironmentConfig;
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;
public class EnvironmentConfigSerializer extends AbstractDaoSerializer<EnvironmentConfig>
{
@Override
public Class<EnvironmentConfig> getSupportedClass()
{
return EnvironmentConfig.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(EnvironmentConfig _o)
{
DaoEntity d = new DaoEntity();
d.put("co2serial_port", _o.getCo2serialPort());
return d;
}
@Override
public EnvironmentConfig fromDaoEntity(DaoEntity _d)
{
EnvironmentConfig o = new EnvironmentConfig();
o.setCo2serialPort(DaoSerializer.getString(_d, "co2serial_port"));
return o;
}
}

View File

@ -2,7 +2,14 @@ package com.lanternsoftware.thermometer.context;
import com.lanternsoftware.thermometer.DS18B20Thermometer; import com.lanternsoftware.thermometer.DS18B20Thermometer;
import com.lanternsoftware.thermometer.HidThermometer; import com.lanternsoftware.thermometer.HidThermometer;
import com.lanternsoftware.thermometer.ICO2Sensor;
import com.lanternsoftware.thermometer.IThermometer; import com.lanternsoftware.thermometer.IThermometer;
import com.lanternsoftware.thermometer.MHZ19BCO2Sensor;
import com.lanternsoftware.thermometer.config.EnvironmentConfig;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.dao.DaoSerializer;
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener; import javax.servlet.ServletContextListener;
@ -10,18 +17,25 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class Globals implements ServletContextListener { public class Globals implements ServletContextListener {
private static EnvironmentConfig config;
public static List<IThermometer> thermometers = new ArrayList<>(); public static List<IThermometer> thermometers = new ArrayList<>();
public static ICO2Sensor co2Sensor;
@Override @Override
public void contextInitialized(ServletContextEvent sce) { public void contextInitialized(ServletContextEvent sce) {
config = DaoSerializer.parse(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "environment.json"), EnvironmentConfig.class);
IThermometer t = new HidThermometer(); IThermometer t = new HidThermometer();
if (t.isConnected()) if (t.isConnected())
thermometers.add(t); thermometers.add(t);
thermometers.addAll(DS18B20Thermometer.devices()); thermometers.addAll(DS18B20Thermometer.devices());
if ((config != null) && NullUtils.isNotEmpty(config.getCo2serialPort()))
co2Sensor = new MHZ19BCO2Sensor(config.getCo2serialPort());
} }
@Override @Override
public void contextDestroyed(ServletContextEvent sce) { public void contextDestroyed(ServletContextEvent sce) {
thermometers.forEach(IThermometer::shutdown); thermometers.forEach(IThermometer::shutdown);
if (co2Sensor != null)
co2Sensor.shutdown();
} }
} }

View File

@ -0,0 +1,19 @@
package com.lanternsoftware.thermometer.servlet;
import com.lanternsoftware.thermometer.context.Globals;
import com.lanternsoftware.util.servlet.LanternServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/co2")
public class CO2Servlet extends LanternServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
if (Globals.co2Sensor == null)
resp.setStatus(404);
else
setResponseEntity(resp, "application/json", "{\"ppm\": "+ Globals.co2Sensor.getPPM() + "}");
}
}

View File

@ -1,45 +0,0 @@
package com.lanternsoftware.thermometer.servlet;
import com.lanternsoftware.util.NullUtils;
import org.apache.commons.io.IOUtils;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
public abstract class ThermoServlet extends HttpServlet {
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) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(os);
}
}
}

View File

@ -0,0 +1 @@
com.lanternsoftware.thermometer.config.dao.EnvironmentConfigSerializer

View File

@ -11,7 +11,7 @@
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/opt/tomcat/log/thermo.txt</file> <file>/opt/tomcat/log/thermo.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/opt/currentmonitor/log/thermo.%d{yyyy-MM-dd}.%i.txt</fileNamePattern> <fileNamePattern>/opt/tomcat/log/thermo.%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>20MB</maxFileSize> <maxFileSize>20MB</maxFileSize>
<maxHistory>20</maxHistory> <maxHistory>20</maxHistory>
</rollingPolicy> </rollingPolicy>

View File

@ -0,0 +1,10 @@
package com.lanternsoftware.thermometer;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.generator.DaoSerializerGenerator;
public class GenerateEnvironmentSerializers {
public static void main(String[] args) {
DaoSerializerGenerator.generateSerializers(LanternFiles.SOURCE_PATH, true, null);
}
}

View File

@ -22,7 +22,8 @@ import com.lanternsoftware.zwave.controller.Controller;
import com.lanternsoftware.zwave.dao.MongoZWaveDao; import com.lanternsoftware.zwave.dao.MongoZWaveDao;
import com.lanternsoftware.zwave.message.IMessageSubscriber; import com.lanternsoftware.zwave.message.IMessageSubscriber;
import com.lanternsoftware.zwave.message.MessageEngine; import com.lanternsoftware.zwave.message.MessageEngine;
import com.lanternsoftware.zwave.message.impl.AddNodeToNetworkRequest; import com.lanternsoftware.zwave.message.impl.AddNodeToNetworkStartRequest;
import com.lanternsoftware.zwave.message.impl.AddNodeToNetworkStopRequest;
import com.lanternsoftware.zwave.message.impl.BinarySwitchReportRequest; import com.lanternsoftware.zwave.message.impl.BinarySwitchReportRequest;
import com.lanternsoftware.zwave.message.impl.BinarySwitchSetRequest; import com.lanternsoftware.zwave.message.impl.BinarySwitchSetRequest;
import com.lanternsoftware.zwave.message.impl.CRC16EncapRequest; import com.lanternsoftware.zwave.message.impl.CRC16EncapRequest;
@ -30,6 +31,8 @@ import com.lanternsoftware.zwave.message.impl.MultilevelSensorGetRequest;
import com.lanternsoftware.zwave.message.impl.MultilevelSensorReportRequest; import com.lanternsoftware.zwave.message.impl.MultilevelSensorReportRequest;
import com.lanternsoftware.zwave.message.impl.MultilevelSwitchReportRequest; import com.lanternsoftware.zwave.message.impl.MultilevelSwitchReportRequest;
import com.lanternsoftware.zwave.message.impl.MultilevelSwitchSetRequest; import com.lanternsoftware.zwave.message.impl.MultilevelSwitchSetRequest;
import com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStartRequest;
import com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStopRequest;
import com.lanternsoftware.zwave.message.impl.ThermostatModeSetRequest; import com.lanternsoftware.zwave.message.impl.ThermostatModeSetRequest;
import com.lanternsoftware.zwave.message.impl.ThermostatSetPointReportRequest; import com.lanternsoftware.zwave.message.impl.ThermostatSetPointReportRequest;
import com.lanternsoftware.zwave.message.impl.ThermostatSetPointSetRequest; import com.lanternsoftware.zwave.message.impl.ThermostatSetPointSetRequest;
@ -127,10 +130,10 @@ public class ZWaveApp {
mySwitches.put(sw.getNodeId(), sw); mySwitches.put(sw.getNodeId(), sw);
CollectionUtils.addToMultiMap(sw.getRoom() + ":" + sw.getName(), sw, groups); CollectionUtils.addToMultiMap(sw.getRoom() + ":" + sw.getName(), sw, groups);
} }
if (CollectionUtils.anyQualify(mySwitches.values(), Switch::isThermometerUrlValid)) { if (CollectionUtils.anyQualify(mySwitches.values(), Switch::isSourceUrlValid)) {
timer.scheduleAtFixedRate(new ThermostatTask(), 0, 30000); timer.scheduleAtFixedRate(new ThermostatTask(), 0, 30000);
} }
if (CollectionUtils.anyQualify(mySwitches.values(), _s->_s.isRelay() || _s.isRelayButton())) { if (CollectionUtils.anyQualify(mySwitches.values(), _s->_s.isRelay() || _s.isRelayButton() || (_s.isSpaceHeaterThermostat() && _s.getGpioPin() != 0))) {
relayController = new RelayController(); relayController = new RelayController();
} }
List<Switch> securitySwitches = CollectionUtils.filter(mySwitches.values(), Switch::isSecurity); List<Switch> securitySwitches = CollectionUtils.filter(mySwitches.values(), Switch::isSecurity);
@ -227,7 +230,7 @@ public class ZWaveApp {
} }
}); });
controller.send(new MultilevelSwitchSetRequest((byte)2, 0xFF)); // controller.send(new MultilevelSwitchSetRequest((byte)2, 0xFF));
// controller.send(new MultilevelSensorGetRequest((byte)11)); // controller.send(new MultilevelSensorGetRequest((byte)11));
// controller.send(new ThermostatSetPointGetRequest((byte)11, ThermostatSetPointIndex.HEATING)); // controller.send(new ThermostatSetPointGetRequest((byte)11, ThermostatSetPointIndex.HEATING));
@ -451,7 +454,8 @@ public class ZWaveApp {
nodes.addAll(CollectionUtils.filter(peers.get(_primary.getNodeId()), _p->!_p.isPrimary())); nodes.addAll(CollectionUtils.filter(peers.get(_primary.getNodeId()), _p->!_p.isPrimary()));
for (Switch node : nodes) { for (Switch node : nodes) {
logger.info("Setting {}, Node {} to {}", node.getName(), node.getNodeId(), _level); 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)); byte nid = (byte) (node.getNodeId()%1000);
controller.send(node.isMultilevel() ? new MultilevelSwitchSetRequest(nid, _level) : new BinarySwitchSetRequest(nid, _level > 0));
} }
} }
@ -469,16 +473,22 @@ public class ZWaveApp {
if (_sw.isSpaceHeaterThermostat()) { if (_sw.isSpaceHeaterThermostat()) {
double tempF = getTemperatureCelsius(_sw) * 1.8 + 32; double tempF = getTemperatureCelsius(_sw) * 1.8 + 32;
if (tempF > _sw.getLevel() + 0.4) { if (tempF > _sw.getLevel() + 0.4) {
if (_sw.getGpioPin() > 0)
relayController.setRelay(_sw.getGpioPin(), _sw.getLowLevel() > 0);
else
setGroupSwitchLevel(_sw, 0); setGroupSwitchLevel(_sw, 0);
logger.info("Turning {} {} off, temp is: {} set to: {}", _sw.getRoom(), _sw.getName(), tempF, _sw.getLevel()); logger.info("Turning {} {} off, temp is: {} set to: {}", _sw.getRoom(), _sw.getName(), tempF, _sw.getLevel());
} else if (tempF < _sw.getLevel() - 0.4) { } else if (tempF < _sw.getLevel() - 0.4) {
if (_sw.getGpioPin() > 0)
relayController.setRelay(_sw.getGpioPin(), _sw.getLowLevel() == 0);
else
setGroupSwitchLevel(_sw, 255); setGroupSwitchLevel(_sw, 255);
logger.info("Turning {} {} on, temp is: {} set to: {}", _sw.getRoom(), _sw.getName(), tempF, _sw.getLevel()); logger.info("Turning {} {} on, temp is: {} set to: {}", _sw.getRoom(), _sw.getName(), tempF, _sw.getLevel());
} }
} }
} }
catch (Throwable t) { catch (Throwable t) {
logger.error("Failed to check temperature for thermostat {}", _sw.getName()); logger.error("Failed to check temperature for thermostat {}", _sw.getName(), t);
} }
} }
@ -506,6 +516,13 @@ public class ZWaveApp {
} }
} }
public int getCO2ppm(int _nodeId) {
Switch sw = switches.get(_nodeId);
if (sw == null)
return 0;
return DaoSerializer.getInteger(DaoSerializer.parse(pool.executeToString(new HttpGet(sw.getSourceUrl()))), "ppm");
}
public double getTemperatureCelsius(int _nodeId) { public double getTemperatureCelsius(int _nodeId) {
return getTemperatureCelsius(switches.get(_nodeId)); return getTemperatureCelsius(switches.get(_nodeId));
} }
@ -513,8 +530,8 @@ public class ZWaveApp {
private double getTemperatureCelsius(Switch _sw) { private double getTemperatureCelsius(Switch _sw) {
if ((pool == null) || (_sw == null)) if ((pool == null) || (_sw == null))
return 0.0; return 0.0;
if (_sw.isThermometerUrlValid()) if (_sw.isSourceUrlValid())
return DaoSerializer.getDouble(DaoSerializer.parse(pool.executeToString(new HttpGet(_sw.getThermometerUrl()))), "temp"); return DaoSerializer.getDouble(DaoSerializer.parse(pool.executeToString(new HttpGet(_sw.getSourceUrl()))), "temp");
else if (_sw.isZWaveThermostat() && config.isMySwitch(_sw)) { else if (_sw.isZWaveThermostat() && config.isMySwitch(_sw)) {
synchronized (ZWAVE_MUTEX) { synchronized (ZWAVE_MUTEX) {
synchronized (sensors) { synchronized (sensors) {
@ -531,4 +548,58 @@ public class ZWaveApp {
} }
return 0.0; return 0.0;
} }
public void addNodeToNetwork(boolean _enable, int _controllerIdx) {
ZWaveConfig config = Globals.app.getConfig();
if (_enable) {
String controllerUrl = CollectionUtils.get(config.getControllers(), _controllerIdx);
if (NullUtils.isEqual(controllerUrl, config.getUrl()) && (controller != null))
controller.send(new AddNodeToNetworkStartRequest());
else {
HttpGet get = new HttpGet(controllerUrl + "/addNode/1/" + _controllerIdx);
get.setHeader("auth_code", authCode);
pool.execute(get);
}
}
else {
if (controller != null)
controller.send(new AddNodeToNetworkStopRequest());
List<String> controllers = config.getControllers();
controllers.remove(config.getUrl());
if (config.isMaster()) {
for (String controllerUrl : controllers) {
HttpGet get = new HttpGet(controllerUrl + "/addNode/0");
get.setHeader("auth_code", authCode);
pool.execute(get);
}
}
}
}
public void removeNodeFromNetwork(boolean _enable, int _controllerIdx) {
ZWaveConfig config = Globals.app.getConfig();
if (_enable) {
String controllerUrl = CollectionUtils.get(config.getControllers(), _controllerIdx);
if (NullUtils.isEqual(controllerUrl, config.getUrl()) && (controller != null))
controller.send(new RemoveNodeFromNetworkStartRequest());
else {
HttpGet get = new HttpGet(controllerUrl + "/removeNode/1/" + _controllerIdx);
get.setHeader("auth_code", authCode);
pool.execute(get);
}
}
else {
if (controller != null)
controller.send(new RemoveNodeFromNetworkStopRequest());
List<String> controllers = config.getControllers();
controllers.remove(config.getUrl());
if (config.isMaster()) {
for (String controllerUrl : controllers) {
HttpGet get = new HttpGet(controllerUrl + "/removeNode/0");
get.setHeader("auth_code", authCode);
pool.execute(get);
}
}
}
}
} }

View File

@ -26,6 +26,7 @@ public class RelayController {
return; return;
} }
} }
LOG.info("Setting pin {} to {}", _pin, _on);
if (_on) if (_on)
pin.high(); pin.high();
else else

View File

@ -0,0 +1,19 @@
package com.lanternsoftware.zwave.servlet;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.zwave.context.Globals;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/addNode/*")
public class AddNodeServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
Globals.app.addNodeToNetwork(NullUtils.isEqual(CollectionUtils.get(path(_req), 0), "1"), DaoSerializer.toInteger(CollectionUtils.get(path(_req), 1)));
}
}

View File

@ -0,0 +1,21 @@
package com.lanternsoftware.zwave.servlet;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.zwave.context.Globals;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/co2/*")
public class CO2Servlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
String[] path = path(_req);
jsonResponse(_rep, DaoSerializer.toJson(new DaoEntity("ppm", Globals.app.getCO2ppm(NullUtils.toInteger(CollectionUtils.get(path, 0))))));
}
}

View File

@ -0,0 +1,19 @@
package com.lanternsoftware.zwave.servlet;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.zwave.context.Globals;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/removeNode/*")
public class RemoveNodeServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
Globals.app.removeNodeFromNetwork(NullUtils.isEqual(CollectionUtils.get(path(_req), 0), "1"), DaoSerializer.toInteger(CollectionUtils.get(path(_req), 1)));
}
}

View File

@ -9,7 +9,7 @@
</appender> </appender>
<logger name="com.lanternsoftware" level="DEBUG"/> <logger name="com.lanternsoftware" level="INFO"/>
<root level="OFF"> <root level="OFF">
<appender-ref ref="STDOUT"/> <appender-ref ref="STDOUT"/>

View File

@ -16,6 +16,7 @@
<groupId>com.neuronrobotics</groupId> <groupId>com.neuronrobotics</groupId>
<artifactId>nrjavaserial</artifactId> <artifactId>nrjavaserial</artifactId>
<version>5.2.1</version> <version>5.2.1</version>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>

View File

@ -0,0 +1,17 @@
package com.lanternsoftware.zwave.message.impl;
import com.lanternsoftware.zwave.message.ControllerMessageType;
import com.lanternsoftware.zwave.message.NoCommandRequestMessage;
public class AddNodeToNetworkStartRequest extends NoCommandRequestMessage {
public AddNodeToNetworkStartRequest() {
super(ControllerMessageType.AddNodeToNetwork);
}
@Override
public byte[] getPayload() {
byte[] payload = new byte[1];
payload[0] = (byte)0xC1;
return payload;
}
}

View File

@ -3,8 +3,8 @@ package com.lanternsoftware.zwave.message.impl;
import com.lanternsoftware.zwave.message.ControllerMessageType; import com.lanternsoftware.zwave.message.ControllerMessageType;
import com.lanternsoftware.zwave.message.NoCommandRequestMessage; import com.lanternsoftware.zwave.message.NoCommandRequestMessage;
public class AddNodeToNetworkRequest extends NoCommandRequestMessage { public class AddNodeToNetworkStopRequest extends NoCommandRequestMessage {
public AddNodeToNetworkRequest() { public AddNodeToNetworkStopRequest() {
super(ControllerMessageType.AddNodeToNetwork); super(ControllerMessageType.AddNodeToNetwork);
} }

View File

@ -0,0 +1,17 @@
package com.lanternsoftware.zwave.message.impl;
import com.lanternsoftware.zwave.message.ControllerMessageType;
import com.lanternsoftware.zwave.message.NoCommandRequestMessage;
public class RemoveNodeFromNetworkStartRequest extends NoCommandRequestMessage {
public RemoveNodeFromNetworkStartRequest() {
super(ControllerMessageType.RemoveNodeFromNetwork);
}
@Override
public byte[] getPayload() {
byte[] payload = new byte[1];
payload[0] = (byte)0xC1;
return payload;
}
}

View File

@ -0,0 +1,17 @@
package com.lanternsoftware.zwave.message.impl;
import com.lanternsoftware.zwave.message.ControllerMessageType;
import com.lanternsoftware.zwave.message.NoCommandRequestMessage;
public class RemoveNodeFromNetworkStopRequest extends NoCommandRequestMessage {
public RemoveNodeFromNetworkStopRequest() {
super(ControllerMessageType.RemoveNodeFromNetwork);
}
@Override
public byte[] getPayload() {
byte[] payload = new byte[1];
payload[0] = (byte)0x05;
return payload;
}
}

View File

@ -1,4 +1,5 @@
com.lanternsoftware.zwave.message.impl.AddNodeToNetworkRequest com.lanternsoftware.zwave.message.impl.AddNodeToNetworkStartRequest
com.lanternsoftware.zwave.message.impl.AddNodeToNetworkStopRequest
com.lanternsoftware.zwave.message.impl.ApplicationUpdateRequest com.lanternsoftware.zwave.message.impl.ApplicationUpdateRequest
com.lanternsoftware.zwave.message.impl.AssociationGetRequest com.lanternsoftware.zwave.message.impl.AssociationGetRequest
com.lanternsoftware.zwave.message.impl.AssociationReportRequest com.lanternsoftware.zwave.message.impl.AssociationReportRequest
@ -20,6 +21,8 @@ com.lanternsoftware.zwave.message.impl.MultilevelSwitchReportRequest
com.lanternsoftware.zwave.message.impl.MultilevelSwitchSetRequest com.lanternsoftware.zwave.message.impl.MultilevelSwitchSetRequest
com.lanternsoftware.zwave.message.impl.NodeInfoRequest com.lanternsoftware.zwave.message.impl.NodeInfoRequest
com.lanternsoftware.zwave.message.impl.NodeInfoResponse com.lanternsoftware.zwave.message.impl.NodeInfoResponse
com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStartRequest
com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStopRequest
com.lanternsoftware.zwave.message.impl.SendDataRequest com.lanternsoftware.zwave.message.impl.SendDataRequest
com.lanternsoftware.zwave.message.impl.SendDataResponse com.lanternsoftware.zwave.message.impl.SendDataResponse
com.lanternsoftware.zwave.message.impl.ThermostatModeGetRequest com.lanternsoftware.zwave.message.impl.ThermostatModeGetRequest