mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-04-17 09:10:52 +00:00
Changes for v2.2
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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", ""},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<form on:submit|preventDefault={handleSubmit} autocomplete="off">
|
||||
<div class="grid xl:grid-cols-4 lg:grid-cols-2 md:grid-cols-2">
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">General</strong>
|
||||
@@ -257,14 +264,14 @@
|
||||
<div class="mx-1">
|
||||
Main fuse<br/>
|
||||
<label class="flex">
|
||||
<input name="mf" bind:value={configuration.m.f} type="number" min="5" max="255" class="in-f tr w-full"/>
|
||||
<input name="mf" bind:value={configuration.m.f} type="number" min="5" max="65535" class="in-f tr w-full"/>
|
||||
<span class="in-post">A</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mx-1">
|
||||
Production<br/>
|
||||
<label class="flex">
|
||||
<input name="mr" bind:value={configuration.m.r} type="number" min="0" max="255" class="in-f tr w-full"/>
|
||||
<input name="mr" bind:value={configuration.m.r} type="number" min="0" max="65535" class="in-f tr w-full"/>
|
||||
<span class="in-post">kWp</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -457,6 +464,31 @@
|
||||
<input name="qb" bind:value={configuration.q.b} type="text" class="in-s"/>
|
||||
</div>
|
||||
</div>
|
||||
{#if configuration.q.m == 3}
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">Domoticz</strong>
|
||||
<a href="https://github.com/gskjold/AmsToMqttBridge/wiki/MQTT-configuration#domoticz" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<input type="hidden" name="o" value="true"/>
|
||||
<div class="my-1 flex">
|
||||
<div class="w-1/2">
|
||||
Electricity IDX<br/>
|
||||
<input name="oe" bind:value={configuration.o.e} type="text" class="in-f tr w-full"/>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Current IDX<br/>
|
||||
<input name="oc" bind:value={configuration.o.c} type="text" class="in-l tr w-full"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-1">
|
||||
Voltage IDX: L1, L2 & L3
|
||||
<div class="flex">
|
||||
<input name="ou1" bind:value={configuration.o.u1} type="text" class="in-f tr w-1/3"/>
|
||||
<input name="ou2" bind:value={configuration.o.u2} type="text" class="in-m tr w-1/3"/>
|
||||
<input name="ou2" bind:value={configuration.o.u3} type="text" class="in-l tr w-1/3"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if configuration.p.r.startsWith("10YNO") || configuration.p.r == '10Y1001A1001A48H'}
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">Tariff thresholds</strong>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
<div class="grid xl:grid-cols-3 lg:grid-cols-2">
|
||||
<div class="cnt">
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<form on:submit|preventDefault={handleSubmit} autocomplete="off">
|
||||
<div>
|
||||
Various permissions we need to do stuff:
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="cnt">
|
||||
<strong>Upload {title}</strong>
|
||||
<p class="mb-4">Select a suitable file and click upload</p>
|
||||
<form action="{action}" enctype="multipart/form-data" method="post" on:submit={() => uploading=true}>
|
||||
<form action="{action}" enctype="multipart/form-data" method="post" on:submit={() => uploading=true} autocomplete="off">
|
||||
<input name="file" type="file">
|
||||
<div class="w-full text-right mt-4">
|
||||
<button type="submit" class="btn-pri">Upload</button>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
<div class="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2">
|
||||
<div class="cnt">
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<form on:submit|preventDefault={handleSubmit} autocomplete="off">
|
||||
<input type="hidden" name="s" value="true"/>
|
||||
<strong class="text-sm">Setup</strong>
|
||||
<div class="my-3">
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
{/if}
|
||||
{#if sysinfo.security == 0 || data.a}
|
||||
<div class="my-2 flex">
|
||||
<form action="/firmware" enctype="multipart/form-data" method="post" on:submit={() => firmwareUploading=true}>
|
||||
<form action="/firmware" enctype="multipart/form-data" method="post" on:submit={() => firmwareUploading=true} autocomplete="off">
|
||||
<input style="display:none" name="file" type="file" accept=".bin" bind:this={firmwareFileInput} bind:files={firmwareFiles}>
|
||||
{#if firmwareFiles.length == 0}
|
||||
<button type="button" on:click={()=>{firmwareFileInput.click();}} class="text-xs py-1 px-2 rounded bg-blue-500 text-white float-right mr-3">Select firmware file for upgrade</button>
|
||||
@@ -150,7 +150,7 @@
|
||||
{#if sysinfo.security == 0 || data.a}
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">Configuration</strong>
|
||||
<form method="get" action="/configfile.cfg">
|
||||
<form method="get" action="/configfile.cfg" autocomplete="off">
|
||||
<div class="grid grid-cols-2">
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="iw" value="true" checked/> WiFi</label>
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="im" value="true" checked/> MQTT</label>
|
||||
@@ -167,7 +167,7 @@
|
||||
<button type="submit" class="ml-2 text-xs py-1 px-2 rounded bg-blue-500 text-white float-right mr-3">Download</button>
|
||||
{/if}
|
||||
</form>
|
||||
<form action="/configfile" enctype="multipart/form-data" method="post" on:submit={() => configUploading=true}>
|
||||
<form action="/configfile" enctype="multipart/form-data" method="post" on:submit={() => configUploading=true} autocomplete="off">
|
||||
<input style="display:none" name="file" type="file" accept=".cfg" bind:this={configFileInput} bind:files={configFiles}>
|
||||
{#if configFiles.length == 0}
|
||||
<button type="button" on:click={()=>{configFileInput.click();}} class="text-xs py-1 px-2 rounded bg-blue-500 text-white mr-3">Select file...</button>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
<div class="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2">
|
||||
<div class="cnt">
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<form on:submit|preventDefault={handleSubmit} autocomplete="off">
|
||||
<input type="hidden" name="v" value="true"/>
|
||||
<strong class="text-sm">Initial configuration</strong>
|
||||
<div class="my-3">
|
||||
|
||||
@@ -17,14 +17,14 @@ export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
server: {
|
||||
proxy: {
|
||||
"/data.json": "http://192.168.233.229",
|
||||
"/energyprice.json": "http://192.168.233.229",
|
||||
"/dayplot.json": "http://192.168.233.229",
|
||||
"/monthplot.json": "http://192.168.233.229",
|
||||
"/temperature.json": "http://192.168.233.229",
|
||||
"/sysinfo.json": "http://192.168.233.229",
|
||||
"/data.json": "https://ams2mqtt.no23.cc",
|
||||
"/energyprice.json": "https://ams2mqtt.no23.cc",
|
||||
"/dayplot.json": "https://ams2mqtt.no23.cc",
|
||||
"/monthplot.json": "https://ams2mqtt.no23.cc",
|
||||
"/temperature.json": "https://ams2mqtt.no23.cc",
|
||||
"/sysinfo.json": "https://ams2mqtt.no23.cc",
|
||||
"/configuration.json": "http://192.168.233.229",
|
||||
"/tariff.json": "http://192.168.233.229",
|
||||
"/tariff.json": "https://ams2mqtt.no23.cc",
|
||||
"/save": "http://192.168.233.229",
|
||||
"/reboot": "http://192.168.233.229",
|
||||
"/configfile": "http://192.168.233.229",
|
||||
|
||||
7
lib/SvelteUi/json/conf_domoticz.json
Normal file
7
lib/SvelteUi/json/conf_domoticz.json
Normal file
@@ -0,0 +1,7 @@
|
||||
"o": {
|
||||
"e" : %d,
|
||||
"c" : %d,
|
||||
"u1" : %d,
|
||||
"u2" : %d,
|
||||
"u3" : %d
|
||||
}
|
||||
@@ -25,4 +25,4 @@
|
||||
},
|
||||
"b": %.1f
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<html>
|
||||
<form action="/firmware" enctype="multipart/form-data" method="post">
|
||||
<form action="/firmware" enctype="multipart/form-data" method="post" autocomplete="off">
|
||||
<input name="file" type="file" accept=".bin">
|
||||
<button type="submit" class="">Upload</button>
|
||||
</form>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "html/conf_thresholds_json.h"
|
||||
#include "html/conf_debug_json.h"
|
||||
#include "html/conf_gpio_json.h"
|
||||
#include "html/conf_domoticz_json.h"
|
||||
#include "html/firmware_html.h"
|
||||
|
||||
#include "version.h"
|
||||
@@ -727,6 +728,8 @@ void AmsWebServer::configurationJson() {
|
||||
config->getEntsoeConfig(entsoe);
|
||||
DebugConfig debugConfig;
|
||||
config->getDebugConfig(debugConfig);
|
||||
DomoticzConfig domo;
|
||||
config->getDomoticzConfig(domo);
|
||||
|
||||
bool qsc = false;
|
||||
bool qsr = false;
|
||||
@@ -854,6 +857,14 @@ void AmsWebServer::configurationJson() {
|
||||
gpioConfig->vccBootLimit / 10.0
|
||||
);
|
||||
server.sendContent(buf);
|
||||
snprintf_P(buf, BufferSize, CONF_DOMOTICZ_JSON,
|
||||
domo.elidx,
|
||||
domo.cl1idx,
|
||||
domo.vl1idx,
|
||||
domo.vl2idx,
|
||||
domo.vl3idx
|
||||
);
|
||||
server.sendContent(buf);
|
||||
server.sendContent("}");
|
||||
}
|
||||
|
||||
@@ -1141,14 +1152,14 @@ void AmsWebServer::handleSave() {
|
||||
config->setMqttConfig(mqtt);
|
||||
}
|
||||
|
||||
if(server.hasArg(F("dc")) && server.arg(F("dc")) == F("true")) {
|
||||
if(server.hasArg(F("o")) && server.arg(F("o")) == F("true")) {
|
||||
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received Domoticz config"));
|
||||
DomoticzConfig domo {
|
||||
static_cast<uint16_t>(server.arg(F("elidx")).toInt()),
|
||||
static_cast<uint16_t>(server.arg(F("vl1idx")).toInt()),
|
||||
static_cast<uint16_t>(server.arg(F("vl2idx")).toInt()),
|
||||
static_cast<uint16_t>(server.arg(F("vl3idx")).toInt()),
|
||||
static_cast<uint16_t>(server.arg(F("cl1idx")).toInt())
|
||||
static_cast<uint16_t>(server.arg(F("oe")).toInt()),
|
||||
static_cast<uint16_t>(server.arg(F("ou1")).toInt()),
|
||||
static_cast<uint16_t>(server.arg(F("ou2")).toInt()),
|
||||
static_cast<uint16_t>(server.arg(F("ou3")).toInt()),
|
||||
static_cast<uint16_t>(server.arg(F("oc")).toInt())
|
||||
};
|
||||
config->setDomoticzConfig(domo);
|
||||
}
|
||||
@@ -1830,10 +1841,11 @@ void AmsWebServer::configFileDownload() {
|
||||
|
||||
if(ds != NULL) {
|
||||
DayDataPoints day = ds->getDayData();
|
||||
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("dayplot %d %lu %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"),
|
||||
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("dayplot %d %lu %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"),
|
||||
day.version,
|
||||
(int32_t) day.lastMeterReadTime,
|
||||
day.activeImport,
|
||||
day.accuracy,
|
||||
ds->getHourImport(0),
|
||||
ds->getHourImport(1),
|
||||
ds->getHourImport(2),
|
||||
@@ -1892,10 +1904,11 @@ void AmsWebServer::configFileDownload() {
|
||||
}
|
||||
|
||||
MonthDataPoints month = ds->getMonthData();
|
||||
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("monthplot %d %lu %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"),
|
||||
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("monthplot %d %lu %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"),
|
||||
month.version,
|
||||
(int32_t) month.lastMeterReadTime,
|
||||
month.activeImport,
|
||||
month.accuracy,
|
||||
ds->getDayImport(1),
|
||||
ds->getDayImport(2),
|
||||
ds->getDayImport(3),
|
||||
|
||||
Reference in New Issue
Block a user