Compare commits

..

13 Commits

Author SHA1 Message Date
Gunnar Skjold
9fd383c1ef Updated release title (#973) 2025-06-05 08:05:50 +02:00
dependabot[bot]
a931f4cef8 Bump vite from 4.5.9 to 4.5.14 in /lib/SvelteUi/app (#972)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.5.9 to 4.5.14.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v4.5.14/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.14/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 4.5.14
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-05 07:42:29 +02:00
Gunnar Skjold
fddecaab39 Added meter multipliers to config file (#971) 2025-06-05 07:42:12 +02:00
Gunnar Skjold
e5eab82d68 Use list type 4 when phase power is included in Slovenian format (#967) 2025-06-05 07:41:50 +02:00
Gunnar Skjold
8ae1d46b2a Fixed DSMR timestamp parsing (#966)
* Fixed DSMR parsing and added support for more formats

* Add current time as package timestamp for DSMR
2025-06-05 07:41:36 +02:00
Gunnar Skjold
99ccb03b45 Added per phase power for L&G (#965)
* Added phase power parsing for Austrian L&G

* Use list type 4 when L&G phase power is present
2025-06-05 07:41:19 +02:00
Gunnar Skjold
b8f2d501a5 Increased kwh resultion in plot json (#961) 2025-06-05 07:40:57 +02:00
Gunnar Skjold
e042806619 Fixed multiplier problem for some L&G meters (#960) 2025-06-05 07:40:44 +02:00
Gunnar Skjold
16f9ed7ecb Fixed hour skew on price modifier (#959)
* Use local timezone in price config

* Additional changes for previous commit

* Use CET/CEST for energy prices collected from server
2025-06-05 07:40:29 +02:00
Gunnar Skjold
3eaefefd26 Fixed price modifier date inclusion (#958)
* Support price config end before start

* More changes to fix price modifiers

* More changes to fix price modifiers
2025-06-05 07:40:09 +02:00
Gunnar Skjold
03c8c3ddbc Fixed release name (#953) 2025-06-05 07:39:36 +02:00
dependabot[bot]
08371b9078 Bump http-proxy-middleware from 2.0.7 to 2.0.9 in /lib/SvelteUi/app (#950)
Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.7 to 2.0.9.
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.9/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.7...v2.0.9)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-version: 2.0.9
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-05 07:39:14 +02:00
Gunnar Skjold
a6ae25f3e7 Changed way of creating release (#944) 2025-03-24 10:51:07 +01:00
10 changed files with 227 additions and 91 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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