diff --git a/lib/AmsConfiguration/include/AmsConfiguration.h b/lib/AmsConfiguration/include/AmsConfiguration.h index 2036fa94..4919104b 100644 --- a/lib/AmsConfiguration/include/AmsConfiguration.h +++ b/lib/AmsConfiguration/include/AmsConfiguration.h @@ -171,6 +171,13 @@ struct DomoticzConfig { }; // 10 struct NtpConfig { + bool enable; + bool dhcp; + char server[64]; + char timezone[32]; +}; // 98 + +struct NtpConfig96 { bool enable; bool dhcp; int16_t offset; diff --git a/lib/AmsConfiguration/include/Timezones.h b/lib/AmsConfiguration/include/Timezones.h new file mode 100644 index 00000000..1e7be4f4 --- /dev/null +++ b/lib/AmsConfiguration/include/Timezones.h @@ -0,0 +1,86 @@ +#include + +#define JULY1970 15634800 + +TimeChangeRule TC_GMT = {"GMT", Last, Sun, Jan, 0, 0}; +TimeChangeRule TC_WET = {"WET", Last, Sun, Oct, 2, 0}; +TimeChangeRule TC_WEST = {"WEST", Last, Sun, Mar, 1, 60}; +TimeChangeRule TC_CET = {"CET", Last, Sun, Oct, 3, 60}; +TimeChangeRule TC_CEST = {"CEST", Last, Sun, Mar, 2, 120}; +TimeChangeRule TC_EET = {"EET", Last, Sun, Oct, 4, 120}; +TimeChangeRule TC_EEST = {"EEST", Last, Sun, Mar, 3, 180}; + +Timezone GMT = Timezone(TC_GMT); +Timezone WesterEuropean = Timezone(TC_WET, TC_WEST); +Timezone CentralEuropean = Timezone(TC_CET, TC_CEST); +Timezone EasternEuropean = Timezone(TC_EET, TC_EEST); + +Timezone* resolveTimezone(char* name) { + if(strncmp_P(name, PSTR("Europe/"), 7) == 0) { + if(strncmp_P(name+7, PSTR("Amsterdam"), 9) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Athens"), 6) == 0) + return &EasternEuropean; + if(strncmp_P(name+7, PSTR("Belfast"), 7) == 0) + return &WesterEuropean; + if(strncmp_P(name+7, PSTR("Berlin"), 6) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Bratislava"), 10) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Brussels"), 8) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Bucharest"), 9) == 0) + return &EasternEuropean; + if(strncmp_P(name+7, PSTR("Budapest"), 8) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Copenhagen"), 10) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Dublin"), 6) == 0) + return &WesterEuropean; + if(strncmp_P(name+7, PSTR("Helsinki"), 8) == 0) + return &EasternEuropean; + if(strncmp_P(name+7, PSTR("Lisbon"), 6) == 0) + return &WesterEuropean; + if(strncmp_P(name+7, PSTR("Ljubljana"), 9) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("London"), 6) == 0) + return &WesterEuropean; + if(strncmp_P(name+7, PSTR("Luxembourg"), 10) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Madrid"), 6) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Malta"), 5) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Nicosia"), 7) == 0) + return &EasternEuropean; + if(strncmp_P(name+7, PSTR("Oslo"), 4) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Paris"), 5) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Podgorica"), 9) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Prague"), 6) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Riga"), 4) == 0) + return &EasternEuropean; + if(strncmp_P(name+7, PSTR("Rome"), 4) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Sofia"), 5) == 0) + return &EasternEuropean; + if(strncmp_P(name+7, PSTR("Stockholm"), 9) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Tallinn"), 7) == 0) + return &EasternEuropean; + if(strncmp_P(name+7, PSTR("Vienna"), 6) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Vilnius"), 7) == 0) + return &EasternEuropean; + if(strncmp_P(name+7, PSTR("Warsaw"), 6) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Zagreb"), 6) == 0) + return &CentralEuropean; + if(strncmp_P(name+7, PSTR("Zurich"), 6) == 0) + return &CentralEuropean; + } + return &GMT; +} diff --git a/lib/AmsConfiguration/src/AmsConfiguration.cpp b/lib/AmsConfiguration/src/AmsConfiguration.cpp index b102da5a..0128a815 100644 --- a/lib/AmsConfiguration/src/AmsConfiguration.cpp +++ b/lib/AmsConfiguration/src/AmsConfiguration.cpp @@ -436,9 +436,8 @@ bool AmsConfiguration::setNtpConfig(NtpConfig& config) { } } ntpChanged |= config.dhcp != existing.dhcp; - ntpChanged |= config.offset != existing.offset; - ntpChanged |= config.summerOffset != existing.summerOffset; ntpChanged |= strcmp(config.server, existing.server) != 0; + ntpChanged |= strcmp(config.timezone, existing.timezone) != 0; } else { ntpChanged = true; } @@ -460,9 +459,8 @@ void AmsConfiguration::ackNtpChange() { void AmsConfiguration::clearNtp(NtpConfig& config) { config.enable = true; config.dhcp = true; - config.offset = 360; - config.summerOffset = 360; strcpy(config.server, "pool.ntp.org"); + strcpy(config.timezone, "Europe/Oslo"); } bool AmsConfiguration::getEntsoeConfig(EntsoeConfig& config) { @@ -857,6 +855,18 @@ bool AmsConfiguration::relocateConfig96() { wifi.mode = 1; // WIFI_STA EEPROM.put(CONFIG_WIFI_START, wifi); + NtpConfig ntp; + NtpConfig96 ntp96; + EEPROM.get(CONFIG_NTP_START, ntp96); + ntp.enable = ntp96.enable; + ntp.dhcp = ntp96.dhcp; + if(ntp96.offset == 0 && ntp96.summerOffset == 0) { + strcpy(ntp.timezone, "GMT"); + } else if(ntp96.offset == 360 && ntp96.summerOffset == 360) { + strcpy(ntp.timezone, "Europe/Oslo"); + } + EEPROM.get(CONFIG_NTP_START, ntp); + EEPROM.put(EEPROM_CONFIG_ADDRESS, 100); bool ret = EEPROM.commit(); EEPROM.end(); @@ -1066,8 +1076,7 @@ void AmsConfiguration::print(Print* debugger) debugger->println("--NTP configuration--"); debugger->printf("Enabled: %s\r\n", ntp.enable ? "Yes" : "No"); if(ntp.enable) { - debugger->printf("Offset: %i\r\n", ntp.offset); - debugger->printf("Summer offset: %i\r\n", ntp.summerOffset); + debugger->printf("Timezone: %s\r\n", ntp.timezone); debugger->printf("Server: %s\r\n", ntp.server); debugger->printf("DHCP: %s\r\n", ntp.dhcp ? "Yes" : "No"); } diff --git a/lib/EnergyAccounting/include/EnergyAccounting.h b/lib/EnergyAccounting/include/EnergyAccounting.h index 1a577fc4..2e9c02a6 100644 --- a/lib/EnergyAccounting/include/EnergyAccounting.h +++ b/lib/EnergyAccounting/include/EnergyAccounting.h @@ -57,7 +57,7 @@ public: float getMonthMax(); uint8_t getCurrentThreshold(); - float getPeak(uint8_t); + EnergyAccountingPeak getPeak(uint8_t); EnergyAccountingData getData(); void setData(EnergyAccountingData&); diff --git a/lib/EnergyAccounting/src/EnergyAccounting.cpp b/lib/EnergyAccounting/src/EnergyAccounting.cpp index 958b212f..a4263547 100644 --- a/lib/EnergyAccounting/src/EnergyAccounting.cpp +++ b/lib/EnergyAccounting/src/EnergyAccounting.cpp @@ -261,8 +261,8 @@ float EnergyAccounting::getMonthMax() { return maxHour > 0 ? maxHour / count / 100.0 : 0.0; } -float EnergyAccounting::getPeak(uint8_t num) { - if(num < 1 || num > 5) return 0.0; +EnergyAccountingPeak EnergyAccounting::getPeak(uint8_t num) { + if(num < 1 || num > 5) return EnergyAccountingPeak({0,0}); uint8_t count = 0; bool included[5] = { false, false, false, false, false }; @@ -288,10 +288,10 @@ float EnergyAccounting::getPeak(uint8_t num) { if(!included[i]) continue; pos++; if(pos == num) { - return data.peaks[i].value / 100.0; + return data.peaks[i]; } } - return 0.0; + return EnergyAccountingPeak({0,0}); } bool EnergyAccounting::load() { diff --git a/lib/EntsoePriceApi/include/EntsoeApi.h b/lib/EntsoePriceApi/include/EntsoeApi.h index b382719c..2fbd2129 100644 --- a/lib/EntsoePriceApi/include/EntsoeApi.h +++ b/lib/EntsoePriceApi/include/EntsoeApi.h @@ -25,6 +25,7 @@ public: char* getToken(); char* getCurrency(); + char* getArea(); float getValueForHour(int8_t); float getValueForHour(time_t, int8_t); diff --git a/lib/EntsoePriceApi/src/EntsoeApi.cpp b/lib/EntsoePriceApi/src/EntsoeApi.cpp index 4d1d31d7..b1f3db2f 100644 --- a/lib/EntsoePriceApi/src/EntsoeApi.cpp +++ b/lib/EntsoePriceApi/src/EntsoeApi.cpp @@ -41,6 +41,10 @@ char* EntsoeApi::getCurrency() { return this->config->currency; } +char* EntsoeApi::getArea() { + return this->config->area; +} + float EntsoeApi::getValueForHour(int8_t hour) { time_t cur = time(nullptr); return getValueForHour(cur, hour); diff --git a/lib/HomeAssistantMqttHandler/src/HomeAssistantMqttHandler.cpp b/lib/HomeAssistantMqttHandler/src/HomeAssistantMqttHandler.cpp index aab5d367..39d42823 100644 --- a/lib/HomeAssistantMqttHandler/src/HomeAssistantMqttHandler.cpp +++ b/lib/HomeAssistantMqttHandler/src/HomeAssistantMqttHandler.cpp @@ -86,7 +86,7 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En if(peakCount > 5) peakCount = 5; for(uint8_t i = 1; i <= peakCount; i++) { if(!peaks.isEmpty()) peaks += ","; - peaks += String(ea->getPeak(i), 2); + peaks += String(ea->getPeak(i).value, 2); } snprintf_P(json, BufferSize, REALTIME_JSON, ea->getMonthMax(), diff --git a/lib/RawMqttHandler/src/RawMqttHandler.cpp b/lib/RawMqttHandler/src/RawMqttHandler.cpp index a4ff9f32..cf4a4007 100644 --- a/lib/RawMqttHandler/src/RawMqttHandler.cpp +++ b/lib/RawMqttHandler/src/RawMqttHandler.cpp @@ -95,7 +95,7 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin uint8_t peakCount = ea->getConfig()->hours; if(peakCount > 5) peakCount = 5; for(uint8_t i = 1; i <= peakCount; i++) { - mqtt->publish(topic + "/realtime/import/peak/" + String(i, 10), String(ea->getPeak(i), 10), true, 0); + mqtt->publish(topic + "/realtime/import/peak/" + String(i, 10), String(ea->getPeak(i).value, 10), true, 0); } mqtt->publish(topic + "/realtime/import/threshold", String(ea->getCurrentThreshold(), 10), true, 0); mqtt->publish(topic + "/realtime/import/monthmax", String(ea->getMonthMax(), 3), true, 0); diff --git a/lib/SvelteUi/app/index.html b/lib/SvelteUi/app/index.html index a11cc552..4a138bae 100644 --- a/lib/SvelteUi/app/index.html +++ b/lib/SvelteUi/app/index.html @@ -3,7 +3,6 @@ - AMS reader diff --git a/lib/SvelteUi/app/package-lock.json b/lib/SvelteUi/app/package-lock.json index 7afd6e4d..e1f46dd0 100644 --- a/lib/SvelteUi/app/package-lock.json +++ b/lib/SvelteUi/app/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "svelte-gui", "version": "0.0.0", + "dependencies": { + "cssnano": "^5.1.14" + }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^1.0.1", "@tailwindcss/forms": "^0.5.2", @@ -124,6 +127,14 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" } }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@types/http-proxy": { "version": "1.17.9", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", @@ -253,6 +264,11 @@ "node": ">=8" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -276,10 +292,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", - "dev": true, + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "funding": [ { "type": "opencollective", @@ -291,10 +306,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" }, "bin": { "browserslist": "cli.js" @@ -321,11 +336,21 @@ "node": ">= 6" } }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, "node_modules/caniuse-lite": { - "version": "1.0.30001388", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001388.tgz", - "integrity": "sha512-znVbq4OUjqgLxMxoNX2ZeeLR0d7lcDiE5uJ4eUiWdml1J1EkxbnQq6opT9jb9SMfJxB0XA16/ziHwni4u1I3GQ==", - "dev": true, + "version": "1.0.30001434", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", + "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", "funding": [ { "type": "opencollective", @@ -382,17 +407,78 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/css-declaration-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, "bin": { "cssesc": "bin/cssesc" }, @@ -400,6 +486,98 @@ "node": ">=4" } }, + "node_modules/cssnano": { + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.14.tgz", + "integrity": "sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==", + "dependencies": { + "cssnano-preset-default": "^5.2.13", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.13", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz", + "integrity": "sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==", + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.3", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.1", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -476,11 +654,69 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "dev": true }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/electron-to-chromium": { - "version": "1.4.240", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.240.tgz", - "integrity": "sha512-r20dUOtZ4vUPTqAajDGonIM1uas5tf85Up+wPdtNBNvBSqGCfkpvMVvQ1T8YJzPV9/Y9g3FbUDcXb94Rafycow==", - "dev": true + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, "node_modules/es6-promise": { "version": "3.3.1", @@ -848,7 +1084,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -1156,11 +1391,20 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", - "dev": true, "engines": { "node": ">=10" } }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -1182,6 +1426,11 @@ "node": ">=12" } }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1262,7 +1511,6 @@ "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1283,8 +1531,7 @@ "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -1304,6 +1551,28 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-hash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", @@ -1350,8 +1619,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -1378,7 +1646,6 @@ "version": "8.4.16", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -1398,6 +1665,94 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, "node_modules/postcss-import": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", @@ -1463,6 +1818,98 @@ } } }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz", + "integrity": "sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, "node_modules/postcss-nested": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", @@ -1482,11 +1929,179 @@ "postcss": "^8.2.14" } }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz", + "integrity": "sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, "node_modules/postcss-selector-parser": { "version": "6.0.10", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -1495,11 +2110,39 @@ "node": ">=4" } }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -1664,11 +2307,18 @@ "sorcery": "bin/index.js" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1679,6 +2329,12 @@ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "dev": true }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -1691,6 +2347,21 @@ "node": ">=8" } }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -1826,6 +2497,26 @@ "typescript": "^4.1.2" } }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/tailwindcss": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.1.8.tgz", @@ -1937,10 +2628,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.6.tgz", - "integrity": "sha512-We7BqM9XFlcW94Op93uW8+2LXvGezs7QA0WY+f1H7RR1q46B06W6hZF6LbmOlpCS1HU22q/6NOGTGW5sCm7NJQ==", - "dev": true, + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "funding": [ { "type": "opencollective", @@ -1965,8 +2655,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/vite": { "version": "3.0.9", @@ -2101,6 +2790,11 @@ "mini-svg-data-uri": "^1.2.3" } }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + }, "@types/http-proxy": { "version": "1.17.9", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", @@ -2196,6 +2890,11 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2216,15 +2915,14 @@ } }, "browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", - "dev": true, + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" } }, "buffer-crc32": { @@ -2239,11 +2937,21 @@ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "dev": true }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, "caniuse-lite": { - "version": "1.0.30001388", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001388.tgz", - "integrity": "sha512-znVbq4OUjqgLxMxoNX2ZeeLR0d7lcDiE5uJ4eUiWdml1J1EkxbnQq6opT9jb9SMfJxB0XA16/ziHwni4u1I3GQ==", - "dev": true + "version": "1.0.30001434", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", + "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==" }, "chokidar": { "version": "3.5.3", @@ -2278,17 +2986,125 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "css-declaration-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "requires": {} + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.14.tgz", + "integrity": "sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==", + "requires": { + "cssnano-preset-default": "^5.2.13", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "dependencies": { + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + } + } + }, + "cssnano-preset-default": { + "version": "5.2.13", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz", + "integrity": "sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==", + "requires": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.3", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.1", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + } + }, + "cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "requires": {} + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "requires": { + "css-tree": "^1.1.2" + } }, "debug": { "version": "4.3.4", @@ -2346,11 +3162,48 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "dev": true }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, "electron-to-chromium": { - "version": "1.4.240", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.240.tgz", - "integrity": "sha512-r20dUOtZ4vUPTqAajDGonIM1uas5tf85Up+wPdtNBNvBSqGCfkpvMVvQ1T8YJzPV9/Y9g3FbUDcXb94Rafycow==", - "dev": true + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" }, "es6-promise": { "version": "3.3.1", @@ -2530,8 +3383,7 @@ "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "estree-walker": { "version": "2.0.2", @@ -2750,8 +3602,17 @@ "lilconfig": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", - "dev": true + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, "lower-case": { "version": "2.0.2", @@ -2771,6 +3632,11 @@ "sourcemap-codec": "^1.4.8" } }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2832,8 +3698,7 @@ "nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, "no-case": { "version": "3.0.4", @@ -2848,8 +3713,7 @@ "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, "normalize-path": { "version": "3.0.0", @@ -2863,6 +3727,19 @@ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + }, "object-hash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", @@ -2903,8 +3780,7 @@ "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "picomatch": { "version": "2.3.1", @@ -2922,13 +3798,65 @@ "version": "8.4.16", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", - "dev": true, "requires": { "nanoid": "^3.3.4", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, + "postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "requires": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "requires": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "requires": {} + }, + "postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "requires": {} + }, + "postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "requires": {} + }, + "postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "requires": {} + }, "postcss-import": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", @@ -2959,6 +3887,62 @@ "yaml": "^2.1.1" } }, + "postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + } + }, + "postcss-merge-rules": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz", + "integrity": "sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "requires": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, "postcss-nested": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", @@ -2968,21 +3952,134 @@ "postcss-selector-parser": "^6.0.6" } }, + "postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "requires": {} + }, + "postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "requires": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "requires": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "requires": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-reduce-initial": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz", + "integrity": "sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, "postcss-selector-parser": { "version": "6.0.10", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, + "postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + } + }, + "postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, "postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "queue-microtask": { "version": "1.2.3", @@ -3088,11 +4185,15 @@ "sourcemap-codec": "^1.3.0" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, "source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, "sourcemap-codec": { "version": "1.4.8", @@ -3100,6 +4201,11 @@ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "dev": true }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, "strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -3109,6 +4215,15 @@ "min-indent": "^1.0.0" } }, + "stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "requires": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + } + }, "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -3172,6 +4287,20 @@ "pascal-case": "^3.1.1" } }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + } + }, "tailwindcss": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.1.8.tgz", @@ -3243,10 +4372,9 @@ "peer": true }, "update-browserslist-db": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.6.tgz", - "integrity": "sha512-We7BqM9XFlcW94Op93uW8+2LXvGezs7QA0WY+f1H7RR1q46B06W6hZF6LbmOlpCS1HU22q/6NOGTGW5sCm7NJQ==", - "dev": true, + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -3255,8 +4383,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "vite": { "version": "3.0.9", diff --git a/lib/SvelteUi/app/package.json b/lib/SvelteUi/app/package.json index 29cb3c62..1de63b5d 100644 --- a/lib/SvelteUi/app/package.json +++ b/lib/SvelteUi/app/package.json @@ -10,6 +10,7 @@ }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^1.0.1", + "@tailwindcss/forms": "^0.5.2", "autoprefixer": "^10.4.7", "http-proxy-middleware": "^2.0.1", "postcss": "^8.4.14", @@ -18,7 +19,9 @@ "svelte-navigator": "^3.2.2", "svelte-preprocess": "^4.10.7", "tailwindcss": "^3.1.5", - "@tailwindcss/forms": "^0.5.2", "vite": "^3.0.7" + }, + "dependencies": { + "cssnano": "^5.1.14" } } diff --git a/lib/SvelteUi/app/postcss.config.cjs b/lib/SvelteUi/app/postcss.config.cjs index e48cff58..04e4301f 100644 --- a/lib/SvelteUi/app/postcss.config.cjs +++ b/lib/SvelteUi/app/postcss.config.cjs @@ -1,5 +1,6 @@ const tailwindcss = require("tailwindcss"); const autoprefixer = require("autoprefixer"); +const cssnano = require("cssnano"); const config = { plugins: [ @@ -7,6 +8,7 @@ const config = { tailwindcss(), //But others, like autoprefixer, need to run after, autoprefixer, + cssnano() ], }; diff --git a/lib/SvelteUi/app/src/App.svelte b/lib/SvelteUi/app/src/App.svelte index 55a30f41..0e767cbc 100644 --- a/lib/SvelteUi/app/src/App.svelte +++ b/lib/SvelteUi/app/src/App.svelte @@ -9,6 +9,7 @@ import VendorPanel from './lib/VendorPanel.svelte'; import SetupPanel from './lib/SetupPanel.svelte'; import Mask from './lib/Mask.svelte'; + import FileUploadComponent from "./lib/FileUploadComponent.svelte"; let sysinfo = {}; sysinfoStore.subscribe(update => { @@ -37,7 +38,16 @@ - + + + + + + + + + + diff --git a/lib/SvelteUi/app/src/app.postcss b/lib/SvelteUi/app/src/app.postcss index 7f7b70f8..43933ab8 100644 --- a/lib/SvelteUi/app/src/app.postcss +++ b/lib/SvelteUi/app/src/app.postcss @@ -5,4 +5,140 @@ .gh-logo { width: 2rem; height: 2rem; -} \ No newline at end of file +} + +.cnt { + @apply bg-white m-2 p-2 rounded shadow-lg +} + +.in-pre { + @apply flex items-center bg-gray-100 rounded-l-md border border-r-0 border-gray-300 px-3 whitespace-nowrap text-sm +} + +.in-post { + @apply flex items-center bg-gray-100 rounded-r-md border border-l-0 border-gray-300 px-3 whitespace-nowrap text-sm +} + +.in-txt { + @apply h-10 shadow-sm border-gray-300 disabled:bg-gray-200 +} +.in-f { + @apply in-txt rounded-l-md +} +.in-m { + @apply in-txt border-l-0 w-full +} +.in-l { + @apply in-txt border-l-0 rounded-r-md +} +.in-s { + @apply in-txt rounded-md w-full +} +.tr { + @apply text-right +} + +.bd-grn { + @apply my-auto bg-green-500 text-green-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded +} +.bd-ylo { + @apply my-auto bg-yellow-500 text-yellow-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded +} +.bd-red { + @apply my-auto bg-red-500 text-red-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded +} +.bd-blu { + @apply my-auto bg-blue-500 text-blue-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded +} +.bd-gry { + @apply my-auto bg-gray-500 text-gray-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded +} + +.btn-pri { + @apply py-2 px-4 rounded bg-blue-500 text-white mr-3 +} + +.pl-root { + position: relative; +} +.pl-ov { + position: absolute; + top: 35%; + left: 25%; + width: 50%; + text-align: center; +} +.pl-val { + font-size: 1.7rem; +} +.pl-unt { + font-size: 1.0rem; + color: grey; +} +.pl-lab { + font-size: 1.0rem; +} + +.chart { + width: 100%; + height: 100%; + margin: 0 auto; +} + +svg { + position: relative; + width: 100%; +} + +.tick { + font-family: Helvetica, Arial; + font-size: 0.85em; + font-weight: 200; +} + +.tick line { + stroke: #e2e2e2; + stroke-dasharray: 2; +} + +.tick text { + fill: #999; + text-anchor: start; +} + +.tick.tick-0 line { + stroke-dasharray: 0; +} + +.tick.tick-green line { + stroke: #32d900 !important; +} + +.tick.tick-green text { + fill: #32d900 !important; +} + +.tick.tick-orange line { + stroke: #d95600 !important; +} + +.tick.tick-orange text { + fill: #d95600 !important; +} + +.x-axis .tick text { + text-anchor: middle; +} + +.bars rect { + stroke: rgb(0,0,0); + stroke-opacity: 0.25; + opacity: 0.9; +} + +.bars text { + font-family: Helvetica, Arial; + font-size: 0.85em; + display: block; + text-align: center; +} diff --git a/lib/SvelteUi/app/src/lib/AccountingData.svelte b/lib/SvelteUi/app/src/lib/AccountingData.svelte index ac4b911e..544f9743 100644 --- a/lib/SvelteUi/app/src/lib/AccountingData.svelte +++ b/lib/SvelteUi/app/src/lib/AccountingData.svelte @@ -8,45 +8,47 @@
Real time calculation + {#if data && data.h !== undefined}
Hour
-
{data && data.h && data.h.u ? data.h.u.toFixed(2) : '-'} kWh {#if currency && (hasExport)}/ {data && data.h && data.h.c ? data.h.c.toFixed(2) : '-'} {currency}{/if}
+
{data.h.u ? data.h.u.toFixed(2) : '-'} kWh {#if currency && (hasExport)}/ {data.h.c ? data.h.c.toFixed(2) : '-'} {currency}{/if}
Day
-
{data && data.d && data.d.u ? data.d.u.toFixed(1) : '-'} kWh {#if currency && (hasExport)}/ {data && data.d && data.d.c ? data.d.c.toFixed(2) : '-'} {currency}{/if}
+
{data.d.u ? data.d.u.toFixed(1) : '-'} kWh {#if currency && (hasExport)}/ {data.d.c ? data.d.c.toFixed(2) : '-'} {currency}{/if}
Month
-
{data && data.m && data.m.u ? data.m.u.toFixed(0) : '-'} kWh {#if currency && (hasExport)}/ {data && data.m && data.m.c ? data.m.c.toFixed(2) : '-'} {currency}{/if}
+
{data.m.u ? data.m.u.toFixed(0) : '-'} kWh {#if currency && (hasExport)}/ {data.m.c ? data.m.c.toFixed(2) : '-'} {currency}{/if}
{#if hasExport}
Hour
-
{data && data.h && data.h.p ? data.h.p.toFixed(2) : '-'} kWh {#if currency}/ {data && data.h && data.h.pc ? data.h.pc.toFixed(2) : '-'} {currency}{/if}
+
{data.h.p ? data.h.p.toFixed(2) : '-'} kWh {#if currency}/ {data.h.pc ? data.h.pc.toFixed(2) : '-'} {currency}{/if}
Day
-
{data && data.d && data.d.p ? data.d.p.toFixed(1) : '-'} kWh {#if currency}/ {data && data.d && data.d.pc ? data.d.pc.toFixed(2) : '-'} {currency}{/if}
+
{data.d.p ? data.d.p.toFixed(1) : '-'} kWh {#if currency}/ {data.d.pc ? data.d.pc.toFixed(2) : '-'} {currency}{/if}
Month
-
{data && data.m && data.m.p ? data.m.p.toFixed(0) : '-'} kWh {#if currency}/ {data && data.m && data.m.pc ? data.m.pc.toFixed(2) : '-'} {currency}{/if}
+
{data.m.p ? data.m.p.toFixed(0) : '-'} kWh {#if currency}/ {data.m.pc ? data.m.pc.toFixed(2) : '-'} {currency}{/if}
{:else}
Hour
-
{data && data.h && data.h.c ? data.h.c.toFixed(2) : '-'} {currency}
+
{data.h.c ? data.h.c.toFixed(2) : '-'} {currency}
Day
-
{data && data.d && data.d.c ? data.d.c.toFixed(2) : '-'} {currency}
+
{data.d.c ? data.d.c.toFixed(2) : '-'} {currency}
Month
-
{data && data.m && data.m.c ? data.m.c.toFixed(2) : '-'} {currency}
+
{data.m.c ? data.m.c.toFixed(2) : '-'} {currency}
{/if}
+ {/if}
\ No newline at end of file diff --git a/lib/SvelteUi/app/src/lib/Badge.svelte b/lib/SvelteUi/app/src/lib/Badge.svelte index 131fb1ae..ae1eb16c 100644 --- a/lib/SvelteUi/app/src/lib/Badge.svelte +++ b/lib/SvelteUi/app/src/lib/Badge.svelte @@ -4,13 +4,13 @@ export let text; {#if color == 'green'} -{text} +{text} {:else if color === `yellow`} -{text} +{text} {:else if color === `red`} -{text} +{text} {:else if color === `blue`} -{text} +{text} {:else if color === `gray`} -{text} +{text} {/if} \ No newline at end of file diff --git a/lib/SvelteUi/app/src/lib/BarChart.svelte b/lib/SvelteUi/app/src/lib/BarChart.svelte index 6be10fae..7dfb6ca2 100644 --- a/lib/SvelteUi/app/src/lib/BarChart.svelte +++ b/lib/SvelteUi/app/src/lib/BarChart.svelte @@ -94,69 +94,3 @@ - - diff --git a/lib/SvelteUi/app/src/lib/Clock.svelte b/lib/SvelteUi/app/src/lib/Clock.svelte index f59e557c..e0f5dc57 100644 --- a/lib/SvelteUi/app/src/lib/Clock.svelte +++ b/lib/SvelteUi/app/src/lib/Clock.svelte @@ -1,8 +1,7 @@ {#if Math.abs(new Date().getTime()-timestamp.getTime()) < 300000 } diff --git a/lib/SvelteUi/app/src/lib/ConfigurationPanel.svelte b/lib/SvelteUi/app/src/lib/ConfigurationPanel.svelte index c69f928e..9f1b2e0a 100644 --- a/lib/SvelteUi/app/src/lib/ConfigurationPanel.svelte +++ b/lib/SvelteUi/app/src/lib/ConfigurationPanel.svelte @@ -5,6 +5,9 @@ import Mask from './Mask.svelte' import Badge from './Badge.svelte'; import HelpIcon from './HelpIcon.svelte'; + import CountrySelectOptions from './CountrySelectOptions.svelte'; + import { Link, navigate } from 'svelte-navigator'; + export let sysinfo = {} @@ -87,71 +90,95 @@ }); loadingOrSaving = false; - getConfiguration(); + navigate("/"); } + + async function reboot() { + const response = await fetch('/reboot', { + method: 'POST' + }); + let res = (await response.json()) + } + + const askReboot = function() { + if(confirm('Are you sure you want to reboot the device?')) { + sysinfoStore.update(s => { + s.booting = true; + return s; + }); + reboot(); + } + } + + const updateMqttPort = function() { + if(configuration.q.s.e) { + if(configuration.q.p == 1883) configuration.q.p = 8883; + } else { + if(configuration.q.p == 8883) configuration.q.p = 1883; + } + }
-
+
General
Hostname
- +
- Timezone
- +
+
+ Price region
+ +
-
- Price region
- -
Currency
- @@ -160,7 +187,7 @@
Multiplier
- +
@@ -168,13 +195,13 @@
{#if configuration.p.e} -
+
{/if}
{/if}
Security
- @@ -183,22 +210,22 @@ {#if configuration.g.s > 0}
Username
- +
Password
- +
{/if}
-
+
Meter
Serial configuration
- @@ -207,37 +234,37 @@ - - +
+
+ Voltage
+ +
-
- Voltage
- -
-
+
Main fuse
-
+
Production
@@ -247,13 +274,13 @@
{#if configuration.m.e.e} -
+
{/if}
{#if configuration.m.e.e}
Authentication key
- +
{/if} @@ -262,39 +289,39 @@
Instant
- +
Volt
- +
Amp
- +
Acc.
- +
{/if}
-
+
WiFi
SSID
- +
Password
- +
Power saving
- @@ -304,24 +331,24 @@
Power
-
+
Network
IP
- - - + +
DNS
- - + +
{/if} @@ -348,62 +375,70 @@
NTP
- +
-
+
MQTT
Server - + {#if sysinfo.chip != 'esp8266'} + + {/if}
- - + +
{#if configuration.q.s.e}
- {#if configuration.q.s.c} - - {:else} - - {/if} + + {#if configuration.q.s.c} + + {:else} + + {/if} + - {#if configuration.q.s.r} - - {:else} - - {/if} + + {#if configuration.q.s.r} + + {:else} + + {/if} + - {#if configuration.q.s.k} - - {:else} - - {/if} + + {#if configuration.q.s.k} + + {:else} + + {/if} +
{/if}
Username
- +
Password
- +
Client ID
- +
Payload
- @@ -415,70 +450,70 @@
Publish topic
- +
{#if configuration.p.r.startsWith("10YNO") || configuration.p.r == '10Y1001A1001A48H'} -
+
Tariff thresholds
{/if} {#if sysinfo.board > 20 || sysinfo.chip == 'esp8266'} -
+
Hardware {#if sysinfo.board > 20} @@ -486,48 +521,48 @@
HAN
-
AP button
- +
LED
- +
RGB
- - - + + +
Temperature
- +
Analog temp
- +
{#if sysinfo.chip != 'esp8266'}
Vcc
- +
{/if} {#if configuration.i.v.p > 0}
Voltage divider
- - + +
{/if} @@ -538,38 +573,39 @@
Vcc offset
- +
- multiplier
- + Multiplier
+
{#if sysinfo.board == 2 || sysinfo.board == 100} -
- boot limit
- +
+ Boot limit
+
{/if}
{/if}
{/if} -
+
Debugging +
{#if configuration.d.s} -
Debug can cause sudden reboots. Do not leave on!
+
Debug can cause sudden reboots. Do not leave on!
{#if configuration.d.t} -
Telnet is unsafe and should be off when not in use
+
Telnet is unsafe and should be off when not in use
{/if}
- @@ -579,8 +615,17 @@ {/if}
- - +
+
+ +
+
+ +
+
+ +
+
diff --git a/lib/SvelteUi/app/src/lib/Counter.svelte b/lib/SvelteUi/app/src/lib/Counter.svelte deleted file mode 100644 index e45f9031..00000000 --- a/lib/SvelteUi/app/src/lib/Counter.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/lib/SvelteUi/app/src/lib/CountrySelectOptions.svelte b/lib/SvelteUi/app/src/lib/CountrySelectOptions.svelte new file mode 100644 index 00000000..c3ff3ac5 --- /dev/null +++ b/lib/SvelteUi/app/src/lib/CountrySelectOptions.svelte @@ -0,0 +1,10 @@ + + + +{#each europe as c} + +{/each} diff --git a/lib/SvelteUi/app/src/lib/Dashboard.svelte b/lib/SvelteUi/app/src/lib/Dashboard.svelte index a09727e8..254f8ac4 100644 --- a/lib/SvelteUi/app/src/lib/Dashboard.svelte +++ b/lib/SvelteUi/app/src/lib/Dashboard.svelte @@ -32,7 +32,7 @@
-
+
@@ -42,7 +42,7 @@
{#if data.om || data.e > 0} -
+
@@ -52,36 +52,40 @@
{/if} -
+ {#if data.u1 > 100 || data.u2 > 100 || data.u3 > 100} +
-
+ {/if} + {#if data.i1 > 0.01 || data.i2 > 0.01 || data.i3 > 0.01} +
-
+ {/if} +
-
+
- {#if prices.currency == 'NOK'} -
+ {#if data && data.ea} +
{/if} {#if prices.currency} -
+
{/if} -
+
-
+
{#if data.t && data.t != -127 && temperatures.c > 1} -
+
{/if} diff --git a/lib/SvelteUi/app/src/lib/DataStores.js b/lib/SvelteUi/app/src/lib/DataStores.js index b1b8aade..215ce85a 100644 --- a/lib/SvelteUi/app/src/lib/DataStores.js +++ b/lib/SvelteUi/app/src/lib/DataStores.js @@ -127,6 +127,19 @@ export const temperaturesStore = writable(temperatures, (set) => { return function stop() {} }); +let tariff = {}; +export async function getTariff() { + const response = await fetchWithTimeout("/tariff.json"); + tariff = (await response.json()) + tariffStore.set(tariff); + let date = new Date(); + setTimeout(getTariff, (61-date.getMinutes())*60000) +} + +export const tariffStore = writable(tariff, (set) => { + return function stop() {} +}); + let releases = []; export const gitHubReleaseStore = writable(releases); diff --git a/lib/SvelteUi/app/src/lib/DayPlot.svelte b/lib/SvelteUi/app/src/lib/DayPlot.svelte index db0495fb..7ee09bdc 100644 --- a/lib/SvelteUi/app/src/lib/DayPlot.svelte +++ b/lib/SvelteUi/app/src/lib/DayPlot.svelte @@ -14,6 +14,7 @@ let xTicks = []; let points = []; let cur = new Date(); + let offset = -cur.getTimezoneOffset()/60; for(i = cur.getUTCHours(); i<24; i++) { let imp = json["i"+zeropad(i)]; let exp = json["e"+zeropad(i)]; @@ -21,7 +22,7 @@ if(exp === undefined) exp = 0; xTicks.push({ - label: zeropad(i) + label: zeropad((i+offset)%24) }); points.push({ label: imp.toFixed(1), @@ -40,7 +41,7 @@ if(exp === undefined) exp = 0; xTicks.push({ - label: zeropad(i) + label: zeropad((i+offset)%24) }); points.push({ label: imp.toFixed(1), @@ -58,13 +59,15 @@ max = boundary; min = min == 0 ? 0 : boundary*-1; - let yTickDistDown = min/4; - for(i = 0; i < 5; i++) { - let val = (yTickDistDown*i); - yTicks.push({ - value: val, - label: (val/10).toFixed(1) - }); + if(min < 0) { + let yTickDistDown = min/4; + for(i = 1; i < 5; i++) { + let val = (yTickDistDown*i); + yTicks.push({ + value: val, + label: (val/10).toFixed(1) + }); + } } let yTickDistUp = max/4; diff --git a/lib/SvelteUi/app/src/lib/FileUploadComponent.svelte b/lib/SvelteUi/app/src/lib/FileUploadComponent.svelte new file mode 100644 index 00000000..46c4dc78 --- /dev/null +++ b/lib/SvelteUi/app/src/lib/FileUploadComponent.svelte @@ -0,0 +1,18 @@ + + +
+
+ Upload {title} +

Select a suitable file and click upload

+
+ +
+ +
+
+
+
diff --git a/lib/SvelteUi/app/src/lib/Header.svelte b/lib/SvelteUi/app/src/lib/Header.svelte index 3520b8a8..c6265dd1 100644 --- a/lib/SvelteUi/app/src/lib/Header.svelte +++ b/lib/SvelteUi/app/src/lib/Header.svelte @@ -10,7 +10,6 @@ import GearIcon from './GearIcon.svelte'; import InfoIcon from "./InfoIcon.svelte"; import HelpIcon from "./HelpIcon.svelte"; - import ReloadIcon from "./ReloadIcon.svelte"; import DownloadIcon from "./DownloadIcon.svelte"; export let data = {} diff --git a/lib/SvelteUi/app/src/lib/Helpers.js b/lib/SvelteUi/app/src/lib/Helpers.js index a71db001..14ce27c6 100644 --- a/lib/SvelteUi/app/src/lib/Helpers.js +++ b/lib/SvelteUi/app/src/lib/Helpers.js @@ -1,3 +1,5 @@ +export let monthnames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; + export function voltcol(pct) { if(pct > 85) return '#d90000'; else if(pct > 75) return'#e32100'; diff --git a/lib/SvelteUi/app/src/lib/MonthPlot.svelte b/lib/SvelteUi/app/src/lib/MonthPlot.svelte index 87905a00..15991410 100644 --- a/lib/SvelteUi/app/src/lib/MonthPlot.svelte +++ b/lib/SvelteUi/app/src/lib/MonthPlot.svelte @@ -61,13 +61,15 @@ max = boundary; min = min == 0 ? 0 : boundary*-1; - let yTickDistDown = min/4; - for(i = 0; i < 5; i++) { - let val = (yTickDistDown*i); - yTicks.push({ - value: val, - label: val.toFixed(0) - }); + if(min < 0) { + let yTickDistDown = min/4; + for(i = 0; i < 5; i++) { + let val = (yTickDistDown*i); + yTicks.push({ + value: val, + label: val.toFixed(0) + }); + } } let yTickDistUp = max/4; diff --git a/lib/SvelteUi/app/src/lib/PowerGauge.svelte b/lib/SvelteUi/app/src/lib/PowerGauge.svelte index 334adf43..a04151b9 100644 --- a/lib/SvelteUi/app/src/lib/PowerGauge.svelte +++ b/lib/SvelteUi/app/src/lib/PowerGauge.svelte @@ -8,35 +8,12 @@ export let label; -
+
- - {val} - {unit} + + {val} + {unit}
- {label} + {label}
- - diff --git a/lib/SvelteUi/app/src/lib/PricePlot.svelte b/lib/SvelteUi/app/src/lib/PricePlot.svelte index f2e9c2a2..8d2509ef 100644 --- a/lib/SvelteUi/app/src/lib/PricePlot.svelte +++ b/lib/SvelteUi/app/src/lib/PricePlot.svelte @@ -56,13 +56,15 @@ max = Math.ceil(max); min = Math.floor(min); - let yTickDistDown = min/4; - for(i = 0; i < 5; i++) { - let val = (yTickDistDown*i); - yTicks.push({ - value: val, - label: (val/100).toFixed(2) - }); + if(min < 0) { + let yTickDistDown = min/4; + for(i = 1; i < 5; i++) { + let val = (yTickDistDown*i); + yTicks.push({ + value: val, + label: (val/100).toFixed(2) + }); + } } let yTickDistUp = max/4; @@ -74,6 +76,8 @@ }); } + console.log(yTicks); + config = { title: "Future energy price (" + json.currency + ")", padding: { top: 20, right: 15, bottom: 20, left: 35 }, diff --git a/lib/SvelteUi/app/src/lib/ReloadIcon.svelte b/lib/SvelteUi/app/src/lib/ReloadIcon.svelte deleted file mode 100644 index d1bbaff3..00000000 --- a/lib/SvelteUi/app/src/lib/ReloadIcon.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/lib/SvelteUi/app/src/lib/SetupPanel.svelte b/lib/SvelteUi/app/src/lib/SetupPanel.svelte index 6f7713e7..087a71f6 100644 --- a/lib/SvelteUi/app/src/lib/SetupPanel.svelte +++ b/lib/SvelteUi/app/src/lib/SetupPanel.svelte @@ -65,30 +65,30 @@ -
-
+
+
Setup
SSID
- +
PSK
- +
Hostname: - +
{#if staticIp}
- - + +
DNS
- +
{/if} @@ -113,7 +113,7 @@ Read more
- +
diff --git a/lib/SvelteUi/app/src/lib/StatusPage.svelte b/lib/SvelteUi/app/src/lib/StatusPage.svelte index cf842279..c15dbb2b 100644 --- a/lib/SvelteUi/app/src/lib/StatusPage.svelte +++ b/lib/SvelteUi/app/src/lib/StatusPage.svelte @@ -3,11 +3,9 @@ import { getSysinfo, gitHubReleaseStore, sysinfoStore } from './DataStores.js'; import { upgrade, getNextVersion } from './UpgradeHelper'; import DownloadIcon from './DownloadIcon.svelte'; - import UploadIcon from './UploadIcon.svelte'; - export let sysinfo = {} - export let data = {} - + export let sysinfo; + let nextVersion = {}; gitHubReleaseStore.subscribe(releases => { nextVersion = getNextVersion(sysinfo.version, releases); @@ -45,14 +43,12 @@ } } - let fileinput; - getSysinfo();
-
+
Device information
Chip: {sysinfo.chip} @@ -68,7 +64,7 @@
{#if sysinfo.meter} -
+
Meter
Manufacturer: {metertype(sysinfo.meter.mfg)} @@ -82,7 +78,7 @@
{/if} {#if sysinfo.net} -
+
Network
IP: {sysinfo.net.ip} @@ -98,7 +94,7 @@
{/if} -
+
Firmware
Installed version: {sysinfo.version} @@ -115,12 +111,12 @@
{#if sysinfo.fwconsent === 2}
-
You have disabled one-click firmware upgrade, link to self-upgrade is disabled
+
You have disabled one-click firmware upgrade, link to self-upgrade is disabled
{/if} {/if} {#if sysinfo.board == 2 || sysinfo.board == 4 || sysinfo.board == 7 } -
+
{boardtype(sysinfo.chip, sysinfo.board)} must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.
{/if} diff --git a/lib/SvelteUi/app/src/lib/TariffPeakChart.svelte b/lib/SvelteUi/app/src/lib/TariffPeakChart.svelte index da039149..859a4ab7 100644 --- a/lib/SvelteUi/app/src/lib/TariffPeakChart.svelte +++ b/lib/SvelteUi/app/src/lib/TariffPeakChart.svelte @@ -1,16 +1,17 @@ -{#if chip == 'esp8266'} +{#if chip == 'esp8266'} - - - - - - - - - {/if} {#if chip == 'esp32' || chip == 'esp32solo'} - +{/if} +{#if chip == 'esp32s2'} + +{/if} +{#if chip.startsWith('esp32')} +{/if} +{#if chip == 'esp8266'} + +{/if} +{#if chip.startsWith('esp32')} +{/if} + +{#if chip.startsWith('esp32')} +{#if chip != 'esp32s2'} +{/if} @@ -45,33 +50,6 @@ {/if} {#if chip == 'esp32s2'} - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/SvelteUi/app/src/lib/UpgradeHelper.js b/lib/SvelteUi/app/src/lib/UpgradeHelper.js index 31ab6ca3..ec2cb068 100644 --- a/lib/SvelteUi/app/src/lib/UpgradeHelper.js +++ b/lib/SvelteUi/app/src/lib/UpgradeHelper.js @@ -1,5 +1,3 @@ - - export async function upgrade(version) { const data = new URLSearchParams() data.append('version', version.tag_name); diff --git a/lib/SvelteUi/app/src/lib/UploadIcon.svelte b/lib/SvelteUi/app/src/lib/UploadIcon.svelte deleted file mode 100644 index 4aefd39e..00000000 --- a/lib/SvelteUi/app/src/lib/UploadIcon.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/lib/SvelteUi/app/src/lib/VendorPanel.svelte b/lib/SvelteUi/app/src/lib/VendorPanel.svelte index d3801879..33f8d8d0 100644 --- a/lib/SvelteUi/app/src/lib/VendorPanel.svelte +++ b/lib/SvelteUi/app/src/lib/VendorPanel.svelte @@ -31,27 +31,27 @@ } -
-
+
+
Initial configuration
Board type
-
{#if sysinfo.board && sysinfo.board > 20}
HAN GPIO
-
{/if}
- +
 
diff --git a/lib/SvelteUi/app/vite.config.js b/lib/SvelteUi/app/vite.config.js index 9e1ad30c..dbfba4be 100644 --- a/lib/SvelteUi/app/vite.config.js +++ b/lib/SvelteUi/app/vite.config.js @@ -17,17 +17,18 @@ export default defineConfig({ plugins: [svelte()], server: { proxy: { - "/data.json": "http://192.168.233.229", - "/energyprice.json": "http://192.168.233.235", - "/dayplot.json": "http://192.168.233.235", - "/monthplot.json": "http://192.168.233.235", - "/temperature.json": "http://192.168.233.235", - "/sysinfo.json": "http://192.168.233.229", - "/configuration.json": "http://192.168.233.229", - "/save": "http://192.168.233.229", - "/reboot": "http://192.168.233.229", - "/firmware": "http://192.168.233.229", - "/upgrade": "http://192.168.233.229" + "/data.json": "http://192.168.233.244", + "/energyprice.json": "http://192.168.233.244", + "/dayplot.json": "http://192.168.233.244", + "/monthplot.json": "http://192.168.233.244", + "/temperature.json": "http://192.168.233.244", + "/sysinfo.json": "http://192.168.233.244", + "/configuration.json": "http://192.168.233.244", + "/tariff.json": "http://192.168.233.244", + "/save": "http://192.168.233.244", + "/reboot": "http://192.168.233.244", + "/firmware": "http://192.168.233.244", + "/upgrade": "http://192.168.233.244" } } }) diff --git a/lib/SvelteUi/include/AmsWebServer.h b/lib/SvelteUi/include/AmsWebServer.h index e763325e..4dc01981 100644 --- a/lib/SvelteUi/include/AmsWebServer.h +++ b/lib/SvelteUi/include/AmsWebServer.h @@ -84,6 +84,7 @@ private: void energyPriceJson(); void temperatureJson(); void wifiScanJson(); + void tariffJson(); void configurationJson(); void handleSave(); @@ -93,6 +94,9 @@ private: void firmwareUpload(); void isAliveCheck(); + void mqttCaUpload(); + void mqttCertUpload(); + void mqttKeyUpload(); HTTPUpload& uploadFile(const char* path); void factoryResetPost(); diff --git a/lib/SvelteUi/json/data.json b/lib/SvelteUi/json/data.json index 0581d6ac..eefea149 100644 --- a/lib/SvelteUi/json/data.json +++ b/lib/SvelteUi/json/data.json @@ -53,5 +53,6 @@ "p" : %.2f } }, + "pr" : "%s", "c" : %lu } \ No newline at end of file diff --git a/lib/SvelteUi/json/response.json b/lib/SvelteUi/json/response.json new file mode 100644 index 00000000..7a5c6ee3 --- /dev/null +++ b/lib/SvelteUi/json/response.json @@ -0,0 +1,5 @@ +{ + "success": %s, + "message": "%s", + "reboot": %s +} \ No newline at end of file diff --git a/lib/SvelteUi/src/AmsWebServer.cpp b/lib/SvelteUi/src/AmsWebServer.cpp index f95226f0..ac82b70a 100644 --- a/lib/SvelteUi/src/AmsWebServer.cpp +++ b/lib/SvelteUi/src/AmsWebServer.cpp @@ -14,6 +14,7 @@ #include "html/monthplot_json.h" #include "html/energyprice_json.h" #include "html/tempsensor_json.h" +#include "html/response_json.h" #include "version.h" @@ -40,6 +41,10 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter server.on(F("/"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); server.on(F("/configuration"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); server.on(F("/status"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); + server.on(F("/mqtt-ca"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); + server.on(F("/mqtt-cert"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); + server.on(F("/mqtt-key"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); + server.on(F("/index.css"), HTTP_GET, std::bind(&AmsWebServer::indexCss, this)); server.on(F("/index.js"), HTTP_GET, std::bind(&AmsWebServer::indexJs, this)); server.on(F("/github.svg"), HTTP_GET, std::bind(&AmsWebServer::githubSvg, this)); @@ -50,6 +55,7 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter server.on(F("/monthplot.json"), HTTP_GET, std::bind(&AmsWebServer::monthplotJson, this)); server.on(F("/energyprice.json"), HTTP_GET, std::bind(&AmsWebServer::energyPriceJson, this)); server.on(F("/temperature.json"), HTTP_GET, std::bind(&AmsWebServer::temperatureJson, this)); + server.on(F("/tariff.json"), HTTP_GET, std::bind(&AmsWebServer::tariffJson, this)); server.on(F("/wifiscan.json"), HTTP_GET, std::bind(&AmsWebServer::wifiScanJson, this)); @@ -64,6 +70,10 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter server.on(F("/robots.txt"), HTTP_GET, std::bind(&AmsWebServer::robotstxt, this)); + server.on(F("/mqtt-ca"), HTTP_POST, std::bind(&AmsWebServer::firmwarePost, this), std::bind(&AmsWebServer::mqttCaUpload, this)); + server.on(F("/mqtt-cert"), HTTP_POST, std::bind(&AmsWebServer::firmwarePost, this), std::bind(&AmsWebServer::mqttCertUpload, this)); + server.on(F("/mqtt-key"), HTTP_POST, std::bind(&AmsWebServer::firmwarePost, this), std::bind(&AmsWebServer::mqttKeyUpload, this)); + server.onNotFound(std::bind(&AmsWebServer::notFound, this)); server.begin(); // Web server start @@ -157,55 +167,55 @@ void AmsWebServer::sysinfoJson() { if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /sysinfo.json over http...\n"); DynamicJsonDocument doc(512); - doc["version"] = VERSION; + doc[PSTR("version")] = VERSION; #if defined(CONFIG_IDF_TARGET_ESP32S2) - doc["chip"] = "esp32s2"; + doc[PSTR("chip")] = "esp32s2"; #elif defined(CONFIG_IDF_TARGET_ESP32C3) - doc["chip"] = "esp32c3"; + doc[PSTR("chip")] = "esp32c3"; #elif defined(ESP32) - doc["chip"] = "esp32"; + doc[PSTR("chip")] = "esp32"; #elif defined(ESP8266) - doc["chip"] = "esp8266"; + doc[PSTR("chip")] = "esp8266"; #endif - uint16_t chipId; + uint32_t chipId; #if defined(ESP32) - chipId = ESP.getEfuseMac(); + chipId = ( ESP.getEfuseMac() >> 32 ) % 0xFFFFFFFF; #else chipId = ESP.getChipId(); #endif String chipIdStr = String(chipId, HEX);; - doc["chipId"] = chipIdStr; - doc["mac"] = WiFi.macAddress(); + doc[PSTR("chipId")] = chipIdStr; + doc[PSTR("mac")] = WiFi.macAddress(); SystemConfig sys; config->getSystemConfig(sys); - doc["board"] = sys.boardType; - doc["vndcfg"] = sys.vendorConfigured; - doc["usrcfg"] = sys.userConfigured; - doc["fwconsent"] = sys.dataCollectionConsent; - doc["country"] = sys.country; + doc[PSTR("board")] = sys.boardType; + doc[PSTR("vndcfg")] = sys.vendorConfigured; + doc[PSTR("usrcfg")] = sys.userConfigured; + doc[PSTR("fwconsent")] = sys.dataCollectionConsent; + doc[PSTR("country")] = sys.country; if(sys.userConfigured) { WiFiConfig wifiConfig; config->getWiFiConfig(wifiConfig); - doc["hostname"] = wifiConfig.hostname; + doc[PSTR("hostname")] = wifiConfig.hostname; } else { - doc["hostname"] = "ams-"+chipIdStr; + doc[PSTR("hostname")] = "ams-"+chipIdStr; } - doc["booting"] = performRestart; - doc["upgrading"] = rebootForUpgrade; + doc[PSTR("booting")] = performRestart; + doc[PSTR("upgrading")] = rebootForUpgrade; - doc["net"]["ip"] = WiFi.localIP().toString(); - doc["net"]["mask"] = WiFi.subnetMask().toString(); - doc["net"]["gw"] = WiFi.gatewayIP().toString(); - doc["net"]["dns1"] = WiFi.dnsIP(0).toString(); - doc["net"]["dns2"] = WiFi.dnsIP(1).toString(); + doc[PSTR("net")][PSTR("ip")] = WiFi.localIP().toString(); + doc[PSTR("net")][PSTR("mask")] = WiFi.subnetMask().toString(); + doc[PSTR("net")][PSTR("gw")] = WiFi.gatewayIP().toString(); + doc[PSTR("net")][PSTR("dns1")] = WiFi.dnsIP(0).toString(); + doc[PSTR("net")][PSTR("dns2")] = WiFi.dnsIP(1).toString(); - doc["meter"]["mfg"] = meterState->getMeterType(); - doc["meter"]["model"] = meterState->getMeterModel(); - doc["meter"]["id"] = meterState->getMeterId(); + doc[PSTR("meter")][PSTR("mfg")] = meterState->getMeterType(); + doc[PSTR("meter")][PSTR("model")] = meterState->getMeterModel(); + doc[PSTR("meter")][PSTR("id")] = meterState->getMeterId(); serializeJson(doc, buf, BufferSize); server.send(200, MIME_JSON, buf); @@ -300,7 +310,7 @@ void AmsWebServer::dataJson() { String peaks = ""; for(uint8_t i = 1; i <= ea->getConfig()->hours; i++) { if(!peaks.isEmpty()) peaks += ","; - peaks += String(ea->getPeak(i)); + peaks += String(ea->getPeak(i).value); } snprintf_P(buf, BufferSize, DATA_JSON, @@ -350,6 +360,7 @@ void AmsWebServer::dataJson() { ea->getUseThisMonth(), ea->getCostThisMonth(), ea->getProducedThisMonth(), + eapi == NULL ? "" : eapi->getArea(), (uint32_t) time(nullptr) ); @@ -659,7 +670,7 @@ void AmsWebServer::configurationJson() { return; DynamicJsonDocument doc(2048); - doc["version"] = VERSION; + doc[PSTR("version")] = VERSION; NtpConfig ntpConfig; config->getNtpConfig(ntpConfig); @@ -668,15 +679,11 @@ void AmsWebServer::configurationJson() { WebConfig webConfig; config->getWebConfig(webConfig); - if(ntpConfig.offset == 0 && ntpConfig.summerOffset == 0) - doc["g"]["t"] = "UTC"; - else if(ntpConfig.offset == 360 && ntpConfig.summerOffset == 360) - doc["g"]["t"] = "CET/CEST"; - - doc["g"]["h"] = wifiConfig.hostname; - doc["g"]["s"] = webConfig.security; - doc["g"]["u"] = webConfig.username; - doc["g"]["p"] = strlen(webConfig.password) > 0 ? "***" : ""; + doc[PSTR("g")][PSTR("t")] = ntpConfig.timezone; + doc[PSTR("g")][PSTR("h")] = wifiConfig.hostname; + doc[PSTR("g")][PSTR("s")] = webConfig.security; + doc[PSTR("g")][PSTR("u")] = webConfig.username; + doc[PSTR("g")][PSTR("p")] = strlen(webConfig.password) > 0 ? "***" : ""; bool encen = false; for(uint8_t i = 0; i < 16; i++) { @@ -686,161 +693,161 @@ void AmsWebServer::configurationJson() { } config->getMeterConfig(*meterConfig); - doc["m"]["b"] = meterConfig->baud; - doc["m"]["p"] = meterConfig->parity; - doc["m"]["i"] = meterConfig->invert; - doc["m"]["d"] = meterConfig->distributionSystem; - doc["m"]["f"] = meterConfig->mainFuse; - doc["m"]["r"] = meterConfig->productionCapacity; - doc["m"]["e"]["e"] = encen; - doc["m"]["e"]["k"] = toHex(meterConfig->encryptionKey, 16); - doc["m"]["e"]["a"] = toHex(meterConfig->authenticationKey, 16); - doc["m"]["m"]["e"] = meterConfig->wattageMultiplier > 1 || meterConfig->voltageMultiplier > 1 || meterConfig->amperageMultiplier > 1 || meterConfig->accumulatedMultiplier > 1; - doc["m"]["m"]["w"] = meterConfig->wattageMultiplier / 1000.0; - doc["m"]["m"]["v"] = meterConfig->voltageMultiplier / 1000.0; - doc["m"]["m"]["a"] = meterConfig->amperageMultiplier / 1000.0; - doc["m"]["m"]["c"] = meterConfig->accumulatedMultiplier / 1000.0; + doc[PSTR("m")][PSTR("b")] = meterConfig->baud; + doc[PSTR("m")][PSTR("p")] = meterConfig->parity; + doc[PSTR("m")][PSTR("i")] = meterConfig->invert; + doc[PSTR("m")][PSTR("d")] = meterConfig->distributionSystem; + doc[PSTR("m")][PSTR("f")] = meterConfig->mainFuse; + doc[PSTR("m")][PSTR("r")] = meterConfig->productionCapacity; + doc[PSTR("m")][PSTR("e")][PSTR("e")] = encen; + doc[PSTR("m")][PSTR("e")][PSTR("k")] = toHex(meterConfig->encryptionKey, 16); + doc[PSTR("m")][PSTR("e")][PSTR("a")] = toHex(meterConfig->authenticationKey, 16); + doc[PSTR("m")][PSTR("m")][PSTR("e")] = meterConfig->wattageMultiplier > 1 || meterConfig->voltageMultiplier > 1 || meterConfig->amperageMultiplier > 1 || meterConfig->accumulatedMultiplier > 1; + doc[PSTR("m")][PSTR("m")][PSTR("w")] = meterConfig->wattageMultiplier / 1000.0; + doc[PSTR("m")][PSTR("m")][PSTR("v")] = meterConfig->voltageMultiplier / 1000.0; + doc[PSTR("m")][PSTR("m")][PSTR("a")] = meterConfig->amperageMultiplier / 1000.0; + doc[PSTR("m")][PSTR("m")][PSTR("c")] = meterConfig->accumulatedMultiplier / 1000.0; EnergyAccountingConfig eac; config->getEnergyAccountingConfig(eac); - doc["t"]["t"][0] = eac.thresholds[0]; - doc["t"]["t"][1] = eac.thresholds[1]; - doc["t"]["t"][2] = eac.thresholds[2]; - doc["t"]["t"][3] = eac.thresholds[3]; - doc["t"]["t"][4] = eac.thresholds[4]; - doc["t"]["t"][5] = eac.thresholds[5]; - doc["t"]["t"][6] = eac.thresholds[6]; - doc["t"]["t"][7] = eac.thresholds[7]; - doc["t"]["t"][8] = eac.thresholds[8]; - doc["t"]["t"][9] = eac.thresholds[9]; - doc["t"]["h"] = eac.hours; + doc[PSTR("t")][PSTR("t")][0] = eac.thresholds[0]; + doc[PSTR("t")][PSTR("t")][1] = eac.thresholds[1]; + doc[PSTR("t")][PSTR("t")][2] = eac.thresholds[2]; + doc[PSTR("t")][PSTR("t")][3] = eac.thresholds[3]; + doc[PSTR("t")][PSTR("t")][4] = eac.thresholds[4]; + doc[PSTR("t")][PSTR("t")][5] = eac.thresholds[5]; + doc[PSTR("t")][PSTR("t")][6] = eac.thresholds[6]; + doc[PSTR("t")][PSTR("t")][7] = eac.thresholds[7]; + doc[PSTR("t")][PSTR("t")][8] = eac.thresholds[8]; + doc[PSTR("t")][PSTR("t")][9] = eac.thresholds[9]; + doc[PSTR("t")][PSTR("h")] = eac.hours; - doc["w"]["s"] = wifiConfig.ssid; - doc["w"]["p"] = strlen(wifiConfig.psk) > 0 ? "***" : ""; - doc["w"]["w"] = wifiConfig.power / 10.0; - doc["w"]["z"] = wifiConfig.sleep; + doc[PSTR("w")][PSTR("s")] = wifiConfig.ssid; + doc[PSTR("w")][PSTR("p")] = strlen(wifiConfig.psk) > 0 ? "***" : ""; + doc[PSTR("w")][PSTR("w")] = wifiConfig.power / 10.0; + doc[PSTR("w")][PSTR("z")] = wifiConfig.sleep; - doc["n"]["m"] = strlen(wifiConfig.ip) > 0 ? "static" : "dhcp"; - doc["n"]["i"] = wifiConfig.ip; - doc["n"]["s"] = wifiConfig.subnet; - doc["n"]["g"] = wifiConfig.gateway; - doc["n"]["d1"] = wifiConfig.dns1; - doc["n"]["d2"] = wifiConfig.dns2; - doc["n"]["d"] = wifiConfig.mdns; - doc["n"]["n1"] = ntpConfig.server; - doc["n"]["h"] = ntpConfig.dhcp; + doc[PSTR("n")][PSTR("m")] = strlen(wifiConfig.ip) > 0 ? "static" : "dhcp"; + doc[PSTR("n")][PSTR("i")] = wifiConfig.ip; + doc[PSTR("n")][PSTR("s")] = wifiConfig.subnet; + doc[PSTR("n")][PSTR("g")] = wifiConfig.gateway; + doc[PSTR("n")][PSTR("d1")] = wifiConfig.dns1; + doc[PSTR("n")][PSTR("d2")] = wifiConfig.dns2; + doc[PSTR("n")][PSTR("d")] = wifiConfig.mdns; + doc[PSTR("n")][PSTR("n1")] = ntpConfig.server; + doc[PSTR("n")][PSTR("h")] = ntpConfig.dhcp; MqttConfig mqttConfig; config->getMqttConfig(mqttConfig); - doc["q"]["h"] = mqttConfig.host; - doc["q"]["p"] = mqttConfig.port; - doc["q"]["u"] = mqttConfig.username; - doc["q"]["a"] = strlen(mqttConfig.password) > 0 ? "***" : ""; - doc["q"]["c"] = mqttConfig.clientId; - doc["q"]["b"] = mqttConfig.publishTopic; - doc["q"]["m"] = mqttConfig.payloadFormat; - doc["q"]["s"]["e"] = mqttConfig.ssl; + doc[PSTR("q")][PSTR("h")] = mqttConfig.host; + doc[PSTR("q")][PSTR("p")] = mqttConfig.port; + doc[PSTR("q")][PSTR("u")] = mqttConfig.username; + doc[PSTR("q")][PSTR("a")] = strlen(mqttConfig.password) > 0 ? "***" : ""; + doc[PSTR("q")][PSTR("c")] = mqttConfig.clientId; + doc[PSTR("q")][PSTR("b")] = mqttConfig.publishTopic; + doc[PSTR("q")][PSTR("m")] = mqttConfig.payloadFormat; + doc[PSTR("q")][PSTR("s")][PSTR("e")] = mqttConfig.ssl; if(LittleFS.begin()) { - doc["q"]["s"]["c"] = LittleFS.exists(FILE_MQTT_CA); - doc["q"]["s"]["r"] = LittleFS.exists(FILE_MQTT_CERT); - doc["q"]["s"]["k"] = LittleFS.exists(FILE_MQTT_KEY); + doc[PSTR("q")][PSTR("s")][PSTR("c")] = LittleFS.exists(FILE_MQTT_CA); + doc[PSTR("q")][PSTR("s")][PSTR("r")] = LittleFS.exists(FILE_MQTT_CERT); + doc[PSTR("q")][PSTR("s")][PSTR("k")] = LittleFS.exists(FILE_MQTT_KEY); LittleFS.end(); } else { - doc["q"]["s"]["c"] = false; - doc["q"]["s"]["r"] = false; - doc["q"]["s"]["k"] = false; + doc[PSTR("q")][PSTR("s")][PSTR("c")] = false; + doc[PSTR("q")][PSTR("s")][PSTR("r")] = false; + doc[PSTR("q")][PSTR("s")][PSTR("k")] = false; } EntsoeConfig entsoe; config->getEntsoeConfig(entsoe); - doc["p"]["e"] = strlen(entsoe.token) > 0; - doc["p"]["t"] = entsoe.token; - doc["p"]["r"] = entsoe.area; - doc["p"]["c"] = entsoe.currency; - doc["p"]["m"] = entsoe.multiplier / 1000.0; + doc[PSTR("p")][PSTR("e")] = strlen(entsoe.token) > 0; + doc[PSTR("p")][PSTR("t")] = entsoe.token; + doc[PSTR("p")][PSTR("r")] = entsoe.area; + doc[PSTR("p")][PSTR("c")] = entsoe.currency; + doc[PSTR("p")][PSTR("m")] = entsoe.multiplier / 1000.0; DebugConfig debugConfig; config->getDebugConfig(debugConfig); - doc["d"]["s"] = debugConfig.serial; - doc["d"]["t"] = debugConfig.telnet; - doc["d"]["l"] = debugConfig.level; + doc[PSTR("d")][PSTR("s")] = debugConfig.serial; + doc[PSTR("d")][PSTR("t")] = debugConfig.telnet; + doc[PSTR("d")][PSTR("l")] = debugConfig.level; GpioConfig gpioConfig; config->getGpioConfig(gpioConfig); if(gpioConfig.hanPin == 0xff) - doc["i"]["h"] = nullptr; + doc[PSTR("i")][PSTR("h")] = nullptr; else - doc["i"]["h"] = gpioConfig.hanPin; + doc[PSTR("i")][PSTR("h")] = gpioConfig.hanPin; if(gpioConfig.apPin == 0xff) - doc["i"]["a"] = nullptr; + doc[PSTR("i")][PSTR("a")] = nullptr; else - doc["i"]["a"] = gpioConfig.apPin; + doc[PSTR("i")][PSTR("a")] = gpioConfig.apPin; if(gpioConfig.ledPin == 0xff) - doc["i"]["l"]["p"] = nullptr; + doc[PSTR("i")][PSTR("l")][PSTR("p")] = nullptr; else - doc["i"]["l"]["p"] = gpioConfig.ledPin; + doc[PSTR("i")][PSTR("l")][PSTR("p")] = gpioConfig.ledPin; - doc["i"]["l"]["i"] = gpioConfig.ledInverted; + doc[PSTR("i")][PSTR("l")][PSTR("i")] = gpioConfig.ledInverted; if(gpioConfig.ledPinRed == 0xff) - doc["i"]["r"]["r"] = nullptr; + doc[PSTR("i")][PSTR("r")][PSTR("r")] = nullptr; else - doc["i"]["r"]["r"] = gpioConfig.ledPinRed; + doc[PSTR("i")][PSTR("r")][PSTR("r")] = gpioConfig.ledPinRed; if(gpioConfig.ledPinGreen == 0xff) - doc["i"]["r"]["g"] = nullptr; + doc[PSTR("i")][PSTR("r")][PSTR("g")] = nullptr; else - doc["i"]["r"]["g"] = gpioConfig.ledPinGreen; + doc[PSTR("i")][PSTR("r")][PSTR("g")] = gpioConfig.ledPinGreen; if(gpioConfig.ledPinBlue == 0xff) - doc["i"]["r"]["b"] = nullptr; + doc[PSTR("i")][PSTR("r")][PSTR("b")] = nullptr; else - doc["i"]["r"]["b"] = gpioConfig.ledPinBlue; + doc[PSTR("i")][PSTR("r")][PSTR("b")] = gpioConfig.ledPinBlue; - doc["i"]["r"]["i"] = gpioConfig.ledRgbInverted; + doc[PSTR("i")][PSTR("r")][PSTR("i")] = gpioConfig.ledRgbInverted; if(gpioConfig.tempSensorPin == 0xff) - doc["i"]["t"]["d"] = nullptr; + doc[PSTR("i")][PSTR("t")][PSTR("d")] = nullptr; else - doc["i"]["t"]["d"] = gpioConfig.tempSensorPin; + doc[PSTR("i")][PSTR("t")][PSTR("d")] = gpioConfig.tempSensorPin; if(gpioConfig.tempAnalogSensorPin == 0xff) - doc["i"]["t"]["a"] = nullptr; + doc[PSTR("i")][PSTR("t")][PSTR("a")] = nullptr; else - doc["i"]["t"]["a"] = gpioConfig.tempAnalogSensorPin; + doc[PSTR("i")][PSTR("t")][PSTR("a")] = gpioConfig.tempAnalogSensorPin; if(gpioConfig.vccPin == 0xff) - doc["i"]["v"]["p"] = nullptr; + doc[PSTR("i")][PSTR("v")][PSTR("p")] = nullptr; else - doc["i"]["v"]["p"] = gpioConfig.vccPin; + doc[PSTR("i")][PSTR("v")][PSTR("p")] = gpioConfig.vccPin; if(gpioConfig.vccOffset == 0) - doc["i"]["v"]["o"] = nullptr; + doc[PSTR("i")][PSTR("v")][PSTR("o")] = nullptr; else - doc["i"]["v"]["o"] = gpioConfig.vccOffset / 100.0; + doc[PSTR("i")][PSTR("v")][PSTR("o")] = gpioConfig.vccOffset / 100.0; if(gpioConfig.vccMultiplier == 0) - doc["i"]["v"]["m"] = nullptr; + doc[PSTR("i")][PSTR("v")][PSTR("m")] = nullptr; else - doc["i"]["v"]["m"] = gpioConfig.vccMultiplier / 1000.0; + doc[PSTR("i")][PSTR("v")][PSTR("m")] = gpioConfig.vccMultiplier / 1000.0; if(gpioConfig.vccResistorVcc == 0) - doc["i"]["v"]["d"]["v"] = nullptr; + doc[PSTR("i")][PSTR("v")][PSTR("d")][PSTR("v")] = nullptr; else - doc["i"]["v"]["d"]["v"] = gpioConfig.vccResistorVcc; + doc[PSTR("i")][PSTR("v")][PSTR("d")][PSTR("v")] = gpioConfig.vccResistorVcc; if(gpioConfig.vccResistorGnd == 0) - doc["i"]["v"]["d"]["g"] = nullptr; + doc[PSTR("i")][PSTR("v")][PSTR("d")][PSTR("g")] = nullptr; else - doc["i"]["v"]["d"]["g"] = gpioConfig.vccResistorGnd; + doc[PSTR("i")][PSTR("v")][PSTR("d")][PSTR("g")] = gpioConfig.vccResistorGnd; if(gpioConfig.vccBootLimit == 0) - doc["i"]["v"]["b"] = nullptr; + doc[PSTR("i")][PSTR("v")][PSTR("b")] = nullptr; else - doc["i"]["v"]["b"] = gpioConfig.vccBootLimit / 10.0; + doc[PSTR("i")][PSTR("v")][PSTR("b")] = gpioConfig.vccBootLimit / 10.0; serializeJson(doc, buf, BufferSize); server.send(200, MIME_JSON, buf); @@ -1094,18 +1101,23 @@ void AmsWebServer::handleSave() { if(server.hasArg(F("q")) && server.arg(F("q")) == F("true")) { if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received MQTT config")); MqttConfig mqtt; + config->getMqttConfig(mqtt); if(server.hasArg(F("qh")) && !server.arg(F("qh")).isEmpty()) { strcpy(mqtt.host, server.arg(F("qh")).c_str()); strcpy(mqtt.clientId, server.arg(F("qc")).c_str()); strcpy(mqtt.publishTopic, server.arg(F("qb")).c_str()); strcpy(mqtt.subscribeTopic, server.arg(F("qr")).c_str()); strcpy(mqtt.username, server.arg(F("qu")).c_str()); - String pass = server.arg(F("qp")); + String pass = server.arg(F("qa")); if(!pass.equals("***")) { strcpy(mqtt.password, pass.c_str()); } mqtt.payloadFormat = server.arg(F("qm")).toInt(); + #if defined(ESP8266) + mqtt.ssl = false; + #else mqtt.ssl = server.arg(F("qs")) == F("true"); + #endif mqtt.port = server.arg(F("qp")).toInt(); if(mqtt.port == 0) { @@ -1156,14 +1168,7 @@ void AmsWebServer::handleSave() { NtpConfig ntp; config->getNtpConfig(ntp); - String tz = server.arg(F("gt")); - if(tz.equals("UTC")) { - ntp.offset = 0; - ntp.summerOffset = 0; - } else if(tz.equals("CET/CEST")) { - ntp.offset = 360; - ntp.summerOffset = 360; - } + strcpy(ntp.timezone, server.arg(F("gt")).c_str()); config->setNtpConfig(ntp); } @@ -1251,22 +1256,23 @@ void AmsWebServer::handleSave() { if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Saving configuration now...")); - DynamicJsonDocument doc(128); if (config->save()) { - doc["success"] = success; if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Successfully saved.")); if(config->isWifiChanged() || performRestart) { performRestart = true; - doc["reboot"] = true; } else { - doc["reboot"] = false; hw->setup(gpioConfig, config); } } else { - doc["success"] = false; - doc["reboot"] = false; + success = false; } - serializeJson(doc, buf, BufferSize); + + snprintf_P(buf, BufferSize, RESPONSE_JSON, + success ? "true" : "false", + "", + performRestart ? "true" : "false" + ); + server.setContentLength(strlen(buf)); server.send(200, MIME_JSON, buf); server.handleClient(); @@ -1300,7 +1306,7 @@ void AmsWebServer::reboot() { if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /reboot over http...\n"); DynamicJsonDocument doc(128); - doc["reboot"] = true; + doc[PSTR("reboot")] = true; serializeJson(doc, buf, BufferSize); server.send(200, MIME_JSON, buf); @@ -1321,14 +1327,18 @@ void AmsWebServer::reboot() { void AmsWebServer::upgrade() { if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /upgrade over http...\n"); + if(!checkSecurity(1)) + return; + SystemConfig sys; config->getSystemConfig(sys); - DynamicJsonDocument doc(128); - doc["success"] = sys.dataCollectionConsent == 1; - doc["reboot"] = sys.dataCollectionConsent == 1; - - serializeJson(doc, buf, BufferSize); + snprintf_P(buf, BufferSize, RESPONSE_JSON, + sys.dataCollectionConsent == 1 ? "true" : "false", + "", + sys.dataCollectionConsent == 1 ? "true" : "false" + ); + server.setContentLength(strlen(buf)); server.send(200, MIME_JSON, buf); if(sys.dataCollectionConsent == 1) { @@ -1460,7 +1470,13 @@ HTTPUpload& AmsWebServer::uploadFile(const char* path) { LittleFS.end(); if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(PSTR("An Error has occurred while writing file")); - server.send_P(500, MIME_JSON, PSTR("{ \"success\": false, \"message\": \"Unable to upload\" }")); + snprintf_P(buf, BufferSize, RESPONSE_JSON, + "false", + "Unable to upload", + "false" + ); + server.setContentLength(strlen(buf)); + server.send(500, MIME_JSON, buf); } } } else if(upload.status == UPLOAD_FILE_END) { @@ -1469,7 +1485,13 @@ HTTPUpload& AmsWebServer::uploadFile(const char* path) { file.close(); // LittleFS.end(); } else { - server.send_P(500, MIME_JSON, PSTR("{ \"success\": false, \"message\": \"Unable to upload\" }")); + snprintf_P(buf, BufferSize, RESPONSE_JSON, + "false", + "Unable to upload", + "false" + ); + server.setContentLength(strlen(buf)); + server.send(500, MIME_JSON, buf); } } return upload; @@ -1496,11 +1518,12 @@ void AmsWebServer::factoryResetPost() { success = true; } - DynamicJsonDocument doc(128); - doc["success"] = success; - doc["reboot"] = success; - - serializeJson(doc, buf, BufferSize); + snprintf_P(buf, BufferSize, RESPONSE_JSON, + success ? "true" : "false", + "", + "true" + ); + server.setContentLength(strlen(buf)); server.send(200, MIME_JSON, buf); server.handleClient(); @@ -1518,3 +1541,85 @@ void AmsWebServer::factoryResetPost() { void AmsWebServer::robotstxt() { server.send_P(200, MIME_HTML, PSTR("User-agent: *\nDisallow: /\n")); } + +void AmsWebServer::mqttCaUpload() { + if(!checkSecurity(1)) + return; + + uploadFile(FILE_MQTT_CA); + HTTPUpload& upload = server.upload(); + if(upload.status == UPLOAD_FILE_END) { + server.sendHeader(HEADER_LOCATION,F("/configuration")); + server.send(303); + + MqttConfig mqttConfig; + if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) { + config->setMqttChanged(); + } + } +} + +void AmsWebServer::mqttCertUpload() { + if(!checkSecurity(1)) + return; + + uploadFile(FILE_MQTT_CERT); + HTTPUpload& upload = server.upload(); + if(upload.status == UPLOAD_FILE_END) { + server.sendHeader(HEADER_LOCATION,F("/configuration")); + server.send(303); + MqttConfig mqttConfig; + if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) { + config->setMqttChanged(); + } + } +} + +void AmsWebServer::mqttKeyUpload() { + if(!checkSecurity(1)) + return; + + uploadFile(FILE_MQTT_KEY); + HTTPUpload& upload = server.upload(); + if(upload.status == UPLOAD_FILE_END) { + server.sendHeader(HEADER_LOCATION,F("/configuration")); + server.send(303); + MqttConfig mqttConfig; + if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) { + config->setMqttChanged(); + } + } +} + +void AmsWebServer::tariffJson() { + if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /tariff.json over http...\n"); + + server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); + server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); + server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); + + if(!checkSecurity(1)) + return; + + EnergyAccountingConfig* eac = ea->getConfig(); + EnergyAccountingData data = ea->getData(); + + DynamicJsonDocument doc(512); + JsonArray thresholds = doc.createNestedArray(PSTR("t")); + for(uint8_t x = 0;x < 10; x++) { + thresholds.add(eac->thresholds[x]); + } + JsonArray peaks = doc.createNestedArray(PSTR("p")); + for(uint8_t x = 0;x < min((uint8_t) 5, eac->hours); x++) { + JsonObject p = peaks.createNestedObject(); + EnergyAccountingPeak peak = ea->getPeak(x); + p["d"] = peak.day; + p["v"] = peak.value / 100.0; + } + doc["c"] = ea->getCurrentThreshold(); + doc["m"] = ea->getMonthMax(); + + serializeJson(doc, buf, BufferSize); + server.send(200, MIME_JSON, buf); + +} \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 0a29ccbc..c11e0b3e 100755 --- a/platformio.ini +++ b/platformio.ini @@ -28,7 +28,7 @@ extra_scripts = ${common.extra_scripts} # Sticking to v2.0.3 because of #298 [env:esp32] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.3/platform-espressif32-2.0.3.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.3/platform-espressif32-2.0.5.3.zip framework = arduino board = esp32dev board_build.f_cpu = 160000000L @@ -42,27 +42,26 @@ extra_scripts = ${common.extra_scripts} # https://github.com/Jason2866/esp32-arduino-lib-builder [env:esp32s2] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.3/platform-espressif32-2.0.3.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.3 +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.3/platform-espressif32-2.0.5.3.zip framework = arduino -board = esp32dev +board = esp32-s2-saola-1 board_build.mcu = esp32s2 board_build.variant = esp32s2 board_build.flash_mode = qio board_build.f_cpu = 160000000L board_build.f_flash = 40000000L -build_flags = -D WEBSOCKET_DISABLED=1 +build_flags = -D WEBSOCKET_DISABLED=1 -fexceptions lib_ldf_mode = off lib_deps = ${esp32.lib_deps} lib_ignore = ${common.lib_ignore} extra_scripts = ${common.extra_scripts} [env:esp32solo] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v.2.0.3/platform-espressif32-solo1-v.2.0.3.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.3/platform-espressif32-2.0.5.3.zip framework = arduino -board = esp32dev +board = esp32-solo1 board_build.f_cpu = 160000000L -build_flags = -D WEBSOCKET_DISABLED=1 -fexceptions +build_flags = -D WEBSOCKET_DISABLED=1 -DFRAMEWORK_ARDUINO_SOLO1 -fexceptions lib_ldf_mode = off lib_deps = ${esp32.lib_deps} lib_ignore = ${common.lib_ignore} diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index 05a8205a..8c112d51 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -72,6 +72,7 @@ ADC_MODE(ADC_VCC); #include "LNG.h" #include "ams/DataParsers.h" +#include "Timezones.h" uint8_t commonBuffer[BUF_SIZE_COMMON]; uint8_t hanBuffer[BUF_SIZE_HAN]; @@ -358,12 +359,10 @@ void setup() { NtpConfig ntp; if(config.getNtpConfig(ntp)) { - configTime(ntp.offset*10, ntp.summerOffset*10, ntp.enable ? strlen(ntp.server) > 0 ? ntp.server : (char*) F("pool.ntp.org") : (char*) F("")); // Add NTP server by default if none is configured + tz = resolveTimezone(ntp.timezone); + configTime(tz->toLocal(0), tz->toLocal(JULY1970)-JULY1970, ntp.enable ? strlen(ntp.server) > 0 ? ntp.server : (char*) F("pool.ntp.org") : (char*) F("")); // Add NTP server by default if none is configured sntp_servermode_dhcp(ntp.enable && ntp.dhcp ? 1 : 0); ntpEnabled = ntp.enable; - TimeChangeRule std = {"STD", Last, Sun, Oct, 3, ntp.offset / 6}; - TimeChangeRule dst = {"DST", Last, Sun, Mar, 2, (ntp.offset + ntp.summerOffset) / 6}; - tz = new Timezone(dst, std); ws.setTimezone(tz); ds.setTimezone(tz); ea.setTimezone(tz); @@ -489,14 +488,11 @@ void loop() { if(config.isNtpChanged()) { NtpConfig ntp; if(config.getNtpConfig(ntp)) { - configTime(ntp.offset*10, ntp.summerOffset*10, ntp.enable ? ntp.server : ""); + tz = resolveTimezone(ntp.timezone); + configTime(tz->toLocal(0), tz->toLocal(JULY1970)-JULY1970, ntp.enable ? strlen(ntp.server) > 0 ? ntp.server : (char*) F("pool.ntp.org") : (char*) F("")); // Add NTP server by default if none is configured sntp_servermode_dhcp(ntp.enable && ntp.dhcp ? 1 : 0); ntpEnabled = ntp.enable; - if(tz != NULL) delete tz; - TimeChangeRule std = {"STD", Last, Sun, Oct, 3, ntp.offset / 6}; - TimeChangeRule dst = {"DST", Last, Sun, Mar, 2, (ntp.offset + ntp.summerOffset) / 6}; - tz = new Timezone(dst, std); ws.setTimezone(tz); ds.setTimezone(tz); ea.setTimezone(tz); @@ -1324,12 +1320,19 @@ void MQTT_connect() { break; } + time_t epoch = time(nullptr); if(mqttConfig.ssl) { + if(epoch < BUILD_EPOCH) { + debugI("NTP not ready for MQTT SSL"); + return; + } debugI("MQTT SSL is configured (%dkb free heap)", ESP.getFreeHeap()); if(mqttSecureClient == NULL) { mqttSecureClient = new WiFiClientSecure(); #if defined(ESP8266) mqttSecureClient->setBufferSizes(512, 512); + debugD("ESP8266 firmware does not have enough memory..."); + return; #endif if(LittleFS.begin()) { @@ -1345,40 +1348,43 @@ void MQTT_connect() { mqttSecureClient->loadCACert(file, file.size()); #endif file.close(); + + if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) { + #if defined(ESP8266) + debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap()); + file = LittleFS.open(FILE_MQTT_CERT, (char*) "r"); + BearSSL::X509List *serverCertList = new BearSSL::X509List(file); + file.close(); + + debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap()); + file = LittleFS.open(FILE_MQTT_KEY, (char*) "r"); + BearSSL::PrivateKey *serverPrivKey = new BearSSL::PrivateKey(file); + file.close(); + + debugD("Setting client certificates (%dkb free heap)", ESP.getFreeHeap()); + mqttSecureClient->setClientRSACert(serverCertList, serverPrivKey); + #elif defined(ESP32) + debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap()); + file = LittleFS.open(FILE_MQTT_CERT, (char*) "r"); + mqttSecureClient->loadCertificate(file, file.size()); + file.close(); + + debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap()); + file = LittleFS.open(FILE_MQTT_KEY, (char*) "r"); + mqttSecureClient->loadPrivateKey(file, file.size()); + file.close(); + #endif + mqttClient = mqttSecureClient; + } } - if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) { - #if defined(ESP8266) - debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap()); - file = LittleFS.open(FILE_MQTT_CERT, (char*) "r"); - BearSSL::X509List *serverCertList = new BearSSL::X509List(file); - file.close(); - - debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap()); - file = LittleFS.open(FILE_MQTT_KEY, (char*) "r"); - BearSSL::PrivateKey *serverPrivKey = new BearSSL::PrivateKey(file); - file.close(); - - debugD("Setting client certificates (%dkb free heap)", ESP.getFreeHeap()); - mqttSecureClient->setClientRSACert(serverCertList, serverPrivKey); - #elif defined(ESP32) - debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap()); - file = LittleFS.open(FILE_MQTT_CERT, (char*) "r"); - mqttSecureClient->loadCertificate(file, file.size()); - file.close(); - - debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap()); - file = LittleFS.open(FILE_MQTT_KEY, (char*) "r"); - mqttSecureClient->loadPrivateKey(file, file.size()); - file.close(); - #endif - } LittleFS.end(); debugD("MQTT SSL setup complete (%dkb free heap)", ESP.getFreeHeap()); } } - mqttClient = mqttSecureClient; - } else if(mqttClient == NULL) { + } + + if(mqttClient == NULL) { mqttClient = new WiFiClient(); } @@ -1390,7 +1396,6 @@ void MQTT_connect() { #if defined(ESP8266) if(mqttSecureClient) { - time_t epoch = time(nullptr); debugD("Setting NTP time %lld for secure MQTT connection", epoch); mqttSecureClient->setX509Time(epoch); } @@ -1425,7 +1430,6 @@ void MQTT_connect() { yield(); } - void configFileParse() { debugD("Parsing config file"); @@ -1623,15 +1627,12 @@ void configFileParse() { } else if(strncmp_P(buf, PSTR("ntpDhcp "), 8) == 0) { if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; }; ntp.dhcp = String(buf+8).toInt() == 1; - } else if(strncmp_P(buf, PSTR("ntpOffset "), 10) == 0) { - if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; }; - ntp.offset = String(buf+10).toInt() / 10; - } else if(strncmp_P(buf, PSTR("ntpSummerOffset "), 16) == 0) { - if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; }; - ntp.summerOffset = String(buf+16).toInt() / 10; } else if(strncmp_P(buf, PSTR("ntpServer "), 10) == 0) { if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; }; memcpy(ntp.server, buf+10, size-10); + } else if(strncmp_P(buf, PSTR("ntpTimezone "), 12) == 0) { + if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; }; + memcpy(ntp.timezone, buf+12, size-12); } else if(strncmp_P(buf, PSTR("entsoeToken "), 12) == 0) { if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; }; memcpy(entsoe.token, buf+12, size-12); @@ -1768,4 +1769,4 @@ void configFileParse() { if(sDs) ds.save(); if(sEa) ea.save(); config.save(); -} +} \ No newline at end of file