mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-02-16 12:54:15 +00:00
Changes in user interface
This commit is contained in:
@@ -287,6 +287,30 @@ void AmsData::extractFromOmnipower(HanReader& hanReader, int listSize) {
|
||||
}
|
||||
|
||||
void AmsData::apply(AmsData& other) {
|
||||
if(other.getListType() < 3) {
|
||||
unsigned long ms = this->lastUpdateMillis > other.getLastUpdateMillis() ? 0 : other.getLastUpdateMillis() - this->lastUpdateMillis;
|
||||
|
||||
if(ms > 0) {
|
||||
if(other.getActiveImportPower() > 0)
|
||||
activeImportCounter += (((double) ms) * other.getActiveImportPower()) / 3600000000;
|
||||
counterEstimated = true;
|
||||
}
|
||||
|
||||
if(other.getListType() > 1) {
|
||||
unsigned long ms2 = this->lastList2UpdateMillis > other.getLastUpdateMillis() ? 0 : other.getLastUpdateMillis() - this->lastList2UpdateMillis;
|
||||
if(ms2 > 0) {
|
||||
// Not sure why, but I cannot make these numbers correct. It seems to be double of what it should, so dividing it by two...
|
||||
if(other.getActiveExportPower() > 0)
|
||||
activeExportCounter += (((double) ms2/2) * other.getActiveExportPower()) / 3600000000;
|
||||
if(other.getReactiveImportPower() > 0)
|
||||
reactiveImportCounter += (((double) ms2/2) * other.getReactiveImportPower()) / 3600000000;
|
||||
if(other.getReactiveExportPower() > 0)
|
||||
reactiveExportCounter += (((double) ms2/2) * other.getReactiveExportPower()) / 3600000000;
|
||||
counterEstimated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->lastUpdateMillis = other.getLastUpdateMillis();
|
||||
this->packageTimestamp = other.getPackageTimestamp();
|
||||
this->listType = max(this->listType, other.getListType());
|
||||
@@ -297,6 +321,7 @@ void AmsData::apply(AmsData& other) {
|
||||
this->activeExportCounter = other.getActiveExportCounter();
|
||||
this->reactiveImportCounter = other.getReactiveImportCounter();
|
||||
this->reactiveExportCounter = other.getReactiveExportCounter();
|
||||
this->counterEstimated = false;
|
||||
case 2:
|
||||
this->listId = other.getListId();
|
||||
this->meterId = other.getMeterId();
|
||||
@@ -311,6 +336,7 @@ void AmsData::apply(AmsData& other) {
|
||||
this->l2voltage = other.getL2Voltage();
|
||||
this->l3voltage = other.getL3Voltage();
|
||||
this->threePhase = other.isThreePhase();
|
||||
this->lastList2UpdateMillis = other.getLastUpdateMillis();
|
||||
case 1:
|
||||
this->activeImportPower = other.getActiveImportPower();
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
|
||||
private:
|
||||
unsigned long lastUpdateMillis = 0;
|
||||
unsigned long lastList2UpdateMillis = 0;
|
||||
int listType = 0;
|
||||
unsigned long packageTimestamp = 0;
|
||||
String listId, meterId, meterType;
|
||||
@@ -58,7 +59,7 @@ private:
|
||||
int activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0;
|
||||
double l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0;
|
||||
double activeImportCounter = 0, reactiveImportCounter = 0, activeExportCounter = 0, reactiveExportCounter = 0;
|
||||
bool threePhase = false;
|
||||
bool threePhase = false, counterEstimated = false;
|
||||
|
||||
void extractFromKaifa(HanReader& hanReader, int listSize);
|
||||
void extractFromAidon(HanReader& hanReader, int listSize, bool substituteMissing);
|
||||
|
||||
@@ -459,11 +459,11 @@ void loop() {
|
||||
delay(1);
|
||||
readHanPort();
|
||||
if(WiFi.status() == WL_CONNECTED) {
|
||||
ws.loop();
|
||||
if(eapi.loop()) {
|
||||
sendPricesToMqtt();
|
||||
}
|
||||
//if(eapi.loop()) {
|
||||
// sendPricesToMqtt();
|
||||
//}
|
||||
}
|
||||
ws.loop();
|
||||
delay(1); // Needed for auto modem sleep
|
||||
}
|
||||
|
||||
@@ -611,62 +611,77 @@ void mqttMessageReceived(String &topic, String &payload)
|
||||
}
|
||||
|
||||
void sendPricesToMqtt() {
|
||||
if(strlen(config.getMqttHost()) == 0 || strlen(config.getMqttPublishTopic()) == 0)
|
||||
return;
|
||||
if(strcmp(config.getEntsoeApiToken(), "") != 0)
|
||||
return;
|
||||
|
||||
time_t now = time(nullptr);
|
||||
|
||||
double min1hr, min3hr, min6hr;
|
||||
int min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
|
||||
double min = INT16_MAX, max = INT16_MIN;
|
||||
double values[48];
|
||||
for(int i = 0; i < 48; i++) {
|
||||
double val1 = eapi.getValueForHour(i);
|
||||
values[i] = val1;
|
||||
double values[24] = {0};
|
||||
for(int i = 0; i < 24; i++) {
|
||||
double val = eapi.getValueForHour(now, i);
|
||||
values[i] = val;
|
||||
|
||||
if(val1 == ENTSOE_NO_VALUE) break;
|
||||
if(val == ENTSOE_NO_VALUE) break;
|
||||
|
||||
if(val1 < min) min = val1;
|
||||
if(val1 > max) max = val1;
|
||||
if(val < min) min = val;
|
||||
if(val > max) max = val;
|
||||
|
||||
if(i >= 24) continue; // Only estimate 1hr, 3hr and 6hr cheapest interval for next 24 hrs
|
||||
|
||||
if(min1hrIdx == -1 || min1hr > val1) {
|
||||
min1hr = val1;
|
||||
if(min1hrIdx == -1 || min1hr > val) {
|
||||
min1hr = val;
|
||||
min1hrIdx = i;
|
||||
}
|
||||
|
||||
double val2 = eapi.getValueForHour(i+1);
|
||||
double val3 = eapi.getValueForHour(i+2);
|
||||
if(val2 == ENTSOE_NO_VALUE || val3 == ENTSOE_NO_VALUE) continue;
|
||||
double val3hr = val1+val2+val3;
|
||||
if(min3hrIdx == -1 || min3hr > val3hr) {
|
||||
min3hr = val3hr;
|
||||
min3hrIdx = i;
|
||||
if(i >= 2) {
|
||||
double val1 = values[i-2];
|
||||
double val2 = values[i-1];
|
||||
double val3 = val;
|
||||
if(val1 == ENTSOE_NO_VALUE || val2 == ENTSOE_NO_VALUE || val3 == ENTSOE_NO_VALUE) continue;
|
||||
double val3hr = val1+val2+val3;
|
||||
if(min3hrIdx == -1 || min3hr > val3hr) {
|
||||
min3hr = val3hr;
|
||||
min3hrIdx = i-2;
|
||||
}
|
||||
}
|
||||
|
||||
double val4 = eapi.getValueForHour(i+3);
|
||||
double val5 = eapi.getValueForHour(i+4);
|
||||
double val6 = eapi.getValueForHour(i+5);
|
||||
if(val4 == ENTSOE_NO_VALUE || val5 == ENTSOE_NO_VALUE || val6 == ENTSOE_NO_VALUE) continue;
|
||||
double val6hr = val1+val2+val3+val4+val5+val6;
|
||||
if(min6hrIdx == -1 || min6hr > val6hr) {
|
||||
min6hr = val6hr;
|
||||
min6hrIdx = i;
|
||||
|
||||
if(i >= 5) {
|
||||
double val1 = values[i-5];
|
||||
double val2 = values[i-4];
|
||||
double val3 = values[i-3];
|
||||
double val4 = values[i-2];
|
||||
double val5 = values[i-1];
|
||||
double val6 = val;
|
||||
if(val1 == ENTSOE_NO_VALUE || val2 == ENTSOE_NO_VALUE || val3 == ENTSOE_NO_VALUE || val4 == ENTSOE_NO_VALUE || val5 == ENTSOE_NO_VALUE || val6 == ENTSOE_NO_VALUE) continue;
|
||||
double val6hr = val1+val2+val3+val4+val5+val6;
|
||||
if(min6hrIdx == -1 || min6hr > val6hr) {
|
||||
min6hr = val6hr;
|
||||
min6hrIdx = i-5;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
char ts1hr[21];
|
||||
if(min1hrIdx != -1) {
|
||||
tmElements_t tm;
|
||||
breakTime(time(nullptr) + (SECS_PER_HOUR * min1hrIdx), tm);
|
||||
breakTime(now + (SECS_PER_HOUR * min1hrIdx), tm);
|
||||
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
||||
}
|
||||
char ts3hr[21];
|
||||
if(min3hrIdx != -1) {
|
||||
tmElements_t tm;
|
||||
breakTime(time(nullptr) + (SECS_PER_HOUR * min3hrIdx), tm);
|
||||
breakTime(now + (SECS_PER_HOUR * min3hrIdx), tm);
|
||||
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
||||
}
|
||||
char ts6hr[21];
|
||||
if(min6hrIdx != -1) {
|
||||
tmElements_t tm;
|
||||
breakTime(time(nullptr) + (SECS_PER_HOUR * min6hrIdx), tm);
|
||||
breakTime(now + (SECS_PER_HOUR * min6hrIdx), tm);
|
||||
sprintf(ts6hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
||||
}
|
||||
|
||||
@@ -678,33 +693,37 @@ void sendPricesToMqtt() {
|
||||
json["name"] = config.getMqttClientId();
|
||||
json["up"] = millis();
|
||||
JsonObject jp = json.createNestedObject("prices");
|
||||
for(int i = 0; i < 48; i++) {
|
||||
double val = values[i];
|
||||
if(val == ENTSOE_NO_VALUE) break;
|
||||
jp[String(i)] = serialized(String(val, 4));
|
||||
}
|
||||
if(min != INT16_MAX) {
|
||||
jp["min"] = serialized(String(min, 4));
|
||||
}
|
||||
if(max != INT16_MIN) {
|
||||
jp["max"] = serialized(String(max, 4));
|
||||
}
|
||||
if(min1hrIdx != -1) {
|
||||
jp["cheapest1hr"] = String(ts1hr);
|
||||
}
|
||||
if(min3hrIdx != -1) {
|
||||
jp["cheapest3hr"] = String(ts1hr);
|
||||
}
|
||||
if(min6hrIdx != -1) {
|
||||
jp["cheapest6hr"] = String(ts1hr);
|
||||
}
|
||||
for(int i = 0; i < 24; i++) {
|
||||
double val = values[i];
|
||||
if(val == ENTSOE_NO_VALUE) break;
|
||||
jp[String(i)] = serialized(String(val, 4));
|
||||
}
|
||||
if(min != INT16_MAX) {
|
||||
jp["min"] = serialized(String(min, 4));
|
||||
}
|
||||
if(max != INT16_MIN) {
|
||||
jp["max"] = serialized(String(max, 4));
|
||||
}
|
||||
|
||||
if(min1hrIdx != -1) {
|
||||
jp["cheapest1hr"] = String(ts1hr);
|
||||
}
|
||||
if(min3hrIdx != -1) {
|
||||
jp["cheapest3hr"] = String(ts3hr);
|
||||
}
|
||||
if(min6hrIdx != -1) {
|
||||
jp["cheapest6hr"] = String(ts6hr);
|
||||
}
|
||||
|
||||
String msg;
|
||||
serializeJson(json, msg);
|
||||
mqtt.publish(config.getMqttPublishTopic(), msg.c_str());
|
||||
break;
|
||||
}
|
||||
case 1: // RAW
|
||||
case 2:
|
||||
// Send updated prices if we have them
|
||||
if(strcmp(config.getEntsoeApiToken(), "") != 0) {
|
||||
for(int i = 0; i < 48; i++) {
|
||||
{
|
||||
for(int i = 0; i < 24; i++) {
|
||||
double val = values[i];
|
||||
if(val == ENTSOE_NO_VALUE) {
|
||||
mqtt.publish(String(config.getMqttPublishTopic()) + "/price/" + String(i), "");
|
||||
@@ -712,6 +731,8 @@ void sendPricesToMqtt() {
|
||||
} else {
|
||||
mqtt.publish(String(config.getMqttPublishTopic()) + "/price/" + String(i), String(val, 4));
|
||||
}
|
||||
mqtt.loop();
|
||||
delay(10);
|
||||
}
|
||||
if(min != INT16_MAX) {
|
||||
mqtt.publish(String(config.getMqttPublishTopic()) + "/price/min", String(min, 4));
|
||||
@@ -719,6 +740,7 @@ void sendPricesToMqtt() {
|
||||
if(max != INT16_MIN) {
|
||||
mqtt.publish(String(config.getMqttPublishTopic()) + "/price/max", String(max, 4));
|
||||
}
|
||||
|
||||
if(min1hrIdx != -1) {
|
||||
mqtt.publish(String(config.getMqttPublishTopic()) + "/price/cheapest/1hr", String(ts1hr));
|
||||
}
|
||||
@@ -910,7 +932,6 @@ void readHanPort() {
|
||||
mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/import/active/accumulated", String(data.getActiveImportCounter(), 2));
|
||||
mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/export/reactive/accumulated", String(data.getReactiveExportCounter(), 2));
|
||||
mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/export/active/accumulated", String(data.getActiveExportCounter(), 2));
|
||||
|
||||
sendPricesToMqtt();
|
||||
case 2:
|
||||
// Only send data if changed. ID and Type is sent on the 10s interval only if changed
|
||||
|
||||
@@ -40,10 +40,13 @@ void EntsoeApi::setMultiplier(double multiplier) {
|
||||
char* EntsoeApi::getCurrency() {
|
||||
return currency;
|
||||
}
|
||||
|
||||
double EntsoeApi::getValueForHour(int hour) {
|
||||
tmElements_t tm;
|
||||
time_t cur = time(nullptr);
|
||||
return getValueForHour(cur, hour);
|
||||
}
|
||||
|
||||
double EntsoeApi::getValueForHour(time_t cur, int hour) {
|
||||
tmElements_t tm;
|
||||
if(tz != NULL)
|
||||
cur = tz->toLocal(cur);
|
||||
breakTime(cur, tm);
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
bool loop();
|
||||
|
||||
double getValueForHour(int hour);
|
||||
double getValueForHour(time_t now, int hour);
|
||||
char* getCurrency();
|
||||
|
||||
void setToken(const char* token);
|
||||
|
||||
@@ -295,19 +295,18 @@ void AmsWebServer::indexHtml() {
|
||||
html.replace("${data.P}", String(data.getActiveImportPower()));
|
||||
html.replace("${data.PO}", String(data.getActiveExportPower()));
|
||||
html.replace("${display.export}", config->getProductionCapacity() > 0 ? "" : "none");
|
||||
html.replace("${text.import}", config->getProductionCapacity() > 0 ? "Import" : "Consumption");
|
||||
html.replace("${display.nonexport}", config->getProductionCapacity() > 0 ? "none" : "");
|
||||
html.replace("${text.import}", config->getProductionCapacity() > 0 ? "Import" : "Use");
|
||||
html.replace("${display.3p}", data.isThreePhase() ? "" : "none");
|
||||
|
||||
html.replace("${data.U1}", u1 > 0 ? String(u1, 1) : "");
|
||||
html.replace("${data.I1}", u1 > 0 ? String(i1, 1) : "");
|
||||
html.replace("${display.P1}", u1 > 0 ? "" : "none");
|
||||
|
||||
html.replace("${data.U2}", u2 > 0 ? String(u2, 1) : "");
|
||||
html.replace("${data.I2}", u2 > 0 ? String(i2, 1) : "");
|
||||
html.replace("${display.P2}", u2 > 0 ? "" : "none");
|
||||
|
||||
html.replace("${data.U3}", u3 > 0 ? String(u3, 1) : "");
|
||||
html.replace("${data.I3}", u3 > 0 ? String(i3, 1) : "");
|
||||
html.replace("${display.P3}", u3 > 0 ? "" : "none");
|
||||
|
||||
html.replace("${data.tPI}", tpi > 0 ? String(tpi, 1) : "");
|
||||
html.replace("${data.tPO}", tpi > 0 ? String(tpo, 1) : "");
|
||||
@@ -327,6 +326,8 @@ void AmsWebServer::indexHtml() {
|
||||
html.replace("${wifi.channel}", WiFi.channel() > 0 ? String(WiFi.channel()) : "");
|
||||
html.replace("${wifi.ssid}", !WiFi.SSID().isEmpty() ? String(WiFi.SSID()) : "");
|
||||
|
||||
html.replace("${currentSeconds}", String((uint32_t)(millis64()/1000), 10));
|
||||
|
||||
server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN);
|
||||
server.send_P(200, "text/html", HEAD_HTML);
|
||||
server.sendContent(html);
|
||||
@@ -628,6 +629,9 @@ void AmsWebServer::dataJson() {
|
||||
double tqi = data.getReactiveImportCounter();
|
||||
double tqo = data.getReactiveExportCounter();
|
||||
|
||||
double volt = u1;
|
||||
double amp = i1;
|
||||
|
||||
if(u1 > 0) {
|
||||
json["data"]["U1"] = u1;
|
||||
json["data"]["I1"] = i1;
|
||||
@@ -635,10 +639,13 @@ void AmsWebServer::dataJson() {
|
||||
if(u2 > 0) {
|
||||
json["data"]["U2"] = u2;
|
||||
json["data"]["I2"] = i2;
|
||||
if(i2 > amp) amp = i2;
|
||||
}
|
||||
if(u3 > 0) {
|
||||
json["data"]["U3"] = u3;
|
||||
json["data"]["I3"] = i3;
|
||||
volt = (u1+u2+u3)/3;
|
||||
if(i3 > amp) amp = i3;
|
||||
}
|
||||
|
||||
if(tpi > 0) {
|
||||
@@ -650,6 +657,14 @@ void AmsWebServer::dataJson() {
|
||||
|
||||
json["p_pct"] = min(data.getActiveImportPower()*100/maxPwr, 100);
|
||||
|
||||
json["v"] = volt;
|
||||
json["v_pct"] = (max((int)volt-207, 1)*100/46);
|
||||
|
||||
int maxAmp = config->getMainFuse() == 0 ? 32 : config->getMainFuse();
|
||||
|
||||
json["a"] = amp;
|
||||
json["a_pct"] = amp * 100 / maxAmp;
|
||||
|
||||
if(config->getProductionCapacity() > 0) {
|
||||
int maxPrd = config->getProductionCapacity() * 1000;
|
||||
json["po_pct"] = min(data.getActiveExportPower()*100/maxPrd, 100);
|
||||
|
||||
Reference in New Issue
Block a user