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>
|
||||
<artifactId>lantern-currentmonitor</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.1.0</version>
|
||||
<version>1.1.1</version>
|
||||
<name>lantern-currentmonitor</name>
|
||||
|
||||
<parent>
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.lanternsoftware.currentmonitor;
|
|||
|
||||
import com.lanternsoftware.currentmonitor.adc.MCP3008Pin;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.Breaker;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.hub.PowerSample;
|
||||
|
||||
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.BreakerPolarity;
|
||||
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.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
|
||||
|
@ -18,6 +21,8 @@ import java.util.Date;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
|
@ -29,6 +34,23 @@ public class CurrentMonitor {
|
|||
private Sampler sampler;
|
||||
private PowerListener listener;
|
||||
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() {
|
||||
stopMonitoring();
|
||||
|
@ -40,10 +62,6 @@ public class CurrentMonitor {
|
|||
LOG.info("Power Monitor Service Stopped");
|
||||
}
|
||||
|
||||
public void setDebug(boolean _debug) {
|
||||
debug = _debug;
|
||||
}
|
||||
|
||||
public CalibrationResult calibrateVoltage(double _curCalibration) {
|
||||
LOG.info("Calibrating Voltage");
|
||||
MCP3008Pin voltagePin = new MCP3008Pin(getChip(0), 0);
|
||||
|
@ -148,10 +166,10 @@ public class CurrentMonitor {
|
|||
private boolean running = true;
|
||||
private final BreakerHub hub;
|
||||
private final List<List<BreakerSamples>> breakers;
|
||||
private final int intervalNs;
|
||||
private final long intervalNs;
|
||||
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;
|
||||
MCP3008Pin voltagePin = new MCP3008Pin(getChip(0), 0);
|
||||
breakers = CollectionUtils.transform(_breakers, _b->{
|
||||
|
@ -177,6 +195,10 @@ public class CurrentMonitor {
|
|||
long interval = 0;
|
||||
int cycle;
|
||||
int curBreaker;
|
||||
long intervalStart;
|
||||
long intervalEnd;
|
||||
long cycleEnd;
|
||||
long curTime;
|
||||
BreakerSamples[] cycleBreakers = new BreakerSamples[concurrentBreakerCnt];
|
||||
try {
|
||||
while (true) {
|
||||
|
@ -187,8 +209,8 @@ public class CurrentMonitor {
|
|||
}
|
||||
}
|
||||
final Date readTime = new Date();
|
||||
final long intervalStart = (interval * intervalNs) + start;
|
||||
long intervalEnd = intervalStart + intervalNs;
|
||||
intervalStart = (interval * intervalNs) + start;
|
||||
intervalEnd = intervalStart + intervalNs;
|
||||
cycle = 0;
|
||||
final int batch = (int) (interval % BATCH_CNT);
|
||||
while (System.nanoTime() < intervalEnd) {
|
||||
|
@ -197,22 +219,73 @@ public class CurrentMonitor {
|
|||
cycleBreakers[curBreaker].incrementCycleCnt();
|
||||
}
|
||||
cycle++;
|
||||
long cycleEnd = intervalStart + (cycle * (intervalNs / hub.getFrequency()));
|
||||
while (System.nanoTime() < cycleEnd) {
|
||||
cycleEnd = intervalStart + (cycle * (intervalNs / hub.getFrequency()));
|
||||
curTime = System.nanoTime();
|
||||
while (curTime < cycleEnd) {
|
||||
for (curBreaker = 0; curBreaker < concurrentBreakerCnt; curBreaker++) {
|
||||
PowerSample sample = cycleBreakers[curBreaker].incrementSample();
|
||||
sample.nanoTime = curTime;
|
||||
sample.cycle = cycle;
|
||||
sample.voltage = cycleBreakers[curBreaker].getVoltagePin().read();
|
||||
sample.current = cycleBreakers[curBreaker].getCurrentPin().read();
|
||||
}
|
||||
curTime = System.nanoTime();
|
||||
}
|
||||
}
|
||||
interval++;
|
||||
final HubSample hubSample = (postSamples && (interval == 10)) ? new HubSample() : null;
|
||||
executor.submit(() -> {
|
||||
long cycleLength = 1000000000/hub.getFrequency();
|
||||
if (hubSample != null) {
|
||||
hubSample.setSampleDate(new Date());
|
||||
hubSample.setBreakers(new ArrayList<>());
|
||||
}
|
||||
for (List<BreakerSamples> breaker : breakers) {
|
||||
double vOffset = 0.0;
|
||||
double iOffset = 0.0;
|
||||
BreakerSamples samples = breaker.get(batch);
|
||||
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) {
|
||||
vOffset += sample.voltage;
|
||||
iOffset += sample.current;
|
||||
|
@ -223,6 +296,7 @@ public class CurrentMonitor {
|
|||
double pSum = 0.0;
|
||||
double vRms = 0.0;
|
||||
double lowPassFilter = samples.getBreaker().getLowPassFilter();
|
||||
|
||||
for (PowerSample sample : validSamples) {
|
||||
sample.current -= iOffset;
|
||||
if (Math.abs(sample.current) < lowPassFilter)
|
||||
|
@ -233,11 +307,15 @@ public class CurrentMonitor {
|
|||
}
|
||||
vRms /= validSamples.size();
|
||||
vRms = hub.getVoltageCalibrationFactor() * Math.sqrt(vRms);
|
||||
int lowSampleRatio = (lowSamples * 100) / samples.getSampleCnt();
|
||||
double realPower = Math.abs((hub.getVoltageCalibrationFactor() * hub.getPortCalibrationFactor() * samples.getBreaker().getFinalCalibrationFactor() * pSum) / samples.getSampleCnt());
|
||||
if ((lowSampleRatio > 75) && realPower < 13.0)
|
||||
int lowSampleRatio = (lowSamples * 100) / validSamples.size();
|
||||
double realPower = (hub.getVoltageCalibrationFactor() * hub.getPortCalibrationFactor() * samples.getBreaker().getFinalCalibrationFactor() * pSum) / validSamples.size();
|
||||
if ((lowSampleRatio > 75) && Math.abs(realPower) < 13.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;
|
||||
if (samples.getBreaker().isDoublePower())
|
||||
realPower *= 2.0;
|
||||
|
@ -263,6 +341,8 @@ public class CurrentMonitor {
|
|||
samples.setCycleCnt(0);
|
||||
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.HubPowerMinute;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.NetworkStatus;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
import com.lanternsoftware.util.ResourceLoader;
|
||||
|
@ -65,16 +66,24 @@ public class MonitorApp {
|
|||
private static final CurrentMonitor monitor = new CurrentMonitor();
|
||||
private static final List<BreakerPower> readings = new ArrayList<>();
|
||||
private static String version;
|
||||
private static final PowerListener logger = _p -> {
|
||||
if (!config.isDebug()) {
|
||||
_p.setHubVersion(version);
|
||||
if (breakerConfig != null)
|
||||
_p.setAccountId(breakerConfig.getAccountId());
|
||||
synchronized (readings) {
|
||||
readings.add(_p);
|
||||
}
|
||||
} else
|
||||
LOG.info("Panel{} - Space{} Power: {}W", _p.getPanel(), Breaker.toSpaceDisplay(_p.getSpace()), String.format("%.3f", _p.getPower()));
|
||||
private static final PowerListener logger = new PowerListener() {
|
||||
@Override
|
||||
public void onPowerEvent(BreakerPower _power) {
|
||||
if (!config.isDebug()) {
|
||||
_power.setHubVersion(version);
|
||||
if (breakerConfig != null)
|
||||
_power.setAccountId(breakerConfig.getAccountId());
|
||||
synchronized (readings) {
|
||||
readings.add(_power);
|
||||
}
|
||||
} 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() {
|
||||
@Override
|
||||
|
@ -214,6 +223,7 @@ public class MonitorApp {
|
|||
if (NullUtils.isNotEmpty(config.getHost()))
|
||||
host = NullUtils.terminateWith(config.getHost(), "/");
|
||||
monitor.setDebug(config.isDebug());
|
||||
monitor.setPostSamples(config.isPostSamples());
|
||||
LEDFlasher.setLEDOn(false);
|
||||
if (NullUtils.isNotEmpty(config.getAuthCode()))
|
||||
authCode = config.getAuthCode();
|
||||
|
|
|
@ -17,6 +17,7 @@ public class MonitorConfig {
|
|||
private boolean debug;
|
||||
private int connectTimeout;
|
||||
private int socketTimeout;
|
||||
private boolean postSamples = false;
|
||||
private boolean needsCalibration = true;
|
||||
private String mqttBrokerUrl;
|
||||
private String mqttUserName;
|
||||
|
@ -98,6 +99,14 @@ public class MonitorConfig {
|
|||
socketTimeout = _socketTimeout;
|
||||
}
|
||||
|
||||
public boolean isPostSamples() {
|
||||
return postSamples;
|
||||
}
|
||||
|
||||
public void setPostSamples(boolean _postSamples) {
|
||||
postSamples = _postSamples;
|
||||
}
|
||||
|
||||
public boolean isNeedsCalibration() {
|
||||
return needsCalibration;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package com.lanternsoftware.currentmonitor;
|
||||
|
||||
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||
|
||||
public interface PowerListener {
|
||||
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("connect_timeout", _o.getConnectTimeout());
|
||||
d.put("socket_timeout", _o.getSocketTimeout());
|
||||
d.put("post_samples", _o.isPostSamples());
|
||||
d.put("needs_calibration", _o.isNeedsCalibration());
|
||||
d.put("mqtt_broker_url", _o.getMqttBrokerUrl());
|
||||
d.put("mqtt_user_name", _o.getMqttUserName());
|
||||
|
@ -57,6 +58,7 @@ public class MonitorConfigSerializer extends AbstractDaoSerializer<MonitorConfig
|
|||
o.setDebug(DaoSerializer.getBoolean(_d, "debug"));
|
||||
o.setConnectTimeout(DaoSerializer.getInteger(_d, "connect_timeout"));
|
||||
o.setSocketTimeout(DaoSerializer.getInteger(_d, "socket_timeout"));
|
||||
o.setPostSamples(DaoSerializer.getBoolean(_d, "post_samples"));
|
||||
o.setNeedsCalibration(DaoSerializer.getBoolean(_d, "needs_calibration"));
|
||||
o.setMqttBrokerUrl(DaoSerializer.getString(_d, "mqtt_broker_url"));
|
||||
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.HubPowerMinute;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||
import com.lanternsoftware.util.DateRange;
|
||||
import com.lanternsoftware.util.dao.auth.AuthCode;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoProxy;
|
||||
|
@ -44,7 +45,6 @@ public interface CurrentMonitorDao {
|
|||
void putConfig(BreakerConfig _config);
|
||||
|
||||
void rebuildSummaries(int _accountId);
|
||||
void rebuildSummariesAsync(int _accountId);
|
||||
void rebuildSummaries(int _accountId, Date _start, Date _end);
|
||||
|
||||
String addPasswordResetKey(String _email);
|
||||
|
@ -66,5 +66,8 @@ public interface CurrentMonitorDao {
|
|||
List<HubCommand> getAllHubCommands();
|
||||
void deleteHubCommand(String _id);
|
||||
|
||||
void putHubSample(HubSample _sample);
|
||||
List<HubSample> getSamplesForAccount(int _accountId);
|
||||
|
||||
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.DailyEnergyArchive;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.archive.MonthlyEnergyArchive;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.DateRange;
|
||||
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
|
||||
public void rebuildSummaries(int _accountId) {
|
||||
HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute"));
|
||||
if (firstMinute == null)
|
||||
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
|
||||
|
@ -812,6 +809,16 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
|
|||
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
|
||||
public MongoProxy getProxy() {
|
||||
return proxy;
|
||||
|
|
|
@ -21,6 +21,7 @@ public class Breaker implements IIdentical<Breaker> {
|
|||
private String name;
|
||||
private String description;
|
||||
private int sizeAmps;
|
||||
private int phaseOffsetNs;
|
||||
private double calibrationFactor;
|
||||
private double lowPassFilter;
|
||||
private BreakerPolarity polarity;
|
||||
|
@ -140,6 +141,14 @@ public class Breaker implements IIdentical<Breaker> {
|
|||
sizeAmps = _sizeAmps;
|
||||
}
|
||||
|
||||
public int getPhaseOffsetNs() {
|
||||
return phaseOffsetNs;
|
||||
}
|
||||
|
||||
public void setPhaseOffsetNs(int _phaseOffsetNs) {
|
||||
phaseOffsetNs = _phaseOffsetNs;
|
||||
}
|
||||
|
||||
public double getLowPassFilter() {
|
||||
return Math.abs(lowPassFilter) < 0.05 ? 1.6 : lowPassFilter;
|
||||
}
|
||||
|
@ -278,7 +287,7 @@ public class Breaker implements IIdentical<Breaker> {
|
|||
@Override
|
||||
public boolean isIdentical(Breaker _o) {
|
||||
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
|
||||
|
|
|
@ -11,6 +11,8 @@ public class BreakerHub implements IIdentical<BreakerHub> {
|
|||
private int hub;
|
||||
private double voltageCalibrationFactor;
|
||||
private double portCalibrationFactor;
|
||||
private int phaseCnt;
|
||||
private int phaseOffsetNs;
|
||||
private int frequency;
|
||||
private String bluetoothMac;
|
||||
|
||||
|
@ -46,8 +48,24 @@ public class BreakerHub implements IIdentical<BreakerHub> {
|
|||
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() {
|
||||
return frequency;
|
||||
return frequency == 0 ? 60 : frequency;
|
||||
}
|
||||
|
||||
public void setFrequency(int _frequency) {
|
||||
|
@ -73,7 +91,7 @@ public class BreakerHub implements IIdentical<BreakerHub> {
|
|||
@Override
|
||||
public boolean isIdentical(BreakerHub _o) {
|
||||
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
|
||||
|
|
|
@ -2,5 +2,7 @@ package com.lanternsoftware.datamodel.currentmonitor;
|
|||
|
||||
public enum BreakerPolarity {
|
||||
NORMAL,
|
||||
SOLAR;
|
||||
SOLAR,
|
||||
BI_DIRECTIONAL,
|
||||
BI_DIRECTIONAL_INVERTED
|
||||
}
|
||||
|
|
|
@ -140,9 +140,9 @@ public class EnergySummary {
|
|||
idx = 0;
|
||||
double flow = 0.0;
|
||||
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]);
|
||||
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]);
|
||||
idx++;
|
||||
}
|
||||
|
@ -152,11 +152,11 @@ public class EnergySummary {
|
|||
}
|
||||
|
||||
public void resetEnergy(Date _readTime) {
|
||||
if (energy == null)
|
||||
return;
|
||||
int idx = viewMode.blockIndex(start, _readTime, timezone);
|
||||
if (idx < energy.length)
|
||||
energy[idx] = 0f;
|
||||
if (energy != null) {
|
||||
int idx = viewMode.blockIndex(start, _readTime, timezone);
|
||||
if (idx < energy.length)
|
||||
energy[idx] = 0f;
|
||||
}
|
||||
for (EnergySummary subGroup : CollectionUtils.makeNotNull(subGroups)) {
|
||||
subGroup.resetEnergy(_readTime);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ public class BreakerHubSerializer extends AbstractDaoSerializer<BreakerHub>
|
|||
d.put("hub", _o.getHub());
|
||||
d.put("voltage_calibration_factor", _o.getRawVoltageCalibrationFactor());
|
||||
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("bluetooth_mac", _o.getBluetoothMac());
|
||||
return d;
|
||||
|
@ -40,6 +42,8 @@ public class BreakerHubSerializer extends AbstractDaoSerializer<BreakerHub>
|
|||
o.setHub(DaoSerializer.getInteger(_d, "hub"));
|
||||
o.setVoltageCalibrationFactor(DaoSerializer.getDouble(_d, "voltage_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.setBluetoothMac(DaoSerializer.getString(_d, "bluetooth_mac"));
|
||||
return o;
|
||||
|
|
|
@ -35,6 +35,7 @@ public class BreakerSerializer extends AbstractDaoSerializer<Breaker>
|
|||
d.put("name", _o.getName());
|
||||
d.put("description", _o.getDescription());
|
||||
d.put("size_amps", _o.getSizeAmps());
|
||||
d.put("phase_offset_ns", _o.getPhaseOffsetNs());
|
||||
d.put("calibration_factor", _o.getCalibrationFactor());
|
||||
d.put("low_pass_filter", _o.getLowPassFilter());
|
||||
d.put("polarity", DaoSerializer.toEnumName(_o.getPolarity()));
|
||||
|
@ -56,6 +57,7 @@ public class BreakerSerializer extends AbstractDaoSerializer<Breaker>
|
|||
o.setName(DaoSerializer.getString(_d, "name"));
|
||||
o.setDescription(DaoSerializer.getString(_d, "description"));
|
||||
o.setSizeAmps(DaoSerializer.getInteger(_d, "size_amps"));
|
||||
o.setPhaseOffsetNs(DaoSerializer.getInteger(_d, "phase_offset_ns"));
|
||||
o.setCalibrationFactor(DaoSerializer.getDouble(_d, "calibration_factor"));
|
||||
o.setLowPassFilter(DaoSerializer.getDouble(_d, "low_pass_filter"));
|
||||
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.SequenceSerializer
|
||||
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.Map;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class Globals implements ServletContextListener {
|
||||
public static CurrentMonitorDao dao;
|
||||
public static ExecutorService opsExecutor;
|
||||
private static final Map<Integer, Map<Integer, List<HubCommand>>> commands = new HashMap<>();
|
||||
|
||||
@Override
|
||||
|
@ -28,10 +31,12 @@ public class Globals implements ServletContextListener {
|
|||
dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||
RulesEngine.instance().start();
|
||||
RulesEngine.instance().schedule(new CommandTask(), 0);
|
||||
opsExecutor = Executors.newFixedThreadPool(7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
opsExecutor.shutdown();
|
||||
dao.shutdown();
|
||||
HttpFactory.shutdown();
|
||||
RulesEngine.shutdown();
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.lanternsoftware.currentmonitor.context.Globals;
|
|||
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
|
||||
import com.lanternsoftware.rules.RulesEngine;
|
||||
import com.lanternsoftware.util.dao.auth.AuthCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -40,6 +41,8 @@ public class ConfigServlet extends SecureServiceServlet {
|
|||
if ((oldConfig == null) || !oldConfig.isIdentical(config))
|
||||
Globals.dao.putHubCommand(new HubCommand(config.getAccountId(), HubConfigCharacteristic.ReloadConfig, null));
|
||||
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) {
|
||||
String[] path = path(_req);
|
||||
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 {
|
||||
for (String sId : Globals.dao.getProxy().queryForField(Account.class, null, "_id")) {
|
||||
int id = DaoSerializer.toInteger(sId);
|
||||
if (id != 0)
|
||||
Globals.dao.rebuildSummariesAsync(id);
|
||||
if (id != 0) {
|
||||
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.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class PIGPIO {
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(PIGPIO.class);
|
||||
|
||||
|
@ -20,10 +17,11 @@ public class PIGPIO {
|
|||
osArch = "armhf";
|
||||
String path = "/lib/" + osArch + "/lantern-pigpio.so";
|
||||
byte[] file = ResourceLoader.getByteArrayResource(PIGPIO.class, path);
|
||||
String target = Files.createTempFile("lantern-pigpio", "so").toAbsolutePath().toString();
|
||||
ResourceLoader.writeFile(target, file);
|
||||
System.load(target);
|
||||
} catch (IOException _e) {
|
||||
LOG.info("library size: {}", file.length);
|
||||
String libPath = "/opt/currentmonitor/lantern-pigpio.so";
|
||||
ResourceLoader.writeFile(libPath, file);
|
||||
System.load(libPath);
|
||||
} catch (Exception _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.EventId;
|
||||
import com.lanternsoftware.datamodel.rules.EventType;
|
||||
import com.lanternsoftware.datamodel.rules.FcmDevice;
|
||||
import com.lanternsoftware.datamodel.rules.Rule;
|
||||
import com.lanternsoftware.rules.actions.ActionImpl;
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
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.mongo.MongoConfig;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -36,6 +39,7 @@ import java.util.concurrent.Executors;
|
|||
|
||||
public class RulesEngine {
|
||||
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 final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
|
@ -69,6 +73,15 @@ public class RulesEngine {
|
|||
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) {
|
||||
if (_event.getType() != EventType.TIME)
|
||||
dao.putEvent(_event);
|
||||
|
@ -108,6 +121,8 @@ public class RulesEngine {
|
|||
return;
|
||||
Collection<Date> dates = CollectionUtils.aggregate(rules, _r->CollectionUtils.transform(_r.getAllCriteria(), _c->_c.getNextTriggerDate(tz)));
|
||||
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));
|
||||
nextTask = new EventTimeTask(_accountId, nextDate);
|
||||
timer.schedule(nextTask, nextDate);
|
||||
|
|
|
@ -1,28 +1,11 @@
|
|||
package com.lanternsoftware.rules.actions;
|
||||
|
||||
import com.lanternsoftware.datamodel.rules.Alert;
|
||||
import com.lanternsoftware.datamodel.rules.FcmDevice;
|
||||
import com.lanternsoftware.datamodel.rules.Rule;
|
||||
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 {
|
||||
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) {
|
||||
List<FcmDevice> devices = RulesEngine.instance().dao().getFcmDevicesForAccount(_rule.getAccountId());
|
||||
if (devices.isEmpty())
|
||||
return;
|
||||
for (FcmDevice device : devices) {
|
||||
firebaseHelper.sendMessage(device.getToken(), new DaoEntity("payload", DaoSerializer.toBase64ZipBson(_alert)).and("payloadClass", Alert.class.getCanonicalName()));
|
||||
}
|
||||
RulesEngine.instance().sendFcmMessage(_rule.getAccountId(), _alert);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.lanternsoftware.zwave.context;
|
|||
|
||||
import com.lanternsoftware.datamodel.rules.Event;
|
||||
import com.lanternsoftware.datamodel.rules.EventType;
|
||||
import com.lanternsoftware.util.dao.auth.AuthCode;
|
||||
import com.lanternsoftware.datamodel.zwave.Switch;
|
||||
import com.lanternsoftware.datamodel.zwave.SwitchSchedule;
|
||||
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.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.DateUtils;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
import com.lanternsoftware.util.ResourceLoader;
|
||||
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
|
||||
import com.lanternsoftware.util.cryptography.AESTool;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
import com.lanternsoftware.util.dao.auth.AuthCode;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.http.HttpPool;
|
||||
import com.lanternsoftware.zwave.controller.Controller;
|
||||
import com.lanternsoftware.zwave.dao.MongoZWaveDao;
|
||||
|
@ -54,6 +54,8 @@ import java.util.Set;
|
|||
import java.util.TimeZone;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class ZWaveApp {
|
||||
public static final AESTool aes = AESTool.authTool();
|
||||
|
@ -75,11 +77,13 @@ public class ZWaveApp {
|
|||
private SwitchScheduleTask nextScheduleTask;
|
||||
private final Map<Integer, Double> sensors = new HashMap<>();
|
||||
private final Object ZWAVE_MUTEX = new Object();
|
||||
private ExecutorService executor = null;
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
pool = new HttpPool(100, 20, 5000, 5000, 5000);
|
||||
config = DaoSerializer.parse(ResourceLoader.loadFile(LanternFiles.CONFIG_PATH + "config.json"), ZWaveConfig.class);
|
||||
executor = Executors.newFixedThreadPool(5);
|
||||
if (config == null) {
|
||||
dao = new MongoZWaveDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||
config = dao.getConfig(1);
|
||||
|
@ -202,6 +206,7 @@ public class ZWaveApp {
|
|||
|
||||
@Override
|
||||
public void onMessage(MultilevelSwitchReportRequest _message) {
|
||||
logger.info("Received MultilevelSwitchReportRequest");
|
||||
onSwitchLevelChange(_message.getNodeId(), _message.getLevel());
|
||||
}
|
||||
});
|
||||
|
@ -214,6 +219,7 @@ public class ZWaveApp {
|
|||
|
||||
@Override
|
||||
public void onMessage(BinarySwitchReportRequest _message) {
|
||||
logger.info("Received BinarySwitchReportRequest");
|
||||
onSwitchLevelChange(_message.getNodeId(), _message.getLevel());
|
||||
}
|
||||
});
|
||||
|
@ -226,10 +232,18 @@ public class ZWaveApp {
|
|||
|
||||
@Override
|
||||
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 MultilevelSensorGetRequest((byte)11));
|
||||
|
@ -244,27 +258,31 @@ public class ZWaveApp {
|
|||
// controller.send(new ThermostatModeGetRequest((byte)11));
|
||||
}
|
||||
|
||||
private void onSwitchLevelChange(int _secondaryNodeId, int _primaryLevel) {
|
||||
private void onSwitchLevelChange(int _nodeId, int _level) {
|
||||
synchronized (switches) {
|
||||
Switch sw = switches.get(_secondaryNodeId);
|
||||
if ((sw != null) && !sw.isPrimary()) {
|
||||
int newLevel = sw.isMultilevel()?_primaryLevel:((_primaryLevel == 0)?0:99);
|
||||
Switch sw = switches.get(_nodeId);
|
||||
if (sw != null) {
|
||||
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);
|
||||
fireSwitchLevelEvent(sw);
|
||||
for (Switch peer : CollectionUtils.makeNotNull(peers.get(_secondaryNodeId))) {
|
||||
if (peer.isPrimary()) {
|
||||
logger.info("Mirror Event from node {} to node {}", _secondaryNodeId, peer.getNodeId());
|
||||
if (peer.isMultilevel()) {
|
||||
peer.setLevel(newLevel);
|
||||
controller.send(new MultilevelSwitchSetRequest((byte) peer.getNodeId(), newLevel));
|
||||
} else {
|
||||
peer.setLevel(newLevel > 0 ? 0xff : 0);
|
||||
controller.send(new BinarySwitchSetRequest((byte) peer.getNodeId(), newLevel > 0));
|
||||
}
|
||||
for (Switch peer : CollectionUtils.makeNotNull(peers.get(_nodeId))) {
|
||||
logger.info("Mirror Event from {} node {} to {} node {} level {}", sw.isPrimary() ? "primary" : "secondary", _nodeId, peer.isPrimary() ? "primary" : "secondary", peer.getNodeId(), newLevel);
|
||||
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();
|
||||
}
|
||||
else {
|
||||
logger.info("Received level change for unknown node {}", _nodeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -294,18 +312,20 @@ public class ZWaveApp {
|
|||
public void fireSwitchLevelEvent(Switch _sw) {
|
||||
if (NullUtils.isEmpty(config.getRulesUrl()) || _sw.isSuppressEvents())
|
||||
return;
|
||||
Event event = new Event();
|
||||
event.setEventDescription(_sw.getFullDisplay() + " set to " + _sw.getLevel());
|
||||
event.setType(EventType.SWITCH_LEVEL);
|
||||
event.setTime(new Date());
|
||||
event.setValue(_sw.getLevel());
|
||||
event.setSourceId(String.valueOf(_sw.getNodeId()));
|
||||
event.setAccountId(config.getAccountId());
|
||||
logger.info("Sending event to rules server - " + event.getEventDescription());
|
||||
HttpPost post = new HttpPost(NullUtils.terminateWith(config.getRulesUrl(), "/") + "event");
|
||||
post.setHeader("auth_code", authCode);
|
||||
post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(event)));
|
||||
pool.execute(post);
|
||||
executor.submit(()->{
|
||||
Event event = new Event();
|
||||
event.setEventDescription(_sw.getFullDisplay() + " set to " + _sw.getLevel());
|
||||
event.setType(EventType.SWITCH_LEVEL);
|
||||
event.setTime(new Date());
|
||||
event.setValue(_sw.getLevel());
|
||||
event.setSourceId(String.valueOf(_sw.getNodeId()));
|
||||
event.setAccountId(config.getAccountId());
|
||||
logger.info("Sending event to rules server - " + event.getEventDescription());
|
||||
HttpPost post = new HttpPost(NullUtils.terminateWith(config.getRulesUrl(), "/") + "event");
|
||||
post.setHeader("auth_code", authCode);
|
||||
post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(event)));
|
||||
pool.execute(post);
|
||||
});
|
||||
}
|
||||
|
||||
public void setSwitchLevel(int _nodeId, int _level, boolean _updatePeers) {
|
||||
|
@ -401,11 +421,13 @@ public class ZWaveApp {
|
|||
peers.remove(config.getUrl());
|
||||
for (String peer : peers) {
|
||||
for (Switch sw : modified) {
|
||||
logger.info("Sending update for switch {} {} level {} to {}", sw.getNodeId(), sw.getFullDisplay(), sw.getLevel(), peer);
|
||||
HttpPost post = new HttpPost(peer + "/switch/" + sw.getNodeId());
|
||||
post.setHeader("auth_code", authCode);
|
||||
post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(sw)));
|
||||
pool.execute(post);
|
||||
executor.submit(()->{
|
||||
logger.info("Sending update for switch {} {} level {} to {}", sw.getNodeId(), sw.getFullDisplay(), sw.getLevel(), peer);
|
||||
HttpPost post = new HttpPost(peer + "/switch/" + sw.getNodeId());
|
||||
post.setHeader("auth_code", authCode);
|
||||
post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(sw)));
|
||||
pool.execute(post);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -441,6 +463,10 @@ public class ZWaveApp {
|
|||
pool.shutdown();
|
||||
pool = null;
|
||||
}
|
||||
if (executor != null) {
|
||||
executor.shutdown();
|
||||
executor = null;
|
||||
}
|
||||
if (dao != null) {
|
||||
dao.shutdown();
|
||||
dao = null;
|
||||
|
@ -455,7 +481,7 @@ public class ZWaveApp {
|
|||
for (Switch node : nodes) {
|
||||
logger.info("Setting {}, Node {} to {}", node.getName(), node.getNodeId(), _level);
|
||||
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
|
||||
public void fromPayload(byte[] _payload) {
|
||||
nodeId = _payload[5];
|
||||
level = _payload[9];
|
||||
level = 0xFF & _payload[9];
|
||||
}
|
||||
|
||||
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.NodeInfoRequest
|
||||
com.lanternsoftware.zwave.message.impl.NodeInfoResponse
|
||||
com.lanternsoftware.zwave.message.impl.NodeNeighborUpdateRequest
|
||||
com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStartRequest
|
||||
com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStopRequest
|
||||
com.lanternsoftware.zwave.message.impl.SendDataRequest
|
||||
|
|
Loading…
Reference in New Issue
Block a user