mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-03-01 17:47:56 +00:00
Some changes after testing
This commit is contained in:
@@ -162,7 +162,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
float kwhi = (amsData->getActiveImportPower() * (((float) ms) / 3600000.0)) / 1000.0;
|
||||
if(kwhi > 0) {
|
||||
realtimeData->use += kwhi;
|
||||
float importPrice = ps == NULL ? PRICE_NO_VALUE : ps->getPrice(PRICE_DIRECTION_IMPORT);
|
||||
float importPrice = ps == NULL ? PRICE_NO_VALUE : ps->getCurrentPrice(PRICE_DIRECTION_IMPORT);
|
||||
if(importPrice != PRICE_NO_VALUE) {
|
||||
float cost = importPrice * kwhi;
|
||||
realtimeData->costHour += cost;
|
||||
@@ -177,7 +177,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
float kwhe = (amsData->getActiveExportPower() * (((float) ms) / 3600000.0)) / 1000.0;
|
||||
if(kwhe > 0) {
|
||||
realtimeData->produce += kwhe;
|
||||
float exportPrice = ps == NULL ? PRICE_NO_VALUE : ps->getPrice(PRICE_DIRECTION_EXPORT);
|
||||
float exportPrice = ps == NULL ? PRICE_NO_VALUE : ps->getCurrentPrice(PRICE_DIRECTION_EXPORT);
|
||||
if(exportPrice != PRICE_NO_VALUE) {
|
||||
float income = exportPrice * kwhe;
|
||||
realtimeData->incomeHour += income;
|
||||
@@ -216,13 +216,13 @@ void EnergyAccounting::calcDayCost() {
|
||||
for(uint8_t i = calcFromHour; i < realtimeData->currentHour; i++) {
|
||||
breakTime(now - ((local.Hour - i) * 3600), utc);
|
||||
|
||||
float priceIn = ps->getPriceForHour(PRICE_DIRECTION_IMPORT, i - local.Hour);
|
||||
float priceIn = ps->getPriceForRelativeHour(PRICE_DIRECTION_IMPORT, i - local.Hour);
|
||||
if(priceIn != PRICE_NO_VALUE) {
|
||||
int16_t wh = ds->getHourImport(utc.Hour);
|
||||
realtimeData->costDay += priceIn * (wh / 1000.0);
|
||||
}
|
||||
|
||||
float priceOut = ps->getPriceForHour(PRICE_DIRECTION_EXPORT, i - local.Hour);
|
||||
float priceOut = ps->getPriceForRelativeHour(PRICE_DIRECTION_EXPORT, i - local.Hour);
|
||||
if(priceOut != PRICE_NO_VALUE) {
|
||||
int16_t wh = ds->getHourExport(utc.Hour);
|
||||
realtimeData->incomeDay += priceOut * (wh / 1000.0);
|
||||
|
||||
@@ -373,7 +373,7 @@ bool HomeAssistantMqttHandler::publishPrices(PriceService* ps) {
|
||||
float values[38];
|
||||
for(int i = 0;i < 38; i++) values[i] = PRICE_NO_VALUE;
|
||||
for(uint8_t i = 0; i < 38; i++) {
|
||||
float val = ps->getPriceForHour(PRICE_DIRECTION_IMPORT, i);
|
||||
float val = ps->getPriceForRelativeHour(PRICE_DIRECTION_IMPORT, i);
|
||||
values[i] = val;
|
||||
|
||||
if(val == PRICE_NO_VALUE) break;
|
||||
|
||||
@@ -294,7 +294,7 @@ bool JsonMqttHandler::publishPrices(PriceService* ps) {
|
||||
float values[38];
|
||||
for(int i = 0;i < 38; i++) values[i] = PRICE_NO_VALUE;
|
||||
for(uint8_t i = 0; i < 38; i++) {
|
||||
float val = ps->getPriceForHour(PRICE_DIRECTION_IMPORT, i);
|
||||
float val = ps->getPriceForRelativeHour(PRICE_DIRECTION_IMPORT, i);
|
||||
values[i] = val;
|
||||
|
||||
if(val == PRICE_NO_VALUE) break;
|
||||
|
||||
@@ -81,17 +81,15 @@ public:
|
||||
uint8_t getResolutionInMinutes();
|
||||
uint8_t getNumberOfPointsAvailable();
|
||||
|
||||
bool isExportPricesDifferentFromImport() {
|
||||
return today != NULL && today->isExportPricesDifferentFromImport();
|
||||
}
|
||||
bool isExportPricesDifferentFromImport();
|
||||
|
||||
bool hasPrice() { return hasPrice(PRICE_DIRECTION_IMPORT); }
|
||||
bool hasPrice(uint8_t direction) { return getPrice(direction) != PRICE_NO_VALUE; }
|
||||
bool hasPrice(uint8_t direction) { return getCurrentPrice(direction) != PRICE_NO_VALUE; }
|
||||
bool hasPricePoint(uint8_t direction, int8_t point) { return getPricePoint(direction, point) != PRICE_NO_VALUE; }
|
||||
|
||||
float getPrice(uint8_t direction) { return getPricePoint(direction, 0); }
|
||||
float getPricePoint(uint8_t direction, int8_t point);
|
||||
float getPriceForHour(uint8_t direction, int8_t hour); // If not 60min interval, average
|
||||
float getCurrentPrice(uint8_t direction);
|
||||
float getPricePoint(uint8_t direction, uint8_t point);
|
||||
float getPriceForRelativeHour(uint8_t direction, int8_t hour); // If not 60min interval, average
|
||||
|
||||
std::vector<PriceConfig>& getPriceConfig();
|
||||
void setPriceConfig(uint8_t index, PriceConfig &priceConfig);
|
||||
@@ -141,6 +139,6 @@ private:
|
||||
float getCurrencyMultiplier(const char* from, const char* to, time_t t);
|
||||
bool timeIsInPeriod(tmElements_t tm, PriceConfig pc);
|
||||
float getFixedPrice(uint8_t direction, int8_t hour);
|
||||
float getEnergyPricePoint(uint8_t direction, int8_t point);
|
||||
float getEnergyPricePoint(uint8_t direction, uint8_t point);
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -118,7 +118,17 @@ uint8_t PriceService::getNumberOfPointsAvailable() {
|
||||
return today->getNumberOfPoints();
|
||||
}
|
||||
|
||||
float PriceService::getPricePoint(uint8_t direction, int8_t point) {
|
||||
bool PriceService::isExportPricesDifferentFromImport() {
|
||||
for (uint8_t i = 0; i < priceConfig.size(); i++) {
|
||||
PriceConfig pc = priceConfig.at(i);
|
||||
if(pc.direction != PRICE_DIRECTION_BOTH) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return today != NULL && today->isExportPricesDifferentFromImport();
|
||||
}
|
||||
|
||||
float PriceService::getPricePoint(uint8_t direction, uint8_t point) {
|
||||
float value = getFixedPrice(direction, point * getResolutionInMinutes() / 60);
|
||||
if(value == PRICE_NO_VALUE) value = getEnergyPricePoint(direction, point);
|
||||
if(value == PRICE_NO_VALUE) return PRICE_NO_VALUE;
|
||||
@@ -126,8 +136,7 @@ float PriceService::getPricePoint(uint8_t direction, int8_t point) {
|
||||
tmElements_t tm;
|
||||
time_t ts = time(nullptr);
|
||||
breakTime(tz->toLocal(ts), tm);
|
||||
tm.Minute = (tm.Minute / getResolutionInMinutes()) * getResolutionInMinutes();
|
||||
tm.Second = 0;
|
||||
tm.Hour = tm.Minute = tm.Second = 0;
|
||||
breakTime(makeTime(tm) + (point * SECS_PER_MIN * getResolutionInMinutes()), tm);
|
||||
|
||||
for (uint8_t i = 0; i < priceConfig.size(); i++) {
|
||||
@@ -135,15 +144,17 @@ float PriceService::getPricePoint(uint8_t direction, int8_t point) {
|
||||
if(pc.type == PRICE_TYPE_FIXED) continue;
|
||||
if((pc.direction & direction) != direction) continue;
|
||||
if(!timeIsInPeriod(tm, pc)) continue;
|
||||
float pcVal = pc.value / 10000.0;
|
||||
|
||||
switch(pc.type) {
|
||||
case PRICE_TYPE_ADD:
|
||||
value += pc.value / 10000.0;
|
||||
value += pcVal;
|
||||
break;
|
||||
case PRICE_TYPE_SUBTRACT:
|
||||
value -= pc.value / 10000.0;
|
||||
value -= pcVal;
|
||||
break;
|
||||
case PRICE_TYPE_PCT:
|
||||
value += ((pc.value / 10000.0) * value) / 100.0;
|
||||
value += (pcVal * value) / 100.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -151,14 +162,27 @@ float PriceService::getPricePoint(uint8_t direction, int8_t point) {
|
||||
return value;
|
||||
}
|
||||
|
||||
float PriceService::getEnergyPricePoint(uint8_t direction, int8_t point) {
|
||||
float value = PRICE_NO_VALUE;
|
||||
float PriceService::getCurrentPrice(uint8_t direction) {
|
||||
if(today == NULL) return PRICE_NO_VALUE;
|
||||
|
||||
time_t ts = time(nullptr);
|
||||
tmElements_t tm;
|
||||
breakTime(tz->toLocal(ts), tm);
|
||||
float pointsPerHour = 60.0 / today->getResolutionInMinutes();
|
||||
uint8_t pos = (uint8_t) floor(pointsPerHour * tm.Hour);
|
||||
|
||||
return getPricePoint(direction, pos);
|
||||
}
|
||||
|
||||
float PriceService::getEnergyPricePoint(uint8_t direction, uint8_t point) {
|
||||
uint8_t pos = point;
|
||||
float multiplier = 1.0;
|
||||
uint8_t numberOfPointsToday = 24;
|
||||
if(today != NULL) {
|
||||
numberOfPointsToday = today->getNumberOfPoints();
|
||||
}
|
||||
|
||||
float value = PRICE_NO_VALUE;
|
||||
if(pos >= numberOfPointsToday) {
|
||||
pos = pos - numberOfPointsToday;
|
||||
if(tomorrow == NULL)
|
||||
@@ -183,25 +207,41 @@ float PriceService::getEnergyPricePoint(uint8_t direction, int8_t point) {
|
||||
return value == PRICE_NO_VALUE ? PRICE_NO_VALUE : value * multiplier;
|
||||
}
|
||||
|
||||
float PriceService::getPriceForHour(uint8_t direction, int8_t hour) {
|
||||
float PriceService::getPriceForRelativeHour(uint8_t direction, int8_t hour) {
|
||||
float value = getFixedPrice(direction, hour);
|
||||
if(value != PRICE_NO_VALUE) return value;
|
||||
if(today == NULL) return PRICE_NO_VALUE;
|
||||
time_t ts = time(nullptr);
|
||||
tmElements_t tm;
|
||||
|
||||
breakTime(tz->toLocal(ts), tm);
|
||||
tm.Hour = tm.Minute = tm.Second = 0;
|
||||
time_t startOfDay = makeTime(tm);
|
||||
|
||||
if(makeTime(tm) < startOfDay) {
|
||||
return PRICE_NO_VALUE;
|
||||
}
|
||||
|
||||
breakTime(tz->toLocal(ts), tm);
|
||||
int8_t targetHour = tm.Hour + hour;
|
||||
|
||||
if(today->getResolutionInMinutes() == 60) {
|
||||
return getPricePoint(direction, hour);
|
||||
return getPricePoint(direction, targetHour);
|
||||
}
|
||||
|
||||
float valueSum = 0.0f;
|
||||
uint8_t valueCount = 0;
|
||||
float indexIncrements = 60 / today->getResolutionInMinutes();
|
||||
uint8_t priceMapIndexStart = (uint8_t) floor(indexIncrements * hour);
|
||||
uint8_t priceMapIndexEnd = (uint8_t) ceil(indexIncrements * (hour+1));
|
||||
float indexIncrements = 60.0 / today->getResolutionInMinutes();
|
||||
uint8_t priceMapIndexStart = (uint8_t) floor(indexIncrements * targetHour);
|
||||
uint8_t priceMapIndexEnd = (uint8_t) ceil(indexIncrements * (targetHour+1));
|
||||
|
||||
for(uint8_t mi = priceMapIndexStart; mi < priceMapIndexEnd; mi++) {
|
||||
float val = getPricePoint(direction, mi);
|
||||
if(val == PRICE_NO_VALUE) continue;
|
||||
valueSum += val;
|
||||
valueCount++;
|
||||
}
|
||||
if(valueCount == 0) return PRICE_NO_VALUE;
|
||||
return valueSum / valueCount;
|
||||
}
|
||||
|
||||
@@ -530,19 +570,34 @@ PricesContainer* PriceService::fetchPrices(time_t t) {
|
||||
GCMParser gcm(key, auth);
|
||||
int8_t gcmRet = gcm.parse(content, ctx);
|
||||
if(gcmRet > 0) {
|
||||
AmsPriceV2Header* header = (AmsPriceV2Header*) (content-gcmRet);
|
||||
AmsPriceV2Header* header = (AmsPriceV2Header*) (content+gcmRet);
|
||||
|
||||
PricesContainer* ret = new PricesContainer(header->source);
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
if (debugger->isActive(RemoteDebug::DEBUG))
|
||||
#endif
|
||||
debugger->printf_P(PSTR("(PriceService) Setting up price container with pt%dm, %dpts, edi: %d\n"), header->resolutionInMinutes, header->numberOfPoints, header->differentExportPrices);
|
||||
|
||||
ret->setup(header->resolutionInMinutes, header->numberOfPoints, header->differentExportPrices);
|
||||
ret->setCurrency(header->currency);
|
||||
int32_t* points = (int32_t*) &header[1];
|
||||
|
||||
for(uint8_t i = 0; i < header->numberOfPoints; i++) {
|
||||
ret->setPrice(i, points[i], PRICE_DIRECTION_IMPORT);
|
||||
float value = ntohl(points[i]) / 10000.0;
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
if (debugger->isActive(RemoteDebug::VERBOSE))
|
||||
#endif
|
||||
debugger->printf_P(PSTR("(PriceService) Import price position and value received: %d :: %.2f\n"), i, value);
|
||||
ret->setPrice(i, value, PRICE_DIRECTION_IMPORT);
|
||||
}
|
||||
if(header->differentExportPrices) {
|
||||
for(uint8_t i = 0; i < header->numberOfPoints; i++) {
|
||||
ret->setPrice(i, points[i], PRICE_DIRECTION_EXPORT);
|
||||
float value = ntohl(points[i]) / 10000.0;
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
if (debugger->isActive(RemoteDebug::VERBOSE))
|
||||
#endif
|
||||
debugger->printf_P(PSTR("(PriceService) Export price position and value received: %d :: %.2f\n"), i, value);
|
||||
ret->setPrice(i, value, PRICE_DIRECTION_EXPORT);
|
||||
}
|
||||
}
|
||||
lastError = 0;
|
||||
|
||||
@@ -250,7 +250,7 @@ bool RawMqttHandler::publishPrices(PriceService* ps) {
|
||||
float values[34];
|
||||
for(int i = 0;i < 34; i++) values[i] = PRICE_NO_VALUE;
|
||||
for(uint8_t i = 0; i < 34; i++) {
|
||||
float val = ps->getPriceForHour(PRICE_DIRECTION_IMPORT, i);
|
||||
float val = ps->getPriceForRelativeHour(PRICE_DIRECTION_IMPORT, i);
|
||||
values[i] = val;
|
||||
|
||||
if(i > 23) continue;
|
||||
|
||||
@@ -581,8 +581,8 @@ void AmsWebServer::dataJson() {
|
||||
mqttStatus = 3;
|
||||
}
|
||||
|
||||
float price = ps == NULL ? PRICE_NO_VALUE : ps->getPrice(PRICE_DIRECTION_IMPORT);
|
||||
float exportPrice = ps == NULL ? PRICE_NO_VALUE : ps->getPrice(PRICE_DIRECTION_EXPORT);
|
||||
float price = ps == NULL ? PRICE_NO_VALUE : ps->getCurrentPrice(PRICE_DIRECTION_IMPORT);
|
||||
float exportPrice = ps == NULL ? PRICE_NO_VALUE : ps->getCurrentPrice(PRICE_DIRECTION_EXPORT);
|
||||
|
||||
String peaks = "";
|
||||
for(uint8_t i = 1; i <= ea->getConfig()->hours; i++) {
|
||||
@@ -731,7 +731,7 @@ void AmsWebServer::energyPriceJson() {
|
||||
|
||||
float prices[36];
|
||||
for(int i = 0; i < 36; i++) {
|
||||
prices[i] = ps->getPriceForHour(PRICE_DIRECTION_IMPORT, i);
|
||||
prices[i] = ps->getPriceForRelativeHour(PRICE_DIRECTION_IMPORT, i);
|
||||
}
|
||||
|
||||
uint16_t pos = snprintf_P(buf, BufferSize, PSTR("{\"currency\":\"%s\",\"source\":\"%s\""),
|
||||
@@ -781,7 +781,7 @@ void AmsWebServer::priceJson(uint8_t direction) {
|
||||
prices[i] = ps->getPricePoint(direction, i);
|
||||
}
|
||||
|
||||
uint16_t pos = snprintf_P(buf, BufferSize, PSTR("{\"currency\":\"%s\",\"source\":\"%s\",\"\"resolution\":%d,\"direction\":\"%s\",\"importExportPriceDifferent\":%s"),
|
||||
snprintf_P(buf, BufferSize, PSTR("{\"currency\":\"%s\",\"source\":\"%s\",\"resolution\":%d,\"direction\":\"%s\",\"importExportPriceDifferent\":%s"),
|
||||
ps->getCurrency(),
|
||||
ps->getSource(),
|
||||
ps->getResolutionInMinutes(),
|
||||
@@ -789,22 +789,24 @@ void AmsWebServer::priceJson(uint8_t direction) {
|
||||
ps->isExportPricesDifferentFromImport() ? "true" : "false"
|
||||
);
|
||||
|
||||
for(uint8_t i = 0;i < numberOfPoints; i++) {
|
||||
if(prices[i] == PRICE_NO_VALUE) {
|
||||
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR(",\"%02d\":null"), i);
|
||||
} else {
|
||||
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR(",\"%02d\":%.4f"), i, prices[i]);
|
||||
}
|
||||
}
|
||||
snprintf_P(buf+pos, BufferSize-pos, PSTR("}"));
|
||||
|
||||
addConditionalCloudHeaders();
|
||||
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
|
||||
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
|
||||
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
|
||||
|
||||
server.setContentLength(strlen(buf));
|
||||
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
|
||||
server.send(200, MIME_JSON, buf);
|
||||
|
||||
for(uint8_t i = 0;i < numberOfPoints; i++) {
|
||||
if(prices[i] == PRICE_NO_VALUE) {
|
||||
snprintf_P(buf, BufferSize, PSTR(",\"%02d\":null"), i);
|
||||
server.sendContent(buf);
|
||||
} else {
|
||||
snprintf_P(buf, BufferSize, PSTR(",\"%02d\":%.4f"), i, prices[i]);
|
||||
server.sendContent(buf);
|
||||
}
|
||||
}
|
||||
server.sendContent_P(PSTR("}"));
|
||||
}
|
||||
|
||||
void AmsWebServer::temperatureJson() {
|
||||
|
||||
Reference in New Issue
Block a user