Password reset functionality, ZWave switch schedule improvement, support zwave controller on pi, support relay switches and security sensors.

This commit is contained in:
MarkBryanMilligan
2021-07-02 12:06:37 -05:00
parent 6c2b567536
commit de50645a2c
65 changed files with 27104 additions and 438 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,11 @@
package com.lanternsoftware.datamodel.zwave;
public enum SwitchType {
BINARY,
DIMMER,
THERMOSTAT,
SPACE_HEATER_THERMOSTAT,
THERMOMETER,
RELAY,
SECURITY
}

View File

@@ -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());
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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")));
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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");
}*/
}

View File

@@ -12,6 +12,7 @@ public class MongoZWaveDao implements ZWaveDao {
proxy = new MongoProxy(_config);
}
@Override
public void shutdown() {
proxy.shutdown();
}

View File

@@ -5,4 +5,5 @@ import com.lanternsoftware.datamodel.zwave.ZWaveConfig;
public interface ZWaveDao {
void putConfig(ZWaveConfig _config);
ZWaveConfig getConfig(int _accountId);
void shutdown();
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,5 @@
package com.lanternsoftware.zwave.security;
public interface SecurityListener {
void onStateChanged(int nodeId, boolean _open);
}

View File

@@ -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;
}

View File

@@ -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));
}
}
}

View File

@@ -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()), "/");

View File

@@ -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>

View File

@@ -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());
}
}
}
}

View File

@@ -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"),

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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