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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user