Merge pull request #79 from gskjold/dev-v1.3.0

Dev v1.3.0
This commit is contained in:
Gunnar Skjold
2020-06-06 20:30:47 +02:00
committed by GitHub
38 changed files with 3527 additions and 1992 deletions

View File

@@ -9,7 +9,7 @@ on:
- web/**
- platformio.ini
branches:
- master
- '*'
tags:
- '*'
- '!v*.*.*'
@@ -39,7 +39,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -U platformio
pip install -U platformio css_html_js_minify
- name: Configure build targets
run: echo "[platformio]\ndefault_envs = hw1esp12e, esp12e, esp32" > platformio-user.ini
- name: PlatformIO lib install

View File

@@ -40,7 +40,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -U platformio
pip install -U platformio css_html_js_minify
- name: PlatformIO lib install
run: pio lib install
- name: PlatformIO run
@@ -109,3 +109,21 @@ jobs:
asset_path: .pio/build/featheresp32/firmware.bin
asset_name: ams2mqtt-featheresp32-${{ steps.release_tag.outputs.tag }}.bin
asset_content_type: application/octet-stream
- name: Upload azdevkit4 binary to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: .pio/build/az-delivery-devkit-v4/firmware.bin
asset_name: ams2mqtt-azdevkit4-${{ steps.release_tag.outputs.tag }}.bin
asset_content_type: application/octet-stream
- name: Upload doitdevkit1 binary to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: .pio/build/esp32doit-devkit-v1/firmware.bin
asset_name: ams2mqtt-doitdevkit1-${{ steps.release_tag.outputs.tag }}.bin
asset_content_type: application/octet-stream

View File

@@ -1,7 +1,10 @@
#include "HanReader.h"
HanReader::HanReader()
{
HanReader::HanReader() {
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
localZone = new Timezone(CEST, CET);
}
void HanReader::setup(Stream *hanPort, RemoteDebug *debug)
@@ -10,11 +13,6 @@ void HanReader::setup(Stream *hanPort, RemoteDebug *debug)
bytesRead = 0;
debugger = debug;
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
localZone = new Timezone(CEST, CET);
if (debug) debug->println("MBUS serial setup complete");
}
@@ -112,6 +110,12 @@ String HanReader::getString(int objectId) {
return getString(objectId, buffer, 0, bytesRead);
}
int HanReader::getBuffer(byte* buf) {
for (int i = 0; i < bytesRead; i++) {
buf[i] = buffer[i];
}
return bytesRead;
}
int HanReader::findValuePosition(int dataPosition, byte *buffer, int start, int length) {
// The first byte after the header gives the length

View File

@@ -29,6 +29,7 @@ public:
uint32_t getUint(int objectId); // Only for uint32
String getString(int objectId);
time_t getTime(int objectId);
int getBuffer(byte* buf);
private:
RemoteDebug* debugger;

View File

@@ -5,10 +5,12 @@
enum class Kamstrup
{
List1PhaseShort = 0x11,
List1PhaseLong = 0x1B,
List3PhaseShort = 0x19,
List3PhaseLong = 0x23,
List1PhaseShort = 0x11,
List1PhaseLong = 0x1B
List3PhaseITShort = 0x17,
List3PhaseITLong = 0x21
};
enum class Kamstrup_List3Phase
@@ -83,5 +85,43 @@ enum class Kamstrup_List1Phase
CumulativeReactiveExportEnergy
};
enum class Kamstrup_List3PhaseIT
{
ListSize,
ListVersionIdentifier,
MeterID_OBIS,
MeterID,
MeterType_OBIS,
MeterType,
ActiveImportPower_OBIS,
ActiveImportPower,
ActiveExportPower_OBIS,
ActiveExportPower,
ReactiveImportPower_OBIS,
ReactiveImportPower,
ReactiveExportPower_OBIS,
ReactiveExportPower,
CurrentL1_OBIS,
CurrentL1,
CurrentL3_OBIS,
CurrentL3,
VoltageL1_OBIS,
VoltageL1,
VoltageL2_OBIS,
VoltageL2,
VoltageL3_OBIS,
VoltageL3,
MeterClock_OBIS,
MeterClock,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy
};
#endif

View File

@@ -7,7 +7,7 @@ framework = arduino
lib_deps = HanReader@1.0.1, ArduinoJson@6.14.1, MQTT@2.4.7, DallasTemperature@3.8.1, EspSoftwareSerial@6.7.1, Base64@1.0.0, RemoteDebug@3.0.5, Time@1.6
[env:hw1esp12e]
platform = espressif8266@2.3.3
platform = espressif8266@2.5.1
board = esp12e
framework = ${common.framework}
lib_deps = ${common.lib_deps}
@@ -18,7 +18,7 @@ extra_scripts =
scripts/makeweb.py
[env:esp12e]
platform = espressif8266@2.3.3
platform = espressif8266@2.5.1
board = esp12e
framework = ${common.framework}
lib_deps = ${common.lib_deps}
@@ -27,7 +27,7 @@ extra_scripts =
scripts/makeweb.py
[env:d1mini]
platform = espressif8266@2.3.3
platform = espressif8266@2.5.1
board = d1_mini
framework = ${common.framework}
lib_deps = ${common.lib_deps}
@@ -36,7 +36,7 @@ extra_scripts =
scripts/makeweb.py
[env:esp32]
platform = espressif32@1.11.2
platform = espressif32@1.12.1
board = esp32dev
framework = ${common.framework}
lib_deps = ${common.lib_deps}
@@ -45,7 +45,7 @@ extra_scripts =
scripts/makeweb.py
[env:lolind32]
platform = espressif32@1.11.2
platform = espressif32@1.12.1
board = lolin_d32
framework = ${common.framework}
lib_deps = ${common.lib_deps}
@@ -54,10 +54,28 @@ extra_scripts =
scripts/makeweb.py
[env:featheresp32]
platform = espressif32@1.11.2
platform = espressif32@1.12.1
board = featheresp32
framework = ${common.framework}
lib_deps = ${common.lib_deps}
extra_scripts =
pre:scripts/addversion.py
scripts/makeweb.py
[env:az-delivery-devkit-v4]
platform = espressif32@1.12.1
board = az-delivery-devkit-v4
framework = ${common.framework}
lib_deps = ${common.lib_deps}
extra_scripts =
pre:scripts/addversion.py
scripts/makeweb.py
[env:esp32doit-devkit-v1]
platform = espressif32@1.12.1
board = esp32doit-devkit-v1
framework = ${common.framework}
lib_deps = ${common.lib_deps}
extra_scripts =
pre:scripts/addversion.py
scripts/makeweb.py

View File

@@ -1,10 +1,14 @@
import os
import re
import shutil
from css_html_js_minify import html_minify, js_minify, css_minify
webroot = "web"
srcroot = "src/web/root"
version = os.environ.get('GITHUB_TAG')
if version == None:
version = "SNAPSHOT"
if os.path.exists(srcroot):
shutil.rmtree(srcroot)
@@ -20,11 +24,25 @@ for filename in os.listdir(webroot):
varname = basename.upper()
with open(srcfile, encoding="utf-8") as f:
content = f.read().replace("${version}", version)
if filename.endswith(".html"):
content = html_minify(content)
elif filename.endswith(".css"):
content = css_minify(content)
elif filename.endswith(".js") and filename != 'gaugemeter.js':
content = js_minify(content)
with open(dstfile, "w") as dst:
dst.write("const char ")
dst.write(varname)
dst.write("[] PROGMEM = R\"==\"==(\n")
with open(srcfile, "r") as src:
for line in src.readlines():
dst.write(line)
dst.write("\n)==\"==\";\n")
dst.write("[] PROGMEM = R\"==\"==(")
dst.write(content)
dst.write(")==\"==\";\n")
dst.write("const int ");
dst.write(varname)
dst.write("_LEN = ");
dst.write(str(len(content)))
dst.write(";");

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,59 @@
#include <EEPROM.h>
#include "Arduino.h"
struct ConfigObject {
uint8_t boardType;
char wifiSsid[32];
char wifiPassword[64];
char wifiIp[15];
char wifiGw[15];
char wifiSubnet[15];
char wifiDns1[15];
char wifiDns2[15];
char wifiHostname[32];
char mqttHost[128];
uint16_t mqttPort;
char mqttClientId[32];
char mqttPublishTopic[64];
char mqttSubscribeTopic[64];
char mqttUser[64];
char mqttPassword[64];
uint8_t mqttPayloadFormat;
bool mqttSsl;
uint8_t authSecurity;
char authUser[64];
char authPassword[64];
uint8_t meterType;
uint8_t distributionSystem;
uint8_t mainFuse;
uint8_t productionCapacity;
bool substituteMissing;
bool sendUnknown;
bool debugTelnet;
bool debugSerial;
uint8_t debugLevel;
uint8_t hanPin;
uint8_t apPin;
uint8_t ledPin;
bool ledInverted;
uint8_t ledPinRed;
uint8_t ledPinGreen;
uint8_t ledPinBlue;
bool ledRgbInverted;
uint8_t tempSensorPin;
uint8_t vccPin;
uint16_t vccMultiplier;
uint8_t vccBootLimit;
uint16_t domoELIDX;
uint16_t domoVL1IDX;
uint16_t domoVL2IDX;
uint16_t domoVL3IDX;
uint16_t domoCL1IDX;
};
class AmsConfiguration {
public:
bool hasConfig();
@@ -10,128 +63,200 @@ public:
bool load();
bool save();
uint8_t getBoardType();
void setBoardType(uint8_t boardType);
String getWifiSsid();
void setWifiSsid(String wifiSsid);
String getWifiPassword();
void setWifiPassword(String wifiPassword);
String getWifiIp();
void setWifiIp(String wifiIp);
String getWifiGw();
void setWifiGw(String wifiGw);
String getWifiSubnet();
void setWifiSubnet(String wifiSubnet);
String getWifiDns1();
void setWifiDns1(String wifiDns1);
String getWifiDns2();
void setWifiDns2(String wifiDns1);
String getWifiHostname();
void setWifiHostname(String wifiHostname);
char* getWifiSsid();
void setWifiSsid(const char* wifiSsid);
char* getWifiPassword();
void setWifiPassword(const char* wifiPassword);
char* getWifiIp();
void setWifiIp(const char* wifiIp);
char* getWifiGw();
void setWifiGw(const char* wifiGw);
char* getWifiSubnet();
void setWifiSubnet(const char* wifiSubnet);
char* getWifiDns1();
void setWifiDns1(const char* wifiDns1);
char* getWifiDns2();
void setWifiDns2(const char* wifiDns1);
char* getWifiHostname();
void setWifiHostname(const char* wifiHostname);
void clearWifi();
void clearWifiIp();
bool isWifiChanged();
void ackWifiChange();
String getMqttHost();
void setMqttHost(String mqttHost);
int getMqttPort();
void setMqttPort(int mqttPort);
String getMqttClientId();
void setMqttClientId(String mqttClientId);
String getMqttPublishTopic();
void setMqttPublishTopic(String mqttPublishTopic);
String getMqttSubscribeTopic();
void setMqttSubscribeTopic(String mqttSubscribeTopic);
String getMqttUser();
void setMqttUser(String mqttUser);
String getMqttPassword();
void setMqttPassword(String mqttPassword);
int getMqttPayloadFormat();
void setMqttPayloadFormat(int mqttPayloadFormat);
char* getMqttHost();
void setMqttHost(const char* mqttHost);
uint16_t getMqttPort();
void setMqttPort(uint16_t mqttPort);
char* getMqttClientId();
void setMqttClientId(const char* mqttClientId);
char* getMqttPublishTopic();
void setMqttPublishTopic(const char* mqttPublishTopic);
char* getMqttSubscribeTopic();
void setMqttSubscribeTopic(const char* mqttSubscribeTopic);
char* getMqttUser();
void setMqttUser(const char* mqttUser);
char* getMqttPassword();
void setMqttPassword(const char* mqttPassword);
uint8_t getMqttPayloadFormat();
void setMqttPayloadFormat(uint8_t mqttPayloadFormat);
bool isMqttSsl();
void setMqttSsl(bool mqttSsl);
void clearMqtt();
void setMqttChanged();
bool isMqttChanged();
void ackMqttChange();
byte getAuthSecurity();
void setAuthSecurity(byte authSecurity);
String getAuthUser();
void setAuthUser(String authUser);
String getAuthPassword();
void setAuthPassword(String authPassword);
char* getAuthUser();
void setAuthUser(const char* authUser);
char* getAuthPassword();
void setAuthPassword(const char* authPassword);
void clearAuth();
int getMeterType();
void setMeterType(int meterType);
int getDistributionSystem();
void setDistributionSystem(int distributionSystem);
int getMainFuse();
void setMainFuse(int mainFuse);
int getProductionCapacity();
void setProductionCapacity(int productionCapacity);
uint8_t getMeterType();
void setMeterType(uint8_t meterType);
uint8_t getDistributionSystem();
void setDistributionSystem(uint8_t distributionSystem);
uint8_t getMainFuse();
void setMainFuse(uint8_t mainFuse);
uint8_t getProductionCapacity();
void setProductionCapacity(uint8_t productionCapacity);
bool isSubstituteMissing();
void setSubstituteMissing(bool substituteMissing);
bool isSendUnknown();
void setSendUnknown(bool sendUnknown);
void clearMeter();
bool isDebugTelnet();
void setDebugTelnet(bool debugTelnet);
bool isDebugSerial();
void setDebugSerial(bool debugSerial);
int getDebugLevel();
void setDebugLevel(int debugLevel);
uint8_t getDebugLevel();
void setDebugLevel(uint8_t debugLevel);
bool pinUsed(uint8_t pin);
uint8_t getHanPin();
void setHanPin(uint8_t hanPin);
uint8_t getApPin();
void setApPin(uint8_t apPin);
uint8_t getLedPin();
void setLedPin(uint8_t ledPin);
bool isLedInverted();
void setLedInverted(bool ledInverted);
uint8_t getLedPinRed();
void setLedPinRed(uint8_t ledPinRed);
uint8_t getLedPinGreen();
void setLedPinGreen(uint8_t ledPinGreen);
uint8_t getLedPinBlue();
void setLedPinBlue(uint8_t ledPinBlue);
bool isLedRgbInverted();
void setLedRgbInverted(bool ledRgbInverted);
uint8_t getTempSensorPin();
void setTempSensorPin(uint8_t tempSensorPin);
uint8_t getVccPin();
void setVccPin(uint8_t vccPin);
double getVccMultiplier();
void setVccMultiplier(double vccMultiplier);
double getVccBootLimit();
void setVccBootLimit(double vccBootLimit);
void print(Print* debugger);
uint16_t getDomoELIDX();
uint16_t getDomoVL1IDX();
uint16_t getDomoVL2IDX();
uint16_t getDomoVL3IDX();
uint16_t getDomoCL1IDX();
void setDomoELIDX(uint16_t domoELIDX);
void setDomoVL1IDX(uint16_t domoVL1IDX);
void setDomoVL2IDX(uint16_t domoVL2IDX);
void setDomoVL3IDX(uint16_t domoVL3IDX);
void setDomoCL1IDX(uint16_t domoCL1IDX);
void clearDomo();
bool isDomoChanged();
void ackDomoChange();
void clear();
protected:
private:
int configVersion = 0;
ConfigObject config {
0, // Board type
"", // SSID
"", // PSK
"", // IP
"", // GW
"", // Subnet
"", // DNS 1
"", // DNS 2
"", // Hostname
"", // MQTT host
1883, // Port
"", // Client ID
"", // Publish topic
"", // Subscribe topic
"", // Username
"", // Password
0, // Format
false, // SSL
0, // Web security
"", // Username
"", // Password
0, // Meter type
0, // Distribution system
0, // Main fuse
0, // Production capacity
false, // Substitute
false, // Send unknown
false, // Debug telnet
false, // Debug serial
5, // Debug level
0x03, // HAN pin
0xFF, // AP pin
0x02, // LED pin
true, // Inverted
0xFF, // Red
0xFF, // Green
0xFF, // Blue
true, // Inverted
0xFF, // Temp sensor
0xFF, // Vcc
100, // Multiplier
0, // Boot limit
//Domoticz
0, // ELIDX
0, // VL1IDX
0, // VL2IDX
0, // VL3IDX
0 // CL1IDX
// 786 bytes
};
bool wifiChanged, mqttChanged, domoChanged;
String wifiSsid;
String wifiPassword;
String wifiIp;
String wifiGw;
String wifiSubnet;
String wifiDns1;
String wifiDns2;
String wifiHostname;
bool wifiChanged = false;
String mqttHost;
int mqttPort = 1883;
String mqttClientId;
String mqttPublishTopic;
String mqttSubscribeTopic;
String mqttUser;
String mqttPassword;
int mqttPayloadFormat = 0;
bool mqttChanged = false;
byte authSecurity;
String authUser;
String authPassword;
int meterType = 0, distributionSystem = 0, mainFuse = 0, productionCapacity = 0;
bool debugTelnet = false, debugSerial = false;
int debugLevel = 3;
const int EEPROM_SIZE = 512;
const int EEPROM_CHECK_SUM = 81; // Used to check if config is stored. Change if structure changes
const int EEPROM_SIZE = 790; // Config size + 4 bytes for config version
const int EEPROM_CHECK_SUM = 82; // Used to check if config is stored. Change if structure changes
const int EEPROM_CONFIG_ADDRESS = 0;
bool loadConfig72(int address);
bool loadConfig75(int address);
bool loadConfig80(int address);
bool loadConfig81(int address);
int saveString(int pAddress, const char* pString);
int readString(int pAddress, char* pString[]);
int saveInt(int pAddress, int pValue);
int readInt(int pAddress, int *pValue);
int saveBool(int pAddress, bool pValue);
int readBool(int pAddress, bool *pValue);
int saveByte(int pAddress, byte pValue);
int readByte(int pAddress, byte *pValue);
template <class T> int writeAnything(int ee, const T& value);
template <class T> int readAnything(int ee, T& value);
};
#endif

View File

@@ -5,7 +5,7 @@
AmsData::AmsData() {}
AmsData::AmsData(int meterType, HanReader& hanReader) {
AmsData::AmsData(int meterType, bool substituteMissing, HanReader& hanReader) {
lastUpdateMillis = millis();
packageTimestamp = hanReader.getPackageTime();
@@ -15,10 +15,10 @@ AmsData::AmsData(int meterType, HanReader& hanReader) {
extractFromKaifa(hanReader, listSize);
break;
case METER_TYPE_AIDON:
extractFromAidon(hanReader, listSize);
extractFromAidon(hanReader, listSize, substituteMissing);
break;
case METER_TYPE_KAMSTRUP:
extractFromKamstrup(hanReader, listSize);
extractFromKamstrup(hanReader, listSize, substituteMissing);
break;
}
@@ -87,7 +87,7 @@ void AmsData::extractFromKaifa(HanReader& hanReader, int listSize) {
}
}
void AmsData::extractFromAidon(HanReader& hanReader, int listSize) {
void AmsData::extractFromAidon(HanReader& hanReader, int listSize, bool substituteMissing) {
switch(listSize) {
case (int)Aidon::List1:
listType = 1;
@@ -168,19 +168,23 @@ void AmsData::extractFromAidon(HanReader& hanReader, int listSize) {
l1voltage = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL1)) / 10;
l2voltage = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL2)) / 10;
l3voltage = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL3)) / 10;
//l2current = ((activeImportPower * sqrt(3)) - (l1voltage * l1current) - (l3voltage * l3current)) / l2voltage;
if(substituteMissing) {
l2current = ((activeImportPower * sqrt(3)) - (l1voltage * l1current) - (l3voltage * l3current)) / l2voltage;
}
break;
}
}
}
void AmsData::extractFromKamstrup(HanReader& hanReader, int listSize) {
void AmsData::extractFromKamstrup(HanReader& hanReader, int listSize, bool substituteMissing) {
switch(listSize) {
case (int)Kamstrup::List3PhaseITShort:
case (int)Kamstrup::List3PhaseShort:
threePhase = true;
case (int)Kamstrup::List1PhaseShort:
listType = 2;
break;
case (int)Kamstrup::List3PhaseITLong:
case (int)Kamstrup::List3PhaseLong:
threePhase = true;
case (int)Kamstrup::List1PhaseLong:
@@ -189,6 +193,23 @@ void AmsData::extractFromKamstrup(HanReader& hanReader, int listSize) {
}
switch(listSize) {
case (int)Kamstrup::List1PhaseLong:
meterTimestamp = hanReader.getTime( (int)Kamstrup_List1Phase::MeterClock);
activeImportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeActiveImportEnergy)) / 100;
activeExportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeActiveExportEnergy)) / 100;
reactiveImportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeReactiveImportEnergy)) / 100;
reactiveExportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeReactiveExportEnergy)) / 100;
case (int)Kamstrup::List1PhaseShort:
listId = hanReader.getString( (int)Kamstrup_List1Phase::ListVersionIdentifier);
meterId = hanReader.getString( (int)Kamstrup_List1Phase::MeterID);
meterType = hanReader.getString( (int)Kamstrup_List1Phase::MeterType);
activeImportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ActiveImportPower);
reactiveImportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ReactiveImportPower);
activeExportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ActiveExportPower);
reactiveExportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ReactiveExportPower);
l1current = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CurrentL1)) / 100;
l1voltage = hanReader.getInt( (int)Kamstrup_List1Phase::VoltageL1);
break;
case (int)Kamstrup::List3PhaseLong:
meterTimestamp = hanReader.getTime( (int)Kamstrup_List3Phase::MeterClock);
activeImportCounter = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CumulativeActiveImportEnergy)) / 100;
@@ -210,22 +231,29 @@ void AmsData::extractFromKamstrup(HanReader& hanReader, int listSize) {
l2voltage = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL2);
l3voltage = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL3);
break;
case (int)Kamstrup::List1PhaseLong:
meterTimestamp = hanReader.getTime( (int)Kamstrup_List1Phase::MeterClock);
activeImportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeActiveImportEnergy)) / 100;
activeExportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeActiveExportEnergy)) / 100;
reactiveImportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeReactiveImportEnergy)) / 100;
reactiveExportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeReactiveExportEnergy)) / 100;
case (int)Kamstrup::List1PhaseShort:
listId = hanReader.getString( (int)Kamstrup_List1Phase::ListVersionIdentifier);
meterId = hanReader.getString( (int)Kamstrup_List1Phase::MeterID);
meterType = hanReader.getString( (int)Kamstrup_List1Phase::MeterType);
activeImportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ActiveImportPower);
reactiveImportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ReactiveImportPower);
activeExportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ActiveExportPower);
reactiveExportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ReactiveExportPower);
l1current = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CurrentL1)) / 100;
l1voltage = hanReader.getInt( (int)Kamstrup_List1Phase::VoltageL1);
case (int)Kamstrup::List3PhaseITLong:
meterTimestamp = hanReader.getTime( (int)Kamstrup_List3Phase::MeterClock);
activeImportCounter = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CumulativeActiveImportEnergy)) / 100;
activeExportCounter = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CumulativeActiveExportEnergy)) / 100;
reactiveImportCounter = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CumulativeReactiveImportEnergy)) / 100;
reactiveExportCounter = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CumulativeReactiveExportEnergy)) / 100;
case (int)Kamstrup::List3PhaseITShort:
listId = hanReader.getString( (int)Kamstrup_List3Phase::ListVersionIdentifier);
meterId = hanReader.getString( (int)Kamstrup_List3Phase::MeterID);
meterType = hanReader.getString( (int)Kamstrup_List3Phase::MeterType);
activeImportPower = hanReader.getInt( (int)Kamstrup_List3Phase::ActiveImportPower);
reactiveImportPower = hanReader.getInt( (int)Kamstrup_List3Phase::ReactiveImportPower);
activeExportPower = hanReader.getInt( (int)Kamstrup_List3Phase::ActiveExportPower);
reactiveExportPower = hanReader.getInt( (int)Kamstrup_List3Phase::ReactiveExportPower);
l1current = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CurrentL1)) / 100;
l2current = 0;
l3current = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CurrentL3)) / 100;
l1voltage = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL1);
l2voltage = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL2);
l3voltage = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL3);
if(substituteMissing) {
l2current = ((activeImportPower * sqrt(3)) - (l1voltage * l1current) - (l3voltage * l3current)) / l2voltage;
}
break;
}
}

View File

@@ -12,7 +12,7 @@
class AmsData {
public:
AmsData();
AmsData(int meterType, HanReader& hanReader);
AmsData(int meterType, bool substituteMissing, HanReader& hanReader);
void apply(AmsData& other);
@@ -60,8 +60,8 @@ private:
bool threePhase = false;
void extractFromKaifa(HanReader& hanReader, int listSize);
void extractFromAidon(HanReader& hanReader, int listSize);
void extractFromKamstrup(HanReader& hanReader, int listSize);
void extractFromAidon(HanReader& hanReader, int listSize, bool substituteMissing);
void extractFromKamstrup(HanReader& hanReader, int listSize, bool substituteMissing);
};
#endif

10
src/AmsStorage.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef _AMSSTORAGE_H
#define _AMSSTORAGE_H
#define FILE_FIRMWARE "/firmware.bin"
#define FILE_MQTT_CA "/mqtt-ca.pem"
#define FILE_MQTT_CERT "/mqtt-cert.pem"
#define FILE_MQTT_KEY "/mqtt-key.pem"
#endif

View File

@@ -5,73 +5,17 @@
#define INVALID_BUTTON_PIN 0xFFFFFFFF
#include <SoftwareSerial.h>
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#elif defined(ESP32)
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <ESPmDNS.h>
#include "SPIFFS.h"
#include "Update.h"
#endif
#define RGB_RED 1
#define RGB_GREEN 2
#define RGB_YELLOW 3
#define RGB_ON 1
#define RGB_OFF 0
// Build settings for custom hardware by Roar Fredriksen
#if HW_ROARFRED
#define LED_PIN 2 // The blue on-board LED of the ESP8266 custom AMS board
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN 0
HardwareSerial *hanSerial = &Serial;
// Build settings for Wemos Lolin D32
#elif defined(ARDUINO_LOLIN_D32)
#define LED_PIN 5
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN 4
HardwareSerial *hanSerial = &Serial2;
// Build settings for Wemos D1 mini
#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI)
#define LED_PIN D4
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN D2
#define SOFTWARE_SERIAL 1
#include <SoftwareSerial.h>
SoftwareSerial *hanSerial = new SoftwareSerial(D1);
// Build settings for Adafruit Feather ESP32
#elif defined(ARDUINO_FEATHER_ESP32)
#define LED_PIN LED_BUILTIN
#define LED_ACTIVE_HIGH 1
#define AP_BUTTON_PIN INVALID_BUTTON_PIN
HardwareSerial *hanSerial = &Serial2;
// Default build for ESP32
#elif defined(ESP32)
#define LED_PIN INVALID_BUTTON_PIN
#define LED_ACTIVE_HIGH 1
#define AP_BUTTON_PIN INVALID_BUTTON_PIN
HardwareSerial *hanSerial = &Serial2;
// Default build settings
#else
#define LED_PIN 2
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN INVALID_BUTTON_PIN
#define SOFTWARE_SERIAL 1
#include <SoftwareSerial.h>
SoftwareSerial *hanSerial = new SoftwareSerial(5);
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,8 @@
#include "HanToJson.h"
void hanToJson(JsonDocument& json, AmsData& data, HwTools& hw, double temperature) {
void hanToJson(JsonDocument& json, AmsData& data, HwTools& hw, double temperature, String name) {
json["id"] = WiFi.macAddress();
json["name"] = name;
json["up"] = millis();
json["t"] = data.getPackageTimestamp();

View File

@@ -11,6 +11,6 @@
#include "AmsData.h"
#include "HwTools.h"
void hanToJson(JsonDocument& json, AmsData& data, HwTools& hw, double temperature);
void hanToJson(JsonDocument& json, AmsData& data, HwTools& hw, double temperature, String name);
#endif

View File

@@ -1,44 +1,184 @@
#include "HwTools.h"
void HwTools::setTempSensorPin(int tempSensorPin) {
if(tempSensorPin != this->tempSensorPin) {
this->tempSensorInit = false;
if(tempSensor)
delete tempSensor;
if(oneWire)
delete oneWire;
if(tempSensorPin > 0 && tempSensorPin < 40) {
this->tempSensorPin = tempSensorPin;
pinMode(tempSensorPin, INPUT);
} else {
this->tempSensorPin = 0xFF;
}
}
}
void HwTools::setVccPin(int vccPin) {
if(vccPin > 0 && vccPin < 40) {
pinMode(vccPin, INPUT);
this->vccPin = vccPin;
} else {
this->vccPin = 0xFF;
}
}
void HwTools::setVccMultiplier(double vccMultiplier) {
this->vccMultiplier = vccMultiplier;
}
double HwTools::getVcc() {
double volts = 0.0;
#if defined(ARDUINO_ESP8266_WEMOS_D1MINI)
volts = (((double) ESP.getVcc()) / 900.0); // This board has a voltage divider on VCC.
#elif defined(ARDUINO_LOLIN_D32)
volts = (analogRead(GPIO_NUM_35) / 4095.0) * 3.3 * 2.25; // We are actually reading battery voltage here
#elif defined(ESP8266)
volts = ((double) ESP.getVcc()) / 1024.0;
#endif
if(vccPin != 0xFF) {
#if defined(ESP8266)
volts = (analogRead(vccPin) / 1024.0) * 3.3;
#elif defined(ESP32)
volts = (analogRead(vccPin) / 4095.0) * 3.3;
#endif
} else {
#if defined(ESP8266)
volts = ((double) ESP.getVcc()) / 1024.0;
#endif
}
#if defined(ESP_VCC_CALIB_FACTOR)
return volts * ESP_VCC_CALIB_FACTOR;
#else
return volts;
#endif
return volts > 0.0 ? volts * vccMultiplier : 0.0;
}
double HwTools::getTemperature() {
if(tempSensorPin != 0xFF) {
if(!tempSensorInit) {
oneWire = new OneWire(tempSensorPin);
tempSensor = new DallasTemperature(this->oneWire);
tempSensor->begin();
delay(50);
tempSensor->requestTemperatures();
hasTempSensor = tempSensor->getTempCByIndex(0) != DEVICE_DISCONNECTED_C;
tempSensorInit = true;
}
#if defined TEMP_SENSOR_PIN
if(!tempSensorInit) {
tempSensor->begin();
delay(50);
tempSensor->requestTemperatures();
hasTempSensor = tempSensor->getTempCByIndex(0) != DEVICE_DISCONNECTED_C;
tempSensorInit = true;
if(hasTempSensor) {
tempSensor->requestTemperatures();
return tempSensor->getTempCByIndex(0);
} else {
return DEVICE_DISCONNECTED_C;
}
}
if(hasTempSensor) {
tempSensor->requestTemperatures();
return tempSensor->getTempCByIndex(0);
} else {
return DEVICE_DISCONNECTED_C;
}
#endif
return DEVICE_DISCONNECTED_C;
}
int HwTools::getWifiRssi() {
int rssi = WiFi.RSSI();
return isnan(rssi) ? -100.0 : rssi;
}
}
void HwTools::setLed(uint8_t ledPin, bool ledInverted) {
if(ledPin > 0 && ledPin < 40) {
this->ledPin = ledPin;
this->ledInverted = ledInverted;
pinMode(ledPin, OUTPUT);
ledOff(LED_INTERNAL);
} else {
this->ledPin = 0xFF;
}
}
void HwTools::setLedRgb(uint8_t ledPinRed, uint8_t ledPinGreen, uint8_t ledPinBlue, bool ledRgbInverted) {
this->ledRgbInverted = ledRgbInverted;
if(ledPinRed > 0 && ledPinRed < 40) {
this->ledPinRed = ledPinRed;
pinMode(ledPinRed, OUTPUT);
ledOff(LED_RED);
} else {
this->ledPinRed = 0xFF;
}
if(ledPinGreen > 0 && ledPinGreen < 40) {
this->ledPinGreen = ledPinGreen;
pinMode(ledPinGreen, OUTPUT);
ledOff(LED_GREEN);
} else {
this->ledPinGreen = 0xFF;
}
if(ledPinBlue > 0 && ledPinBlue < 40) {
this->ledPinBlue = ledPinBlue;
pinMode(ledPinBlue, OUTPUT);
ledOff(LED_BLUE);
} else {
this->ledPinBlue = 0xFF;
}
}
bool HwTools::ledOn(uint8_t color) {
if(color == LED_INTERNAL) {
return writeLedPin(color, ledInverted ? LOW : HIGH);
} else {
return writeLedPin(color, ledRgbInverted ? LOW : HIGH);
}
}
bool HwTools::ledOff(uint8_t color) {
if(color == LED_INTERNAL) {
return writeLedPin(color, ledInverted ? HIGH : LOW);
} else {
return writeLedPin(color, ledRgbInverted ? HIGH : LOW);
}
}
bool HwTools::ledBlink(uint8_t color, uint8_t blink) {
for(int i = 0; i < blink; i++) {
if(!ledOn(color)) return false;
delay(50);
ledOff(color);
if(i != blink)
delay(50);
}
}
bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
switch(color) {
case LED_INTERNAL:
if(ledPin != 0xFF) {
digitalWrite(ledPin, state);
return true;
} else {
return false;
}
break;
case LED_RED:
if(ledPinRed != 0xFF) {
digitalWrite(ledPinRed, state);
return true;
} else {
return false;
}
break;
case LED_GREEN:
if(ledPinGreen != 0xFF) {
digitalWrite(ledPinGreen, state);
return true;
} else {
return false;
}
break;
case LED_BLUE:
if(ledPinBlue != 0xFF) {
digitalWrite(ledPinBlue, state);
return true;
} else {
return false;
}
break;
case LED_YELLOW:
if(ledPinRed != 0xFF && ledPinGreen != 0xFF) {
digitalWrite(ledPinRed, state);
digitalWrite(ledPinGreen, state);
return true;
} else {
return false;
}
break;
}
return false;
}

View File

@@ -12,35 +12,38 @@
#include <DallasTemperature.h>
#include <OneWire.h>
#if HW_ROARFRED
#define TEMP_SENSOR_PIN 5
#elif defined(ARDUINO_LOLIN_D32)
#define TEMP_SENSOR_PIN 14
#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI)
#define TEMP_SENSOR_PIN D5
#else
#define TEMP_SENSOR_PIN 0xFFFFFFFF
#endif
#define LED_INTERNAL 0
#define LED_RED 1
#define LED_GREEN 2
#define LED_BLUE 3
#define LED_YELLOW 4
class HwTools {
public:
void setTempSensorPin(int tempSensorPin);
void setVccPin(int vccPin);
void setVccMultiplier(double vccMultiplier);
double getVcc();
double getTemperature();
int getWifiRssi();
void setLed(uint8_t ledPin, bool ledInverted);
void setLedRgb(uint8_t ledPinRed, uint8_t ledPinGreen, uint8_t ledPinBlue, bool ledRgbInverted);
bool ledOn(uint8_t color);
bool ledOff(uint8_t color);
bool ledBlink(uint8_t color, uint8_t blink);
HwTools() {
#if defined(ARDUINO_LOLIN_D32)
pinMode(GPIO_NUM_35, INPUT);
#endif
oneWire = new OneWire(TEMP_SENSOR_PIN);
tempSensor = new DallasTemperature(this->oneWire);
};
HwTools() {};
private:
uint8_t tempSensorPin = -1;
uint8_t vccPin = -1;
uint8_t ledPin = -1, ledPinRed = -1, ledPinGreen = -1, ledPinBlue = -1;
bool ledInverted, ledRgbInverted;
double vccMultiplier = 1.0;
bool tempSensorInit, hasTempSensor;
OneWire *oneWire;
DallasTemperature *tempSensor;
bool writeLedPin(uint8_t color, uint8_t state);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -20,9 +20,11 @@
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#elif defined(ESP32) // ARDUINO_ARCH_ESP32
#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include "SPIFFS.h"
#include "Update.h"
#else
@@ -31,7 +33,7 @@
class AmsWebServer {
public:
AmsWebServer(RemoteDebug* Debug);
AmsWebServer(RemoteDebug* Debug, HwTools* hw);
void setup(AmsConfiguration* config, MQTTClient* mqtt);
void loop();
@@ -40,11 +42,12 @@ public:
private:
RemoteDebug* debugger;
int maxPwr = 0;
HwTools hw;
HwTools* hw;
AmsConfiguration* config;
AmsData data;
MQTTClient* mqtt;
File firmwareFile;
bool uploading = false;
File file;
bool performRestart = false;
#if defined(ESP8266)
@@ -56,22 +59,48 @@ private:
bool checkSecurity(byte level);
void indexHtml();
void applicationJs();
void configMeterHtml();
void configWifiHtml();
void configMqttHtml();
void configWebHtml();
void configDomoticzHtml();
void bootCss();
void gaugemeterJs();
void githubSvg();
void dataJson();
void handleSetup();
void handleSave();
void configSystemHtml();
void configSystemPost();
void configSystemUpload();
String getSerialSelectOptions(int selected);
void firmwareHtml();
void firmwareUpload();
void firmwareDownload();
void restartWaitHtml();
void isAliveCheck();
void uploadHtml(const char* label, const char* action, const char* menu);
void deleteHtml(const char* label, const char* action, const char* menu);
void uploadFile(const char* path);
void deleteFile(const char* path);
void uploadPost();
void mqttCa();
void mqttCaUpload();
void mqttCaDelete();
void mqttCert();
void mqttCertUpload();
void mqttCertDelete();
void mqttKey();
void mqttKeyUpload();
void mqttKeyDelete();
void factoryResetHtml();
void factoryResetPost();
void notFound();
void printD(String fmt, ...);
void printI(String fmt, ...);
void printW(String fmt, ...);

339
web/application.js Normal file
View File

@@ -0,0 +1,339 @@
var nextVersion;
var im, em;
$(function() {
im = $("#importMeter");
if(im && im.gaugeMeter) {
im.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
}
em = $("#exportMeter");
if(em && em.gaugeMeter) {
em.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
}
var meters = $('.SimpleMeter');
if(meters.length > 0) {
fetch();
}
// For config-mqtt
$('#mqttEnable').on('change', function() {
var inputs = $('.mqtt-config');
inputs.prop('disabled', !$(this).is(':checked'));
});
$('#mqttPayloadFormat').on('change', function() {
var val = parseInt($(this).val());
if(val == 3) {
$('.format-type-domoticz').show();
} else {
$('.format-type-domoticz').hide();
}
});
$('#mqttSsl').on('change', function() {
var port = $('#mqttPort');
if($(this).is(':checked')) {
if(port.val() == 1883) {
port.val(8883);
}
} else {
if(port.val() == 8883) {
port.val(1883);
}
}
});
$('#mqttEnable').trigger('change');
$('#mqttPayloadFormat').trigger('change');
// For config-meter
$('.subtitute-dependent').on('change', function() {
console.log("test");
if(($('#meterType').val() == 2 || $('#meterType').val() == 3) && $('#distributionSystem').val() == 1) {
$('#substitute').show();
} else {
$('#substitute').hide();
}
});
$('#meterType').trigger('change');
// For config-wifi
$('#wifiIpType').on('change', function() {
if($(this).is(':checked')) {
$('#staticIp').show();
} else {
$('#staticIp').hide();
}
});
$('#wifiIpType').trigger('change');
// For config-web
$('#authSecurity').on('change', function() {
var inputs = $('.auth-config');
inputs.prop('disabled', $(this).val() == 0);
});
$('#authSecurity').trigger('change');
// For file upload
$('#fileUploadField').on('change',function(){
var fileName = $(this).val();
$(this).next('.custom-file-label').html(fileName);
})
switch(window.location.pathname) {
case '/config-meter':
$('#config-meter-link').addClass('active');
break;
case '/config-wifi':
$('#config-wifi-link').addClass('active');
break;
case '/config-mqtt':
case '/mqtt-ca':
case '/mqtt-cert':
case '/mqtt-key':
case '/config-domoticz':
$('#config-mqtt-link').addClass('active');
break;
case '/config-web':
$('#config-web-link').addClass('active');
break;
case '/config-system':
case '/firmware':
case '/reset':
$('#config-system-link').addClass('active');
break;
}
var swv = $('#swVersion')
if(meters.length > 0 && swv.length == 1 && swv.text() != "SNAPSHOT") {
var v = swv.text().substring(1).split('.');
var v_major = parseInt(v[0]);
var v_minor = parseInt(v[1]);
var v_patch = parseInt(v[2]);
$.ajax({
url: swv.data('url'),
dataType: 'json'
}).done(function(releases) {
releases.reverse();
var me;
var next_patch;
var next_minor;
var next_major;
$.each(releases, function(i, release) {
var ver2 = release.tag_name;
var v2 = ver2.substring(1).split('.');
var v2_major = parseInt(v2[0]);
var v2_minor = parseInt(v2[1]);
var v2_patch = parseInt(v2[2]);
if(v2_major == v_major) {
if(v2_minor == v_minor) {
if(v2_patch > v_patch) {
next_patch = release;
}
} else if(v2_minor == v_minor+1) {
next_minor = release;
}
} else if(v2_major == v_major+1) {
if(next_major) {
var mv = next_major.tag_name.substring(1).split('.');
var mv_major = parseInt(mv[0]);
var mv_minor = parseInt(mv[1]);
var mv_patch = parseInt(mv[2]);
if(v2_minor == mv_minor) {
next_major = release;
}
} else {
next_major = release;
}
}
});
if(next_minor) {
nextVersion = next_minor;
} else if(next_major) {
nextVersion = next_major;
} else if(next_patch) {
nextVersion = next_patch;
}
if(nextVersion) {
$('#newVersionTag').text(nextVersion.tag_name);
$('#newVersionUrl').prop('href', nextVersion.html_url);
$('#newVersion').removeClass('d-none');
}
});
}
});
var setStatus = function(id, status) {
var item = $('#'+id);
item.removeClass('d-none');
item.removeClass (function (index, className) {
return (className.match (/(^|\s)badge-\S+/g) || []).join(' ');
});
item.addClass('badge badge-' + status);
};
var interval = 5000;
var fetch = function() {
$.ajax({
url: '/data.json',
timeout: 10000,
dataType: 'json',
}).done(function(json) {
if(im && em) {
$(".SimpleMeter").hide();
im.show();
em.show();
}
for(var id in json) {
var str = json[id];
if(typeof str === "object")
continue;
if(isNaN(str)) {
$('#'+id).html(str);
} else {
var num = parseFloat(str);
$('#'+id).html(num.toFixed(num < 0 ? 0 : num < 10 ? 2 : 1));
}
}
if(window.moment) {
$('#currentMillis').html(moment.duration(parseInt(json.uptime_seconds), 'seconds').humanize());
$('#currentMillis').closest('.row').show();
}
if(json.status) {
for(var id in json.status) {
setStatus(id, json.status[id]);
}
}
if(json.mqtt) {
$('.mqtt-error').addClass('d-none');
$('.mqtt-error'+json.mqtt.lastError).removeClass('d-none');
$('#mqtt-lastError').html(json.mqtt.lastError);
}
if(json.wifi) {
for(var id in json.wifi) {
var str = json.wifi[id];
dst = $('#'+id);
if(isNaN(str)) {
dst.html(str);
} else {
var num = parseFloat(str);
dst.html(num.toFixed(0));
$('#'+id+'-row').show();
}
}
}
if(json.data) {
var p = 0;
var p_pct = parseInt(json.p_pct);
var p_append = "W";
if(json.data.P) {
p = parseFloat(json.data.P);
if(p > 1000) {
p = (p/1000).toFixed(1);
p_append = "kW";
}
}
if(im && im.gaugeMeter) {
im.gaugeMeter({
percent: p_pct,
text: p,
append: p_append
});
}
var po = 0;
var po_pct = parseInt(json.po_pct);
var po_append = "W";
if(json.data.PO) {
po = parseFloat(json.data.PO);
if(po > 1000) {
po = (po/1000).toFixed(1);
po_append = "kW";
}
}
if(em && em.gaugeMeter) {
em.gaugeMeter({
percent: po_pct,
text: po,
append: po_append
});
}
for(var id in json.data) {
var str = json.data[id];
if(isNaN(str)) {
$('#'+id).html(str);
} else {
var num = parseFloat(str);
$('#'+id).html(num.toFixed(1));
$('#'+id+'-row').show();
}
}
} else {
if(im && im.gaugeMeter) {
im.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
}
if(em && em.gaugeMeter) {
em.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
}
}
setTimeout(fetch, interval);
}).fail(function() {
setTimeout(fetch, interval*4);
if(im && im.gaugeMeter) {
im.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
}
if(em && em.gaugeMeter) {
em.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
}
setStatus("mqtt", "secondary");
setStatus("wifi", "secondary");
setStatus("han", "secondary");
setStatus("esp", "danger");
});
}
var upgrade = function() {
if(nextVersion) {
if(confirm("Are you sure you want to perform upgrade to " + nextVersion.tag_name + "?")) {
window.location.href="/upgrade?version=" + nextVersion.tag_name;
}
}
}

View File

@@ -56,24 +56,9 @@ body {
.bg-purple {
background-color: var(--purple);
}
.text-white-50 {
color: rgba(255,255,255,.5)!important;
}
.mb-0, .my-0 {
margin-bottom: 0!important;
}
.m-2 {
margin: .5rem!important;
}
.mb-2, .my-2 {
margin-bottom: .5rem!important;
}
.mt-2, .my-2 {
margin-top: .5rem!important;
}
.pb-2, .py-2 {
padding-bottom: .5rem!important;
}
.p-3 {
padding: 1rem!important;
}
@@ -89,19 +74,9 @@ body {
.shadow {
box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important;
}
.align-items-center {
-ms-flex-align: center!important;
align-items: center!important;
}
.border-bottom {
border-bottom: 1px solid #dee2e6!important;
}
.rounded {
border-radius: .25rem!important;
}
div {
display: block;
}
.container {
width: 100%;
padding-right: 15px;
@@ -115,15 +90,9 @@ article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
main {
display: block;
}
.text-white {
color: #fff!important;
}
.text-right {
text-align: right!important;
}
.text-center {
text-align: center!important;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
margin-bottom: .5rem;
font-weight: 500;
@@ -133,6 +102,9 @@ h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: .5rem;
}
.h5, h5 {
font-size: 1.25rem;
}
.h6, h6 {
font-size: 1rem;
}
@@ -150,48 +122,14 @@ h1, h2, h3, h4, h5, h6 {
padding-right: 15px;
padding-left: 15px;
}
.col-2 {
-ms-flex: 0 0 16.666667%;
flex: 0 0 16.666667%;
max-width: 16.666667%;
}
.col-3 {
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
}
.col-4 {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
.col-5 {
-ms-flex: 0 0 41.666667%;
flex: 0 0 41.666667%;
max-width: 41.666667%;
}
.col-6 {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
.col-8 {
-ms-flex: 0 0 66.666667%;
flex: 0 0 66.666667%;
max-width: 66.666667%;
}
.col-9 {
-ms-flex: 0 0 75%;
flex: 0 0 75%;
max-width: 75%;
}
.d-none {
display: none!important;
}
.d-flex {
display: -ms-flexbox!important;
display: flex!important;
}
.flex-row {
-ms-flex-direction: row!important;
flex-direction: row!important;
@@ -225,9 +163,11 @@ a {
border-radius: .25rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.btn-outline-secondary {
color: #6c757d;
border-color: #6c757d;
.btn-group-sm>.btn, .btn-sm {
padding: .25rem .5rem;
font-size: .875rem;
line-height: 1.5;
border-radius: .2rem;
}
.btn-primary {
color: #fff;
@@ -295,46 +235,6 @@ a {
.navbar-dark .navbar-nav .active>.nav-link, .navbar-dark .navbar-nav .nav-link.active, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .show>.nav-link {
color: #fff;
}
.badge {
display: inline-block;
padding: .25em .4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.badge-secondary {
color: #fff;
background-color: #6c757d;
}
.badge-success {
color: #fff;
background-color: #28a745;
}
.badge-warning {
color: #212529;
background-color: #ffc107;
}
.badge-danger {
color: #fff;
background-color: #dc3545;
}
.alert {
position: relative;
padding: .75rem 1.25rem;
margin-bottom: 1rem;
border: 1px solid transparent;
border-radius: .25rem;
}
.alert-warning {
color: #856404;
background-color: #fff3cd;
border-color: #ffeeba;
}
.form-group {
margin-bottom: 1rem;
}
@@ -386,6 +286,9 @@ input[type="radio"], input[type="checkbox"] {
.input-group-append {
margin-left: -1px;
}
.input-group-prepend {
margin-right: -1px;
}
.input-group-append, .input-group-prepend {
display: -ms-flexbox;
display: flex;
@@ -394,6 +297,14 @@ input[type="radio"], input[type="checkbox"] {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle), .input-group>.input-group-append:last-child>.input-group-text:not(:last-child), .input-group>.input-group-append:not(:last-child)>.btn, .input-group>.input-group-append:not(:last-child)>.input-group-text, .input-group>.input-group-prepend>.btn, .input-group>.input-group-prepend>.input-group-text {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group>.custom-select:not(:first-child), .input-group>.form-control:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.input-group-text {
display: -ms-flexbox;
display: flex;
@@ -453,6 +364,11 @@ dl, ol, ul {
.container, .container-sm {
max-width: 540px;
}
.col-sm-6 {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
}
@media (min-width: 768px) {
.container, .container-md, .container-sm {
@@ -463,11 +379,6 @@ dl, ol, ul {
flex: 0 0 25%;
max-width: 25%;
}
.col-md-4 {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
.col-md-6 {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
@@ -499,6 +410,21 @@ dl, ol, ul {
.container, .container-lg, .container-md, .container-sm, .container-xl {
max-width: 1140px;
}
.col-xl-2 {
-ms-flex: 0 0 16.666667%;
flex: 0 0 16.666667%;
max-width: 16.666667%;
}
.col-xl-3 {
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
}
.col-xl-4 {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
}
*, ::after, ::before {

60
web/configdomoticz.html Normal file
View File

@@ -0,0 +1,60 @@
<form method="post" action="/save">
<input type="hidden" name="domoConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<p>Domoticz Configuration. Requires that a Domoticz MQTT-message-broker is setup. HOWTO: https://www.domoticz.com/wiki/MQTT.</p>
<p>The following virtual sensors can currently be used:</p>
<ul>
<li>Electricity (instant and counter)</li>
<li>Electricity Current/Ampere 3 Phase</li>
<li>Voltage</li>
</ul>
<p>see: <a href="https://www.domoticz.com/wiki/Domoticz_API/JSON_URL's">https://www.domoticz.com/wiki/Domoticz_API/JSON_URL's</a></p>
<p>Create the sensors in Domoticz under Hardware > Dummy > Create virtual sensor, and use the IDX assigned to the sensor as input here.</p>
<p>"Electricity (instant and counter)" relies on Total energy import "tPI" and will not start before the first value is read (once an hour).</p>
</div>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="d-flex flex-row flex-wrap">
<div class="m-2 input-group input-group-sm" style="width: 200px;">
<div class="input-group-prepend">
<span class="input-group-text">Electricity IDX</span>
</div>
<input type="number" class="form-control" name="domoELIDX" value="${config.domoELIDX}" min="0" max="65535"/>
</div>
<div class="m-2 input-group input-group-sm" style="width: 240px;">
<div class="input-group-prepend">
<span class="input-group-text">Current (3 Phase) IDX</span>
</div>
<input type="number" class="form-control" name="domoCL1IDX" value="${config.domoCL1IDX}" min="0" max="65535"/>
</div>
</div>
<div class="d-flex flex-row flex-wrap">
<div class="m-2 input-group input-group-sm" style="width: 200px;">
<div class="input-group-prepend">
<span class="input-group-text">Voltage L1 IDX</span>
</div>
<input type="number" class="form-control" name="domoVL1IDX" value="${config.domoVL1IDX}" min="0" max="65535"/>
</div>
<div class="m-2 input-group input-group-sm" style="width: 200px;">
<div class="input-group-prepend">
<span class="input-group-text">Voltage L1 IDX</span>
</div>
<input type="number" class="form-control" name="domoVL2IDX" value="${config.domoVL2IDX}" min="0" max="65535"/>
</div>
<div class="m-2 input-group input-group-sm" style="width: 200px;">
<div class="input-group-prepend">
<span class="input-group-text">Voltage L1 IDX</span>
</div>
<input type="number" class="form-control" name="domoVL3IDX" value="${config.domoVL3IDX}" min="0" max="65535"/>
</div>
</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="/" class="btn btn-outline-secondary">Back</a>
</div>
<div class="col-6 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div>
</form>

View File

@@ -1,106 +1,78 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - Meter configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
</head>
<body class="bg-light">
<main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-md-row bg-purple rounded mt-2 mb-4" style="background-color: var(--purple);">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a>
<div class="navbar-nav-scroll">
<ul class="navbar-nav bd-navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link active" href="/config-meter">Meter</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-wifi">WiFi</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-mqtt">MQTT</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-web">Web</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-system">System</a>
</li>
</ul>
</div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
<svg class="d-inline-block align-text-top" style="width: 2rem; height: 2rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 499.36" focusable="false"><title>GitHub</title><path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="currentColor" fill-rule="evenodd"></path></svg>
</a>
</li>
</ul>
</header>
<form method="post" action="/save">
<input type="hidden" name="meterConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-md-4">
<div class="row form-group">
<label class="col-6">Meter type</label>
<div class="col-6">
<select class="form-control" name="meterType">
<option value="0" ${config.meterType0}>Autodetect</option>
<option value="1" ${config.meterType1}>Kaifa</option>
<option value="2" ${config.meterType2}>Aidon</option>
<option value="3" ${config.meterType3}>Kamstrup</option>
</select>
</div>
</div>
<div class="row form-group">
<label class="col-6">Distribution system</label>
<div class="col-6">
<select class="form-control" name="distributionSystem">
<option value="0" ${config.distributionSystem0}></option>
<option value="1" ${config.distributionSystem1}>IT (230V)</option>
<option value="2" ${config.distributionSystem2}>TN (400V)</option>
</select>
</div>
<form method="post" action="/save">
<input type="hidden" name="meterConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-lg-3 col-md-4 col-sm-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Meter type</span>
</div>
<select id="meterType" class="form-control subtitute-dependent" name="meterType">
<option value="0" ${config.meterType0}>Autodetect</option>
<option value="1" ${config.meterType1}>Kaifa</option>
<option value="2" ${config.meterType2}>Aidon</option>
<option value="3" ${config.meterType3}>Kamstrup</option>
</select>
</div>
<div class="col-md-4">
<div class="row form-group">
<label class="col-6">Main fuse</label>
<div class="col-6">
<select class="form-control" name="mainFuse">
<option value="0" ${config.mainFuse0}></option>
<option value="25" ${config.mainFuse25}>25A</option>
<option value="32" ${config.mainFuse32}>32A</option>
<option value="35" ${config.mainFuse32}>35A</option>
<option value="40" ${config.mainFuse40}>40A</option>
<option value="50" ${config.mainFuse50}>50A</option>
<option value="63" ${config.mainFuse63}>63A</option>
</select>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-md-5 col-sm-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Distribution system</span>
</div>
<div class="row form-group">
<label class="col-6">Production capacity</label>
<div class="col-6">
<div class="input-group">
<input class="form-control" name="productionCapacity" type="number" min="0" max="50" value="${config.productionCapacity}"/>
<div class="input-group-append"><span class="input-group-text">kWp</span></div>
</div>
</div>
<select id="distributionSystem" class="form-control subtitute-dependent" name="distributionSystem">
<option value="0" ${config.distributionSystem0}></option>
<option value="1" ${config.distributionSystem1}>IT (230V)</option>
<option value="2" ${config.distributionSystem2}>TN (400V)</option>
</select>
</div>
</div>
<div class="col-xl-2 col-md-3 col-sm-5">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Main fuse</span>
</div>
<select class="form-control" name="mainFuse">
<option value="0" ${config.mainFuse0}></option>
<option value="25" ${config.mainFuse25}>25A</option>
<option value="32" ${config.mainFuse32}>32A</option>
<option value="35" ${config.mainFuse32}>35A</option>
<option value="40" ${config.mainFuse40}>40A</option>
<option value="50" ${config.mainFuse50}>50A</option>
<option value="63" ${config.mainFuse63}>63A</option>
</select>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-md-5 col-sm-7">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Production capacity</span>
</div>
<input class="form-control" name="productionCapacity" type="number" min="0" max="50" value="${config.productionCapacity}"/>
<div class="input-group-append">
<span class="input-group-text">kWp</span>
</div>
</div>
</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="/" class="btn btn-outline-secondary">Back</a>
<div id="substitute" class="col-lg-3 col-md-4 col-sm-5">
<div class="m-2">
<label class="small"><input id="substituteMissing" type="checkbox" name="substituteMissing" value="true" ${config.substituteMissing}/> Substitute missing values</label>
</div>
</div>
<div class="col-6 text-right">
<button class="btn btn-primary">Save</button>
<div class="col-lg-3 col-md-4 col-sm-5">
<div class="m-2">
<label class="small"><input type="checkbox" name="sendUnknown" value="true" ${config.sendUnknown}/> Send unknown packets to MQTT</label>
</div>
</div>
</div>
</form>
</main>
</body>
</html>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="/" class="btn btn-outline-secondary">Back</a>
</div>
<div class="col-6 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div>
</form>

View File

@@ -1,103 +1,123 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - WiFi configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body class="bg-light">
<main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-md-row bg-purple rounded mt-2 mb-4" style="background-color: var(--purple);">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a>
<div class="navbar-nav-scroll">
<ul class="navbar-nav bd-navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link" href="/config-meter">Meter</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-wifi">WiFi</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/config-mqtt">MQTT</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-web">Web</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-system">System</a>
</li>
</ul>
</div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
<svg class="d-inline-block align-text-top" style="width: 2rem; height: 2rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 499.36" focusable="false"><title>GitHub</title><path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="currentColor" fill-rule="evenodd"></path></svg>
</a>
</li>
</ul>
</header>
<form method="post" action="/save">
<input type="hidden" name="mqttConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<label class="m-2"><input id="mqttEnable" type="checkbox" name="mqtt" value="true" ${config.mqtt}/> Enable</label>
<div class="row">
<div class="col-md-3">
<div class="row form-group">
<label class="col-4">Enable</label>
<div class="col-8">
<input id="mqttEnable" type="checkbox" name="mqtt" value="true" ${config.mqtt}/>
<div class="col-xl-4 col-lg-5 col-md-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Host</span>
</div>
<input type="text" class="form-control mqtt-config" name="mqttHost" value="${config.mqttHost}" maxlength="128"/>
</div>
<div class="row form-group">
<label class="col-4">Payload</label>
<div class="col-8">
<select class="form-control mqtt-config" name="mqttPayloadFormat">
<option value="0" ${config.mqttPayloadFormat0}>JSON</option>
<option value="1" ${config.mqttPayloadFormat1}>Raw</option>
</select>
</div>
<div class="col-xl-2 col-lg-3 col-md-3 col-sm-4">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Port</span>
</div>
<input id="mqttPort" type="number" class="form-control mqtt-config" name="mqttPort" value="${config.mqttPort}" min="1024" max="65535" placeholder="1883"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-md-6 col-sm-8">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Client ID</span>
</div>
<input type="text" class="form-control mqtt-config" name="mqttClientId" value="${config.mqttClientId}" maxlength="32"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Publish topic</span>
</div>
<input type="text" class="form-control mqtt-config" name="mqttPublishTopic" value="${config.mqttPublishTopic}" maxlength="64"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-md-6 col-sm-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Username</span>
</div>
<input type="text" class="form-control mqtt-config" name="mqttUser" value="${config.mqttUser}" maxlength="64"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-md-6 col-sm-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Password</span>
</div>
<input type="password" class="form-control mqtt-config" name="mqttPassword" value="${config.mqttPassword}" maxlength="64"/>
</div>
</div>
<div class="col-lg-3 col-md-4 col-sm-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Payload</span>
</div>
<select id="mqttPayloadFormat" class="form-control mqtt-config" name="mqttPayloadFormat">
<option value="0" ${config.mqttPayloadFormat0}>JSON</option>
<option value="1" ${config.mqttPayloadFormat1}>Raw (minimal)</option>
<option value="2" ${config.mqttPayloadFormat2}>Raw (full)</option>
<option value="3" ${config.mqttPayloadFormat3}>Domoticz</option>
</select>
</div>
</div>
<div class="col-md-3 col-sm-6 format-type-domoticz">
<div class="m-2">
<a href="/config-domoticz" class="btn btn-sm btn-outline-secondary">Configuration</a>
</div>
</div>
</div>
</div>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-md-2">
<label class="m-2"><input id="mqttSsl" type="checkbox" name="mqttSsl" value="true" ${config.mqttSsl}/> SSL</label>
</div>
<div class="col-lg-2 col-md-3">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">CA</span>
</div>
<div class="input-group-append">
<span style="display: ${display.ca.upload};">
<a href="/mqtt-ca" class="btn btn-sm btn-outline-secondary">Upload</a>
</span>
<span style="display: ${display.ca.file};">
<a href="/mqtt-ca" class="btn btn-sm btn-danger">Delete</a>
</span>
</div>
</div>
</div>
<div class="col-md-3">
<div class="row form-group">
<label class="col-4">Hostname</label>
<div class="col-8">
<input type="text" class="form-control mqtt-config" name="mqttHost" value="${config.mqttHost}"/>
<div class="col-xl-2 col-md-3">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Certificate</span>
</div>
</div>
<div class="row form-group">
<label class="col-4">Port</label>
<div class="col-8">
<input type="text" class="form-control mqtt-config" name="mqttPort" value="${config.mqttPort}"/>
<div class="input-group-append">
<span style="display: ${display.cert.upload};">
<a href="/mqtt-cert" class="btn btn-sm btn-outline-secondary">Upload</a>
</span>
<span style="display: ${display.cert.file};">
<a href="/mqtt-cert" class="btn btn-sm btn-danger">Delete</a>
</span>
</div>
</div>
</div>
<div class="col-md-3">
<div class="row form-group">
<label class="col-4">Client ID</label>
<div class="col-8">
<input type="text" class="form-control mqtt-config" name="mqttClientId" value="${config.mqttClientId}"/>
<div class="col-xl-2 col-md-4">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Private key</span>
</div>
</div>
<div class="row form-group">
<label class="col-4">Topic</label>
<div class="col-8">
<input type="text" class="form-control mqtt-config" name="mqttPublishTopic" value="${config.mqttPublishTopic}"/>
</div>
</div>
</div>
<div class="col-md-3">
<div class="row form-group">
<label class="col-4">Username</label>
<div class="col-8">
<input type="text" class="form-control mqtt-config" name="mqttUser" value="${config.mqttUser}"/>
</div>
</div>
<div class="row form-group">
<label class="col-4">Password</label>
<div class="col-8">
<input type="password" class="form-control mqtt-config" name="mqttPassword" value="${config.mqttPassword}"/>
<div class="input-group-append">
<span style="display: ${display.key.upload};">
<a href="/mqtt-key" class="btn btn-sm btn-outline-secondary">Upload</a>
</span>
<span style="display: ${display.key.file};">
<a href="/mqtt-key" class="btn btn-sm btn-danger">Delete</a>
</span>
</div>
</div>
</div>
@@ -113,16 +133,3 @@
</div>
</div>
</form>
</main>
<script>
$('#mqttEnable').on('change', function() {
var inputs = $('.mqtt-config');
inputs.prop('disabled', !$(this).is(':checked'));
});
$(function() {
$('#mqttEnable').trigger('change');
});
</script>
</body>
</html>

View File

@@ -1,100 +1,134 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - System configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/>
</head>
<body class="bg-light">
<main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-md-row bg-purple rounded mt-2 mb-4" style="background-color: var(--purple);">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a>
<div class="navbar-nav-scroll">
<ul class="navbar-nav bd-navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link" href="/config-meter">Meter</a>
</li>
<li class="nav-item">
<a class="nav-link " href="/config-wifi">WiFi</a>
</li>
<li class="nav-item">
<a class="nav-link " href="/config-mqtt">MQTT</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-web">Web</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/config-system">System</a>
</li>
</ul>
</div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
<svg class="d-inline-block align-text-top" style="width: 2rem; height: 2rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 499.36" focusable="false"><title>GitHub</title><path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="currentColor" fill-rule="evenodd"></path></svg>
</a>
</li>
</ul>
</header>
<form method="post" action="/save">
<input type="hidden" name="sysConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="alert alert-warning">!!WARNING!!<br/>Remote debugging is considered insecure and should not be left enabled when not debugging</div>
<div class="row">
<div class="col-md-3">
<label><input type="checkbox" name="debugTelnet" value="true" ${config.debugTelnet}/> Telnet debugger</label>
<div class="alert alert-warning">!!WARNING!!<br/>Do not change anything here unless you know exactly what you are doing! Changing things here could cause the device to stop responding</div>
<form method="post" action="/save">
<input type="hidden" name="sysConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<h6>GPIO settings</h6>
<div class="row">
<div class="col-xl-2 col-md-3 col-sm-6 col-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">HAN</span>
</div>
<select name="hanPin" class="form-control">
${options.han}
</select>
</div>
<div class="col-md-3">
<label><input type="checkbox" name="debugSerial" value="true" ${config.debugSerial}/> Serial debugger</label>
</div>
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-5 col-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">LED</span>
</div>
<input name="ledPin" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPin}"/>
<div class="input-group-append" title="Inverted">
<label class="input-group-text">
<input type="checkbox" name="ledInverted" value="true" ${config.ledInverted}/> inv
</label>
</div>
</div>
<div class="col-md-5">
<div class="row form-group">
<label class="col-6">Debug level</label>
<div class="col-6">
<select class="form-control" name="debugLevel">
<option value="2" ${config.debugLevel2}>Debug</option>
<option value="3" ${config.debugLevel3}>Info</option>
<option value="4" ${config.debugLevel4}>Warning</option>
<option value="5" ${config.debugLevel5}>Error</option>
</select>
</div>
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">RGB</span>
</div>
<input name="ledPinRed" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinRed}"/>
<input name="ledPinGreen" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinGreen}"/>
<input name="ledPinBlue" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinBlue}"/>
<div class="input-group-append" title="Inverted">
<label class="input-group-text">
<input type="checkbox" name="ledRgbInverted" value="true" ${config.ledRgbInverted}/> inv
</label>
</div>
</div>
</div>
<div class="col-lg-2 col-md-3 col-sm-6 col-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">AP button</span>
</div>
<input name="apPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.apPin}"/>
</div>
</div>
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 col-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Temperature</span>
</div>
<input name="tempSensorPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.tempSensorPin}"/>
</div>
</div>
<div class="col-xl-6 col-lg-8">
<div class="row p-2">
<div class="col-sm-3 col-5">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Vcc</span>
</div>
<input name="vccPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.vccPin}"/>
</div>
</div>
<div class="col-sm-4 col-7">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Multiplier</span>
</div>
<input type="number" min="0.1" max="10" step="0.01" class="form-control" name="vccMultiplier" value="${config.vccMultiplier}" />
</div>
</div>
<div class="col-sm-4 col-7">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Boot limit</span>
</div>
<input type="number" min="2.5" max="3.5" step="0.1" class="form-control" name="vccBootLimit" value="${config.vccBootLimit}" />
</div>
</div>
</div>
<div class="col-md-1 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div>
</div>
</form>
<form method="post" enctype="multipart/form-data">
<div class="my-3 p-3 bg-white rounded shadow">
<div class="alert alert-warning">!!WARNING!!<br/>Do not use this unless you know what you are doing. Uploading the wrong image could cause your device to stop working. Use with extreme caution!</div>
<div class="row">
<div class="col-md-4">
<div class="row form-group">
<label class="col-4">Firmware</label>
<div class="col-8">
<input type="file" name="file"/>
</div>
</div>
<div class="my-3 p-3 bg-white rounded shadow">
<h6>Debugger</h6>
<div class="row">
<div class="col-xl-2 col-md-3">
<label><input type="checkbox" name="debugTelnet" value="true" ${config.debugTelnet}/> Telnet debugger</label>
</div>
<div class="col-xl-2 col-md-3">
<label><input type="checkbox" name="debugSerial" value="true" ${config.debugSerial}/> Serial debugger</label>
</div>
<div class="col-xl-3 col-md-4">
<div class="row form-group">
<label class="col-6">Debug level</label>
<div class="col-6">
<select class="form-control form-control-sm" name="debugLevel">
<option value="2" ${config.debugLevel2}>Debug</option>
<option value="3" ${config.debugLevel3}>Info</option>
<option value="4" ${config.debugLevel4}>Warning</option>
<option value="5" ${config.debugLevel5}>Error</option>
</select>
</div>
</div>
<div class="col-md-8 text-right">
<button class="btn btn-primary">Upload</button>
</div>
</div>
</div>
<hr/>
<div class="row form-group">
</div>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-6">
<a href="/" class="btn btn-outline-secondary">Back</a>
<a href="/firmware" class="btn btn-sm btn-outline-secondary">Upload firmware</a>
</div>
<div class="col-6 text-right">
<a href="/reset" class="btn btn-sm btn-danger">Factory reset</a>
</div>
</div>
</form>
</main>
</body>
</html>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="/" class="btn btn-outline-secondary">Back</a>
</div>
<div class="col-6 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div>
</form>

View File

@@ -1,97 +1,44 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - Meter configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body class="bg-light">
<main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-md-row bg-purple rounded mt-2 mb-4" style="background-color: var(--purple);">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a>
<div class="navbar-nav-scroll">
<ul class="navbar-nav bd-navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link" href="/config-meter">Meter</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-wifi">WiFi</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-mqtt">MQTT</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/config-web">Web</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-system">System</a>
</li>
</ul>
</div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
<svg class="d-inline-block align-text-top" style="width: 2rem; height: 2rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 499.36" focusable="false"><title>GitHub</title><path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="currentColor" fill-rule="evenodd"></path></svg>
</a>
</li>
</ul>
</header>
<form method="post" action="/save">
<input type="hidden" name="authConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-md-4">
<div class="row form-group">
<label class="col-4">Security</label>
<div class="col-8">
<select id="authSecurity" class="form-control" name="authSecurity">
<option value="0" ${config.authSecurity0}>None</option>
<option value="1" ${config.authSecurity1}>Only configuration</option>
<option value="2" ${config.authSecurity2}>Everything</option>
</select>
</div>
<form method="post" action="/save">
<input type="hidden" name="authConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-xl-3 col-lg-4 col-md-7">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Security</span>
</div>
<select id="authSecurity" class="form-control" name="authSecurity">
<option value="0" ${config.authSecurity0}>None</option>
<option value="1" ${config.authSecurity1}>Only configuration</option>
<option value="2" ${config.authSecurity2}>Everything</option>
</select>
</div>
<div class="col-md-4">
<div class="row form-group">
<label class="col-4">Username</label>
<div class="col-8">
<input type="text" class="form-control auth-config" name="authUser" value="${config.authUser}"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Username</span>
</div>
<input type="text" class="form-control auth-config" name="authUser" value="${config.authUser}" maxlength="64"/>
</div>
<div class="col-md-4">
<div class="row form-group">
<label class="col-4">Password</label>
<div class="col-8">
<input type="password" class="form-control auth-config" name="authPassword" value="${config.authPassword}"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Password</span>
</div>
<input type="password" class="form-control auth-config" name="authPassword" value="${config.authPassword}" maxlength="64"/>
</div>
</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="/" class="btn btn-outline-secondary">Back</a>
</div>
<div class="col-6 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="/" class="btn btn-outline-secondary">Back</a>
</div>
</form>
</main>
<script>
$('#authSecurity').on('change', function() {
var inputs = $('.auth-config');
inputs.prop('disabled', $(this).val() == 0);
});
$(function() {
$('#authSecurity').trigger('change');
});
</script>
</body>
</html>
<div class="col-6 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div>
</form>

View File

@@ -1,132 +1,85 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - WiFi configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body class="bg-light">
<main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-md-row bg-purple rounded mt-2 mb-4" style="background-color: var(--purple);">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a>
<div class="navbar-nav-scroll">
<ul class="navbar-nav bd-navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link" href="/config-meter">Meter</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/config-wifi">WiFi</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-mqtt">MQTT</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-web">Web</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-system">System</a>
</li>
</ul>
<form method="post" action="/save">
<input type="hidden" name="wifiConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-xl-3 col-md-6 form-group">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">SSID</span>
</div>
<input type="text" name="wifiSsid" class="form-control" maxlength="32" placeholder="Name of your WiFi" value="${config.wifiSsid}" required/>
</div>
</div>
<div class="col-xl-3 col-md-6 form-group">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">PSK</span>
</div>
<input type="password" name="wifiPassword" class="form-control" maxlength="63" placeholder="Password for WiFi" value="${config.wifiPassword}" required/>
</div>
</div>
<div class="col-xl-4 col-md-6 form-group">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Hostname</span>
</div>
<input type="text" name="wifiHostname" class="form-control" maxlength="32" pattern="[a-z0-9_-]+" placeholder="Optional, ex.: ams-reader" value="${config.wifiHostname}"/>
</div>
</div>
<div class="col-xl-2 col-md-6 form-group">
<label><input id="wifiIpType" type="checkbox" name="wifiIpType" value="1" ${config.wifiStaticIp}/> Static IP</label>
</div>
</div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
<svg class="d-inline-block align-text-top" style="width: 2rem; height: 2rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 499.36" focusable="false"><title>GitHub</title><path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="currentColor" fill-rule="evenodd"></path></svg>
</a>
</li>
</ul>
</header>
<form method="post" action="/save">
<input type="hidden" name="wifiConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-md-4">
<div class="row form-group">
<label class="col-3">SSID</label>
<div class="col-9">
<input type="text" class="form-control" name="wifiSsid" value="${config.wifiSsid}"/>
</div>
</div>
<div class="row form-group">
<label class="col-3">Password</label>
<div class="col-9">
<input type="password" class="form-control" name="wifiPassword" value="${config.wifiPassword}"/>
</div>
</div>
<div class="row form-group">
<label class="col-3">Hostname</label>
<div class="col-9">
<input type="text" class="form-control" name="wifiHostname" value="${config.wifiHostname}"/>
</div>
<div class="row" id="staticIp">
<div class="col-xl-3 col-lg-4 form-group">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">IP</span>
</div>
<input type="text" name="wifiIp" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex: 192.168.1.200" value="${config.wifiIp}"/>
</div>
<div class="col-md-4">
<div class="row form-group">
<label class="col-6">IP configuration</label>
<div class="col-6">
<select id="wifiIpType" class="form-control" name="wifiIpType">
<option value="0" ${config.wifiIpType0}>DHCP</option>
<option value="1" ${config.wifiIpType1}>Static</option>
</select>
</div>
</div>
<div class="row form-group">
<label class="col-6">IP</label>
<div class="col-6">
<input type="text" class="form-control wifiip-config" name="wifiIp" value="${config.wifiIp}"/>
</div>
</div>
<div class="row form-group">
<label class="col-6">Subnet</label>
<div class="col-6">
<input type="text" class="form-control wifiip-config" name="wifiSubnet" value="${config.wifiSubnet}"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 form-group">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Subnet</span>
</div>
<input type="text" name="wifiSubnet" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex.: 255.255.255.0" value="${config.wifiSubnet}"/>
</div>
<div class="col-md-4">
<div class="row form-group">
<label class="col-6">Gateway</label>
<div class="col-6">
<input type="text" class="form-control wifiip-config" name="wifiGw" value="${config.wifiGw}"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 form-group">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Gateway</span>
</div>
<div class="row form-group">
<label class="col-6">Primary DNS</label>
<div class="col-6">
<input type="text" class="form-control wifiip-config" name="wifiDns1" value="${config.wifiDns1}"/>
</div>
<input type="text" name="wifiGw" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex.: 192.168.1.1" value="${config.wifiGw}"/>
</div>
</div>
<div class="col-xl-4 col-lg-5 form-group">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Primary DNS</span>
</div>
<div class="row form-group">
<label class="col-6">Secondary DNS</label>
<div class="col-6">
<input type="text" class="form-control wifiip-config" name="wifiDns2" value="${config.wifiDns2}"/>
</div>
<input type="text" name="wifiDns1" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex.: 192.168.1.1" value="${config.wifiDns1}"/>
</div>
</div>
<div class="col-xl-4 col-lg-5 form-group">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Secondary DNS</span>
</div>
<input type="text" name="wifiDns2" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex.: 8.8.8.8" value="${config.wifiDns2}"/>
</div>
</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="/" class="btn btn-outline-secondary">Back</a>
</div>
<div class="col-6 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="/" class="btn btn-outline-secondary">Back</a>
</div>
</form>
</main>
<script>
$('#wifiIpType').on('change', function() {
var inputs = $('.wifiip-config');
inputs.prop('disabled', $(this).val() != 1);
});
$(function() {
$('#wifiIpType').trigger('change');
});
</script>
</body>
</html>
<div class="col-6 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div>
</form>

14
web/delete.html Normal file
View File

@@ -0,0 +1,14 @@
<form method="post">
<div class="my-3 p-3 bg-white rounded shadow">
<div class="alert alert-warning">Are you sure you want to delete this file?</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="javascript:history.back();" class="btn btn-outline-secondary">Back</a>
</div>
<div class="col-6 text-right">
<button class="btn btn-danger">Delete</button>
</div>
</div>
</form>

13
web/foot.html Normal file
View File

@@ -0,0 +1,13 @@
<div id="newVersion" class="alert alert-info d-none">New version <span id="newVersionTag"></span>! <a id="newVersionUrl" href="#" target="_blank">view</a></div>
</main>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<script src="gaugemeter.js"></script>
<script src="application.js"></script>
</body>
</html>

View File

@@ -58,7 +58,7 @@
"White" === option.theme && (t = "#fff"),
"Black" === option.theme && (t = "#000"),
t;
}
};
/* The label below gauge. */
function createLabel(t, a) {
if(t.children("b").length === 0){
@@ -67,7 +67,7 @@
color: option.label_color
});
}
}
};
/* Prepend and append text, the gauge text or percentage value. */
function createSpanTag(t) {
var fgcolor = "";
@@ -90,7 +90,7 @@
"font-size": option.text_size * option.size + "px",
color: fgcolor
});
}
};
/* Get data attributes as options from div tag. Fall back to defaults when not exists. */
function getDataAttr(t) {
$.each(dataAttr, function (index, element) {
@@ -116,10 +116,10 @@
option[element] = parseFloat(option[element]);
}
});
}
};
/* Draws the gauge. */
function drawGauge(a) {
if(M < 0) M = 0;
if(M < 0) M = 0;
if(M > 100) M = 100;
var lw = option.width < 1 || isNaN(option.width) ? option.size / 20 : option.width;
g.clearRect(0, 0, b.width, b.height);
@@ -141,7 +141,7 @@
c > M && (M += z, requestAnimationFrame(function(){
drawGauge(Math.min(M, c) / 100);
}, p));
}
};
$(this).attr("data-id", $(this).attr("id"));
var r,

6
web/github.svg Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 499.36" focusable="false">
<title>GitHub</title>
<path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="#f8f9fa" fill-rule="evenodd"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

86
web/head.html Normal file
View File

@@ -0,0 +1,86 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<style>
.GaugeMeter {
position: Relative;
text-align: Center;
overflow: Hidden;
cursor: Default;
display: inline-block;
}
.GaugeMeter SPAN, .GaugeMeter B {
width: 54%;
position: Absolute;
text-align: Center;
display: Inline-Block;
color: RGBa(0,0,0,.8);
font-weight: 100;
font-family: "Open Sans", Arial;
overflow: Hidden;
white-space: NoWrap;
text-overflow: Ellipsis;
margin: 0 23%;
}
.GaugeMeter[data-style="Semi"] B {
width: 80%;
margin: 0 10%;
}
.GaugeMeter S, .GaugeMeter U {
text-decoration: None;
font-size: .60em;
font-weight: 200;
opacity: .6;
}
.GaugeMeter B {
color: #000;
font-weight: 200;
opacity: .8;
}
</style>
</head>
<body class="bg-light">
<main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-lg-row rounded mt-2 mb-4" style="background-color: var(--purple);">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small id="swVersion" data-url="https://api.github.com/repos/gskjold/AmsToMqttBridge/releases">${version}</small></h6></a>
<div class="navbar-nav-scroll">
<ul class="navbar-nav bd-navbar-nav flex-row">
<li class="nav-item">
<a id="config-meter-link" class="nav-link" href="/config-meter">Meter</a>
</li>
<li class="nav-item">
<a id="config-wifi-link" class="nav-link" href="/config-wifi">WiFi</a>
</li>
<li class="nav-item">
<a id="config-mqtt-link" class="nav-link" href="/config-mqtt">MQTT</a>
</li>
<li class="nav-item">
<a id="config-web-link" class="nav-link" href="/config-web">Web</a>
</li>
<li class="nav-item">
<a id="config-system-link" class="nav-link" href="/config-system">System</a>
</li>
</ul>
</div>
<div class="flex-row">
<div id="esp" class="d-none m-2">ESP</div>
<div id="han" class="d-none m-2">HAN</div>
<div id="wifi" class="d-none m-2">WiFi</div>
<div id="mqtt" class="d-none m-2">MQTT</div>
</div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-lg-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
<img class="d-inline-block align-text-top" style="width: 2rem; height: 2rem;" src="github.svg"/>
</a>
</li>
</ul>
</header>

View File

@@ -1,358 +1,107 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="gaugemeter.js"></script>
<style>
.GaugeMeter {
position: Relative;
text-align: Center;
overflow: Hidden;
cursor: Default;
display: inline-block;
}
.GaugeMeter SPAN, .GaugeMeter B {
width: 54%;
position: Absolute;
text-align: Center;
display: Inline-Block;
color: RGBa(0,0,0,.8);
font-weight: 100;
font-family: "Open Sans", Arial;
overflow: Hidden;
white-space: NoWrap;
text-overflow: Ellipsis;
margin: 0 23%;
}
.GaugeMeter[data-style="Semi"] B {
width: 80%;
margin: 0 10%;
}
.GaugeMeter S, .GaugeMeter U {
text-decoration: None;
font-size: .60em;
font-weight: 200;
opacity: .6;
}
.GaugeMeter B {
color: #000;
font-weight: 200;
opacity: .8;
}
</style>
</head>
<body class="bg-light">
<main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-md-row rounded mt-2 mb-4" style="background-color: var(--purple);">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a>
<div class="navbar-nav-scroll">
<ul class="navbar-nav bd-navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link" href="/config-meter">Meter</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-wifi">WiFi</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-mqtt">MQTT</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-web">Web</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-system">System</a>
</li>
</ul>
</div>
<div class="flex-row ml-md-auto d-md-flex">
<div id="esp" class="d-none m-2">ESP</div>
<div id="han" class="d-none m-2">HAN</div>
<div id="wifi" class="d-none m-2">WiFi</div>
<div id="mqtt" class="d-none m-2">MQTT</div>
</div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
<svg class="d-inline-block align-text-top" style="width: 2rem; height: 2rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 499.36" focusable="false"><title>GitHub</title><path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="currentColor" fill-rule="evenodd"></path></svg>
</a>
</li>
</ul>
</header>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-md-3">
<div class="text-center">
<div id="P" class="SimpleMeter" style="display: inline;">
${data.P} W
</div>
<div id="importMeter" class="GaugeMeter rounded"
style="display: none;"
data-size="200px"
data-text_size="0.11"
data-width="25"
data-style="Arch"
data-theme="Green-Gold-Red"
data-animationstep="0"
data-label="${text.import}"
></div>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-sm-6 col-lg-3">
<div class="text-center">
<div id="P" class="SimpleMeter" style="display: inline;">
${data.P} W
</div>
<div id="importMeter" class="GaugeMeter rounded"
style="display: none;"
data-size="200px"
data-text_size="0.11"
data-width="25"
data-style="Arch"
data-theme="Green-Gold-Red"
data-animationstep="0"
data-label="${text.import}"
></div>
</div>
<div class="col-md-3">
<div id="U1-row" class="row" style="display: ${display.P1};">
<div class="col-2">P1</div>
<div class="col-5 text-right"><span id="U1">${data.U1}</span> V</div>
<div class="col-5 text-right"><span id="I1">${data.I1}</span> A</div>
</div>
<div id="U2-row" class="row" style="display: ${display.P2};">
<div class="col-2">P2</div>
<div class="col-5 text-right"><span id="U2">${data.U2}</span> V</div>
<div class="col-5 text-right"><span id="I2">${data.I2}</span> A</div>
</div>
<div id="U3-row" class="row" style="display: ${display.P3};">
<div class="col-2">P3</div>
<div class="col-5 text-right"><span id="U3">${data.U3}</span> V</div>
<div class="col-5 text-right"><span id="I3">${data.I3}</span> A</div>
</div>
<hr/>
<div id="tPI-row" class="row" style="display: ${display.accumulative};">
<div class="col-6">Active in</div>
<div class="col-6 text-right"><span id="tPI">${data.tPI}</span> kWh</div>
</div>
<div id="tPO-row" class="row" style="display: ${display.accumulative};">
<div class="col-6">Active out</div>
<div class="col-6 text-right"><span id="tPO">${data.tPO}</span> kWh</div>
</div>
<div id="tQI-row" class="row" style="display: ${display.accumulative};">
<div class="col-6">Reactive in</div>
<div class="col-6 text-right"><span id="tQI">${data.tQI}</span> kvarh</div>
</div>
<div id="tQO-row" class="row" style="display: ${display.accumulative};">
<div class="col-6">Reactive out</div>
<div class="col-6 text-right"><span id="tQO">${data.tQO}</span> kvarh</div>
</div>
</div>
<div class="col-md-3">
<div class="text-center" style="display: ${display.export};">
<div id="P" class="SimpleMeter" style="display: inline;">
${data.PO} W
</div>
<div id="exportMeter" class="GaugeMeter rounded"
style="display: none;"
data-size="200px"
data-text_size="0.11"
data-width="25"
data-style="Arch"
data-theme="DarkGreen-LightGreen"
data-animationstep="0"
data-label="Export"
></div>
</div>
</div>
<div class="col-md-3">
<hr class="d-md-inline"/>
<div class="row">
<div class="col-6">Vcc</div>
<div class="col-6 text-right"><span id="vcc">${vcc}</span> V</div>
</div>
<div class="row" style="display: ${display.temp};">
<div class="col-6">Temperature</div>
<div class="col-6 text-right"><span id="temp">${temp}</span> °C</div>
</div>
<div class="row" style="display: none;">
<div class="col-6">Uptime</div>
<div class="col-6 text-right"><span id="currentMillis">${currentMillis}</span></div>
</div>
<hr/>
<div class="row">
<div class="col-6">SSID</div>
<div class="col-6 text-right"><span id="ssid">${wifi.ssid}</span></div>
</div>
<div class="row">
<div class="col-6">Channel</div>
<div class="col-6 text-right"><span id="channel">${wifi.channel}</span></div>
</div>
<div class="row">
<div class="col-6">RSSI</div>
<div class="col-6 text-right"><span id="rssi">${wifi.rssi}</span> dBm</div>
</div>
<hr class="d-none mqtt-error mqtt-error-1 mqtt-error-2 mqtt-error-3 mqtt-error-4 mqtt-error-5 mqtt-error-6 mqtt-error-7 mqtt-error-8 mqtt-error-9 mqtt-error-10 mqtt-error-11 mqtt-error-12 mqtt-error-13"/>
<div class="d-none badge badge-danger mqtt-error mqtt-error-1 mqtt-error-2 mqtt-error-5 mqtt-error-6 mqtt-error-7 mqtt-error-8 mqtt-error-9 mqtt-error-12">MQTT communication error (<span id="mqtt-lastError">-</span>)</div>
<div class="d-none badge badge-danger mqtt-error mqtt-error-3">MQTT failed to connect</div>
<div class="d-none badge badge-danger mqtt-error mqtt-error-4">MQTT network timeout</div>
<div class="d-none badge badge-danger mqtt-error mqtt-error-10">MQTT connection denied</div>
<div class="d-none badge badge-danger mqtt-error mqtt-error-11">MQTT failed to subscribe</div>
<div class="d-none badge badge-danger mqtt-error mqtt-error-13">MQTT lost connection</div>
<div class="col-sm-6 col-lg-3">
<div id="U1-row" class="row" style="display: ${display.P1};">
<div class="col-2">L1</div>
<div class="col-5 text-right"><span id="U1">${data.U1}</span> V</div>
<div class="col-5 text-right"><span id="I1">${data.I1}</span> A</div>
</div>
<div id="U2-row" class="row" style="display: ${display.P2};">
<div class="col-2">L2</div>
<div class="col-5 text-right"><span id="U2">${data.U2}</span> V</div>
<div class="col-5 text-right"><span id="I2">${data.I2}</span> A</div>
</div>
<div id="U3-row" class="row" style="display: ${display.P3};">
<div class="col-2">L3</div>
<div class="col-5 text-right"><span id="U3">${data.U3}</span> V</div>
<div class="col-5 text-right"><span id="I3">${data.I3}</span> A</div>
</div>
<hr/>
<div id="tPI-row" class="row" style="display: ${display.accumulative};">
<div class="col-6">Active in</div>
<div class="col-6 text-right"><span id="tPI">${data.tPI}</span> kWh</div>
</div>
<div id="tPO-row" class="row" style="display: ${display.accumulative};">
<div class="col-6">Active out</div>
<div class="col-6 text-right"><span id="tPO">${data.tPO}</span> kWh</div>
</div>
<div id="tQI-row" class="row" style="display: ${display.accumulative};">
<div class="col-6">Reactive in</div>
<div class="col-6 text-right"><span id="tQI">${data.tQI}</span> kvarh</div>
</div>
<div id="tQO-row" class="row" style="display: ${display.accumulative};">
<div class="col-6">Reactive out</div>
<div class="col-6 text-right"><span id="tQO">${data.tQO}</span> kvarh</div>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="text-center" style="display: ${display.export};">
<div id="P" class="SimpleMeter" style="display: inline;">
${data.PO} W
</div>
<div id="exportMeter" class="GaugeMeter rounded"
style="display: none;"
data-size="200px"
data-text_size="0.11"
data-width="25"
data-style="Arch"
data-theme="DarkGreen-LightGreen"
data-animationstep="0"
data-label="Export"
></div>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<hr class="d-md-inline"/>
<div class="row">
<div class="col-6">Vcc</div>
<div class="col-6 text-right"><span id="vcc">${vcc}</span> V</div>
</div>
<div class="row" style="display: ${display.temp};">
<div class="col-6">Temperature</div>
<div class="col-6 text-right"><span id="temp">${temp}</span> °C</div>
</div>
<div class="row" style="display: none;">
<div class="col-6">Uptime</div>
<div class="col-6 text-right"><span id="currentMillis">${currentMillis}</span></div>
</div>
<hr/>
<div class="row">
<div class="col-6">SSID</div>
<div class="col-6 text-right"><span id="ssid">${wifi.ssid}</span></div>
</div>
<div class="row">
<div class="col-6">Channel</div>
<div class="col-6 text-right"><span id="channel">${wifi.channel}</span></div>
</div>
<div class="row">
<div class="col-6">RSSI</div>
<div class="col-6 text-right"><span id="rssi">${wifi.rssi}</span> dBm</div>
</div>
<hr class="d-none mqtt-error mqtt-error-1 mqtt-error-2 mqtt-error-3 mqtt-error-4 mqtt-error-5 mqtt-error-6 mqtt-error-7 mqtt-error-8 mqtt-error-9 mqtt-error-10 mqtt-error-11 mqtt-error-12 mqtt-error-13"/>
<div class="d-none badge badge-danger mqtt-error mqtt-error-1 mqtt-error-2 mqtt-error-5 mqtt-error-6 mqtt-error-7 mqtt-error-8 mqtt-error-9 mqtt-error-12">MQTT communication error (<span id="mqtt-lastError">-</span>)</div>
<div class="d-none badge badge-danger mqtt-error mqtt-error-3">MQTT failed to connect</div>
<div class="d-none badge badge-danger mqtt-error mqtt-error-4">MQTT network timeout</div>
<div class="d-none badge badge-danger mqtt-error mqtt-error-10">MQTT connection denied</div>
<div class="d-none badge badge-danger mqtt-error mqtt-error-11">MQTT failed to subscribe</div>
<div class="d-none badge badge-danger mqtt-error mqtt-error-13">MQTT lost connection</div>
</div>
</div>
</main>
<script>
var im = $("#importMeter")
im.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
var em = $("#exportMeter")
em.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
var setStatus = function(id, status) {
var item = $('#'+id);
item.removeClass('d-none');
item.removeClass (function (index, className) {
return (className.match (/(^|\s)badge-\S+/g) || []).join(' ');
});
item.addClass('badge badge-' + status);
};
var interval = 5000;
var fetch = function() {
$.ajax({
url: '/data.json',
timeout: 10000,
dataType: 'json',
}).done(function(json) {
$(".SimpleMeter").hide();
im.show();
em.show();
for(var id in json) {
var str = json[id];
if(typeof str === "object")
continue;
if(isNaN(str)) {
$('#'+id).html(str);
} else {
var num = parseFloat(str);
$('#'+id).html(num.toFixed(num < 0 ? 0 : num < 10 ? 2 : 1));
}
}
if(window.moment) {
$('#currentMillis').html(moment.duration(parseInt(json.uptime_seconds), 'seconds').humanize());
$('#currentMillis').closest('.row').show();
}
if(json.status) {
for(var id in json.status) {
setStatus(id, json.status[id]);
}
}
if(json.mqtt) {
$('.mqtt-error').addClass('d-none');
$('.mqtt-error'+json.mqtt.lastError).removeClass('d-none');
$('#mqtt-lastError').html(json.mqtt.lastError);
}
if(json.wifi) {
for(var id in json.wifi) {
var str = json.wifi[id];
dst = $('#'+id);
if(isNaN(str)) {
dst.html(str);
} else {
var num = parseFloat(str);
dst.html(num.toFixed(0));
$('#'+id+'-row').show();
}
}
}
if(json.data) {
var p = 0;
var p_pct = parseInt(json.p_pct);
var p_append = "W";
if(json.data.P) {
p = parseFloat(json.data.P);
if(p > 1000) {
p = (p/1000).toFixed(1);
p_append = "kW";
}
}
im.gaugeMeter({
percent: p_pct,
text: p,
append: p_append
});
var po = 0;
var po_pct = parseInt(json.po_pct);
var po_append = "W";
if(json.data.PO) {
po = parseFloat(json.data.PO);
if(po > 1000) {
po = (po/1000).toFixed(1);
po_append = "kW";
}
}
em.gaugeMeter({
percent: po_pct,
text: po,
append: po_append
});
for(var id in json.data) {
var str = json.data[id];
if(isNaN(str)) {
$('#'+id).html(str);
} else {
var num = parseFloat(str);
$('#'+id).html(num.toFixed(1));
$('#'+id+'-row').show();
}
}
} else {
im.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
em.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
}
setTimeout(fetch, interval);
}).fail(function() {
setTimeout(fetch, interval*4);
im.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
em.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
setStatus("mqtt", "secondary");
setStatus("wifi", "secondary");
setStatus("han", "secondary");
setStatus("esp", "danger");
});
}
fetch();
</script>
</body>
</html>
</div>

14
web/reset.html Normal file
View File

@@ -0,0 +1,14 @@
<form method="post">
<div class="my-3 p-3 bg-white rounded shadow">
<div class="alert alert-danger">Are you sure you want reset this device to factory settings? ALL configuration will be erased!</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="javascript:history.back();" class="btn btn-outline-secondary">Back</a>
</div>
<div class="col-6 text-right">
<button class="btn btn-danger" name="perform" value="true">Perform factory reset</button>
</div>
</div>
</form>

View File

@@ -5,35 +5,15 @@
<title>AMS reader - Restarting, please wait</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body class="bg-light">
<main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-md-row bg-purple rounded mt-2 mb-4" style="background-color: var(--purple);">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a>
<div class="navbar-nav-scroll">
<ul class="navbar-nav bd-navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link" href="/config-meter">Meter</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-wifi">WiFi</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-mqtt">MQTT</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-web">Web</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-system">System</a>
</li>
</ul>
</div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
<svg class="d-inline-block align-text-top" style="width: 2rem; height: 2rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 499.36" focusable="false"><title>GitHub</title><path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="currentColor" fill-rule="evenodd"></path></svg>
<img class="d-inline-block align-text-top" style="width: 2rem; height: 2rem;" src="github.svg"/>
</a>
</li>
</ul>
@@ -44,7 +24,6 @@
</main>
<script>
var tries = 0;
var ip = "${ip}";
var hostname = "${hostname}";
var url;
@@ -60,15 +39,22 @@
} else {
url = "";
}
if(console) console.log("Trying url " + url)
$.ajax({
url: url + '/is-alive',
timeout: 5000
}).done(function(json) {
window.location.href = url ? url : "/";
}).fail(function() {
if(console) console.log("Trying url " + url);
var retry = function() {
setTimeout(fetch, 1000);
};
var xhr = new XMLHttpRequest();
xhr.timeout = 5000;
xhr.addEventListener('abort', retry);
xhr.addEventListener('error', retry);
xhr.addEventListener('timeout', retry);
xhr.addEventListener('load', function(e) {
window.location.href = url ? url : "/";
});
xhr.open("GET", url + "/is-alive", true);
xhr.send();
};
setTimeout(fetch, 10000);
</script>

137
web/setup.html Normal file
View File

@@ -0,0 +1,137 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - Setup</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
</head>
<body class="bg-light">
<main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-md-row bg-purple rounded mt-2 mb-4">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
<img style="width: 2rem; height: 2rem;" src="github.svg"/>
</a>
</li>
</ul>
</header>
<form method="post">
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-xl-4 col-md-6">
<h5>Hardware</h5>
<select name="board" class="form-control">
<optgroup label="Custom hardware">
<option value="0" ${config.boardType0}>Custom hardware by Roar Fredriksen</option>
<option value="1" ${config.boardType1}>Kamstrup module by Egil Opsahl</option>
<option value="2" ${config.boardType2}>HAN Reader 2.0 by Max Spencer</option>
</optgroup>
<optgroup label="ESP8266">
<option value="101" ${config.boardType101}>Wemos D1</option>
<option value="100" ${config.boardType100}>Generic ESP8266</option>
</optgroup>
<optgroup label="ESP32">
<option value="201" ${config.boardType201}>Wemos LOLIN D32</option>
<option value="202" ${config.boardType202}>Adafruit HUZZAH32</option>
<option value="203" ${config.boardType203}>DevKitC</option>
<option value="200" ${config.boardType200}>Generic ESP32</option>
</optgroup>
</select>
</div>
<div class="col-xl-2 col-md-3">
<h5>Meter</h5>
<select name="meterType" class="form-control">
<option value="0" ${config.meterType0}>Autodetect</option>
<option value="1" ${config.meterType1}>Kaifa</option>
<option value="2" ${config.meterType2}>Aidon</option>
<option value="3" ${config.meterType3}>Kamstrup</option>
</select>
</div>
</div>
</div>
<div class="my-3 p-3 bg-white rounded shadow">
<h5>WiFi</h5>
<div class="row">
<div class="col-xl-3 col-md-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">SSID</span>
</div>
<input type="text" name="wifiSsid" class="form-control" maxlength="32" placeholder="Name of your WiFi" required value="${config.wifiSsid}"/>
</div>
</div>
<div class="col-xl-3 col-md-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">PSK</span>
</div>
<input type="password" name="wifiPassword" class="form-control" maxlength="63" placeholder="Password for WiFi" required value="${config.wifiPassword}"/>
</div>
</div>
<div class="col-xl-4 col-md-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Hostname</span>
</div>
<input type="text" name="wifiHostname" class="form-control" maxlength="32" pattern="[a-z0-9_-]+" placeholder="Optional, ex.: ams-reader" value="${config.wifiHostname}"/>
</div>
</div>
<div class="col-xl-2 col-md-6 form-group">
<label><input type="checkbox" name="wifiIpType" value="1" onchange="staticChecked(this);" ${config.wifiStaticIp}/> Static IP</label>
</div>
</div>
<div class="row" id="staticIp">
<div class="col-xl-3 col-lg-4 col-sm-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">IP</span>
</div>
<input type="text" name="wifiIp" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex: 192.168.1.200" value="${config.wifiIp}"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-sm-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Subnet</span>
</div>
<input type="text" name="wifiSubnet" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex.: 255.255.255.0" value="${config.wifiSubnet}"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-sm-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Gateway</span>
</div>
<input type="text" name="wifiGw" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex.: 192.168.1.1" value="${config.wifiGw}"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-sm-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">DNS</span>
</div>
<input type="text" name="wifiDns1" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex.: 192.168.1.1" value="${config.wifiDns1}"/>
</div>
</div>
</div>
</div>
<hr/>
<div class="row">
<div class="col-6"></div>
<div class="col-6 text-right">
<button type="submit" class="btn btn-primary">Save & reboot</button>
</div>
</div>
</form>
</main>
<script>
document.getElementById('staticIp').style.display = "none";
var staticChecked = function(el) {
document.getElementById('staticIp').style.display = el.checked ? "" : "none";
}
</script>
</body>
</html>

26
web/upload.html Normal file
View File

@@ -0,0 +1,26 @@
<form method="post" enctype="multipart/form-data">
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-lg-6">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Upload</span>
</div>
<div class="custom-file">
<input name="file" type="file" class="custom-file-input" id="fileUploadField">
<label class="custom-file-label" for="fileUploadField">Choose file</label>
</div>
</div>
</div>
</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="javascript:history.back();" class="btn btn-outline-secondary">Back</a>
</div>
<div class="col-6 text-right">
<button class="btn btn-primary">Upload</button>
</div>
</div>
</form>