mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-03-11 13:05:31 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fd383c1ef | ||
|
|
a931f4cef8 | ||
|
|
fddecaab39 | ||
|
|
e5eab82d68 | ||
|
|
8ae1d46b2a | ||
|
|
99ccb03b45 | ||
|
|
b8f2d501a5 | ||
|
|
e042806619 | ||
|
|
16f9ed7ecb | ||
|
|
3eaefefd26 | ||
|
|
03c8c3ddbc | ||
|
|
08371b9078 | ||
|
|
a6ae25f3e7 |
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
@@ -68,14 +68,11 @@ jobs:
|
||||
run: pio lib install
|
||||
|
||||
- name: Create release with release notes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref_name }}
|
||||
run: |
|
||||
gh release create "$tag" \
|
||||
--repo="$GITHUB_REPOSITORY" \
|
||||
--title="${tag#v}" \
|
||||
--generate-notes
|
||||
id: create_release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: Release v${{ steps.release_tag.outputs.tag }}
|
||||
generateReleaseNotes: true
|
||||
|
||||
- name: Build esp8266 firmware
|
||||
run: pio run -e esp8266
|
||||
|
||||
@@ -11,6 +11,8 @@ IEC6205621::IEC6205621(const char* p, Timezone* tz, MeterConfig* meterConfig) {
|
||||
if(strlen(p) < 16)
|
||||
return;
|
||||
|
||||
this->packageTimestamp = time(nullptr);
|
||||
|
||||
String payload(p+1);
|
||||
|
||||
lastUpdateMillis = millis64();
|
||||
@@ -59,15 +61,44 @@ IEC6205621::IEC6205621(const char* p, Timezone* tz, MeterConfig* meterConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
tmElements_t tm { 0, 0, 0, 0, 0, 0, 0 };
|
||||
String timestamp = extract(payload, F("1.0.0"));
|
||||
if(timestamp.length() > 10) {
|
||||
tmElements_t tm;
|
||||
tm.Year = (timestamp.substring(0,2).toInt() + 2000) - 1970;
|
||||
tm.Month = timestamp.substring(4,6).toInt();
|
||||
tm.Day = timestamp.substring(2,4).toInt();
|
||||
tm.Hour = timestamp.substring(6,8).toInt();
|
||||
tm.Minute = timestamp.substring(8,10).toInt();
|
||||
tm.Second = timestamp.substring(10,12).toInt();
|
||||
if(timestamp.length() == 13) { // yyMMddHHmmssX
|
||||
char x = timestamp.charAt(12);
|
||||
if(x == 'S' || x == 'W') {
|
||||
tm.Year = (timestamp.substring(0,2).toInt() + 2000) - 1970;
|
||||
tm.Month = timestamp.substring(2,4).toInt();
|
||||
tm.Day = timestamp.substring(4,6).toInt();
|
||||
tm.Hour = timestamp.substring(6,8).toInt();
|
||||
tm.Minute = timestamp.substring(8,10).toInt();
|
||||
tm.Second = timestamp.substring(10,12).toInt();
|
||||
}
|
||||
} else if(timestamp.length() == 17) { // yyyyMMdd HH:mm:ss
|
||||
char x = timestamp.charAt(11);
|
||||
char y = timestamp.charAt(14);
|
||||
if(x == ':' && y == ':') {
|
||||
tm.Year = (timestamp.substring(0,4).toInt()) - 1970;
|
||||
tm.Month = timestamp.substring(4,6).toInt();
|
||||
tm.Day = timestamp.substring(6,8).toInt();
|
||||
tm.Hour = timestamp.substring(9,11).toInt();
|
||||
tm.Minute = timestamp.substring(12,14).toInt();
|
||||
tm.Second = timestamp.substring(15,17).toInt();
|
||||
}
|
||||
} else if(timestamp.length() == 19) { // yyyy-MM-dd HH:mm:ss
|
||||
char x = timestamp.charAt(4);
|
||||
char y = timestamp.charAt(13);
|
||||
if(x == '-' && y == ':') {
|
||||
tm.Year = (timestamp.substring(0,4).toInt()) - 1970;
|
||||
tm.Month = timestamp.substring(5,7).toInt();
|
||||
tm.Day = timestamp.substring(8,10).toInt();
|
||||
tm.Hour = timestamp.substring(11,13).toInt();
|
||||
tm.Minute = timestamp.substring(14,16).toInt();
|
||||
tm.Second = timestamp.substring(17,19).toInt();
|
||||
}
|
||||
} else {
|
||||
meterTimestamp = 0;
|
||||
}
|
||||
if(tm.Year > 0) {
|
||||
meterTimestamp = makeTime(tm);
|
||||
if(tz != NULL) meterTimestamp = tz->toUTC(meterTimestamp);
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
|
||||
data = getCosemDataAt(idx++, ((char *) (d)));
|
||||
if(data->base.length == 0x12) {
|
||||
apply(state);
|
||||
listType = state.getListType() > 2 ? state.getListType() : 2;
|
||||
listType = state.getListType() > 4 ? state.getListType() : 4;
|
||||
|
||||
// 42.0.0 COSEM logical device name
|
||||
idx++;
|
||||
@@ -327,7 +327,7 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
|
||||
CosemData* no7 = getCosemDataAt(7, ((char *) (d)));
|
||||
if(no7->base.type == CosemTypeLongUnsigned) {
|
||||
apply(state);
|
||||
listType = state.getListType() > 2 ? state.getListType() : 2;
|
||||
listType = state.getListType() > 4 ? state.getListType() : 4;
|
||||
|
||||
// 42.0.0 COSEM logical device name
|
||||
idx++;
|
||||
|
||||
@@ -42,33 +42,75 @@ LNG::LNG(AmsData& meterState, const char* payload, uint8_t useMeterType, MeterCo
|
||||
switch(descriptor->obis[2]) {
|
||||
case 1:
|
||||
o170 = getNumber(item);
|
||||
if(meterConfig->wattageMultiplier > 0) {
|
||||
o170 = o170 > 0 ? o170 * (meterConfig->wattageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
o270 = getNumber(item);
|
||||
if(meterConfig->wattageMultiplier > 0) {
|
||||
o270 = o270 > 0 ? o270 * (meterConfig->wattageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
reactiveImportPower = getNumber(item);
|
||||
if(meterConfig->wattageMultiplier > 0) {
|
||||
reactiveImportPower = reactiveImportPower > 0 ? reactiveImportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
reactiveExportPower = getNumber(item);
|
||||
if(meterConfig->wattageMultiplier > 0) {
|
||||
reactiveExportPower = reactiveExportPower > 0 ? reactiveExportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 21:
|
||||
l1activeImportPower = getNumber(item);
|
||||
listType = listType >= 4 ? listType : 4;
|
||||
break;
|
||||
case 41:
|
||||
l2activeImportPower = getNumber(item);
|
||||
listType = listType >= 4 ? listType : 4;
|
||||
break;
|
||||
case 61:
|
||||
l3activeImportPower = getNumber(item);
|
||||
listType = listType >= 4 ? listType : 4;
|
||||
break;
|
||||
case 31:
|
||||
l1current = getNumber(item) / 100.0;
|
||||
if(meterConfig->amperageMultiplier > 0) {
|
||||
l1current = l1current > 0 ? l1current * (meterConfig->amperageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 51:
|
||||
l2current = getNumber(item) / 100.0;
|
||||
if(meterConfig->amperageMultiplier > 0) {
|
||||
l2current = l2current > 0 ? l2current * (meterConfig->amperageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 71:
|
||||
l3current = getNumber(item) / 100.0;
|
||||
if(meterConfig->amperageMultiplier > 0) {
|
||||
l3current = l3current > 0 ? l3current * (meterConfig->amperageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
l1voltage = getNumber(item) / 10.0;
|
||||
if(meterConfig->voltageMultiplier > 0) {
|
||||
l1voltage = l1voltage > 0 ? l1voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 52:
|
||||
l2voltage = getNumber(item) / 10.0;
|
||||
if(meterConfig->voltageMultiplier > 0) {
|
||||
l2voltage = l2voltage > 0 ? l2voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 72:
|
||||
l3voltage = getNumber(item) / 10.0;
|
||||
if(meterConfig->voltageMultiplier > 0) {
|
||||
l3voltage = l3voltage > 0 ? l3voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -79,30 +121,54 @@ LNG::LNG(AmsData& meterState, const char* payload, uint8_t useMeterType, MeterCo
|
||||
case 1:
|
||||
o180 = getNumber(item);
|
||||
activeImportCounter = o180 / 1000.0;
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
activeImportCounter = activeImportCounter > 0 ? activeImportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
o280 = getNumber(item);
|
||||
activeExportCounter = o280 / 1000.0;
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
activeExportCounter = activeExportCounter > 0 ? activeExportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
o380 = getNumber(item);
|
||||
reactiveImportCounter = o380 / 1000.0;
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
reactiveImportCounter = reactiveImportCounter > 0 ? reactiveImportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
o480 = getNumber(item);
|
||||
reactiveExportCounter = o480 / 1000.0;
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
reactiveExportCounter = reactiveExportCounter > 0 ? reactiveExportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
o580 = getNumber(item);
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
o580 = o580 > 0 ? o580 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
o680 = getNumber(item);
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
o680 = o680 > 0 ? o680 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
o780 = getNumber(item);
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
o780 = o780 > 0 ? o780 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
o880 = getNumber(item);
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
o880 = o880 > 0 ? o880 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if(descriptor->obis[4] == 1) {
|
||||
@@ -110,9 +176,15 @@ LNG::LNG(AmsData& meterState, const char* payload, uint8_t useMeterType, MeterCo
|
||||
switch(descriptor->obis[2]) {
|
||||
case 1:
|
||||
o181 = getNumber(item);
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
o181 = o181 > 0 ? o181 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
o281 = getNumber(item);
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
o281 = o281 > 0 ? o281 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if(descriptor->obis[4] == 2) {
|
||||
@@ -120,9 +192,15 @@ LNG::LNG(AmsData& meterState, const char* payload, uint8_t useMeterType, MeterCo
|
||||
switch(descriptor->obis[2]) {
|
||||
case 1:
|
||||
o182 = getNumber(item);
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
o182 = o182 > 0 ? o182 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
o282 = getNumber(item);
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
o282 = o282 > 0 ? o282 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -183,28 +261,6 @@ LNG::LNG(AmsData& meterState, const char* payload, uint8_t useMeterType, MeterCo
|
||||
lastUpdateMillis = millis64();
|
||||
}
|
||||
lastUpdateMillis = millis64();
|
||||
if(meterConfig->wattageMultiplier > 0) {
|
||||
activeImportPower = activeImportPower > 0 ? activeImportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
|
||||
activeExportPower = activeExportPower > 0 ? activeExportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
|
||||
reactiveImportPower = reactiveImportPower > 0 ? reactiveImportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
|
||||
reactiveExportPower = reactiveExportPower > 0 ? reactiveExportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
if(meterConfig->voltageMultiplier > 0) {
|
||||
l1voltage = l1voltage > 0 ? l1voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
|
||||
l2voltage = l2voltage > 0 ? l2voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
|
||||
l3voltage = l3voltage > 0 ? l3voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
if(meterConfig->amperageMultiplier > 0) {
|
||||
l1current = l1current > 0 ? l1current * (meterConfig->amperageMultiplier / 1000.0) : 0;
|
||||
l2current = l2current > 0 ? l2current * (meterConfig->amperageMultiplier / 1000.0) : 0;
|
||||
l3current = l3current > 0 ? l3current * (meterConfig->amperageMultiplier / 1000.0) : 0;
|
||||
}
|
||||
if(meterConfig->accumulatedMultiplier > 0) {
|
||||
activeImportCounter = activeImportCounter > 0 ? activeImportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
activeExportCounter = activeExportCounter > 0 ? activeExportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
reactiveImportCounter = reactiveImportCounter > 0 ? reactiveImportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
reactiveExportCounter = reactiveExportCounter > 0 ? reactiveExportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
|
||||
}
|
||||
|
||||
threePhase = l1voltage > 0 && l2voltage > 0 && l3voltage > 0;
|
||||
if(!threePhase)
|
||||
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
PriceService(Stream*);
|
||||
#endif
|
||||
void setup(PriceServiceConfig&);
|
||||
void setTimezone(Timezone* tz);
|
||||
bool loop();
|
||||
|
||||
char* getToken();
|
||||
@@ -114,6 +115,7 @@ private:
|
||||
std::vector<PriceConfig> priceConfig;
|
||||
|
||||
Timezone* tz = NULL;
|
||||
Timezone* entsoeTz = NULL;
|
||||
|
||||
static const uint16_t BufferSize = 256;
|
||||
char* buf;
|
||||
@@ -129,5 +131,6 @@ private:
|
||||
PricesContainer* fetchPrices(time_t);
|
||||
bool retrieve(const char* url, Stream* doc);
|
||||
float getCurrencyMultiplier(const char* from, const char* to, time_t t);
|
||||
bool timeIsInPeriod(tmElements_t tm, PriceConfig pc);
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -32,7 +32,8 @@ PriceService::PriceService(Stream* Debug) : priceConfig(std::vector<PriceConfig>
|
||||
// Entso-E uses CET/CEST
|
||||
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};
|
||||
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60};
|
||||
tz = new Timezone(CEST, CET);
|
||||
entsoeTz = new Timezone(CEST, CET);
|
||||
tz = entsoeTz;
|
||||
|
||||
tomorrowFetchMinute = 15 + random(45); // Random between 13:15 and 14:00
|
||||
}
|
||||
@@ -72,6 +73,10 @@ void PriceService::setup(PriceServiceConfig& config) {
|
||||
load();
|
||||
}
|
||||
|
||||
void PriceService::setTimezone(Timezone* tz) {
|
||||
this->tz = tz;
|
||||
}
|
||||
|
||||
char* PriceService::getToken() {
|
||||
return this->config->entsoeToken;
|
||||
}
|
||||
@@ -111,29 +116,22 @@ float PriceService::getValueForHour(uint8_t direction, time_t ts, int8_t hour) {
|
||||
|
||||
tmElements_t tm;
|
||||
breakTime(tz->toLocal(ts + (hour * SECS_PER_HOUR)), tm);
|
||||
uint8_t day = 0x01 << ((tm.Wday+5)%7);
|
||||
uint32_t hrs = 0x01 << tm.Hour;
|
||||
|
||||
for (uint8_t i = 0; i < priceConfig.size(); i++) {
|
||||
PriceConfig pc = priceConfig.at(i);
|
||||
if(pc.type == PRICE_TYPE_FIXED) continue;
|
||||
uint8_t start_month = pc.start_month == 0 || pc.start_month > 12 ? 1 : pc.start_month;
|
||||
uint8_t start_dayofmonth = pc.start_dayofmonth == 0 || pc.start_dayofmonth > 31 ? 1 : pc.start_dayofmonth;
|
||||
uint8_t end_month = pc.end_month == 0 || pc.end_month > 12 ? 12 : pc.end_month;
|
||||
uint8_t end_dayofmonth = pc.end_dayofmonth == 0 || pc.end_dayofmonth > 31 ? 31 : pc.end_dayofmonth;
|
||||
|
||||
if((pc.direction & direction) == direction && (pc.days & day) == day && (pc.hours & hrs) == hrs && tm.Month >= start_month && tm.Day >= start_dayofmonth && tm.Month <= end_month && tm.Day <= end_dayofmonth) {
|
||||
switch(pc.type) {
|
||||
case PRICE_TYPE_ADD:
|
||||
ret += pc.value / 10000.0;
|
||||
break;
|
||||
case PRICE_TYPE_SUBTRACT:
|
||||
ret -= pc.value / 10000.0;
|
||||
break;
|
||||
case PRICE_TYPE_PCT:
|
||||
ret += ((pc.value / 10000.0) * ret) / 100.0;
|
||||
break;
|
||||
}
|
||||
if((pc.direction & direction) != direction) continue;
|
||||
if(!timeIsInPeriod(tm, pc)) continue;
|
||||
switch(pc.type) {
|
||||
case PRICE_TYPE_ADD:
|
||||
ret += pc.value / 10000.0;
|
||||
break;
|
||||
case PRICE_TYPE_SUBTRACT:
|
||||
ret -= pc.value / 10000.0;
|
||||
break;
|
||||
case PRICE_TYPE_PCT:
|
||||
ret += ((pc.value / 10000.0) * ret) / 100.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -142,48 +140,42 @@ float PriceService::getValueForHour(uint8_t direction, time_t ts, int8_t hour) {
|
||||
float PriceService::getEnergyPriceForHour(uint8_t direction, time_t ts, int8_t hour) {
|
||||
tmElements_t tm;
|
||||
breakTime(tz->toLocal(ts + (hour * SECS_PER_HOUR)), tm);
|
||||
uint8_t day = 0x01 << ((tm.Wday+5)%7);
|
||||
uint32_t hrs = 0x01 << tm.Hour;
|
||||
|
||||
float value = PRICE_NO_VALUE;
|
||||
for (uint8_t i = 0; i < priceConfig.size(); i++) {
|
||||
PriceConfig pc = priceConfig.at(i);
|
||||
if(pc.type != PRICE_TYPE_FIXED) continue;
|
||||
uint8_t start_month = pc.start_month == 0 || pc.start_month > 12 ? 1 : pc.start_month;
|
||||
uint8_t start_dayofmonth = pc.start_dayofmonth == 0 || pc.start_dayofmonth > 31 ? 1 : pc.start_dayofmonth;
|
||||
uint8_t end_month = pc.end_month == 0 || pc.end_month > 12 ? 12 : pc.end_month;
|
||||
uint8_t end_dayofmonth = pc.end_dayofmonth == 0 || pc.end_dayofmonth > 31 ? 31 : pc.end_dayofmonth;
|
||||
if((pc.direction & direction) != direction) continue;
|
||||
if(!timeIsInPeriod(tm, pc)) continue;
|
||||
|
||||
if((pc.direction & direction) == direction && (pc.days & day) == day && (pc.hours & hrs) == hrs && tm.Month >= start_month && tm.Day >= start_dayofmonth && tm.Month <= end_month && tm.Day <= end_dayofmonth) {
|
||||
if(value == PRICE_NO_VALUE) {
|
||||
value = pc.value / 10000.0;
|
||||
} else {
|
||||
value += pc.value / 10000.0;
|
||||
}
|
||||
if(value == PRICE_NO_VALUE) {
|
||||
value = pc.value / 10000.0;
|
||||
} else {
|
||||
value += pc.value / 10000.0;
|
||||
}
|
||||
}
|
||||
if(value != PRICE_NO_VALUE) return value;
|
||||
|
||||
int8_t pos = hour;
|
||||
|
||||
breakTime(tz->toLocal(ts), tm);
|
||||
breakTime(entsoeTz->toLocal(ts), tm);
|
||||
while(tm.Hour > 0) {
|
||||
ts -= 3600;
|
||||
breakTime(tz->toLocal(ts), tm);
|
||||
breakTime(entsoeTz->toLocal(ts), tm);
|
||||
pos++;
|
||||
}
|
||||
uint8_t hoursToday = 0;
|
||||
uint8_t todayDate = tm.Day;
|
||||
while(tm.Day == todayDate) {
|
||||
ts += 3600;
|
||||
breakTime(tz->toLocal(ts), tm);
|
||||
breakTime(entsoeTz->toLocal(ts), tm);
|
||||
hoursToday++;
|
||||
}
|
||||
uint8_t hoursTomorrow = 0;
|
||||
uint8_t tomorrowDate = tm.Day;
|
||||
while(tm.Day == tomorrowDate) {
|
||||
ts += 3600;
|
||||
breakTime(tz->toLocal(ts), tm);
|
||||
breakTime(entsoeTz->toLocal(ts), tm);
|
||||
hoursTomorrow++;
|
||||
}
|
||||
|
||||
@@ -244,7 +236,7 @@ bool PriceService::loop() {
|
||||
return false;
|
||||
|
||||
tmElements_t tm;
|
||||
breakTime(tz->toLocal(t), tm);
|
||||
breakTime(entsoeTz->toLocal(t), tm);
|
||||
|
||||
if(currentDay == 0) {
|
||||
currentDay = tm.Day;
|
||||
@@ -415,7 +407,7 @@ float PriceService::getCurrencyMultiplier(const char* from, const char* to, time
|
||||
PricesContainer* PriceService::fetchPrices(time_t t) {
|
||||
if(strlen(getToken()) > 0) {
|
||||
tmElements_t tm;
|
||||
breakTime(tz->toLocal(t), tm);
|
||||
breakTime(entsoeTz->toLocal(t), tm);
|
||||
time_t e1 = t - (tm.Hour * 3600) - (tm.Minute * 60) - tm.Second; // Local midnight
|
||||
time_t e2 = e1 + SECS_PER_DAY;
|
||||
tmElements_t d1, d2;
|
||||
@@ -452,7 +444,7 @@ PricesContainer* PriceService::fetchPrices(time_t t) {
|
||||
}
|
||||
} else if(hub) {
|
||||
tmElements_t tm;
|
||||
breakTime(tz->toLocal(t), tm);
|
||||
breakTime(entsoeTz->toLocal(t), tm);
|
||||
|
||||
String data;
|
||||
snprintf_P(buf, BufferSize, PSTR("http://hub.amsleser.no/hub/price/%s/%d/%d/%d?currency=%s"),
|
||||
@@ -632,4 +624,38 @@ bool PriceService::load() {
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PriceService::timeIsInPeriod(tmElements_t tm, PriceConfig pc) {
|
||||
uint8_t day = 0x01 << ((tm.Wday+5)%7);
|
||||
uint32_t hrs = 0x01 << tm.Hour;
|
||||
|
||||
if((pc.days & day) != day) return false;
|
||||
if((pc.hours & hrs) != hrs) return false;
|
||||
|
||||
tmElements_t tms;
|
||||
tms.Year = tm.Year;
|
||||
tms.Month = pc.start_month;
|
||||
tms.Day = pc.start_dayofmonth;
|
||||
tms.Hour = 0;
|
||||
tms.Minute = 0;
|
||||
tms.Second = 0;
|
||||
|
||||
tmElements_t tme;
|
||||
tme.Year = tm.Year;
|
||||
tme.Month = pc.end_month;
|
||||
tme.Day = pc.end_dayofmonth;
|
||||
tme.Hour = 23;
|
||||
tme.Minute = 59;
|
||||
tme.Second = 59;
|
||||
if(makeTime(tms) > makeTime(tme)) {
|
||||
if(makeTime(tm) <= makeTime(tme)) {
|
||||
tms.Year--;
|
||||
} else {
|
||||
tme.Year++;
|
||||
}
|
||||
}
|
||||
|
||||
return makeTime(tms) <= makeTime(tm) && makeTime(tme) >= makeTime(tm);
|
||||
}
|
||||
18
lib/SvelteUi/app/package-lock.json
generated
18
lib/SvelteUi/app/package-lock.json
generated
@@ -15,7 +15,7 @@
|
||||
"@sveltejs/vite-plugin-svelte": "^2.1.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"http-proxy-middleware": "^2.0.9",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"svelte": "^4.2.19",
|
||||
@@ -23,7 +23,7 @@
|
||||
"svelte-preprocess": "^5.0.3",
|
||||
"svelte-qrcode": "^1.0.0",
|
||||
"tailwindcss": "^3.3.1",
|
||||
"vite": "^4.5.9"
|
||||
"vite": "^4.5.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@@ -1543,10 +1543,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-middleware": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz",
|
||||
"integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==",
|
||||
"version": "2.0.9",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
|
||||
"integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/http-proxy": "^1.17.8",
|
||||
"http-proxy": "^1.18.1",
|
||||
@@ -3359,10 +3360,11 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "4.5.9",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.9.tgz",
|
||||
"integrity": "sha512-qK9W4xjgD3gXbC0NmdNFFnVFLMWSNiR3swj957yutwzzN16xF/E7nmtAyp1rT9hviDroQANjE4HK3H4WqWdFtw==",
|
||||
"version": "4.5.14",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz",
|
||||
"integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.18.10",
|
||||
"postcss": "^8.4.27",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"@sveltejs/vite-plugin-svelte": "^2.1.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"http-proxy-middleware": "^2.0.9",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"svelte": "^4.2.19",
|
||||
@@ -26,7 +26,7 @@
|
||||
"svelte-preprocess": "^5.0.3",
|
||||
"svelte-qrcode": "^1.0.0",
|
||||
"tailwindcss": "^3.3.1",
|
||||
"vite": "^4.5.9"
|
||||
"vite": "^4.5.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"cssnano": "^5.1.15",
|
||||
|
||||
@@ -680,7 +680,7 @@ void AmsWebServer::dayplotJson() {
|
||||
} else {
|
||||
uint16_t pos = snprintf_P(buf, BufferSize, PSTR("{\"unit\":\"kwh\""));
|
||||
for(uint8_t i = 0; i < 24; i++) {
|
||||
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR(",\"i%02d\":%.2f,\"e%02d\":%.2f"), i, ds->getHourImport(i) / 1000.0, i, ds->getHourExport(i) / 1000.0);
|
||||
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR(",\"i%02d\":%.3f,\"e%02d\":%.3f"), i, ds->getHourImport(i) / 1000.0, i, ds->getHourExport(i) / 1000.0);
|
||||
}
|
||||
snprintf_P(buf+pos, BufferSize-pos, PSTR("}"));
|
||||
|
||||
@@ -703,7 +703,7 @@ void AmsWebServer::monthplotJson() {
|
||||
} else {
|
||||
uint16_t pos = snprintf_P(buf, BufferSize, PSTR("{\"unit\":\"kwh\""));
|
||||
for(uint8_t i = 1; i < 32; i++) {
|
||||
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR(",\"i%02d\":%.2f,\"e%02d\":%.2f"), i, ds->getDayImport(i) / 1000.0, i, ds->getDayExport(i) / 1000.0);
|
||||
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR(",\"i%02d\":%.3f,\"e%02d\":%.3f"), i, ds->getDayImport(i) / 1000.0, i, ds->getDayExport(i) / 1000.0);
|
||||
}
|
||||
snprintf_P(buf+pos, BufferSize-pos, PSTR("}"));
|
||||
|
||||
@@ -2283,6 +2283,14 @@ void AmsWebServer::configFileDownload() {
|
||||
if(meter.encryptionKey[0] != 0x00) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterEncryptionKey %s\n"), toHex(meter.encryptionKey, 16).c_str()));
|
||||
if(meter.authenticationKey[0] != 0x00) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterAuthenticationKey %s\n"), toHex(meter.authenticationKey, 16).c_str()));
|
||||
}
|
||||
if(meter.wattageMultiplier != 1.0 && meter.wattageMultiplier != 0.0)
|
||||
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterWattageMultiplier %.3f\n"), meter.wattageMultiplier / 1000.0));
|
||||
if(meter.voltageMultiplier != 1.0 && meter.voltageMultiplier != 0.0)
|
||||
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterVoltageMultiplier %.3f\n"), meter.voltageMultiplier / 1000.0));
|
||||
if(meter.amperageMultiplier != 1.0 && meter.amperageMultiplier != 0.0)
|
||||
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterAmperageMultiplier %.3f\n"), meter.amperageMultiplier / 1000.0));
|
||||
if(meter.accumulatedMultiplier != 1.0 && meter.accumulatedMultiplier != 0.0)
|
||||
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterAccumulatedMultiplier %.3f\n"), meter.accumulatedMultiplier / 1000.0));
|
||||
}
|
||||
|
||||
if(includeGpio) {
|
||||
|
||||
@@ -977,6 +977,7 @@ void handleNtpChange() {
|
||||
ws.setTimezone(tz);
|
||||
ds.setTimezone(tz);
|
||||
ea.setTimezone(tz);
|
||||
ps->setTimezone(tz);
|
||||
}
|
||||
|
||||
config.ackNtpChange();
|
||||
@@ -1718,6 +1719,18 @@ void configFileParse() {
|
||||
} else if(strncmp_P(buf, PSTR("meterAuthenticationKey "), 23) == 0) {
|
||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||
fromHex(meter.authenticationKey, String(buf+23), 16);
|
||||
} else if(strncmp_P(buf, PSTR("meterWattageMultiplier "), 23) == 0) {
|
||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||
meter.wattageMultiplier = String(buf+23).toDouble() * 1000;
|
||||
} else if(strncmp_P(buf, PSTR("meterVoltageMultiplier "), 23) == 0) {
|
||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||
meter.voltageMultiplier = String(buf+23).toDouble() * 1000;
|
||||
} else if(strncmp_P(buf, PSTR("meterAmperageMultiplier "), 24) == 0) {
|
||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||
meter.amperageMultiplier = String(buf+24).toDouble() * 1000;
|
||||
} else if(strncmp_P(buf, PSTR("meterAccumulatedMultiplier "), 27) == 0) {
|
||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||
meter.accumulatedMultiplier = String(buf+27).toDouble() * 1000;
|
||||
} else if(strncmp_P(buf, PSTR("gpioHanPin "), 11) == 0) {
|
||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||
meter.rxPin = String(buf+11).toInt();
|
||||
|
||||
Reference in New Issue
Block a user