diff --git a/currentmonitor/lantern-currentmonitor/pom.xml b/currentmonitor/lantern-currentmonitor/pom.xml index 4127a67..25ece54 100644 --- a/currentmonitor/lantern-currentmonitor/pom.xml +++ b/currentmonitor/lantern-currentmonitor/pom.xml @@ -3,7 +3,7 @@ com.lanternsoftware.currentmonitor lantern-currentmonitor jar - 1.0.0 + 0.9.5 lantern-currentmonitor diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BluetoothConfig.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BluetoothConfig.java index c95a8dc..9290598 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BluetoothConfig.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BluetoothConfig.java @@ -9,10 +9,8 @@ import com.lanternsoftware.datamodel.currentmonitor.HubConfigService; import com.lanternsoftware.util.CollectionUtils; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -public class BluetoothConfig implements Runnable { - private final AtomicBoolean running = new AtomicBoolean(true); +public class BluetoothConfig { private final BleApplication app; public BluetoothConfig(String _hubName, BleCharacteristicListener _listener) { @@ -25,15 +23,11 @@ public class BluetoothConfig implements Runnable { app = new BleApplication("Lantern", _hubName, new BleService("HubConfig", service.getServiceUUID(), chars)); } - @Override - public void run() { + public void start() { app.start(); } public void stop() { - synchronized (running) { - running.set(false); - } app.stop(); } } diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java index 54407e8..ac5d61f 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java @@ -90,59 +90,65 @@ public class MonitorApp { HubConfigCharacteristic ch = NullUtils.toEnum(HubConfigCharacteristic.class, _name); LOG.info("Char Received, Name: {} Value: {}", _name, _value); monitor.submit(()->{ - switch (ch) { - case HubIndex: - if ((_value.length > 0)) { - config.setHub(_value[0]); - ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config)); - } - break; - case AuthCode: - String value = NullUtils.toString(_value); - if (NullUtils.isNotEmpty(value)) { - authCode = value; - config.setAuthCode(value); - ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config)); - } - break; - case WifiCredentials: - String ssid = HubConfigService.decryptWifiSSID(_value); - String pwd = HubConfigService.decryptWifiPassword(_value); - if (NullUtils.isNotEmpty(ssid) && NullUtils.isNotEmpty(pwd)) - WifiConfig.setCredentials(ssid, pwd); - break; - case Flash: - if ((CollectionUtils.length(_value) == 0) || (_value[0] == 0)) { - if (flasher != null) { - flasher.stop(); - flasher = null; + synchronized (monitor) { + switch (ch) { + case Host: + if ((_value.length > 0)) { + config.setHost(NullUtils.terminateWith(NullUtils.toString(_value), "/") + "currentmonitor/"); + ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config)); } - else - LEDFlasher.setLEDOn(false); - } - else { - if (flasher == null) { - flasher = new LEDFlasher(); - monitor.submit(flasher); + break; + case HubIndex: + if ((_value.length > 0)) { + config.setHub(_value[0]); + ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config)); } - } - break; - case Restart: - LOG.info("Restarting Current Monitor..."); - try { - Runtime.getRuntime().exec("echo \"sudo systemctl restart currentmonitor\" | at now + 1 minute"); - } catch (IOException _e) { - LOG.error("Exception occurred while trying to restart", _e); - } - break; - case Reboot: - LOG.info("Rebooting Pi..."); - try { - Runtime.getRuntime().exec("sudo reboot now"); - } catch (IOException _e) { - LOG.error("Exception occurred while trying to reboot", _e); - } - break; + break; + case AuthCode: + String value = NullUtils.toString(_value); + if (NullUtils.isNotEmpty(value)) { + authCode = value; + config.setAuthCode(value); + ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config)); + } + break; + case WifiCredentials: + String ssid = HubConfigService.decryptWifiSSID(_value); + String pwd = HubConfigService.decryptWifiPassword(_value); + if (NullUtils.isNotEmpty(ssid) && NullUtils.isNotEmpty(pwd)) + WifiConfig.setCredentials(ssid, pwd); + break; + case Flash: + if ((CollectionUtils.length(_value) == 0) || (_value[0] == 0)) { + if (flasher != null) { + flasher.stop(); + flasher = null; + } else + LEDFlasher.setLEDOn(false); + } else { + if (flasher == null) { + flasher = new LEDFlasher(); + monitor.submit(flasher); + } + } + break; + case Restart: + LOG.info("Restarting Current Monitor..."); + try { + Runtime.getRuntime().exec(new String[]{"systemctl","restart","currentmonitor"}); + } catch (IOException _e) { + LOG.error("Exception occurred while trying to restart", _e); + } + break; + case Reboot: + LOG.info("Rebooting Pi..."); + try { + Runtime.getRuntime().exec(new String[]{"reboot","now"}); + } catch (IOException _e) { + LOG.error("Exception occurred while trying to reboot", _e); + } + break; + } } }); } @@ -159,7 +165,7 @@ public class MonitorApp { return null; } }); - monitor.submit(bluetoothConfig); + bluetoothConfig.start(); if (NullUtils.isNotEmpty(config.getAuthCode())) authCode = config.getAuthCode(); else { @@ -190,7 +196,8 @@ public class MonitorApp { } List breakers = breakerConfig.getBreakersForHub(config.getHub()); LOG.info("Monitoring {} breakers for hub {}", CollectionUtils.size(breakers), hub.getHub()); - monitor.monitorPower(hub, breakers, 1000, logger); + if (CollectionUtils.size(breakers) > 0) + monitor.monitorPower(hub, breakers, 1000, logger); } monitor.submit(new PowerPoster()); Runtime.getRuntime().addShutdownHook(new Thread(() -> { @@ -341,7 +348,7 @@ public class MonitorApp { ResourceLoader.writeFile(WORKING_DIR + "lantern-currentmonitor.jar", jar); ConcurrencyUtils.sleep(10000); try { - Runtime.getRuntime().exec("echo \"sudo systemctl restart currentmonitor\" | at now + 1 minute"); + Runtime.getRuntime().exec(new String[]{"systemctl","restart","currentmonitor"}); } catch (IOException _e) { LOG.error("Exception occurred while trying to restart", _e); } @@ -375,9 +382,9 @@ public class MonitorApp { else if (NullUtils.isEqual(command, "extend_filesystem")) { LOG.info("Extending filesystem and rebooting"); try { - Runtime.getRuntime().exec("sudo raspi-config --expand-rootfs"); + Runtime.getRuntime().exec(new String[]{"sudo","raspi-config","--expand-rootfs"}); ConcurrencyUtils.sleep(5000); - Runtime.getRuntime().exec("sudo reboot now"); + Runtime.getRuntime().exec(new String[]{"reboot","now"}); } catch (IOException _e) { LOG.error("Exception occurred while trying to extend filesystem", _e); } @@ -386,7 +393,7 @@ public class MonitorApp { else if (NullUtils.isEqual(command, "restart")) { LOG.info("Restarting..."); try { - Runtime.getRuntime().exec("echo \"sudo systemctl restart currentmonitor\" | at now + 1 minute"); + Runtime.getRuntime().exec(new String[]{"systemctl","restart","currentmonitor"}); } catch (IOException _e) { LOG.error("Exception occurred while trying to restart", _e); } diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/wifi/WifiConfig.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/wifi/WifiConfig.java index cd3e9ed..d0e1d53 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/wifi/WifiConfig.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/wifi/WifiConfig.java @@ -1,5 +1,7 @@ package com.lanternsoftware.currentmonitor.wifi; +import com.lanternsoftware.util.CollectionUtils; +import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.ResourceLoader; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; @@ -11,28 +13,17 @@ public abstract class WifiConfig { private static final Logger LOG = LoggerFactory.getLogger(WifiConfig.class); private static final String WIFI_CONFIG_PATH = "/etc/wpa_supplicant/wpa_supplicant.conf"; - private static final String CONF_FORMAT = "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\ncountry=US\nnetwork={\n\tssid=\"%s\"\n\t%s\n}\n"; + private static final String CONF_FORMAT = "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\ncountry=US\n"; public static void setCredentials(String _ssid, String _password) { - String[] commands = {"wpa_passphrase", _ssid, _password}; InputStream is = null; try { - is = Runtime.getRuntime().exec(commands).getInputStream(); - String newConf = IOUtils.toString(is); + is = Runtime.getRuntime().exec(new String[]{"wpa_passphrase", _ssid, _password}).getInputStream(); + String newConf = CollectionUtils.delimit(CollectionUtils.filter(CollectionUtils.asArrayList(NullUtils.cleanSplit(IOUtils.toString(is), "\\r?\\n")), _s->!_s.trim().startsWith("#")), "\n"); if (newConf == null) return; - int idx = newConf.indexOf("psk="); - if (idx > 0) { - if (newConf.charAt(idx-1) == '#') - idx = newConf.indexOf("psk=", idx+1); - if (idx > 0) { - int endIdx = newConf.indexOf("\n", idx); - if (endIdx > 0) { - String finalConf = String.format(CONF_FORMAT, _ssid, newConf.substring(idx, endIdx)); - ResourceLoader.writeFile(WIFI_CONFIG_PATH, finalConf); - } - } - } + ResourceLoader.writeFile(WIFI_CONFIG_PATH, CONF_FORMAT+newConf); + Runtime.getRuntime().exec(new String[]{"wpa_cli","-i","wlan0","reconfigure"}); } catch (Exception _e) { LOG.error("Failed to write wifi credentials", _e); diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubConfigCharacteristic.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubConfigCharacteristic.java index b58eea9..740a8f4 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubConfigCharacteristic.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubConfigCharacteristic.java @@ -14,7 +14,8 @@ public enum HubConfigCharacteristic { Reboot(6, CharacteristicFlag.WRITE), AccountId(7, CharacteristicFlag.READ), NetworkState(8, CharacteristicFlag.READ), - Flash(9, CharacteristicFlag.WRITE); + Flash(9, CharacteristicFlag.WRITE), + Host(10, CharacteristicFlag.WRITE); public final int idx; public final UUID uuid; diff --git a/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java b/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java index 0129530..ab57e6f 100644 --- a/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java +++ b/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java @@ -17,6 +17,7 @@ import com.lanternsoftware.zwave.controller.Controller; import com.lanternsoftware.zwave.dao.MongoZWaveDao; 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.MultilevelSensorGetRequest; import com.lanternsoftware.zwave.message.impl.MultilevelSensorReportRequest; @@ -45,7 +46,7 @@ public class ZWaveApp { private ZWaveConfig config; private Controller controller; private final Map switches = new HashMap<>(); - private final Map> peers = new HashMap<>(); + private final Map> peers = new HashMap<>(); private Timer timer; private HttpPool pool; private SwitchScheduleTask nextScheduleTask; @@ -78,17 +79,17 @@ public class ZWaveApp { t.printStackTrace(); } config = dao.getConfig(1); - Map> groups = new HashMap<>(); + Map> groups = new HashMap<>(); for (Switch sw : CollectionUtils.makeNotNull(config.getSwitches())) { switches.put(sw.getNodeId(), sw); - CollectionUtils.addToMultiMap(sw.getRoom() + ":" + sw.getName(), sw.getNodeId(), groups); + CollectionUtils.addToMultiMap(sw.getRoom() + ":" + sw.getName(), sw, groups); } if (CollectionUtils.filterOne(config.getSwitches(), Switch::isUrlThermostat) != null) { timer.scheduleAtFixedRate(new ThermostatTask(), 0, 30000); } - for (List group : groups.values()) { - for (Integer node : group) { - peers.put(node, CollectionUtils.filter(group, _i -> !_i.equals(node))); + for (List group : groups.values()) { + for (Switch sw : group) { + peers.put(sw.getNodeId(), CollectionUtils.filter(group, _sw -> _sw.getNodeId() != sw.getNodeId())); } } scheduleNextTransition(); @@ -136,19 +137,19 @@ public class ZWaveApp { @Override public void onMessage(MultilevelSwitchReportRequest _message) { - synchronized (switches) { - Switch sw = switches.get((int) _message.getNodeId()); - if (sw != null) { - sw.setLevel(_message.getLevel()); - for (Integer node : CollectionUtils.makeNotNull(peers.get((int) _message.getNodeId()))) { - sw = switches.get(node); - sw.setLevel(_message.getLevel()); - logger.info("Mirror Event from node {} to node {}", _message.getNodeId(), node); - controller.send(new MultilevelSwitchSetRequest(node.byteValue(), _message.getLevel())); - } - persistConfig(); - } - } + onSwitchLevelChange(_message.getNodeId(), _message.getLevel()); + } + }); + + MessageEngine.subscribe(new IMessageSubscriber() { + @Override + public Class getHandledMessageClass() { + return BinarySwitchReportRequest.class; + } + + @Override + public void onMessage(BinarySwitchReportRequest _message) { + onSwitchLevelChange(_message.getNodeId(), _message.getLevel()); } }); @@ -164,6 +165,28 @@ public class ZWaveApp { // controller.send(new ThermostatModeGetRequest((byte)11)); } + private void onSwitchLevelChange(int _primaryNodeId, int _primaryLevel) { + synchronized (switches) { + Switch sw = switches.get(_primaryNodeId); + if (sw != null) { + 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)); + } + } + persistConfig(); + } + } + } + private void scheduleNextTransition() { TimeZone tz = TimeZone.getTimeZone("America/Chicago"); if (nextScheduleTask != null) @@ -185,7 +208,7 @@ public class ZWaveApp { return; sw.setLevel(_level); if (!sw.isThermostat()) { - setGroupSwitchLevel(_nodeId, _level, sw.isMultilevel()); + setGroupSwitchLevel(sw, _level); } else if (sw.isZWaveThermostat()) { controller.send(new ThermostatSetPointSetRequest((byte) sw.getNodeId(), sw.getThermostatMode() == ThermostatMode.COOL ? ThermostatSetPointIndex.COOLING : ThermostatSetPointIndex.HEATING, _level)); } else { @@ -252,11 +275,13 @@ public class ZWaveApp { } } - private void setGroupSwitchLevel(int _primary, int _level, boolean _multilevel) { - List nodes = CollectionUtils.asArrayList(_primary); - nodes.addAll(CollectionUtils.makeNotNull(peers.get(_primary))); - for (int node : nodes) { - controller.send(_multilevel ? new MultilevelSwitchSetRequest((byte) node, _level) : new BinarySwitchSetRequest((byte) node, _level > 0)); + private void setGroupSwitchLevel(Switch _primary, int _level) { + if (_primary == null) + return; + List nodes = CollectionUtils.asArrayList(_primary); + nodes.addAll(CollectionUtils.makeNotNull(peers.get(_primary.getNodeId()))); + for (Switch node : nodes) { + controller.send(node.isMultilevel() ? new MultilevelSwitchSetRequest((byte) node.getNodeId(), _level) : new BinarySwitchSetRequest((byte) node.getNodeId(), _level > 0)); } } @@ -268,10 +293,10 @@ public class ZWaveApp { if (sw.isUrlThermostat() && !sw.isThermometer()) { double tempF = getTemperatureCelsius(sw) * 1.8 + 32; if (tempF > sw.getLevel() + 0.4) { - setGroupSwitchLevel(sw.getNodeId(), 0, false); + 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.getNodeId(), (byte) 0xf, false); + setGroupSwitchLevel(sw, (byte) 0xf); logger.info("Turning {} {} on, temp is: {} set to: {}", sw.getRoom(), sw.getName(), tempF + " set to: ", sw.getLevel()); } } diff --git a/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/controller/Controller.java b/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/controller/Controller.java index ad85e90..ee78e31 100644 --- a/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/controller/Controller.java +++ b/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/controller/Controller.java @@ -53,8 +53,8 @@ public class Controller { CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(_port); serialPort = portIdentifier.open("zwaveport", 2000); serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); - serialPort.enableReceiveThreshold(1); - serialPort.enableReceiveTimeout(1000); + serialPort.disableReceiveThreshold(); + serialPort.enableReceiveTimeout(500); os = serialPort.getOutputStream(); running = true; executor.submit(new MessageReceiver()); diff --git a/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/message/impl/BinarySwitchReportRequest.java b/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/message/impl/BinarySwitchReportRequest.java new file mode 100644 index 0000000..9b6feef --- /dev/null +++ b/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/message/impl/BinarySwitchReportRequest.java @@ -0,0 +1,28 @@ +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 BinarySwitchReportRequest extends RequestMessage { + private int level; + + public BinarySwitchReportRequest() { + super(ControllerMessageType.ApplicationCommandHandler, CommandClass.SWITCH_BINARY, (byte) 0x03); + } + + @Override + public void fromPayload(byte[] _payload) { + nodeId = _payload[5]; + level = _payload[9]; + } + + public int getLevel() { + return level; + } + + @Override + public String describe() { + return name() + " node: " + nodeId + " level: " + level; + } +} diff --git a/zwave/lantern-zwave/src/main/resources/META-INF/services/com.lanternsoftware.zwave.message.Message b/zwave/lantern-zwave/src/main/resources/META-INF/services/com.lanternsoftware.zwave.message.Message index 4480d56..9a6e892 100644 --- a/zwave/lantern-zwave/src/main/resources/META-INF/services/com.lanternsoftware.zwave.message.Message +++ b/zwave/lantern-zwave/src/main/resources/META-INF/services/com.lanternsoftware.zwave.message.Message @@ -1,5 +1,6 @@ com.lanternsoftware.zwave.message.impl.ApplicationUpdateRequest com.lanternsoftware.zwave.message.impl.BinarySwitchSetRequest +com.lanternsoftware.zwave.message.impl.BinarySwitchReportRequest com.lanternsoftware.zwave.message.impl.ByteMessage com.lanternsoftware.zwave.message.impl.ControllerCapabilitiesRequest com.lanternsoftware.zwave.message.impl.ControllerCapabilitiesResponse