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

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