Compare commits

...

22 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
dependabot[bot]
8051db6a9b Bump esbuild from 0.18.20 to 0.25.1 in /lib/SvelteUi/app (#943)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.18.20 to 0.25.1.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2023.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.18.20...v0.25.1)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-24 09:10:54 +01:00
Gunnar Skjold
d0bfdae5d8 Automatic release notes (#942) 2025-03-24 09:05:02 +01:00
Gunnar Skjold
4d681ed2e2 Support fw upgrade via MQTT with JSON payload (#941) 2025-03-24 09:00:05 +01:00
Gunnar Skjold
8ee3f53714 Fixed firmare upload when web context is defined (#938) 2025-03-24 08:59:52 +01:00
Gunnar Skjold
e8cf8a98ed Fixed baud/parity autodetect (#937) 2025-03-24 08:59:40 +01:00
Gunnar Skjold
9153a98694 Fix: #935 - Brownout reboot loop (#936) 2025-03-24 08:59:30 +01:00
Gunnar Skjold
37aa6ae816 Fix: #918 - MQTT/SSL does not reconnect after disconnect (#933) 2025-03-24 08:59:18 +01:00
Gunnar Skjold
8a35346fcf Fixed backup and restore of price modifiers (#925) (#932)
* Fix: #925 - Backup/restore of price modifiers

* Removed unused importy
2025-03-24 08:58:31 +01:00
Gunnar Skjold
792ae4c935 Fix: #926 - Historical data lost when upgrading ESP8266 between 2.4.x versions (#931) 2025-03-24 08:58:14 +01:00
24 changed files with 776 additions and 424 deletions

View File

@@ -67,16 +67,12 @@ jobs:
- name: PlatformIO lib install
run: pio lib install
- name: Create Release
- name: Create release with release notes
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: ncipollo/release-action@v1
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
name: Release v${{ steps.release_tag.outputs.tag }}
generateReleaseNotes: true
- name: Build esp8266 firmware
run: pio run -e esp8266

View File

@@ -283,6 +283,8 @@ public:
bool getWebConfig(WebConfig&);
bool setWebConfig(WebConfig&);
void clearWebConfig(WebConfig&);
bool isWebChanged();
void ackWebChange();
bool getMeterConfig(MeterConfig&);
bool setMeterConfig(MeterConfig&);
@@ -353,7 +355,7 @@ protected:
private:
uint8_t configVersion = 0;
bool sysChanged = false, networkChanged, mqttChanged, meterChanged = true, ntpChanged = true, priceChanged = false, energyAccountingChanged = true, cloudChanged = true, uiLanguageChanged = false;
bool sysChanged = false, networkChanged = false, mqttChanged = false, webChanged = false, meterChanged = true, ntpChanged = true, priceChanged = false, energyAccountingChanged = true, cloudChanged = true, uiLanguageChanged = false;
bool relocateConfig103(); // 2.2.12, until, but not including 2.3

View File

@@ -242,6 +242,14 @@ bool AmsConfiguration::getWebConfig(WebConfig& config) {
}
bool AmsConfiguration::setWebConfig(WebConfig& config) {
WebConfig existing;
if(getWebConfig(existing)) {
webChanged |= strcmp(config.username, existing.username) != 0;
webChanged |= strcmp(config.password, existing.password) != 0;
webChanged |= strcmp(config.context, existing.context) != 0;
} else {
webChanged = true;
}
stripNonAscii((uint8_t*) config.username, 37);
stripNonAscii((uint8_t*) config.password, 37);
@@ -261,6 +269,14 @@ void AmsConfiguration::clearWebConfig(WebConfig& config) {
memset(config.context, 0, 37);
}
bool AmsConfiguration::isWebChanged() {
return webChanged;
}
void AmsConfiguration::ackWebChange() {
webChanged = false;
}
bool AmsConfiguration::getMeterConfig(MeterConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);

View File

@@ -17,6 +17,9 @@
#define AMS_PARTITION_MIN_SPIFFS_SIZE 0x20000
#elif defined(ESP8266)
#include <ESP8266HTTPClient.h>
#define AMS_FLASH_SKETCH_SIZE 0xFEFF0
#define AMS_FLASH_OTA_START AMS_FLASH_OTA_SIZE
#endif
#if defined(AMS_REMOTE_DEBUG)

View File

@@ -97,6 +97,11 @@ float AmsFirmwareUpdater::getProgress() {
}
void AmsFirmwareUpdater::loop() {
if(millis() < 30000) {
// Wait 30 seconds before starting upgrade. This allows the device to deal with other tasks first
// It will also allow BUS powered devices to reach a stable voltage so that hw->isVoltageOptimal will behave properly
return;
}
if(strlen(updateStatus.toVersion) > 0 && updateStatus.errorCode == AMS_UPDATE_ERR_OK) {
if(!hw->isVoltageOptimal(0.1)) {
writeUpdateStatus();
@@ -1128,7 +1133,7 @@ bool AmsFirmwareUpdater::moveLittleFsFromApp1ToNew() {
}
#elif defined(ESP8266)
uintptr_t AmsFirmwareUpdater::getFirmwareUpdateStart() {
return FS_start - 0x40200000;
return (AMS_FLASH_SKETCH_SIZE + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
}
bool AmsFirmwareUpdater::isFlashReadyForNextUpdateVersion(uint32_t size) {
@@ -1137,6 +1142,14 @@ bool AmsFirmwareUpdater::isFlashReadyForNextUpdateVersion(uint32_t size) {
#endif
debugger->printf_P(PSTR("Checking if we can upgrade\n"));
if(FS_PHYS_ADDR < (getFirmwareUpdateStart() + AMS_FLASH_SKETCH_SIZE)) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("No room for OTA update\n"));
return false;
}
if(!ESP.checkFlashConfig(false)) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
@@ -1145,24 +1158,12 @@ bool AmsFirmwareUpdater::isFlashReadyForNextUpdateVersion(uint32_t size) {
return false;
}
//size of current sketch rounded to a sector
size_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
//size of the update rounded to a sector
size_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
//address of the end of the space available for sketch and update
uintptr_t updateEndAddress = FS_start - 0x40200000;
uintptr_t updateStartAddress = (updateEndAddress > roundedSize) ? (updateEndAddress - roundedSize) : 0;
//make sure that the size of both sketches is less than the total space (updateEndAddress)
if(updateStartAddress < currentSketchSize) {
if(size > AMS_FLASH_SKETCH_SIZE) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("New firmware does not fit flash\n"));
return false;
return false;
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
@@ -1180,14 +1181,28 @@ bool AmsFirmwareUpdater::writeBufferToFlash() {
uint32_t offset = updateStatus.block_position * UPDATE_BUF_SIZE;
uintptr_t currentAddress = getFirmwareUpdateStart() + offset;
uint32_t sector = currentAddress/FLASH_SECTOR_SIZE;
if(!ESP.flashEraseSector(sector)) {
if (currentAddress % FLASH_SECTOR_SIZE == 0) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("flashEraseSector(%lu) failed\n"), sector);
updateStatus.errorCode = AMS_UPDATE_ERR_ERASE;
return false;
debugger->printf_P(PSTR("flashEraseSector(%lu)\n"), sector);
yield();
if(!ESP.flashEraseSector(sector)) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("flashEraseSector(%lu) failed\n"), sector);
updateStatus.errorCode = AMS_UPDATE_ERR_ERASE;
return false;
}
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("flashWrite(%lu)\n"), sector);
yield();
if(!ESP.flashWrite(currentAddress, buf, UPDATE_BUF_SIZE)) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))

View File

@@ -33,15 +33,18 @@ bool AmsMqttHandler::connect() {
if(epoch < FirmwareVersion::BuildEpoch) {
return false;
}
bool applySslConfiguration = mqttConfigChanged;
if(mqttSecureClient == NULL) {
mqttSecureClient = new WiFiClientSecure();
#if defined(ESP8266)
mqttSecureClient->setBufferSizes(512, 512);
return false;
#endif
applySslConfiguration = true;
}
if(mqttConfigChanged) {
if(applySslConfiguration) {
if(caVerification && LittleFS.begin()) {
File file;

View File

@@ -67,7 +67,7 @@ private:
bool ledInvert, rgbInvert;
uint8_t vccPin, vccGnd_r, vccVcc_r;
float vccOffset, vccMultiplier;
float maxVcc = 3.2; // Best to have this close to max as a start, in case Pow-U reboots and starts off with a low voltage, we dont want that to be perceived as max
float maxVcc = 3.25; // Best to have this close to max as a start, in case Pow-U reboots and starts off with a low voltage, we dont want that to be perceived as max
uint16_t analogRange = 1024;
AdcConfig voltAdc, tempAdc;

View File

@@ -25,6 +25,7 @@ public:
bool publishPrices(PriceService*);
bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea);
bool publishRaw(String data);
bool publishFirmware();
void onMessage(String &topic, String &payload);

View File

@@ -432,5 +432,26 @@ bool JsonMqttHandler::publishRaw(String data) {
return false;
}
void JsonMqttHandler::onMessage(String &topic, String &payload) {
bool JsonMqttHandler::publishFirmware() {
snprintf_P(json, BufferSize, PSTR("{\"installed_version\":\"%s\",\"latest_version\":\"%s\",\"title\":\"amsreader firmware\",\"release_url\":\"https://github.com/UtilitechAS/amsreader-firmware/releases\",\"release_summary\":\"New version %s is available\",\"update_percentage\":%s}"),
FirmwareVersion::VersionString,
strlen(updater->getNextVersion()) == 0 ? FirmwareVersion::VersionString : updater->getNextVersion(),
strlen(updater->getNextVersion()) == 0 ? FirmwareVersion::VersionString : updater->getNextVersion(),
updater->getProgress() < 0 ? "null" : String(updater->getProgress(), 0)
);
char topic[192];
snprintf_P(topic, 192, PSTR("%s/firmware"), mqttConfig.publishTopic);
bool ret = mqtt.publish(topic, json);
loop();
return ret;
}
void JsonMqttHandler::onMessage(String &topic, String &payload) {
if(strncmp(topic.c_str(), mqttConfig.subscribeTopic, 12) == 0) {
if(payload.equals("fwupgrade")) {
if(strcmp(updater->getNextVersion(), FirmwareVersion::VersionString) != 0) {
updater->setTargetVersion(updater->getNextVersion());
}
}
}
}

View File

@@ -66,7 +66,7 @@ protected:
unsigned long meterAutodetectLastChange = 0;
long rate = 10000;
uint32_t autodetectBaud = 0;
uint8_t autodetectParity = 11;
uint8_t autodetectParity = 11; // 8E1
bool autodetectInvert = false;
uint8_t autodetectCount = 0;
@@ -91,6 +91,7 @@ protected:
int16_t unwrapData(uint8_t *buf, DataParserContext &context);
void printHanReadError(int pos);
void handleAutodetect(unsigned long now);
uint8_t getNextParity(uint8_t parityOrdinal);
};
#endif

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

@@ -234,20 +234,20 @@ AmsData* PassiveMeterCommunicator::getData(AmsData& meterState) {
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("Using application data:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("Using application data:\n"));
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugPrint((byte*) payload, 0, ctx.length, debugger);
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugPrint((byte*) payload, 0, ctx.length, debugger);
// Rudimentary detector for L&G proprietary format, this is terrible code... Fix later
if(payload[0] == CosemTypeStructure && payload[2] == CosemTypeArray && payload[1] == payload[3]) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("LNG\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("LNG\n"));
LNG lngData = LNG(meterState, payload, meterState.getMeterType(), &meterConfig, ctx);
if(lngData.getListType() >= 1) {
data = new AmsData();
@@ -263,9 +263,9 @@ debugger->printf_P(PSTR("LNG\n"));
payload[17] == CosemTypeLongUnsigned
) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("LNG2\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("LNG2\n"));
LNG2 lngData = LNG2(meterState, payload, meterState.getMeterType(), &meterConfig, ctx);
if(lngData.getListType() >= 1) {
data = new AmsData();
@@ -274,9 +274,9 @@ debugger->printf_P(PSTR("LNG2\n"));
}
} else {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("DLMS\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("DLMS\n"));
// TODO: Split IEC6205675 into DataParserKaifa and DataParserObis. This way we can add other means of parsing, for those other proprietary formats
data = new IEC6205675(payload, meterState.getMeterType(), &meterConfig, ctx, meterState);
}
@@ -299,9 +299,9 @@ int PassiveMeterCommunicator::getLastError() {
if(hwSerial != NULL) {
if(hwSerial->hasRxError()) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Serial RX error\n"));
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Serial RX error\n"));
lastError = 96;
}
if(hwSerial->hasOverrun()) {
@@ -505,75 +505,75 @@ int16_t PassiveMeterCommunicator::unwrapData(uint8_t *buf, DataParserContext &co
void PassiveMeterCommunicator::printHanReadError(int pos) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
{
if (debugger->isActive(RemoteDebug::WARNING))
#endif
{
switch(pos) {
case DATA_PARSE_BOUNDRY_FLAG_MISSING:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Boundry flag missing\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Boundry flag missing\n"));
break;
case DATA_PARSE_HEADER_CHECKSUM_ERROR:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Header checksum error\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Header checksum error\n"));
break;
case DATA_PARSE_FOOTER_CHECKSUM_ERROR:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Frame checksum error\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Frame checksum error\n"));
break;
case DATA_PARSE_INCOMPLETE:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Received frame is incomplete\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Received frame is incomplete\n"));
break;
case GCM_AUTH_FAILED:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Decrypt authentication failed\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Decrypt authentication failed\n"));
break;
case GCM_ENCRYPTION_KEY_FAILED:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Setting decryption key failed\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Setting decryption key failed\n"));
break;
case GCM_DECRYPT_FAILED:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Decryption failed\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Decryption failed\n"));
break;
case MBUS_FRAME_LENGTH_NOT_EQUAL:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Frame length mismatch\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Frame length mismatch\n"));
break;
case DATA_PARSE_INTERMEDIATE_SEGMENT:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Intermediate segment received\n"));
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Intermediate segment received\n"));
break;
case DATA_PARSE_UNKNOWN_DATA:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Unknown data format %02X\n"), hanBuffer[0]);
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Unknown data format %02X\n"), hanBuffer[0]);
break;
default:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Unspecified error while reading data: %d\n"), pos);
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Unspecified error while reading data: %d\n"), pos);
}
}
}
@@ -587,9 +587,9 @@ void PassiveMeterCommunicator::setupHanPort(uint32_t baud, uint8_t parityOrdinal
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("(setupHanPort) Setting up HAN on pin %d/%d with baud %d and parity %d\n"), rxpin, txpin, baud, parityOrdinal);
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("(setupHanPort) Setting up HAN on pin %d/%d with baud %d and parity %d\n"), rxpin, txpin, baud, parityOrdinal);
if(parityOrdinal == 0) {
parityOrdinal = 3; // 8N1
@@ -617,9 +617,9 @@ debugger->printf_P(PSTR("(setupHanPort) Setting up HAN on pin %d/%d with baud %d
if(rxpin == 0) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Invalid GPIO configured for HAN\n"));
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Invalid GPIO configured for HAN\n"));
return;
}
@@ -628,9 +628,9 @@ debugger->printf_P(PSTR("Invalid GPIO configured for HAN\n"));
if(hwSerial != NULL) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Hardware serial\n"));
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Hardware serial\n"));
Serial.flush();
#if defined(ESP8266)
SerialConfig serialConfig;
@@ -667,15 +667,15 @@ debugger->printf_P(PSTR("Hardware serial\n"));
#if defined(ESP8266)
if(rxpin == 3) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Switching UART0 to pin 1 & 3\n"));
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Switching UART0 to pin 1 & 3\n"));
Serial.pins(1,3);
} else if(rxpin == 113) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Switching UART0 to pin 15 & 13\n"));
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Switching UART0 to pin 15 & 13\n"));
Serial.pins(15,13);
}
#endif
@@ -708,9 +708,9 @@ debugger->printf_P(PSTR("Switching UART0 to pin 15 & 13\n"));
} else {
#if defined(ESP8266)
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Software serial\n"));
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Software serial\n"));
Serial.flush();
if(swSerial == NULL) {
@@ -743,17 +743,17 @@ debugger->printf_P(PSTR("Software serial\n"));
if(bufferSize > 2) bufferSize = 2;
#endif
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Using serial buffer size %d\n"), 64 * bufferSize);
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Using serial buffer size %d\n"), 64 * bufferSize);
swSerial->begin(baud, serialConfig, rxpin, txpin, invert, meterConfig.bufferSize * 64, meterConfig.bufferSize * 64);
hanSerial = swSerial;
hwSerial = NULL;
#else
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Software serial not available\n"));
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Software serial not available\n"));
return;
#endif
}
@@ -767,9 +767,9 @@ debugger->printf_P(PSTR("Software serial not available\n"));
// The library automatically sets the pullup in Serial.begin()
if(!meterConfig.rxPinPullup) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("HAN pin pullup disabled\n"));
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("HAN pin pullup disabled\n"));
pinMode(meterConfig.rxPin, INPUT);
}
@@ -802,75 +802,44 @@ void PassiveMeterCommunicator::rxerr(int err) {
switch(err) {
case 2:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Serial buffer overflow\n"));
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Serial buffer overflow\n"));
rxBufferErrors++;
if(rxBufferErrors > 1 && meterConfig.bufferSize < 8) {
meterConfig.bufferSize += 2;
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Increasing RX buffer to %d bytes\n"), meterConfig.bufferSize * 64);
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Increasing RX buffer to %d bytes\n"), meterConfig.bufferSize * 64);
configChanged = true;
rxBufferErrors = 0;
}
break;
case 3:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Serial FIFO overflow\n"));
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Serial FIFO overflow\n"));
break;
case 4:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Serial frame error\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Serial frame error\n"));
break;
case 5:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Serial parity error\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Serial parity error\n"));
unsigned long now = millis();
if(now - meterAutodetectLastChange < 120000) {
switch(autodetectParity) {
case 2: // 7N1
autodetectParity = 10;
break;
case 10: // 7E1
autodetectParity = 6;
break;
case 6: // 7N2
autodetectParity = 14;
break;
case 14: // 7E2
autodetectParity = 2;
break;
case 3: // 8N1
autodetectParity = 11;
break;
case 11: // 8E1
autodetectParity = 7;
break;
case 7: // 8N2
autodetectParity = 15;
break;
case 15: // 8E2
autodetectParity = 3;
break;
default:
autodetectParity = 3;
break;
}
if(validDataReceived) {
meterConfig.parity = autodetectParity;
configChanged = true;
setupHanPort(meterConfig.baud, meterConfig.parity, meterConfig.invert);
}
if(autodetect) {
meterAutodetectLastChange = 0;
} else if(meterAutodetectLastChange > 0 && now - meterAutodetectLastChange < 120000 && validDataReceived) {
meterConfig.parity = getNextParity(meterConfig.parity);
configChanged = true;
setupHanPort(meterConfig.baud, meterConfig.parity, meterConfig.invert);
}
break;
}
@@ -882,26 +851,31 @@ void PassiveMeterCommunicator::handleAutodetect(unsigned long now) {
if(!autodetect) return;
if(!validDataReceived) {
if(now - meterAutodetectLastChange > 20000 && (meterConfig.baud == 0 || meterConfig.parity == 0)) {
if(now - meterAutodetectLastChange > 12000 && (meterConfig.baud == 0 || meterConfig.parity == 0)) {
autodetect = true;
if(autodetectCount == 2) {
if(lastError == 95) { // If parity error, switch parity
autodetectParity = getNextParity(autodetectParity);
} else {
autodetectCount++;
}
if(autodetectCount == sizeof(AUTO_BAUD_RATES)/sizeof(AUTO_BAUD_RATES[0])) {
autodetectInvert = !autodetectInvert;
autodetectCount = 0;
}
autodetectBaud = AUTO_BAUD_RATES[autodetectCount++];
autodetectBaud = AUTO_BAUD_RATES[autodetectCount];
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Meter serial autodetect, swapping to: %d, %d, %s\n"), autodetectBaud, autodetectParity, autodetectInvert ? "true" : "false");
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Meter serial autodetect, swapping to: %d, %d, %s\n"), autodetectBaud, autodetectParity, autodetectInvert ? "true" : "false");
meterConfig.bufferSize = max((uint32_t) 1, autodetectBaud / 14400);
setupHanPort(autodetectBaud, autodetectParity, autodetectInvert);
meterAutodetectLastChange = now;
}
} else if(autodetect) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Meter serial autodetected, saving: %d, %d, %s\n"), autodetectBaud, autodetectParity, autodetectInvert ? "true" : "false");
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Meter serial autodetected, saving: %d, %d, %s\n"), autodetectBaud, autodetectParity, autodetectInvert ? "true" : "false");
autodetect = false;
meterConfig.baud = autodetectBaud;
meterConfig.parity = autodetectParity;
@@ -910,3 +884,18 @@ debugger->printf_P(PSTR("Meter serial autodetected, saving: %d, %d, %s\n"), auto
setupHanPort(meterConfig.baud, meterConfig.parity, meterConfig.invert);
}
}
uint8_t PassiveMeterCommunicator::getNextParity(uint8_t parityOrdinal) {
switch(parityOrdinal) {
case 10: // 7E1
return 2; // 7N1
case 14: // 7E2
return 6; // 7N2
case 11: // 8E1
return 3; // 8N1
case 15: // 8E2
return 7; // 8N2
}
return 3;
}

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;
@@ -336,17 +328,17 @@ bool PriceService::retrieve(const char* url, Stream* doc) {
nextFetchDelayMinutes = 2;
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("(PriceService) Communication error, returned status: %d\n"), status);
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("(PriceService) Communication error, returned status: %d\n"), status);
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf(http->errorToString(status).c_str());
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf(http->errorToString(status).c_str());
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf(http->getString().c_str());
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf(http->getString().c_str());
http->end();
return false;
@@ -393,18 +385,18 @@ float PriceService::getCurrencyMultiplier(const char* from, const char* to, time
}
if(currencyMultiplier != 0) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("(PriceService) Resulting currency multiplier: %.4f\n"), currencyMultiplier);
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("(PriceService) Resulting currency multiplier: %.4f\n"), currencyMultiplier);
tmElements_t tm;
breakTime(t, tm);
lastCurrencyFetch = now + (SECS_PER_DAY * 1000) - (((((tm.Hour * 60) + tm.Minute) * 60) + tm.Second) * 1000) + (3600000 * 6) + (tomorrowFetchMinute * 60);
this->currencyMultiplier = currencyMultiplier;
} else {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("(PriceService) Multiplier ended in success, but without value\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("(PriceService) Multiplier ended in success, but without value\n"));
lastCurrencyFetch = now + (SECS_PER_HOUR * 1000);
if(this->currencyMultiplier == 1) return 0;
}
@@ -415,7 +407,7 @@ debugger->printf_P(PSTR("(PriceService) Multiplier ended in success, but without
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;
@@ -435,13 +427,13 @@ PricesContainer* PriceService::fetchPrices(time_t t) {
#endif
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("(PriceService) Fetching prices for %02d.%02d.%04d\n"), tm.Day, tm.Month, tm.Year+1970);
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("(PriceService) Fetching prices for %02d.%02d.%04d\n"), tm.Day, tm.Month, tm.Year+1970);
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("(PriceService) url: %s\n"), buf);
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("(PriceService) url: %s\n"), buf);
EntsoeA44Parser a44;
if(retrieve(buf, &a44) && a44.getPoint(0) != PRICE_NO_VALUE) {
PricesContainer* ret = new PricesContainer();
@@ -452,7 +444,7 @@ debugger->printf_P(PSTR("(PriceService) url: %s\n"), buf);
}
} 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"),
@@ -463,13 +455,13 @@ debugger->printf_P(PSTR("(PriceService) url: %s\n"), buf);
config->currency
);
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("(PriceService) Fetching prices for %02d.%02d.%04d\n"), tm.Day, tm.Month, tm.Year+1970);
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("(PriceService) Fetching prices for %02d.%02d.%04d\n"), tm.Day, tm.Month, tm.Year+1970);
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("(PriceService) url: %s\n"), buf);
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("(PriceService) url: %s\n"), buf);
#if defined(ESP8266)
WiFiClient client;
client.setTimeout(5000);
@@ -511,9 +503,9 @@ debugger->printf_P(PSTR("(PriceService) url: %s\n"), buf);
lastError = gcmRet;
nextFetchDelayMinutes = 60;
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("(PriceService) Error code while decrypting prices: %d\n"), gcmRet);
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("(PriceService) Error code while decrypting prices: %d\n"), gcmRet);
}
} else {
lastError = status;
@@ -525,20 +517,20 @@ debugger->printf_P(PSTR("(PriceService) Error code while decrypting prices: %d\n
nextFetchDelayMinutes = 5;
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("(PriceService) Communication error, returned status: %d\n"), status);
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("(PriceService) Communication error, returned status: %d\n"), status);
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
{
if (debugger->isActive(RemoteDebug::ERROR))
#endif
{
debugger->printf(http->errorToString(status).c_str());
debugger->println();
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf(http->getString().c_str());
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf(http->getString().c_str());
http->end();
}
@@ -575,16 +567,16 @@ void PriceService::cropPriceConfig(uint8_t size) {
bool PriceService::save() {
if(!LittleFS.begin()) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("(PriceService) Unable to load LittleFS\n"));
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("(PriceService) Unable to load LittleFS\n"));
return false;
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("(PriceService) Saving price config\n"));
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("(PriceService) Saving price config\n"));
PriceConfig pc;
File file = LittleFS.open(FILE_PRICE_CONF, "w");
@@ -607,18 +599,18 @@ debugger->printf_P(PSTR("(PriceService) Saving price config\n"));
bool PriceService::load() {
if(!LittleFS.begin()) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("(PriceService) Unable to load LittleFS\n"));
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("(PriceService) Unable to load LittleFS\n"));
return false;
}
if(!LittleFS.exists(FILE_PRICE_CONF)) {
return false;
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("(PriceService) Loading price config\n"));
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("(PriceService) Loading price config\n"));
this->priceConfig.clear();
@@ -632,4 +624,38 @@ debugger->printf_P(PSTR("(PriceService) Loading price config\n"));
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

@@ -7,6 +7,7 @@
#include "RawMqttHandler.h"
#include "hexutils.h"
#include "Uptime.h"
#include "FirmwareVersion.h"
bool RawMqttHandler::publish(AmsData* update, AmsData* previousState, EnergyAccounting* ea, PriceService* ps) {
if(topic.isEmpty() || !mqtt.connected())
@@ -382,4 +383,11 @@ bool RawMqttHandler::publishRaw(String data) {
}
void RawMqttHandler::onMessage(String &topic, String &payload) {
if(strncmp(topic.c_str(), mqttConfig.subscribeTopic, 12) == 0) {
if(payload.equals("fwupgrade")) {
if(strcmp(updater->getNextVersion(), FirmwareVersion::VersionString) != 0) {
updater->setTargetVersion(updater->getNextVersion());
}
}
}
}

File diff suppressed because one or more lines are too long

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": {
@@ -52,12 +52,13 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
"integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz",
"integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"aix"
@@ -67,12 +68,13 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
"integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz",
"integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"android"
@@ -82,12 +84,13 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
"integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz",
"integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
@@ -97,12 +100,13 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
"integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz",
"integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
@@ -112,12 +116,13 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
"integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz",
"integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -127,12 +132,13 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
"integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz",
"integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -142,12 +148,13 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
"integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz",
"integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
@@ -157,12 +164,13 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
"integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz",
"integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
@@ -172,12 +180,13 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
"integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz",
"integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -187,12 +196,13 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
"integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz",
"integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -202,12 +212,13 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
"integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz",
"integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -217,12 +228,13 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
"integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz",
"integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==",
"cpu": [
"loong64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -232,12 +244,13 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
"integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz",
"integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==",
"cpu": [
"mips64el"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -247,12 +260,13 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
"integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz",
"integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -262,12 +276,13 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
"integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz",
"integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -277,12 +292,13 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
"integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz",
"integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==",
"cpu": [
"s390x"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -292,12 +308,13 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
"integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz",
"integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -307,12 +324,13 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
"integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz",
"integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"netbsd"
@@ -322,12 +340,13 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
"integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz",
"integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"netbsd"
@@ -337,12 +356,13 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
"integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz",
"integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openbsd"
@@ -352,12 +372,13 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
"integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz",
"integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"openbsd"
@@ -367,12 +388,13 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
"integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz",
"integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"sunos"
@@ -382,12 +404,13 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
"integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz",
"integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
@@ -397,12 +420,13 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
"integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz",
"integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
@@ -412,12 +436,13 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
"integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz",
"integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
@@ -1263,10 +1288,11 @@
"dev": true
},
"node_modules/esbuild": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
"integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
"integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
@@ -1274,31 +1300,31 @@
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.0",
"@esbuild/android-arm": "0.25.0",
"@esbuild/android-arm64": "0.25.0",
"@esbuild/android-x64": "0.25.0",
"@esbuild/darwin-arm64": "0.25.0",
"@esbuild/darwin-x64": "0.25.0",
"@esbuild/freebsd-arm64": "0.25.0",
"@esbuild/freebsd-x64": "0.25.0",
"@esbuild/linux-arm": "0.25.0",
"@esbuild/linux-arm64": "0.25.0",
"@esbuild/linux-ia32": "0.25.0",
"@esbuild/linux-loong64": "0.25.0",
"@esbuild/linux-mips64el": "0.25.0",
"@esbuild/linux-ppc64": "0.25.0",
"@esbuild/linux-riscv64": "0.25.0",
"@esbuild/linux-s390x": "0.25.0",
"@esbuild/linux-x64": "0.25.0",
"@esbuild/netbsd-arm64": "0.25.0",
"@esbuild/netbsd-x64": "0.25.0",
"@esbuild/openbsd-arm64": "0.25.0",
"@esbuild/openbsd-x64": "0.25.0",
"@esbuild/sunos-x64": "0.25.0",
"@esbuild/win32-arm64": "0.25.0",
"@esbuild/win32-ia32": "0.25.0",
"@esbuild/win32-x64": "0.25.0"
"@esbuild/aix-ppc64": "0.25.1",
"@esbuild/android-arm": "0.25.1",
"@esbuild/android-arm64": "0.25.1",
"@esbuild/android-x64": "0.25.1",
"@esbuild/darwin-arm64": "0.25.1",
"@esbuild/darwin-x64": "0.25.1",
"@esbuild/freebsd-arm64": "0.25.1",
"@esbuild/freebsd-x64": "0.25.1",
"@esbuild/linux-arm": "0.25.1",
"@esbuild/linux-arm64": "0.25.1",
"@esbuild/linux-ia32": "0.25.1",
"@esbuild/linux-loong64": "0.25.1",
"@esbuild/linux-mips64el": "0.25.1",
"@esbuild/linux-ppc64": "0.25.1",
"@esbuild/linux-riscv64": "0.25.1",
"@esbuild/linux-s390x": "0.25.1",
"@esbuild/linux-x64": "0.25.1",
"@esbuild/netbsd-arm64": "0.25.1",
"@esbuild/netbsd-x64": "0.25.1",
"@esbuild/openbsd-arm64": "0.25.1",
"@esbuild/openbsd-x64": "0.25.1",
"@esbuild/sunos-x64": "0.25.1",
"@esbuild/win32-arm64": "0.25.1",
"@esbuild/win32-ia32": "0.25.1",
"@esbuild/win32-x64": "0.25.1"
}
},
"node_modules/escalade": {
@@ -1517,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",
@@ -3333,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

@@ -243,7 +243,7 @@
{/if}
{#if sysinfo.security == 0 || data.a}
<div class="my-2 flex">
<form action="/firmware" enctype="multipart/form-data" method="post" on:submit={() => firmwareUploading=true} autocomplete="off">
<form action="firmware" enctype="multipart/form-data" method="post" on:submit={() => firmwareUploading=true} autocomplete="off">
<input style="display:none" name="file" type="file" accept=".bin" bind:this={firmwareFileInput} bind:files={firmwareFiles}>
{#if firmwareFiles.length == 0}
<button type="button" on:click={()=>{firmwareFileInput.click();}} class="btn-pri-sm float-right">{translations.status?.firmware?.btn_select_file ?? "Select file"}</button>
@@ -258,7 +258,7 @@
{#if sysinfo.security == 0 || data.a}
<div class="cnt">
<strong class="text-sm">{translations.status?.backup?.title ?? "Backup"}</strong>
<form method="get" action="/configfile.cfg" autocomplete="off">
<form method="get" action="configfile.cfg" autocomplete="off">
<div class="grid grid-cols-2">
{#each cfgItems as el}
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="{el.key}" value="true" checked/> {translations.status?.backup?.[el.key] ?? el.name}</label>

View File

@@ -89,9 +89,9 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, AmsDa
context = "";
} else {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Using context path: '%s'\n"), context.c_str());
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Using context path: '%s'\n"), context.c_str());
}
}
@@ -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("}"));
@@ -814,7 +814,6 @@ void AmsWebServer::indexHtml() {
}
void AmsWebServer::indexCss() {
if(!checkSecurity(2))
return;
@@ -1662,7 +1661,7 @@ void AmsWebServer::handleSave() {
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Successfully saved.\n"));
if(config->isNetworkConfigChanged() || performRestart) {
if(config->isNetworkConfigChanged() || config->isWebChanged() || performRestart) {
performRestart = true;
} else {
hw->setup(&sys, gpioConfig);
@@ -2284,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) {
@@ -2343,6 +2350,8 @@ void AmsWebServer::configFileDownload() {
case PRICE_DIRECTION_BOTH:
strcpy_P(direction, PSTR("both"));
break;
default:
strcpy_P(direction, PSTR("--"));
}
char type[9] = "";
switch(p.type) {
@@ -2358,11 +2367,38 @@ void AmsWebServer::configFileDownload() {
case PRICE_TYPE_SUBTRACT:
strcpy_P(type, PSTR("subtract"));
break;
default:
strcpy_P(direction, PSTR("--"));
}
char days[3*7] = "";
if(p.days == 0x7F) {
strcpy_P(days, PSTR("all"));
} else {
if((p.days >> 0) & 0x01) strcat_P(days, PSTR("mo,"));
if((p.days >> 1) & 0x01) strcat_P(days, PSTR("tu,"));
if((p.days >> 2) & 0x01) strcat_P(days, PSTR("we,"));
if((p.days >> 3) & 0x01) strcat_P(days, PSTR("th,"));
if((p.days >> 4) & 0x01) strcat_P(days, PSTR("fr,"));
if((p.days >> 5) & 0x01) strcat_P(days, PSTR("sa,"));
if((p.days >> 6) & 0x01) strcat_P(days, PSTR("su,"));
if(strlen(days) > 0) days[strlen(days)-1] = '\0';
}
char days[12] = "";
char hours[12] = "";
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("priceModifier %i %s %s %s %.4f %s %s %02d-%02d %02d-%02d\n"),
char hours[3*24] = "";
if(p.hours == 0xFFFFFF) {
strcpy_P(hours, PSTR("all"));
} else {
for(uint8_t i = 0; i < 24; i++) {
if((p.hours >> i) & 0x01) {
char h[4];
snprintf_P(h, 4, PSTR("%02d,"), i);
strcat(hours, h);
}
}
if(strlen(hours) > 0) hours[strlen(hours)-1] = '\0';
}
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("priceModifier %i \"%s\" %s %s %.4f %s %s %02d-%02d %02d-%02d\n"),
i,
p.name,
direction,

View File

@@ -22,7 +22,7 @@ build_flags =
lib_deps = WiFi, Ethernet, ESPmDNS, WiFiClientSecure, HTTPClient, FS, WebServer, ESP32 Async UDP, ESP32SSDP, mulmer89/ESPRandom@1.5.0, ${common.lib_deps}, CloudConnector, SvelteUi
[env:esp8266]
platform = espressif8266@4.2.0
platform = espressif8266@4.2.1
framework = arduino
board = esp12e
board_build.ldscript = eagle.flash.4m2m.ld

View File

@@ -977,6 +977,7 @@ void handleNtpChange() {
ws.setTimezone(tz);
ds.setTimezone(tz);
ea.setTimezone(tz);
ps->setTimezone(tz);
}
config.ackNtpChange();
@@ -1550,6 +1551,22 @@ void MQTT_connect() {
}
}
String getSplit(String input, int index) {
char separator = ' ';
String ret = input;
ret.trim();
for(int i = 0; i < index; i++) {
int pos = ret.indexOf(separator);
if(pos == -1) {
return "";
}
ret = ret.substring(pos+1);
}
ret = ret.substring(0, ret.indexOf(separator));
ret.trim();
return ret;
}
void configFileParse() {
debugD_P(PSTR("Parsing config file"));
@@ -1587,6 +1604,7 @@ void configFileParse() {
NtpConfig ntp;
PriceServiceConfig price;
EnergyAccountingConfig eac;
uint8_t priceModifierCount = 0;
size_t size;
char* buf = (char*) commonBuffer;
@@ -1701,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();
@@ -1821,6 +1851,92 @@ void configFileParse() {
} else if(strncmp_P(buf, PSTR("priceFixedPrice "), 16) == 0) {
if(!lPrice) { config.getPriceServiceConfig(price); lPrice = true; };
price.unused2 = String(buf+16).toFloat() * 1000;
} else if(strncmp_P(buf, PSTR("priceModifier "), 14) == 0) {
PriceConfig pc;
memset(&pc, 0, sizeof(PriceConfig));
String line = String(buf+14);
uint8_t priceIndex = line.substring(0, line.indexOf(" ")).toInt();
int nameStart = line.indexOf("\"");
if(nameStart < 0) {
debugW_P(PSTR("Price modifier without name start"));
continue;
}
int nameEnd = line.indexOf("\"", nameStart+1);
if(nameEnd < nameStart) {
debugW_P(PSTR("Price modifier without name end"));
continue;
}
String name = line.substring(nameStart+1, nameEnd);
strcpy(pc.name, name.c_str());
String rest = line.substring(nameEnd+1);
String direction = getSplit(rest, 0);
if(direction.equals("import")) {
pc.direction = PRICE_DIRECTION_IMPORT;
} else if(direction.equals("export")) {
pc.direction = PRICE_DIRECTION_EXPORT;
} else if(direction.equals("both")) {
pc.direction = PRICE_DIRECTION_BOTH;
} else {
debugW_P(PSTR("Price modifier with unknown direction \"%s\""), direction.c_str());
continue;
}
String type = getSplit(rest, 1);
if(type.equals("fixed")) {
pc.type = PRICE_TYPE_FIXED;
} else if(type.equals("add")) {
pc.type = PRICE_TYPE_ADD;
} else if(type.equals("percent")) {
pc.type = PRICE_TYPE_PCT;
} else if(type.equals("subtract")) {
pc.type = PRICE_TYPE_SUBTRACT;
} else {
debugW_P(PSTR("Price modifier unknown type"));
continue;
}
pc.value = getSplit(rest, 2).toFloat() * 10000;
String days = getSplit(rest, 3);
if(days.equals("all")) {
pc.days = 0x7F;
} else {
pc.days = 0;
if(days.indexOf("mo") >= 0) pc.days |= 1 << 0;
if(days.indexOf("tu") >= 0) pc.days |= 1 << 1;
if(days.indexOf("we") >= 0) pc.days |= 1 << 2;
if(days.indexOf("th") >= 0) pc.days |= 1 << 3;
if(days.indexOf("fr") >= 0) pc.days |= 1 << 4;
if(days.indexOf("sa") >= 0) pc.days |= 1 << 5;
if(days.indexOf("su") >= 0) pc.days |= 1 << 6;
}
String hours = getSplit(rest, 4);
if(hours.equals("all")) {
pc.hours = 0xFFFFFF;
} else {
pc.hours = 0;
for(uint8_t i = 0; i < 24; i++) {
char h[4];
snprintf_P(h, 4, PSTR("%02d"), i);
if(hours.indexOf(String(h)) >= 0) pc.hours |= 1 << i;
}
}
String start = getSplit(rest, 5);
pc.start_dayofmonth = start.substring(0, start.indexOf("-")).toInt();
pc.start_month = start.substring(start.indexOf("-")+1).toInt();
String end = getSplit(rest, 6);
pc.end_dayofmonth = end.substring(0, end.indexOf("-")).toInt();
pc.end_month = end.substring(end.indexOf("-")+1).toInt();
ps->setPriceConfig(priceIndex, pc);
priceModifierCount = priceIndex+1;
} else if(strncmp_P(buf, PSTR("thresholds "), 11) == 0) {
if(!lEac) { config.getEnergyAccountingConfig(eac); lEac = true; };
int i = 0;
@@ -2057,6 +2173,7 @@ void configFileParse() {
if(lEac) config.setEnergyAccountingConfig(eac);
if(sDs) ds.save();
if(sEa) ea.save();
if(priceModifierCount > 0) ps->save();
config.save();
LittleFS.end();
}