Some changes after testing

This commit is contained in:
Gunnar Skjold
2025-09-04 14:06:14 +02:00
parent 060d5e0072
commit a4d4581442
7 changed files with 100 additions and 45 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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() {