mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
1.9.0 web - new implementation
This commit is contained in:
47
.gitignore
vendored
47
.gitignore
vendored
@@ -1,11 +1,50 @@
|
|||||||
.pio*
|
# vscode
|
||||||
|
.vscode
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/*
|
||||||
|
|
||||||
|
# platformio
|
||||||
|
.pio
|
||||||
.clang_complete
|
.clang_complete
|
||||||
.gcc-flags.json
|
.gcc-flags.json
|
||||||
.vscode
|
|
||||||
.env
|
|
||||||
.DS_Store
|
|
||||||
platformio.ini
|
platformio.ini
|
||||||
lib/readme.txt
|
lib/readme.txt
|
||||||
.travis.yml
|
.travis.yml
|
||||||
|
|
||||||
|
# web stuff compiled
|
||||||
|
src/websrc/css/required.css
|
||||||
|
src/websrc/js/required.js
|
||||||
|
src/websrc/fonts
|
||||||
|
src/websrc/gzipped
|
||||||
|
src/websrc/temp
|
||||||
|
*.gz.h
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# project specfic
|
||||||
|
.DS_Store
|
||||||
scripts/stackdmp.txt
|
scripts/stackdmp.txt
|
||||||
*.bin
|
*.bin
|
||||||
|
|||||||
@@ -3,45 +3,59 @@
|
|||||||
;
|
;
|
||||||
|
|
||||||
[platformio]
|
[platformio]
|
||||||
;default_envs = release
|
default_envs = release
|
||||||
default_envs = debug
|
;default_envs = debug
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
; -DMYESP_TIMESTAMP -DTESTS -DCRASH -DFORCE_SERIAL -DNO_GLOBAL_EEPROM -DLOGICANALYZER
|
; -DMYESP_TIMESTAMP -DTESTS -DCRASH -DFORCE_SERIAL -DNO_GLOBAL_EEPROM -DLOGICANALYZER
|
||||||
extra_flags = -DNO_GLOBAL_EEPROM
|
general_flags = -DNO_GLOBAL_EEPROM
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
board = d1_mini
|
board = d1_mini
|
||||||
; board = nodemcuv2
|
; board = nodemcuv2
|
||||||
; board = d1_mini_pro
|
|
||||||
framework = arduino
|
framework = arduino
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
lib_deps =
|
lib_deps =
|
||||||
CRC32
|
CRC32
|
||||||
CircularBuffer
|
CircularBuffer
|
||||||
|
OneWire
|
||||||
JustWifi
|
JustWifi
|
||||||
AsyncMqttClient
|
AsyncMqttClient
|
||||||
ArduinoJson
|
ArduinoJson
|
||||||
OneWire
|
|
||||||
EEPROM_rotate
|
EEPROM_rotate
|
||||||
|
ESP Async WebServer
|
||||||
|
ESPAsyncTCP
|
||||||
|
ESPAsyncUDP
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
; example ports for OSX
|
||||||
|
;upload_port = /dev/cu.wchusbserial14403
|
||||||
|
;upload_port = /dev/cu.usbserial-1440
|
||||||
|
|
||||||
; uncomment next 2 lines for OTA
|
; uncomment next 2 lines for OTA
|
||||||
;upload_protocol = espota
|
upload_protocol = espota
|
||||||
;upload_port = ems-esp.local
|
upload_port = ems-esp.local
|
||||||
|
|
||||||
|
[env:buildweb]
|
||||||
|
extra_scripts = pre:scripts/buildweb.py
|
||||||
|
|
||||||
[env:debug]
|
[env:debug]
|
||||||
build_type = debug
|
build_type = debug
|
||||||
build_flags = ${common.extra_flags} -DCRASH
|
build_flags = ${common.general_flags} -DCRASH -DTESTS
|
||||||
extra_scripts = pre:scripts/rename_fw.py
|
extra_scripts =
|
||||||
|
pre:scripts/rename_fw.py
|
||||||
|
pre:scripts/buildweb.py
|
||||||
|
|
||||||
[env:clean]
|
[env:clean]
|
||||||
extra_scripts = pre:scripts/clean_fw.py
|
extra_scripts = pre:scripts/clean_fw.py
|
||||||
|
|
||||||
[env:release]
|
[env:release]
|
||||||
build_flags = ${common.extra_flags}
|
build_flags = ${common.general_flags}
|
||||||
extra_scripts = pre:scripts/rename_fw.py
|
extra_scripts = pre:scripts/rename_fw.py
|
||||||
|
|
||||||
[env:checkcode]
|
[env:checkcode]
|
||||||
build_flags = ${common.extra_flags}
|
build_type = debug
|
||||||
|
build_flags = ${common.general_flags} -Wall
|
||||||
extra_scripts = scripts/checkcode.py
|
extra_scripts = scripts/checkcode.py
|
||||||
|
|
||||||
|
|||||||
3
scripts/buildweb.py
Normal file
3
scripts/buildweb.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Import("env")
|
||||||
|
|
||||||
|
env.Execute("node ./tools/webfilesbuilder/node_modules/gulp/bin/gulp.js --cwd ./tools/webfilesbuilder")
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
from subprocess import call
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
|
def build_web(source, target, env):
|
||||||
|
print("\n** Build web...")
|
||||||
|
call(["gulp", "-f", os.getcwd()+"/tools/webfilesbuilder/gulpfile.js"])
|
||||||
|
|
||||||
bag = {}
|
bag = {}
|
||||||
exprs = [
|
exprs = [
|
||||||
(re.compile(r'^#define APP_VERSION\s+"(\S+)"'), 'app_version'),
|
(re.compile(r'^#define APP_VERSION\s+"(\S+)"'), 'app_version'),
|
||||||
@@ -22,5 +28,9 @@ app_hostname = bag.get('app_hostname')
|
|||||||
board = env['BOARD']
|
board = env['BOARD']
|
||||||
branch = env['PIOENV']
|
branch = env['PIOENV']
|
||||||
|
|
||||||
|
# build the web files
|
||||||
|
env.AddPreAction("buildprog", build_web)
|
||||||
|
|
||||||
# build filename, replacing . with _ for the version
|
# build filename, replacing . with _ for the version
|
||||||
env.Replace(PROGNAME="firmware_%s" % branch + "_" + app_version.replace(".", "_"))
|
env.Replace(PROGNAME="firmware_%s" % branch + "_" + app_version.replace(".", "_"))
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -9,15 +9,18 @@
|
|||||||
#ifndef MyESP_h
|
#ifndef MyESP_h
|
||||||
#define MyESP_h
|
#define MyESP_h
|
||||||
|
|
||||||
#define MYESP_VERSION "1.1.24"
|
#define MYESP_VERSION "1.2"
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
#include <AsyncMqttClient.h> // https://github.com/marvinroger/async-mqtt-client and for ESP32 see https://github.com/marvinroger/async-mqtt-client/issues/127
|
#include <AsyncMqttClient.h> // https://github.com/marvinroger/async-mqtt-client and for ESP32 see https://github.com/marvinroger/async-mqtt-client/issues/127
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESPAsyncUDP.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <FS.h>
|
#include <FS.h>
|
||||||
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
||||||
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
|
|
||||||
|
#include "Ntp.h"
|
||||||
|
#include "TelnetSpy.h" // modified from https://github.com/yasheena/telnetspy
|
||||||
|
|
||||||
#ifdef CRASH
|
#ifdef CRASH
|
||||||
#include <EEPROM_Rotate.h>
|
#include <EEPROM_Rotate.h>
|
||||||
@@ -30,7 +33,6 @@ extern struct rst_info resetInfo;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
//#include <ESPmDNS.h>
|
|
||||||
#include <SPIFFS.h> // added for ESP32
|
#include <SPIFFS.h> // added for ESP32
|
||||||
#define ets_vsnprintf vsnprintf // added for ESP32
|
#define ets_vsnprintf vsnprintf // added for ESP32
|
||||||
#define OTA_PORT 3232
|
#define OTA_PORT 3232
|
||||||
@@ -39,13 +41,31 @@ extern struct rst_info resetInfo;
|
|||||||
#define OTA_PORT 8266
|
#define OTA_PORT 8266
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MYEMS_CONFIG_FILE "/config.json"
|
// web files
|
||||||
|
// reference libs
|
||||||
|
#include "webh/glyphicons-halflings-regular.woff.gz.h"
|
||||||
|
#include "webh/required.css.gz.h"
|
||||||
|
#include "webh/required.js.gz.h"
|
||||||
|
|
||||||
#define LOADAVG_INTERVAL 30000 // Interval between calculating load average (in ms) = 30 seconds
|
// custom stuff
|
||||||
|
#include "webh/index.html.gz.h"
|
||||||
|
#include "webh/myesp.html.gz.h"
|
||||||
|
#include "webh/myesp.js.gz.h"
|
||||||
|
|
||||||
|
#define MYESP_CONFIG_FILE "/myesp.json"
|
||||||
|
#define MYESP_CUSTOMCONFIG_FILE "/customconfig.json"
|
||||||
|
#define MYESP_EVENTLOG_FILE "/eventlog.json"
|
||||||
|
|
||||||
|
#define MYESP_HTTP_USERNAME "admin" // HTTP username
|
||||||
|
#define MYESP_HTTP_PASSWORD "admin" // default password
|
||||||
|
|
||||||
|
#define MYESP_NTP_SERVER "pool.ntp.org" // default ntp server
|
||||||
|
|
||||||
|
#define MYESP_LOADAVG_INTERVAL 30000 // Interval between calculating load average (in ms) = 30 seconds
|
||||||
|
|
||||||
// WIFI
|
// WIFI
|
||||||
#define WIFI_CONNECT_TIMEOUT 10000 // Connecting timeout for WIFI in ms (10 seconds)
|
#define MYESP_WIFI_CONNECT_TIMEOUT 10000 // Connecting timeout for WIFI in ms (10 seconds)
|
||||||
#define WIFI_RECONNECT_INTERVAL 600000 // If could not connect to WIFI, retry after this time in ms. 10 minutes
|
#define MYESP_WIFI_RECONNECT_INTERVAL 600000 // If could not connect to WIFI, retry after this time in ms. 10 minutes
|
||||||
|
|
||||||
// MQTT
|
// MQTT
|
||||||
#define MQTT_PORT 1883 // MQTT port
|
#define MQTT_PORT 1883 // MQTT port
|
||||||
@@ -57,6 +77,12 @@ extern struct rst_info resetInfo;
|
|||||||
#define MQTT_TOPIC_HEARTBEAT "heartbeat"
|
#define MQTT_TOPIC_HEARTBEAT "heartbeat"
|
||||||
#define MQTT_TOPIC_START_PAYLOAD "start"
|
#define MQTT_TOPIC_START_PAYLOAD "start"
|
||||||
#define MQTT_TOPIC_RESTART "restart"
|
#define MQTT_TOPIC_RESTART "restart"
|
||||||
|
#define MQTT_WILL_ONLINE_PAYLOAD "online" // for last will & testament payload
|
||||||
|
#define MQTT_WILL_OFFLINE_PAYLOAD "offline" // for last will & testament payload
|
||||||
|
#define MQTT_RETAIN false
|
||||||
|
#define MQTT_KEEPALIVE 60 // 1 minute
|
||||||
|
#define MQTT_QOS 1
|
||||||
|
#define MQTT_WILL_TOPIC "status" // for last will & testament topic name
|
||||||
|
|
||||||
// Internal MQTT events
|
// Internal MQTT events
|
||||||
#define MQTT_CONNECT_EVENT 0
|
#define MQTT_CONNECT_EVENT 0
|
||||||
@@ -94,18 +120,21 @@ extern struct rst_info resetInfo;
|
|||||||
|
|
||||||
// reset reason codes
|
// reset reason codes
|
||||||
PROGMEM const char custom_reset_hardware[] = "Hardware button";
|
PROGMEM const char custom_reset_hardware[] = "Hardware button";
|
||||||
PROGMEM const char custom_reset_terminal[] = "Reboot from terminal";
|
PROGMEM const char custom_reset_terminal[] = "Restart from terminal";
|
||||||
PROGMEM const char custom_reset_mqtt[] = "Reboot from MQTT";
|
PROGMEM const char custom_reset_mqtt[] = "Restart from MQTT";
|
||||||
PROGMEM const char custom_reset_ota[] = "Reboot after successful OTA update";
|
PROGMEM const char custom_reset_ota[] = "Restart after successful OTA update";
|
||||||
PROGMEM const char * const custom_reset_string[] = {custom_reset_hardware, custom_reset_terminal, custom_reset_mqtt, custom_reset_ota};
|
PROGMEM const char custom_reset_factory[] = "Factory reset";
|
||||||
|
PROGMEM const char * const custom_reset_string[] = {custom_reset_hardware, custom_reset_terminal, custom_reset_mqtt, custom_reset_ota, custom_reset_factory};
|
||||||
#define CUSTOM_RESET_HARDWARE 1 // Reset from hardware button
|
#define CUSTOM_RESET_HARDWARE 1 // Reset from hardware button
|
||||||
#define CUSTOM_RESET_TERMINAL 2 // Reset from terminal
|
#define CUSTOM_RESET_TERMINAL 2 // Reset from terminal
|
||||||
#define CUSTOM_RESET_MQTT 3 // Reset via MQTT
|
#define CUSTOM_RESET_MQTT 3 // Reset via MQTT
|
||||||
#define CUSTOM_RESET_OTA 4 // Reset after successful OTA update
|
#define CUSTOM_RESET_OTA 4 // Reset after successful OTA update
|
||||||
#define CUSTOM_RESET_MAX 4
|
#define CUSTOM_RESET_FACTORY 5 // Factory reset
|
||||||
|
#define CUSTOM_RESET_MAX 5
|
||||||
|
|
||||||
// SPIFFS
|
// SPIFFS
|
||||||
#define SPIFFS_MAXSIZE 800 // https://arduinojson.org/v6/assistant/
|
#define MYESP_SPIFFS_MAXSIZE 800 // https://arduinojson.org/v6/assistant/
|
||||||
|
#define MYESP_JSON_MAXSIZE 2000 // for large Dynamic json files
|
||||||
|
|
||||||
// CRASH
|
// CRASH
|
||||||
/**
|
/**
|
||||||
@@ -157,9 +186,9 @@ struct RtcmemData {
|
|||||||
|
|
||||||
static_assert(sizeof(RtcmemData) <= (RTCMEM_BLOCKS * 4u), "RTCMEM struct is too big");
|
static_assert(sizeof(RtcmemData) <= (RTCMEM_BLOCKS * 4u), "RTCMEM struct is too big");
|
||||||
|
|
||||||
#define SYSTEM_CHECK_TIME 60000 // The system is considered stable after these many millis (1 minute)
|
#define MYESP_SYSTEM_CHECK_TIME 60000 // The system is considered stable after these many millis (1 minute)
|
||||||
#define SYSTEM_CHECK_MAX 5 // After this many crashes on boot
|
#define MYESP_SYSTEM_CHECK_MAX 10 // After this many crashes on boot
|
||||||
#define HEARTBEAT_INTERVAL 120000 // in milliseconds, how often the MQTT heartbeat is sent (2 mins)
|
#define MYESP_HEARTBEAT_INTERVAL 120000 // in milliseconds, how often the MQTT heartbeat is sent (2 mins)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool set; // is it a set command
|
bool set; // is it a set command
|
||||||
@@ -181,9 +210,9 @@ typedef std::function<void()>
|
|||||||
typedef std::function<void()> ota_callback_f;
|
typedef std::function<void()> ota_callback_f;
|
||||||
typedef std::function<void(uint8_t, const char *)> telnetcommand_callback_f;
|
typedef std::function<void(uint8_t, const char *)> telnetcommand_callback_f;
|
||||||
typedef std::function<void(uint8_t)> telnet_callback_f;
|
typedef std::function<void(uint8_t)> telnet_callback_f;
|
||||||
typedef std::function<bool(MYESP_FSACTION, const JsonObject json)> fs_callback_f;
|
typedef std::function<bool(MYESP_FSACTION, JsonObject json)> fs_loadsave_callback_f;
|
||||||
typedef std::function<bool(MYESP_FSACTION, uint8_t, const char *, const char *)> fs_settings_callback_f;
|
typedef std::function<bool(MYESP_FSACTION, uint8_t, const char *, const char *)> fs_setlist_callback_f;
|
||||||
typedef std::function<void(char *)> web_callback_f;
|
typedef std::function<void(JsonObject root)> web_callback_f;
|
||||||
|
|
||||||
// calculates size of an 2d array at compile time
|
// calculates size of an 2d array at compile time
|
||||||
template <typename T, size_t N>
|
template <typename T, size_t N>
|
||||||
@@ -191,58 +220,34 @@ constexpr size_t ArraySize(T (&)[N]) {
|
|||||||
return N;
|
return N;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
#define MYESP_UPTIME_OVERFLOW 4294967295 // Uptime overflow value
|
||||||
void PROGMEM_readAnything(const T * sce, T & dest) {
|
|
||||||
memcpy_P(&dest, sce, sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define UPTIME_OVERFLOW 4294967295 // Uptime overflow value
|
|
||||||
|
|
||||||
// web min and max length of wifi ssid and password
|
// web min and max length of wifi ssid and password
|
||||||
#define MAX_SSID_LEN 32
|
#define MYESP_MAX_STR_LEN 16
|
||||||
#define MAX_PWD_LEN 64
|
|
||||||
|
|
||||||
#define MYESP_BOOTUP_FLASHDELAY 50 // flash duration for LED at bootup sequence
|
#define MYESP_BOOTUP_FLASHDELAY 50 // flash duration for LED at bootup sequence
|
||||||
#define MYESP_BOOTUP_DELAY 2000 // time before we open the window to reset. This is to stop resetting values when uploading firmware via USB
|
#define MYESP_BOOTUP_DELAY 2000 // time before we open the window to reset. This is to stop resetting values when uploading firmware via USB
|
||||||
|
|
||||||
// max size of char buffer for storing web page
|
|
||||||
#define MYESP_MAXCHARBUFFER 800
|
|
||||||
|
|
||||||
// Holds the admin webpage in the program memory
|
|
||||||
const char webCommonPage_start[] = "<html>"
|
|
||||||
"<head>"
|
|
||||||
"<style>input {font-size: 1.2em; width: 100%; max-width: 350px; display: block; margin: 5px auto; }"
|
|
||||||
"body {background-color: #FFA500;font: normal 18px Verdana, Arial, sans-serif;} </style>";
|
|
||||||
|
|
||||||
const char webCommonPage_start_body[] = "</head><body>";
|
|
||||||
|
|
||||||
const char webCommonPage_end[] = "</body></html>";
|
|
||||||
|
|
||||||
const char webResetPage_form[] = "<form id='form' action='/reset' method='post'>"
|
|
||||||
"<input name='newssid' type='text' maxlength='32' placeholder='SSID'>"
|
|
||||||
"<input name='newpassword' id='password1' type='password' maxlength='64' placeholder='Password'>"
|
|
||||||
"<input type='submit' value='Save and reboot'>"
|
|
||||||
"</form>";
|
|
||||||
|
|
||||||
const char webResetPage_post[] =
|
|
||||||
"<p>New wifi credentials set. System is now rebooting. Please wait a few seconds and then reconnect via telnet or browser to its new IP given address.</p>";
|
|
||||||
|
|
||||||
const char webResetAllPage_form[] = "<form id='resetform' action='/resetall' method='post'>"
|
|
||||||
"<input name='confirm' type='text' minlength='3' maxlength='16' placeholder='yes'>"
|
|
||||||
"<input type='submit' value='Reset All'>"
|
|
||||||
"</form>";
|
|
||||||
|
|
||||||
// class definition
|
// class definition
|
||||||
class MyESP {
|
class MyESP {
|
||||||
|
protected:
|
||||||
|
// webserver
|
||||||
|
AsyncWebServer * _webServer;
|
||||||
|
AsyncWebSocket * _ws;
|
||||||
|
|
||||||
|
// NTP
|
||||||
|
NtpClient NTP;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MyESP();
|
MyESP();
|
||||||
~MyESP();
|
~MyESP();
|
||||||
|
|
||||||
ESP8266WebServer webServer; // Web server on port 80
|
// write event called from within lambda classs
|
||||||
|
static void _writeEvent(const char * type, const char * src, const char * desc, const char * data);
|
||||||
|
|
||||||
// wifi
|
// wifi
|
||||||
void setWIFICallback(void (*callback)());
|
void setWIFICallback(void (*callback)());
|
||||||
void setWIFI(const char * wifi_ssid, const char * wifi_password, wifi_callback_f callback);
|
void setWIFI(wifi_callback_f callback);
|
||||||
bool isWifiConnected();
|
bool isWifiConnected();
|
||||||
bool isAPmode();
|
bool isAPmode();
|
||||||
|
|
||||||
@@ -251,17 +256,7 @@ class MyESP {
|
|||||||
void mqttSubscribe(const char * topic);
|
void mqttSubscribe(const char * topic);
|
||||||
void mqttUnsubscribe(const char * topic);
|
void mqttUnsubscribe(const char * topic);
|
||||||
void mqttPublish(const char * topic, const char * payload);
|
void mqttPublish(const char * topic, const char * payload);
|
||||||
void setMQTT(const char * mqtt_host,
|
void setMQTT(mqtt_callback_f callback);
|
||||||
const char * mqtt_username,
|
|
||||||
const char * mqtt_password,
|
|
||||||
const char * mqtt_base,
|
|
||||||
unsigned long mqtt_keepalive,
|
|
||||||
unsigned char mqtt_qos,
|
|
||||||
bool mqtt_retain,
|
|
||||||
const char * mqtt_will_topic,
|
|
||||||
const char * mqtt_will_online_payload,
|
|
||||||
const char * mqtt_will_offline_payload,
|
|
||||||
mqtt_callback_f callback);
|
|
||||||
|
|
||||||
// OTA
|
// OTA
|
||||||
void setOTA(ota_callback_f OTACallback_pre, ota_callback_f OTACallback_post);
|
void setOTA(ota_callback_f OTACallback_pre, ota_callback_f OTACallback_post);
|
||||||
@@ -274,8 +269,9 @@ class MyESP {
|
|||||||
void setUseSerial(bool toggle);
|
void setUseSerial(bool toggle);
|
||||||
|
|
||||||
// FS
|
// FS
|
||||||
void setSettings(fs_callback_f callback, fs_settings_callback_f fs_settings_callback);
|
void setSettings(fs_loadsave_callback_f loadsave, fs_setlist_callback_f setlist, bool useSerial = true);
|
||||||
bool fs_saveConfig();
|
bool fs_saveConfig(JsonObject root);
|
||||||
|
bool fs_saveCustomConfig(JsonObject root);
|
||||||
|
|
||||||
// Web
|
// Web
|
||||||
void setWeb(web_callback_f callback_web);
|
void setWeb(web_callback_f callback_web);
|
||||||
@@ -289,8 +285,7 @@ class MyESP {
|
|||||||
// general
|
// general
|
||||||
void end();
|
void end();
|
||||||
void loop();
|
void loop();
|
||||||
void begin(const char * app_hostname, const char * app_name, const char * app_version);
|
void begin(const char * app_hostname, const char * app_name, const char * app_version, const char * app_helpurl, const char * app_updateurl);
|
||||||
void setBoottime(const char * boottime);
|
|
||||||
void resetESP();
|
void resetESP();
|
||||||
int getWifiQuality();
|
int getWifiQuality();
|
||||||
void showSystemStats();
|
void showSystemStats();
|
||||||
@@ -298,6 +293,7 @@ class MyESP {
|
|||||||
uint32_t getSystemLoadAverage();
|
uint32_t getSystemLoadAverage();
|
||||||
uint32_t getSystemResetReason();
|
uint32_t getSystemResetReason();
|
||||||
uint8_t getSystemBootStatus();
|
uint8_t getSystemBootStatus();
|
||||||
|
bool _have_ntp_time;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// mqtt
|
// mqtt
|
||||||
@@ -306,40 +302,41 @@ class MyESP {
|
|||||||
void _mqttOnMessage(char * topic, char * payload, size_t len);
|
void _mqttOnMessage(char * topic, char * payload, size_t len);
|
||||||
void _mqttConnect();
|
void _mqttConnect();
|
||||||
void _mqtt_setup();
|
void _mqtt_setup();
|
||||||
mqtt_callback_f _mqtt_callback;
|
mqtt_callback_f _mqtt_callback_f;
|
||||||
void _mqttOnConnect();
|
void _mqttOnConnect();
|
||||||
void _sendStart();
|
void _sendStart();
|
||||||
char * _mqttTopic(const char * topic);
|
char * _mqttTopic(const char * topic);
|
||||||
char * _mqtt_host;
|
char * _mqtt_ip;
|
||||||
char * _mqtt_username;
|
char * _mqtt_user;
|
||||||
char * _mqtt_password;
|
char * _mqtt_password;
|
||||||
|
int _mqtt_port;
|
||||||
char * _mqtt_base;
|
char * _mqtt_base;
|
||||||
|
bool _mqtt_enabled;
|
||||||
unsigned long _mqtt_keepalive;
|
unsigned long _mqtt_keepalive;
|
||||||
unsigned char _mqtt_qos;
|
unsigned char _mqtt_qos;
|
||||||
bool _mqtt_retain;
|
bool _mqtt_retain;
|
||||||
char * _mqtt_will_topic;
|
char * _mqtt_will_topic;
|
||||||
char * _mqtt_will_online_payload;
|
char * _mqtt_will_online_payload;
|
||||||
char * _mqtt_will_offline_payload;
|
char * _mqtt_will_offline_payload;
|
||||||
char * _mqtt_topic;
|
|
||||||
unsigned long _mqtt_last_connection;
|
unsigned long _mqtt_last_connection;
|
||||||
bool _mqtt_connecting;
|
bool _mqtt_connecting;
|
||||||
bool _rtcmem_status;
|
bool _mqtt_heartbeat;
|
||||||
|
|
||||||
// wifi
|
// wifi
|
||||||
void _wifiCallback(justwifi_messages_t code, char * parameter);
|
void _wifiCallback(justwifi_messages_t code, char * parameter);
|
||||||
void _wifi_setup();
|
void _wifi_setup();
|
||||||
wifi_callback_f _wifi_callback;
|
wifi_callback_f _wifi_callback_f;
|
||||||
char * _wifi_ssid;
|
char * _network_ssid;
|
||||||
char * _wifi_password;
|
char * _network_password;
|
||||||
|
uint8_t _network_wmode;
|
||||||
bool _wifi_connected;
|
bool _wifi_connected;
|
||||||
String _getESPhostname();
|
String _getESPhostname();
|
||||||
|
|
||||||
// ota
|
// ota
|
||||||
ota_callback_f _ota_pre_callback;
|
ota_callback_f _ota_pre_callback_f;
|
||||||
ota_callback_f _ota_post_callback;
|
ota_callback_f _ota_post_callback_f;
|
||||||
void _ota_setup();
|
void _ota_setup();
|
||||||
void _OTACallback();
|
void _OTACallback();
|
||||||
bool _ota_doing_update;
|
|
||||||
|
|
||||||
// crash
|
// crash
|
||||||
void _eeprom_setup();
|
void _eeprom_setup();
|
||||||
@@ -354,37 +351,41 @@ class MyESP {
|
|||||||
void _telnet_setup();
|
void _telnet_setup();
|
||||||
char _command[TELNET_MAX_COMMAND_LENGTH]; // the input command from either Serial or Telnet
|
char _command[TELNET_MAX_COMMAND_LENGTH]; // the input command from either Serial or Telnet
|
||||||
void _consoleShowHelp();
|
void _consoleShowHelp();
|
||||||
telnetcommand_callback_f _telnetcommand_callback; // Callable for projects commands
|
telnetcommand_callback_f _telnetcommand_callback_f; // Callable for projects commands
|
||||||
telnet_callback_f _telnet_callback; // callback for connect/disconnect
|
telnet_callback_f _telnet_callback_f; // callback for connect/disconnect
|
||||||
bool _changeSetting(uint8_t wc, const char * setting, const char * value);
|
bool _changeSetting(uint8_t wc, const char * setting, const char * value);
|
||||||
|
|
||||||
// fs
|
// fs and settings
|
||||||
void _fs_setup();
|
void _fs_setup();
|
||||||
bool _fs_loadConfig();
|
bool _fs_loadConfig();
|
||||||
void _fs_printConfig();
|
bool _fs_loadCustomConfig();
|
||||||
void _fs_eraseConfig();
|
void _fs_printFile(const char * file);
|
||||||
|
void _fs_eraseConfig();
|
||||||
|
bool _fs_writeConfig();
|
||||||
|
bool _fs_createCustomConfig();
|
||||||
|
bool _fs_sendConfig();
|
||||||
|
fs_loadsave_callback_f _fs_loadsave_callback_f;
|
||||||
|
fs_setlist_callback_f _fs_setlist_callback_f;
|
||||||
|
|
||||||
// settings
|
void _printSetCommands();
|
||||||
fs_callback_f _fs_callback;
|
|
||||||
fs_settings_callback_f _fs_settings_callback;
|
|
||||||
void _printSetCommands();
|
|
||||||
|
|
||||||
// web
|
|
||||||
web_callback_f _web_callback;
|
|
||||||
|
|
||||||
// general
|
// general
|
||||||
char * _app_hostname;
|
char * _general_hostname;
|
||||||
char * _app_name;
|
char * _app_name;
|
||||||
char * _app_version;
|
char * _app_version;
|
||||||
char * _boottime;
|
char * _app_helpurl;
|
||||||
|
char * _app_updateurl;
|
||||||
bool _suspendOutput;
|
bool _suspendOutput;
|
||||||
bool _serial;
|
bool _general_serial;
|
||||||
bool _heartbeat;
|
|
||||||
unsigned long _getUptime();
|
unsigned long _getUptime();
|
||||||
String _buildTime();
|
char * _getBuildTime();
|
||||||
bool _firstInstall;
|
char * _buildTime;
|
||||||
|
bool _timerequest;
|
||||||
|
bool _formatreq;
|
||||||
|
bool _hasValue(char * s);
|
||||||
|
|
||||||
// reset reason and rtcmem
|
// reset reason and rtcmem
|
||||||
|
bool _rtcmem_status;
|
||||||
bool _rtcmemStatus();
|
bool _rtcmemStatus();
|
||||||
bool _getRtcmemStatus();
|
bool _getRtcmemStatus();
|
||||||
|
|
||||||
@@ -418,11 +419,30 @@ class MyESP {
|
|||||||
// heartbeat
|
// heartbeat
|
||||||
void _heartbeatCheck(bool force);
|
void _heartbeatCheck(bool force);
|
||||||
|
|
||||||
// webserver
|
// web
|
||||||
|
web_callback_f _web_callback_f;
|
||||||
|
const char * _http_username;
|
||||||
|
|
||||||
|
// log
|
||||||
|
void _sendEventLog(uint8_t page);
|
||||||
|
|
||||||
|
// web
|
||||||
|
void _onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len);
|
||||||
|
void _procMsg(AsyncWebSocketClient * client, size_t sz);
|
||||||
|
void _sendStatus();
|
||||||
|
void _sendCustomStatus();
|
||||||
|
void _printScanResult(int networksFound);
|
||||||
|
void _sendTime();
|
||||||
void _webserver_setup();
|
void _webserver_setup();
|
||||||
void _webRootPage();
|
void _webRootPage();
|
||||||
void _webResetPage();
|
void _webResetPage();
|
||||||
void _webResetAllPage();
|
void _webResetAllPage();
|
||||||
|
|
||||||
|
// ntp
|
||||||
|
uint8_t _ntp_timezone;
|
||||||
|
char * _ntp_server;
|
||||||
|
uint8_t _ntp_interval;
|
||||||
|
bool _ntp_enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MyESP myESP;
|
extern MyESP myESP;
|
||||||
49
src/Ntp.cpp
Normal file
49
src/Ntp.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Ntp.cpp
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Ntp.h"
|
||||||
|
|
||||||
|
char * NtpClient::TimeServerName;
|
||||||
|
int8_t NtpClient::timezone;
|
||||||
|
time_t NtpClient::syncInterval;
|
||||||
|
IPAddress NtpClient::timeServer;
|
||||||
|
|
||||||
|
AsyncUDP NtpClient::udpListener;
|
||||||
|
byte NtpClient::NTPpacket[NTP_PACKET_SIZE];
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR NtpClient::Ntp(const char * server, int8_t tz, time_t syncSecs) {
|
||||||
|
TimeServerName = strdup(server);
|
||||||
|
timezone = tz;
|
||||||
|
syncInterval = syncSecs;
|
||||||
|
WiFi.hostByName(TimeServerName, timeServer);
|
||||||
|
setSyncProvider(getNtpTime);
|
||||||
|
setSyncInterval(syncInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
ICACHE_FLASH_ATTR NtpClient::~NtpClient() {
|
||||||
|
udpListener.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// send an NTP request to the time server at the given address
|
||||||
|
time_t ICACHE_FLASH_ATTR NtpClient::getNtpTime() {
|
||||||
|
memset(NTPpacket, 0, sizeof(NTPpacket));
|
||||||
|
NTPpacket[0] = 0b11100011;
|
||||||
|
NTPpacket[1] = 0;
|
||||||
|
NTPpacket[2] = 6;
|
||||||
|
NTPpacket[3] = 0xEC;
|
||||||
|
NTPpacket[12] = 49;
|
||||||
|
NTPpacket[13] = 0x4E;
|
||||||
|
NTPpacket[14] = 49;
|
||||||
|
NTPpacket[15] = 52;
|
||||||
|
if (udpListener.connect(timeServer, 123)) {
|
||||||
|
udpListener.onPacket([](AsyncUDPPacket packet) {
|
||||||
|
unsigned long highWord = word(packet.data()[40], packet.data()[41]);
|
||||||
|
unsigned long lowWord = word(packet.data()[42], packet.data()[43]);
|
||||||
|
time_t UnixUTCtime = (highWord << 16 | lowWord) - 2208988800UL;
|
||||||
|
setTime(UnixUTCtime);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
udpListener.write(NTPpacket, sizeof(NTPpacket));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
35
src/Ntp.h
Normal file
35
src/Ntp.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Ntp.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NTP_H_
|
||||||
|
#define NTP_H_
|
||||||
|
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESPAsyncUDP.h>
|
||||||
|
|
||||||
|
#include "TimeLib.h" // customized version of the time library
|
||||||
|
|
||||||
|
#define NTP_PACKET_SIZE 48 // NTP time is in the first 48 bytes of message
|
||||||
|
|
||||||
|
class NtpClient {
|
||||||
|
public:
|
||||||
|
void ICACHE_FLASH_ATTR Ntp(const char * server, int8_t tz, time_t syncSecs);
|
||||||
|
ICACHE_FLASH_ATTR virtual ~NtpClient();
|
||||||
|
|
||||||
|
static char * TimeServerName;
|
||||||
|
static IPAddress timeServer;
|
||||||
|
static int8_t timezone;
|
||||||
|
static time_t syncInterval;
|
||||||
|
|
||||||
|
static AsyncUDP udpListener;
|
||||||
|
|
||||||
|
static byte NTPpacket[NTP_PACKET_SIZE];
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ICACHE_FLASH_ATTR time_t getNtpTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -303,7 +303,7 @@ int TelnetSpy::available(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int TelnetSpy::read(void) {
|
int TelnetSpy::read(void) {
|
||||||
int val;
|
int val = 0;
|
||||||
if (usedSer) {
|
if (usedSer) {
|
||||||
val = usedSer->read();
|
val = usedSer->read();
|
||||||
if (val != -1) {
|
if (val != -1) {
|
||||||
@@ -319,7 +319,7 @@ int TelnetSpy::read(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int TelnetSpy::peek(void) {
|
int TelnetSpy::peek(void) {
|
||||||
int val;
|
int val = 0;
|
||||||
if (usedSer) {
|
if (usedSer) {
|
||||||
val = usedSer->peek();
|
val = usedSer->peek();
|
||||||
if (val != -1) {
|
if (val != -1) {
|
||||||
185
src/TimeLib.cpp
Normal file
185
src/TimeLib.cpp
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
#include "TimeLib.h"
|
||||||
|
|
||||||
|
static tmElements_t tm; // a cache of time elements
|
||||||
|
static time_t cacheTime; // the time the cache was updated
|
||||||
|
static uint32_t syncInterval = 300; // time sync will be attempted after this many seconds
|
||||||
|
static uint32_t sysTime = 0;
|
||||||
|
static uint32_t prevMillis = 0;
|
||||||
|
static uint32_t nextSyncTime = 0;
|
||||||
|
static timeStatus_t Status = timeNotSet;
|
||||||
|
getExternalTime getTimePtr; // pointer to external sync function
|
||||||
|
|
||||||
|
#define LEAP_YEAR(Y) (((1970 + (Y)) > 0) && !((1970 + (Y)) % 4) && (((1970 + (Y)) % 100) || !((1970 + (Y)) % 400)))
|
||||||
|
static const uint8_t monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // API starts months from 1, this array starts from 0
|
||||||
|
|
||||||
|
time_t now() {
|
||||||
|
// calculate number of seconds passed since last call to now()
|
||||||
|
while (millis() - prevMillis >= 1000) {
|
||||||
|
// millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference
|
||||||
|
sysTime++;
|
||||||
|
prevMillis += 1000;
|
||||||
|
}
|
||||||
|
if (nextSyncTime <= sysTime) {
|
||||||
|
if (getTimePtr != 0) {
|
||||||
|
time_t t = getTimePtr();
|
||||||
|
if (t != 0) {
|
||||||
|
setTime(t);
|
||||||
|
} else {
|
||||||
|
nextSyncTime = sysTime + syncInterval;
|
||||||
|
Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (time_t)sysTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// indicates if time has been set and recently synchronized
|
||||||
|
timeStatus_t timeStatus() {
|
||||||
|
now(); // required to actually update the status
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSyncProvider(getExternalTime getTimeFunction) {
|
||||||
|
getTimePtr = getTimeFunction;
|
||||||
|
nextSyncTime = sysTime;
|
||||||
|
now(); // this will sync the clock
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSyncInterval(time_t interval) { // set the number of seconds between re-sync
|
||||||
|
syncInterval = (uint32_t)interval;
|
||||||
|
nextSyncTime = sysTime + syncInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void breakTime(time_t timeInput, tmElements_t & tm) {
|
||||||
|
// break the given time_t into time components
|
||||||
|
// this is a more compact version of the C library localtime function
|
||||||
|
// note that year is offset from 1970 !!!
|
||||||
|
|
||||||
|
uint8_t year;
|
||||||
|
uint8_t month, monthLength;
|
||||||
|
uint32_t time;
|
||||||
|
unsigned long days;
|
||||||
|
|
||||||
|
time = (uint32_t)timeInput;
|
||||||
|
tm.Second = time % 60;
|
||||||
|
time /= 60; // now it is minutes
|
||||||
|
tm.Minute = time % 60;
|
||||||
|
time /= 60; // now it is hours
|
||||||
|
tm.Hour = time % 24;
|
||||||
|
time /= 24; // now it is days
|
||||||
|
tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1
|
||||||
|
|
||||||
|
year = 0;
|
||||||
|
days = 0;
|
||||||
|
while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
|
||||||
|
year++;
|
||||||
|
}
|
||||||
|
tm.Year = year; // year is offset from 1970
|
||||||
|
|
||||||
|
days -= LEAP_YEAR(year) ? 366 : 365;
|
||||||
|
time -= days; // now it is days in this year, starting at 0
|
||||||
|
|
||||||
|
days = 0;
|
||||||
|
month = 0;
|
||||||
|
monthLength = 0;
|
||||||
|
for (month = 0; month < 12; month++) {
|
||||||
|
if (month == 1) { // february
|
||||||
|
if (LEAP_YEAR(year)) {
|
||||||
|
monthLength = 29;
|
||||||
|
} else {
|
||||||
|
monthLength = 28;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
monthLength = monthDays[month];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time >= monthLength) {
|
||||||
|
time -= monthLength;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tm.Month = month + 1; // jan is month 1
|
||||||
|
tm.Day = time + 1; // day of month
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t makeTime(const tmElements_t & tm) {
|
||||||
|
// assemble time elements into time_t
|
||||||
|
// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
|
||||||
|
// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
|
||||||
|
|
||||||
|
int i;
|
||||||
|
uint32_t seconds;
|
||||||
|
|
||||||
|
// seconds from 1970 till 1 jan 00:00:00 of the given year
|
||||||
|
seconds = tm.Year * (SECS_PER_DAY * 365);
|
||||||
|
for (i = 0; i < tm.Year; i++) {
|
||||||
|
if (LEAP_YEAR(i)) {
|
||||||
|
seconds += SECS_PER_DAY; // add extra days for leap years
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add days for this year, months start from 1
|
||||||
|
for (i = 1; i < tm.Month; i++) {
|
||||||
|
if ((i == 2) && LEAP_YEAR(tm.Year)) {
|
||||||
|
seconds += SECS_PER_DAY * 29;
|
||||||
|
} else {
|
||||||
|
seconds += SECS_PER_DAY * monthDays[i - 1]; //monthDay array starts from 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seconds += (tm.Day - 1) * SECS_PER_DAY;
|
||||||
|
seconds += tm.Hour * SECS_PER_HOUR;
|
||||||
|
seconds += tm.Minute * SECS_PER_MIN;
|
||||||
|
seconds += tm.Second;
|
||||||
|
return (time_t)seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshCache(time_t t) {
|
||||||
|
if (t != cacheTime) {
|
||||||
|
breakTime(t, tm);
|
||||||
|
cacheTime = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int day(time_t t) { // the day for the given time (0-6)
|
||||||
|
refreshCache(t);
|
||||||
|
return tm.Day;
|
||||||
|
}
|
||||||
|
|
||||||
|
int month(time_t t) { // the month for the given time
|
||||||
|
refreshCache(t);
|
||||||
|
return tm.Month;
|
||||||
|
}
|
||||||
|
|
||||||
|
int second(time_t t) { // the second for the given time
|
||||||
|
refreshCache(t);
|
||||||
|
return tm.Second;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minute(time_t t) { // the minute for the given time
|
||||||
|
refreshCache(t);
|
||||||
|
return tm.Minute;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hour(time_t t) { // the hour for the given time
|
||||||
|
refreshCache(t);
|
||||||
|
return tm.Hour;
|
||||||
|
}
|
||||||
|
|
||||||
|
int weekday(time_t t) {
|
||||||
|
refreshCache(t);
|
||||||
|
return tm.Wday;
|
||||||
|
}
|
||||||
|
|
||||||
|
int year(time_t t) { // the year for the given time
|
||||||
|
refreshCache(t);
|
||||||
|
return tmYearToCalendar(tm.Year);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTime(time_t t) {
|
||||||
|
sysTime = (uint32_t)t;
|
||||||
|
nextSyncTime = (uint32_t)t + syncInterval;
|
||||||
|
Status = timeSet;
|
||||||
|
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
|
||||||
|
}
|
||||||
|
|
||||||
49
src/TimeLib.h
Normal file
49
src/TimeLib.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef _Time_h
|
||||||
|
#define _Time_h
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define SECS_PER_MIN ((time_t)(60UL))
|
||||||
|
#define SECS_PER_HOUR ((time_t)(3600UL))
|
||||||
|
#define SECS_PER_DAY ((time_t)(SECS_PER_HOUR * 24UL))
|
||||||
|
#define tmYearToCalendar(Y) ((Y) + 1970) // full four digit year
|
||||||
|
|
||||||
|
// This ugly hack allows us to define C++ overloaded functions, when included
|
||||||
|
// from within an extern "C", as newlib's sys/stat.h does. Actually it is
|
||||||
|
// intended to include "time.h" from the C library (on ARM, but AVR does not
|
||||||
|
// have that file at all). On Mac and Windows, the compiler will find this
|
||||||
|
// "Time.h" instead of the C library "time.h", so we may cause other weird
|
||||||
|
// and unpredictable effects by conflicting with the C library header "time.h",
|
||||||
|
// but at least this hack lets us define C++ functions as intended. Hopefully
|
||||||
|
// nothing too terrible will result from overriding the C library header?!
|
||||||
|
extern "C++" {
|
||||||
|
typedef enum { timeNotSet, timeNeedsSync, timeSet } timeStatus_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t Second;
|
||||||
|
uint8_t Minute;
|
||||||
|
uint8_t Hour;
|
||||||
|
uint8_t Wday; // day of week, sunday is day 1
|
||||||
|
uint8_t Day;
|
||||||
|
uint8_t Month;
|
||||||
|
uint8_t Year; // offset from 1970;
|
||||||
|
} tmElements_t, TimeElements, *tmElementsPtr_t;
|
||||||
|
|
||||||
|
typedef time_t (*getExternalTime)();
|
||||||
|
|
||||||
|
time_t now(); // return the current time as seconds since Jan 1 1970
|
||||||
|
void setTime(time_t t);
|
||||||
|
timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
|
||||||
|
void setSyncProvider(getExternalTime getTimeFunction); // identify the external time provider
|
||||||
|
void setSyncInterval(time_t interval); // set the number of seconds between re-sync
|
||||||
|
time_t makeTime(const tmElements_t & tm); // convert time elements into time_t
|
||||||
|
|
||||||
|
int hour(time_t t); // the hour for the given time
|
||||||
|
int minute(time_t t); // the minute for the given time
|
||||||
|
int second(time_t t); // the second for the given time
|
||||||
|
int day(time_t t); // the day for the given time
|
||||||
|
int month(time_t t); // the month for the given time
|
||||||
|
int weekday(time_t t); // the weekday for the given time
|
||||||
|
int year(time_t t); // the year for the given time
|
||||||
|
}
|
||||||
|
#endif
|
||||||
239
src/custom.htm
Normal file
239
src/custom.htm
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
<div id="customcontent">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<legend>Custom Settings</legend>
|
||||||
|
<h6 class="text-muted">Please refer to the Help for configuration options.</h6>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">LED<i style="margin-left: 10px;"
|
||||||
|
class="glyphicon glyphicon-exclamation-sign text-danger" aria-hidden="true" data-toggle="popover"
|
||||||
|
data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Please choose if you want to enable the LED"></i></label>
|
||||||
|
<div class="col-xs-9">
|
||||||
|
<form>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="1" name="led">Enabled</label>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="0" name="led" checked>Disabled</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">LED pin<i style="margin-left: 10px;"
|
||||||
|
class="glyphicon glyphicon-exclamation-sign text-danger" aria-hidden="true" data-toggle="popover"
|
||||||
|
data-trigger="hover" data-placement="right" data-content="Choose "LED pin" pin"></i></label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<select class="form-control input-sm" id="led_gpio">
|
||||||
|
<option value="0">GPIO-0</option>
|
||||||
|
<option selected="selected" value="2">GPIO-2 (LED_BUILTIN)</option>
|
||||||
|
<option value="4">GPIO-4</option>
|
||||||
|
<option value="5">GPIO-5</option>
|
||||||
|
<option value="12">GPIO-12</option>
|
||||||
|
<option value="14">GPIO-14</option>
|
||||||
|
<option value="16">GPIO-16</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Dallas parasite<i style="margin-left: 10px;"
|
||||||
|
class="glyphicon glyphicon-exclamation-sign text-danger" aria-hidden="true" data-toggle="popover"
|
||||||
|
data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Enable if Dallas sensors powered via parasite"></i></label>
|
||||||
|
<div class="col-xs-9">
|
||||||
|
<form>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="1" name="dallas_parasite">Enabled</label>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="0" name="dallas_parasite" checked>Disabled</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Dallas pin<i style="margin-left: 10px;"
|
||||||
|
class="glyphicon glyphicon-exclamation-sign text-danger" aria-hidden="true" data-toggle="popover"
|
||||||
|
data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Choose "Dallas pin" pin."></i></label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<select class="form-control input-sm" id="dallas_gpio">
|
||||||
|
<option value="0">GPIO-0</option>
|
||||||
|
<option value="4">GPIO-4</option>
|
||||||
|
<option value="5">GPIO-5</option>
|
||||||
|
<option value="12">GPIO-12</option>
|
||||||
|
<option selected="selected" value="14">GPIO-14</option>
|
||||||
|
<option value="16">GPIO-16</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Listen mode<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Listen mode disables Tx"></i></label>
|
||||||
|
<div class="col-xs-9">
|
||||||
|
<form>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="1" name="listen_mode">Enabled</label>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="0" name="listen_mode" checked>Disabled</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Shower timer<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Monitors and alerts on shower duration"></i></label>
|
||||||
|
<div class="col-xs-9">
|
||||||
|
<form>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="1" name="shower_timer">Enabled</label>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="0" name="shower_timer" checked>Disabled</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Shower alert<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Sends blasts of cold water after a fixed shower duration"></i></label>
|
||||||
|
<div class="col-xs-9">
|
||||||
|
<form>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="1" name="shower_alert">Enabled</label>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="0" name="shower_alert" checked>Disabled</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Publish time<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Publish Time in seconds"></i></label>
|
||||||
|
<span class="col-xs-9">
|
||||||
|
<input class="form-control input-sm" placeholder="120" value="" style="display:inline;max-width:185px"
|
||||||
|
id="publish_time" type="text">
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Heating circuit<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Select main heating circuit to use"></i></label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<select class="form-control input-sm" id="heating_circuit">
|
||||||
|
<option selected="selected" value="1">HC1 (default)</option>
|
||||||
|
<option value="2">HC2</option>
|
||||||
|
<option value="3">HC3</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">TX mode<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="TX mode settings for various EMS brands"></i></label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<select class="form-control input-sm" id="tx_mode">
|
||||||
|
<option selected="selected" value="0">0=EMS 1.0 (default)</option>
|
||||||
|
<option value="1">1=EMS+</option>
|
||||||
|
<option value="2">2=Generic (experimental!)</option>
|
||||||
|
<option value="3">3=Junkers</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<div class="row form-group">
|
||||||
|
<div class="col-xs-9 col-md-8">
|
||||||
|
<button onclick="savecustom()" class="btn btn-primary btn-sm pull-right">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h6 class="text-muted">Note: any setting marked with a <span
|
||||||
|
class="glyphicon glyphicon-exclamation-sign text-danger"></span> requires a restart after saving.</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="custom_statuscontent">
|
||||||
|
<br>
|
||||||
|
<div class="row text-center">
|
||||||
|
<div class="col-md-8 col-md-offset-2">
|
||||||
|
<h1>Dashboard</h1>
|
||||||
|
<p>Real-time values from the EMS-ESP device are shown here.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8 col-md-offset-2">
|
||||||
|
<div class="panel panel-default table-responsive">
|
||||||
|
<table class="table table-hover table-striped table-condensed">
|
||||||
|
<caption>EMS Bus stats</caption>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<h3><span id="msg"></span></h3>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Detected Devices:</th>
|
||||||
|
<td>
|
||||||
|
<ul class="list-group">
|
||||||
|
<div id="devices"></div>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-info table-responsive" id="boiler_show">
|
||||||
|
<div class="panel-heading">Boiler</div>
|
||||||
|
<table class="table table-hover table-bordered table-condensed">
|
||||||
|
<tr>
|
||||||
|
<th>Model:</th>
|
||||||
|
<td colspan="3" id="bm"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Hot Tap Water:</th>
|
||||||
|
<td id="b1"></td>
|
||||||
|
<th>Central Heating:</th>
|
||||||
|
<td id="b2"></td>
|
||||||
|
</tr>
|
||||||
|
<th>Selected Flow Temperature:</th>
|
||||||
|
<td id="b3"></td>
|
||||||
|
<th>Boiler Temperature:</th>
|
||||||
|
<td id="b4"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-success table-responsive" id="thermostat_show">
|
||||||
|
<div class="panel-heading">Thermostat</div>
|
||||||
|
<table class="table table-hover table-bordered table-condensed">
|
||||||
|
<tr>
|
||||||
|
<th>Model:</th>
|
||||||
|
<td colspan="3" id="tm"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Setpoint Temperature:</th>
|
||||||
|
<td id="ts"></td>
|
||||||
|
<th>Current Temperature:</th>
|
||||||
|
<td id="tc"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Mode:</th>
|
||||||
|
<td colspan="3" id="tmode"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<div class="row form-group">
|
||||||
|
<div class="col-xs-9 col-md-8">
|
||||||
|
<button onclick="refreshEMS()" class="btn btn-primary btn-sm pull-center">Refresh...</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
141
src/custom.js
Normal file
141
src/custom.js
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
var custom_config = {
|
||||||
|
"command": "custom_configfile",
|
||||||
|
"settings": {
|
||||||
|
"led": true,
|
||||||
|
"led_gpio": 2,
|
||||||
|
"dallas_gpio": 14,
|
||||||
|
"dallas_parasite": false,
|
||||||
|
"listen_mode": false,
|
||||||
|
"shower_timer": false,
|
||||||
|
"shower_alert": false,
|
||||||
|
"publish_time": 120,
|
||||||
|
"heating_circuit": 1,
|
||||||
|
"tx_mode": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function custom_commit() {
|
||||||
|
websock.send(JSON.stringify(custom_config));
|
||||||
|
}
|
||||||
|
|
||||||
|
function listcustom() {
|
||||||
|
|
||||||
|
document.getElementById("led_gpio").value = custom_config.settings.led_gpio;
|
||||||
|
document.getElementById("dallas_gpio").value = custom_config.settings.dallas_gpio;
|
||||||
|
document.getElementById("publish_time").value = custom_config.settings.publish_time;
|
||||||
|
document.getElementById("heating_circuit").value = custom_config.settings.heating_circuit;
|
||||||
|
document.getElementById("tx_mode").value = custom_config.settings.tx_mode;
|
||||||
|
|
||||||
|
if (custom_config.settings.led) {
|
||||||
|
$("input[name=\"led\"][value=\"1\"]").prop("checked", true);
|
||||||
|
}
|
||||||
|
if (custom_config.settings.dallas_parasite) {
|
||||||
|
$("input[name=\"dallas_parasite\"][value=\"1\"]").prop("checked", true);
|
||||||
|
}
|
||||||
|
if (custom_config.settings.listen_mode) {
|
||||||
|
$("input[name=\"listen_mode\"][value=\"1\"]").prop("checked", true);
|
||||||
|
}
|
||||||
|
if (custom_config.settings.shower_timer) {
|
||||||
|
$("input[name=\"shower_timer\"][value=\"1\"]").prop("checked", true);
|
||||||
|
}
|
||||||
|
if (custom_config.settings.shower_alert) {
|
||||||
|
$("input[name=\"shower_alert\"][value=\"1\"]").prop("checked", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function savecustom() {
|
||||||
|
custom_config.settings.led_gpio = parseInt(document.getElementById("led_gpio").value);
|
||||||
|
custom_config.settings.dallas_gpio = parseInt(document.getElementById("dallas_gpio").value);
|
||||||
|
|
||||||
|
custom_config.settings.dallas_parasite = false;
|
||||||
|
if (parseInt($("input[name=\"dallas_parasite\"]:checked").val()) === 1) {
|
||||||
|
custom_config.settings.dallas_parasite = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_config.settings.listen_mode = false;
|
||||||
|
if (parseInt($("input[name=\"listen_mode\"]:checked").val()) === 1) {
|
||||||
|
custom_config.settings.listen_mode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_config.settings.shower_timer = false;
|
||||||
|
if (parseInt($("input[name=\"shower_timer\"]:checked").val()) === 1) {
|
||||||
|
custom_config.settings.shower_timer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_config.settings.shower_alert = false;
|
||||||
|
if (parseInt($("input[name=\"shower_alert\"]:checked").val()) === 1) {
|
||||||
|
custom_config.settings.shower_alert = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_config.settings.led = false;
|
||||||
|
if (parseInt($("input[name=\"led\"]:checked").val()) === 1) {
|
||||||
|
custom_config.settings.led = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_config.settings.publish_time = parseInt(document.getElementById("publish_time").value);
|
||||||
|
custom_config.settings.heating_circuit = parseInt(document.getElementById("heating_circuit").value);
|
||||||
|
custom_config.settings.tx_mode = parseInt(document.getElementById("tx_mode").value);
|
||||||
|
|
||||||
|
custom_uncommited();
|
||||||
|
}
|
||||||
|
|
||||||
|
function listCustomStats() {
|
||||||
|
document.getElementById("msg").innerHTML = ajaxobj.emsbus.msg;
|
||||||
|
if (ajaxobj.emsbus.ok) {
|
||||||
|
document.getElementById("msg").className = "label label-success";
|
||||||
|
} else {
|
||||||
|
document.getElementById("msg").className = "label label-danger";
|
||||||
|
document.getElementById("thermostat_show").style.display = "none";
|
||||||
|
document.getElementById("boiler_show").style.display = "none";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var list = document.getElementById("devices");
|
||||||
|
var obj = ajaxobj.emsbus.devices;
|
||||||
|
for (var i = 0; i < obj.length; i++) {
|
||||||
|
var l = document.createElement("li");
|
||||||
|
var type = obj[i].type;
|
||||||
|
if (type == 1) {
|
||||||
|
var color = "info";
|
||||||
|
} else if (type == 2) {
|
||||||
|
var color = "success";
|
||||||
|
} else if (type == 3) {
|
||||||
|
var color = "warning";
|
||||||
|
} else if (type == 4) {
|
||||||
|
var color = "danger";
|
||||||
|
} else {
|
||||||
|
var color = "";
|
||||||
|
}
|
||||||
|
l.innerHTML = "Model:" + obj[i].model + ", Version:" + obj[i].version + ", ProductID:" + obj[i].productid + ", DeviceID:" + obj[i].deviceid;
|
||||||
|
l.className = "list-group-item list-group-item-" + color;
|
||||||
|
list.appendChild(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ajaxobj.boiler.ok) {
|
||||||
|
document.getElementById("boiler_show").style.display = "block";
|
||||||
|
|
||||||
|
document.getElementById("bm").innerHTML = ajaxobj.boiler.bm;
|
||||||
|
document.getElementById("b1").innerHTML = ajaxobj.boiler.b1;
|
||||||
|
document.getElementById("b2").innerHTML = ajaxobj.boiler.b2;
|
||||||
|
document.getElementById("b3").innerHTML = ajaxobj.boiler.b3 + " ℃";
|
||||||
|
document.getElementById("b4").innerHTML = ajaxobj.boiler.b4 + " ℃";
|
||||||
|
} else {
|
||||||
|
document.getElementById("boiler_show").style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ajaxobj.thermostat.ok) {
|
||||||
|
document.getElementById("thermostat_show").style.display = "block";
|
||||||
|
|
||||||
|
document.getElementById("tm").innerHTML = ajaxobj.thermostat.tm;
|
||||||
|
document.getElementById("ts").innerHTML = ajaxobj.thermostat.ts + " ℃";
|
||||||
|
document.getElementById("tc").innerHTML = ajaxobj.thermostat.tc + " ℃";
|
||||||
|
document.getElementById("tmode").innerHTML = ajaxobj.thermostat.tmode;
|
||||||
|
} else {
|
||||||
|
document.getElementById("thermostat_show").style.display = "none";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
276
src/ems-esp.cpp
276
src/ems-esp.cpp
@@ -8,19 +8,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// local libraries
|
// local libraries
|
||||||
#include "ds18.h"
|
|
||||||
#include "ems.h"
|
#include "ems.h"
|
||||||
#include "ems_devices.h"
|
#include "ems_devices.h"
|
||||||
#include "emsuart.h"
|
#include "emsuart.h"
|
||||||
#include "my_config.h"
|
#include "my_config.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include <MyESP.h>
|
||||||
|
|
||||||
// Dallas external temp sensors
|
// Dallas external temp sensors
|
||||||
|
#include "ds18.h"
|
||||||
DS18 ds18;
|
DS18 ds18;
|
||||||
|
|
||||||
// shared libraries
|
|
||||||
#include <MyESP.h>
|
|
||||||
|
|
||||||
// public libraries
|
// public libraries
|
||||||
#include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson
|
#include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson
|
||||||
#include <CRC32.h> // https://github.com/bakercp/CRC32
|
#include <CRC32.h> // https://github.com/bakercp/CRC32
|
||||||
@@ -32,7 +30,7 @@ DS18 ds18;
|
|||||||
#define myDebug_P(...) myESP.myDebug_P(__VA_ARGS__)
|
#define myDebug_P(...) myESP.myDebug_P(__VA_ARGS__)
|
||||||
|
|
||||||
// set to value >0 if the ESP is overheating or there are timing issues. Recommend a value of 1.
|
// set to value >0 if the ESP is overheating or there are timing issues. Recommend a value of 1.
|
||||||
#define EMSESP_DELAY 1 // initially set to 0 for no delay
|
#define EMSESP_DELAY 1 // initially set to 0 for no delay // TODO change delay to 0?
|
||||||
|
|
||||||
#define DEFAULT_HEATINGCIRCUIT 1 // default to HC1 for thermostats that support multiple heating circuits like the RC35
|
#define DEFAULT_HEATINGCIRCUIT 1 // default to HC1 for thermostats that support multiple heating circuits like the RC35
|
||||||
|
|
||||||
@@ -69,6 +67,8 @@ Ticker showerColdShotStopTimer;
|
|||||||
#define SHOWER_COLDSHOT_DURATION 10 // in seconds. 10 seconds for cold water before turning back hot water
|
#define SHOWER_COLDSHOT_DURATION 10 // in seconds. 10 seconds for cold water before turning back hot water
|
||||||
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
||||||
|
|
||||||
|
#define MQTT_MAX_SIZE 700 // max size of a JSON object. See https://arduinojson.org/v6/assistant/
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t timestamp; // for internal timings, via millis()
|
uint32_t timestamp; // for internal timings, via millis()
|
||||||
uint8_t dallas_sensors; // count of dallas sensors
|
uint8_t dallas_sensors; // count of dallas sensors
|
||||||
@@ -98,9 +98,7 @@ static const command_t project_cmds[] PROGMEM = {
|
|||||||
{true, "led <on | off>", "toggle status LED on/off"},
|
{true, "led <on | off>", "toggle status LED on/off"},
|
||||||
{true, "led_gpio <gpio>", "set the LED pin. Default is the onboard LED 2. For external D1 use 5"},
|
{true, "led_gpio <gpio>", "set the LED pin. Default is the onboard LED 2. For external D1 use 5"},
|
||||||
{true, "dallas_gpio <gpio>", "set the external Dallas temperature sensors pin. Default is 14 for D5"},
|
{true, "dallas_gpio <gpio>", "set the external Dallas temperature sensors pin. Default is 14 for D5"},
|
||||||
{true, "dallas_parasite <on | off>", "set to on if powering Dallas sesnsors via parasite power"},
|
{true, "dallas_parasite <on | off>", "set to on if powering Dallas sensors via parasite power"},
|
||||||
{true, "thermostat_type <device ID>", "set the thermostat type ID (e.g. 10 for 0x10)"},
|
|
||||||
{true, "boiler_type <device ID>", "set the boiler type ID (e.g. 8 for 0x08)"},
|
|
||||||
{true, "listen_mode <on | off>", "when set to on all automatic Tx are disabled"},
|
{true, "listen_mode <on | off>", "when set to on all automatic Tx are disabled"},
|
||||||
{true, "shower_timer <on | off>", "send MQTT notification on all shower durations"},
|
{true, "shower_timer <on | off>", "send MQTT notification on all shower durations"},
|
||||||
{true, "shower_alert <on | off>", "stop hot water to send 3 cold burst warnings after max shower time is exceeded"},
|
{true, "shower_alert <on | off>", "stop hot water to send 3 cold burst warnings after max shower time is exceeded"},
|
||||||
@@ -550,7 +548,7 @@ void showInfo() {
|
|||||||
if (ems_getSolarModuleEnabled()) {
|
if (ems_getSolarModuleEnabled()) {
|
||||||
myDebug_P(PSTR("")); // newline
|
myDebug_P(PSTR("")); // newline
|
||||||
myDebug_P(PSTR("%sSolar Module stats:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
myDebug_P(PSTR("%sSolar Module stats:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||||
myDebug_P(PSTR(" Solar Module: %s"), ems_getSolarModuleDescription(buffer_type));
|
myDebug_P(PSTR(" Solar module: %s"), ems_getSolarModuleDescription(buffer_type));
|
||||||
_renderShortValue("Collector temperature", "C", EMS_SolarModule.collectorTemp);
|
_renderShortValue("Collector temperature", "C", EMS_SolarModule.collectorTemp);
|
||||||
_renderShortValue("Bottom temperature", "C", EMS_SolarModule.bottomTemp);
|
_renderShortValue("Bottom temperature", "C", EMS_SolarModule.bottomTemp);
|
||||||
_renderIntValue("Pump modulation", "%", EMS_SolarModule.pumpModulation);
|
_renderIntValue("Pump modulation", "%", EMS_SolarModule.pumpModulation);
|
||||||
@@ -561,16 +559,16 @@ void showInfo() {
|
|||||||
(EMS_SolarModule.pumpWorkMin % 1440) / 60,
|
(EMS_SolarModule.pumpWorkMin % 1440) / 60,
|
||||||
EMS_SolarModule.pumpWorkMin % 60);
|
EMS_SolarModule.pumpWorkMin % 60);
|
||||||
}
|
}
|
||||||
_renderUShortValue("Energy Last Hour", "Wh", EMS_SolarModule.EnergyLastHour, 1); // *10
|
_renderUShortValue("Energy last hour", "Wh", EMS_SolarModule.EnergyLastHour, 1); // *10
|
||||||
_renderUShortValue("Energy Today", "Wh", EMS_SolarModule.EnergyToday, 0);
|
_renderUShortValue("Energy today", "Wh", EMS_SolarModule.EnergyToday, 0);
|
||||||
_renderUShortValue("Energy Total", "kWH", EMS_SolarModule.EnergyTotal, 1); // *10
|
_renderUShortValue("Energy total", "kWH", EMS_SolarModule.EnergyTotal, 1); // *10
|
||||||
}
|
}
|
||||||
|
|
||||||
// For HeatPumps
|
// For HeatPumps
|
||||||
if (ems_getHeatPumpEnabled()) {
|
if (ems_getHeatPumpEnabled()) {
|
||||||
myDebug_P(PSTR("")); // newline
|
myDebug_P(PSTR("")); // newline
|
||||||
myDebug_P(PSTR("%sHeat Pump stats:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
myDebug_P(PSTR("%sHeat Pump stats:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||||
myDebug_P(PSTR(" Solar Module: %s"), ems_getHeatPumpDescription(buffer_type));
|
myDebug_P(PSTR(" Heat Pump module: %s"), ems_getHeatPumpDescription(buffer_type));
|
||||||
_renderIntValue("Pump modulation", "%", EMS_HeatPump.HPModulation);
|
_renderIntValue("Pump modulation", "%", EMS_HeatPump.HPModulation);
|
||||||
_renderIntValue("Pump speed", "%", EMS_HeatPump.HPSpeed);
|
_renderIntValue("Pump speed", "%", EMS_HeatPump.HPSpeed);
|
||||||
}
|
}
|
||||||
@@ -579,7 +577,7 @@ void showInfo() {
|
|||||||
if (ems_getThermostatEnabled()) {
|
if (ems_getThermostatEnabled()) {
|
||||||
myDebug_P(PSTR("")); // newline
|
myDebug_P(PSTR("")); // newline
|
||||||
myDebug_P(PSTR("%sThermostat stats:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
myDebug_P(PSTR("%sThermostat stats:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||||
myDebug_P(PSTR(" Thermostat: %s"), ems_getThermostatDescription(buffer_type));
|
myDebug_P(PSTR(" Thermostat: %s"), ems_getThermostatDescription(buffer_type, false));
|
||||||
|
|
||||||
// Render Current & Setpoint Room Temperature
|
// Render Current & Setpoint Room Temperature
|
||||||
if (ems_getThermostatModel() == EMS_MODEL_EASY) {
|
if (ems_getThermostatModel() == EMS_MODEL_EASY) {
|
||||||
@@ -683,8 +681,6 @@ void publishSensorValues() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// send values via MQTT
|
// send values via MQTT
|
||||||
// a json object is created for the boiler and one for the thermostat
|
// a json object is created for the boiler and one for the thermostat
|
||||||
// CRC check is done to see if there are changes in the values since the last send to avoid too much wifi traffic
|
// CRC check is done to see if there are changes in the values since the last send to avoid too much wifi traffic
|
||||||
@@ -1131,44 +1127,42 @@ void runUnitTest(uint8_t test_num) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// callback for loading/saving settings to the file system (SPIFFS)
|
// callback for loading/saving settings to the file system (SPIFFS)
|
||||||
bool FSCallback(MYESP_FSACTION action, const JsonObject json) {
|
bool LoadSaveCallback(MYESP_FSACTION action, JsonObject json) {
|
||||||
if (action == MYESP_FSACTION_LOAD) {
|
if (action == MYESP_FSACTION_LOAD) {
|
||||||
EMSESP_Status.led = json["led"];
|
const JsonObject & settings = json["settings"];
|
||||||
EMSESP_Status.led_gpio = json["led_gpio"] | EMSESP_LED_GPIO;
|
|
||||||
EMSESP_Status.dallas_gpio = json["dallas_gpio"] | EMSESP_DALLAS_GPIO;
|
|
||||||
EMSESP_Status.dallas_parasite = json["dallas_parasite"] | EMSESP_DALLAS_PARASITE;
|
|
||||||
|
|
||||||
EMS_Thermostat.device_id = json["thermostat_type"] | EMSESP_THERMOSTAT_TYPE;
|
EMSESP_Status.led = settings["led"];
|
||||||
EMS_Boiler.device_id = json["boiler_type"] | EMSESP_BOILER_TYPE;
|
EMSESP_Status.led_gpio = settings["led_gpio"] | EMSESP_LED_GPIO;
|
||||||
|
EMSESP_Status.dallas_gpio = settings["dallas_gpio"] | EMSESP_DALLAS_GPIO;
|
||||||
|
EMSESP_Status.dallas_parasite = settings["dallas_parasite"] | EMSESP_DALLAS_PARASITE;
|
||||||
|
EMSESP_Status.shower_timer = settings["shower_timer"];
|
||||||
|
EMSESP_Status.shower_alert = settings["shower_alert"];
|
||||||
|
EMSESP_Status.publish_time = settings["publish_time"] | DEFAULT_PUBLISHTIME;
|
||||||
|
|
||||||
EMSESP_Status.shower_timer = json["shower_timer"];
|
ems_setTxMode(settings["tx_mode"]);
|
||||||
EMSESP_Status.shower_alert = json["shower_alert"];
|
|
||||||
EMSESP_Status.publish_time = json["publish_time"] | DEFAULT_PUBLISHTIME;
|
|
||||||
|
|
||||||
ems_setTxMode(json["tx_mode"]);
|
EMSESP_Status.listen_mode = settings["listen_mode"];
|
||||||
|
|
||||||
EMSESP_Status.listen_mode = json["listen_mode"];
|
|
||||||
ems_setTxDisabled(EMSESP_Status.listen_mode);
|
ems_setTxDisabled(EMSESP_Status.listen_mode);
|
||||||
|
|
||||||
EMSESP_Status.heating_circuit = json["heating_circuit"] | DEFAULT_HEATINGCIRCUIT;
|
EMSESP_Status.heating_circuit = settings["heating_circuit"] | DEFAULT_HEATINGCIRCUIT;
|
||||||
ems_setThermostatHC(EMSESP_Status.heating_circuit);
|
ems_setThermostatHC(EMSESP_Status.heating_circuit);
|
||||||
|
|
||||||
return true; // return false if some settings are missing and we need to rebuild the file
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action == MYESP_FSACTION_SAVE) {
|
if (action == MYESP_FSACTION_SAVE) {
|
||||||
json["thermostat_type"] = EMS_Thermostat.device_id;
|
JsonObject settings = json.createNestedObject("settings");
|
||||||
json["boiler_type"] = EMS_Boiler.device_id;
|
|
||||||
json["led"] = EMSESP_Status.led;
|
settings["led"] = EMSESP_Status.led;
|
||||||
json["led_gpio"] = EMSESP_Status.led_gpio;
|
settings["led_gpio"] = EMSESP_Status.led_gpio;
|
||||||
json["dallas_gpio"] = EMSESP_Status.dallas_gpio;
|
settings["dallas_gpio"] = EMSESP_Status.dallas_gpio;
|
||||||
json["dallas_parasite"] = EMSESP_Status.dallas_parasite;
|
settings["dallas_parasite"] = EMSESP_Status.dallas_parasite;
|
||||||
json["listen_mode"] = EMSESP_Status.listen_mode;
|
settings["listen_mode"] = EMSESP_Status.listen_mode;
|
||||||
json["shower_timer"] = EMSESP_Status.shower_timer;
|
settings["shower_timer"] = EMSESP_Status.shower_timer;
|
||||||
json["shower_alert"] = EMSESP_Status.shower_alert;
|
settings["shower_alert"] = EMSESP_Status.shower_alert;
|
||||||
json["publish_time"] = EMSESP_Status.publish_time;
|
settings["publish_time"] = EMSESP_Status.publish_time;
|
||||||
json["heating_circuit"] = EMSESP_Status.heating_circuit;
|
settings["heating_circuit"] = EMSESP_Status.heating_circuit;
|
||||||
json["tx_mode"] = ems_getTxMode();
|
settings["tx_mode"] = ems_getTxMode();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1179,7 +1173,7 @@ bool FSCallback(MYESP_FSACTION action, const JsonObject json) {
|
|||||||
// callback for custom settings when showing Stored Settings with the 'set' command
|
// callback for custom settings when showing Stored Settings with the 'set' command
|
||||||
// wc is number of arguments after the 'set' command
|
// wc is number of arguments after the 'set' command
|
||||||
// returns true if the setting was recognized and changed and should be saved back to SPIFFs
|
// returns true if the setting was recognized and changed and should be saved back to SPIFFs
|
||||||
bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, const char * value) {
|
bool SetListCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, const char * value) {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
if (action == MYESP_FSACTION_SET) {
|
if (action == MYESP_FSACTION_SET) {
|
||||||
@@ -1243,18 +1237,6 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// thermostat_type
|
|
||||||
if (strcmp(setting, "thermostat_type") == 0) {
|
|
||||||
EMS_Thermostat.device_id = ((wc == 2) ? (uint8_t)strtol(value, 0, 16) : EMS_ID_NONE);
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// boiler_type
|
|
||||||
if (strcmp(setting, "boiler_type") == 0) {
|
|
||||||
EMS_Boiler.device_id = ((wc == 2) ? (uint8_t)strtol(value, 0, 16) : EMS_ID_NONE);
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// shower timer
|
// shower timer
|
||||||
if ((strcmp(setting, "shower_timer") == 0) && (wc == 2)) {
|
if ((strcmp(setting, "shower_timer") == 0) && (wc == 2)) {
|
||||||
if (strcmp(value, "on") == 0) {
|
if (strcmp(value, "on") == 0) {
|
||||||
@@ -1311,21 +1293,7 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c
|
|||||||
myDebug_P(PSTR(" led_gpio=%d"), EMSESP_Status.led_gpio);
|
myDebug_P(PSTR(" led_gpio=%d"), EMSESP_Status.led_gpio);
|
||||||
myDebug_P(PSTR(" dallas_gpio=%d"), EMSESP_Status.dallas_gpio);
|
myDebug_P(PSTR(" dallas_gpio=%d"), EMSESP_Status.dallas_gpio);
|
||||||
myDebug_P(PSTR(" dallas_parasite=%s"), EMSESP_Status.dallas_parasite ? "on" : "off");
|
myDebug_P(PSTR(" dallas_parasite=%s"), EMSESP_Status.dallas_parasite ? "on" : "off");
|
||||||
|
|
||||||
if (EMS_Thermostat.device_id == EMS_ID_NONE) {
|
|
||||||
myDebug_P(PSTR(" thermostat_type=<not set>"));
|
|
||||||
} else {
|
|
||||||
myDebug_P(PSTR(" thermostat_type=%02X"), EMS_Thermostat.device_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
myDebug_P(PSTR(" heating_circuit=%d"), EMSESP_Status.heating_circuit);
|
myDebug_P(PSTR(" heating_circuit=%d"), EMSESP_Status.heating_circuit);
|
||||||
|
|
||||||
if (EMS_Boiler.device_id == EMS_ID_NONE) {
|
|
||||||
myDebug_P(PSTR(" boiler_type=<not set>"));
|
|
||||||
} else {
|
|
||||||
myDebug_P(PSTR(" boiler_type=%02X"), EMS_Boiler.device_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
myDebug_P(PSTR(" listen_mode=%s"), EMSESP_Status.listen_mode ? "on" : "off");
|
myDebug_P(PSTR(" listen_mode=%s"), EMSESP_Status.listen_mode ? "on" : "off");
|
||||||
myDebug_P(PSTR(" shower_timer=%s"), EMSESP_Status.shower_timer ? "on" : "off");
|
myDebug_P(PSTR(" shower_timer=%s"), EMSESP_Status.shower_timer ? "on" : "off");
|
||||||
myDebug_P(PSTR(" shower_alert=%s"), EMSESP_Status.shower_alert ? "on" : "off");
|
myDebug_P(PSTR(" shower_alert=%s"), EMSESP_Status.shower_alert ? "on" : "off");
|
||||||
@@ -1697,58 +1665,110 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// web information for diagnostics
|
|
||||||
void WebCallback(char * body) {
|
|
||||||
strlcpy(body, "<b>EMS stats:</b><br>", MYESP_MAXCHARBUFFER);
|
|
||||||
|
|
||||||
if (ems_getBusConnected()) {
|
|
||||||
char s[10];
|
|
||||||
strlcat(body, "EMS Bus is connected<br>", MYESP_MAXCHARBUFFER);
|
|
||||||
strlcat(body, "Rx: # successful read requests=", MYESP_MAXCHARBUFFER);
|
|
||||||
strlcat(body, itoa(EMS_Sys_Status.emsRxPgks, s, 10), MYESP_MAXCHARBUFFER);
|
|
||||||
strlcat(body, ", # CRC errors=", MYESP_MAXCHARBUFFER);
|
|
||||||
strlcat(body, itoa(EMS_Sys_Status.emxCrcErr, s, 10), MYESP_MAXCHARBUFFER);
|
|
||||||
if (ems_getTxCapable()) {
|
|
||||||
strlcat(body, "<br>Tx: # successful write requests=", MYESP_MAXCHARBUFFER);
|
|
||||||
strlcat(body, itoa(EMS_Sys_Status.emsTxPkgs, s, 10), MYESP_MAXCHARBUFFER);
|
|
||||||
} else {
|
|
||||||
strlcat(body, "<br>Tx: no signal<br><br>", MYESP_MAXCHARBUFFER);
|
|
||||||
}
|
|
||||||
|
|
||||||
// show device list
|
|
||||||
strlcpy(body, "<b>EMS devices found:</b><br>", MYESP_MAXCHARBUFFER);
|
|
||||||
|
|
||||||
char buffer[MYESP_MAXCHARBUFFER] = {0};
|
|
||||||
uint8_t num_devices = ems_printDevices_s(buffer, MYESP_MAXCHARBUFFER);
|
|
||||||
if (num_devices == 0) {
|
|
||||||
strlcat(body, "(any detected and compatible EMS devices will show up here)", MYESP_MAXCHARBUFFER);
|
|
||||||
} else {
|
|
||||||
strlcat(body, buffer, MYESP_MAXCHARBUFFER);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
strlcat(body, "Unable to establish a connection to the EMS Bus.", MYESP_MAXCHARBUFFER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init callback, which is used to set functions and call methods after a wifi connection has been established
|
// Init callback, which is used to set functions and call methods after a wifi connection has been established
|
||||||
void WIFICallback() {
|
void WIFICallback() {
|
||||||
// This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals
|
// This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals
|
||||||
// This is done after we have a WiFi signal to avoid any resource conflicts
|
// This is done after we have a WiFi signal to avoid any resource conflicts
|
||||||
system_uart_swap(); // TODO check
|
// system_uart_swap(); // TODO see if this still blocks the EMS lines
|
||||||
|
}
|
||||||
|
|
||||||
|
// web information for diagnostics
|
||||||
|
void WebCallback(JsonObject root) {
|
||||||
|
JsonObject emsbus = root.createNestedObject("emsbus");
|
||||||
|
|
||||||
/*
|
|
||||||
if (myESP.getUseSerial()) {
|
if (myESP.getUseSerial()) {
|
||||||
myDebug_P(PSTR("Warning! EMS bus communication disabled when Serial mode enabled. Use 'set serial off' to start communication."));
|
emsbus["ok"] = false;
|
||||||
|
emsbus["msg"] = "EMS Bus is disabled when in Serial mode. Check Settings->General Settings";
|
||||||
} else {
|
} else {
|
||||||
emsuart_init();
|
if (ems_getBusConnected()) {
|
||||||
myDebug_P(PSTR("[UART] Opened Rx/Tx connection"));
|
if (ems_getTxCapable()) {
|
||||||
if (!EMSESP_Status.listen_mode) {
|
emsbus["ok"] = true;
|
||||||
// go and find the boiler and thermostat types, if not in listen mode
|
emsbus["msg"] = "EMS Bus Connected, Rx and Tx active";
|
||||||
ems_discoverModels();
|
} else {
|
||||||
|
emsbus["ok"] = false;
|
||||||
|
emsbus["msg"] = "EMS Bus Connected, Tx is failing";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emsbus["ok"] = false;
|
||||||
|
emsbus["msg"] = "EMS Bus is not connected. Check event logs for errors.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
JsonArray list = emsbus.createNestedArray("devices");
|
||||||
|
|
||||||
|
for (std::list<_Generic_Device>::iterator it = Devices.begin(); it != Devices.end(); it++) {
|
||||||
|
JsonObject item = list.createNestedObject();
|
||||||
|
item["type"] = (it)->model_type;
|
||||||
|
item["model"] = (it)->model_string;
|
||||||
|
item["deviceid"] = (it)->device_id;
|
||||||
|
item["version"] = (it)->version;
|
||||||
|
item["productid"] = (it)->product_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject thermostat = root.createNestedObject("thermostat");
|
||||||
|
|
||||||
|
if (ems_getThermostatEnabled()) {
|
||||||
|
thermostat["ok"] = true;
|
||||||
|
|
||||||
|
char buffer[200];
|
||||||
|
thermostat["tm"] = ems_getThermostatDescription(buffer, true);
|
||||||
|
|
||||||
|
// Render Current & Setpoint Room Temperature
|
||||||
|
if (ems_getThermostatModel() == EMS_MODEL_EASY) {
|
||||||
|
if (EMS_Thermostat.setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET)
|
||||||
|
thermostat["ts"] = (double)EMS_Thermostat.setpoint_roomTemp / 100;
|
||||||
|
if (EMS_Thermostat.curr_roomTemp != EMS_VALUE_SHORT_NOTSET)
|
||||||
|
thermostat["tc"] = (double)EMS_Thermostat.curr_roomTemp / 100;
|
||||||
|
} else if ((ems_getThermostatModel() == EMS_MODEL_FR10) || (ems_getThermostatModel() == EMS_MODEL_FW100)) {
|
||||||
|
if (EMS_Thermostat.setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET)
|
||||||
|
thermostat["ts"] = (double)EMS_Thermostat.setpoint_roomTemp / 10;
|
||||||
|
if (EMS_Thermostat.curr_roomTemp != EMS_VALUE_SHORT_NOTSET)
|
||||||
|
thermostat["tc"] = (double)EMS_Thermostat.curr_roomTemp / 10;
|
||||||
|
} else {
|
||||||
|
if (EMS_Thermostat.setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET)
|
||||||
|
thermostat["ts"] = (double)EMS_Thermostat.setpoint_roomTemp / 2;
|
||||||
|
if (EMS_Thermostat.curr_roomTemp != EMS_VALUE_SHORT_NOTSET)
|
||||||
|
thermostat["tc"] = (double)EMS_Thermostat.curr_roomTemp / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render Termostat Mode, if we have a mode
|
||||||
|
uint8_t thermoMode = _getThermostatMode(); // 0xFF=unknown, 0=low, 1=manual, 2=auto, 3=night, 4=day
|
||||||
|
if (thermoMode == 0) {
|
||||||
|
thermostat["tmode"] = "low";
|
||||||
|
} else if (thermoMode == 1) {
|
||||||
|
thermostat["tmode"] = "manual";
|
||||||
|
} else if (thermoMode == 2) {
|
||||||
|
thermostat["tmode"] = "auto";
|
||||||
|
} else if (thermoMode == 3) {
|
||||||
|
thermostat["tmode"] = "night";
|
||||||
|
} else if (thermoMode == 4) {
|
||||||
|
thermostat["tmode"] = "day";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thermostat["ok"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject boiler = root.createNestedObject("boiler");
|
||||||
|
if (ems_getBoilerEnabled()) {
|
||||||
|
boiler["ok"] = true;
|
||||||
|
|
||||||
|
char buffer[200];
|
||||||
|
boiler["bm"] = ems_getBoilerDescription(buffer, true);
|
||||||
|
|
||||||
|
boiler["b1"] = (EMS_Boiler.tapwaterActive ? "running" : "off");
|
||||||
|
boiler["b2"] = (EMS_Boiler.heatingActive ? "active" : "off");
|
||||||
|
|
||||||
|
if (EMS_Boiler.selFlowTemp != EMS_VALUE_INT_NOTSET)
|
||||||
|
boiler["b3"] = EMS_Boiler.selFlowTemp;
|
||||||
|
|
||||||
|
if (EMS_Boiler.boilTemp != EMS_VALUE_USHORT_NOTSET)
|
||||||
|
boiler["b4"] = (double)EMS_Boiler.boilTemp / 10;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
boiler["ok"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializeJsonPretty(root, Serial); // turn on for debugging
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the boiler settings and shower settings
|
// Initialize the boiler settings and shower settings
|
||||||
@@ -1862,25 +1882,14 @@ void setup() {
|
|||||||
|
|
||||||
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if EMS is reachable
|
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if EMS is reachable
|
||||||
|
|
||||||
// set up myESP for Wifi, MQTT, MDNS and Telnet
|
// set up myESP for Wifi, MQTT, MDNS and Telnet callbacks
|
||||||
myESP.setTelnet(TelnetCommandCallback, TelnetCallback); // set up Telnet commands
|
myESP.setTelnet(TelnetCommandCallback, TelnetCallback); // set up Telnet commands
|
||||||
myESP.setWIFI(NULL, NULL, WIFICallback); // empty ssid and password as we take this from the config file
|
myESP.setWIFI(WIFICallback); // wifi callback
|
||||||
|
myESP.setMQTT(MQTTCallback); // MQTT ip, username and password taken from the SPIFFS settings
|
||||||
// MQTT host, username and password taken from the SPIFFS settings
|
myESP.setSettings(LoadSaveCallback, SetListCallback, true); // default is Serial off
|
||||||
myESP.setMQTT(
|
myESP.setWeb(WebCallback); // web custom settings
|
||||||
NULL, NULL, NULL, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL_TOPIC, MQTT_WILL_ONLINE_PAYLOAD, MQTT_WILL_OFFLINE_PAYLOAD, MQTTCallback);
|
myESP.setOTA(OTACallback_pre, OTACallback_post); // OTA callback which is called when OTA is starting and stopping
|
||||||
|
myESP.begin(APP_HOSTNAME, APP_NAME, APP_VERSION, APP_HELPURL, APP_UPDATEURL);
|
||||||
// OTA callback which is called when OTA is starting and stopping
|
|
||||||
myESP.setOTA(OTACallback_pre, OTACallback_post);
|
|
||||||
|
|
||||||
// custom settings in SPIFFS
|
|
||||||
myESP.setSettings(FSCallback, SettingsCallback);
|
|
||||||
|
|
||||||
// web custom settings
|
|
||||||
myESP.setWeb(WebCallback);
|
|
||||||
|
|
||||||
// start up all the services
|
|
||||||
myESP.begin(APP_HOSTNAME, APP_NAME, APP_VERSION);
|
|
||||||
|
|
||||||
// at this point we have all the settings from our internall SPIFFS config file
|
// at this point we have all the settings from our internall SPIFFS config file
|
||||||
// fire up the UART now
|
// fire up the UART now
|
||||||
@@ -1889,7 +1898,8 @@ void setup() {
|
|||||||
} else {
|
} else {
|
||||||
Serial.println("Note: Serial output will now be disabled. Please use Telnet.");
|
Serial.println("Note: Serial output will now be disabled. Please use Telnet.");
|
||||||
Serial.flush();
|
Serial.flush();
|
||||||
emsuart_init();
|
myESP.setUseSerial(false);
|
||||||
|
emsuart_init(); // start EMS bus transmissions
|
||||||
myDebug_P(PSTR("[UART] Opened Rx/Tx connection"));
|
myDebug_P(PSTR("[UART] Opened Rx/Tx connection"));
|
||||||
if (!EMSESP_Status.listen_mode) {
|
if (!EMSESP_Status.listen_mode) {
|
||||||
// go and find the boiler and thermostat types, if not in listen mode
|
// go and find the boiler and thermostat types, if not in listen mode
|
||||||
|
|||||||
52
src/ems.cpp
52
src/ems.cpp
@@ -11,7 +11,6 @@
|
|||||||
#include "emsuart.h"
|
#include "emsuart.h"
|
||||||
#include <CircularBuffer.h> // https://github.com/rlogiacco/CircularBuffer
|
#include <CircularBuffer.h> // https://github.com/rlogiacco/CircularBuffer
|
||||||
#include <MyESP.h>
|
#include <MyESP.h>
|
||||||
#include <list> // std::list
|
|
||||||
|
|
||||||
#ifdef TESTS
|
#ifdef TESTS
|
||||||
#include "test_data.h"
|
#include "test_data.h"
|
||||||
@@ -739,7 +738,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
|||||||
*/
|
*/
|
||||||
if (EMS_Sys_Status.emsRxStatus != EMS_RX_STATUS_IDLE) {
|
if (EMS_Sys_Status.emsRxStatus != EMS_RX_STATUS_IDLE) {
|
||||||
if (EMS_Sys_Status.emsLogging > EMS_SYS_LOGGING_NONE) {
|
if (EMS_Sys_Status.emsLogging > EMS_SYS_LOGGING_NONE) {
|
||||||
myDebug_P(PSTR("** [DEBUG MODE] We missed the bus - Rx non-idle!")); //TODO tidy up error logging
|
myDebug_P(PSTR("** [DEBUG MODE] Warning, we missed the bus - Rx non-idle!")); // TODO tidy up error logging
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1609,8 +1608,9 @@ void ems_clearDeviceList() {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* add an EMS device to our list of detected devices
|
* add an EMS device to our list of detected devices
|
||||||
|
* model_type = 1=info=boiler, 2=success=thermostat, 3=warning=sm, 4=danger=other, 5=none=unknown
|
||||||
*/
|
*/
|
||||||
void _addDevice(uint8_t product_id, uint8_t device_id, char * version, const char * model_string) {
|
void _addDevice(uint8_t model_type, uint8_t product_id, uint8_t device_id, char * version, const char * model_string) {
|
||||||
_Generic_Device device;
|
_Generic_Device device;
|
||||||
// if its a duplicate don't add
|
// if its a duplicate don't add
|
||||||
bool found = false;
|
bool found = false;
|
||||||
@@ -1620,6 +1620,7 @@ void _addDevice(uint8_t product_id, uint8_t device_id, char * version, const cha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
|
device.model_type = model_type;
|
||||||
device.product_id = product_id;
|
device.product_id = product_id;
|
||||||
device.device_id = device_id;
|
device.device_id = device_id;
|
||||||
strlcpy(device.version, version, sizeof(device.version));
|
strlcpy(device.version, version, sizeof(device.version));
|
||||||
@@ -1659,7 +1660,7 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
|||||||
myDebug_P(PSTR("Boiler found: %s (DeviceID:0x%02X ProductID:%d Version:%s)"), Boiler_Devices[i].model_string, EMS_ID_BOILER, product_id, version);
|
myDebug_P(PSTR("Boiler found: %s (DeviceID:0x%02X ProductID:%d Version:%s)"), Boiler_Devices[i].model_string, EMS_ID_BOILER, product_id, version);
|
||||||
|
|
||||||
// add to list
|
// add to list
|
||||||
_addDevice(product_id, EMS_ID_BOILER, version, Boiler_Devices[i].model_string);
|
_addDevice(1, product_id, EMS_ID_BOILER, version, Boiler_Devices[i].model_string); // type 1 = boiler
|
||||||
|
|
||||||
// if its a boiler set it, unless it already has been set by checking for a productID
|
// if its a boiler set it, unless it already has been set by checking for a productID
|
||||||
// it will take the first one found in the list
|
// it will take the first one found in the list
|
||||||
@@ -1679,8 +1680,6 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
|||||||
EMS_Sys_Status.emsReverse = true;
|
EMS_Sys_Status.emsReverse = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
myESP.fs_saveConfig(); // save config to SPIFFS
|
|
||||||
|
|
||||||
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
|
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -1707,7 +1706,7 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add to list
|
// add to list
|
||||||
_addDevice(product_id, Thermostat_Devices[i].device_id, version, Thermostat_Devices[i].model_string);
|
_addDevice(2, product_id, Thermostat_Devices[i].device_id, version, Thermostat_Devices[i].model_string); // type 2 = thermostat
|
||||||
|
|
||||||
// if we don't have a thermostat set, use this one
|
// if we don't have a thermostat set, use this one
|
||||||
if (((EMS_Thermostat.device_id == EMS_ID_NONE) || (EMS_Thermostat.model_id == EMS_MODEL_NONE)
|
if (((EMS_Thermostat.device_id == EMS_ID_NONE) || (EMS_Thermostat.model_id == EMS_MODEL_NONE)
|
||||||
@@ -1725,8 +1724,6 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
|||||||
EMS_Thermostat.product_id = product_id;
|
EMS_Thermostat.product_id = product_id;
|
||||||
strlcpy(EMS_Thermostat.version, version, sizeof(EMS_Thermostat.version));
|
strlcpy(EMS_Thermostat.version, version, sizeof(EMS_Thermostat.version));
|
||||||
|
|
||||||
myESP.fs_saveConfig(); // save config to SPIFFS
|
|
||||||
|
|
||||||
// get Thermostat values (if supported)
|
// get Thermostat values (if supported)
|
||||||
ems_getThermostatValues();
|
ems_getThermostatValues();
|
||||||
}
|
}
|
||||||
@@ -1752,7 +1749,7 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
|||||||
version);
|
version);
|
||||||
|
|
||||||
// add to list
|
// add to list
|
||||||
_addDevice(product_id, SolarModule_Devices[i].device_id, version, SolarModule_Devices[i].model_string);
|
_addDevice(3, product_id, SolarModule_Devices[i].device_id, version, SolarModule_Devices[i].model_string); // type 3 = other
|
||||||
|
|
||||||
myDebug_P(PSTR("Solar Module support enabled."));
|
myDebug_P(PSTR("Solar Module support enabled."));
|
||||||
EMS_SolarModule.device_id = SolarModule_Devices[i].device_id;
|
EMS_SolarModule.device_id = SolarModule_Devices[i].device_id;
|
||||||
@@ -1782,7 +1779,7 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
|||||||
version);
|
version);
|
||||||
|
|
||||||
// add to list
|
// add to list
|
||||||
_addDevice(product_id, HeatPump_Devices[i].device_id, version, HeatPump_Devices[i].model_string);
|
_addDevice(3, product_id, HeatPump_Devices[i].device_id, version, HeatPump_Devices[i].model_string); // type 3 = other
|
||||||
|
|
||||||
myDebug_P(PSTR("Heat Pump support enabled."));
|
myDebug_P(PSTR("Heat Pump support enabled."));
|
||||||
EMS_HeatPump.device_id = SolarModule_Devices[i].device_id;
|
EMS_HeatPump.device_id = SolarModule_Devices[i].device_id;
|
||||||
@@ -1805,12 +1802,12 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
|||||||
myDebug_P(PSTR("Device found: %s (DeviceID:0x%02X ProductID:%d Version:%s)"), Other_Devices[i].model_string, Other_Devices[i].device_id, product_id, version);
|
myDebug_P(PSTR("Device found: %s (DeviceID:0x%02X ProductID:%d Version:%s)"), Other_Devices[i].model_string, Other_Devices[i].device_id, product_id, version);
|
||||||
|
|
||||||
// add to list
|
// add to list
|
||||||
_addDevice(product_id, Other_Devices[i].device_id, version, Other_Devices[i].model_string);
|
_addDevice(4, product_id, Other_Devices[i].device_id, version, Other_Devices[i].model_string); // type 3 = other
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
myDebug_P(PSTR("Unrecognized device found: %s (DeviceID:0x%02X ProductID:%d Version:%s)"), EMS_RxTelegram->src, product_id, version);
|
myDebug_P(PSTR("Unrecognized device found: %s (DeviceID:0x%02X ProductID:%d Version:%s)"), EMS_RxTelegram->src, product_id, version);
|
||||||
// add to list
|
// add to list
|
||||||
_addDevice(product_id, EMS_RxTelegram->src, version, "unknown?");
|
_addDevice(5, product_id, EMS_RxTelegram->src, version, "unknown?"); // type 4 = unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1978,7 +1975,7 @@ void ems_getSolarModuleValues() {
|
|||||||
* returns current thermostat type as a string
|
* returns current thermostat type as a string
|
||||||
* by looking up the product_id
|
* by looking up the product_id
|
||||||
*/
|
*/
|
||||||
char * ems_getThermostatDescription(char * buffer) {
|
char * ems_getThermostatDescription(char * buffer, bool name_only) {
|
||||||
uint8_t size = 128;
|
uint8_t size = 128;
|
||||||
if (!ems_getThermostatEnabled()) {
|
if (!ems_getThermostatEnabled()) {
|
||||||
strlcpy(buffer, "<not enabled>", size);
|
strlcpy(buffer, "<not enabled>", size);
|
||||||
@@ -1998,6 +1995,9 @@ char * ems_getThermostatDescription(char * buffer) {
|
|||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
strlcpy(buffer, Thermostat_Devices[i].model_string, size);
|
strlcpy(buffer, Thermostat_Devices[i].model_string, size);
|
||||||
|
if (name_only) {
|
||||||
|
return buffer; // only interested in the model name
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
strlcpy(buffer, "DeviceID: 0x", size);
|
strlcpy(buffer, "DeviceID: 0x", size);
|
||||||
strlcat(buffer, _hextoa(EMS_Thermostat.device_id, tmp), size);
|
strlcat(buffer, _hextoa(EMS_Thermostat.device_id, tmp), size);
|
||||||
@@ -2020,7 +2020,7 @@ char * ems_getThermostatDescription(char * buffer) {
|
|||||||
/**
|
/**
|
||||||
* returns current boiler type as a string
|
* returns current boiler type as a string
|
||||||
*/
|
*/
|
||||||
char * ems_getBoilerDescription(char * buffer) {
|
char * ems_getBoilerDescription(char * buffer, bool name_only) {
|
||||||
uint8_t size = 128;
|
uint8_t size = 128;
|
||||||
if (!ems_getBoilerEnabled()) {
|
if (!ems_getBoilerEnabled()) {
|
||||||
strlcpy(buffer, "<not enabled>", size);
|
strlcpy(buffer, "<not enabled>", size);
|
||||||
@@ -2039,6 +2039,9 @@ char * ems_getBoilerDescription(char * buffer) {
|
|||||||
}
|
}
|
||||||
if (found) {
|
if (found) {
|
||||||
strlcpy(buffer, Boiler_Devices[i].model_string, size);
|
strlcpy(buffer, Boiler_Devices[i].model_string, size);
|
||||||
|
if (name_only) {
|
||||||
|
return buffer; // only interested in the model name
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
strlcpy(buffer, "DeviceID: 0x", size);
|
strlcpy(buffer, "DeviceID: 0x", size);
|
||||||
strlcat(buffer, _hextoa(EMS_Boiler.device_id, tmp), size);
|
strlcat(buffer, _hextoa(EMS_Boiler.device_id, tmp), size);
|
||||||
@@ -2261,23 +2264,6 @@ void ems_printDevices() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* prints the device list to a string for html parsing
|
|
||||||
*/
|
|
||||||
uint8_t ems_printDevices_s(char * buffer, uint16_t len) {
|
|
||||||
if (Devices.size() == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char s[100];
|
|
||||||
for (std::list<_Generic_Device>::iterator it = Devices.begin(); it != Devices.end(); it++) {
|
|
||||||
sprintf(s, "%s (DeviceID:0x%02X ProductID:%d Version:%s)<br>", (it)->model_string, (it)->device_id, (it)->product_id, (it)->version);
|
|
||||||
strlcat(buffer, s, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Devices.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a command to UART Tx to Read from another device
|
* Send a command to UART Tx to Read from another device
|
||||||
* Read commands when sent must respond by the destination (target) immediately (or within 10ms)
|
* Read commands when sent must respond by the destination (target) immediately (or within 10ms)
|
||||||
@@ -2695,7 +2681,7 @@ void ems_setWarmTapWaterActivated(bool activated) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Start up sequence for UBA Master, hopefully to initialize a handshake
|
* Start up sequence for UBA Master, hopefully to initialize a handshake
|
||||||
* Still experimental
|
* Still experimental and not used yet!
|
||||||
*/
|
*/
|
||||||
void ems_startupTelegrams() {
|
void ems_startupTelegrams() {
|
||||||
if ((EMS_Sys_Status.emsTxDisabled) || (!EMS_Sys_Status.emsBusConnected)) {
|
if ((EMS_Sys_Status.emsTxDisabled) || (!EMS_Sys_Status.emsBusConnected)) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <list> // std::list
|
||||||
|
|
||||||
/* debug helper for logic analyzer
|
/* debug helper for logic analyzer
|
||||||
* create marker puls on GPIOx
|
* create marker puls on GPIOx
|
||||||
@@ -250,6 +251,7 @@ typedef struct {
|
|||||||
|
|
||||||
// for consolidating all types
|
// for consolidating all types
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
uint8_t model_type; // 1=info=boiler, 2=success=thermostat, 3=warning=sm, 4=danger=other, 5=none=unknown
|
||||||
uint8_t product_id;
|
uint8_t product_id;
|
||||||
uint8_t device_id;
|
uint8_t device_id;
|
||||||
char version[10];
|
char version[10];
|
||||||
@@ -421,8 +423,8 @@ void ems_setTxDisabled(bool b);
|
|||||||
bool ems_getTxDisabled();
|
bool ems_getTxDisabled();
|
||||||
uint8_t ems_getTxMode();
|
uint8_t ems_getTxMode();
|
||||||
|
|
||||||
char * ems_getThermostatDescription(char * buffer);
|
char * ems_getThermostatDescription(char * buffer, bool name_only = false);
|
||||||
char * ems_getBoilerDescription(char * buffer);
|
char * ems_getBoilerDescription(char * buffer, bool name_only = false);
|
||||||
char * ems_getSolarModuleDescription(char * buffer);
|
char * ems_getSolarModuleDescription(char * buffer);
|
||||||
char * ems_getHeatPumpDescription(char * buffer);
|
char * ems_getHeatPumpDescription(char * buffer);
|
||||||
void ems_getThermostatValues();
|
void ems_getThermostatValues();
|
||||||
@@ -457,3 +459,5 @@ extern _EMS_Thermostat EMS_Thermostat;
|
|||||||
extern _EMS_SolarModule EMS_SolarModule;
|
extern _EMS_SolarModule EMS_SolarModule;
|
||||||
extern _EMS_HeatPump EMS_HeatPump;
|
extern _EMS_HeatPump EMS_HeatPump;
|
||||||
extern _EMS_Other EMS_Other;
|
extern _EMS_Other EMS_Other;
|
||||||
|
|
||||||
|
extern std::list<_Generic_Device> Devices;
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ void ICACHE_FLASH_ATTR emsuart_init() {
|
|||||||
// UCFFT = RX FIFO Full Threshold (7 bit) = want this to be 31 for 32 bytes of buffer (default was 127)
|
// UCFFT = RX FIFO Full Threshold (7 bit) = want this to be 31 for 32 bytes of buffer (default was 127)
|
||||||
// see https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf
|
// see https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf
|
||||||
//
|
//
|
||||||
// change: we set UCFFT to 1 to get an immediate indicator about incoming trafffic.
|
// change: we set UCFFT to 1 to get an immediate indicator about incoming traffic.
|
||||||
// Otherwise, we're only noticed by UCTOT or RxBRK!
|
// Otherwise, we're only noticed by UCTOT or RxBRK!
|
||||||
USC1(EMSUART_UART) = 0; // reset config first
|
USC1(EMSUART_UART) = 0; // reset config first
|
||||||
USC1(EMSUART_UART) = (0x01 << UCFFT) | (0x01 << UCTOT) | (1 << UCTOE); // enable interupts
|
USC1(EMSUART_UART) = (0x01 << UCFFT) | (0x01 << UCTOT) | (1 << UCTOE); // enable interupts
|
||||||
@@ -141,7 +141,7 @@ void ICACHE_FLASH_ATTR emsuart_init() {
|
|||||||
system_set_os_print(0);
|
system_set_os_print(0);
|
||||||
|
|
||||||
// swap Rx and Tx pins to use GPIO13 (D7) and GPIO15 (D8) respectively
|
// swap Rx and Tx pins to use GPIO13 (D7) and GPIO15 (D8) respectively
|
||||||
//system_uart_swap();
|
system_uart_swap();
|
||||||
|
|
||||||
ETS_UART_INTR_ATTACH(emsuart_rx_intr_handler, nullptr);
|
ETS_UART_INTR_ATTACH(emsuart_rx_intr_handler, nullptr);
|
||||||
ETS_UART_INTR_ENABLE();
|
ETS_UART_INTR_ENABLE();
|
||||||
|
|||||||
@@ -10,18 +10,6 @@
|
|||||||
|
|
||||||
#include "ems.h"
|
#include "ems.h"
|
||||||
|
|
||||||
// MQTT base name
|
|
||||||
#define MQTT_BASE "home" // all MQTT topics are prefix with this string, in the format <MQTT_BASE>/<app name>/<topic>
|
|
||||||
|
|
||||||
// MQTT general settings
|
|
||||||
#define MQTT_WILL_TOPIC "status" // for last will & testament topic name
|
|
||||||
#define MQTT_WILL_ONLINE_PAYLOAD "online" // for last will & testament payload
|
|
||||||
#define MQTT_WILL_OFFLINE_PAYLOAD "offline" // for last will & testament payload
|
|
||||||
#define MQTT_RETAIN false
|
|
||||||
#define MQTT_KEEPALIVE 120 // 2 minutes
|
|
||||||
#define MQTT_QOS 1
|
|
||||||
#define MQTT_MAX_SIZE 700 // max size of a JSON object. See https://arduinojson.org/v6/assistant/
|
|
||||||
|
|
||||||
// MQTT for thermostat
|
// MQTT for thermostat
|
||||||
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values to MQTT
|
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values to MQTT
|
||||||
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes via MQTT
|
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes via MQTT
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#define APP_NAME "EMS-ESP"
|
#define APP_NAME "EMS-ESP"
|
||||||
#define APP_VERSION "1.9.0"
|
#define APP_VERSION "1.9.0b_web"
|
||||||
#define APP_HOSTNAME "ems-esp"
|
#define APP_HOSTNAME "ems-esp"
|
||||||
|
#define APP_HELPURL "https://github.com/proddy/EMS-ESP/wiki"
|
||||||
|
#define APP_UPDATEURL "https://api.github.com/repos/proddy/EMS-ESP/releases/latest"
|
||||||
|
|||||||
0
src/webh/.gitkeep
Normal file
0
src/webh/.gitkeep
Normal file
6
src/websrc/3rdparty/css/bootstrap-3.3.7.min.css
vendored
Normal file
6
src/websrc/3rdparty/css/bootstrap-3.3.7.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/websrc/3rdparty/css/footable.bootstrap-3.1.6.min.css
vendored
Normal file
1
src/websrc/3rdparty/css/footable.bootstrap-3.1.6.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/websrc/3rdparty/css/sidebar.css
vendored
Normal file
1
src/websrc/3rdparty/css/sidebar.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
html {position: relative;overflow: scroll;overflow-x: hidden;min-height: 100% }::-webkit-scrollbar {width: 0px;background: transparent;}::-webkit-scrollbar-thumb {background: #e8e8e8;}body {background: #f1f3f6;margin-bottom: 60px }p {font-size: 1.1em;font-weight: 300;line-height: 1.7em;color: #999 }a, a:focus, a:hover {color: inherit;text-decoration: none;transition: all .3s }.navbar {padding: 15px 10px;background: #fff;border: none;border-radius: 0;margin-bottom: 40px;box-shadow: 1px 1px 3px rgba(0, 0, 0, .1) }#dismiss, #sidebar {background: #337ab7 }#content.navbar-btn {box-shadow: none;outline: 0;border: none }.line {width: 100%;height: 1px;border-bottom: 1px dashed #ddd;margin: 40px 0 }#sidebar {width: 250px;position: fixed;top: 0;left: -250px;height: 100vh;z-index: 999;color: #fff;transition: all .3s;overflow-y: auto;box-shadow: 3px 3px 3px rgba(0, 0, 0, .2) }@media screen and (min-width:768px) {#sidebar {left: 0 }.footer {margin-left: 250px }#ajaxcontent {margin-left: 250px }#dismiss, .navbar-btn {display: none }}#sidebar.active {left: 0 }#dismiss {width: 35px;height: 35px;line-height: 35px;text-align: center;position: absolute;top: 10px;right: 10px;cursor: pointer;-webkit-transition: all .3s;-o-transition: all .3s;transition: all .3s }#dismiss:hover {background: #fff;color: #337ab7 }.overlay {position: fixed;width: 100vw;height: 100vh;background: rgba(0, 0, 0, .7);z-index: 998;display: none }#sidebar .sidebar-header {padding: 20px;background: #337ab7 }#sidebar ul.components {padding: 20px 0;border-bottom: 1px solid #47748b }#content, ul.CTAs {padding: 20px }#sidebar ul p {color: #fff;padding: 10px }#sidebar ul li a {padding: 10px;font-size: 1.1em;display: block }#sidebar ul li a:hover {color: #337ab7;background: #fff }#sidebar ul li.active>a, a[aria-expanded=true] {color: #fff;background: #2e6da4 }a[data-toggle=collapse] {position: relative }a[aria-expanded=false]::before, a[aria-expanded=true]::before {content: '\e259';display: block;position: absolute;right: 20px;font-family: 'Glyphicons Halflings';font-size: .6em }#sidebar ul ul a, ul.CTAs a {font-size: .9em }a[aria-expanded=true]::before {content: '\e260' }#sidebar ul ul a {padding-left: 30px;background: #2e6da4 }ul.CTAs a {text-align: center;display: block;border-radius: 5px;margin-bottom: 5px }a.download {background: #fff;color: #337ab7 }#sidebar a.article, a.article:hover {background: #2e6da4;color: #fff }#content {width: 100%;min-height: 100vh;transition: all .3s;position: absolute;top: 0;right: 0 }.footer {position: fixed;bottom: 0;width: 100%;height: 45px;background-color: #f1f3f6 }i {margin-right: 1em }
|
||||||
BIN
src/websrc/3rdparty/fonts/glyphicons-halflings-regular.woff
vendored
Normal file
BIN
src/websrc/3rdparty/fonts/glyphicons-halflings-regular.woff
vendored
Normal file
Binary file not shown.
7
src/websrc/3rdparty/js/bootstrap-3.3.7.min.js
vendored
Normal file
7
src/websrc/3rdparty/js/bootstrap-3.3.7.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10
src/websrc/3rdparty/js/footable-3.1.6.min.js
vendored
Normal file
10
src/websrc/3rdparty/js/footable-3.1.6.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
src/websrc/3rdparty/js/jquery-1.12.4.min.js
vendored
Normal file
5
src/websrc/3rdparty/js/jquery-1.12.4.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
280
src/websrc/index.html
Normal file
280
src/websrc/index.html
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link
|
||||||
|
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAYFBMVEX///8zMzM1NTU0NDQ2Njby8vJ4eHhBQUGSkpJdXV36+vqbm5tpaWlPT0/Ly8uGhoZxcXHAwMBVVVVJSUm3t7fu7u6mpqY8PDzo6OiPj4/U1NR+fn5ra2tGRkbb29vExMQQm/MfAAAA4klEQVQ4jd1SbXaDMAwD2/kAElpKIRTa7v63nGPY9gjhAKt+OZGeItspik9D3Vqt9eU+nPCtC0CMsh/rHN8EwjICga5Thicof4Dk1ME/CA8EYkOPNF9FYu5dhVGJYU4MSozOzVSrzkcF2b3AxktopJ4Ni6Had6L5BfDbAC58QKMOAnLbYTwR/LrajEBcQye1unEg9HvBHWMw33I5aOli2Xcx9NKGWUZ7WyfWJZOKwWQN68Tpme6rvm6rEtDrXaSY3J+C+q8Dz+Ft4E0hvwLPOcMzZlsZY/zSZf/LmkQxTtl/jG/cCAezKUvMMwAAAABJRU5ErkJggg=="
|
||||||
|
rel="icon" type="image/x-icon" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="author" content="">
|
||||||
|
<title id="customname"></title>
|
||||||
|
<!-- Bootstrap core CSS -->
|
||||||
|
<link href="css/required.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="wrapper">
|
||||||
|
<!-- Sidebar Holder -->
|
||||||
|
<nav id="sidebar">
|
||||||
|
<div id="dismiss">
|
||||||
|
<i class="glyphicon glyphicon-arrow-left"></i>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-header">
|
||||||
|
<h1 id="customname2" class="text-center"></h1>
|
||||||
|
<h6 id="mainver" class="text-center"></h6>
|
||||||
|
</div>
|
||||||
|
<ul class="list-unstyled components">
|
||||||
|
<li class="active">
|
||||||
|
<a href="#" id="custom_status"><i class="glyphicon glyphicon-home"></i>Dashboard</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" id="status"><i class="glyphicon glyphicon-equalizer"></i>System Status</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#homeSubmenu" data-toggle="collapse" aria-expanded="false"><i
|
||||||
|
class="glyphicon glyphicon-cog"></i>Settings</a>
|
||||||
|
<ul class="collapse list-unstyled" id="homeSubmenu">
|
||||||
|
<li>
|
||||||
|
<a href="#" id="network"><i class="glyphicon glyphicon-signal"></i>Wireless Network</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" id="general"><i class="glyphicon glyphicon-list-alt"></i>General Settings</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" id="mqtt"><i class="glyphicon glyphicon-link"></i>MQTT Settings</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" id="ntp"><i class="glyphicon glyphicon-hourglass"></i>NTP (Time) Settings</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" id="custom"><i class="glyphicon glyphicon-wrench"></i>Custom Settings</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" id="eventlog"><i class="glyphicon glyphicon-transfer"></i>Event Log</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" id="backup"><i class="glyphicon glyphicon-floppy-disk"></i>Backup & Restore</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" id="reset"><i class="glyphicon glyphicon-repeat"></i>Factory Reset</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" id="restart"><i class="glyphicon glyphicon-refresh"></i>Restart System</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a data-toggle="modal" href="#update"><i class="glyphicon glyphicon-open"></i>Check for Updates</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="list-unstyled CTAs">
|
||||||
|
<li>
|
||||||
|
<a id="helpurl" href="https://github.com/proddy" class="download">Help</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" class="article" onclick="logout();">Logout</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Page Content Holder -->
|
||||||
|
<div id="content">
|
||||||
|
<button type="button" id="sidebarCollapse" class="btn btn-info navbar-btn">
|
||||||
|
<i class="glyphicon glyphicon-menu-hamburger"></i>
|
||||||
|
<span>Menu</span>
|
||||||
|
</button>
|
||||||
|
<div id="ajaxcontent">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="revcommit" class="modal fade" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<!-- Modal content-->
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="modal-title">Please review your system changes</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<pre id="jsonholder"></pre>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" onclick="commit();" class="btn btn-success" data-dismiss="modal">Save
|
||||||
|
& Restart</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="custom_revcommit" class="modal fade" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<!-- Modal content-->
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="modal-title">Please review your custom changes</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<pre id="jsonholder2"></pre>
|
||||||
|
Note: some settings my require a <b>Restart System</b> first to take effect.
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" onclick="custom_commit();" class="btn btn-success"
|
||||||
|
data-dismiss="modal">Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="destroy" class="modal fade" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<!-- Modal content-->
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="modal-title">Factory Reset</h4>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<h5><b>Warning!</b> This action <strong>cannot</strong> be undone. This will permanently
|
||||||
|
delete <strong>all
|
||||||
|
the settings and logs.</strong> Please make sure you've made a backup first.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<h5>Please type in the hostname of the device to confirm.</h5>
|
||||||
|
<div style="clear:both;">
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
<input type="text" class="form-control input-block" id="compare"
|
||||||
|
oninput="compareDestroy()">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button id="destroybtn" type="button" disabled="" onclick="destroy();"
|
||||||
|
class="btn btn-block btn-danger">I understand, reset all my settings</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="reboot" class="modal fade" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<!-- Modal content-->
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="modal-title">Restart System</h4>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<h5><b>Are you sure you want to restart the system?</b></h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button id="restartbtn" type="button" onclick="restart();"
|
||||||
|
class="btn btn-block btn-danger">Restart</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="signin" class="modal fade" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<!-- Modal content-->
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="modal-title">Please log in</h4>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<br>
|
||||||
|
<div class="col-md-8 col-md-offset-2">
|
||||||
|
<div class="login-panel panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<form role="form" onsubmit="login(); return false">
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group">
|
||||||
|
<input id="password" class="form-control" placeholder="Password"
|
||||||
|
name="password" type="password" value="" required=""
|
||||||
|
title="Please enter your password">
|
||||||
|
</div>
|
||||||
|
<button type="submit"
|
||||||
|
class="btn btn-success btn-md pull-right">Login</button>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="update" class="modal fade" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<!-- Modal content-->
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="modal-title">Update Firmware</h4>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<strong>Warning!</strong> Please make sure you've made a backup first
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div>
|
||||||
|
<h4>Latest Stable Release</h4>
|
||||||
|
<div id="onlineupdate">
|
||||||
|
<h5 id=releasehead></h5>
|
||||||
|
<div style="clear:both;">
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<pre id="releasebody">Getting update information from GitHub...</pre>
|
||||||
|
<div class="pull-right">
|
||||||
|
<a class="pull-right" id="downloadupdate">
|
||||||
|
<button type="button" class="btn btn-primary">Download</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;">
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4>Current Version:</h4>
|
||||||
|
<h5 id="versionhead"></h5>
|
||||||
|
<div class="form-group">
|
||||||
|
<input id="binform" onchange="allowUpload();" type="file" name="update"
|
||||||
|
accept=".bin">
|
||||||
|
</div>
|
||||||
|
<div class="pull-right">
|
||||||
|
<button onclick="upload();" class="btn btn-primary" id="upbtn"
|
||||||
|
disabled="">Update</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;">
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<div id="commit" class="container">
|
||||||
|
<h6 class="text-muted">(running on <a href="https://github.com/proddy/MyESP">MyESP</a>)</h6>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<div class="overlay"></div>
|
||||||
|
<script src="js/required.js"></script>
|
||||||
|
<script src="js/myesp.js"></script>
|
||||||
|
<script>start();</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
484
src/websrc/myesp.htm
Normal file
484
src/websrc/myesp.htm
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
<div id="backupcontent">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<legend>Backup</legend>
|
||||||
|
<h6 class="text-muted">Please make sure that you have made a backup on regular basis.</h6>
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-link btn-sm" onclick="backupset();">Backup System Settings</button>
|
||||||
|
<a id="downloadSet" style="display:none"></a>
|
||||||
|
<button class="btn btn-link btn-sm" onclick="backupCustomSet();">Backup Custom Settings</button>
|
||||||
|
<a id="downloadCustomSet" style="display:none"></a>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div>
|
||||||
|
<legend>Restore</legend>
|
||||||
|
<h6 class="text-muted">Restore system and custom settings.</h6>
|
||||||
|
<label for="restoreSet" class="btn btn-link btn-sm">Restore System Settings</label>
|
||||||
|
<input id="restoreSet" type="file" accept="text/json" onchange="restoreSet();" style="display:none;">
|
||||||
|
<label for="restoreCustomSet" class="btn btn-link btn-sm">Restore Custom Settings</label>
|
||||||
|
<input id="restoreCustomSet" type="file" accept="text/json" onchange="restoreCustomSet();"
|
||||||
|
style="display:none;">
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div>
|
||||||
|
<legend>Restart</legend>
|
||||||
|
<h6 class="text-muted">Click to restart your device without saving changes.</h6>
|
||||||
|
<label for="restart" class="btn btn-link btn-sm">Restart Device</label>
|
||||||
|
<button id="restart" class="btn btn-link btn-sm" onclick="restartESP();" style="display:none;"></button>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div id="restoremodal" class="modal fade" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="modal-title">Please wait while data is restoring...</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="pbar" class="progress">
|
||||||
|
<div id="dynamic" class="progress-bar progress-bar-primary progress-bar-striped active">
|
||||||
|
Restoring...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" id="restoreclose" style="display:none;" class="btn btn-default"
|
||||||
|
data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="progresscontent">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<br>
|
||||||
|
<div class="col-md-8 col-md-offset-2">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Please wait about 10 seconds while the system restarts...</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="progress">
|
||||||
|
<div id="updateprog" class="progress-bar progress-bar-striped active" role="progressbar"
|
||||||
|
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:0%">0%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer text-center" id="reconnect" style="display:none;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="generalcontent">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<legend>General Settings</legend>
|
||||||
|
<br>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Admin Password<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Log On password"></i></label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<input class="form-control input-sm" placeholder="Administrator Password" id="adminpwd" type="password">
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Host Name<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Hostname. When Bonjour is installed on your computer you can access via http://hostname.local"></i></label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<input class="form-control input-sm" placeholder="Hostname" id="hostname" type="text">
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Serial<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Please choose if you want to enable Serial output for debugging"></i></label>
|
||||||
|
<div class="col-xs-9">
|
||||||
|
<form>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="1" name="serialenabled">Enabled</label>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="0" name="serialenabled" checked>Disabled</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<div class="col-xs-9 col-md-8">
|
||||||
|
<button onclick="savegeneral()" class="btn btn-primary btn-sm pull-right">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="eventcontent">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<div class="text-center" id="loading-img">
|
||||||
|
<h5>Please wait while fetching data...<span id="loadpages"></span></h5>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<legend>Event Log</legend>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div>
|
||||||
|
<table id="eventtable" class="table" data-paging="true" data-filtering="true" data-sorting="true"
|
||||||
|
data-editing="false" data-state="true"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button onclick="clearevent()" class="btn btn-primary btn-sm">Clear Log</button>
|
||||||
|
<div style="clear:both;">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="mqttcontent">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<legend>MQTT Settings</legend>
|
||||||
|
<br>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">MQTT<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Please choose if you want to enable MQTT"></i></label>
|
||||||
|
<div class="col-xs-9">
|
||||||
|
<form>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="1" name="mqttenabled">Enabled</label>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="0" name="mqttenabled" checked>Disabled</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">IP<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign" aria-hidden="true"
|
||||||
|
data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="MQTT server IP Address"></i></label>
|
||||||
|
<span class="col-xs-9">
|
||||||
|
<input class="form-control input-sm" placeholder="MQTT IP" style="display:inline;max-width:185px"
|
||||||
|
id="mqttip" type="text">
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Port<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="MQTT server port"></i></label>
|
||||||
|
<span class="col-xs-9">
|
||||||
|
<input class="form-control input-sm" placeholder="MQTT Port" value="" style="display:inline;max-width:185px"
|
||||||
|
id="mqttport" type="text">
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Username<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="MQTT username"></i></label>
|
||||||
|
<span class="col-xs-9">
|
||||||
|
<input class="form-control input-sm" placeholder="" value="" style="display:inline;max-width:185px"
|
||||||
|
id="mqttuser" type="text">
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Password<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="MQTT server password if any"></i></label>
|
||||||
|
<span class="col-xs-9">
|
||||||
|
<input class="form-control input-sm" placeholder="" value="" style="display:inline;max-width:185px"
|
||||||
|
id="mqttpwd" type="password">
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Base<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="MQTT base prefix (optional)"></i></label>
|
||||||
|
<span class="col-xs-9">
|
||||||
|
<input class="form-control input-sm" placeholder="MQTT base" value="" style="display:inline;max-width:185px"
|
||||||
|
id="mqttbase" type="text">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Heartbeat<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Please choose if you want to enable the MQTT heartbeat"></i></label>
|
||||||
|
<div class="col-xs-9">
|
||||||
|
<form>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="1" name="mqttheartbeat">Enabled</label>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="0" name="mqttheartbeat" checked>Disabled</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="col-xs-9 col-md-8">
|
||||||
|
<button onclick="savemqtt()" class="btn btn-primary btn-sm pull-right">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="networkcontent">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<legend>Wi-Fi Settings</legend>
|
||||||
|
<h6 class="text-muted">Type your Wi-Fi Network's SSID or Scan for nerby Wireless Networks to join.</h6>
|
||||||
|
<br>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Wi-Fi Mode<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="You can run your ESP in AP Mode or Client Mode. In client mode you will need to connect to an existing Wi-Fi network, in AP Mode ESP creates a Wi-Fi network itself."></i></label>
|
||||||
|
<div class="col-xs-9">
|
||||||
|
<form>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="1" name="wmode" id="wmodeap" onclick="handleAP();" checked>Access Point
|
||||||
|
</label>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="0" name="wmode" id="wmodesta" onclick="handleSTA();">Client</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group" style="display:none" id="hidessid">
|
||||||
|
<label class="col-xs-3">SSID<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Wi-Fi Network's Name"></i></label>
|
||||||
|
<span class="col-xs-7 col-md-5">
|
||||||
|
<input class="form-control input-sm" id="inputtohide" type="text" name="ap_ssid">
|
||||||
|
<select class="form-control input-sm" style="display:none;" id="ssid" onchange="listBSSID();"></select>
|
||||||
|
</span>
|
||||||
|
<span class="col-xs-2">
|
||||||
|
<button id="scanb" type="button" class="btn btn-primary btn-xs" style="display:none;"
|
||||||
|
onclick="scanWifi()">Scan</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group" style="display:none" id="hidepasswd">
|
||||||
|
<label class="col-xs-3">Password<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Wi-Fi Password"></i></label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<input id="wifipass" class="form-control input-sm" name="ap_passwd" type="password">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="col-xs-9 col-md-8">
|
||||||
|
<button onclick="savenetwork()" class="btn btn-primary btn-sm pull-right">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="ntpcontent">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<legend>Time Settings</legend>
|
||||||
|
<h6 class="text-muted">Daylight saving times are taken into account</h6>
|
||||||
|
<br>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Device Time</label>
|
||||||
|
<span id="utc" class="col-xs-9 col-md-5">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Browser Time</label>
|
||||||
|
<span id="rtc" class="col-xs-9 col-md-5">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<button onclick="syncBrowserTime()" class="btn btn-link btn-sm">Sync Browser Time to Device</button><i
|
||||||
|
style="margin-left: 10px;" class="glyphicon glyphicon-info-sign" aria-hidden="true"
|
||||||
|
data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Use your browser time. Useful when the system does not have an internet connection."></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">NTP<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Enable NTP - requires an internet connection"></i></label>
|
||||||
|
<div class="col-xs-9">
|
||||||
|
<form>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="1" name="ntpenabled">Enabled</label>
|
||||||
|
<label class="radio-inline">
|
||||||
|
<input type="radio" value="0" name="ntpenabled" checked>Disabled</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">NTP Server<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="The server for the time sync. Choose nearest server for better accuracy, see https://www.ntppool.org for servers nearby."></i></label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<input class="form-control input-sm" placeholder="eg. pool.ntp.org" value="pool.ntp.org" id="ntpserver"
|
||||||
|
type="text">
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Intervals<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||||
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
|
data-content="Intervals between Time Sync in Minutes"></i></label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<input class="form-control input-sm" placeholder="in Minutes" value="30" id="intervals" type="text">
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Time Zone</label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<select class="form-control input-sm" name="DropDownTimezone" id="DropDownTimezone">
|
||||||
|
<option value="-12">(GMT -12:00) Eniwetok, Kwajalein</option>
|
||||||
|
<option value="-11">(GMT -11:00) Midway Island, Samoa</option>
|
||||||
|
<option value="-10">(GMT -10:00) Hawaii</option>
|
||||||
|
<option value="-9">(GMT -9:00) Alaska</option>
|
||||||
|
<option value="-8">(GMT -8:00) Pacific Time (US & Canada)</option>
|
||||||
|
<option value="-7">(GMT -7:00) Mountain Time (US & Canada)</option>
|
||||||
|
<option value="-6">(GMT -6:00) Central Time (US & Canada), Mexico City</option>
|
||||||
|
<option value="-5">(GMT -5:00) Eastern Time (US & Canada), Bogota, Lima</option>
|
||||||
|
<option value="-4">(GMT -4:00) Atlantic Time (Canada), Caracas, La Paz</option>
|
||||||
|
<option value="-3.5">(GMT -3:30) Newfoundland</option>
|
||||||
|
<option value="-3">(GMT -3:00) Brazil, Buenos Aires, Georgetown</option>
|
||||||
|
<option value="-2">(GMT -2:00) Mid-Atlantic</option>
|
||||||
|
<option value="-1">(GMT -1:00 hour) Azores, Cape Verde Islands</option>
|
||||||
|
<option value="0">(GMT) Western Europe Time, London, Lisbon, Casablanca</option>
|
||||||
|
<option selected="selected" value="1">(GMT +1:00 hour) Brussels, Copenhagen, Madrid, Paris</option>
|
||||||
|
<option value="2">(GMT +2:00) Kaliningrad, South Africa</option>
|
||||||
|
<option value="3">(GMT +3:00) Baghdad, Riyadh, Moscow, St. Petersburg</option>
|
||||||
|
<option value="3.5">(GMT +3:30) Tehran</option>
|
||||||
|
<option value="4">(GMT +4:00) Abu Dhabi, Muscat, Baku, Tbilisi</option>
|
||||||
|
<option value="4.5">(GMT +4:30) Kabul</option>
|
||||||
|
<option value="5">(GMT +5:00) Ekaterinburg, Islamabad, Karachi, Tashkent</option>
|
||||||
|
<option value="5.5">(GMT +5:30) Bombay, Calcutta, Madras, New Delhi</option>
|
||||||
|
<option value="5.75">(GMT +5:45) Kathmandu</option>
|
||||||
|
<option value="6">(GMT +6:00) Almaty, Dhaka, Colombo</option>
|
||||||
|
<option value="7">(GMT +7:00) Bangkok, Hanoi, Jakarta</option>
|
||||||
|
<option value="8">(GMT +8:00) Beijing, Perth, Singapore, Hong Kong</option>
|
||||||
|
<option value="9">(GMT +9:00) Tokyo, Seoul, Osaka, Sapporo, Yakutsk</option>
|
||||||
|
<option value="9.5">(GMT +9:30) Adelaide, Darwin</option>
|
||||||
|
<option value="10">(GMT +10:00) Eastern Australia, Guam, Vladivostok</option>
|
||||||
|
<option value="11">(GMT +11:00) Magadan, Solomon Islands, New Caledonia</option>
|
||||||
|
<option value="12">(GMT +12:00) Auckland, Wellington, Fiji, Kamchatka</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="col-xs-9 col-md-8">
|
||||||
|
<button onclick="saventp()" class="btn btn-primary btn-sm pull-right">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="statuscontent">
|
||||||
|
<br><br>
|
||||||
|
<legend>System Status</legend>
|
||||||
|
<br>
|
||||||
|
<div class="row text-center">
|
||||||
|
<div class="col-md-8 col-md-offset-2">
|
||||||
|
<div class="panel panel-default table-responsive">
|
||||||
|
<table class="table table-hover table-striped table-condensed">
|
||||||
|
<caption>System</caption>
|
||||||
|
<tr>
|
||||||
|
<th>Uptime</th>
|
||||||
|
<td id="uptime"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>System Load</th>
|
||||||
|
<td id="systemload"> %</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default table-responsive">
|
||||||
|
<table class="table table-hover table-striped table-condensed">
|
||||||
|
<caption>Storage</caption>
|
||||||
|
<tr>
|
||||||
|
<th>Free Heap</th>
|
||||||
|
<td>
|
||||||
|
<div class="progress" style="margin-bottom: 0 !important;">
|
||||||
|
<div id="heap" class="progress-bar progress-bar-primary" role="progressbar">
|
||||||
|
Progress
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Free Flash</th>
|
||||||
|
<td>
|
||||||
|
<div class='progress' style="margin-bottom: 0 !important;">
|
||||||
|
<div id="flash" class="progress-bar progress-bar-primary" role="progressbar">
|
||||||
|
Progress
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Free SPIFFS</th>
|
||||||
|
<td>
|
||||||
|
<div class='progress' style="margin-bottom: 0 !important;">
|
||||||
|
<div id="spiffs" class="progress-bar progress-bar-primary" role="progressbar">
|
||||||
|
Progress
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default table-responsive">
|
||||||
|
<table class="table table-hover table-striped table-condensed">
|
||||||
|
<caption>Wireless Network</caption>
|
||||||
|
<tr>
|
||||||
|
<th>SSID</th>
|
||||||
|
<td id="ssidstat"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>IP Address</th>
|
||||||
|
<td id="ip"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>MAC Address</th>
|
||||||
|
<td id="mac"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Signal Strength</th>
|
||||||
|
<td id="signalstr"> %</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-default table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<caption>MQTT</caption>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div id="mqttconnected"></div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div id="mqttheartbeat"></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
934
src/websrc/myesp.js
Normal file
934
src/websrc/myesp.js
Normal file
@@ -0,0 +1,934 @@
|
|||||||
|
var version = "";
|
||||||
|
|
||||||
|
var websock = null;
|
||||||
|
var wsUri = "ws://" + window.location.host + "/ws";
|
||||||
|
var utcSeconds;
|
||||||
|
var timezone;
|
||||||
|
var data = [];
|
||||||
|
var ajaxobj;
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
"command": "configfile",
|
||||||
|
"network": {
|
||||||
|
"ssid": "",
|
||||||
|
"wmode": 1,
|
||||||
|
"password": ""
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"hostname": "",
|
||||||
|
"serial": false,
|
||||||
|
"password": "admin"
|
||||||
|
},
|
||||||
|
"mqtt": {
|
||||||
|
"enabled": false,
|
||||||
|
"ip": "",
|
||||||
|
"port": 1883,
|
||||||
|
"base": "",
|
||||||
|
"user": "",
|
||||||
|
"password": "",
|
||||||
|
"heartbeat": false
|
||||||
|
},
|
||||||
|
"ntp": {
|
||||||
|
"server": "pool.ntp.org",
|
||||||
|
"interval": 30,
|
||||||
|
"timezone": 1,
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var page = 1;
|
||||||
|
var haspages;
|
||||||
|
var file = {};
|
||||||
|
var backupstarted = false;
|
||||||
|
var updateurl = "";
|
||||||
|
|
||||||
|
var myespcontent;
|
||||||
|
|
||||||
|
function browserTime() {
|
||||||
|
var d = new Date(0);
|
||||||
|
var c = new Date();
|
||||||
|
var timestamp = Math.floor((c.getTime() / 1000) + ((c.getTimezoneOffset() * 60) * -1));
|
||||||
|
d.setUTCSeconds(timestamp);
|
||||||
|
document.getElementById("rtc").innerHTML = d.toUTCString().slice(0, -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deviceTime() {
|
||||||
|
var t = new Date(0); // The 0 there is the key, which sets the date to the epoch,
|
||||||
|
var devTime = Math.floor(utcSeconds + ((t.getTimezoneOffset() * 60) * -1));
|
||||||
|
t.setUTCSeconds(devTime);
|
||||||
|
document.getElementById("utc").innerHTML = t.toUTCString().slice(0, -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncBrowserTime() {
|
||||||
|
var d = new Date();
|
||||||
|
var timestamp = Math.floor((d.getTime() / 1000));
|
||||||
|
var datatosend = {};
|
||||||
|
datatosend.command = "settime";
|
||||||
|
datatosend.epoch = timestamp;
|
||||||
|
websock.send(JSON.stringify(datatosend));
|
||||||
|
$("#ntp").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function listntp() {
|
||||||
|
websock.send("{\"command\":\"gettime\"}");
|
||||||
|
|
||||||
|
document.getElementById("ntpserver").value = config.ntp.server;
|
||||||
|
document.getElementById("intervals").value = config.ntp.interval;
|
||||||
|
document.getElementById("DropDownTimezone").value = config.ntp.timezone;
|
||||||
|
|
||||||
|
if (config.ntp.enabled) {
|
||||||
|
$("input[name=\"ntpenabled\"][value=\"1\"]").prop("checked", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
browserTime();
|
||||||
|
deviceTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
function revcommit() {
|
||||||
|
document.getElementById("jsonholder").innerText = JSON.stringify(config, null, 2);
|
||||||
|
$("#revcommit").modal("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
function uncommited() {
|
||||||
|
$("#commit").fadeOut(200, function () {
|
||||||
|
$(this).css("background", "gold").fadeIn(1000);
|
||||||
|
});
|
||||||
|
document.getElementById("commit").innerHTML = "<h6>Settings have changed. Click here to review and save.</h6>";
|
||||||
|
$("#commit").click(function () {
|
||||||
|
revcommit();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function custom_uncommited() {
|
||||||
|
document.getElementById("jsonholder2").innerText = JSON.stringify(custom_config.settings, null, 2);
|
||||||
|
$("#custom_revcommit").modal("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
function saventp() {
|
||||||
|
config.ntp.server = document.getElementById("ntpserver").value;
|
||||||
|
config.ntp.interval = parseInt(document.getElementById("intervals").value);
|
||||||
|
config.ntp.timezone = parseInt(document.getElementById("DropDownTimezone").value);
|
||||||
|
|
||||||
|
config.ntp.enabled = false;
|
||||||
|
if (parseInt($("input[name=\"ntpenabled\"]:checked").val()) === 1) {
|
||||||
|
config.ntp.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uncommited();
|
||||||
|
}
|
||||||
|
|
||||||
|
function savegeneral() {
|
||||||
|
var a = document.getElementById("adminpwd").value;
|
||||||
|
if (a === null || a === "") {
|
||||||
|
alert("Administrator password cannot be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config.general.password = a;
|
||||||
|
config.general.hostname = document.getElementById("hostname").value;
|
||||||
|
|
||||||
|
config.general.serial = false;
|
||||||
|
if (parseInt($("input[name=\"serialenabled\"]:checked").val()) === 1) {
|
||||||
|
config.general.serial = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uncommited();
|
||||||
|
}
|
||||||
|
|
||||||
|
function savemqtt() {
|
||||||
|
config.mqtt.enabled = false;
|
||||||
|
if (parseInt($("input[name=\"mqttenabled\"]:checked").val()) === 1) {
|
||||||
|
config.mqtt.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.mqtt.heartbeat = false;
|
||||||
|
if (parseInt($("input[name=\"mqttheartbeat\"]:checked").val()) === 1) {
|
||||||
|
config.mqtt.heartbeat = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.mqtt.ip = document.getElementById("mqttip").value;
|
||||||
|
config.mqtt.port = parseInt(document.getElementById("mqttport").value);
|
||||||
|
config.mqtt.base = document.getElementById("mqttbase").value;
|
||||||
|
config.mqtt.user = document.getElementById("mqttuser").value;
|
||||||
|
config.mqtt.password = document.getElementById("mqttpwd").value;
|
||||||
|
|
||||||
|
uncommited();
|
||||||
|
}
|
||||||
|
|
||||||
|
function savenetwork() {
|
||||||
|
var wmode = 0;
|
||||||
|
if (document.getElementById("inputtohide").style.display === "none") {
|
||||||
|
var b = document.getElementById("ssid");
|
||||||
|
config.network.ssid = b.options[b.selectedIndex].value;
|
||||||
|
} else {
|
||||||
|
config.network.ssid = document.getElementById("inputtohide").value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.getElementById("wmodeap").checked) {
|
||||||
|
wmode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.network.wmode = wmode;
|
||||||
|
config.network.password = document.getElementById("wifipass").value;
|
||||||
|
|
||||||
|
uncommited();
|
||||||
|
}
|
||||||
|
|
||||||
|
var formData = new FormData();
|
||||||
|
|
||||||
|
function inProgress(callback) {
|
||||||
|
$("body").load("myesp.html #progresscontent", function (responseTxt, statusTxt, xhr) {
|
||||||
|
if (statusTxt === "success") {
|
||||||
|
$(".progress").css("height", "40");
|
||||||
|
$(".progress").css("font-size", "xx-large");
|
||||||
|
var i = 0;
|
||||||
|
var prg = setInterval(function () {
|
||||||
|
$(".progress-bar").css("width", i + "%").attr("aria-valuenow", i).html(i + "%");
|
||||||
|
i++;
|
||||||
|
if (i === 101) {
|
||||||
|
clearInterval(prg);
|
||||||
|
var a = document.createElement("a");
|
||||||
|
a.href = "http://" + config.general.hostname + ".local";
|
||||||
|
a.innerText = "Try to reconnect ESP";
|
||||||
|
document.getElementById("reconnect").appendChild(a);
|
||||||
|
document.getElementById("reconnect").style.display = "block";
|
||||||
|
document.getElementById("updateprog").className = "progress-bar progress-bar-success";
|
||||||
|
document.getElementById("updateprog").innerHTML = "Completed";
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
switch (callback) {
|
||||||
|
case "upload":
|
||||||
|
$.ajax({
|
||||||
|
url: "/update",
|
||||||
|
type: "POST",
|
||||||
|
data: formData,
|
||||||
|
processData: false,
|
||||||
|
contentType: false
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "commit":
|
||||||
|
websock.send(JSON.stringify(config));
|
||||||
|
break;
|
||||||
|
case "destroy":
|
||||||
|
websock.send("{\"command\":\"destroy\"}");
|
||||||
|
break;
|
||||||
|
case "restart":
|
||||||
|
websock.send("{\"command\":\"restart\"}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).hide().fadeIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
function commit() {
|
||||||
|
inProgress("commit");
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSTA() {
|
||||||
|
document.getElementById("scanb").style.display = "block";
|
||||||
|
document.getElementById("hidessid").style.display = "block";
|
||||||
|
document.getElementById("hidepasswd").style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAP() {
|
||||||
|
document.getElementById("ssid").style.display = "none";
|
||||||
|
document.getElementById("scanb").style.display = "none";
|
||||||
|
document.getElementById("hidessid").style.display = "none";
|
||||||
|
document.getElementById("hidepasswd").style.display = "none";
|
||||||
|
|
||||||
|
document.getElementById("inputtohide").style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
function listnetwork() {
|
||||||
|
document.getElementById("inputtohide").value = config.network.ssid;
|
||||||
|
document.getElementById("wifipass").value = config.network.password;
|
||||||
|
if (config.network.wmode === 1) {
|
||||||
|
document.getElementById("wmodeap").checked = true;
|
||||||
|
handleAP();
|
||||||
|
} else {
|
||||||
|
document.getElementById("wmodesta").checked = true;
|
||||||
|
handleSTA();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function listgeneral() {
|
||||||
|
document.getElementById("adminpwd").value = config.general.password;
|
||||||
|
document.getElementById("hostname").value = config.general.hostname;
|
||||||
|
|
||||||
|
if (config.general.serial) {
|
||||||
|
$("input[name=\"serialenabled\"][value=\"1\"]").prop("checked", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function listmqtt() {
|
||||||
|
if (config.mqtt.enabled) {
|
||||||
|
$("input[name=\"mqttenabled\"][value=\"1\"]").prop("checked", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.mqtt.heartbeat) {
|
||||||
|
$("input[name=\"mqttheartbeat\"][value=\"1\"]").prop("checked", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("mqttip").value = config.mqtt.ip;
|
||||||
|
document.getElementById("mqttport").value = config.mqtt.port;
|
||||||
|
document.getElementById("mqttbase").value = config.mqtt.base;
|
||||||
|
document.getElementById("mqttuser").value = config.mqtt.user;
|
||||||
|
document.getElementById("mqttpwd").value = config.mqtt.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
function listBSSID() {
|
||||||
|
var select = document.getElementById("ssid");
|
||||||
|
document.getElementById("wifibssid").value = select.options[select.selectedIndex].bssidvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
function listSSID(obj) {
|
||||||
|
var select = document.getElementById("ssid");
|
||||||
|
for (var i = 0; i < obj.list.length; i++) {
|
||||||
|
var x = parseInt(obj.list[i].rssi);
|
||||||
|
var percentage = Math.min(Math.max(2 * (x + 100), 0), 100);
|
||||||
|
var opt = document.createElement("option");
|
||||||
|
opt.value = obj.list[i].ssid;
|
||||||
|
opt.bssidvalue = obj.list[i].bssid;
|
||||||
|
opt.innerHTML = "BSSID: " + obj.list[i].bssid + ", Signal Strength: %" + percentage + ", Network: " + obj.list[i].ssid;
|
||||||
|
select.appendChild(opt);
|
||||||
|
}
|
||||||
|
document.getElementById("scanb").innerHTML = "Re-Scan";
|
||||||
|
listBSSID();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scanWifi() {
|
||||||
|
websock.send("{\"command\":\"scan\"}");
|
||||||
|
document.getElementById("scanb").innerHTML = "...";
|
||||||
|
document.getElementById("inputtohide").style.display = "none";
|
||||||
|
var node = document.getElementById("ssid");
|
||||||
|
node.style.display = "inline";
|
||||||
|
while (node.hasChildNodes()) {
|
||||||
|
node.removeChild(node.lastChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEvents() {
|
||||||
|
websock.send("{\"command\":\"geteventlog\", \"page\":" + page + "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVisible(e) {
|
||||||
|
return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getnextpage(mode) {
|
||||||
|
if (!backupstarted) {
|
||||||
|
document.getElementById("loadpages").innerHTML = "Loading " + page + "/" + haspages;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page < haspages) {
|
||||||
|
page = page + 1;
|
||||||
|
var commandtosend = {};
|
||||||
|
commandtosend.command = mode;
|
||||||
|
commandtosend.page = page;
|
||||||
|
websock.send(JSON.stringify(commandtosend));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function builddata(obj) {
|
||||||
|
data = data.concat(obj.list);
|
||||||
|
}
|
||||||
|
|
||||||
|
function colorStatusbar(ref) {
|
||||||
|
var percentage = ref.style.width.slice(0, -1);
|
||||||
|
if (percentage > 50) { ref.className = "progress-bar progress-bar-success"; } else if (percentage > 25) { ref.className = "progress-bar progress-bar-warning"; } else { ref.class = "progress-bar progress-bar-danger"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function listStats() {
|
||||||
|
|
||||||
|
document.getElementById("uptime").innerHTML = ajaxobj.uptime;
|
||||||
|
|
||||||
|
document.getElementById("heap").innerHTML = ajaxobj.heap + " KB";
|
||||||
|
document.getElementById("heap").style.width = (ajaxobj.heap * 100) / 41 + "%";
|
||||||
|
colorStatusbar(document.getElementById("heap"));
|
||||||
|
|
||||||
|
document.getElementById("flash").innerHTML = ajaxobj.availsize + " KB";
|
||||||
|
document.getElementById("flash").style.width = (ajaxobj.availsize * 100) / (ajaxobj.availsize + ajaxobj.sketchsize) + "%";
|
||||||
|
colorStatusbar(document.getElementById("flash"));
|
||||||
|
|
||||||
|
document.getElementById("spiffs").innerHTML = ajaxobj.availspiffs + " KB";
|
||||||
|
document.getElementById("spiffs").style.width = (ajaxobj.availspiffs * 100) / ajaxobj.spiffssize + "%";
|
||||||
|
colorStatusbar(document.getElementById("spiffs"));
|
||||||
|
|
||||||
|
document.getElementById("ssidstat").innerHTML = ajaxobj.ssid;
|
||||||
|
document.getElementById("ip").innerHTML = ajaxobj.ip;
|
||||||
|
document.getElementById("mac").innerHTML = ajaxobj.mac;
|
||||||
|
document.getElementById("signalstr").innerHTML = ajaxobj.signalstr + " %";
|
||||||
|
document.getElementById("systemload").innerHTML = ajaxobj.systemload + " %";
|
||||||
|
|
||||||
|
if (ajaxobj.mqttconnected) {
|
||||||
|
document.getElementById("mqttconnected").innerHTML = "MQTT is connected";
|
||||||
|
document.getElementById("mqttconnected").className = "label label-success";
|
||||||
|
} else {
|
||||||
|
document.getElementById("mqttconnected").innerHTML = "MQTT is not connected";
|
||||||
|
document.getElementById("mqttconnected").className = "label label-danger";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ajaxobj.mqttheartbeat) {
|
||||||
|
document.getElementById("mqttheartbeat").innerHTML = "MQTT hearbeat is enabled";
|
||||||
|
document.getElementById("mqttheartbeat").className = "label label-success";
|
||||||
|
} else {
|
||||||
|
document.getElementById("mqttheartbeat").innerHTML = "MQTT hearbeat is disabled";
|
||||||
|
document.getElementById("mqttheartbeat").className = "label label-primary";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContent(contentname) {
|
||||||
|
$("#dismiss").click();
|
||||||
|
$(".overlay").fadeOut().promise().done(function () {
|
||||||
|
var content = $(contentname).html();
|
||||||
|
$("#ajaxcontent").html(content).promise().done(function () {
|
||||||
|
switch (contentname) {
|
||||||
|
case "#statuscontent":
|
||||||
|
listStats();
|
||||||
|
break;
|
||||||
|
case "#backupcontent":
|
||||||
|
break;
|
||||||
|
case "#ntpcontent":
|
||||||
|
listntp();
|
||||||
|
break;
|
||||||
|
case "#mqttcontent":
|
||||||
|
listmqtt();
|
||||||
|
break;
|
||||||
|
case "#generalcontent":
|
||||||
|
listgeneral();
|
||||||
|
break;
|
||||||
|
case "#networkcontent":
|
||||||
|
listnetwork();
|
||||||
|
break;
|
||||||
|
case "#eventcontent":
|
||||||
|
page = 1;
|
||||||
|
data = [];
|
||||||
|
getEvents();
|
||||||
|
break;
|
||||||
|
case "#customcontent":
|
||||||
|
listcustom();
|
||||||
|
break;
|
||||||
|
case "#custom_statuscontent":
|
||||||
|
var version = "version " + ajaxobj.version;
|
||||||
|
$("#mainver").text(version);
|
||||||
|
$("#customname").text(ajaxobj.customname);
|
||||||
|
var customname2 = " " + ajaxobj.customname;
|
||||||
|
$("#customname2").text(customname2);
|
||||||
|
var elem = document.getElementById("helpurl");
|
||||||
|
elem.setAttribute("href", ajaxobj.helpurl);
|
||||||
|
updateurl = ajaxobj.updateurl;
|
||||||
|
listCustomStats();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$("[data-toggle=\"popover\"]").popover({
|
||||||
|
container: "body"
|
||||||
|
});
|
||||||
|
$(this).hide().fadeIn();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function backupset() {
|
||||||
|
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(config, null, 2));
|
||||||
|
var dlAnchorElem = document.getElementById("downloadSet");
|
||||||
|
dlAnchorElem.setAttribute("href", dataStr);
|
||||||
|
dlAnchorElem.setAttribute("download", "system_config.json");
|
||||||
|
dlAnchorElem.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function backupCustomSet() {
|
||||||
|
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(custom_config, null, 2));
|
||||||
|
var dlAnchorElem = document.getElementById("downloadCustomSet");
|
||||||
|
dlAnchorElem.setAttribute("href", dataStr);
|
||||||
|
dlAnchorElem.setAttribute("download", "custom_config.json");
|
||||||
|
dlAnchorElem.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreSet() {
|
||||||
|
var input = document.getElementById("restoreSet");
|
||||||
|
var reader = new FileReader();
|
||||||
|
if ("files" in input) {
|
||||||
|
if (input.files.length === 0) {
|
||||||
|
alert("You did not select file to restore!");
|
||||||
|
} else {
|
||||||
|
reader.onload = function () {
|
||||||
|
var json;
|
||||||
|
try {
|
||||||
|
json = JSON.parse(reader.result);
|
||||||
|
} catch (e) {
|
||||||
|
alert("Not a valid backup file!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (json.command === "configfile") {
|
||||||
|
var x = confirm("File seems to be valid, do you wish to continue?");
|
||||||
|
if (x) {
|
||||||
|
config = json;
|
||||||
|
uncommited();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(input.files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreCustomSet() {
|
||||||
|
var input = document.getElementById("restoreCustomSet");
|
||||||
|
var reader = new FileReader();
|
||||||
|
if ("files" in input) {
|
||||||
|
if (input.files.length === 0) {
|
||||||
|
alert("You did not select file to restore!");
|
||||||
|
} else {
|
||||||
|
reader.onload = function () {
|
||||||
|
var json;
|
||||||
|
try {
|
||||||
|
json = JSON.parse(reader.result);
|
||||||
|
} catch (e) {
|
||||||
|
alert("Not a valid backup file!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (json.command === "custom_configfile") {
|
||||||
|
var x = confirm("File seems to be valid, do you wish to continue?");
|
||||||
|
if (x) {
|
||||||
|
custom_config = json;
|
||||||
|
custom_uncommited();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(input.files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function twoDigits(value) {
|
||||||
|
if (value < 10) {
|
||||||
|
return "0" + value;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initEventTable() {
|
||||||
|
var newlist = [];
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
var dup = JSON.parse(data[i]);
|
||||||
|
dup.uid = i;
|
||||||
|
newlist[i] = {};
|
||||||
|
newlist[i].options = {};
|
||||||
|
newlist[i].value = {};
|
||||||
|
newlist[i].value = dup;
|
||||||
|
var c = dup.type;
|
||||||
|
switch (c) {
|
||||||
|
case "WARN":
|
||||||
|
newlist[i].options.classes = "warning";
|
||||||
|
break;
|
||||||
|
case "INFO":
|
||||||
|
newlist[i].options.classes = "info";
|
||||||
|
break;
|
||||||
|
case "ERRO":
|
||||||
|
newlist[i].options.classes = "danger";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
jQuery(function ($) {
|
||||||
|
window.FooTable.init("#eventtable", {
|
||||||
|
columns: [{
|
||||||
|
"name": "uid",
|
||||||
|
"title": "ID",
|
||||||
|
"type": "text",
|
||||||
|
"sorted": true,
|
||||||
|
"direction": "DESC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"title": "Event Type",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "src",
|
||||||
|
"title": "Source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "desc",
|
||||||
|
"title": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "data",
|
||||||
|
"title": "Additional Data",
|
||||||
|
"breakpoints": "xs sm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "time",
|
||||||
|
"title": "Date",
|
||||||
|
"parser": function (value) {
|
||||||
|
if (value < 1563300000) {
|
||||||
|
return "(" + value + ")";
|
||||||
|
} else {
|
||||||
|
var comp = new Date();
|
||||||
|
value = Math.floor(value + ((comp.getTimezoneOffset() * 60) * -1));
|
||||||
|
var vuepoch = new Date(value * 1000);
|
||||||
|
var formatted = vuepoch.getUTCFullYear() +
|
||||||
|
"-" + twoDigits(vuepoch.getUTCMonth() + 1) +
|
||||||
|
"-" + twoDigits(vuepoch.getUTCDate()) +
|
||||||
|
" " + twoDigits(vuepoch.getUTCHours()) +
|
||||||
|
":" + twoDigits(vuepoch.getUTCMinutes()) +
|
||||||
|
":" + twoDigits(vuepoch.getUTCSeconds());
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"breakpoints": "xs sm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
rows: newlist
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function restartESP() {
|
||||||
|
inProgress("restart");
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextIsNotJson = false;
|
||||||
|
|
||||||
|
function socketMessageListener(evt) {
|
||||||
|
var obj = JSON.parse(evt.data);
|
||||||
|
if (obj.hasOwnProperty("command")) {
|
||||||
|
switch (obj.command) {
|
||||||
|
case "status":
|
||||||
|
ajaxobj = obj;
|
||||||
|
getContent("#statuscontent");
|
||||||
|
break;
|
||||||
|
case "custom_settings":
|
||||||
|
ajaxobj = obj;
|
||||||
|
break;
|
||||||
|
case "custom_status":
|
||||||
|
ajaxobj = obj;
|
||||||
|
getContent("#custom_statuscontent");
|
||||||
|
break;
|
||||||
|
case "eventlist":
|
||||||
|
haspages = obj.haspages;
|
||||||
|
if (haspages === 0) {
|
||||||
|
document.getElementById("loading-img").style.display = "none";
|
||||||
|
initEventTable();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
builddata(obj);
|
||||||
|
break;
|
||||||
|
case "gettime":
|
||||||
|
utcSeconds = obj.epoch;
|
||||||
|
timezone = obj.timezone;
|
||||||
|
deviceTime();
|
||||||
|
break;
|
||||||
|
case "ssidlist":
|
||||||
|
listSSID(obj);
|
||||||
|
break;
|
||||||
|
case "configfile":
|
||||||
|
config = obj;
|
||||||
|
break;
|
||||||
|
case "custom_configfile":
|
||||||
|
custom_config = obj;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.hasOwnProperty("resultof")) {
|
||||||
|
switch (obj.resultof) {
|
||||||
|
case "eventlist":
|
||||||
|
if (page < haspages && obj.result === true) {
|
||||||
|
getnextpage("geteventlog");
|
||||||
|
} else if (page === haspages) {
|
||||||
|
initEventTable();
|
||||||
|
document.getElementById("loading-img").style.display = "none";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearevent() {
|
||||||
|
websock.send("{\"command\":\"clearevent\"}");
|
||||||
|
$("#eventlog").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareDestroy() {
|
||||||
|
if (config.general.hostname === document.getElementById("compare").value) {
|
||||||
|
$("#destroybtn").prop("disabled", false);
|
||||||
|
} else { $("#destroybtn").prop("disabled", true); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroy() {
|
||||||
|
inProgress("destroy");
|
||||||
|
}
|
||||||
|
|
||||||
|
function restart() {
|
||||||
|
inProgress("restart");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#dismiss, .overlay").on("click", function () {
|
||||||
|
$("#sidebar").removeClass("active");
|
||||||
|
$(".overlay").fadeOut();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#sidebarCollapse").on("click", function () {
|
||||||
|
$("#sidebar").addClass("active");
|
||||||
|
$(".overlay").fadeIn();
|
||||||
|
$(".collapse.in").toggleClass("in");
|
||||||
|
$("a[aria-expanded=true]").attr("aria-expanded", "false");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#custom_status").click(function () {
|
||||||
|
websock.send("{\"command\":\"custom_status\"}");
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#status").click(function () {
|
||||||
|
websock.send("{\"command\":\"status\"}");
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#custom").click(function () { getContent("#customcontent"); return false; });
|
||||||
|
|
||||||
|
$("#network").on("click", (function () { getContent("#networkcontent"); return false; }));
|
||||||
|
$("#general").click(function () { getContent("#generalcontent"); return false; });
|
||||||
|
$("#mqtt").click(function () { getContent("#mqttcontent"); return false; });
|
||||||
|
$("#ntp").click(function () { getContent("#ntpcontent"); return false; });
|
||||||
|
$("#backup").click(function () { getContent("#backupcontent"); return false; });
|
||||||
|
$("#reset").click(function () { $("#destroy").modal("show"); return false; });
|
||||||
|
$("#restart").click(function () { $("#reboot").modal("show"); return false; });
|
||||||
|
$("#eventlog").click(function () { getContent("#eventcontent"); return false; });
|
||||||
|
|
||||||
|
$(".noimp").on("click", function () {
|
||||||
|
$("#noimp").modal("show");
|
||||||
|
});
|
||||||
|
|
||||||
|
window.FooTable.MyFiltering = window.FooTable.Filtering.extend({
|
||||||
|
construct: function (instance) {
|
||||||
|
this._super(instance);
|
||||||
|
this.acctypes = ["1", "99", "0"];
|
||||||
|
this.acctypesstr = ["Always", "Admin", "Disabled"];
|
||||||
|
this.def = "Access Type";
|
||||||
|
this.$acctype = null;
|
||||||
|
},
|
||||||
|
$create: function () {
|
||||||
|
this._super();
|
||||||
|
var self = this,
|
||||||
|
$formgrp = $("<div/>", {
|
||||||
|
"class": "form-group"
|
||||||
|
})
|
||||||
|
.append($("<label/>", {
|
||||||
|
"class": "sr-only",
|
||||||
|
text: "Status"
|
||||||
|
}))
|
||||||
|
.prependTo(self.$form);
|
||||||
|
|
||||||
|
self.$acctype = $("<select/>", {
|
||||||
|
"class": "form-control"
|
||||||
|
})
|
||||||
|
.on("change", {
|
||||||
|
self: self
|
||||||
|
}, self._onStatusDropdownChanged)
|
||||||
|
.append($("<option/>", {
|
||||||
|
text: self.def
|
||||||
|
}))
|
||||||
|
.appendTo($formgrp);
|
||||||
|
|
||||||
|
$.each(self.acctypes, function (i, acctype) {
|
||||||
|
self.$acctype.append($("<option/>").text(self.acctypesstr[i]).val(self.acctypes[i]));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_onStatusDropdownChanged: function (e) {
|
||||||
|
var self = e.data.self,
|
||||||
|
selected = $(this).val();
|
||||||
|
if (selected !== self.def) {
|
||||||
|
self.addFilter("acctype", selected, ["acctype"]);
|
||||||
|
} else {
|
||||||
|
self.removeFilter("acctype");
|
||||||
|
}
|
||||||
|
self.filter();
|
||||||
|
},
|
||||||
|
draw: function () {
|
||||||
|
this._super();
|
||||||
|
var acctype = this.find("acctype");
|
||||||
|
if (acctype instanceof window.FooTable.Filter) {
|
||||||
|
this.$acctype.val(acctype.query.val());
|
||||||
|
} else {
|
||||||
|
this.$acctype.val(this.def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var xDown = null;
|
||||||
|
var yDown = null;
|
||||||
|
|
||||||
|
function handleTouchStart(evt) {
|
||||||
|
xDown = evt.touches[0].clientX;
|
||||||
|
yDown = evt.touches[0].clientY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTouchMove(evt) {
|
||||||
|
if (!xDown || !yDown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xUp = evt.touches[0].clientX;
|
||||||
|
var yUp = evt.touches[0].clientY;
|
||||||
|
|
||||||
|
var xDiff = xDown - xUp;
|
||||||
|
var yDiff = yDown - yUp;
|
||||||
|
|
||||||
|
if (Math.abs(xDiff) > Math.abs(yDiff)) { /*most significant*/
|
||||||
|
if (xDiff > 0) {
|
||||||
|
$("#dismiss").click();
|
||||||
|
} else {
|
||||||
|
$("#sidebarCollapse").click();
|
||||||
|
/* right swipe */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (yDiff > 0) {
|
||||||
|
/* up swipe */
|
||||||
|
} else {
|
||||||
|
/* down swipe */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* reset values */
|
||||||
|
xDown = null;
|
||||||
|
yDown = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
jQuery.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: "/login",
|
||||||
|
async: false,
|
||||||
|
username: "logmeout",
|
||||||
|
password: "logmeout",
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
// If we don"t get an error, we actually got an error as we expect an 401!
|
||||||
|
})
|
||||||
|
.fail(function () {
|
||||||
|
// We expect to get an 401 Unauthorized error! In this case we are successfully
|
||||||
|
// logged out and we redirect the user.
|
||||||
|
document.location = "index.html";
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectWS() {
|
||||||
|
if (window.location.protocol === "https:") {
|
||||||
|
wsUri = "wss://" + window.location.host + "/ws";
|
||||||
|
} else if (window.location.protocol === "file:") {
|
||||||
|
wsUri = "ws://" + "localhost" + "/ws";
|
||||||
|
}
|
||||||
|
|
||||||
|
websock = new WebSocket(wsUri);
|
||||||
|
websock.addEventListener("message", socketMessageListener);
|
||||||
|
|
||||||
|
websock.onopen = function (evt) {
|
||||||
|
websock.send("{\"command\":\"getconf\"}");
|
||||||
|
websock.send("{\"command\":\"custom_status\"}");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function upload() {
|
||||||
|
formData.append("bin", $("#binform")[0].files[0]);
|
||||||
|
inProgress("upload");
|
||||||
|
}
|
||||||
|
|
||||||
|
function login() {
|
||||||
|
if (document.getElementById("password").value === "neo") {
|
||||||
|
$("#signin").modal("hide");
|
||||||
|
connectWS();
|
||||||
|
} else {
|
||||||
|
var username = "admin";
|
||||||
|
var password = document.getElementById("password").value;
|
||||||
|
var url = "/login";
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("get", url, true, username, password);
|
||||||
|
xhr.onload = function (e) {
|
||||||
|
if (xhr.readyState === 4) {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
$("#signin").modal("hide");
|
||||||
|
connectWS();
|
||||||
|
} else {
|
||||||
|
alert("Incorrect password!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLatestReleaseInfo() {
|
||||||
|
$.getJSON(updateurl).done(function (release) {
|
||||||
|
var asset = release.assets[0];
|
||||||
|
var downloadCount = 0;
|
||||||
|
for (var i = 0; i < release.assets.length; i++) {
|
||||||
|
downloadCount += release.assets[i].download_count;
|
||||||
|
}
|
||||||
|
var oneHour = 60 * 60 * 1000;
|
||||||
|
var oneDay = 24 * oneHour;
|
||||||
|
var dateDiff = new Date() - new Date(release.published_at);
|
||||||
|
var timeAgo;
|
||||||
|
if (dateDiff < oneDay) {
|
||||||
|
timeAgo = (dateDiff / oneHour).toFixed(1) + " hours ago";
|
||||||
|
} else {
|
||||||
|
timeAgo = (dateDiff / oneDay).toFixed(1) + " days ago";
|
||||||
|
}
|
||||||
|
|
||||||
|
var releaseInfo = release.name + " was updated " + timeAgo + " and downloaded " + downloadCount.toLocaleString() + " times.";
|
||||||
|
$("#downloadupdate").attr("href", asset.browser_download_url);
|
||||||
|
$("#releasehead").text(releaseInfo);
|
||||||
|
$("#releasebody").text(release.body);
|
||||||
|
$("#releaseinfo").fadeIn("slow");
|
||||||
|
$("#versionhead").text(version);
|
||||||
|
}).error(function () { $("#onlineupdate").html("<h5>Couldn't get release details. Make sure there is an Internet connection.</h5>"); });
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#update").on("shown.bs.modal", function (e) {
|
||||||
|
getLatestReleaseInfo();
|
||||||
|
});
|
||||||
|
|
||||||
|
function allowUpload() {
|
||||||
|
$("#upbtn").prop("disabled", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
myespcontent = document.createElement("div");
|
||||||
|
myespcontent.id = "mastercontent";
|
||||||
|
myespcontent.style.display = "none";
|
||||||
|
document.body.appendChild(myespcontent);
|
||||||
|
$("#signin").on("shown.bs.modal", function () {
|
||||||
|
$("#password").focus().select();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#mastercontent").load("myesp.html", function (responseTxt, statusTxt, xhr) {
|
||||||
|
if (statusTxt === "success") {
|
||||||
|
$("#signin").modal({ backdrop: "static", keyboard: false });
|
||||||
|
$("[data-toggle=\"popover\"]").popover({
|
||||||
|
container: "body"
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshEMS() {
|
||||||
|
websock.send("{\"command\":\"custom_status\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("touchstart", handleTouchStart, false);
|
||||||
|
document.addEventListener("touchmove", handleTouchMove, false);
|
||||||
19
tools/webfilesbuilder/gulp.js
Normal file
19
tools/webfilesbuilder/gulp.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/* eslint-disable no-path-concat */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var path = require('path');
|
||||||
|
process.env.NODE_PATH = (process.env.NODE_PATH || '').split(path.delimiter)
|
||||||
|
.filter((p) => p).concat(__dirname + '/node_modules').join(path.delimiter);
|
||||||
|
require('module')._initPaths(); // eslint-disable-line no-underscore-dangle
|
||||||
|
|
||||||
|
require('gulp');
|
||||||
|
require('gulp-concat');
|
||||||
|
require('gulp/bin/gulp.js');
|
||||||
|
require('fs');
|
||||||
|
require('gulp-gzip');
|
||||||
|
require('gulp-flatmap');
|
||||||
|
require('path');
|
||||||
|
require('gulp-htmlmin');
|
||||||
|
require('gulp-uglify');
|
||||||
|
require('pump');
|
||||||
9
tools/webfilesbuilder/gulp.meta.js
Normal file
9
tools/webfilesbuilder/gulp.meta.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
return {
|
||||||
|
packages: ['gulp-concat', 'gulp-htmlmin', 'gulp-flatmap', 'gulp-gzip', 'gulp-uglify', 'fs', 'path', 'pump'],
|
||||||
|
deployFiles: ['gulpfile.js'],
|
||||||
|
take: 'last-line'
|
||||||
|
};
|
||||||
|
};
|
||||||
232
tools/webfilesbuilder/gulpfile.js
Normal file
232
tools/webfilesbuilder/gulpfile.js
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
var gulp = require('gulp');
|
||||||
|
var fs = require('fs');
|
||||||
|
var concat = require('gulp-concat');
|
||||||
|
var gzip = require('gulp-gzip');
|
||||||
|
var flatmap = require('gulp-flatmap');
|
||||||
|
var path = require('path');
|
||||||
|
var htmlmin = require('gulp-htmlmin');
|
||||||
|
var uglify = require('gulp-uglify');
|
||||||
|
var pump = require('pump');
|
||||||
|
|
||||||
|
|
||||||
|
gulp.task('myespjs-concat', function () {
|
||||||
|
return gulp.src(['../../src/websrc/myesp.js', '../../src/custom.js'])
|
||||||
|
.pipe(concat({
|
||||||
|
path: 'myesp.js',
|
||||||
|
stat: {
|
||||||
|
mode: 0666
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/js'))
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('myespjsminify', ["myespjs-concat"], function (cb) {
|
||||||
|
pump([
|
||||||
|
gulp.src('../../src/websrc/temp/js/myesp.js'),
|
||||||
|
uglify(),
|
||||||
|
gulp.dest('../../src/websrc/temp/js/ugly'),
|
||||||
|
],
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("myespjsgz", ["myespjsminify"], function () {
|
||||||
|
return gulp.src("../../src/websrc/temp/js/ugly/myesp.js")
|
||||||
|
.pipe(gzip({
|
||||||
|
append: true
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/gzipped/js'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('myespjsgzh', ["myespjsgz"], function () {
|
||||||
|
var source = "../../src/websrc/temp/gzipped/js/" + "myesp.js.gz";
|
||||||
|
var destination = "../../src/webh/" + "myesp.js.gz.h";
|
||||||
|
|
||||||
|
var wstream = fs.createWriteStream(destination);
|
||||||
|
wstream.on('error', function (err) {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
var data = fs.readFileSync(source);
|
||||||
|
|
||||||
|
wstream.write('#define myesp_js_gz_len ' + data.length + '\n');
|
||||||
|
wstream.write('const uint8_t myesp_js_gz[] PROGMEM = {')
|
||||||
|
|
||||||
|
for (i = 0; i < data.length; i++) {
|
||||||
|
if (i % 1000 == 0) wstream.write("\n");
|
||||||
|
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||||
|
if (i < data.length - 1) wstream.write(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
wstream.write('\n};')
|
||||||
|
wstream.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("scripts", ["scripts-concat"], function () {
|
||||||
|
|
||||||
|
var source = "../../src/websrc/temp/gzipped/js/" + "required.js.gz";
|
||||||
|
var destination = "../../src/webh/" + "required.js.gz.h";
|
||||||
|
|
||||||
|
var wstream = fs.createWriteStream(destination);
|
||||||
|
wstream.on('error', function (err) {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
var data = fs.readFileSync(source);
|
||||||
|
|
||||||
|
wstream.write('#define required_js_gz_len ' + data.length + '\n');
|
||||||
|
wstream.write('const uint8_t required_js_gz[] PROGMEM = {')
|
||||||
|
|
||||||
|
for (i = 0; i < data.length; i++) {
|
||||||
|
if (i % 1000 == 0) wstream.write("\n");
|
||||||
|
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||||
|
if (i < data.length - 1) wstream.write(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
wstream.write('\n};')
|
||||||
|
wstream.end();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('scripts-concat', ["myespjsgzh"], function () {
|
||||||
|
return gulp.src(['../../src/websrc/3rdparty/js/jquery-1.12.4.min.js', '../../src/websrc/3rdparty/js/bootstrap-3.3.7.min.js', '../../src/websrc/3rdparty/js/footable-3.1.6.min.js'])
|
||||||
|
.pipe(concat({
|
||||||
|
path: 'required.js',
|
||||||
|
stat: {
|
||||||
|
mode: 0666
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/js/'))
|
||||||
|
.pipe(gzip({
|
||||||
|
append: true
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/gzipped/js/'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('styles-concat', function () {
|
||||||
|
return gulp.src(['../../src/websrc/3rdparty/css/bootstrap-3.3.7.min.css', '../../src/websrc/3rdparty/css/footable.bootstrap-3.1.6.min.css', '../../src/websrc/3rdparty/css/sidebar.css'])
|
||||||
|
.pipe(concat({
|
||||||
|
path: 'required.css',
|
||||||
|
stat: {
|
||||||
|
mode: 0666
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/css/'))
|
||||||
|
.pipe(gzip({
|
||||||
|
append: true
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/gzipped/css/'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("styles", ["styles-concat"], function () {
|
||||||
|
|
||||||
|
var source = "../../src/websrc/temp/gzipped/css/" + "required.css.gz";
|
||||||
|
var destination = "../../src/webh/" + "required.css.gz.h";
|
||||||
|
|
||||||
|
var wstream = fs.createWriteStream(destination);
|
||||||
|
wstream.on('error', function (err) {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
var data = fs.readFileSync(source);
|
||||||
|
|
||||||
|
wstream.write('#define required_css_gz_len ' + data.length + '\n');
|
||||||
|
wstream.write('const uint8_t required_css_gz[] PROGMEM = {')
|
||||||
|
|
||||||
|
for (i = 0; i < data.length; i++) {
|
||||||
|
if (i % 1000 == 0) wstream.write("\n");
|
||||||
|
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||||
|
if (i < data.length - 1) wstream.write(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
wstream.write('\n};')
|
||||||
|
wstream.end();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("fontgz", function () {
|
||||||
|
return gulp.src("../../src/websrc/3rdparty/fonts/*.*")
|
||||||
|
.pipe(gulp.dest("../../src/websrc/temp/fonts/"))
|
||||||
|
.pipe(gzip({
|
||||||
|
append: true
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/gzipped/fonts/'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("fonts", ["fontgz"], function () {
|
||||||
|
return gulp.src("../../src/websrc/temp/gzipped/fonts/*.*")
|
||||||
|
.pipe(flatmap(function (stream, file) {
|
||||||
|
var filename = path.basename(file.path);
|
||||||
|
var wstream = fs.createWriteStream("../../src/webh/" + filename + ".h");
|
||||||
|
wstream.on("error", function (err) {
|
||||||
|
gutil.log(err);
|
||||||
|
});
|
||||||
|
var data = file.contents;
|
||||||
|
wstream.write("#define " + filename.replace(/\.|-/g, "_") + "_len " + data.length + "\n");
|
||||||
|
wstream.write("const uint8_t " + filename.replace(/\.|-/g, "_") + "[] PROGMEM = {")
|
||||||
|
|
||||||
|
for (i = 0; i < data.length; i++) {
|
||||||
|
if (i % 1000 == 0) wstream.write("\n");
|
||||||
|
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||||
|
if (i < data.length - 1) wstream.write(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
wstream.write("\n};")
|
||||||
|
wstream.end();
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('html-concat', function () {
|
||||||
|
return gulp.src(['../../src/websrc/myesp.htm', '../../src/custom.htm'])
|
||||||
|
.pipe(concat({
|
||||||
|
path: 'myesp.html',
|
||||||
|
stat: {
|
||||||
|
mode: 0666
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.pipe(htmlmin({ collapseWhitespace: true, minifyJS: true }))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/'))
|
||||||
|
.pipe(gzip({
|
||||||
|
append: true
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/gzipped/'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('htmlsprep', ["html-concat"], function () {
|
||||||
|
return gulp.src('../../src/websrc/index.html')
|
||||||
|
.pipe(htmlmin({ collapseWhitespace: true, minifyJS: true }))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/'))
|
||||||
|
.pipe(gzip({
|
||||||
|
append: true
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('../../src/websrc/temp/gzipped/'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("htmls", ["htmlsprep"], function () {
|
||||||
|
return gulp.src("../../src/websrc/temp/gzipped/*.gz")
|
||||||
|
.pipe(flatmap(function (stream, file) {
|
||||||
|
var filename = path.basename(file.path);
|
||||||
|
var wstream = fs.createWriteStream("../../src/webh/" + filename + ".h");
|
||||||
|
wstream.on("error", function (err) {
|
||||||
|
gutil.log(err);
|
||||||
|
});
|
||||||
|
var data = file.contents;
|
||||||
|
wstream.write("#define " + filename.replace(/\.|-/g, "_") + "_len " + data.length + "\n");
|
||||||
|
wstream.write("const uint8_t " + filename.replace(/\.|-/g, "_") + "[] PROGMEM = {")
|
||||||
|
|
||||||
|
for (i = 0; i < data.length; i++) {
|
||||||
|
if (i % 1000 == 0) wstream.write("\n");
|
||||||
|
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||||
|
if (i < data.length - 1) wstream.write(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
wstream.write("\n};")
|
||||||
|
wstream.end();
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('default', ['scripts', 'styles', "fonts", "htmls"]);
|
||||||
2715
tools/webfilesbuilder/package-lock.json
generated
Normal file
2715
tools/webfilesbuilder/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
tools/webfilesbuilder/package.json
Normal file
24
tools/webfilesbuilder/package.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "uglifier",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Combine all js and css files into one and gzip them for the myESP project",
|
||||||
|
"main": "unglify.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "gulp"
|
||||||
|
},
|
||||||
|
"author": "proddy",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"dependencies": {
|
||||||
|
"gulp-concat": "^2.6.1",
|
||||||
|
"gulp-flatmap": "^1.0.2",
|
||||||
|
"gulp-gzip": "^1.4.2",
|
||||||
|
"gulp-htmlmin": "^4.0.0",
|
||||||
|
"gulp-uglify": "^3.0.2",
|
||||||
|
"pump": "^3.0.0"
|
||||||
|
},
|
||||||
|
"bin": "node_modules\\gulp\\bin\\gulp.js",
|
||||||
|
"devDependencies": {
|
||||||
|
"gulp": "^3.9.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
27
tools/wsemulator/package-lock.json
generated
Normal file
27
tools/wsemulator/package-lock.json
generated
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "wsemulator",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"async-limiter": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
|
||||||
|
"requires": {
|
||||||
|
"async-limiter": "~1.0.0",
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
tools/wsemulator/package.json
Normal file
14
tools/wsemulator/package.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "wsemulator",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Emulate websocket communication ",
|
||||||
|
"main": "wserver.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "proddy",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"dependencies": {
|
||||||
|
"ws": "^4.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
15
tools/wsemulator/run.ps1
Normal file
15
tools/wsemulator/run.ps1
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
$ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path
|
||||||
|
|
||||||
|
# build web
|
||||||
|
$webfilesbuilder = $ScriptDir + "\..\webfilesbuilder"
|
||||||
|
node $webfilesbuilder\node_modules\gulp\bin\gulp.js --cwd $webfilesbuilder
|
||||||
|
|
||||||
|
# run chrome
|
||||||
|
$pathToChrome = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
|
||||||
|
$tempFolder = '--user-data-dir=c:\temp'
|
||||||
|
$startmode = '--remote-debugging-port=9222 --disable-web-security --disable-gpu'
|
||||||
|
$startPage = $ScriptDir + "\..\..\src\websrc\temp\index.html"
|
||||||
|
Start-Process -FilePath $pathToChrome -ArgumentList $tempFolder, $startmode, $startPage
|
||||||
|
|
||||||
|
# run ws fake server
|
||||||
|
node wserver.js
|
||||||
7
tools/wsemulator/run.sh
Normal file
7
tools/wsemulator/run.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
node $PWD/../webfilesbuilder/node_modules/gulp/bin/gulp.js --cwd $PWD/../webfilesbuilder
|
||||||
|
|
||||||
|
open -na Google\ Chrome --args --disable-web-security --remote-debugging-port=9222 --user-data-dir="/tmp/chrome_dev" $PWD/../../src/websrc/temp/index.html
|
||||||
|
|
||||||
|
node wserver.js
|
||||||
250
tools/wsemulator/wserver.js
Normal file
250
tools/wsemulator/wserver.js
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
console.log("[INFO] Starting MyESP WebSocket Emulation Server");
|
||||||
|
|
||||||
|
const WebSocket = require("ws");
|
||||||
|
|
||||||
|
console.log("[INFO] You can connect to ws://localhost or load URL .../src/websrc/temp/index.html");
|
||||||
|
console.log("[INFO] Password is 'neo'");
|
||||||
|
|
||||||
|
const wss = new WebSocket.Server({
|
||||||
|
port: 80
|
||||||
|
});
|
||||||
|
|
||||||
|
wss.broadcast = function broadcast(data) {
|
||||||
|
wss.clients.forEach(function each(client) {
|
||||||
|
if (client.readyState === WebSocket.OPEN) {
|
||||||
|
client.send(JSON.stringify(data));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var networks = {
|
||||||
|
"command": "ssidlist",
|
||||||
|
"list": [{
|
||||||
|
"ssid": "Company's Network",
|
||||||
|
"bssid": "4c:f4:39:a1:41",
|
||||||
|
"rssi": "-84"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ssid": "Home Router",
|
||||||
|
"bssid": "8a:e6:63:a8:15",
|
||||||
|
"rssi": "-42"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ssid": "SSID Shown Here",
|
||||||
|
"bssid": "8a:f5:86:c3:12",
|
||||||
|
"rssi": "-77"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ssid": "Great Wall of WPA",
|
||||||
|
"bssid": "9c:f1:90:c5:15",
|
||||||
|
"rssi": "-80"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ssid": "Not Internet",
|
||||||
|
"bssid": "8c:e4:57:c5:16",
|
||||||
|
"rssi": "-87"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventlog = {
|
||||||
|
"command": "eventlist",
|
||||||
|
"page": 1,
|
||||||
|
"haspages": 1,
|
||||||
|
"list": [
|
||||||
|
"{ \"type\": \"WARN\", \"src\": \"sys\", \"desc\": \"Event log cleared!\", \"data\": \"\", \"time\": 1563371160 }",
|
||||||
|
"{ \"type\": \"WARN\", \"src\": \"sys\", \"desc\": \"Event log cleared!\", \"data\": \"\", \"time\": 1563371160 }",
|
||||||
|
"{ \"type\": \"INFO\", \"src\": \"wifi\", \"desc\": \"WiFi is connected\", \"data\": \"SMC\", \"time\": 13 }",
|
||||||
|
"{ \"type\": \"INFO\", \"src\": \"sys\", \"desc\": \"System setup completed, running\", \"data\": \"\", \"time\": 13 }",
|
||||||
|
"{ \"type\": \"INFO\", \"src\": \"wifi\", \"desc\": \"WiFi is connected\", \"data\": \"SMC\", \"time\": 13 }",
|
||||||
|
"{ \"type\": \"INFO\", \"src\": \"sys\", \"desc\": \"System setup completed, running\", \"data\": \"\", \"time\": 13 }",
|
||||||
|
"{ \"type\": \"WARN\", \"src\": \"websrv\", \"desc\": \"New login attempt\", \"data\": \"\", \"time\": 1563371160 }",
|
||||||
|
"{ \"type\": \"INFO\", \"src\": \"websrv\", \"desc\": \"Login success!\", \"data\": \"\", \"time\": 1563371160 }",
|
||||||
|
"{ \"type\": \"INFO\", \"src\": \"wifi\", \"desc\": \"WiFi is connected\", \"data\": \"SMC\", \"time\": 13 }",
|
||||||
|
"{ \"type\": \"INFO\", \"src\": \"sys\", \"desc\": \"System setup completed, running\", \"data\": \"\", \"time\": 13 }",
|
||||||
|
"{ \"type\": \"WARN\", \"src\": \"websrv\", \"desc\": \"New login attempt\", \"data\": \"\", \"time\": 1563371160 }"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
var configfile = {
|
||||||
|
"command": "configfile",
|
||||||
|
"network": {
|
||||||
|
"ssid": "myssid",
|
||||||
|
"wmode": "0",
|
||||||
|
"password": "password"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"hostname": "myesp",
|
||||||
|
"password": "admin",
|
||||||
|
"serial": true
|
||||||
|
},
|
||||||
|
"mqtt": {
|
||||||
|
"enabled": false,
|
||||||
|
"ip": "ip",
|
||||||
|
"port": "port",
|
||||||
|
"base": "base",
|
||||||
|
"user": "user",
|
||||||
|
"password": "password",
|
||||||
|
"heartbeat": false
|
||||||
|
},
|
||||||
|
"ntp": {
|
||||||
|
"server": "pool.ntp.org",
|
||||||
|
"interval": "30",
|
||||||
|
"timezone": "0",
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var custom_configfile = {
|
||||||
|
"command": "custom_configfile",
|
||||||
|
"settings": {
|
||||||
|
"led": true,
|
||||||
|
"led_gpio": 2,
|
||||||
|
"dallas_gpio": 14,
|
||||||
|
"dallas_parasite": false,
|
||||||
|
"listen_mode": false,
|
||||||
|
"shower_timer": false,
|
||||||
|
"shower_alert": false,
|
||||||
|
"publish_time": 120,
|
||||||
|
"heating_circuit": 1,
|
||||||
|
"tx_mode": 2
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function sendEventLog() {
|
||||||
|
wss.broadcast(eventlog);
|
||||||
|
var res = {
|
||||||
|
"command": "result",
|
||||||
|
"resultof": "eventlist",
|
||||||
|
"result": true
|
||||||
|
};
|
||||||
|
wss.broadcast(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendStatus() {
|
||||||
|
var stats = {
|
||||||
|
"command": "status",
|
||||||
|
"heap": 30,
|
||||||
|
"availsize": 555,
|
||||||
|
"availspiffs": 445,
|
||||||
|
"spiffssize": 888,
|
||||||
|
"sketchsize": 222,
|
||||||
|
"uptime": "1 Day 6 Hours",
|
||||||
|
"ssid": "SSID",
|
||||||
|
"mac": "EM:44:11:33:22",
|
||||||
|
"ip": "192.168.2.2",
|
||||||
|
"signalstr": 66,
|
||||||
|
"systemload": 10,
|
||||||
|
"mqttconnected": true,
|
||||||
|
"mqttheartbeat": false
|
||||||
|
};
|
||||||
|
wss.broadcast(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendCustomStatus() {
|
||||||
|
var stats = {
|
||||||
|
"command": "custom_status",
|
||||||
|
"version": "1.9.0b",
|
||||||
|
"customname": "ems-esp",
|
||||||
|
"helpurl": "https://github.com/proddy/EMS-ESP/wiki",
|
||||||
|
"updateurl": "https://api.github.com/repos/proddy/EMS-ESP/releases/latest",
|
||||||
|
|
||||||
|
"emsbus": {
|
||||||
|
"ok": true,
|
||||||
|
"msg": "everything is OK",
|
||||||
|
"devices": [
|
||||||
|
{ "type": 1, "model": "model 1", "deviceid": "device id1", "version": "version id1", "productid": "product id1" },
|
||||||
|
{ "type": 2, "model": "model 2", "deviceid": "device id2", "version": "version id2", "productid": "product id2" },
|
||||||
|
{ "type": 3, "model": "model 3", "deviceid": "device id3", "version": "version id3", "productid": "product id3" },
|
||||||
|
{ "type": 4, "model": "model 4", "deviceid": "device id3", "version": "version id3", "productid": "product id3" },
|
||||||
|
{ "type": 5, "model": "model 5", "deviceid": "device id3", "version": "version id3", "productid": "product id3" }
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"thermostat": {
|
||||||
|
"ok": true,
|
||||||
|
"tm": "model abc",
|
||||||
|
"ts": "23",
|
||||||
|
"tc": "27.5",
|
||||||
|
"tmode": "manual"
|
||||||
|
},
|
||||||
|
|
||||||
|
"boiler": {
|
||||||
|
"ok": true,
|
||||||
|
"bm": "mode boiler",
|
||||||
|
"b1": "on",
|
||||||
|
"b2": "off",
|
||||||
|
"b3": 5.8,
|
||||||
|
"b4": 61.5
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
wss.broadcast(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
wss.on('connection', function connection(ws) {
|
||||||
|
ws.on("error", () => console.log("[WARN] WebSocket Error - Assume a client is disconnected."));
|
||||||
|
ws.on('message', function incoming(message) {
|
||||||
|
var obj = JSON.parse(message);
|
||||||
|
console.log("[INFO] Got Command: " + obj.command);
|
||||||
|
switch (obj.command) {
|
||||||
|
case "configfile":
|
||||||
|
console.log("[INFO] New system settings file received");
|
||||||
|
configfile = obj;
|
||||||
|
break;
|
||||||
|
case "custom_configfile":
|
||||||
|
console.log("[INFO] New custom config file received");
|
||||||
|
custom_configfile = obj;
|
||||||
|
break;
|
||||||
|
case "status":
|
||||||
|
console.log("[INFO] Sending Fake Emulator Status");
|
||||||
|
sendStatus();
|
||||||
|
break;
|
||||||
|
case "custom_status":
|
||||||
|
console.log("[INFO] Sending custom status");
|
||||||
|
sendCustomStatus();
|
||||||
|
break;
|
||||||
|
case "scan":
|
||||||
|
console.log("[INFO] Sending Fake Wireless Networks");
|
||||||
|
wss.broadcast(networks);
|
||||||
|
break;
|
||||||
|
case "gettime":
|
||||||
|
console.log("[INFO] Sending time");
|
||||||
|
var res = {};
|
||||||
|
res.command = "gettime";
|
||||||
|
res.epoch = Math.floor((new Date).getTime() / 1000);
|
||||||
|
res.timezone = configfile.timezone;
|
||||||
|
wss.broadcast(res);
|
||||||
|
break;
|
||||||
|
case "settime":
|
||||||
|
console.log("[INFO] Setting time (fake)");
|
||||||
|
var res = {};
|
||||||
|
res.command = "gettime";
|
||||||
|
res.epoch = Math.floor((new Date).getTime() / 1000);
|
||||||
|
res.timezone = configfile.timezone;
|
||||||
|
wss.broadcast(res);
|
||||||
|
break;
|
||||||
|
case "getconf":
|
||||||
|
console.log("[INFO] Sending system configuration file (if set any)");
|
||||||
|
wss.broadcast(configfile);
|
||||||
|
break;
|
||||||
|
case "geteventlog":
|
||||||
|
console.log("[INFO] Sending eventlog");
|
||||||
|
sendEventLog();
|
||||||
|
break;
|
||||||
|
case "clearevent":
|
||||||
|
console.log("[INFO] Clearing eventlog");
|
||||||
|
break;
|
||||||
|
case "restart":
|
||||||
|
console.log("[INFO] Restart");
|
||||||
|
break;
|
||||||
|
case "destroy":
|
||||||
|
console.log("[INFO] Destroy");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("[WARN] Unknown command ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user