mirror of
https://github.com/zyphlar/LanternPowerMonitor.git
synced 2024-03-08 14:07:47 +00:00
Implement 3-phase voltage logic.
This commit is contained in:
parent
1369529e8d
commit
e0d4dafd3a
|
@ -2,7 +2,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>lantern-currentmonitor</artifactId>
|
<artifactId>lantern-currentmonitor</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>1.1.0</version>
|
<version>1.1.1</version>
|
||||||
<name>lantern-currentmonitor</name>
|
<name>lantern-currentmonitor</name>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.lanternsoftware.currentmonitor;
|
||||||
|
|
||||||
import com.lanternsoftware.currentmonitor.adc.MCP3008Pin;
|
import com.lanternsoftware.currentmonitor.adc.MCP3008Pin;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.Breaker;
|
import com.lanternsoftware.datamodel.currentmonitor.Breaker;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.PowerSample;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@ import com.lanternsoftware.datamodel.currentmonitor.Breaker;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.BreakerHub;
|
import com.lanternsoftware.datamodel.currentmonitor.BreakerHub;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.BreakerPolarity;
|
import com.lanternsoftware.datamodel.currentmonitor.BreakerPolarity;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
|
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.BreakerSample;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.PowerSample;
|
||||||
import com.lanternsoftware.pigpio.PiGpioFactory;
|
import com.lanternsoftware.pigpio.PiGpioFactory;
|
||||||
import com.lanternsoftware.util.CollectionUtils;
|
import com.lanternsoftware.util.CollectionUtils;
|
||||||
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
|
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
|
||||||
|
@ -18,6 +21,8 @@ import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
@ -29,6 +34,23 @@ public class CurrentMonitor {
|
||||||
private Sampler sampler;
|
private Sampler sampler;
|
||||||
private PowerListener listener;
|
private PowerListener listener;
|
||||||
private boolean debug = false;
|
private boolean debug = false;
|
||||||
|
private boolean postSamples = false;
|
||||||
|
|
||||||
|
public boolean isDebug() {
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDebug(boolean _debug) {
|
||||||
|
debug = _debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPostSamples() {
|
||||||
|
return postSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostSamples(boolean _postSamples) {
|
||||||
|
postSamples = _postSamples;
|
||||||
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
stopMonitoring();
|
stopMonitoring();
|
||||||
|
@ -40,10 +62,6 @@ public class CurrentMonitor {
|
||||||
LOG.info("Power Monitor Service Stopped");
|
LOG.info("Power Monitor Service Stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDebug(boolean _debug) {
|
|
||||||
debug = _debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CalibrationResult calibrateVoltage(double _curCalibration) {
|
public CalibrationResult calibrateVoltage(double _curCalibration) {
|
||||||
LOG.info("Calibrating Voltage");
|
LOG.info("Calibrating Voltage");
|
||||||
MCP3008Pin voltagePin = new MCP3008Pin(getChip(0), 0);
|
MCP3008Pin voltagePin = new MCP3008Pin(getChip(0), 0);
|
||||||
|
@ -148,10 +166,10 @@ public class CurrentMonitor {
|
||||||
private boolean running = true;
|
private boolean running = true;
|
||||||
private final BreakerHub hub;
|
private final BreakerHub hub;
|
||||||
private final List<List<BreakerSamples>> breakers;
|
private final List<List<BreakerSamples>> breakers;
|
||||||
private final int intervalNs;
|
private final long intervalNs;
|
||||||
private final int concurrentBreakerCnt;
|
private final int concurrentBreakerCnt;
|
||||||
|
|
||||||
public Sampler(BreakerHub _hub, List<Breaker> _breakers, int _intervalMs, int _concurrentBreakerCnt) {
|
public Sampler(BreakerHub _hub, List<Breaker> _breakers, long _intervalMs, int _concurrentBreakerCnt) {
|
||||||
hub = _hub;
|
hub = _hub;
|
||||||
MCP3008Pin voltagePin = new MCP3008Pin(getChip(0), 0);
|
MCP3008Pin voltagePin = new MCP3008Pin(getChip(0), 0);
|
||||||
breakers = CollectionUtils.transform(_breakers, _b->{
|
breakers = CollectionUtils.transform(_breakers, _b->{
|
||||||
|
@ -177,6 +195,10 @@ public class CurrentMonitor {
|
||||||
long interval = 0;
|
long interval = 0;
|
||||||
int cycle;
|
int cycle;
|
||||||
int curBreaker;
|
int curBreaker;
|
||||||
|
long intervalStart;
|
||||||
|
long intervalEnd;
|
||||||
|
long cycleEnd;
|
||||||
|
long curTime;
|
||||||
BreakerSamples[] cycleBreakers = new BreakerSamples[concurrentBreakerCnt];
|
BreakerSamples[] cycleBreakers = new BreakerSamples[concurrentBreakerCnt];
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -187,8 +209,8 @@ public class CurrentMonitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final Date readTime = new Date();
|
final Date readTime = new Date();
|
||||||
final long intervalStart = (interval * intervalNs) + start;
|
intervalStart = (interval * intervalNs) + start;
|
||||||
long intervalEnd = intervalStart + intervalNs;
|
intervalEnd = intervalStart + intervalNs;
|
||||||
cycle = 0;
|
cycle = 0;
|
||||||
final int batch = (int) (interval % BATCH_CNT);
|
final int batch = (int) (interval % BATCH_CNT);
|
||||||
while (System.nanoTime() < intervalEnd) {
|
while (System.nanoTime() < intervalEnd) {
|
||||||
|
@ -197,22 +219,73 @@ public class CurrentMonitor {
|
||||||
cycleBreakers[curBreaker].incrementCycleCnt();
|
cycleBreakers[curBreaker].incrementCycleCnt();
|
||||||
}
|
}
|
||||||
cycle++;
|
cycle++;
|
||||||
long cycleEnd = intervalStart + (cycle * (intervalNs / hub.getFrequency()));
|
cycleEnd = intervalStart + (cycle * (intervalNs / hub.getFrequency()));
|
||||||
while (System.nanoTime() < cycleEnd) {
|
curTime = System.nanoTime();
|
||||||
|
while (curTime < cycleEnd) {
|
||||||
for (curBreaker = 0; curBreaker < concurrentBreakerCnt; curBreaker++) {
|
for (curBreaker = 0; curBreaker < concurrentBreakerCnt; curBreaker++) {
|
||||||
PowerSample sample = cycleBreakers[curBreaker].incrementSample();
|
PowerSample sample = cycleBreakers[curBreaker].incrementSample();
|
||||||
|
sample.nanoTime = curTime;
|
||||||
|
sample.cycle = cycle;
|
||||||
sample.voltage = cycleBreakers[curBreaker].getVoltagePin().read();
|
sample.voltage = cycleBreakers[curBreaker].getVoltagePin().read();
|
||||||
sample.current = cycleBreakers[curBreaker].getCurrentPin().read();
|
sample.current = cycleBreakers[curBreaker].getCurrentPin().read();
|
||||||
}
|
}
|
||||||
|
curTime = System.nanoTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
interval++;
|
interval++;
|
||||||
|
final HubSample hubSample = (postSamples && (interval == 10)) ? new HubSample() : null;
|
||||||
executor.submit(() -> {
|
executor.submit(() -> {
|
||||||
|
long cycleLength = 1000000000/hub.getFrequency();
|
||||||
|
if (hubSample != null) {
|
||||||
|
hubSample.setSampleDate(new Date());
|
||||||
|
hubSample.setBreakers(new ArrayList<>());
|
||||||
|
}
|
||||||
for (List<BreakerSamples> breaker : breakers) {
|
for (List<BreakerSamples> breaker : breakers) {
|
||||||
double vOffset = 0.0;
|
|
||||||
double iOffset = 0.0;
|
|
||||||
BreakerSamples samples = breaker.get(batch);
|
BreakerSamples samples = breaker.get(batch);
|
||||||
List<PowerSample> validSamples = samples.getSamples().subList(0, samples.getSampleCnt());
|
List<PowerSample> validSamples = samples.getSamples().subList(0, samples.getSampleCnt());
|
||||||
|
if (hubSample != null) {
|
||||||
|
BreakerSample breakerSample = new BreakerSample();
|
||||||
|
breakerSample.setSamples(validSamples);
|
||||||
|
breakerSample.setPanel(samples.getBreaker().getPanel());
|
||||||
|
breakerSample.setSpace(samples.getBreaker().getSpace());
|
||||||
|
hubSample.getBreakers().add(breakerSample);
|
||||||
|
}
|
||||||
|
int phaseOffsetNs = samples.getBreaker().getPhaseOffsetNs()-hub.getPhaseOffsetNs();
|
||||||
|
if (phaseOffsetNs != 0) {
|
||||||
|
Map<Integer, List<PowerSample>> cycles = CollectionUtils.transformToMultiMap(validSamples, _p->_p.cycle);
|
||||||
|
for (List<PowerSample> cycleSamples : cycles.values()) {
|
||||||
|
long minNano;
|
||||||
|
long maxNano = minNano = cycleSamples.get(0).nanoTime;
|
||||||
|
for (PowerSample sample : cycleSamples) {
|
||||||
|
if (sample.nanoTime < minNano)
|
||||||
|
minNano = sample.nanoTime;
|
||||||
|
if (sample.nanoTime > maxNano)
|
||||||
|
maxNano = sample.nanoTime;
|
||||||
|
}
|
||||||
|
TreeMap<Long, Double> offsetSamples = new TreeMap<>();
|
||||||
|
for (PowerSample sample : cycleSamples) {
|
||||||
|
if (sample.nanoTime + phaseOffsetNs < minNano)
|
||||||
|
offsetSamples.put(sample.nanoTime + phaseOffsetNs + cycleLength, sample.voltage);
|
||||||
|
else if (sample.nanoTime + phaseOffsetNs > maxNano)
|
||||||
|
offsetSamples.put(sample.nanoTime + phaseOffsetNs - cycleLength, sample.voltage);
|
||||||
|
else
|
||||||
|
offsetSamples.put(sample.nanoTime + phaseOffsetNs, sample.voltage);
|
||||||
|
}
|
||||||
|
for (PowerSample sample : cycleSamples) {
|
||||||
|
List<Double> voltages = new ArrayList<>();
|
||||||
|
Entry<Long, Double> floorEntry = offsetSamples.floorEntry(sample.nanoTime);
|
||||||
|
if (floorEntry != null)
|
||||||
|
voltages.add(floorEntry.getValue());
|
||||||
|
Entry<Long, Double> ceilingEntry = offsetSamples.ceilingEntry(sample.nanoTime);
|
||||||
|
if (ceilingEntry != null)
|
||||||
|
voltages.add(ceilingEntry.getValue());
|
||||||
|
sample.voltage = CollectionUtils.mean(voltages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double vOffset = 0.0;
|
||||||
|
double iOffset = 0.0;
|
||||||
for (PowerSample sample : validSamples) {
|
for (PowerSample sample : validSamples) {
|
||||||
vOffset += sample.voltage;
|
vOffset += sample.voltage;
|
||||||
iOffset += sample.current;
|
iOffset += sample.current;
|
||||||
|
@ -223,6 +296,7 @@ public class CurrentMonitor {
|
||||||
double pSum = 0.0;
|
double pSum = 0.0;
|
||||||
double vRms = 0.0;
|
double vRms = 0.0;
|
||||||
double lowPassFilter = samples.getBreaker().getLowPassFilter();
|
double lowPassFilter = samples.getBreaker().getLowPassFilter();
|
||||||
|
|
||||||
for (PowerSample sample : validSamples) {
|
for (PowerSample sample : validSamples) {
|
||||||
sample.current -= iOffset;
|
sample.current -= iOffset;
|
||||||
if (Math.abs(sample.current) < lowPassFilter)
|
if (Math.abs(sample.current) < lowPassFilter)
|
||||||
|
@ -233,11 +307,15 @@ public class CurrentMonitor {
|
||||||
}
|
}
|
||||||
vRms /= validSamples.size();
|
vRms /= validSamples.size();
|
||||||
vRms = hub.getVoltageCalibrationFactor() * Math.sqrt(vRms);
|
vRms = hub.getVoltageCalibrationFactor() * Math.sqrt(vRms);
|
||||||
int lowSampleRatio = (lowSamples * 100) / samples.getSampleCnt();
|
int lowSampleRatio = (lowSamples * 100) / validSamples.size();
|
||||||
double realPower = Math.abs((hub.getVoltageCalibrationFactor() * hub.getPortCalibrationFactor() * samples.getBreaker().getFinalCalibrationFactor() * pSum) / samples.getSampleCnt());
|
double realPower = (hub.getVoltageCalibrationFactor() * hub.getPortCalibrationFactor() * samples.getBreaker().getFinalCalibrationFactor() * pSum) / validSamples.size();
|
||||||
if ((lowSampleRatio > 75) && realPower < 13.0)
|
if ((lowSampleRatio > 75) && Math.abs(realPower) < 13.0)
|
||||||
realPower = 0.0;
|
realPower = 0.0;
|
||||||
if (samples.getBreaker().getPolarity() == BreakerPolarity.SOLAR)
|
if (samples.getBreaker().getPolarity() == BreakerPolarity.NORMAL)
|
||||||
|
realPower = Math.abs(realPower);
|
||||||
|
else if (samples.getBreaker().getPolarity() == BreakerPolarity.SOLAR)
|
||||||
|
realPower = -Math.abs(realPower);
|
||||||
|
else if (samples.getBreaker().getPolarity() == BreakerPolarity.BI_DIRECTIONAL_INVERTED)
|
||||||
realPower = -realPower;
|
realPower = -realPower;
|
||||||
if (samples.getBreaker().isDoublePower())
|
if (samples.getBreaker().isDoublePower())
|
||||||
realPower *= 2.0;
|
realPower *= 2.0;
|
||||||
|
@ -263,6 +341,8 @@ public class CurrentMonitor {
|
||||||
samples.setCycleCnt(0);
|
samples.setCycleCnt(0);
|
||||||
listener.onPowerEvent(new BreakerPower(samples.getBreaker().getPanel(), samples.getBreaker().getSpace(), readTime, realPower, vRms));
|
listener.onPowerEvent(new BreakerPower(samples.getBreaker().getPanel(), samples.getBreaker().getSpace(), readTime, realPower, vRms));
|
||||||
}
|
}
|
||||||
|
if (hubSample != null)
|
||||||
|
listener.onSampleEvent(hubSample);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.HubConfigService;
|
import com.lanternsoftware.datamodel.currentmonitor.HubConfigService;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
|
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.NetworkStatus;
|
import com.lanternsoftware.datamodel.currentmonitor.NetworkStatus;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||||
import com.lanternsoftware.util.CollectionUtils;
|
import com.lanternsoftware.util.CollectionUtils;
|
||||||
import com.lanternsoftware.util.NullUtils;
|
import com.lanternsoftware.util.NullUtils;
|
||||||
import com.lanternsoftware.util.ResourceLoader;
|
import com.lanternsoftware.util.ResourceLoader;
|
||||||
|
@ -65,16 +66,24 @@ public class MonitorApp {
|
||||||
private static final CurrentMonitor monitor = new CurrentMonitor();
|
private static final CurrentMonitor monitor = new CurrentMonitor();
|
||||||
private static final List<BreakerPower> readings = new ArrayList<>();
|
private static final List<BreakerPower> readings = new ArrayList<>();
|
||||||
private static String version;
|
private static String version;
|
||||||
private static final PowerListener logger = _p -> {
|
private static final PowerListener logger = new PowerListener() {
|
||||||
if (!config.isDebug()) {
|
@Override
|
||||||
_p.setHubVersion(version);
|
public void onPowerEvent(BreakerPower _power) {
|
||||||
if (breakerConfig != null)
|
if (!config.isDebug()) {
|
||||||
_p.setAccountId(breakerConfig.getAccountId());
|
_power.setHubVersion(version);
|
||||||
synchronized (readings) {
|
if (breakerConfig != null)
|
||||||
readings.add(_p);
|
_power.setAccountId(breakerConfig.getAccountId());
|
||||||
}
|
synchronized (readings) {
|
||||||
} else
|
readings.add(_power);
|
||||||
LOG.info("Panel{} - Space{} Power: {}W", _p.getPanel(), Breaker.toSpaceDisplay(_p.getSpace()), String.format("%.3f", _p.getPower()));
|
}
|
||||||
|
} else
|
||||||
|
LOG.info("Panel{} - Space{} Power: {}W", _power.getPanel(), Breaker.toSpaceDisplay(_power.getSpace()), String.format("%.3f", _power.getPower()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSampleEvent(HubSample _sample) {
|
||||||
|
post(DaoSerializer.toZipBson(_sample), "sample");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
private static final BleCharacteristicListener bluetoothListener = new BleCharacteristicListener() {
|
private static final BleCharacteristicListener bluetoothListener = new BleCharacteristicListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -214,6 +223,7 @@ public class MonitorApp {
|
||||||
if (NullUtils.isNotEmpty(config.getHost()))
|
if (NullUtils.isNotEmpty(config.getHost()))
|
||||||
host = NullUtils.terminateWith(config.getHost(), "/");
|
host = NullUtils.terminateWith(config.getHost(), "/");
|
||||||
monitor.setDebug(config.isDebug());
|
monitor.setDebug(config.isDebug());
|
||||||
|
monitor.setPostSamples(config.isPostSamples());
|
||||||
LEDFlasher.setLEDOn(false);
|
LEDFlasher.setLEDOn(false);
|
||||||
if (NullUtils.isNotEmpty(config.getAuthCode()))
|
if (NullUtils.isNotEmpty(config.getAuthCode()))
|
||||||
authCode = config.getAuthCode();
|
authCode = config.getAuthCode();
|
||||||
|
|
|
@ -17,6 +17,7 @@ public class MonitorConfig {
|
||||||
private boolean debug;
|
private boolean debug;
|
||||||
private int connectTimeout;
|
private int connectTimeout;
|
||||||
private int socketTimeout;
|
private int socketTimeout;
|
||||||
|
private boolean postSamples = false;
|
||||||
private boolean needsCalibration = true;
|
private boolean needsCalibration = true;
|
||||||
private String mqttBrokerUrl;
|
private String mqttBrokerUrl;
|
||||||
private String mqttUserName;
|
private String mqttUserName;
|
||||||
|
@ -98,6 +99,14 @@ public class MonitorConfig {
|
||||||
socketTimeout = _socketTimeout;
|
socketTimeout = _socketTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPostSamples() {
|
||||||
|
return postSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostSamples(boolean _postSamples) {
|
||||||
|
postSamples = _postSamples;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isNeedsCalibration() {
|
public boolean isNeedsCalibration() {
|
||||||
return needsCalibration;
|
return needsCalibration;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package com.lanternsoftware.currentmonitor;
|
package com.lanternsoftware.currentmonitor;
|
||||||
|
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
|
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||||
|
|
||||||
public interface PowerListener {
|
public interface PowerListener {
|
||||||
void onPowerEvent(BreakerPower _power);
|
void onPowerEvent(BreakerPower _power);
|
||||||
|
void onSampleEvent(HubSample _sample);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package com.lanternsoftware.currentmonitor;
|
|
||||||
|
|
||||||
public class PowerSample {
|
|
||||||
public double voltage;
|
|
||||||
public double current;
|
|
||||||
}
|
|
|
@ -34,6 +34,7 @@ public class MonitorConfigSerializer extends AbstractDaoSerializer<MonitorConfig
|
||||||
d.put("debug", _o.isDebug());
|
d.put("debug", _o.isDebug());
|
||||||
d.put("connect_timeout", _o.getConnectTimeout());
|
d.put("connect_timeout", _o.getConnectTimeout());
|
||||||
d.put("socket_timeout", _o.getSocketTimeout());
|
d.put("socket_timeout", _o.getSocketTimeout());
|
||||||
|
d.put("post_samples", _o.isPostSamples());
|
||||||
d.put("needs_calibration", _o.isNeedsCalibration());
|
d.put("needs_calibration", _o.isNeedsCalibration());
|
||||||
d.put("mqtt_broker_url", _o.getMqttBrokerUrl());
|
d.put("mqtt_broker_url", _o.getMqttBrokerUrl());
|
||||||
d.put("mqtt_user_name", _o.getMqttUserName());
|
d.put("mqtt_user_name", _o.getMqttUserName());
|
||||||
|
@ -57,6 +58,7 @@ public class MonitorConfigSerializer extends AbstractDaoSerializer<MonitorConfig
|
||||||
o.setDebug(DaoSerializer.getBoolean(_d, "debug"));
|
o.setDebug(DaoSerializer.getBoolean(_d, "debug"));
|
||||||
o.setConnectTimeout(DaoSerializer.getInteger(_d, "connect_timeout"));
|
o.setConnectTimeout(DaoSerializer.getInteger(_d, "connect_timeout"));
|
||||||
o.setSocketTimeout(DaoSerializer.getInteger(_d, "socket_timeout"));
|
o.setSocketTimeout(DaoSerializer.getInteger(_d, "socket_timeout"));
|
||||||
|
o.setPostSamples(DaoSerializer.getBoolean(_d, "post_samples"));
|
||||||
o.setNeedsCalibration(DaoSerializer.getBoolean(_d, "needs_calibration"));
|
o.setNeedsCalibration(DaoSerializer.getBoolean(_d, "needs_calibration"));
|
||||||
o.setMqttBrokerUrl(DaoSerializer.getString(_d, "mqtt_broker_url"));
|
o.setMqttBrokerUrl(DaoSerializer.getString(_d, "mqtt_broker_url"));
|
||||||
o.setMqttUserName(DaoSerializer.getString(_d, "mqtt_user_name"));
|
o.setMqttUserName(DaoSerializer.getString(_d, "mqtt_user_name"));
|
||||||
|
|
|
@ -8,6 +8,7 @@ import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
|
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
|
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus;
|
import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||||
import com.lanternsoftware.util.DateRange;
|
import com.lanternsoftware.util.DateRange;
|
||||||
import com.lanternsoftware.util.dao.auth.AuthCode;
|
import com.lanternsoftware.util.dao.auth.AuthCode;
|
||||||
import com.lanternsoftware.util.dao.mongo.MongoProxy;
|
import com.lanternsoftware.util.dao.mongo.MongoProxy;
|
||||||
|
@ -44,7 +45,6 @@ public interface CurrentMonitorDao {
|
||||||
void putConfig(BreakerConfig _config);
|
void putConfig(BreakerConfig _config);
|
||||||
|
|
||||||
void rebuildSummaries(int _accountId);
|
void rebuildSummaries(int _accountId);
|
||||||
void rebuildSummariesAsync(int _accountId);
|
|
||||||
void rebuildSummaries(int _accountId, Date _start, Date _end);
|
void rebuildSummaries(int _accountId, Date _start, Date _end);
|
||||||
|
|
||||||
String addPasswordResetKey(String _email);
|
String addPasswordResetKey(String _email);
|
||||||
|
@ -66,5 +66,8 @@ public interface CurrentMonitorDao {
|
||||||
List<HubCommand> getAllHubCommands();
|
List<HubCommand> getAllHubCommands();
|
||||||
void deleteHubCommand(String _id);
|
void deleteHubCommand(String _id);
|
||||||
|
|
||||||
|
void putHubSample(HubSample _sample);
|
||||||
|
List<HubSample> getSamplesForAccount(int _accountId);
|
||||||
|
|
||||||
MongoProxy getProxy();
|
MongoProxy getProxy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.archive.BreakerEnergyArchive;
|
import com.lanternsoftware.datamodel.currentmonitor.archive.BreakerEnergyArchive;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.archive.DailyEnergyArchive;
|
import com.lanternsoftware.datamodel.currentmonitor.archive.DailyEnergyArchive;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.archive.MonthlyEnergyArchive;
|
import com.lanternsoftware.datamodel.currentmonitor.archive.MonthlyEnergyArchive;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||||
import com.lanternsoftware.util.CollectionUtils;
|
import com.lanternsoftware.util.CollectionUtils;
|
||||||
import com.lanternsoftware.util.DateRange;
|
import com.lanternsoftware.util.DateRange;
|
||||||
import com.lanternsoftware.util.DateUtils;
|
import com.lanternsoftware.util.DateUtils;
|
||||||
|
@ -487,17 +488,13 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void rebuildSummariesAsync(int _accountId) {
|
|
||||||
executor.submit(() -> rebuildSummaries(_accountId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void rebuildSummaries(int _accountId) {
|
public void rebuildSummaries(int _accountId) {
|
||||||
HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute"));
|
HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute"));
|
||||||
if (firstMinute == null)
|
if (firstMinute == null)
|
||||||
return;
|
return;
|
||||||
rebuildSummaries(_accountId, firstMinute.getMinuteAsDate(), new Date());
|
HubPowerMinute lastMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sortDesc("minute"));
|
||||||
|
rebuildSummaries(_accountId, firstMinute.getMinuteAsDate(), lastMinute.getMinuteAsDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -812,6 +809,16 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
|
||||||
proxy.delete(HubCommand.class, new DaoQuery("_id", _id));
|
proxy.delete(HubCommand.class, new DaoQuery("_id", _id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putHubSample(HubSample _sample) {
|
||||||
|
proxy.save(_sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HubSample> getSamplesForAccount(int _accountId) {
|
||||||
|
return proxy.query(HubSample.class, new DaoQuery("account_id", _accountId));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MongoProxy getProxy() {
|
public MongoProxy getProxy() {
|
||||||
return proxy;
|
return proxy;
|
||||||
|
|
|
@ -21,6 +21,7 @@ public class Breaker implements IIdentical<Breaker> {
|
||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
private int sizeAmps;
|
private int sizeAmps;
|
||||||
|
private int phaseOffsetNs;
|
||||||
private double calibrationFactor;
|
private double calibrationFactor;
|
||||||
private double lowPassFilter;
|
private double lowPassFilter;
|
||||||
private BreakerPolarity polarity;
|
private BreakerPolarity polarity;
|
||||||
|
@ -140,6 +141,14 @@ public class Breaker implements IIdentical<Breaker> {
|
||||||
sizeAmps = _sizeAmps;
|
sizeAmps = _sizeAmps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPhaseOffsetNs() {
|
||||||
|
return phaseOffsetNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhaseOffsetNs(int _phaseOffsetNs) {
|
||||||
|
phaseOffsetNs = _phaseOffsetNs;
|
||||||
|
}
|
||||||
|
|
||||||
public double getLowPassFilter() {
|
public double getLowPassFilter() {
|
||||||
return Math.abs(lowPassFilter) < 0.05 ? 1.6 : lowPassFilter;
|
return Math.abs(lowPassFilter) < 0.05 ? 1.6 : lowPassFilter;
|
||||||
}
|
}
|
||||||
|
@ -278,7 +287,7 @@ public class Breaker implements IIdentical<Breaker> {
|
||||||
@Override
|
@Override
|
||||||
public boolean isIdentical(Breaker _o) {
|
public boolean isIdentical(Breaker _o) {
|
||||||
if (this == _o) return true;
|
if (this == _o) return true;
|
||||||
return panel == _o.panel && space == _o.space && meter == _o.meter && hub == _o.hub && port == _o.port && sizeAmps == _o.sizeAmps && Double.compare(_o.calibrationFactor, calibrationFactor) == 0 && Double.compare(_o.lowPassFilter, lowPassFilter) == 0 && doublePower == _o.doublePower && Objects.equals(name, _o.name) && Objects.equals(description, _o.description) && polarity == _o.polarity && type == _o.type;
|
return panel == _o.panel && space == _o.space && meter == _o.meter && hub == _o.hub && port == _o.port && sizeAmps == _o.sizeAmps && phaseOffsetNs == _o.phaseOffsetNs && Double.compare(_o.calibrationFactor, calibrationFactor) == 0 && Double.compare(_o.lowPassFilter, lowPassFilter) == 0 && doublePower == _o.doublePower && Objects.equals(name, _o.name) && Objects.equals(description, _o.description) && polarity == _o.polarity && type == _o.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,6 +11,8 @@ public class BreakerHub implements IIdentical<BreakerHub> {
|
||||||
private int hub;
|
private int hub;
|
||||||
private double voltageCalibrationFactor;
|
private double voltageCalibrationFactor;
|
||||||
private double portCalibrationFactor;
|
private double portCalibrationFactor;
|
||||||
|
private int phaseCnt;
|
||||||
|
private int phaseOffsetNs;
|
||||||
private int frequency;
|
private int frequency;
|
||||||
private String bluetoothMac;
|
private String bluetoothMac;
|
||||||
|
|
||||||
|
@ -46,8 +48,24 @@ public class BreakerHub implements IIdentical<BreakerHub> {
|
||||||
portCalibrationFactor = _portCalibrationFactor;
|
portCalibrationFactor = _portCalibrationFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPhaseCnt() {
|
||||||
|
return phaseCnt == 0 ? 2 : phaseCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhaseCnt(int _phaseCnt) {
|
||||||
|
phaseCnt = _phaseCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPhaseOffsetNs() {
|
||||||
|
return phaseOffsetNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhaseOffsetNs(int _phaseOffsetNs) {
|
||||||
|
phaseOffsetNs = _phaseOffsetNs;
|
||||||
|
}
|
||||||
|
|
||||||
public int getFrequency() {
|
public int getFrequency() {
|
||||||
return frequency;
|
return frequency == 0 ? 60 : frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFrequency(int _frequency) {
|
public void setFrequency(int _frequency) {
|
||||||
|
@ -73,7 +91,7 @@ public class BreakerHub implements IIdentical<BreakerHub> {
|
||||||
@Override
|
@Override
|
||||||
public boolean isIdentical(BreakerHub _o) {
|
public boolean isIdentical(BreakerHub _o) {
|
||||||
if (this == _o) return true;
|
if (this == _o) return true;
|
||||||
return hub == _o.hub && Double.compare(_o.voltageCalibrationFactor, voltageCalibrationFactor) == 0 && Double.compare(_o.portCalibrationFactor, portCalibrationFactor) == 0 && frequency == _o.frequency && Objects.equals(bluetoothMac, _o.bluetoothMac);
|
return hub == _o.hub && Double.compare(_o.voltageCalibrationFactor, voltageCalibrationFactor) == 0 && Double.compare(_o.portCalibrationFactor, portCalibrationFactor) == 0 && getPhaseCnt() == _o.getPhaseCnt() && getPhaseOffsetNs() == _o.getPhaseOffsetNs() && getFrequency() == _o.getFrequency() && Objects.equals(bluetoothMac, _o.bluetoothMac);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2,5 +2,7 @@ package com.lanternsoftware.datamodel.currentmonitor;
|
||||||
|
|
||||||
public enum BreakerPolarity {
|
public enum BreakerPolarity {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
SOLAR;
|
SOLAR,
|
||||||
|
BI_DIRECTIONAL,
|
||||||
|
BI_DIRECTIONAL_INVERTED
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,9 +140,9 @@ public class EnergySummary {
|
||||||
idx = 0;
|
idx = 0;
|
||||||
double flow = 0.0;
|
double flow = 0.0;
|
||||||
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
|
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
|
||||||
if ((b.getPolarity() == BreakerPolarity.SOLAR) && (meter.flow[idx] < 0.0))
|
if (power < 0 && (meter.flow[idx] < 0.0))
|
||||||
flow -= meter.flow[idx] * (power / meter.solar[idx]);
|
flow -= meter.flow[idx] * (power / meter.solar[idx]);
|
||||||
else if ((b.getPolarity() != BreakerPolarity.SOLAR) && (meter.flow[idx] > 0.0))
|
else if (power > 0 && (meter.flow[idx] > 0.0))
|
||||||
flow += meter.flow[idx] * (power / meter.usage[idx]);
|
flow += meter.flow[idx] * (power / meter.usage[idx]);
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
@ -152,11 +152,11 @@ public class EnergySummary {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetEnergy(Date _readTime) {
|
public void resetEnergy(Date _readTime) {
|
||||||
if (energy == null)
|
if (energy != null) {
|
||||||
return;
|
int idx = viewMode.blockIndex(start, _readTime, timezone);
|
||||||
int idx = viewMode.blockIndex(start, _readTime, timezone);
|
if (idx < energy.length)
|
||||||
if (idx < energy.length)
|
energy[idx] = 0f;
|
||||||
energy[idx] = 0f;
|
}
|
||||||
for (EnergySummary subGroup : CollectionUtils.makeNotNull(subGroups)) {
|
for (EnergySummary subGroup : CollectionUtils.makeNotNull(subGroups)) {
|
||||||
subGroup.resetEnergy(_readTime);
|
subGroup.resetEnergy(_readTime);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ public class BreakerHubSerializer extends AbstractDaoSerializer<BreakerHub>
|
||||||
d.put("hub", _o.getHub());
|
d.put("hub", _o.getHub());
|
||||||
d.put("voltage_calibration_factor", _o.getRawVoltageCalibrationFactor());
|
d.put("voltage_calibration_factor", _o.getRawVoltageCalibrationFactor());
|
||||||
d.put("port_calibration_factor", _o.getRawPortCalibrationFactor());
|
d.put("port_calibration_factor", _o.getRawPortCalibrationFactor());
|
||||||
|
d.put("phase_cnt", _o.getPhaseCnt());
|
||||||
|
d.put("phase_offset_ns", _o.getPhaseOffsetNs());
|
||||||
d.put("frequency", _o.getFrequency());
|
d.put("frequency", _o.getFrequency());
|
||||||
d.put("bluetooth_mac", _o.getBluetoothMac());
|
d.put("bluetooth_mac", _o.getBluetoothMac());
|
||||||
return d;
|
return d;
|
||||||
|
@ -40,6 +42,8 @@ public class BreakerHubSerializer extends AbstractDaoSerializer<BreakerHub>
|
||||||
o.setHub(DaoSerializer.getInteger(_d, "hub"));
|
o.setHub(DaoSerializer.getInteger(_d, "hub"));
|
||||||
o.setVoltageCalibrationFactor(DaoSerializer.getDouble(_d, "voltage_calibration_factor"));
|
o.setVoltageCalibrationFactor(DaoSerializer.getDouble(_d, "voltage_calibration_factor"));
|
||||||
o.setPortCalibrationFactor(DaoSerializer.getDouble(_d, "port_calibration_factor"));
|
o.setPortCalibrationFactor(DaoSerializer.getDouble(_d, "port_calibration_factor"));
|
||||||
|
o.setPhaseCnt(DaoSerializer.getInteger(_d, "phase_cnt"));
|
||||||
|
o.setPhaseOffsetNs(DaoSerializer.getInteger(_d, "phase_offset_ns"));
|
||||||
o.setFrequency(DaoSerializer.getInteger(_d, "frequency"));
|
o.setFrequency(DaoSerializer.getInteger(_d, "frequency"));
|
||||||
o.setBluetoothMac(DaoSerializer.getString(_d, "bluetooth_mac"));
|
o.setBluetoothMac(DaoSerializer.getString(_d, "bluetooth_mac"));
|
||||||
return o;
|
return o;
|
||||||
|
|
|
@ -35,6 +35,7 @@ public class BreakerSerializer extends AbstractDaoSerializer<Breaker>
|
||||||
d.put("name", _o.getName());
|
d.put("name", _o.getName());
|
||||||
d.put("description", _o.getDescription());
|
d.put("description", _o.getDescription());
|
||||||
d.put("size_amps", _o.getSizeAmps());
|
d.put("size_amps", _o.getSizeAmps());
|
||||||
|
d.put("phase_offset_ns", _o.getPhaseOffsetNs());
|
||||||
d.put("calibration_factor", _o.getCalibrationFactor());
|
d.put("calibration_factor", _o.getCalibrationFactor());
|
||||||
d.put("low_pass_filter", _o.getLowPassFilter());
|
d.put("low_pass_filter", _o.getLowPassFilter());
|
||||||
d.put("polarity", DaoSerializer.toEnumName(_o.getPolarity()));
|
d.put("polarity", DaoSerializer.toEnumName(_o.getPolarity()));
|
||||||
|
@ -56,6 +57,7 @@ public class BreakerSerializer extends AbstractDaoSerializer<Breaker>
|
||||||
o.setName(DaoSerializer.getString(_d, "name"));
|
o.setName(DaoSerializer.getString(_d, "name"));
|
||||||
o.setDescription(DaoSerializer.getString(_d, "description"));
|
o.setDescription(DaoSerializer.getString(_d, "description"));
|
||||||
o.setSizeAmps(DaoSerializer.getInteger(_d, "size_amps"));
|
o.setSizeAmps(DaoSerializer.getInteger(_d, "size_amps"));
|
||||||
|
o.setPhaseOffsetNs(DaoSerializer.getInteger(_d, "phase_offset_ns"));
|
||||||
o.setCalibrationFactor(DaoSerializer.getDouble(_d, "calibration_factor"));
|
o.setCalibrationFactor(DaoSerializer.getDouble(_d, "calibration_factor"));
|
||||||
o.setLowPassFilter(DaoSerializer.getDouble(_d, "low_pass_filter"));
|
o.setLowPassFilter(DaoSerializer.getDouble(_d, "low_pass_filter"));
|
||||||
o.setPolarity(DaoSerializer.getEnum(_d, "polarity", BreakerPolarity.class));
|
o.setPolarity(DaoSerializer.getEnum(_d, "polarity", BreakerPolarity.class));
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.lanternsoftware.datamodel.currentmonitor.hub;
|
||||||
|
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.Breaker;
|
||||||
|
import com.lanternsoftware.util.dao.annotations.DBSerializable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@DBSerializable
|
||||||
|
public class BreakerSample {
|
||||||
|
private int panel;
|
||||||
|
private int space;
|
||||||
|
private List<PowerSample> samples;
|
||||||
|
|
||||||
|
public int key() {
|
||||||
|
return Breaker.intKey(panel, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPanel() {
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPanel(int _panel) {
|
||||||
|
panel = _panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSpace() {
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpace(int _space) {
|
||||||
|
space = _space;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PowerSample> getSamples() {
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSamples(List<PowerSample> _samples) {
|
||||||
|
samples = _samples;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.lanternsoftware.datamodel.currentmonitor.hub;
|
||||||
|
|
||||||
|
import com.lanternsoftware.util.DateUtils;
|
||||||
|
import com.lanternsoftware.util.dao.annotations.DBSerializable;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@DBSerializable(autogen = false)
|
||||||
|
public class HubSample {
|
||||||
|
private int accountId;
|
||||||
|
private Date sampleDate;
|
||||||
|
private List<BreakerSample> breakers;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return String.format("%d-%d", accountId, DateUtils.toLong(sampleDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAccountId() {
|
||||||
|
return accountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccountId(int _accountId) {
|
||||||
|
accountId = _accountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getSampleDate() {
|
||||||
|
return sampleDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSampleDate(Date _sampleDate) {
|
||||||
|
sampleDate = _sampleDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BreakerSample> getBreakers() {
|
||||||
|
return breakers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBreakers(List<BreakerSample> _breakers) {
|
||||||
|
breakers = _breakers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.lanternsoftware.datamodel.currentmonitor.hub;
|
||||||
|
|
||||||
|
import com.lanternsoftware.util.dao.annotations.DBSerializable;
|
||||||
|
|
||||||
|
@DBSerializable
|
||||||
|
public class PowerSample {
|
||||||
|
public long nanoTime;
|
||||||
|
public int cycle;
|
||||||
|
public double voltage;
|
||||||
|
public double current;
|
||||||
|
|
||||||
|
public long getNanoTime() {
|
||||||
|
return nanoTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNanoTime(long _nanoTime) {
|
||||||
|
nanoTime = _nanoTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCycle() {
|
||||||
|
return cycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCycle(int _cycle) {
|
||||||
|
cycle = _cycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getVoltage() {
|
||||||
|
return voltage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVoltage(double _voltage) {
|
||||||
|
voltage = _voltage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCurrent() {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrent(double _current) {
|
||||||
|
current = _current;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.lanternsoftware.datamodel.currentmonitor.hub.dao;
|
||||||
|
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.BreakerSample;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.PowerSample;
|
||||||
|
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
|
||||||
|
import com.lanternsoftware.util.dao.DaoEntity;
|
||||||
|
import com.lanternsoftware.util.dao.DaoProxyType;
|
||||||
|
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BreakerSampleSerializer extends AbstractDaoSerializer<BreakerSample>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Class<BreakerSample> getSupportedClass()
|
||||||
|
{
|
||||||
|
return BreakerSample.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DaoProxyType> getSupportedProxies() {
|
||||||
|
return Collections.singletonList(DaoProxyType.MONGO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DaoEntity toDaoEntity(BreakerSample _o)
|
||||||
|
{
|
||||||
|
DaoEntity d = new DaoEntity();
|
||||||
|
d.put("panel", _o.getPanel());
|
||||||
|
d.put("space", _o.getSpace());
|
||||||
|
d.put("samples", DaoSerializer.toDaoEntities(_o.getSamples(), DaoProxyType.MONGO));
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BreakerSample fromDaoEntity(DaoEntity _d)
|
||||||
|
{
|
||||||
|
BreakerSample o = new BreakerSample();
|
||||||
|
o.setPanel(DaoSerializer.getInteger(_d, "panel"));
|
||||||
|
o.setSpace(DaoSerializer.getInteger(_d, "space"));
|
||||||
|
o.setSamples(DaoSerializer.getList(_d, "samples", PowerSample.class));
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.lanternsoftware.datamodel.currentmonitor.hub.dao;
|
||||||
|
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.BreakerSample;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||||
|
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
|
||||||
|
import com.lanternsoftware.util.dao.DaoEntity;
|
||||||
|
import com.lanternsoftware.util.dao.DaoProxyType;
|
||||||
|
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HubSampleSerializer extends AbstractDaoSerializer<HubSample>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Class<HubSample> getSupportedClass()
|
||||||
|
{
|
||||||
|
return HubSample.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DaoProxyType> getSupportedProxies() {
|
||||||
|
return Collections.singletonList(DaoProxyType.MONGO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DaoEntity toDaoEntity(HubSample _o)
|
||||||
|
{
|
||||||
|
DaoEntity d = new DaoEntity();
|
||||||
|
d.put("_id", _o.getId());
|
||||||
|
d.put("account_id", _o.getAccountId());
|
||||||
|
d.put("sample_date", DaoSerializer.toLong(_o.getSampleDate()));
|
||||||
|
d.put("breakers", DaoSerializer.toDaoEntities(_o.getBreakers(), DaoProxyType.MONGO));
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HubSample fromDaoEntity(DaoEntity _d)
|
||||||
|
{
|
||||||
|
HubSample o = new HubSample();
|
||||||
|
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
|
||||||
|
o.setSampleDate(DaoSerializer.getDate(_d, "sample_date"));
|
||||||
|
o.setBreakers(DaoSerializer.getList(_d, "breakers", BreakerSample.class));
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.lanternsoftware.datamodel.currentmonitor.hub.dao;
|
||||||
|
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.PowerSample;
|
||||||
|
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
|
||||||
|
import com.lanternsoftware.util.dao.DaoEntity;
|
||||||
|
import com.lanternsoftware.util.dao.DaoProxyType;
|
||||||
|
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PowerSampleSerializer extends AbstractDaoSerializer<PowerSample>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Class<PowerSample> getSupportedClass()
|
||||||
|
{
|
||||||
|
return PowerSample.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DaoProxyType> getSupportedProxies() {
|
||||||
|
return Collections.singletonList(DaoProxyType.MONGO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DaoEntity toDaoEntity(PowerSample _o)
|
||||||
|
{
|
||||||
|
DaoEntity d = new DaoEntity();
|
||||||
|
d.put("nano_time", _o.getNanoTime());
|
||||||
|
d.put("cycle", _o.getCycle());
|
||||||
|
d.put("voltage", _o.getVoltage());
|
||||||
|
d.put("current", _o.getCurrent());
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PowerSample fromDaoEntity(DaoEntity _d)
|
||||||
|
{
|
||||||
|
PowerSample o = new PowerSample();
|
||||||
|
o.setNanoTime(DaoSerializer.getLong(_d, "nano_time"));
|
||||||
|
o.setCycle(DaoSerializer.getInteger(_d, "cycle"));
|
||||||
|
o.setVoltage(DaoSerializer.getDouble(_d, "voltage"));
|
||||||
|
o.setCurrent(DaoSerializer.getDouble(_d, "current"));
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,3 +26,6 @@ com.lanternsoftware.datamodel.currentmonitor.dao.MeterSerializer
|
||||||
com.lanternsoftware.datamodel.currentmonitor.dao.NetworkStatusSerializer
|
com.lanternsoftware.datamodel.currentmonitor.dao.NetworkStatusSerializer
|
||||||
com.lanternsoftware.datamodel.currentmonitor.dao.SequenceSerializer
|
com.lanternsoftware.datamodel.currentmonitor.dao.SequenceSerializer
|
||||||
com.lanternsoftware.datamodel.currentmonitor.dao.SignupResponseSerializer
|
com.lanternsoftware.datamodel.currentmonitor.dao.SignupResponseSerializer
|
||||||
|
com.lanternsoftware.datamodel.currentmonitor.hub.dao.BreakerSampleSerializer
|
||||||
|
com.lanternsoftware.datamodel.currentmonitor.hub.dao.HubSampleSerializer
|
||||||
|
com.lanternsoftware.datamodel.currentmonitor.hub.dao.PowerSampleSerializer
|
||||||
|
|
|
@ -18,9 +18,12 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class Globals implements ServletContextListener {
|
public class Globals implements ServletContextListener {
|
||||||
public static CurrentMonitorDao dao;
|
public static CurrentMonitorDao dao;
|
||||||
|
public static ExecutorService opsExecutor;
|
||||||
private static final Map<Integer, Map<Integer, List<HubCommand>>> commands = new HashMap<>();
|
private static final Map<Integer, Map<Integer, List<HubCommand>>> commands = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -28,10 +31,12 @@ public class Globals implements ServletContextListener {
|
||||||
dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||||
RulesEngine.instance().start();
|
RulesEngine.instance().start();
|
||||||
RulesEngine.instance().schedule(new CommandTask(), 0);
|
RulesEngine.instance().schedule(new CommandTask(), 0);
|
||||||
|
opsExecutor = Executors.newFixedThreadPool(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contextDestroyed(ServletContextEvent sce) {
|
public void contextDestroyed(ServletContextEvent sce) {
|
||||||
|
opsExecutor.shutdown();
|
||||||
dao.shutdown();
|
dao.shutdown();
|
||||||
HttpFactory.shutdown();
|
HttpFactory.shutdown();
|
||||||
RulesEngine.shutdown();
|
RulesEngine.shutdown();
|
||||||
|
|
|
@ -4,6 +4,7 @@ import com.lanternsoftware.currentmonitor.context.Globals;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
|
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
|
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
|
||||||
import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
|
import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
|
||||||
|
import com.lanternsoftware.rules.RulesEngine;
|
||||||
import com.lanternsoftware.util.dao.auth.AuthCode;
|
import com.lanternsoftware.util.dao.auth.AuthCode;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -40,6 +41,8 @@ public class ConfigServlet extends SecureServiceServlet {
|
||||||
if ((oldConfig == null) || !oldConfig.isIdentical(config))
|
if ((oldConfig == null) || !oldConfig.isIdentical(config))
|
||||||
Globals.dao.putHubCommand(new HubCommand(config.getAccountId(), HubConfigCharacteristic.ReloadConfig, null));
|
Globals.dao.putHubCommand(new HubCommand(config.getAccountId(), HubConfigCharacteristic.ReloadConfig, null));
|
||||||
Globals.dao.putConfig(config);
|
Globals.dao.putConfig(config);
|
||||||
zipBsonResponse(_rep, Globals.dao.getMergedConfig(_authCode));
|
config = Globals.dao.getMergedConfig(_authCode);
|
||||||
|
RulesEngine.instance().sendFcmMessage(config.getAccountId(), config);
|
||||||
|
zipBsonResponse(_rep, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,14 @@ public class RebuildSummariesServlet extends SecureServiceServlet {
|
||||||
if (_authCode.getAccountId() == 100) {
|
if (_authCode.getAccountId() == 100) {
|
||||||
String[] path = path(_req);
|
String[] path = path(_req);
|
||||||
if (path.length > 0) {
|
if (path.length > 0) {
|
||||||
Globals.dao.rebuildSummariesAsync(DaoSerializer.toInteger(CollectionUtils.get(path, 0)));
|
Globals.opsExecutor.submit(() -> Globals.dao.rebuildSummaries(DaoSerializer.toInteger(CollectionUtils.get(path, 0))));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (String sId : Globals.dao.getProxy().queryForField(Account.class, null, "_id")) {
|
for (String sId : Globals.dao.getProxy().queryForField(Account.class, null, "_id")) {
|
||||||
int id = DaoSerializer.toInteger(sId);
|
int id = DaoSerializer.toInteger(sId);
|
||||||
if (id != 0)
|
if (id != 0) {
|
||||||
Globals.dao.rebuildSummariesAsync(id);
|
Globals.opsExecutor.submit(() -> Globals.dao.rebuildSummaries(id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.lanternsoftware.currentmonitor.servlet;
|
||||||
|
|
||||||
|
import com.lanternsoftware.currentmonitor.context.Globals;
|
||||||
|
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||||
|
import com.lanternsoftware.util.dao.auth.AuthCode;
|
||||||
|
|
||||||
|
import javax.servlet.annotation.WebServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@WebServlet("/sample")
|
||||||
|
public class SampleServlet extends SecureServiceServlet {
|
||||||
|
@Override
|
||||||
|
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
|
||||||
|
HubSample sample = getRequestPayload(_req, HubSample.class);
|
||||||
|
if (sample == null)
|
||||||
|
return;
|
||||||
|
sample.setAccountId(_authCode.getAccountId());
|
||||||
|
Globals.dao.putHubSample(sample);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,9 +4,6 @@ import com.lanternsoftware.util.ResourceLoader;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
|
|
||||||
public class PIGPIO {
|
public class PIGPIO {
|
||||||
protected static final Logger LOG = LoggerFactory.getLogger(PIGPIO.class);
|
protected static final Logger LOG = LoggerFactory.getLogger(PIGPIO.class);
|
||||||
|
|
||||||
|
@ -20,10 +17,11 @@ public class PIGPIO {
|
||||||
osArch = "armhf";
|
osArch = "armhf";
|
||||||
String path = "/lib/" + osArch + "/lantern-pigpio.so";
|
String path = "/lib/" + osArch + "/lantern-pigpio.so";
|
||||||
byte[] file = ResourceLoader.getByteArrayResource(PIGPIO.class, path);
|
byte[] file = ResourceLoader.getByteArrayResource(PIGPIO.class, path);
|
||||||
String target = Files.createTempFile("lantern-pigpio", "so").toAbsolutePath().toString();
|
LOG.info("library size: {}", file.length);
|
||||||
ResourceLoader.writeFile(target, file);
|
String libPath = "/opt/currentmonitor/lantern-pigpio.so";
|
||||||
System.load(target);
|
ResourceLoader.writeFile(libPath, file);
|
||||||
} catch (IOException _e) {
|
System.load(libPath);
|
||||||
|
} catch (Exception _e) {
|
||||||
LOG.error("Failed to load lantern-pigpio.so from resource", _e);
|
LOG.error("Failed to load lantern-pigpio.so from resource", _e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,16 @@ import com.lanternsoftware.datamodel.rules.Criteria;
|
||||||
import com.lanternsoftware.datamodel.rules.Event;
|
import com.lanternsoftware.datamodel.rules.Event;
|
||||||
import com.lanternsoftware.datamodel.rules.EventId;
|
import com.lanternsoftware.datamodel.rules.EventId;
|
||||||
import com.lanternsoftware.datamodel.rules.EventType;
|
import com.lanternsoftware.datamodel.rules.EventType;
|
||||||
|
import com.lanternsoftware.datamodel.rules.FcmDevice;
|
||||||
import com.lanternsoftware.datamodel.rules.Rule;
|
import com.lanternsoftware.datamodel.rules.Rule;
|
||||||
import com.lanternsoftware.rules.actions.ActionImpl;
|
import com.lanternsoftware.rules.actions.ActionImpl;
|
||||||
import com.lanternsoftware.util.CollectionUtils;
|
import com.lanternsoftware.util.CollectionUtils;
|
||||||
import com.lanternsoftware.util.DateUtils;
|
import com.lanternsoftware.util.DateUtils;
|
||||||
import com.lanternsoftware.util.external.LanternFiles;
|
import com.lanternsoftware.util.cloudservices.google.FirebaseHelper;
|
||||||
|
import com.lanternsoftware.util.dao.DaoEntity;
|
||||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||||
|
import com.lanternsoftware.util.external.LanternFiles;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -36,6 +39,7 @@ import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class RulesEngine {
|
public class RulesEngine {
|
||||||
protected static final Logger LOG = LoggerFactory.getLogger(RulesEngine.class);
|
protected static final Logger LOG = LoggerFactory.getLogger(RulesEngine.class);
|
||||||
|
protected static final FirebaseHelper firebaseHelper = new FirebaseHelper(LanternFiles.CONFIG_PATH + "google_account_key.json");
|
||||||
|
|
||||||
private static RulesEngine INSTANCE;
|
private static RulesEngine INSTANCE;
|
||||||
private final ExecutorService executor = Executors.newCachedThreadPool();
|
private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||||
|
@ -69,6 +73,15 @@ public class RulesEngine {
|
||||||
return dao;
|
return dao;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendFcmMessage(int _accountId, Object _payload) {
|
||||||
|
List<FcmDevice> devices = RulesEngine.instance().dao().getFcmDevicesForAccount(_accountId);
|
||||||
|
if (devices.isEmpty())
|
||||||
|
return;
|
||||||
|
for (FcmDevice device : devices) {
|
||||||
|
firebaseHelper.sendMessage(device.getToken(), new DaoEntity("payload", DaoSerializer.toBase64ZipBson(_payload)).and("payloadClass", _payload.getClass().getCanonicalName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void fireEvent(Event _event) {
|
public void fireEvent(Event _event) {
|
||||||
if (_event.getType() != EventType.TIME)
|
if (_event.getType() != EventType.TIME)
|
||||||
dao.putEvent(_event);
|
dao.putEvent(_event);
|
||||||
|
@ -108,6 +121,8 @@ public class RulesEngine {
|
||||||
return;
|
return;
|
||||||
Collection<Date> dates = CollectionUtils.aggregate(rules, _r->CollectionUtils.transform(_r.getAllCriteria(), _c->_c.getNextTriggerDate(tz)));
|
Collection<Date> dates = CollectionUtils.aggregate(rules, _r->CollectionUtils.transform(_r.getAllCriteria(), _c->_c.getNextTriggerDate(tz)));
|
||||||
Date nextDate = CollectionUtils.getSmallest(dates);
|
Date nextDate = CollectionUtils.getSmallest(dates);
|
||||||
|
if (nextDate == null)
|
||||||
|
return;
|
||||||
LOG.info("Scheduling next time event for account {} at {}", _accountId, DateUtils.format("MM/dd/yyyy HH:mm:ss", nextDate));
|
LOG.info("Scheduling next time event for account {} at {}", _accountId, DateUtils.format("MM/dd/yyyy HH:mm:ss", nextDate));
|
||||||
nextTask = new EventTimeTask(_accountId, nextDate);
|
nextTask = new EventTimeTask(_accountId, nextDate);
|
||||||
timer.schedule(nextTask, nextDate);
|
timer.schedule(nextTask, nextDate);
|
||||||
|
|
|
@ -1,28 +1,11 @@
|
||||||
package com.lanternsoftware.rules.actions;
|
package com.lanternsoftware.rules.actions;
|
||||||
|
|
||||||
import com.lanternsoftware.datamodel.rules.Alert;
|
import com.lanternsoftware.datamodel.rules.Alert;
|
||||||
import com.lanternsoftware.datamodel.rules.FcmDevice;
|
|
||||||
import com.lanternsoftware.datamodel.rules.Rule;
|
import com.lanternsoftware.datamodel.rules.Rule;
|
||||||
import com.lanternsoftware.rules.RulesEngine;
|
import com.lanternsoftware.rules.RulesEngine;
|
||||||
import com.lanternsoftware.util.dao.DaoEntity;
|
|
||||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
|
||||||
import com.lanternsoftware.util.external.LanternFiles;
|
|
||||||
import com.lanternsoftware.util.cloudservices.google.FirebaseHelper;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public abstract class AbstractAlertAction implements ActionImpl {
|
public abstract class AbstractAlertAction implements ActionImpl {
|
||||||
protected static final Logger logger = LoggerFactory.getLogger(AbstractAlertAction.class);
|
|
||||||
protected static final FirebaseHelper firebaseHelper = new FirebaseHelper(LanternFiles.CONFIG_PATH + "google_account_key.json");
|
|
||||||
|
|
||||||
protected void sendAlert(Rule _rule, Alert _alert) {
|
protected void sendAlert(Rule _rule, Alert _alert) {
|
||||||
List<FcmDevice> devices = RulesEngine.instance().dao().getFcmDevicesForAccount(_rule.getAccountId());
|
RulesEngine.instance().sendFcmMessage(_rule.getAccountId(), _alert);
|
||||||
if (devices.isEmpty())
|
|
||||||
return;
|
|
||||||
for (FcmDevice device : devices) {
|
|
||||||
firebaseHelper.sendMessage(device.getToken(), new DaoEntity("payload", DaoSerializer.toBase64ZipBson(_alert)).and("payloadClass", Alert.class.getCanonicalName()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.lanternsoftware.zwave.context;
|
||||||
|
|
||||||
import com.lanternsoftware.datamodel.rules.Event;
|
import com.lanternsoftware.datamodel.rules.Event;
|
||||||
import com.lanternsoftware.datamodel.rules.EventType;
|
import com.lanternsoftware.datamodel.rules.EventType;
|
||||||
import com.lanternsoftware.util.dao.auth.AuthCode;
|
|
||||||
import com.lanternsoftware.datamodel.zwave.Switch;
|
import com.lanternsoftware.datamodel.zwave.Switch;
|
||||||
import com.lanternsoftware.datamodel.zwave.SwitchSchedule;
|
import com.lanternsoftware.datamodel.zwave.SwitchSchedule;
|
||||||
import com.lanternsoftware.datamodel.zwave.SwitchTransition;
|
import com.lanternsoftware.datamodel.zwave.SwitchTransition;
|
||||||
|
@ -10,13 +9,14 @@ import com.lanternsoftware.datamodel.zwave.ThermostatMode;
|
||||||
import com.lanternsoftware.datamodel.zwave.ZWaveConfig;
|
import com.lanternsoftware.datamodel.zwave.ZWaveConfig;
|
||||||
import com.lanternsoftware.util.CollectionUtils;
|
import com.lanternsoftware.util.CollectionUtils;
|
||||||
import com.lanternsoftware.util.DateUtils;
|
import com.lanternsoftware.util.DateUtils;
|
||||||
import com.lanternsoftware.util.external.LanternFiles;
|
|
||||||
import com.lanternsoftware.util.NullUtils;
|
import com.lanternsoftware.util.NullUtils;
|
||||||
import com.lanternsoftware.util.ResourceLoader;
|
import com.lanternsoftware.util.ResourceLoader;
|
||||||
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
|
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
|
||||||
import com.lanternsoftware.util.cryptography.AESTool;
|
import com.lanternsoftware.util.cryptography.AESTool;
|
||||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||||
|
import com.lanternsoftware.util.dao.auth.AuthCode;
|
||||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||||
|
import com.lanternsoftware.util.external.LanternFiles;
|
||||||
import com.lanternsoftware.util.http.HttpPool;
|
import com.lanternsoftware.util.http.HttpPool;
|
||||||
import com.lanternsoftware.zwave.controller.Controller;
|
import com.lanternsoftware.zwave.controller.Controller;
|
||||||
import com.lanternsoftware.zwave.dao.MongoZWaveDao;
|
import com.lanternsoftware.zwave.dao.MongoZWaveDao;
|
||||||
|
@ -54,6 +54,8 @@ import java.util.Set;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class ZWaveApp {
|
public class ZWaveApp {
|
||||||
public static final AESTool aes = AESTool.authTool();
|
public static final AESTool aes = AESTool.authTool();
|
||||||
|
@ -75,11 +77,13 @@ public class ZWaveApp {
|
||||||
private SwitchScheduleTask nextScheduleTask;
|
private SwitchScheduleTask nextScheduleTask;
|
||||||
private final Map<Integer, Double> sensors = new HashMap<>();
|
private final Map<Integer, Double> sensors = new HashMap<>();
|
||||||
private final Object ZWAVE_MUTEX = new Object();
|
private final Object ZWAVE_MUTEX = new Object();
|
||||||
|
private ExecutorService executor = null;
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
try {
|
try {
|
||||||
pool = new HttpPool(100, 20, 5000, 5000, 5000);
|
pool = new HttpPool(100, 20, 5000, 5000, 5000);
|
||||||
config = DaoSerializer.parse(ResourceLoader.loadFile(LanternFiles.CONFIG_PATH + "config.json"), ZWaveConfig.class);
|
config = DaoSerializer.parse(ResourceLoader.loadFile(LanternFiles.CONFIG_PATH + "config.json"), ZWaveConfig.class);
|
||||||
|
executor = Executors.newFixedThreadPool(5);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
dao = new MongoZWaveDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
dao = new MongoZWaveDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||||
config = dao.getConfig(1);
|
config = dao.getConfig(1);
|
||||||
|
@ -202,6 +206,7 @@ public class ZWaveApp {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(MultilevelSwitchReportRequest _message) {
|
public void onMessage(MultilevelSwitchReportRequest _message) {
|
||||||
|
logger.info("Received MultilevelSwitchReportRequest");
|
||||||
onSwitchLevelChange(_message.getNodeId(), _message.getLevel());
|
onSwitchLevelChange(_message.getNodeId(), _message.getLevel());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -214,6 +219,7 @@ public class ZWaveApp {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(BinarySwitchReportRequest _message) {
|
public void onMessage(BinarySwitchReportRequest _message) {
|
||||||
|
logger.info("Received BinarySwitchReportRequest");
|
||||||
onSwitchLevelChange(_message.getNodeId(), _message.getLevel());
|
onSwitchLevelChange(_message.getNodeId(), _message.getLevel());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -226,10 +232,18 @@ public class ZWaveApp {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(CRC16EncapRequest _message) {
|
public void onMessage(CRC16EncapRequest _message) {
|
||||||
onSwitchLevelChange(_message.getNodeId(), _message.isOn()?0xFF:0);
|
// logger.info("Received CRC16EncapRequest");
|
||||||
|
// onSwitchLevelChange(_message.getNodeId(), _message.isOn()?0xFF:0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// for (Switch sw : config.getSwitches()) {
|
||||||
|
// if (sw.getNodeId() < 255) {
|
||||||
|
// controller.send(new NodeNeighborUpdateRequest(sw.getNodeId()));
|
||||||
|
// ConcurrencyUtils.sleep(5000);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// controller.send(new MultilevelSwitchSetRequest((byte)2, 0xFF));
|
// controller.send(new MultilevelSwitchSetRequest((byte)2, 0xFF));
|
||||||
|
|
||||||
// controller.send(new MultilevelSensorGetRequest((byte)11));
|
// controller.send(new MultilevelSensorGetRequest((byte)11));
|
||||||
|
@ -244,27 +258,31 @@ public class ZWaveApp {
|
||||||
// controller.send(new ThermostatModeGetRequest((byte)11));
|
// controller.send(new ThermostatModeGetRequest((byte)11));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSwitchLevelChange(int _secondaryNodeId, int _primaryLevel) {
|
private void onSwitchLevelChange(int _nodeId, int _level) {
|
||||||
synchronized (switches) {
|
synchronized (switches) {
|
||||||
Switch sw = switches.get(_secondaryNodeId);
|
Switch sw = switches.get(_nodeId);
|
||||||
if ((sw != null) && !sw.isPrimary()) {
|
if (sw != null) {
|
||||||
int newLevel = sw.isMultilevel()?_primaryLevel:((_primaryLevel == 0)?0:99);
|
logger.info("Received level change for node {} to level {} via z-wave", _nodeId, _level);
|
||||||
|
if (_level == -1)
|
||||||
|
_level = 255;
|
||||||
|
int newLevel = sw.isMultilevel()?NullUtils.bound(_level, 0, 99):((_level == 0)?0:99);
|
||||||
sw.setLevel(newLevel);
|
sw.setLevel(newLevel);
|
||||||
fireSwitchLevelEvent(sw);
|
fireSwitchLevelEvent(sw);
|
||||||
for (Switch peer : CollectionUtils.makeNotNull(peers.get(_secondaryNodeId))) {
|
for (Switch peer : CollectionUtils.makeNotNull(peers.get(_nodeId))) {
|
||||||
if (peer.isPrimary()) {
|
logger.info("Mirror Event from {} node {} to {} node {} level {}", sw.isPrimary() ? "primary" : "secondary", _nodeId, peer.isPrimary() ? "primary" : "secondary", peer.getNodeId(), newLevel);
|
||||||
logger.info("Mirror Event from node {} to node {}", _secondaryNodeId, peer.getNodeId());
|
if (peer.isMultilevel()) {
|
||||||
if (peer.isMultilevel()) {
|
peer.setLevel(newLevel);
|
||||||
peer.setLevel(newLevel);
|
controller.send(new MultilevelSwitchSetRequest((byte) peer.getNodeId(), newLevel));
|
||||||
controller.send(new MultilevelSwitchSetRequest((byte) peer.getNodeId(), newLevel));
|
} else {
|
||||||
} else {
|
peer.setLevel(newLevel != 0 ? 0xff : 0);
|
||||||
peer.setLevel(newLevel > 0 ? 0xff : 0);
|
controller.send(new BinarySwitchSetRequest((byte) peer.getNodeId(), newLevel != 0));
|
||||||
controller.send(new BinarySwitchSetRequest((byte) peer.getNodeId(), newLevel > 0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
persistConfig();
|
persistConfig();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
logger.info("Received level change for unknown node {}", _nodeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,18 +312,20 @@ public class ZWaveApp {
|
||||||
public void fireSwitchLevelEvent(Switch _sw) {
|
public void fireSwitchLevelEvent(Switch _sw) {
|
||||||
if (NullUtils.isEmpty(config.getRulesUrl()) || _sw.isSuppressEvents())
|
if (NullUtils.isEmpty(config.getRulesUrl()) || _sw.isSuppressEvents())
|
||||||
return;
|
return;
|
||||||
Event event = new Event();
|
executor.submit(()->{
|
||||||
event.setEventDescription(_sw.getFullDisplay() + " set to " + _sw.getLevel());
|
Event event = new Event();
|
||||||
event.setType(EventType.SWITCH_LEVEL);
|
event.setEventDescription(_sw.getFullDisplay() + " set to " + _sw.getLevel());
|
||||||
event.setTime(new Date());
|
event.setType(EventType.SWITCH_LEVEL);
|
||||||
event.setValue(_sw.getLevel());
|
event.setTime(new Date());
|
||||||
event.setSourceId(String.valueOf(_sw.getNodeId()));
|
event.setValue(_sw.getLevel());
|
||||||
event.setAccountId(config.getAccountId());
|
event.setSourceId(String.valueOf(_sw.getNodeId()));
|
||||||
logger.info("Sending event to rules server - " + event.getEventDescription());
|
event.setAccountId(config.getAccountId());
|
||||||
HttpPost post = new HttpPost(NullUtils.terminateWith(config.getRulesUrl(), "/") + "event");
|
logger.info("Sending event to rules server - " + event.getEventDescription());
|
||||||
post.setHeader("auth_code", authCode);
|
HttpPost post = new HttpPost(NullUtils.terminateWith(config.getRulesUrl(), "/") + "event");
|
||||||
post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(event)));
|
post.setHeader("auth_code", authCode);
|
||||||
pool.execute(post);
|
post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(event)));
|
||||||
|
pool.execute(post);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSwitchLevel(int _nodeId, int _level, boolean _updatePeers) {
|
public void setSwitchLevel(int _nodeId, int _level, boolean _updatePeers) {
|
||||||
|
@ -401,11 +421,13 @@ public class ZWaveApp {
|
||||||
peers.remove(config.getUrl());
|
peers.remove(config.getUrl());
|
||||||
for (String peer : peers) {
|
for (String peer : peers) {
|
||||||
for (Switch sw : modified) {
|
for (Switch sw : modified) {
|
||||||
logger.info("Sending update for switch {} {} level {} to {}", sw.getNodeId(), sw.getFullDisplay(), sw.getLevel(), peer);
|
executor.submit(()->{
|
||||||
HttpPost post = new HttpPost(peer + "/switch/" + sw.getNodeId());
|
logger.info("Sending update for switch {} {} level {} to {}", sw.getNodeId(), sw.getFullDisplay(), sw.getLevel(), peer);
|
||||||
post.setHeader("auth_code", authCode);
|
HttpPost post = new HttpPost(peer + "/switch/" + sw.getNodeId());
|
||||||
post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(sw)));
|
post.setHeader("auth_code", authCode);
|
||||||
pool.execute(post);
|
post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(sw)));
|
||||||
|
pool.execute(post);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -441,6 +463,10 @@ public class ZWaveApp {
|
||||||
pool.shutdown();
|
pool.shutdown();
|
||||||
pool = null;
|
pool = null;
|
||||||
}
|
}
|
||||||
|
if (executor != null) {
|
||||||
|
executor.shutdown();
|
||||||
|
executor = null;
|
||||||
|
}
|
||||||
if (dao != null) {
|
if (dao != null) {
|
||||||
dao.shutdown();
|
dao.shutdown();
|
||||||
dao = null;
|
dao = null;
|
||||||
|
@ -455,7 +481,7 @@ public class ZWaveApp {
|
||||||
for (Switch node : nodes) {
|
for (Switch node : nodes) {
|
||||||
logger.info("Setting {}, Node {} to {}", node.getName(), node.getNodeId(), _level);
|
logger.info("Setting {}, Node {} to {}", node.getName(), node.getNodeId(), _level);
|
||||||
byte nid = (byte) (node.getNodeId()%1000);
|
byte nid = (byte) (node.getNodeId()%1000);
|
||||||
controller.send(node.isMultilevel() ? new MultilevelSwitchSetRequest(nid, _level) : new BinarySwitchSetRequest(nid, _level > 0));
|
controller.send(node.isMultilevel() ? new MultilevelSwitchSetRequest(nid, _level) : new BinarySwitchSetRequest(nid, _level != 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class BinarySwitchReportRequest extends RequestMessage {
|
||||||
@Override
|
@Override
|
||||||
public void fromPayload(byte[] _payload) {
|
public void fromPayload(byte[] _payload) {
|
||||||
nodeId = _payload[5];
|
nodeId = _payload[5];
|
||||||
level = _payload[9];
|
level = 0xFF & _payload[9];
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLevel() {
|
public int getLevel() {
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
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 NodeNeighborUpdateRequest extends RequestMessage {
|
||||||
|
public NodeNeighborUpdateRequest() {
|
||||||
|
super(ControllerMessageType.RequestNodeNeighborUpdate, CommandClass.NO_OPERATION, (byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeNeighborUpdateRequest(int _nodeId) {
|
||||||
|
super((byte) _nodeId, ControllerMessageType.RequestNodeNeighborUpdate, CommandClass.NO_OPERATION, (byte) 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ com.lanternsoftware.zwave.message.impl.MultilevelSwitchReportRequest
|
||||||
com.lanternsoftware.zwave.message.impl.MultilevelSwitchSetRequest
|
com.lanternsoftware.zwave.message.impl.MultilevelSwitchSetRequest
|
||||||
com.lanternsoftware.zwave.message.impl.NodeInfoRequest
|
com.lanternsoftware.zwave.message.impl.NodeInfoRequest
|
||||||
com.lanternsoftware.zwave.message.impl.NodeInfoResponse
|
com.lanternsoftware.zwave.message.impl.NodeInfoResponse
|
||||||
|
com.lanternsoftware.zwave.message.impl.NodeNeighborUpdateRequest
|
||||||
com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStartRequest
|
com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStartRequest
|
||||||
com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStopRequest
|
com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStopRequest
|
||||||
com.lanternsoftware.zwave.message.impl.SendDataRequest
|
com.lanternsoftware.zwave.message.impl.SendDataRequest
|
||||||
|
|
Loading…
Reference in New Issue
Block a user