Merge branch 'master' into low_power

This commit is contained in:
Gunnar Skjold 2020-02-06 19:02:14 +01:00
commit 7e6ac860fd
20 changed files with 483 additions and 772 deletions

View File

@ -5,6 +5,8 @@ on:
paths:
- src/**
- lib/**
- scripts/**
- web/**
- platformio.ini
branches:
- master

2
.gitignore vendored
View File

@ -8,3 +8,5 @@
.pio
platformio-user.ini
/src/version.h
/src/web/root
/src/AmsToMqttBridge.ino.cpp

View File

@ -3,15 +3,16 @@
Stream* HanConfigAp::debugger;
bool HanConfigAp::hasConfig() {
return config.hasConfig();
return config->hasConfig();
}
void HanConfigAp::setup(int accessPointButtonPin, Stream* debugger)
void HanConfigAp::setup(int accessPointButtonPin, configuration* config, Stream* debugger)
{
this->debugger = debugger;
this->config = config;
// Test if we're missing configuration
if (!config.hasConfig())
if (!config->hasConfig())
{
print("No config. We're booting as AP. Look for SSID ");
println(this->AP_SSID);
@ -20,8 +21,7 @@ void HanConfigAp::setup(int accessPointButtonPin, Stream* debugger)
else
{
// Load the configuration
config.load();
if (this->debugger) config.print(this->debugger);
if (this->debugger) config->print(this->debugger);
if (accessPointButtonPin != INVALID_BUTTON_PIN)
{

View File

@ -24,15 +24,16 @@
class HanConfigAp {
public:
void setup(int accessPointButtonPin, Stream* debugger);
void setup(int accessPointButtonPin, configuration* config, Stream* debugger);
bool loop();
bool hasConfig();
configuration config;
bool isActivated = false;
private:
const char* AP_SSID = "AMS2MQTT";
configuration* config;
// DNS server
const byte DNS_PORT = 53;
DNSServer dnsServer;

View File

@ -24,11 +24,18 @@ bool configuration::save()
address += saveString(address, ssid);
address += saveString(address, ssidPassword);
address += saveByte(address, meterType);
address += saveString(address, mqtt);
address += saveInt(address, mqttPort);
address += saveString(address, mqttClientID);
address += saveString(address, mqttPublishTopic);
address += saveString(address, mqttSubscribeTopic);
if(mqttHost) {
address += saveBool(address, true);
address += saveString(address, mqttHost);
address += saveInt(address, mqttPort);
address += saveString(address, mqttClientID);
address += saveString(address, mqttPublishTopic);
address += saveString(address, mqttSubscribeTopic);
} else {
address += saveBool(address, false);
}
if (isSecure()) {
address += saveBool(address, true);
@ -46,6 +53,7 @@ bool configuration::save()
}
address += saveInt(address, fuseSize);
address += saveInt(address, distSys);
bool success = EEPROM.commit();
EEPROM.end();
@ -59,13 +67,13 @@ bool configuration::load()
int address = EEPROM_CONFIG_ADDRESS;
bool success = false;
ssid = (char*)String("").c_str();
ssidPassword = (char*)String("").c_str();
ssid = 0;
ssidPassword = 0;
meterType = (byte)0;
mqtt = (char*)String("").c_str();
mqttClientID = (char*)String("").c_str();
mqttPublishTopic = (char*)String("").c_str();
mqttSubscribeTopic = (char*)String("").c_str();
mqttHost = 0;
mqttClientID = 0;
mqttPublishTopic = 0;
mqttSubscribeTopic = 0;
mqttUser = 0;
mqttPass = 0;
mqttPort = 1883;
@ -73,25 +81,30 @@ bool configuration::load()
authUser = 0;
authPass = 0;
fuseSize = 0;
distSys = 0;
EEPROM.begin(EEPROM_SIZE);
int cs = EEPROM.read(address);
if (cs >= 71)
if (cs == EEPROM_CHECK_SUM)
{
address++;
address += readString(address, &ssid);
address += readString(address, &ssidPassword);
address += readByte(address, &meterType);
address += readString(address, &mqtt);
address += readInt(address, &mqttPort);
address += readString(address, &mqttClientID);
address += readString(address, &mqttPublishTopic);
address += readString(address, &mqttSubscribeTopic);
bool mqtt = false;
address += readBool(address, &mqtt);
if(mqtt) {
address += readString(address, &mqttHost);
address += readInt(address, &mqttPort);
address += readString(address, &mqttClientID);
address += readString(address, &mqttPublishTopic);
address += readString(address, &mqttSubscribeTopic);
}
bool secure = false;
address += readBool(address, &secure);
if (secure)
{
address += readString(address, &mqttUser);
@ -103,9 +116,6 @@ bool configuration::load()
mqttPass = 0;
}
success = true;
}
if(cs >= 72) {
address += readByte(address, &authSecurity);
if (authSecurity > 0) {
address += readString(address, &authUser);
@ -114,9 +124,11 @@ bool configuration::load()
authUser = 0;
authPass = 0;
}
}
if(cs >= 73) {
address += readInt(address, &fuseSize);
address += readByte(address, &distSys);
success = true;
}
EEPROM.end();
return success;
@ -177,11 +189,13 @@ void configuration::print(Stream* debugger)
debugger->printf("ssid: %s\r\n", this->ssid);
debugger->printf("ssidPassword: %s\r\n", this->ssidPassword);
debugger->printf("meterType: %i\r\n", this->meterType);
debugger->printf("mqtt: %s\r\n", this->mqtt);
debugger->printf("mqttPort: %i\r\n", this->mqttPort);
debugger->printf("mqttClientID: %s\r\n", this->mqttClientID);
debugger->printf("mqttPublishTopic: %s\r\n", this->mqttPublishTopic);
debugger->printf("mqttSubscribeTopic: %s\r\n", this->mqttSubscribeTopic);
if(this->mqttHost) {
debugger->printf("mqttHost: %s\r\n", this->mqttHost);
debugger->printf("mqttPort: %i\r\n", this->mqttPort);
debugger->printf("mqttClientID: %s\r\n", this->mqttClientID);
debugger->printf("mqttPublishTopic: %s\r\n", this->mqttPublishTopic);
debugger->printf("mqttSubscribeTopic: %s\r\n", this->mqttSubscribeTopic);
}
if (this->isSecure())
{
@ -197,6 +211,7 @@ void configuration::print(Stream* debugger)
debugger->printf("authPass: %s\r\n", this->authPass);
}
debugger->printf("fuseSize: %i\r\n", this->fuseSize);
debugger->printf("distSys: %i\r\n", this->distSys);
debugger->println("-----------------------------------------------");
}
@ -236,7 +251,7 @@ int configuration::readString(int pAddress, char* pString[])
int configuration::saveString(int pAddress, char* pString)
{
int address = 0;
int length = strlen(pString) + 1;
int length = pString ? strlen(pString) + 1 : 0;
EEPROM.put(pAddress + address, length);
address++;

View File

@ -16,7 +16,7 @@ class configuration {
public:
char* ssid;
char* ssidPassword;
char* mqtt;
char* mqttHost;
int mqttPort;
char* mqttClientID;
char* mqttPublishTopic;
@ -30,6 +30,7 @@ public:
char* authPass;
int fuseSize;
byte distSys;
bool hasConfig();
bool isSecure();
@ -41,7 +42,7 @@ protected:
private:
const int EEPROM_SIZE = 512;
const byte EEPROM_CHECK_SUM = 73; // Used to check if config is stored. Change if structure changes
const byte EEPROM_CHECK_SUM = 75; // Used to check if config is stored. Change if structure changes
const int EEPROM_CONFIG_ADDRESS = 0;
int saveString(int pAddress, char* pString);

View File

@ -15,7 +15,8 @@ build_flags =
-D HAS_DALLAS_TEMP_SENSOR=0
-D IS_CUSTOM_AMS_BOARD=0
extra_scripts =
pre:addversion.py
pre:scripts/addversion.py
scripts/makeweb.py
[env:hw1esp12e]
platform = espressif8266
@ -26,7 +27,8 @@ build_flags =
-D HAS_DALLAS_TEMP_SENSOR=1
-D IS_CUSTOM_AMS_BOARD=1
extra_scripts =
pre:addversion.py
pre:scripts/addversion.py
scripts/makeweb.py
[env:featheresp32]
platform = espressif32
@ -37,4 +39,5 @@ build_flags =
-D HAS_DALLAS_TEMP_SENSOR=0
-D IS_CUSTOM_AMS_BOARD=0
extra_scripts =
pre:addversion.py
pre:scripts/addversion.py
scripts/makeweb.py

30
scripts/makeweb.py Normal file
View File

@ -0,0 +1,30 @@
import os
import re
import shutil
webroot = "web"
srcroot = "src/web/root"
if os.path.exists(srcroot):
shutil.rmtree(srcroot)
os.mkdir(srcroot)
else:
os.mkdir(srcroot)
for filename in os.listdir(webroot):
basename = re.sub("[^0-9a-zA-Z]+", "_", filename)
srcfile = webroot + "/" + filename
dstfile = srcroot + "/" + basename + ".h"
varname = basename.upper()
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")

View File

@ -20,7 +20,7 @@ ADC_MODE(ADC_VCC);
#include <WiFi.h>
#endif
#include "AmsWebServer.h"
#include "web/AmsWebServer.h"
#include "HanConfigAp.h"
#include "HanReader.h"
#include "HanToJson.h"
@ -31,6 +31,10 @@ ADC_MODE(ADC_VCC);
#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
#elif defined(ARDUINO_LOLIN_D32)
#define LED_PIN 5
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN INVALID_BUTTON_PIN
#else
#define LED_PIN LED_BUILTIN
#define LED_ACTIVE_HIGH 1
@ -44,6 +48,8 @@ OneWire oneWire(TEMP_SENSOR_PIN);
DallasTemperature tempSensor(&oneWire);
#endif
configuration config;
// Object used to boot as Access Point
HanConfigAp ap;
@ -66,11 +72,18 @@ void setup() {
debugger = &Serial;
#endif
if(config.hasConfig()) {
config.load();
}
if(config.meterType == 3) {
Serial.begin(2400, SERIAL_8N1);
} else {
Serial.begin(2400, SERIAL_8E1);
}
while (!Serial);
if (debugger) {
// Setup serial port for debugging
debugger->begin(2400, SERIAL_8E1);
//debugger->begin(115200);
while (!debugger);
debugger->println("");
debugger->println("Started...");
debugger->print("Voltage: ");
@ -94,28 +107,29 @@ void setup() {
delay(1000);
// Initialize the AP
ap.setup(AP_BUTTON_PIN, debugger);
ap.setup(AP_BUTTON_PIN, &config, debugger);
led_off();
if (!ap.isActivated)
{
setupWiFi();
// Configure uart for AMS data
if(ap.config.meterType == 3) {
Serial.begin(2400, SERIAL_8N1);
} else {
Serial.begin(2400, SERIAL_8E1);
if(config.mqttHost) {
mqtt.begin(config.mqttHost, *client);
// Notify everyone we're here!
sendMqttData("Connected!");
}
while (!Serial);
// Configure uart for AMS data
hanReader.setup(&Serial, debugger);
// Compensate for the known Kaifa bug
hanReader.compensateFor09HeaderBug = (ap.config.meterType == 1);
hanReader.compensateFor09HeaderBug = (config.meterType == 1);
}
ws.setup(&ap.config, debugger);
ws.setup(&config, debugger);
}
// the loop function runs over and over again until power down or reset
@ -127,16 +141,19 @@ void loop()
// Turn off the LED
led_off();
// allow the MQTT client some resources
mqtt.loop();
delay(10); // <- fixes some issues with WiFi stability
// Reconnect to WiFi and MQTT as needed
if (!mqtt.connected()) {
MQTT_connect();
} else {
readHanPort();
if (WiFi.status() != WL_CONNECTED) {
WiFi_connect();
}
if (config.mqttHost) {
mqtt.loop();
delay(10); // <- fixes some issues with WiFi stability
if(!mqtt.connected()) {
MQTT_connect();
}
}
readHanPort();
}
else
{
@ -178,7 +195,7 @@ void setupWiFi()
// Connect to WiFi
WiFi.mode(WIFI_STA);
WiFi.begin(ap.config.ssid, ap.config.ssidPassword);
WiFi.begin(config.ssid, config.ssidPassword);
// Wait for WiFi connection
if (debugger) debugger->print("\nWaiting for WiFi to connect...");
@ -189,16 +206,6 @@ void setupWiFi()
if (debugger) debugger->println(" connected");
client = new WiFiClient();
mqtt.begin(ap.config.mqtt, *client);
// Direct incoming MQTT messages
if (ap.config.mqttSubscribeTopic != 0 && strlen(ap.config.mqttSubscribeTopic) > 0) {
mqtt.subscribe(ap.config.mqttSubscribeTopic);
mqtt.onMessage(mqttMessageReceived);
}
// Notify everyone we're here!
sendMqttData("Connected!");
}
void mqttMessageReceived(String &topic, String &payload)
@ -218,7 +225,7 @@ void mqttMessageReceived(String &topic, String &payload)
void readHanPort()
{
if (hanReader.read() && ap.config.hasConfig())
if (hanReader.read() && config.hasConfig())
{
// Flash LED on, this shows us that data is received
led_on();
@ -247,9 +254,9 @@ void readHanPort()
data["temp"] = tempSensor.getTempCByIndex(0);
#endif
hanToJson(data, ap.config.meterType, hanReader);
hanToJson(data, config.meterType, hanReader);
if(ap.config.mqtt != 0 && strlen(ap.config.mqtt) != 0 && ap.config.mqttPublishTopic != 0 && strlen(ap.config.mqttPublishTopic) != 0) {
if(config.mqttHost != 0 && strlen(config.mqttHost) != 0 && config.mqttPublishTopic != 0 && strlen(config.mqttPublishTopic) != 0) {
// Write the json to the debug port
if (debugger) {
debugger->print("Sending data to MQTT: ");
@ -261,7 +268,7 @@ void readHanPort()
String msg;
serializeJson(json, msg);
mqtt.publish(ap.config.mqttPublishTopic, msg.c_str());
mqtt.publish(config.mqttPublishTopic, msg.c_str());
mqtt.loop();
}
ws.setJson(json);
@ -271,25 +278,21 @@ void readHanPort()
}
}
// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect()
{
void WiFi_connect() {
// Connect to WiFi access point.
if (debugger)
{
debugger->println();
debugger->println();
debugger->print("Connecting to WiFi network ");
debugger->println(ap.config.ssid);
debugger->println(config.ssid);
}
if (WiFi.status() != WL_CONNECTED)
{
// Make one first attempt at connect, this seems to considerably speed up the first connection
WiFi.disconnect();
WiFi.begin(ap.config.ssid, ap.config.ssidPassword);
WiFi.begin(config.ssid, config.ssidPassword);
delay(1000);
}
@ -308,7 +311,7 @@ void MQTT_connect()
debugger->println(WiFi.status());
}
WiFi.disconnect();
WiFi.begin(ap.config.ssid, ap.config.ssidPassword);
WiFi.begin(config.ssid, config.ssidPassword);
vTimeout = millis() + WIFI_CONNECTION_TIMEOUT;
}
yield();
@ -319,26 +322,33 @@ void MQTT_connect()
debugger->println("WiFi connected");
debugger->println("IP address: ");
debugger->println(WiFi.localIP());
debugger->print("\nconnecting to MQTT: ");
debugger->print(ap.config.mqtt);
}
}
// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect()
{
if(debugger) {
debugger->print("Connecting to MQTT: ");
debugger->print(config.mqttHost);
debugger->print(", port: ");
debugger->print(ap.config.mqttPort);
debugger->print(config.mqttPort);
debugger->println();
}
// Wait for the MQTT connection to complete
while (!mqtt.connected()) {
// Connect to a unsecure or secure MQTT server
if ((ap.config.mqttUser == 0 && mqtt.connect(ap.config.mqttClientID)) ||
(ap.config.mqttUser != 0 && mqtt.connect(ap.config.mqttClientID, ap.config.mqttUser, ap.config.mqttPass)))
if ((config.mqttUser == 0 && mqtt.connect(config.mqttClientID)) ||
(config.mqttUser != 0 && mqtt.connect(config.mqttClientID, config.mqttUser, config.mqttPass)))
{
if (debugger) debugger->println("\nSuccessfully connected to MQTT!");
// Subscribe to the chosen MQTT topic, if set in configuration
if (ap.config.mqttSubscribeTopic != 0 && strlen(ap.config.mqttSubscribeTopic) > 0)
if (config.mqttSubscribeTopic != 0 && strlen(config.mqttSubscribeTopic) > 0)
{
mqtt.subscribe(ap.config.mqttSubscribeTopic);
if (debugger) debugger->printf(" Subscribing to [%s]\r\n", ap.config.mqttSubscribeTopic);
mqtt.subscribe(config.mqttSubscribeTopic);
if (debugger) debugger->printf(" Subscribing to [%s]\r\n", config.mqttSubscribeTopic);
}
}
else
@ -366,7 +376,7 @@ void MQTT_connect()
void sendMqttData(String data)
{
// Make sure we have configured a publish topic
if (ap.config.mqttPublishTopic == 0 || strlen(ap.config.mqttPublishTopic) == 0)
if (config.mqttPublishTopic == 0 || strlen(config.mqttPublishTopic) == 0)
return;
// Make sure we're connected
@ -386,7 +396,7 @@ void sendMqttData(String data)
serializeJson(json, msg);
// Send the json over MQTT
mqtt.publish(ap.config.mqttPublishTopic, msg.c_str());
mqtt.publish(config.mqttPublishTopic, msg.c_str());
if (debugger) debugger->print("sendMqttData: ");
if (debugger) debugger->println(data);

View File

@ -1,380 +0,0 @@
# 1 "/tmp/tmpfprbzre1"
#include <Arduino.h>
# 1 "/home/gunnar/src/AmsToMqttBridge/src/AmsToMqttBridge.ino"
# 11 "/home/gunnar/src/AmsToMqttBridge/src/AmsToMqttBridge.ino"
#include <ArduinoJson.h>
#include <MQTT.h>
#if HAS_DALLAS_TEMP_SENSOR
#include <DallasTemperature.h>
#include <OneWire.h>
#endif
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WiFi.h>
#endif
#include "AmsWebServer.h"
#include "HanConfigAp.h"
#include "HanReader.h"
#include "HanToJson.h"
#define WIFI_CONNECTION_TIMEOUT 30000;
#if IS_CUSTOM_AMS_BOARD
#define LED_PIN 2
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN 0
#else
#define LED_PIN LED_BUILTIN
#define LED_ACTIVE_HIGH 1
#define AP_BUTTON_PIN INVALID_BUTTON_PIN
#endif
#if HAS_DALLAS_TEMP_SENSOR
#define TEMP_SENSOR_PIN 5
OneWire oneWire(TEMP_SENSOR_PIN);
DallasTemperature tempSensor(&oneWire);
#endif
HanConfigAp ap;
AmsWebServer ws;
WiFiClient *client;
MQTTClient mqtt(384);
HardwareSerial* debugger = NULL;
HanReader hanReader;
void setup();
void loop();
void led_on();
void led_off();
void setupWiFi();
void mqttMessageReceived(String &topic, String &payload);
void readHanPort();
void MQTT_connect();
void sendMqttData(String data);
#line 65 "/home/gunnar/src/AmsToMqttBridge/src/AmsToMqttBridge.ino"
void setup() {
#if DEBUG_MODE
debugger = &Serial;
#endif
if (debugger) {
debugger->begin(2400, SERIAL_8E1);
while (!debugger);
debugger->println("");
debugger->println("Started...");
}
pinMode(LED_PIN, OUTPUT);
led_on();
delay(1000);
ap.setup(AP_BUTTON_PIN, debugger);
led_off();
if (!ap.isActivated)
{
setupWiFi();
if(ap.config.meterType == 3) {
Serial.begin(2400, SERIAL_8N1);
} else {
Serial.begin(2400, SERIAL_8E1);
}
while (!Serial);
hanReader.setup(&Serial, debugger);
hanReader.compensateFor09HeaderBug = (ap.config.meterType == 1);
}
ws.setup(&ap.config, debugger);
}
void loop()
{
if (!ap.loop())
{
led_off();
mqtt.loop();
delay(10);
if (!mqtt.connected()) {
MQTT_connect();
} else {
readHanPort();
}
}
else
{
if (millis() / 1000 % 2 == 0) led_on();
else led_off();
}
ws.loop();
}
void led_on()
{
#if LED_ACTIVE_HIGH
digitalWrite(LED_PIN, HIGH);
#else
digitalWrite(LED_PIN, LOW);
#endif
}
void led_off()
{
#if LED_ACTIVE_HIGH
digitalWrite(LED_PIN, LOW);
#else
digitalWrite(LED_PIN, HIGH);
#endif
}
void setupWiFi()
{
WiFi.enableAP(false);
WiFi.mode(WIFI_STA);
WiFi.begin(ap.config.ssid, ap.config.ssidPassword);
if (debugger) debugger->print("\nWaiting for WiFi to connect...");
while (WiFi.status() != WL_CONNECTED) {
if (debugger) debugger->print(".");
delay(500);
}
if (debugger) debugger->println(" connected");
client = new WiFiClient();
mqtt.begin(ap.config.mqtt, *client);
if (ap.config.mqttSubscribeTopic != 0 && strlen(ap.config.mqttSubscribeTopic) > 0) {
mqtt.subscribe(ap.config.mqttSubscribeTopic);
mqtt.onMessage(mqttMessageReceived);
}
sendMqttData("Connected!");
}
void mqttMessageReceived(String &topic, String &payload)
{
if (debugger) {
debugger->println("Incoming MQTT message:");
debugger->print("[");
debugger->print(topic);
debugger->print("] ");
debugger->println(payload);
}
}
void readHanPort()
{
if (hanReader.read() && ap.config.hasConfig())
{
led_on();
time_t time = hanReader.getPackageTime();
if (debugger) debugger->print("Time of the package is: ");
if (debugger) debugger->println(time);
StaticJsonDocument<500> json;
json["id"] = WiFi.macAddress();
json["up"] = millis();
json["t"] = time;
JsonObject data = json.createNestedObject("data");
#if HAS_DALLAS_TEMP_SENSOR
tempSensor.requestTemperatures();
data["temp"] = tempSensor.getTempCByIndex(0);
#endif
hanToJson(data, ap.config.meterType, hanReader);
if(ap.config.mqtt != 0 && strlen(ap.config.mqtt) != 0 && ap.config.mqttPublishTopic != 0 && strlen(ap.config.mqttPublishTopic) != 0) {
if (debugger) {
debugger->print("Sending data to MQTT: ");
serializeJsonPretty(json, *debugger);
debugger->println();
}
String msg;
serializeJson(json, msg);
mqtt.publish(ap.config.mqttPublishTopic, msg.c_str());
mqtt.loop();
}
ws.setJson(json);
led_off();
}
}
void MQTT_connect()
{
if (debugger)
{
debugger->println();
debugger->println();
debugger->print("Connecting to WiFi network ");
debugger->println(ap.config.ssid);
}
if (WiFi.status() != WL_CONNECTED)
{
WiFi.disconnect();
WiFi.begin(ap.config.ssid, ap.config.ssidPassword);
delay(1000);
}
long vTimeout = millis() + WIFI_CONNECTION_TIMEOUT;
while (WiFi.status() != WL_CONNECTED) {
delay(50);
if (debugger) debugger->print(".");
if (vTimeout < millis())
{
if (debugger)
{
debugger->print("Timout during connect. WiFi status is: ");
debugger->println(WiFi.status());
}
WiFi.disconnect();
WiFi.begin(ap.config.ssid, ap.config.ssidPassword);
vTimeout = millis() + WIFI_CONNECTION_TIMEOUT;
}
yield();
}
if (debugger) {
debugger->println();
debugger->println("WiFi connected");
debugger->println("IP address: ");
debugger->println(WiFi.localIP());
debugger->print("\nconnecting to MQTT: ");
debugger->print(ap.config.mqtt);
debugger->print(", port: ");
debugger->print(ap.config.mqttPort);
debugger->println();
}
while (!mqtt.connected()) {
if ((ap.config.mqttUser == 0 && mqtt.connect(ap.config.mqttClientID)) ||
(ap.config.mqttUser != 0 && mqtt.connect(ap.config.mqttClientID, ap.config.mqttUser, ap.config.mqttPass)))
{
if (debugger) debugger->println("\nSuccessfully connected to MQTT!");
if (ap.config.mqttSubscribeTopic != 0 && strlen(ap.config.mqttSubscribeTopic) > 0)
{
mqtt.subscribe(ap.config.mqttSubscribeTopic);
if (debugger) debugger->printf(" Subscribing to [%s]\r\n", ap.config.mqttSubscribeTopic);
}
}
else
{
if (debugger)
{
debugger->print(".");
debugger->print("failed, ");
debugger->println(" trying again in 5 seconds");
}
mqtt.disconnect();
delay(2000);
}
yield();
delay(2000);
}
}
void sendMqttData(String data)
{
if (ap.config.mqttPublishTopic == 0 || strlen(ap.config.mqttPublishTopic) == 0)
return;
if (!client->connected() || !mqtt.connected()) {
MQTT_connect();
}
StaticJsonDocument<500> json;
json["id"] = WiFi.macAddress();
json["up"] = millis();
json["data"] = data;
String msg;
serializeJson(json, msg);
mqtt.publish(ap.config.mqttPublishTopic, msg.c_str());
if (debugger) debugger->print("sendMqttData: ");
if (debugger) debugger->println(data);
}

View File

@ -1,46 +0,0 @@
const char APPLICATION_CSS[] PROGMEM = R"=="==(
.bg-purple {
background-color: var(--purple);
}
.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;
}
)=="==";

View File

@ -1,79 +0,0 @@
const char INDEX_HTML[] PROGMEM = R"=="==(
<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="/css/boot.css"/>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.css"/>
<link rel="stylesheet" type="text/css" href="/css/application.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="/js/gaugemeter.js"></script>
</head>
<body class="bg-light">
<main role="main" class="container">
<div class="d-flex align-items-center p-3 my-2 text-white-50 bg-purple rounded shadow">
<div class="lh-100">
<h6 class="mb-0 text-white lh-100">AMS reader</h6>
<small>${version}</small>
</div>
</div>
<div class="my-3 p-3 bg-white rounded shadow">
<h6 class="border-bottom border-gray pb-2 mb-4">Current meter values</h6>
<div class="row">
<div class="col-md-4">
<div class="text-center">
<div id="P" class="SimpleMeter">
${data.P} W
</div>
<div 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-animate_gauge_colors="1"
data-percent="0"
data-text="-"
data-label="Consumption"
data-append="W"
></div>
</div>
</div>
<div class="col-md-4">
<div id="P1" 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="P2" 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="P3" 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>
</div>
</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="https://github.com/gskjold/AmsToMqttBridge/releases" class="btn btn-outline-secondary">Release notes</a>
</div>
<div class="col-6 text-right">
<a href="/configuration" class="btn btn-primary">Configuration</a>
</div>
</div>
</main>
<script src="/js/index.js"></script>
</body>
</html>
)=="==";

View File

@ -1,80 +0,0 @@
const char INDEX_JS[] PROGMEM = R"=="==(
$(".GaugeMeter").gaugeMeter();
var wait = 500;
var nextrefresh = wait;
var fetch = function() {
$.ajax({
url: '/data.json',
dataType: 'json',
}).done(function(json) {
$(".SimpleMeter").hide();
var el = $(".GaugeMeter");
el.show();
var rate = 2500;
if(json.data) {
el.data('percent', json.pct);
if(json.data.P) {
var num = parseFloat(json.data.P);
if(num > 1000) {
num = num / 1000;
el.data('text', num.toFixed(1));
el.data('append','kW');
} else {
el.data('text', num);
el.data('append','W');
}
}
el.gaugeMeter();
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));
}
}
if(json.data.U1 > 0) {
$('#P1').show();
}
if(json.data.U2 > 0) {
$('#P2').show();
}
if(json.data.U3 > 0) {
$('#P3').show();
}
if(json.meterType == 3) {
rate = 10000;
}
if(json.currentMillis && json.up) {
nextrefresh = rate - ((json.currentMillis - json.up) % rate) + wait;
} else {
nextrefresh = 2500;
}
} else {
el.data('percent', 0);
el.data('text', '-');
el.gaugeMeter();
nextrefresh = 2500;
}
if(!nextrefresh || nextrefresh < 500) {
nextrefresh = 2500;
}
setTimeout(fetch, nextrefresh);
}).fail(function() {
el.data('percent', 0);
el.data('text', '-');
el.gaugeMeter();
nextrefresh = 10000;
setTimeout(fetch, nextrefresh);
});
}
setTimeout(fetch, nextrefresh);
)=="==";

View File

@ -1,12 +1,10 @@
#include "AmsWebServer.h"
#include "version.h"
#include "index_html.h"
#include "configuration_html.h"
#include "boot_css.h"
#include "application_css.h"
#include "gaugemeter_js.h"
#include "index_js.h"
#include "root/index_html.h"
#include "root/configuration_html.h"
#include "root/boot_css.h"
#include "root/gaugemeter_js.h"
#include "Base64.h"
@ -22,10 +20,8 @@ void AmsWebServer::setup(configuration* config, Stream* debugger) {
server.on("/", std::bind(&AmsWebServer::indexHtml, this));
server.on("/configuration", std::bind(&AmsWebServer::configurationHtml, this));
server.on("/css/boot.css", std::bind(&AmsWebServer::bootCss, this));
server.on("/css/application.css", std::bind(&AmsWebServer::applicationCss, this));
server.on("/js/gaugemeter.js", std::bind(&AmsWebServer::gaugemeterJs, this));
server.on("/js/index.js", std::bind(&AmsWebServer::indexJs, this));
server.on("/boot.css", std::bind(&AmsWebServer::bootCss, this));
server.on("/gaugemeter.js", std::bind(&AmsWebServer::gaugemeterJs, this));
server.on("/data.json", std::bind(&AmsWebServer::dataJson, this));
server.on("/save", std::bind(&AmsWebServer::handleSave, this));
@ -39,12 +35,6 @@ void AmsWebServer::setup(configuration* config, Stream* debugger) {
print(WiFi.localIP());
}
println("/");
if(config->hasConfig() && config->fuseSize > 0) {
maxPwr = config->fuseSize * 230;
} else {
maxPwr = 20000;
}
}
void AmsWebServer::loop() {
@ -62,13 +52,12 @@ void AmsWebServer::setJson(StaticJsonDocument<500> json) {
i2 = json["data"]["I2"].as<double>();
i3 = json["data"]["I3"].as<double>();
if(config->hasConfig() && u1 > 0) {
maxPwr = config->fuseSize * u1;
if(maxPwr == 0 && config->hasConfig() && config->fuseSize > 0 && config->distSys > 0) {
int volt = config->distSys == 2 ? 400 : 230;
if(u2 > 0) {
maxPwr += config->fuseSize * u2;
if(u3 > 0) {
maxPwr += config->fuseSize * u3;
}
maxPwr = config->fuseSize * sqrt(3) * volt;
} else {
maxPwr = config->fuseSize * 230;
}
}
} else {
@ -165,7 +154,8 @@ void AmsWebServer::configurationHtml() {
for(int i = 0; i<4; i++) {
html.replace("${config.meterType" + String(i) + "}", config->meterType == i ? "selected" : "");
}
html.replace("${config.mqtt}", config->mqtt);
html.replace("${config.mqtt}", config->mqttHost == 0 ? "" : "checked");
html.replace("${config.mqttHost}", config->mqttHost);
html.replace("${config.mqttPort}", String(config->mqttPort));
html.replace("${config.mqttClientID}", config->mqttClientID);
html.replace("${config.mqttPublishTopic}", config->mqttPublishTopic);
@ -182,6 +172,9 @@ void AmsWebServer::configurationHtml() {
for(int i = 0; i<64; i++) {
html.replace("${config.fuseSize" + String(i) + "}", config->fuseSize == i ? "selected" : "");
}
for(int i = 0; i<3; i++) {
html.replace("${config.distSys" + String(i) + "}", config->distSys == i ? "selected" : "");
}
} else {
html.replace("${config.ssid}", "");
html.replace("${config.ssidPassword}", "");
@ -190,6 +183,7 @@ void AmsWebServer::configurationHtml() {
html.replace("${config.meterType" + String(i) + "}", i == 0 ? "selected" : "");
}
html.replace("${config.mqtt}", "");
html.replace("${config.mqttHost}", "");
html.replace("${config.mqttPort}", "1883");
html.replace("${config.mqttClientID}", "");
html.replace("${config.mqttPublishTopic}", "");
@ -206,6 +200,9 @@ void AmsWebServer::configurationHtml() {
for(int i = 0; i<64; i++) {
html.replace("${config.fuseSize" + String(i) + "}", i == 0 ? "selected" : "");
}
for(int i = 0; i<3; i++) {
html.replace("${config.distSys" + String(i) + "}", i == 0 ? "selected" : "");
}
}
server.send(200, "text/html", html);
}
@ -219,31 +216,13 @@ void AmsWebServer::bootCss() {
server.send(200, "text/css", BOOT_CSS);
}
void AmsWebServer::applicationCss() {
println("Serving /application.css over http...");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send(200, "text/css", APPLICATION_CSS);
}
void AmsWebServer::gaugemeterJs() {
println("Serving /gaugemeter.js over http...");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send(200, "application/javascript", GAUEGMETER_JS);
}
void AmsWebServer::indexJs() {
println("Serving /index.js over http...");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send(200, "application/javascript", INDEX_JS);
server.send(200, "application/javascript", GAUGEMETER_JS);
}
void AmsWebServer::dataJson() {
@ -256,6 +235,15 @@ void AmsWebServer::dataJson() {
if(!json.isNull()) {
println(" json has data");
int maxPwr = this->maxPwr;
if(maxPwr == 0) {
if(u2 > 0) {
maxPwr = 20000;
} else {
maxPwr = 10000;
}
}
json["maxPower"] = maxPwr;
json["pct"] = min(p*100/maxPwr, 100);
json["meterType"] = config->meterType;
@ -286,44 +274,56 @@ void AmsWebServer::handleSave() {
config->meterType = (byte)server.arg("meterType").toInt();
temp = server.arg("mqtt");
config->mqtt = new char[temp.length() + 1];
temp.toCharArray(config->mqtt, temp.length() + 1, 0);
if(server.hasArg("mqtt") && server.arg("mqtt") == "true") {
println("MQTT enabled");
temp = server.arg("mqttHost");
config->mqttHost = new char[temp.length() + 1];
temp.toCharArray(config->mqttHost, temp.length() + 1, 0);
config->mqttPort = (int)server.arg("mqttPort").toInt();
config->mqttPort = (int)server.arg("mqttPort").toInt();
temp = server.arg("mqttClientID");
config->mqttClientID = new char[temp.length() + 1];
temp.toCharArray(config->mqttClientID, temp.length() + 1, 0);
temp = server.arg("mqttClientID");
config->mqttClientID = new char[temp.length() + 1];
temp.toCharArray(config->mqttClientID, temp.length() + 1, 0);
temp = server.arg("mqttPublishTopic");
config->mqttPublishTopic = new char[temp.length() + 1];
temp.toCharArray(config->mqttPublishTopic, temp.length() + 1, 0);
temp = server.arg("mqttPublishTopic");
config->mqttPublishTopic = new char[temp.length() + 1];
temp.toCharArray(config->mqttPublishTopic, temp.length() + 1, 0);
temp = server.arg("mqttSubscribeTopic");
config->mqttSubscribeTopic = new char[temp.length() + 1];
temp.toCharArray(config->mqttSubscribeTopic, temp.length() + 1, 0);
temp = server.arg("mqttSubscribeTopic");
config->mqttSubscribeTopic = new char[temp.length() + 1];
temp.toCharArray(config->mqttSubscribeTopic, temp.length() + 1, 0);
temp = server.arg("mqttUser");
config->mqttUser = new char[temp.length() + 1];
temp.toCharArray(config->mqttUser, temp.length() + 1, 0);
temp = server.arg("mqttUser");
config->mqttUser = new char[temp.length() + 1];
temp.toCharArray(config->mqttUser, temp.length() + 1, 0);
temp = server.arg("mqttPass");
config->mqttPass = new char[temp.length() + 1];
temp.toCharArray(config->mqttPass, temp.length() + 1, 0);
temp = server.arg("mqttPass");
config->mqttPass = new char[temp.length() + 1];
temp.toCharArray(config->mqttPass, temp.length() + 1, 0);
} else {
println("MQTT disabled");
config->mqttHost = NULL;
config->mqttUser = NULL;
config->mqttPass = NULL;
}
config->authSecurity = (byte)server.arg("authSecurity").toInt();
temp = server.arg("authUser");
config->authUser = new char[temp.length() + 1];
temp.toCharArray(config->authUser, temp.length() + 1, 0);
if(config->authSecurity > 0) {
temp = server.arg("authUser");
config->authUser = new char[temp.length() + 1];
temp.toCharArray(config->authUser, temp.length() + 1, 0);
temp = server.arg("authPass");
config->authPass = new char[temp.length() + 1];
temp.toCharArray(config->authPass, temp.length() + 1, 0);
temp = server.arg("authPass");
config->authPass = new char[temp.length() + 1];
temp.toCharArray(config->authPass, temp.length() + 1, 0);
}
config->fuseSize = (int)server.arg("fuseSize").toInt();
config->distSys = (byte)server.arg("distSys").toInt();
println("Saving configuration now...");
if (debugger) config->print(debugger);

View File

@ -45,9 +45,7 @@ private:
void indexHtml();
void configurationHtml();
void bootCss();
void applicationCss();
void gaugemeterJs();
void indexJs();
void dataJson();
void handleSave();

View File

@ -1,4 +1,3 @@
const char BOOT_CSS[] PROGMEM = R"=="==(
/* Ripped necessary style from bootstrap 4.4.1 to make the page look good without internet access. Meant to be overridden by CSS from CDN */
:root {
--blue: #007bff;
@ -237,6 +236,10 @@ a {
border-radius: .25rem;
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.form-control:disabled, .form-control[readonly] {
background-color: #e9ecef;
opacity: 1;
}
input:not([type="image" i]) {
box-sizing: border-box;
}
@ -324,4 +327,3 @@ hr {
*, ::after, ::before {
box-sizing: border-box;
}
)=="==";

View File

@ -1,13 +1,17 @@
const char CONFIGURATION_HTML[] PROGMEM = R"=="==(
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="/css/boot.css"/>
<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.css"/>
<link rel="stylesheet" type="text/css" href="/css/application.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<style>
.bg-purple {
background-color: var(--purple);
}
</style>
</head>
<body class="bg-light">
<main role="main" class="container">
@ -38,8 +42,18 @@ const char CONFIGURATION_HTML[] PROGMEM = R"=="==(
<div class="my-3 p-3 bg-white rounded shadow">
<h6 class="border-bottom border-gray pb-2 mb-4">AMS meter</h6>
<div class="row form-group">
<label class="col-4">Meter type</label>
<div class="col-8">
<label class="col-6">Distribution system</label>
<div class="col-6">
<select class="form-control" name="distSys">
<option value="" ${config.distSys0}></option>
<option value="1" ${config.distSys1}>IT (230V)</option>
<option value="2" ${config.distSys2}>TN (400V)</option>
</select>
</div>
</div>
<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} disabled></option>
<option value="1" ${config.meterType1}>Kaifa</option>
@ -49,8 +63,8 @@ const char CONFIGURATION_HTML[] PROGMEM = R"=="==(
</div>
</div>
<div class="row form-group">
<label class="col-4">Main fuse</label>
<div class="col-8">
<label class="col-6">Main fuse</label>
<div class="col-6">
<select class="form-control" name="fuseSize">
<option value="" ${config.fuseSize0}></option>
<option value="25" ${config.fuseSize25}>25A</option>
@ -66,40 +80,46 @@ const char CONFIGURATION_HTML[] PROGMEM = R"=="==(
<div class="col-md-6 col-lg-4">
<div class="my-2 p-3 bg-white rounded shadow">
<h6 class="border-bottom border-gray pb-2 mb-4">MQTT</h6>
<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>
</div>
<div class="row form-group">
<label class="col-4">Hostname</label>
<div class="col-8">
<input type="text" class="form-control" name="mqtt" value="${config.mqtt}"/>
<input type="text" class="form-control mqtt-config" name="mqttHost" value="${config.mqttHost}"/>
</div>
</div>
<div class="row form-group">
<label class="col-4">Port</label>
<div class="col-8">
<input type="text" class="form-control" name="mqttPort" value="${config.mqttPort}"/>
<input type="text" class="form-control mqtt-config" name="mqttPort" value="${config.mqttPort}"/>
</div>
</div>
<div class="row form-group">
<label class="col-4">Client ID</label>
<div class="col-8">
<input type="text" class="form-control" name="mqttClientID" value="${config.mqttClientID}"/>
<input type="text" class="form-control mqtt-config" name="mqttClientID" value="${config.mqttClientID}"/>
</div>
</div>
<div class="row form-group">
<label class="col-4">Topic</label>
<div class="col-8">
<input type="text" class="form-control" name="mqttPublishTopic" value="${config.mqttPublishTopic}"/>
<input type="text" class="form-control mqtt-config" name="mqttPublishTopic" value="${config.mqttPublishTopic}"/>
</div>
</div>
<div class="row form-group">
<label class="col-4">Username</label>
<div class="col-8">
<input type="text" class="form-control" name="mqttUser" value="${config.mqttUser}"/>
<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" name="mqttPass" value="${config.mqttPass}"/>
<input type="password" class="form-control mqtt-config" name="mqttPass" value="${config.mqttPass}"/>
</div>
</div>
</div>
@ -110,7 +130,7 @@ const char CONFIGURATION_HTML[] PROGMEM = R"=="==(
<div class="row form-group">
<label class="col-4">Security</label>
<div class="col-8">
<select class="form-control" name="authSecurity">
<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>
@ -120,13 +140,13 @@ const char CONFIGURATION_HTML[] PROGMEM = R"=="==(
<div class="row form-group">
<label class="col-4">Username</label>
<div class="col-8">
<input type="text" class="form-control" name="authUser" value="${config.authUser}"/>
<input type="text" class="form-control auth-config" name="authUser" value="${config.authUser}"/>
</div>
</div>
<div class="row form-group">
<label class="col-4">Password</label>
<div class="col-8">
<input type="password" class="form-control" name="authPass" value="${config.authPass}"/>
<input type="password" class="form-control auth-config" name="authPass" value="${config.authPass}"/>
</div>
</div>
</div>
@ -143,6 +163,21 @@ const char CONFIGURATION_HTML[] PROGMEM = R"=="==(
</div>
</form>
</main>
<script>
$('#mqttEnable').on('change', function() {
var inputs = $('.mqtt-config');
inputs.prop('disabled', !$(this).is(':checked'));
});
$('#authSecurity').on('change', function() {
var inputs = $('.auth-config');
inputs.prop('disabled', $(this).val() == 0);
});
$(function() {
$('#mqttEnable').trigger('change');
$('#authSecurity').trigger('change');
});
</script>
</body>
</html>
)=="==";

View File

@ -1,4 +1,3 @@
const char GAUEGMETER_JS[] PROGMEM = R"=="==(
/*
* AshAlom Gauge Meter. Version 2.0.0
* Copyright AshAlom.com All rights reserved.
@ -274,4 +273,3 @@ const char GAUEGMETER_JS[] PROGMEM = R"=="==(
};
}
(jQuery);
)=="==";

199
web/index.html Normal file
View File

@ -0,0 +1,199 @@
<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"/>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="gaugemeter.js"></script>
<style>
.bg-purple {
background-color: var(--purple);
}
.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">
<div class="d-flex align-items-center p-3 my-2 text-white-50 bg-purple rounded shadow">
<div class="lh-100">
<h6 class="mb-0 text-white lh-100">AMS reader</h6>
<small>${version}</small>
</div>
</div>
<div class="my-3 p-3 bg-white rounded shadow">
<h6 class="border-bottom border-gray pb-2 mb-4">Current meter values</h6>
<div class="row">
<div class="col-md-4">
<div class="text-center">
<div id="P" class="SimpleMeter">
${data.P} W
</div>
<div 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-animate_gauge_colors="1"
data-percent="0"
data-text="-"
data-label="Consumption"
data-append="W"
></div>
</div>
</div>
<div class="col-md-4">
<div id="P1" 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="P2" 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="P3" 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>
</div>
</div>
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="https://github.com/gskjold/AmsToMqttBridge/releases" class="btn btn-outline-secondary">Release notes</a>
</div>
<div class="col-6 text-right">
<a href="configuration" class="btn btn-primary">Configuration</a>
</div>
</div>
</main>
<script>
$(".GaugeMeter").gaugeMeter();
var wait = 500;
var nextrefresh = wait;
var fetch = function() {
$.ajax({
url: '/data.json',
dataType: 'json',
}).done(function(json) {
$(".SimpleMeter").hide();
var el = $(".GaugeMeter");
el.show();
var rate = 2500;
if(json.data) {
el.data('percent', json.pct);
if(json.data.P) {
var num = parseFloat(json.data.P);
if(num > 1000) {
num = num / 1000;
el.data('text', num.toFixed(1));
el.data('append','kW');
} else {
el.data('text', num);
el.data('append','W');
}
}
el.gaugeMeter();
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));
}
}
if(json.data.U1 > 0) {
$('#P1').show();
}
if(json.data.U2 > 0) {
$('#P2').show();
}
if(json.data.U3 > 0) {
$('#P3').show();
}
if(json.meterType == 3) {
rate = 10000;
}
if(json.currentMillis && json.up) {
nextrefresh = rate - ((json.currentMillis - json.up) % rate) + wait;
} else {
nextrefresh = 2500;
}
} else {
el.data('percent', 0);
el.data('text', '-');
el.gaugeMeter();
nextrefresh = 2500;
}
if(!nextrefresh || nextrefresh < 500) {
nextrefresh = 2500;
}
setTimeout(fetch, nextrefresh);
}).fail(function() {
el.data('percent', 0);
el.data('text', '-');
el.gaugeMeter();
nextrefresh = 10000;
setTimeout(fetch, nextrefresh);
});
}
setTimeout(fetch, nextrefresh);
</script>
</body>
</html>