diff --git a/lib/AmsConfiguration/include/AmsConfiguration.h b/lib/AmsConfiguration/include/AmsConfiguration.h index cbbcb8f2..c66885ac 100644 --- a/lib/AmsConfiguration/include/AmsConfiguration.h +++ b/lib/AmsConfiguration/include/AmsConfiguration.h @@ -4,7 +4,7 @@ #include "Arduino.h" #define EEPROM_SIZE 1024*3 -#define EEPROM_CHECK_SUM 100 // Used to check if config is stored. Change if structure changes +#define EEPROM_CHECK_SUM 101 // Used to check if config is stored. Change if structure changes #define EEPROM_CLEARED_INDICATOR 0xFC #define EEPROM_CONFIG_ADDRESS 0 #define EEPROM_TEMP_CONFIG_ADDRESS 2048 @@ -95,6 +95,23 @@ struct WebConfig { }; // 129 struct MeterConfig { + uint32_t baud; + uint8_t parity; + bool invert; + uint8_t distributionSystem; + uint16_t mainFuse; + uint16_t productionCapacity; + uint8_t encryptionKey[16]; + uint8_t authenticationKey[16]; + uint32_t wattageMultiplier; + uint32_t voltageMultiplier; + uint32_t amperageMultiplier; + uint32_t accumulatedMultiplier; + uint8_t source; + uint8_t parser; +}; // 52 + +struct MeterConfig100 { uint32_t baud; uint8_t parity; bool invert; @@ -302,6 +319,7 @@ private: bool relocateConfig94(); // 2.1.0 bool relocateConfig95(); // 2.1.4 bool relocateConfig96(); // 2.1.14 + bool relocateConfig100(); // 2.2-dev void saveToFs(); bool loadFromFs(uint8_t version); diff --git a/lib/AmsConfiguration/src/AmsConfiguration.cpp b/lib/AmsConfiguration/src/AmsConfiguration.cpp index 50b07ea1..f88f7ea1 100644 --- a/lib/AmsConfiguration/src/AmsConfiguration.cpp +++ b/lib/AmsConfiguration/src/AmsConfiguration.cpp @@ -230,6 +230,8 @@ void AmsConfiguration::clearMeter(MeterConfig& config) { config.voltageMultiplier = 0; config.amperageMultiplier = 0; config.accumulatedMultiplier = 0; + config.source = 1; // Serial + config.parser = 0; // Auto } bool AmsConfiguration::isMeterChanged() { @@ -694,6 +696,14 @@ bool AmsConfiguration::hasConfig() { configVersion = 0; return false; } + case 100: + configVersion = -1; // Prevent loop + if(relocateConfig100()) { + configVersion = 101; + } else { + configVersion = 0; + return false; + } case EEPROM_CHECK_SUM: return true; default: @@ -848,9 +858,13 @@ bool AmsConfiguration::relocateConfig96() { SystemConfig sys; EEPROM.get(CONFIG_SYSTEM_START, sys); - #if defined(ESP8266) MeterConfig meter; EEPROM.get(CONFIG_METER_START, meter); + meter.source = 1; // Serial + meter.parser = 0; // Auto + EEPROM.put(CONFIG_METER_START, meter); + + #if defined(ESP8266) GpioConfig gpio; EEPROM.get(CONFIG_GPIO_START, gpio); @@ -912,6 +926,35 @@ bool AmsConfiguration::relocateConfig96() { return ret; } +bool AmsConfiguration::relocateConfig100() { + EEPROM.begin(EEPROM_SIZE); + + MeterConfig100 meter100; + EEPROM.get(CONFIG_METER_START, meter100); + MeterConfig meter; + meter.baud = meter100.baud; + meter.parity = meter100.parity; + meter.invert = meter100.invert; + meter.distributionSystem = meter100.distributionSystem; + meter.mainFuse = meter100.mainFuse; + meter.productionCapacity = meter100.productionCapacity; + memcpy(meter.encryptionKey, meter100.encryptionKey, 16); + memcpy(meter.authenticationKey, meter100.authenticationKey, 16); + meter.wattageMultiplier = meter100.wattageMultiplier; + meter.voltageMultiplier = meter100.voltageMultiplier; + meter.amperageMultiplier = meter100.amperageMultiplier; + meter.accumulatedMultiplier = meter100.accumulatedMultiplier; + meter.source = meter100.source; + meter.parser = meter100.parser; + + EEPROM.put(CONFIG_METER_START, meter); + + EEPROM.put(EEPROM_CONFIG_ADDRESS, 101); + bool ret = EEPROM.commit(); + EEPROM.end(); + return ret; +} + bool AmsConfiguration::save() { EEPROM.begin(EEPROM_SIZE); EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM); diff --git a/lib/AmsDataStorage/include/AmsDataStorage.h b/lib/AmsDataStorage/include/AmsDataStorage.h index f542bc99..46e1bdb9 100644 --- a/lib/AmsDataStorage/include/AmsDataStorage.h +++ b/lib/AmsDataStorage/include/AmsDataStorage.h @@ -12,7 +12,8 @@ struct DayDataPoints { uint32_t activeImport; uint32_t activeExport; uint16_t hExport[24]; -}; // 112 bytes + uint8_t accuracy; +}; // 113 bytes struct MonthDataPoints { uint8_t version; @@ -21,17 +22,18 @@ struct MonthDataPoints { uint32_t activeImport; uint32_t activeExport; uint16_t dExport[31]; -}; // 141 bytes + uint8_t accuracy; +}; // 142 bytes class AmsDataStorage { public: AmsDataStorage(RemoteDebug*); void setTimezone(Timezone*); bool update(AmsData*); - int32_t getHourImport(uint8_t); - int32_t getHourExport(uint8_t); - int32_t getDayImport(uint8_t); - int32_t getDayExport(uint8_t); + uint32_t getHourImport(uint8_t); + uint32_t getHourExport(uint8_t); + uint32_t getDayImport(uint8_t); + uint32_t getDayExport(uint8_t); bool load(); bool save(); @@ -40,6 +42,11 @@ public: MonthDataPoints getMonthData(); bool setMonthData(MonthDataPoints&); + uint8_t getDayAccuracy(); + void setDayAccuracy(uint8_t); + uint8_t getMonthAccuracy(); + void setMonthAccuracy(uint8_t); + bool isHappy(); bool isDayHappy(); bool isMonthHappy(); @@ -50,19 +57,21 @@ private: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10 }; MonthDataPoints month = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10 }; RemoteDebug* debugger; - void setHourImport(uint8_t, int32_t); - void setHourExport(uint8_t, int32_t); - void setDayImport(uint8_t, int32_t); - void setDayExport(uint8_t, int32_t); + void setHourImport(uint8_t, uint32_t); + void setHourExport(uint8_t, uint32_t); + void setDayImport(uint8_t, uint32_t); + void setDayExport(uint8_t, uint32_t); }; #endif diff --git a/lib/AmsDataStorage/src/AmsDataStorage.cpp b/lib/AmsDataStorage/src/AmsDataStorage.cpp index 5031f2e1..66c012e4 100644 --- a/lib/AmsDataStorage/src/AmsDataStorage.cpp +++ b/lib/AmsDataStorage/src/AmsDataStorage.cpp @@ -5,8 +5,10 @@ #include "version.h" AmsDataStorage::AmsDataStorage(RemoteDebug* debugger) { - day.version = 4; - month.version = 5; + day.version = 5; + day.accuracy = 1; + month.version = 6; + month.accuracy = 1; this->debugger = debugger; } @@ -237,44 +239,160 @@ bool AmsDataStorage::update(AmsData* data) { return ret; } -void AmsDataStorage::setHourImport(uint8_t hour, int32_t val) { +void AmsDataStorage::setHourImport(uint8_t hour, uint32_t val) { if(hour < 0 || hour > 24) return; - day.hImport[hour] = val / 10; + + uint8_t accuracy = day.accuracy; + uint32_t update = val / pow(10, accuracy); + while(update > UINT16_MAX) { + accuracy++; + update = val / pow(10, accuracy); + } + + if(accuracy != day.accuracy) { + setDayAccuracy(accuracy); + } + + day.hImport[hour] = update; + + uint32_t max = 0; + for(uint8_t i = 0; i < 24; i++) { + if(day.hImport[i] > max) + max = day.hImport[i]; + if(day.hExport[i] > max) + max = day.hExport[i]; + } + + while(max < UINT16_MAX/10 && accuracy > 0) { + accuracy--; + max = max*10; + } + + if(accuracy != day.accuracy) { + setDayAccuracy(accuracy); + } } -int32_t AmsDataStorage::getHourImport(uint8_t hour) { +uint32_t AmsDataStorage::getHourImport(uint8_t hour) { if(hour < 0 || hour > 24) return 0; - return day.hImport[hour] * 10; + return day.hImport[hour] * pow(10, day.accuracy); } -void AmsDataStorage::setHourExport(uint8_t hour, int32_t val) { +void AmsDataStorage::setHourExport(uint8_t hour, uint32_t val) { if(hour < 0 || hour > 24) return; - day.hExport[hour] = val / 10; + + uint8_t accuracy = day.accuracy; + uint32_t update = val / pow(10, accuracy); + while(update > UINT16_MAX) { + accuracy++; + update = val / pow(10, accuracy); + } + + if(accuracy != day.accuracy) { + setDayAccuracy(accuracy); + } + + day.hExport[hour] = update; + + uint32_t max = 0; + for(uint8_t i = 0; i < 24; i++) { + if(day.hImport[i] > max) + max = day.hImport[i]; + if(day.hExport[i] > max) + max = day.hExport[i]; + } + + while(max < UINT16_MAX/10 && accuracy > 0) { + accuracy--; + max = max*10; + } + + if(accuracy != day.accuracy) { + setDayAccuracy(accuracy); + } } -int32_t AmsDataStorage::getHourExport(uint8_t hour) { +uint32_t AmsDataStorage::getHourExport(uint8_t hour) { if(hour < 0 || hour > 24) return 0; - return day.hExport[hour] * 10; + return day.hExport[hour] * pow(10, day.accuracy); } -void AmsDataStorage::setDayImport(uint8_t day, int32_t val) { +void AmsDataStorage::setDayImport(uint8_t day, uint32_t val) { if(day < 1 || day > 31) return; - month.dImport[day-1] = val / 10; + + uint8_t accuracy = month.accuracy; + uint32_t update = val / pow(10, accuracy); + while(update > UINT16_MAX) { + accuracy++; + update = val / pow(10, accuracy); + } + + if(accuracy != month.accuracy) { + setMonthAccuracy(accuracy); + } + + month.dImport[day-1] = update; + + uint32_t max = 0; + for(uint8_t i = 0; i < 31; i++) { + if(month.dImport[i] > max) + max = month.dImport[i]; + if(month.dExport[i] > max) + max = month.dExport[i]; + } + + while(max < UINT16_MAX/10 && accuracy > 0) { + accuracy--; + max = max*10; + } + + if(accuracy != month.accuracy) { + setMonthAccuracy(accuracy); + } } -int32_t AmsDataStorage::getDayImport(uint8_t day) { +uint32_t AmsDataStorage::getDayImport(uint8_t day) { if(day < 1 || day > 31) return 0; - return (month.dImport[day-1] * 10); + return (month.dImport[day-1] * pow(10, month.accuracy)); } -void AmsDataStorage::setDayExport(uint8_t day, int32_t val) { +void AmsDataStorage::setDayExport(uint8_t day, uint32_t val) { if(day < 1 || day > 31) return; - month.dExport[day-1] = val / 10; + + uint8_t accuracy = month.accuracy; + uint32_t update = val / pow(10, accuracy); + while(update > UINT16_MAX) { + accuracy++; + update = val / pow(10, accuracy); + } + + if(accuracy != month.accuracy) { + setMonthAccuracy(accuracy); + } + + month.dExport[day-1] = update; + + uint32_t max = 0; + for(uint8_t i = 0; i < 31; i++) { + if(month.dImport[i] > max) + max = month.dImport[i]; + if(month.dExport[i] > max) + max = month.dExport[i]; + } + + while(max < UINT16_MAX/10 && accuracy > 0) { + accuracy--; + max = max*10; + } + + if(accuracy != month.accuracy) { + setMonthAccuracy(accuracy); + } } -int32_t AmsDataStorage::getDayExport(uint8_t day) { +uint32_t AmsDataStorage::getDayExport(uint8_t day) { if(day < 1 || day > 31) return 0; - return (month.dExport[day-1] * 10); + return (month.dExport[day-1] * pow(10, month.accuracy)); } bool AmsDataStorage::load() { @@ -348,31 +466,74 @@ MonthDataPoints AmsDataStorage::getMonthData() { } bool AmsDataStorage::setDayData(DayDataPoints& day) { - if(day.version == 4) { + if(day.version == 5) { this->day = day; return true; + } else if(day.version == 4) { + this->day = day; + this->day.accuracy = 1; + this->day.version = 5; + return true; } else if(day.version == 3) { this->day = day; for(uint8_t i = 0; i < 24; i++) this->day.hExport[i] = 0; - this->day.version = 4; + this->day.accuracy = 1; + this->day.version = 5; return true; } return false; } bool AmsDataStorage::setMonthData(MonthDataPoints& month) { - if(month.version == 5) { + if(month.version == 6) { this->month = month; return true; + } else if(month.version == 5) { + this->month = month; + this->month.accuracy = 1; + this->month.version = 6; + return true; } else if(month.version == 4) { this->month = month; for(uint8_t i = 0; i < 31; i++) this->month.dExport[i] = 0; - this->month.version = 5; + this->month.accuracy = 1; + this->month.version = 6; return true; } return false; } +uint8_t AmsDataStorage::getDayAccuracy() { + return day.accuracy; +} + +void AmsDataStorage::setDayAccuracy(uint8_t accuracy) { + if(day.accuracy != accuracy) { + uint16_t multiplier = pow(10, day.accuracy)/pow(10, accuracy); + for(uint8_t i = 0; i < 24; i++) { + day.hImport[i] = day.hImport[i] * multiplier; + day.hExport[i] = day.hExport[i] * multiplier; + } + day.accuracy = accuracy; + } +} + +uint8_t AmsDataStorage::getMonthAccuracy() { + return month.accuracy; +} + +void AmsDataStorage::setMonthAccuracy(uint8_t accuracy) { + if(month.accuracy != accuracy) { + uint16_t multiplier = pow(10, month.accuracy)/pow(10, accuracy); + for(uint8_t i = 0; i < 31; i++) { + month.dImport[i] = month.dImport[i] * multiplier; + month.dExport[i] = month.dExport[i] * multiplier; + } + month.accuracy = accuracy; + } + month.accuracy = accuracy; +} + bool AmsDataStorage::isHappy() { return isDayHappy() && isMonthHappy(); } diff --git a/lib/HomeAssistantMqttHandler/include/HomeAssistantStatic.h b/lib/HomeAssistantMqttHandler/include/HomeAssistantStatic.h index 8f781d6f..baf495d5 100644 --- a/lib/HomeAssistantMqttHandler/include/HomeAssistantStatic.h +++ b/lib/HomeAssistantMqttHandler/include/HomeAssistantStatic.h @@ -22,12 +22,12 @@ HomeAssistantSensor HA_SENSORS[HA_SENSOR_COUNT] PROGMEM = { {"L1 active import", "/power", "P1", "W", "power", "\"measurement\""}, {"L2 active import", "/power", "P2", "W", "power", "\"measurement\""}, {"L3 active import", "/power", "P3", "W", "power", "\"measurement\""}, - {"Reactive import", "/power", "Q", "VAr", "reactive_power", "\"measurement\""}, + {"Reactive import", "/power", "Q", "var", "reactive_power", "\"measurement\""}, {"Active export", "/power", "PO", "W", "power", "\"measurement\""}, {"L1 active export", "/power", "PO1", "W", "power", "\"measurement\""}, {"L2 active export", "/power", "PO2", "W", "power", "\"measurement\""}, {"L3 active export", "/power", "PO3", "W", "power", "\"measurement\""}, - {"Reactive export", "/power", "QO", "VAr", "reactive_power", "\"measurement\""}, + {"Reactive export", "/power", "QO", "var", "reactive_power", "\"measurement\""}, {"L1 current", "/power", "I1", "A", "current", "\"measurement\""}, {"L2 current", "/power", "I2", "A", "current", "\"measurement\""}, {"L3 current", "/power", "I3", "A", "current", "\"measurement\""}, @@ -36,12 +36,12 @@ HomeAssistantSensor HA_SENSORS[HA_SENSOR_COUNT] PROGMEM = { {"L3 voltage", "/power", "U3", "V", "voltage", "\"measurement\""}, {"Accumulated active import", "/energy", "tPI", "kWh", "energy", "\"total_increasing\""}, {"Accumulated active export", "/energy", "tPO", "kWh", "energy", "\"total_increasing\""}, - {"Accumulated reactive import","/energy", "tQI", "kVArh","energy", "\"total_increasing\""}, - {"Accumulated reactive export","/energy", "tQO", "kVArh","energy", "\"total_increasing\""}, - {"Power factor", "/power", "PF", "", "power_factor", "\"measurement\""}, - {"L1 power factor", "/power", "PF1", "", "power_factor", "\"measurement\""}, - {"L2 power factor", "/power", "PF2", "", "power_factor", "\"measurement\""}, - {"L3 power factor", "/power", "PF3", "", "power_factor", "\"measurement\""}, + {"Accumulated reactive import","/energy", "tQI", "kvarh","energy", "\"total_increasing\""}, + {"Accumulated reactive export","/energy", "tQO", "kvarh","energy", "\"total_increasing\""}, + {"Power factor", "/power", "PF", "%", "power_factor", "\"measurement\""}, + {"L1 power factor", "/power", "PF1", "%", "power_factor", "\"measurement\""}, + {"L2 power factor", "/power", "PF2", "%", "power_factor", "\"measurement\""}, + {"L3 power factor", "/power", "PF3", "%", "power_factor", "\"measurement\""}, {"Price current hour", "/prices", "prices['0']", "", "monetary", ""}, {"Price next hour", "/prices", "prices['1']", "", "monetary", ""}, {"Price in two hour", "/prices", "prices['2']", "", "monetary", ""}, diff --git a/lib/HomeAssistantMqttHandler/src/HomeAssistantMqttHandler.cpp b/lib/HomeAssistantMqttHandler/src/HomeAssistantMqttHandler.cpp index 71253a6c..17066021 100644 --- a/lib/HomeAssistantMqttHandler/src/HomeAssistantMqttHandler.cpp +++ b/lib/HomeAssistantMqttHandler/src/HomeAssistantMqttHandler.cpp @@ -286,6 +286,9 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, Energ if(peaks >= peakCount) continue; peaks++; } + if(strncmp(sensor.path, "temp", 4) == 0) { + if(hw->getTemperature() < 0) continue; + } snprintf_P(json, BufferSize, HADISCOVER_JSON, sensor.name, topic.c_str(), sensor.topic, diff --git a/lib/RawMqttHandler/src/RawMqttHandler.cpp b/lib/RawMqttHandler/src/RawMqttHandler.cpp index 1767e3d9..ee8ae40c 100644 --- a/lib/RawMqttHandler/src/RawMqttHandler.cpp +++ b/lib/RawMqttHandler/src/RawMqttHandler.cpp @@ -92,6 +92,7 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin } mqtt->publish(topic + "/realtime/import/hour", String(ea->getUseThisHour(), 3)); mqtt->publish(topic + "/realtime/import/day", String(ea->getUseToday(), 2)); + mqtt->publish(topic + "/realtime/import/month", String(ea->getUseThisMonth(), 1)); uint8_t peakCount = ea->getConfig()->hours; if(peakCount > 5) peakCount = 5; for(uint8_t i = 1; i <= peakCount; i++) { @@ -101,6 +102,7 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin mqtt->publish(topic + "/realtime/import/monthmax", String(ea->getMonthMax(), 3), true, 0); mqtt->publish(topic + "/realtime/export/hour", String(ea->getProducedThisHour(), 3)); mqtt->publish(topic + "/realtime/export/day", String(ea->getProducedToday(), 2)); + mqtt->publish(topic + "/realtime/export/month", String(ea->getProducedThisMonth(), 1)); return true; } diff --git a/lib/SvelteUi/app/src/app.postcss b/lib/SvelteUi/app/src/app.postcss index a3e3dcf9..dace2220 100644 --- a/lib/SvelteUi/app/src/app.postcss +++ b/lib/SvelteUi/app/src/app.postcss @@ -63,7 +63,7 @@ } .pl-ov { position: absolute; - top: 28%; + top: 27%; left: 25%; width: 50%; text-align: center; @@ -76,6 +76,7 @@ color: grey; } .pl-sub { + padding-top: 10px; font-size: 1.0rem; } .pl-snt { diff --git a/lib/SvelteUi/app/src/lib/BarChart.svelte b/lib/SvelteUi/app/src/lib/BarChart.svelte index 8713a153..b6532b5a 100644 --- a/lib/SvelteUi/app/src/lib/BarChart.svelte +++ b/lib/SvelteUi/app/src/lib/BarChart.svelte @@ -19,8 +19,13 @@ return (i*barWidth)+config.padding.left; }; yScale = function(i) { - if(i > config.y.max) return heightAvailable; - let ret = heightAvailable-config.padding.bottom-((i-config.y.min)*yPerUnit); + let ret = 0; + if(i > config.y.max) + ret = config.padding.bottom; + else if(i < config.y.min) + ret = heightAvailable-config.padding.bottom; + else + ret = heightAvailable-config.padding.bottom-((i-config.y.min)*yPerUnit); return ret > heightAvailable || ret < 0.0 ? 0.0 : ret; }; }; diff --git a/lib/SvelteUi/app/src/lib/ConfigurationPanel.svelte b/lib/SvelteUi/app/src/lib/ConfigurationPanel.svelte index 01a83724..a64f2afb 100644 --- a/lib/SvelteUi/app/src/lib/ConfigurationPanel.svelte +++ b/lib/SvelteUi/app/src/lib/ConfigurationPanel.svelte @@ -31,6 +31,13 @@ h: '', p: 1883, u: '', a: '', b: '', s: { e: false, c: false, r: true, k: false } }, + o: { + e: '', + c: '', + u1: '', + u2: '', + u3: '' + }, t: { t: [0,0,0,0,0,0,0,0,0,0], h: 1 }, @@ -120,7 +127,7 @@ } -