diff --git a/doc/Aidon_data.xml b/doc/Aidon_data.xml new file mode 100644 index 00000000..64cd5654 --- /dev/null +++ b/doc/Aidon_data.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AmsData.cpp b/src/AmsData.cpp index 1b7625f8..adeb2db7 100644 --- a/src/AmsData.cpp +++ b/src/AmsData.cpp @@ -4,35 +4,35 @@ uint8_t AMS_OBIS_VERSION[6] = { 1, 1, 0, 2, 129, 255 }; uint8_t AMS_OBIS_METER_MODEL[6] = { 0, 0, 96, 1, 7, 255 }; uint8_t AMS_OBIS_METER_ID[6] = { 0, 0, 96, 1, 0, 255 }; -uint8_t AMS_OBIS_METER_TIMESTAMP[6] = { 1, 0, 0, 255 }; -uint8_t AMS_OBIS_ACTIVE_IMPORT[6] = { 1, 7, 0, 255 }; -uint8_t AMS_OBIS_ACTIVE_IMPORT_L1[6] = { 21, 7, 0, 255 }; -uint8_t AMS_OBIS_ACTIVE_IMPORT_L2[6] = { 41, 7, 0, 255 }; -uint8_t AMS_OBIS_ACTIVE_IMPORT_L3[6] = { 61, 7, 0, 255 }; -uint8_t AMS_OBIS_ACTIVE_EXPORT[6] = { 2, 7, 0, 255 }; -uint8_t AMS_OBIS_ACTIVE_EXPORT_L1[6] = { 22, 7, 0, 255 }; -uint8_t AMS_OBIS_ACTIVE_EXPORT_L2[6] = { 42, 7, 0, 255 }; -uint8_t AMS_OBIS_ACTIVE_EXPORT_L3[6] = { 62, 7, 0, 255 }; -uint8_t AMS_OBIS_REACTIVE_IMPORT[6] = { 3, 7, 0, 255 }; -uint8_t AMS_OBIS_REACTIVE_IMPORT_L1[6] = { 23, 7, 0, 255 }; -uint8_t AMS_OBIS_REACTIVE_IMPORT_L2[6] = { 43, 7, 0, 255 }; -uint8_t AMS_OBIS_REACTIVE_IMPORT_L3[6] = { 63, 7, 0, 255 }; -uint8_t AMS_OBIS_REACTIVE_EXPORT[6] = { 4, 7, 0, 255 }; -uint8_t AMS_OBIS_REACTIVE_EXPORT_L1[6] = { 24, 7, 0, 255 }; -uint8_t AMS_OBIS_REACTIVE_EXPORT_L2[6] = { 44, 7, 0, 255 }; -uint8_t AMS_OBIS_REACTIVE_EXPORT_L3[6] = { 64, 7, 0, 255 }; -uint8_t AMS_OBIS_CURRENT[6] = { 11, 7, 0, 255 }; -uint8_t AMS_OBIS_CURRENT_L1[6] = { 31, 7, 0, 255 }; -uint8_t AMS_OBIS_CURRENT_L2[6] = { 51, 7, 0, 255 }; -uint8_t AMS_OBIS_CURRENT_L3[6] = { 71, 7, 0, 255 }; -uint8_t AMS_OBIS_VOLTAGE[6] = { 12, 7, 0, 255 }; -uint8_t AMS_OBIS_VOLTAGE_L1[6] = { 32, 7, 0, 255 }; -uint8_t AMS_OBIS_VOLTAGE_L2[6] = { 52, 7, 0, 255 }; -uint8_t AMS_OBIS_VOLTAGE_L3[6] = { 72, 7, 0, 255 }; -uint8_t AMS_OBIS_ACTIVE_IMPORT_COUNT[6] = { 1, 8, 0, 255 }; -uint8_t AMS_OBIS_ACTIVE_EXPORT_COUNT[6] = { 2, 8, 0, 255 }; -uint8_t AMS_OBIS_REACTIVE_IMPORT_COUNT[6] = { 3, 8, 0, 255 }; -uint8_t AMS_OBIS_REACTIVE_EXPORT_COUNT[6] = { 4, 8, 0, 255 }; +uint8_t AMS_OBIS_METER_TIMESTAMP[4] = { 1, 0, 0, 255 }; +uint8_t AMS_OBIS_ACTIVE_IMPORT[4] = { 1, 7, 0, 255 }; +uint8_t AMS_OBIS_ACTIVE_IMPORT_L1[4] = { 21, 7, 0, 255 }; +uint8_t AMS_OBIS_ACTIVE_IMPORT_L2[4] = { 41, 7, 0, 255 }; +uint8_t AMS_OBIS_ACTIVE_IMPORT_L3[4] = { 61, 7, 0, 255 }; +uint8_t AMS_OBIS_ACTIVE_EXPORT[4] = { 2, 7, 0, 255 }; +uint8_t AMS_OBIS_ACTIVE_EXPORT_L1[4] = { 22, 7, 0, 255 }; +uint8_t AMS_OBIS_ACTIVE_EXPORT_L2[4] = { 42, 7, 0, 255 }; +uint8_t AMS_OBIS_ACTIVE_EXPORT_L3[4] = { 62, 7, 0, 255 }; +uint8_t AMS_OBIS_REACTIVE_IMPORT[4] = { 3, 7, 0, 255 }; +uint8_t AMS_OBIS_REACTIVE_IMPORT_L1[4] = { 23, 7, 0, 255 }; +uint8_t AMS_OBIS_REACTIVE_IMPORT_L2[4] = { 43, 7, 0, 255 }; +uint8_t AMS_OBIS_REACTIVE_IMPORT_L3[4] = { 63, 7, 0, 255 }; +uint8_t AMS_OBIS_REACTIVE_EXPORT[4] = { 4, 7, 0, 255 }; +uint8_t AMS_OBIS_REACTIVE_EXPORT_L1[4] = { 24, 7, 0, 255 }; +uint8_t AMS_OBIS_REACTIVE_EXPORT_L2[4] = { 44, 7, 0, 255 }; +uint8_t AMS_OBIS_REACTIVE_EXPORT_L3[4] = { 64, 7, 0, 255 }; +uint8_t AMS_OBIS_CURRENT[4] = { 11, 7, 0, 255 }; +uint8_t AMS_OBIS_CURRENT_L1[4] = { 31, 7, 0, 255 }; +uint8_t AMS_OBIS_CURRENT_L2[4] = { 51, 7, 0, 255 }; +uint8_t AMS_OBIS_CURRENT_L3[4] = { 71, 7, 0, 255 }; +uint8_t AMS_OBIS_VOLTAGE[4] = { 12, 7, 0, 255 }; +uint8_t AMS_OBIS_VOLTAGE_L1[4] = { 32, 7, 0, 255 }; +uint8_t AMS_OBIS_VOLTAGE_L2[4] = { 52, 7, 0, 255 }; +uint8_t AMS_OBIS_VOLTAGE_L3[4] = { 72, 7, 0, 255 }; +uint8_t AMS_OBIS_ACTIVE_IMPORT_COUNT[4] = { 1, 8, 0, 255 }; +uint8_t AMS_OBIS_ACTIVE_EXPORT_COUNT[4] = { 2, 8, 0, 255 }; +uint8_t AMS_OBIS_REACTIVE_IMPORT_COUNT[4] = { 3, 8, 0, 255 }; +uint8_t AMS_OBIS_REACTIVE_EXPORT_COUNT[4] = { 4, 8, 0, 255 }; AmsData::AmsData() {} @@ -41,14 +41,14 @@ AmsData::AmsData(const char* d, bool substituteMissing) { int32_t s32; char str[64]; - u32 = AMS_getUnsignedNumber(AMS_OBIS_ACTIVE_IMPORT, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_ACTIVE_IMPORT, sizeof(AMS_OBIS_ACTIVE_IMPORT), ((char *) (d))); if(u32 != 0xFFFFFFFF) { listType = 1; activeImportPower = u32; } - int meterType = AmsTypeUnknown; - CosemData* version = AMS_findObis(AMS_OBIS_VERSION, d); + meterType = AmsTypeUnknown; + CosemData* version = AMS_findObis(AMS_OBIS_VERSION, sizeof(AMS_OBIS_VERSION), d); if(version != NULL && version->base.type == CosemTypeString) { if(memcmp(version->str.data, "AIDON", 5) == 0) { meterType = AmsTypeAidon; @@ -57,53 +57,53 @@ AmsData::AmsData(const char* d, bool substituteMissing) { } } - u32 = AMS_getString(AMS_OBIS_VERSION, ((char *) (d)), str); + u32 = AMS_getString(AMS_OBIS_VERSION, sizeof(AMS_OBIS_VERSION), ((char *) (d)), str); if(u32 > 0) { listId = String(str); } - u32 = AMS_getUnsignedNumber(AMS_OBIS_ACTIVE_EXPORT, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_ACTIVE_EXPORT, sizeof(AMS_OBIS_ACTIVE_EXPORT), ((char *) (d))); if(u32 != 0xFFFFFFFF) { activeExportPower = u32; } - u32 = AMS_getUnsignedNumber(AMS_OBIS_REACTIVE_IMPORT, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_REACTIVE_IMPORT, sizeof(AMS_OBIS_REACTIVE_IMPORT), ((char *) (d))); if(u32 != 0xFFFFFFFF) { reactiveImportPower = u32; } - u32 = AMS_getUnsignedNumber(AMS_OBIS_REACTIVE_EXPORT, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_REACTIVE_EXPORT, sizeof(AMS_OBIS_REACTIVE_EXPORT), ((char *) (d))); if(u32 != 0xFFFFFFFF) { reactiveExportPower = u32; } - u32 = AMS_getUnsignedNumber(AMS_OBIS_VOLTAGE_L1, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_VOLTAGE_L1, sizeof(AMS_OBIS_VOLTAGE_L1), ((char *) (d))); if(u32 != 0xFFFFFFFF) { listType = 2; l1voltage = u32; } - u32 = AMS_getUnsignedNumber(AMS_OBIS_VOLTAGE_L2, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_VOLTAGE_L2, sizeof(AMS_OBIS_VOLTAGE_L2), ((char *) (d))); if(u32 != 0xFFFFFFFF) { listType = 2; l2voltage = u32; } - u32 = AMS_getUnsignedNumber(AMS_OBIS_VOLTAGE_L3, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_VOLTAGE_L3, sizeof(AMS_OBIS_VOLTAGE_L3), ((char *) (d))); if(u32 != 0xFFFFFFFF) { listType = 2; l3voltage = u32; } - s32 = AMS_getSignedNumber(AMS_OBIS_CURRENT_L1, ((char *) (d))); + s32 = AMS_getSignedNumber(AMS_OBIS_CURRENT_L1, sizeof(AMS_OBIS_CURRENT_L1), ((char *) (d))); if(s32 != 0xFFFFFFFF) { listType = 2; l1current = s32; } - s32 = AMS_getSignedNumber(AMS_OBIS_CURRENT_L2, ((char *) (d))); + s32 = AMS_getSignedNumber(AMS_OBIS_CURRENT_L2, sizeof(AMS_OBIS_CURRENT_L2), ((char *) (d))); if(s32 != 0xFFFFFFFF) { listType = 2; l2current = s32; } - s32 = AMS_getSignedNumber(AMS_OBIS_CURRENT_L3, ((char *) (d))); + s32 = AMS_getSignedNumber(AMS_OBIS_CURRENT_L3, sizeof(AMS_OBIS_CURRENT_L3), ((char *) (d))); if(s32 != 0xFFFFFFFF) { listType = 2; l3current = s32; @@ -133,38 +133,38 @@ AmsData::AmsData(const char* d, bool substituteMissing) { l3current = l3current != 0 ? l3current / adiv : 0; } - u32 = AMS_getUnsignedNumber(AMS_OBIS_ACTIVE_IMPORT_COUNT, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_ACTIVE_IMPORT_COUNT, sizeof(AMS_OBIS_ACTIVE_IMPORT_COUNT), ((char *) (d))); if(u32 != 0xFFFFFFFF) { listType = 3; activeImportCounter = u32 / 100.0; } - u32 = AMS_getUnsignedNumber(AMS_OBIS_ACTIVE_EXPORT_COUNT, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_ACTIVE_EXPORT_COUNT, sizeof(AMS_OBIS_ACTIVE_EXPORT_COUNT), ((char *) (d))); if(u32 != 0xFFFFFFFF) { listType = 3; activeExportCounter = u32 / 100.0; } - u32 = AMS_getUnsignedNumber(AMS_OBIS_REACTIVE_IMPORT_COUNT, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_REACTIVE_IMPORT_COUNT, sizeof(AMS_OBIS_REACTIVE_IMPORT_COUNT), ((char *) (d))); if(u32 != 0xFFFFFFFF) { listType = 3; reactiveImportCounter = u32 / 100.0; } - u32 = AMS_getUnsignedNumber(AMS_OBIS_REACTIVE_EXPORT_COUNT, ((char *) (d))); + u32 = AMS_getUnsignedNumber(AMS_OBIS_REACTIVE_EXPORT_COUNT, sizeof(AMS_OBIS_REACTIVE_EXPORT_COUNT), ((char *) (d))); if(u32 != 0xFFFFFFFF) { listType = 3; reactiveExportCounter = u32 / 100.0; } - u32 = AMS_getString(AMS_OBIS_METER_MODEL, ((char *) (d)), str); + u32 = AMS_getString(AMS_OBIS_METER_MODEL, sizeof(AMS_OBIS_METER_MODEL), ((char *) (d)), str); if(u32 > 0) { meterModel = String(str); } - u32 = AMS_getString(AMS_OBIS_METER_ID, ((char *) (d)), str); + u32 = AMS_getString(AMS_OBIS_METER_ID, sizeof(AMS_OBIS_METER_ID), ((char *) (d)), str); if(u32 > 0) { meterId = String(str); } - time_t ts = AMS_getTimestamp(AMS_OBIS_METER_TIMESTAMP, ((char *) (d))); + time_t ts = AMS_getTimestamp(AMS_OBIS_METER_TIMESTAMP, sizeof(AMS_OBIS_METER_TIMESTAMP), ((char *) (d))); if(ts > 0) { meterTimestamp = ts; } @@ -225,6 +225,7 @@ void AmsData::apply(AmsData& other) { case 2: this->listId = other.getListId(); this->meterId = other.getMeterId(); + this->meterType = other.getMeterType(); this->meterModel = other.getMeterModel(); this->reactiveImportPower = other.getReactiveImportPower(); this->activeExportPower = other.getActiveExportPower(); @@ -262,6 +263,10 @@ String AmsData::getMeterId() { return this->meterId; } +uint8_t AmsData::getMeterType() { + return this->meterType; +} + String AmsData::getMeterModel() { return this->meterModel; } diff --git a/src/AmsData.h b/src/AmsData.h index 4709d672..8d96f8b3 100644 --- a/src/AmsData.h +++ b/src/AmsData.h @@ -4,10 +4,12 @@ #include "Arduino.h" #include -#define METER_TYPE_KAIFA 1 -#define METER_TYPE_AIDON 2 -#define METER_TYPE_KAMSTRUP 3 -#define METER_TYPE_OMNIPOWER 4 +enum AmsType { + AmsTypeAidon = 0x01, + AmsTypeKaifa = 0x02, + AmsTypeKamstrup = 0x03, + AmsTypeUnknown = 0xFF +}; class AmsData { public: @@ -24,6 +26,7 @@ public: String getListId(); String getMeterId(); + uint8_t getMeterType(); String getMeterModel(); time_t getMeterTimestamp(); @@ -51,7 +54,7 @@ public: private: unsigned long lastUpdateMillis = 0; - uint8_t listType = 0; + uint8_t listType = 0, meterType = AmsTypeUnknown; time_t packageTimestamp = 0; String listId, meterId, meterModel; time_t meterTimestamp = 0; diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index 931b59c2..4c0da94c 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -633,8 +633,8 @@ void readHanPort() { mqttHandler->publishSystem(&hw); } time_t now = time(nullptr); - if(now < EPOCH_2021_01_01 || data.getListType() == 3) { - if(data.getMeterTimestamp() > EPOCH_2021_01_01 || !ntpEnabled) { + if(now < EPOCH_2021_01_01 && data.getListType() == 3 && !ntpEnabled) { + if(data.getMeterTimestamp() > EPOCH_2021_01_01) { debugI("Using timestamp from meter"); now = data.getMeterTimestamp(); } else if(data.getPackageTimestamp() > EPOCH_2021_01_01) { diff --git a/src/ams/ams.cpp b/src/ams/ams.cpp index b36aa4f2..a0235069 100644 --- a/src/ams/ams.cpp +++ b/src/ams/ams.cpp @@ -3,8 +3,8 @@ #include "lwip/def.h" #include "Time.h" -time_t AMS_getTimestamp(uint8_t* obis, const char* ptr) { - CosemData* item = AMS_findObis(obis, ptr); +time_t AMS_getTimestamp(uint8_t* obis, int matchlength, const char* ptr) { + CosemData* item = AMS_findObis(obis, matchlength, ptr); if(item != NULL) { switch(item->base.type) { case CosemTypeOctetString: { @@ -31,8 +31,8 @@ time_t AMS_getTimestamp(uint8_t* obis, const char* ptr) { return 0; } -uint8_t AMS_getString(uint8_t* obis, const char* ptr, char* target) { - CosemData* item = AMS_findObis(obis, ptr); +uint8_t AMS_getString(uint8_t* obis, int matchlength, const char* ptr, char* target) { + CosemData* item = AMS_findObis(obis, matchlength, ptr); if(item != NULL) { switch(item->base.type) { case CosemTypeString: @@ -48,8 +48,8 @@ uint8_t AMS_getString(uint8_t* obis, const char* ptr, char* target) { return 0; } -uint32_t AMS_getUnsignedNumber(uint8_t* obis, const char* ptr) { - CosemData* item = AMS_findObis(obis, ptr); +uint32_t AMS_getUnsignedNumber(uint8_t* obis, int matchlength, const char* ptr) { + CosemData* item = AMS_findObis(obis, matchlength, ptr); if(item != NULL) { switch(item->base.type) { case CosemTypeLongUnsigned: @@ -61,8 +61,8 @@ uint32_t AMS_getUnsignedNumber(uint8_t* obis, const char* ptr) { return 0xFFFFFFFF; } -int32_t AMS_getSignedNumber(uint8_t* obis, const char* ptr) { - CosemData* item = AMS_findObis(obis, ptr); +int32_t AMS_getSignedNumber(uint8_t* obis, int matchlength, const char* ptr) { + CosemData* item = AMS_findObis(obis, matchlength, ptr); if(item != NULL) { switch(item->base.type) { case CosemTypeLongUnsigned: @@ -76,7 +76,7 @@ int32_t AMS_getSignedNumber(uint8_t* obis, const char* ptr) { return 0xFFFFFFFF; } -CosemData* AMS_findObis(uint8_t* obis, const char* ptr) { +CosemData* AMS_findObis(uint8_t* obis, int matchlength, const char* ptr) { CosemData* item = (CosemData*) ptr; int ret = 0; char* pos = (char*) ptr; @@ -91,11 +91,11 @@ CosemData* AMS_findObis(uint8_t* obis, const char* ptr) { case CosemTypeOctetString: { ret = 1; uint8_t* found = item->oct.data; - int x = 6 - sizeof(&obis); + int x = 6 - matchlength; for(int i = x; i < 6; i++) { if(found[i] != obis[i-x]) ret = 0; } - } + } // Fallthrough case CosemTypeString: { pos += 2 + item->base.length; break; diff --git a/src/ams/ams.h b/src/ams/ams.h index 343d61a9..69cc0c8d 100644 --- a/src/ams/ams.h +++ b/src/ams/ams.h @@ -4,13 +4,6 @@ #include "Arduino.h" #include "hdlc.h" -enum AmsType { - AmsTypeAidon = 0x01, - AmsTypeKaifa = 0x02, - AmsTypeKamstrup = 0x03, - AmsTypeUnknown = 0xFF -}; - struct AmsOctetTimestamp { uint16_t year; uint8_t month; @@ -25,10 +18,10 @@ struct AmsOctetTimestamp { } __attribute__((packed)); -CosemData* AMS_findObis(uint8_t* obis, const char* ptr); -uint32_t AMS_getUnsignedNumber(uint8_t* obis, const char* ptr); -int32_t AMS_getSignedNumber(uint8_t* obis, const char* ptr); -uint8_t AMS_getString(uint8_t* obis, const char* ptr, char* target); -time_t AMS_getTimestamp(uint8_t* obis, const char* ptr); +CosemData* AMS_findObis(uint8_t* obis, int matchlength, const char* ptr); +uint32_t AMS_getUnsignedNumber(uint8_t* obis, int matchlength, const char* ptr); +int32_t AMS_getSignedNumber(uint8_t* obis, int matchlength, const char* ptr); +uint8_t AMS_getString(uint8_t* obis, int matchlength, const char* ptr, char* target); +time_t AMS_getTimestamp(uint8_t* obis, int matchlength, const char* ptr); #endif diff --git a/src/web/AmsWebServer.cpp b/src/web/AmsWebServer.cpp index b8af05a6..3ea40f5e 100644 --- a/src/web/AmsWebServer.cpp +++ b/src/web/AmsWebServer.cpp @@ -2,6 +2,7 @@ #include "version.h" #include "AmsStorage.h" #include "hexutils.h" +#include "AmsData.h" #include "root/head_html.h" #include "root/foot_html.h" @@ -364,6 +365,8 @@ void AmsWebServer::indexHtml() { int rssi = hw->getWifiRssi(); html.replace("{rssi}", String(rssi)); + html.replace("{mem}", String(ESP.getFreeHeap()/1000, 1)); + html.replace("{cs}", String((uint32_t)(millis64()/1000), 10)); server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); @@ -391,6 +394,22 @@ void AmsWebServer::configMeterHtml() { server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Pragma", "no-cache"); + String manufacturer; + switch(meterState->getMeterType()) { + case AmsTypeAidon: + manufacturer = "Aidon"; + break; + case AmsTypeKamstrup: + manufacturer = "Kamstrup"; + break; + default: + manufacturer = "Unknown"; + break; + } + + html.replace("{maf}", manufacturer); + html.replace("{mod}", meterState->getMeterModel()); + html.replace("{mid}", meterState->getMeterId()); html.replace("{b}", String(meterConfig->baud)); html.replace("{b2400}", meterConfig->baud == 2400 ? "selected" : ""); html.replace("{b115200}", meterConfig->baud == 115200 ? "selected" : ""); diff --git a/web/application.js b/web/application.js index 88d0decb..2fb1868f 100644 --- a/web/application.js +++ b/web/application.js @@ -253,6 +253,8 @@ var fetch = function() { $('.ju').html(moment.duration(parseInt(json.u), 'seconds').humanize()); } + $('.jm').html((json.m.toFixed(0)/1000).toFixed(1)); + setStatus("esp", json.em); setStatus("han", json.hm); setStatus("wifi", json.wm); diff --git a/web/index.html b/web/index.html index b73beb81..18b73357 100644 --- a/web/index.html +++ b/web/index.html @@ -1,17 +1,20 @@ - + Up {cs} Temperature: {temp}°C - + ESP volt: {vcc}V WiFi RSSI: {rssi}dBm + + Free mem: {mem}kb + diff --git a/web/meter.html b/web/meter.html index 6ea59565..d6d1539e 100644 --- a/web/meter.html +++ b/web/meter.html @@ -2,6 +2,32 @@ Meter + + + + + Manufacturer + + + + + + + + Model + + + + + + + + ID + + + + +