Browse Source

Refactor get/set/del/hasSetting (#2048)

* experimental: refactor get/set/del/hasSetting

* sensors

* lights

* cleaup

* r

* tuya

* fixup! sensors

* fixup! tuya

* header defaults types

* fix lights

* setup already checks for max

* helper for flashstring

* fix overload

* oops

* refactor includes

* warnings

* test with migrate

* add ids in a separate file

* cleanup

rev: crash

rev: domoticz

rev: encoder

rev: loopdelay

rev: hass

rev: i2c

rev2: hass

rev: mqtt

rev: rfm69

rev: relay

rev: rpn

rev: settings setup

rev: hb settings

rev: telnet preprocessor fix

rev: settings wrap

rev: tspk bool style

rev: wifi types

rev: util hb

rev: settings

fixup! rev: settings

* rev: cleanup wifi injections based on new getters

* hasSetting now can return true for empty key

* show hardcoded network in web

* oops

* fix ws referencing wrong index

* ensure empty strings are written

* c/p

* use experimental schema style for payload, mark network as not deletable

* allow to customize converter

* shorter syntax, try using with wifi

* use proper #if syntax to handle definitions that are missing

* fixup ota sc checks getter, cast schEnabled to bool

* add utils header to sensors
mcspr-patch-1
Max Prokhorov 4 years ago
committed by GitHub
parent
commit
298ce8cac2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 20195 additions and 19405 deletions
  1. +1
    -1
      code/espurna/alexa.ino
  2. +24
    -12
      code/espurna/api.ino
  3. +18
    -0
      code/espurna/board.h
  4. +359
    -0
      code/espurna/board.ino
  5. +5
    -5
      code/espurna/button.ino
  6. +43
    -5
      code/espurna/config/general.h
  7. +6
    -38
      code/espurna/config/prototypes.h
  8. +21
    -10
      code/espurna/config/sensors.h
  9. +2
    -4
      code/espurna/crash.ino
  10. BIN
      code/espurna/data/index.all.html.gz
  11. BIN
      code/espurna/data/index.light.html.gz
  12. BIN
      code/espurna/data/index.lightfox.html.gz
  13. BIN
      code/espurna/data/index.rfbridge.html.gz
  14. BIN
      code/espurna/data/index.rfm69.html.gz
  15. BIN
      code/espurna/data/index.sensor.html.gz
  16. BIN
      code/espurna/data/index.small.html.gz
  17. BIN
      code/espurna/data/index.thermostat.html.gz
  18. +11
    -7
      code/espurna/debug.ino
  19. +14
    -16
      code/espurna/domoticz.ino
  20. +1
    -1
      code/espurna/encoder.ino
  21. +7
    -4
      code/espurna/espurna.ino
  22. +4
    -4
      code/espurna/homeassistant.ino
  23. +17
    -8
      code/espurna/i2c.ino
  24. +4
    -4
      code/espurna/influxdb.ino
  25. +8
    -8
      code/espurna/led.ino
  26. +4
    -2
      code/espurna/libs/EmbedisWrap.h
  27. +23
    -23
      code/espurna/light.ino
  28. +1
    -1
      code/espurna/mdns.ino
  29. +7
    -6
      code/espurna/migrate.ino
  30. +26
    -19
      code/espurna/mqtt.ino
  31. +3
    -3
      code/espurna/nofuss.ino
  32. +9
    -9
      code/espurna/ntp.ino
  33. +1
    -1
      code/espurna/ota_asynctcp.ino
  34. +3
    -3
      code/espurna/ota_httpupdate.ino
  35. +34
    -33
      code/espurna/relay.ino
  36. +16
    -12
      code/espurna/rfbridge.ino
  37. +9
    -9
      code/espurna/rfm69.ino
  38. +16
    -16
      code/espurna/rpnrules.ino
  39. +38
    -37
      code/espurna/scheduler.ino
  40. +81
    -73
      code/espurna/sensor.ino
  41. +4
    -2
      code/espurna/sensors/ADE7953Sensor.h
  42. +2
    -0
      code/espurna/sensors/BMP180Sensor.h
  43. +3
    -1
      code/espurna/sensors/BMX280Sensor.h
  44. +3
    -1
      code/espurna/sensors/DHTSensor.h
  45. +3
    -1
      code/espurna/sensors/EmonADC121Sensor.h
  46. +3
    -1
      code/espurna/sensors/GUVAS12SDSensor.h
  47. +2
    -1
      code/espurna/sensors/SHT3XI2CSensor.h
  48. +3
    -1
      code/espurna/sensors/SI7021Sensor.h
  49. +105
    -0
      code/espurna/settings.h
  50. +85
    -35
      code/espurna/settings.ino
  51. +75
    -0
      code/espurna/settings_internal.h
  52. +3045
    -3042
      code/espurna/static/index.all.html.gz.h
  53. +2174
    -2171
      code/espurna/static/index.light.html.gz.h
  54. +2419
    -2415
      code/espurna/static/index.lightfox.html.gz.h
  55. +2446
    -2443
      code/espurna/static/index.rfbridge.html.gz.h
  56. +3184
    -3181
      code/espurna/static/index.rfm69.html.gz.h
  57. +1898
    -1895
      code/espurna/static/index.sensor.html.gz.h
  58. +1793
    -1790
      code/espurna/static/index.small.html.gz.h
  59. +1829
    -1826
      code/espurna/static/index.thermostat.html.gz.h
  60. +3
    -3
      code/espurna/system.ino
  61. +8
    -6
      code/espurna/telnet.ino
  62. +5
    -6
      code/espurna/terminal.ino
  63. +16
    -16
      code/espurna/thermostat.ino
  64. +8
    -8
      code/espurna/thinkspeak.ino
  65. +8
    -8
      code/espurna/tuya.ino
  66. +2
    -9
      code/espurna/utils.h
  67. +9
    -40
      code/espurna/utils.ino
  68. +3
    -2
      code/espurna/web.ino
  69. +29
    -0
      code/espurna/wifi.h
  70. +77
    -104
      code/espurna/wifi.ino
  71. +91
    -0
      code/espurna/wifi_config.h
  72. +17
    -12
      code/espurna/ws.ino
  73. +27
    -11
      code/html/custom.js

+ 1
- 1
code/espurna/alexa.ino View File

@ -76,7 +76,7 @@ void _alexaBrokerCallback(const String& topic, unsigned char id, unsigned int va
// -----------------------------------------------------------------------------
bool alexaEnabled() {
return (getSetting("alexaEnabled", ALEXA_ENABLED).toInt() == 1);
return getSetting<bool>("alexaEnabled", 1 == ALEXA_ENABLED);
}
void alexaSetup() {


+ 24
- 12
code/espurna/api.ino View File

@ -24,15 +24,27 @@ std::vector<web_api_t> _apis;
// -----------------------------------------------------------------------------
bool _apiEnabled() {
return getSetting("apiEnabled", 1 == API_ENABLED);
}
bool _apiRestFul() {
return getSetting("apiRestFul", 1 == API_RESTFUL);
}
String _apiKey() {
return getSetting("apiKey", API_KEY);
}
bool _apiWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
return (strncmp(key, "api", 3) == 0);
}
void _apiWebSocketOnConnected(JsonObject& root) {
root["apiEnabled"] = getSetting("apiEnabled", API_ENABLED).toInt() == 1;
root["apiKey"] = getSetting("apiKey", API_KEY);
root["apiRealTime"] = getSetting("apiRealTime", API_REAL_TIME_VALUES).toInt() == 1;
root["apiRestFul"] = getSetting("apiRestFul", API_RESTFUL).toInt() == 1;
root["apiEnabled"] = _apiEnabled();
root["apiKey"] = _apiKey();
root["apiRestFul"] = _apiRestFul();
root["apiRealTime"] = getSetting("apiRealTime", 1 == API_REAL_TIME_VALUES);
}
void _apiConfigure() {
@ -45,15 +57,15 @@ void _apiConfigure() {
bool _authAPI(AsyncWebServerRequest *request) {
const String key = getSetting("apiKey", API_KEY);
if (!key.length() || getSetting("apiEnabled", API_ENABLED).toInt() == 0) {
const auto key = _apiKey();
if (!key.length() || !_apiEnabled()) {
DEBUG_MSG_P(PSTR("[WEBSERVER] HTTP API is not enabled\n"));
request->send(403);
return false;
}
AsyncWebParameter* p = request->getParam("apikey", (request->method() == HTTP_PUT));
if (!p || !p->value().equals(key)) {
AsyncWebParameter* keyParam = request->getParam("apikey", (request->method() == HTTP_PUT));
if (!keyParam || !keyParam->value().equals(key)) {
DEBUG_MSG_P(PSTR("[WEBSERVER] Wrong / missing apikey parameter\n"));
request->send(403);
return false;
@ -76,12 +88,12 @@ void _onAPIsText(AsyncWebServerRequest *request) {
AsyncResponseStream *response = request->beginResponseStream("text/plain");
String output;
output.reserve(48);
for (unsigned int i=0; i < _apis.size(); i++) {
for (auto& api : _apis) {
output = "";
output += _apis[i].key;
output += api.key;
output += " -> ";
output += "/api/";
output += _apis[i].key;
output += api.key;
output += '\n';
response->write(output.c_str());
}
@ -185,7 +197,7 @@ bool _apiRequestCallback(AsyncWebServerRequest *request) {
// Check if its a PUT
if (api.putFn != NULL) {
if ((getSetting("apiRestFul", API_RESTFUL).toInt() != 1) || (request->method() == HTTP_PUT)) {
if (!_apiRestFul() || (request->method() == HTTP_PUT)) {
if (request->hasParam("value", request->method() == HTTP_PUT)) {
AsyncWebParameter* p = request->getParam("value", request->method() == HTTP_PUT);
(api.putFn)((p->value()).c_str());


+ 18
- 0
code/espurna/board.h View File

@ -0,0 +1,18 @@
/*
BOARD MODULE
*/
#pragma once
String getIdentifier();
String getEspurnaModules();
String getEspurnaOTAModules();
String getEspurnaSensors();
String getEspurnaWebUI();
int getBoardId();

+ 359
- 0
code/espurna/board.ino View File

@ -0,0 +1,359 @@
/*
BOARD MODULE
*/
#include "board.h"
String getIdentifier() {
char buffer[20];
snprintf_P(buffer, sizeof(buffer), PSTR("%s-%06X"), APP_NAME, ESP.getChipId());
return String(buffer);
}
String getEspurnaModules() {
return FPSTR(espurna_modules);
}
String getEspurnaOTAModules() {
return FPSTR(espurna_ota_modules);
}
#if SENSOR_SUPPORT
String getEspurnaSensors() {
return FPSTR(espurna_sensors);
}
#endif
String getEspurnaWebUI() {
return FPSTR(espurna_webui);
}
bool haveRelaysOrSensors() {
bool result = false;
result = (relayCount() > 0);
#if SENSOR_SUPPORT
result = result || (magnitudeCount() > 0);
#endif
return result;
}
int getBoardId() {
#if defined(ESPURNA_CORE)
return 0;
#elif defined(ESPURNA_BASE)
return 1;
#elif defined(NODEMCU_LOLIN)
return 2;
#elif defined(NODEMCU_BASIC)
return 3;
#elif defined(WEMOS_D1_MINI)
return 4;
#elif defined(WEMOS_D1_MINI_RELAYSHIELD)
return 5;
#elif defined(WEMOS_D1_TARPUNA_SHIELD)
return 6;
#elif defined(TINKERMAN_ESPURNA_H06)
return 7;
#elif defined(TINKERMAN_ESPURNA_H08)
return 8;
#elif defined(TINKERMAN_ESPURNA_SWITCH)
return 9;
#elif defined(TINKERMAN_RFM69GW)
return 10;
#elif defined(ITEAD_SONOFF_BASIC)
return 11;
#elif defined(ITEAD_SONOFF_RF)
return 12;
#elif defined(ITEAD_SONOFF_MINI)
return 13;
#elif defined(ITEAD_SONOFF_TH)
return 14;
#elif defined(ITEAD_SONOFF_SV)
return 15;
#elif defined(ITEAD_SLAMPHER)
return 16;
#elif defined(ITEAD_S20)
return 17;
#elif defined(ITEAD_SONOFF_TOUCH)
return 18;
#elif defined(ITEAD_SONOFF_POW)
return 19;
#elif defined(ITEAD_SONOFF_POW_R2)
return 20;
#elif defined(ITEAD_SONOFF_DUAL)
return 21;
#elif defined(ITEAD_SONOFF_DUAL_R2)
return 22;
#elif defined(ITEAD_SONOFF_4CH)
return 23;
#elif defined(ITEAD_SONOFF_4CH_PRO)
return 24;
#elif defined(ITEAD_1CH_INCHING)
return 25;
#elif defined(ITEAD_MOTOR)
return 26;
#elif defined(ITEAD_BNSZ01)
return 27;
#elif defined(ITEAD_SONOFF_RFBRIDGE)
return 28;
#elif defined(ITEAD_SONOFF_B1)
return 29;
#elif defined(ITEAD_SONOFF_LED)
return 30;
#elif defined(ITEAD_SONOFF_T1_1CH)
return 31;
#elif defined(ITEAD_SONOFF_T1_2CH)
return 32;
#elif defined(ITEAD_SONOFF_T1_3CH)
return 33;
#elif defined(ITEAD_SONOFF_S31)
return 34;
#elif defined(ITEAD_SONOFF_S31_LITE)
return 35;
#elif defined(ITEAD_SONOFF_IFAN02)
return 36;
#elif defined(ORVIBO_B25)
return 37;
#elif defined(YJZK_SWITCH_1CH)
return 38;
#elif defined(YJZK_SWITCH_2CH)
return 39;
#elif defined(YJZK_SWITCH_3CH)
return 40;
#elif defined(ELECTRODRAGON_WIFI_IOT)
return 41;
#elif defined(WORKCHOICE_ECOPLUG)
return 42;
#elif defined(AITHINKER_AI_LIGHT)
return 43;
#elif defined(LYASI_LIGHT)
return 44;
#elif defined(MAGICHOME_LED_CONTROLLER)
return 45;
#elif defined(MAGICHOME_LED_CONTROLLER_20)
return 46;
#elif defined(MAGICHOME_ZJ_WFMN_A_11)
return 47;
#elif defined(MAGICHOME_ZJ_WFMN_B_11)
return 48;
#elif defined(MAGICHOME_ZJ_WFMN_C_11)
return 49;
#elif defined(MAGICHOME_ZJ_ESPM_5CH_B_13)
return 50;
#elif defined(MAGICHOME_ZJ_LB_RGBWW_L)
return 51;
#elif defined(HUACANXING_H801)
return 52;
#elif defined(HUACANXING_H802)
return 53;
#elif defined(JANGOE_WIFI_RELAY_NC)
return 54;
#elif defined(JANGOE_WIFI_RELAY_NO)
return 55;
#elif defined(JORGEGARCIA_WIFI_RELAYS)
return 56;
#elif defined(OPENENERGYMONITOR_MQTT_RELAY)
return 57;
#elif defined(WION_50055)
return 58;
#elif defined(EXS_WIFI_RELAY_V31)
return 59;
#elif defined(EXS_WIFI_RELAY_V50)
return 60;
#elif defined(GENERIC_V9261F)
return 61;
#elif defined(GENERIC_ECH1560)
return 62;
#elif defined(MANCAVEMADE_ESPLIVE)
return 63;
#elif defined(INTERMITTECH_QUINLED)
return 64;
#elif defined(ARILUX_AL_LC01)
return 65;
#elif defined(ARILUX_AL_LC02)
return 66;
#elif defined(ARILUX_AL_LC02_V14)
return 67;
#elif defined(ARILUX_AL_LC06)
return 68;
#elif defined(ARILUX_AL_LC11)
return 69;
#elif defined(ARILUX_E27)
return 70;
#elif defined(XENON_SM_PW702U)
return 71;
#elif defined(ISELECTOR_SM_PW702)
return 72;
#elif defined(AUTHOMETION_LYT8266)
return 73;
#elif defined(GIZWITS_WITTY_CLOUD)
return 74;
#elif defined(KMC_70011)
return 75;
#elif defined(EUROMATE_WIFI_STECKER_SCHUKO)
return 76;
#elif defined(EUROMATE_WIFI_STECKER_SCHUKO_V2)
return 77;
#elif defined(GENERIC_8CH)
return 78;
#elif defined(STM_RELAY)
return 79;
#elif defined(TONBUX_POWERSTRIP02)
return 80;
#elif defined(LINGAN_SWA1)
return 81;
#elif defined(HEYGO_HY02)
return 82;
#elif defined(MAXCIO_WUS002S)
return 83;
#elif defined(MAXCIO_WDE004)
return 84;
#elif defined(OUKITEL_P1)
return 85;
#elif defined(YIDIAN_XSSSA05)
return 86;
#elif defined(TONBUX_XSSSA01)
return 87;
#elif defined(TONBUX_XSSSA06)
return 88;
#elif defined(GREEN_ESP8266RELAY)
return 89;
#elif defined(IKE_ESPIKE)
return 90;
#elif defined(ARNIEX_SWIFITCH)
return 91;
#elif defined(GENERIC_ESP01S_RELAY_V40)
return 92;
#elif defined(GENERIC_ESP01S_RGBLED_V10)
return 93;
#elif defined(GENERIC_ESP01S_DHT11_V10)
return 94;
#elif defined(GENERIC_ESP01S_DS18B20_V10)
return 95;
#elif defined(PILOTAK_ESP_DIN_V1)
return 96;
#elif defined(HELTEC_TOUCHRELAY)
return 97;
#elif defined(ZHILDE_EU44_W)
return 98;
#elif defined(ALLNET_4DUINO_IOT_WLAN_RELAIS)
return 99;
#elif defined(LUANI_HVIO)
return 100;
#elif defined(TONBUX_MOSQUITO_KILLER)
return 101;
#elif defined(NEO_COOLCAM_NAS_WR01W)
return 102;
#elif defined(DELTACO_SH_P01)
return 103;
#elif defined(DELTACO_SH_P03USB)
return 104;
#elif defined(FORNORM_ZLD_34EU)
return 105;
#elif defined(BH_ONOFRE)
return 106;
#elif defined(BLITZWOLF_BWSHPX)
return 107;
#elif defined(BLITZWOLF_BWSHPX_V23)
return 108;
#elif defined(BLITZWOLF_BWSHP5)
return 109;
#elif defined(TECKIN_SP21)
return 110;
#elif defined(TECKIN_SP22_V14)
return 111;
#elif defined(GOSUND_WS1)
return 112;
#elif defined(HOMECUBE_16A)
return 113;
#elif defined(VANZAVANZU_SMART_WIFI_PLUG_MINI)
return 114;
#elif defined(GENERIC_AG_L4)
return 115;
#elif defined(ALLTERCO_SHELLY1)
return 116;
#elif defined(ALLTERCO_SHELLY2)
return 117;
#elif defined(ALLTERCO_SHELLY1PM)
return 118;
#elif defined(ALLTERCO_SHELLY25)
return 119;
#elif defined(LOHAS_E27_9W)
return 120;
#elif defined(LOHAS_E26_A19)
return 121;
#elif defined(TECKIN_SB53)
return 122;
#elif defined(XIAOMI_SMART_DESK_LAMP)
return 123;
#elif defined(PHYX_ESP12_RGB)
return 124;
#elif defined(IWOOLE_LED_TABLE_LAMP)
return 125;
#elif defined(GENERIC_GU10)
return 126;
#elif defined(GENERIC_E14)
return 127;
#elif defined(DELTACO_SH_LEXXW)
return 128;
#elif defined(DELTACO_SH_LEXXRGB)
return 129;
#elif defined(NEXETE_A19)
return 130;
#elif defined(LOMBEX_LUX_NOVA2_TUNABLE_WHITE)
return 131;
#elif defined(LOMBEX_LUX_NOVA2_WHITE_COLOR)
return 132;
#elif defined(BESTEK_MRJ1011)
return 133;
#elif defined(GBLIFE_RGBW_SOCKET)
return 134;
#elif defined(SMARTLIFE_MINI_SMART_SOCKET)
return 135;
#elif defined(HAMA_WIFI_STECKDOSE_00176533)
return 136;
#elif defined(DIGOO_NX_SP202)
return 137;
#elif defined(FOXEL_LIGHTFOX_DUAL)
return 138;
#elif defined(TECKIN_SP20)
return 139;
#elif defined(LITESUN_LA_WF3)
return 140;
#elif defined(PSH_WIFI_PLUG)
return 141;
#elif defined(PSH_RGBW_CONTROLLER)
return 142;
#elif defined(PSH_WIFI_SENSOR)
return 143;
#elif defined(JINVOO_VALVE_SM_AW713)
return 144;
#elif defined(TUYA_GENERIC_DIMMER)
return 145;
#elif defined(ETEKCITY_ESW01_USA)
return 146;
#elif defined(FS_UAP1)
return 147;
#elif defined(TFLAG_NX_SMX00)
return 148;
#elif defined(MUVIT_IO_MIOBULB001)
return 149;
#elif defined(HYKKER_SMART_HOME_POWER_PLUG)
return 150;
#elif defined(KOGAN_SMARTER_HOME_PLUG_W_POW)
return 151;
#elif defined(LSC_SMART_LED_LIGHT_STRIP)
return 152;
#elif defined(EHOMEDIY_WT02)
return 153;
#elif defined(EHOMEDIY_WT03)
return 154;
#elif defined(LINKSPRITE_LINKNODE_R4)
return 155;
#else
return -1; // CUSTOM
#endif
}

+ 5
- 5
code/espurna/button.ino View File

@ -192,18 +192,18 @@ void buttonSetup() {
#elif defined(FOXEL_LIGHTFOX_DUAL)
unsigned int actions = buttonStore(BUTTON_MODE_NONE, BUTTON_MODE_TOGGLE, BUTTON_MODE_NONE, BUTTON_MODE_NONE, BUTTON_MODE_NONE, BUTTON_MODE_NONE);
unsigned int btn1Relay = getSetting("btnRelay", 0, BUTTON1_RELAY - 1).toInt() + 1;
unsigned int btn1Relay = getSetting<int>({"btnRelay", 0}, BUTTON1_RELAY - 1) + 1;
_buttons.push_back({new DebounceEvent(0, BUTTON_PUSHBUTTON), actions, btn1Relay});
unsigned int btn2Relay = getSetting("btnRelay", 1, BUTTON2_RELAY - 1).toInt() + 1;
unsigned int btn2Relay = getSetting<int>({"btnRelay", 1}, BUTTON2_RELAY - 1) + 1;
_buttons.push_back({new DebounceEvent(0, BUTTON_PUSHBUTTON), actions, btn2Relay});
unsigned int btn3Relay = getSetting("btnRelay", 2, BUTTON3_RELAY - 1).toInt() + 1;
unsigned int btn3Relay = getSetting<int>({"btnRelay", 2}, BUTTON3_RELAY - 1) + 1;
_buttons.push_back({new DebounceEvent(0, BUTTON_PUSHBUTTON), actions, btn3Relay});
unsigned int btn4Relay = getSetting("btnRelay", 3, BUTTON4_RELAY - 1).toInt() + 1;
unsigned int btn4Relay = getSetting<int>({"btnRelay", 3}, BUTTON4_RELAY - 1) + 1;
_buttons.push_back({new DebounceEvent(0, BUTTON_PUSHBUTTON), actions, btn4Relay});
#else
unsigned long btnDelay = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt();
unsigned long btnDelay = getSetting<int>("btnDelay", BUTTON_DBLCLICK_DELAY);
UNUSED(btnDelay);
#if BUTTON1_PIN != GPIO_NONE


+ 43
- 5
code/espurna/config/general.h View File

@ -203,6 +203,16 @@
#define EEPROM_ROTATE_DATA 11 // Reserved for the EEPROM_ROTATE library (3 bytes)
#define EEPROM_DATA_END 14 // End of custom EEPROM data block
#ifndef SAVE_CRASH_ENABLED
#define SAVE_CRASH_ENABLED 1 // Save stack trace to EEPROM by default
// Depends on DEBUG_SUPPORT == 1
#endif
#ifndef SAVE_CRASH_STACK_TRACE_MAX
#define SAVE_CRASH_STACK_TRACE_MAX 0x80 // limit at 128 bytes (increment/decrement by 16)
#endif
//------------------------------------------------------------------------------
// THERMOSTAT
//------------------------------------------------------------------------------
@ -237,10 +247,10 @@
#endif
#ifndef HEARTBEAT_INTERVAL
#define HEARTBEAT_INTERVAL 300 // Interval between heartbeat messages (in sec)
#define HEARTBEAT_INTERVAL 300UL // Interval between heartbeat messages (in sec)
#endif
#define UPTIME_OVERFLOW 4294967295 // Uptime overflow value
#define UPTIME_OVERFLOW 4294967295UL // Uptime overflow value
// Values that will be reported in heartbeat
#ifndef HEARTBEAT_REPORT_STATUS
@ -410,7 +420,7 @@
// Relay requests flood protection window - in seconds
#ifndef RELAY_FLOOD_WINDOW
#define RELAY_FLOOD_WINDOW 3
#define RELAY_FLOOD_WINDOW 3.0
#endif
// Allowed actual relay changes inside requests flood protection window
@ -574,6 +584,30 @@
#define WIFI4_DNS ""
#endif
#ifndef WIFI5_SSID
#define WIFI5_SSID ""
#endif
#ifndef WIFI5_PASS
#define WIFI5_PASS ""
#endif
#ifndef WIFI5_IP
#define WIFI5_IP ""
#endif
#ifndef WIFI5_GW
#define WIFI5_GW ""
#endif
#ifndef WIFI5_MASK
#define WIFI5_MASK ""
#endif
#ifndef WIFI5_DNS
#define WIFI5_DNS ""
#endif
#ifndef WIFI_RSSI_1M
#define WIFI_RSSI_1M -30 // Calibrate it with your router reading the RSSI at 1m
#endif
@ -606,7 +640,7 @@
//
// Issue #6366 turned out to be high tx power causing weird behavior. Lowering tx power achieved stability.
#ifndef WIFI_OUTPUT_POWER_DBM
#define WIFI_OUTPUT_POWER_DBM 20.0
#define WIFI_OUTPUT_POWER_DBM 20.0f
#endif
@ -1153,7 +1187,7 @@
#define SETTINGS_AUTOSAVE 1 // Autosave settings or force manual commit
#endif
#define SETTINGS_MAX_LIST_COUNT 10 // Maximum index for settings lists
#define SETTINGS_MAX_LIST_COUNT 16 // Maximum index for settings lists
// -----------------------------------------------------------------------------
// LIGHT
@ -1428,6 +1462,10 @@
#define RPN_DELAY 100 // Execute rules after 100ms without messages
#endif
#ifndef RPN_STICKY
#define RPN_STICKY 1 // Keeps variable after rule execution
#endif
// -----------------------------------------------------------------------------
// NTP
// -----------------------------------------------------------------------------


+ 6
- 38
code/espurna/config/prototypes.h View File

@ -2,6 +2,7 @@
#include <ArduinoJson.h>
#include <functional>
#include <algorithm>
#include <limits>
#include <vector>
#include <memory>
@ -231,49 +232,16 @@ typedef struct {
int16_t rssi;
} packet_t;
// -----------------------------------------------------------------------------
// Settings
// -----------------------------------------------------------------------------
#include <Embedis.h>
template<typename T> bool setSetting(const String& key, T value);
template<typename T> bool setSetting(const String& key, unsigned int index, T value);
template<typename T> String getSetting(const String& key, T defaultValue);
template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue);
void settingsGetJson(JsonObject& data);
bool settingsRestoreJson(JsonObject& data);
struct settings_cfg_t {
String& setting;
const char* key;
const char* default_value;
};
using settings_filter_t = std::function<String(String& value)>;
using settings_cfg_list_t = std::initializer_list<settings_cfg_t>;
void settingsProcessConfig(const settings_cfg_list_t& config, settings_filter_t filter = nullptr);
// -----------------------------------------------------------------------------
// Terminal
// -----------------------------------------------------------------------------
#if TERMINAL_SUPPORT
void terminalRegisterCommand(const String& name, void (*call)(Embedis*));
void terminalInject(void *data, size_t len);
Stream & terminalSerial();
#endif
// -----------------------------------------------------------------------------
// Utils
// -----------------------------------------------------------------------------
char * ltrim(char * s);
void nice_delay(unsigned long ms);
bool inline eraseSDKConfig();
#define ARRAYINIT(type, name, ...) type name[] = {__VA_ARGS__};
class Embedis; // FIXME: order
using embedis_command_f = void (*)(Embedis*);
size_t strnlen(const char*, size_t);
char* strnstr(const char*, const char*, size_t);
void terminalRegisterCommand(const String& name, embedis_command_f func);
void terminalInject(void *data, size_t len);
Stream& terminalSerial();
// -----------------------------------------------------------------------------
// WebServer


+ 21
- 10
code/espurna/config/sensors.h View File

@ -33,11 +33,11 @@
#endif
#ifndef HUMIDITY_MIN_CHANGE
#define HUMIDITY_MIN_CHANGE 0 // Minimum humidity change to report
#define HUMIDITY_MIN_CHANGE 0.0 // Minimum humidity change to report
#endif
#ifndef ENERGY_MAX_CHANGE
#define ENERGY_MAX_CHANGE 0 // Maximum energy change to report (if >0 it will allways report when delta(E) is greater than this)
#define ENERGY_MAX_CHANGE 0.0 // Maximum energy change to report (if >0 it will allways report when delta(E) is greater than this)
#endif
#ifndef SENSOR_SAVE_EVERY
@ -403,7 +403,7 @@
#endif
#ifndef EMON_CURRENT_RATIO
#define EMON_CURRENT_RATIO 30 // Current ratio in the clamp (30A/1V)
#define EMON_CURRENT_RATIO 30.0 // Current ratio in the clamp (30A/1V)
#endif
#ifndef EMON_REPORT_CURRENT
@ -710,15 +710,15 @@
#endif
#ifndef HLW8012_CURRENT_RATIO
#define HLW8012_CURRENT_RATIO 0 // Set to 0 to use factory defaults
#define HLW8012_CURRENT_RATIO 0.0 // Set to 0.0 to use factory defaults
#endif
#ifndef HLW8012_VOLTAGE_RATIO
#define HLW8012_VOLTAGE_RATIO 0 // Set to 0 to use factory defaults
#define HLW8012_VOLTAGE_RATIO 0.0 // Set to 0.0 to use factory defaults
#endif
#ifndef HLW8012_POWER_RATIO
#define HLW8012_POWER_RATIO 0 // Set to 0 to use factory defaults
#define HLW8012_POWER_RATIO 0.0 // Set to 0.0 to use factory defaults
#endif
#ifndef HLW8012_USE_INTERRUPTS
@ -1263,17 +1263,28 @@
#define I2C_USE_BRZO 0 // Use brzo_i2c library or standard Wire
#ifndef I2C_SDA_PIN
#define I2C_SDA_PIN SDA // SDA GPIO (Sonoff => 4)
#define I2C_SDA_PIN SDA // SDA GPIO (Sonoff => 4, using Arduino Core variant definition as default)
#endif
#ifndef I2C_SCL_PIN
#define I2C_SCL_PIN SCL // SCL GPIO (Sonoff => 14)
#define I2C_SCL_PIN SCL // SCL GPIO (Sonoff => 14, using Arduino Core variant definition as default)
#endif
#define I2C_CLOCK_STRETCH_TIME 200 // BRZO clock stretch time
#define I2C_SCL_FREQUENCY 1000 // BRZO SCL frequency
#ifndef I2C_CLOCK_STRETCH_TIME
#define I2C_CLOCK_STRETCH_TIME 200UL // BRZO clock stretch time
#endif
#ifndef I2C_SCL_FREQUENCY
#define I2C_SCL_FREQUENCY 1000UL // BRZO SCL frequency
#endif
#ifndef I2C_CLEAR_BUS
#define I2C_CLEAR_BUS 0 // Clear I2C bus on boot
#endif
#ifndef I2C_PERFORM_SCAN
#define I2C_PERFORM_SCAN 1 // Perform a bus scan on boot
#endif
// -----------------------------------------------------------------------------
// ADE7953 Shelly Sensor


+ 2
- 4
code/espurna/crash.ino View File

@ -46,8 +46,6 @@ extern "C" {
#define SAVE_CRASH_STACK_SIZE 0x22 // 2 bytes
#define SAVE_CRASH_STACK_TRACE 0x24 // variable
#define SAVE_CRASH_STACK_TRACE_MAX 0x80 // limit at 128 bytes (increment/decrement by 16)
uint16_t _save_crash_stack_trace_max = SAVE_CRASH_STACK_TRACE_MAX;
bool _save_crash_enabled = true;
@ -190,7 +188,7 @@ void crashSetup() {
// Minumum of 16 and align for column formatter in crashDump()
// Maximum of flash sector size minus reserved space at the beginning
const uint16_t trace_max = constrain(
abs((getSetting("sysTraceMax", SAVE_CRASH_STACK_TRACE_MAX).toInt() + 15) & -16),
abs((getSetting<int>("sysTraceMax", SAVE_CRASH_STACK_TRACE_MAX) + 15) & -16),
0, (SPI_FLASH_SEC_SIZE - crashUsedSpace())
);
@ -199,7 +197,7 @@ void crashSetup() {
}
_save_crash_stack_trace_max = trace_max;
_save_crash_enabled = getSetting("sysCrashSave", 1).toInt() == 1;
_save_crash_enabled = getSetting("sysCrashSave", 1 == SAVE_CRASH_ENABLED);
}


BIN
code/espurna/data/index.all.html.gz View File


BIN
code/espurna/data/index.light.html.gz View File


BIN
code/espurna/data/index.lightfox.html.gz View File


BIN
code/espurna/data/index.rfbridge.html.gz View File


BIN
code/espurna/data/index.rfm69.html.gz View File


BIN
code/espurna/data/index.sensor.html.gz View File


BIN
code/espurna/data/index.small.html.gz View File


BIN
code/espurna/data/index.thermostat.html.gz View File


+ 11
- 7
code/espurna/debug.ino View File

@ -238,16 +238,20 @@ void debugSetup() {
// HardwareSerial::begin() will automatically enable this when
// `#if defined(DEBUG_ESP_PORT) && !defined(NDEBUG)`
// Core debugging also depends on various DEBUG_ESP_... being defined
#define DEBUG_SERIAL_SDK (int)(defined(DEBUG_ESP_PORT) && !defined(NDEBUG))
if (getSetting("dbgSDK", DEBUG_SERIAL_SDK).toInt() == 1) {
DEBUG_PORT.setDebugOutput(true);
}
#undef DEBUG_SERIAL_SDK
#if defined(DEBUG_ESP_PORT)
#if not defined(NDEBUG)
constexpr const bool debug_sdk = true;
#endif // !defined(NDEBUG)
#else
constexpr const bool debug_sdk = false;
#endif // defined(DEBUG_ESP_PORT)
DEBUG_PORT.setDebugOutput(getSetting("dbgSDK", debug_sdk));
#if DEBUG_LOG_BUFFER_SUPPORT
{
auto enabled = getSetting("dbgBufEnabled", DEBUG_LOG_BUFFER_ENABLED).toInt() == 1;
auto size = getSetting("dbgBufSize", DEBUG_LOG_BUFFER_SIZE).toInt();
const auto enabled = getSetting("dbgBufEnabled", 1 == DEBUG_LOG_BUFFER_ENABLED);
const auto size = getSetting("dbgBufSize", DEBUG_LOG_BUFFER_SIZE);
if (enabled) {
_debug_log_buffer_enabled = true;
_debug_log_buffer.reserve(size);


+ 14
- 16
code/espurna/domoticz.ino View File

@ -21,9 +21,13 @@ std::bitset<RELAYS_MAX> _dcz_relay_state;
// Private methods
//------------------------------------------------------------------------------
unsigned char _domoticzIdx(unsigned char relayID, unsigned char defaultValue = 0) {
return getSetting({"dczRelayIdx", relayID}, defaultValue);
}
int _domoticzRelay(unsigned int idx) {
for (unsigned char relayID=0; relayID<relayCount(); relayID++) {
if (domoticzIdx(relayID) == idx) {
if (_domoticzIdx(relayID) == idx) {
return relayID;
}
}
@ -32,7 +36,7 @@ int _domoticzRelay(unsigned int idx) {
void _domoticzMqttSubscribe(bool value) {
String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
const String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
if (value) {
mqttSubscribeRaw(dczTopicOut.c_str());
} else {
@ -117,7 +121,7 @@ void _domoticzMqtt(unsigned int type, const char * topic, char * payload) {
if (!_dcz_enabled) return;
String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
const String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
if (type == MQTT_CONNECT_EVENT) {
@ -147,7 +151,7 @@ void _domoticzMqtt(unsigned int type, const char * topic, char * payload) {
String stype = root["stype"];
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
if (stype.startsWith("RGB") && (domoticzIdx(0) == idx)) {
if (stype.startsWith("RGB") && (_domoticzIdx(0) == idx)) {
_domoticzLight(idx, root);
}
#endif
@ -201,13 +205,13 @@ void _domoticzWebSocketOnVisible(JsonObject& root) {
void _domoticzWebSocketOnConnected(JsonObject& root) {
root["dczEnabled"] = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1;
root["dczEnabled"] = getSetting("dczEnabled", 1 == DOMOTICZ_ENABLED);
root["dczTopicIn"] = getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC);
root["dczTopicOut"] = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
JsonArray& relays = root.createNestedArray("dczRelays");
for (unsigned char i=0; i<relayCount(); i++) {
relays.add(domoticzIdx(i));
relays.add(_domoticzIdx(i));
}
#if SENSOR_SUPPORT
@ -225,7 +229,7 @@ void _domoticzRelayConfigure(size_t size) {
}
void _domoticzConfigure() {
bool enabled = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1;
const bool enabled = getSetting("dczEnabled", 1 == DOMOTICZ_ENABLED);
if (enabled != _dcz_enabled) _domoticzMqttSubscribe(enabled);
_domoticzRelayConfigure(relayCount());
@ -238,11 +242,11 @@ void _domoticzConfigure() {
template<typename T> void domoticzSend(const char * key, T nvalue, const char * svalue) {
if (!_dcz_enabled) return;
unsigned int idx = getSetting(key).toInt();
const auto idx = getSetting<int>(key, 0);
if (idx > 0) {
char payload[128];
snprintf(payload, sizeof(payload), "{\"idx\": %u, \"nvalue\": %s, \"svalue\": \"%s\"}", idx, String(nvalue).c_str(), svalue);
mqttSendRaw(getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC).c_str(), payload);
snprintf(payload, sizeof(payload), "{\"idx\": %d, \"nvalue\": %s, \"svalue\": \"%s\"}", idx, String(nvalue).c_str(), svalue);
mqttSendRaw(getSetting<String>("dczTopicIn", DOMOTICZ_IN_TOPIC).c_str(), payload);
}
}
@ -263,12 +267,6 @@ void domoticzSendRelays() {
}
}
unsigned int domoticzIdx(unsigned char relayID) {
char buffer[15];
snprintf_P(buffer, sizeof(buffer), PSTR("dczRelayIdx%u"), relayID);
return getSetting(buffer).toInt();
}
void domoticzSetup() {
_domoticzConfigure();


+ 1
- 1
code/espurna/encoder.ino View File

@ -26,7 +26,7 @@ unsigned long _encoder_min_delta = 1;
void _encoderConfigure() {
_encoder_min_delta = getSetting("encMinDelta", ENCODER_MINIMUM_DELTA).toInt();
_encoder_min_delta = getSetting("encMinDelta", ENCODER_MINIMUM_DELTA);
if (!_encoder_min_delta) _encoder_min_delta = 1;
// no need to reload objects right now


+ 7
- 4
code/espurna/espurna.ino View File

@ -20,14 +20,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config/all.h"
#include <vector>
#include "board.h"
#include "broker.h"
#include "debug.h"
#include "relay.h"
#include "settings.h"
#include "system.h"
#include "tuya.h"
#include "utils.h"
#include "wifi.h"
#include "ws.h"
#include "libs/HeapStats.h"
@ -102,7 +104,7 @@ void setup() {
// Return bogus free heap value for broken devices
// XXX: device is likely to trigger other bugs! tread carefuly
wtfHeap(getSetting("wtfHeap", 0).toInt());
wtfHeap(getSetting<int>("wtfHeap", 0));
// Init Serial, SPIFFS and system check
systemSetup();
@ -257,8 +259,9 @@ void setup() {
// Set up delay() after loop callbacks are finished
// Note: should be after settingsSetup()
_loop_delay = atol(getSetting("loopDelay", LOOP_DELAY_TIME).c_str());
_loop_delay = constrain(_loop_delay, 0, 300);
_loop_delay = constrain(
getSetting("loopDelay", LOOP_DELAY_TIME), 0, 300
);
saveSettings();


+ 4
- 4
code/espurna/homeassistant.ino View File

@ -270,7 +270,7 @@ void _haSendSwitch(unsigned char i, JsonObject& config) {
config["rgb_state_topic"] = mqttTopic(MQTT_TOPIC_COLOR_RGB, false);
config["rgb_command_topic"] = mqttTopic(MQTT_TOPIC_COLOR_RGB, true);
}
if (lightUseCCT()) {
if (lightHasColor() || lightUseCCT()) {
config["color_temp_command_topic"] = mqttTopic(MQTT_TOPIC_MIRED, true);
config["color_temp_state_topic"] = mqttTopic(MQTT_TOPIC_MIRED, false);
}
@ -429,7 +429,7 @@ void _haSend() {
}
void _haConfigure() {
const bool enabled = getSetting("haEnabled", HOMEASSISTANT_ENABLED).toInt() == 1;
const bool enabled = getSetting("haEnabled", 1 == HOMEASSISTANT_ENABLED);
_ha_send_flag = (enabled != _ha_enabled);
_ha_enabled = enabled;
@ -439,7 +439,7 @@ void _haConfigure() {
// in case useCSS value is ever cached by the lights module
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
if (enabled) {
if (getSetting("useCSS", LIGHT_USE_CSS).toInt() == 1) {
if (getSetting("useCSS", 1 == LIGHT_USE_CSS)) {
setSetting("useCSS", 0);
}
}
@ -460,7 +460,7 @@ void _haWebSocketOnVisible(JsonObject& root) {
void _haWebSocketOnConnected(JsonObject& root) {
root["haPrefix"] = getSetting("haPrefix", HOMEASSISTANT_PREFIX);
root["haEnabled"] = getSetting("haEnabled", HOMEASSISTANT_ENABLED).toInt() == 1;
root["haEnabled"] = getSetting("haEnabled", 1 == HOMEASSISTANT_ENABLED);
}
void _haWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {


+ 17
- 8
code/espurna/i2c.ino View File

@ -21,6 +21,14 @@ unsigned long _i2c_scl_frequency = 0;
// Private
// -----------------------------------------------------------------------------
int _i2cGetSDA() {
return getSetting("i2cSDA", I2C_SDA_PIN);
}
int _i2cGetSCL() {
return getSetting("i2cSCL", I2C_SCL_PIN);
}
int _i2cClearbus(int sda, int scl) {
#if defined(TWCR) && defined(TWEN)
@ -279,9 +287,10 @@ int16_t i2c_read_int16_le(uint8_t address, uint8_t reg) {
// -----------------------------------------------------------------------------
void i2cClearBus() {
unsigned char sda = getSetting("i2cSDA", I2C_SDA_PIN).toInt();
unsigned char scl = getSetting("i2cSCL", I2C_SCL_PIN).toInt();
DEBUG_MSG_P(PSTR("[I2C] Clear bus (response: %d)\n"), _i2cClearbus(sda, scl));
DEBUG_MSG_P(
PSTR("[I2C] Clear bus (response: %d)\n"),
_i2cClearbus(_i2cGetSDA(), _i2cGetSCL())
);
}
bool i2cCheck(unsigned char address) {
@ -371,18 +380,18 @@ void _i2cInitCommands() {
void i2cSetup() {
unsigned char sda = getSetting("i2cSDA", I2C_SDA_PIN).toInt();
unsigned char scl = getSetting("i2cSCL", I2C_SCL_PIN).toInt();
const auto sda = _i2cGetSDA();
const auto scl = _i2cGetSCL();
#if I2C_USE_BRZO
unsigned long cst = getSetting("i2cCST", I2C_CLOCK_STRETCH_TIME).toInt();
_i2c_scl_frequency = getSetting("i2cFreq", I2C_SCL_FREQUENCY).toInt();
auto cst = getSetting("i2cCST", I2C_CLOCK_STRETCH_TIME);
_i2c_scl_frequency = getSetting("i2cFreq", I2C_SCL_FREQUENCY);
brzo_i2c_setup(sda, scl, cst);
#else
Wire.begin(sda, scl);
#endif
DEBUG_MSG_P(PSTR("[I2C] Using GPIO%u for SDA and GPIO%u for SCL\n"), sda, scl);
DEBUG_MSG_P(PSTR("[I2C] Using GPIO%02d for SDA and GPIO%02d for SCL\n"), sda, scl);
#if TERMINAL_SUPPORT
_i2cInitCommands();


+ 4
- 4
code/espurna/influxdb.ino View File

@ -118,16 +118,16 @@ void _idbWebSocketOnVisible(JsonObject& root) {
}
void _idbWebSocketOnConnected(JsonObject& root) {
root["idbEnabled"] = getSetting("idbEnabled", INFLUXDB_ENABLED).toInt() == 1;
root["idbEnabled"] = getSetting("idbEnabled", 1 == INFLUXDB_ENABLED);
root["idbHost"] = getSetting("idbHost", INFLUXDB_HOST);
root["idbPort"] = getSetting("idbPort", INFLUXDB_PORT).toInt();
root["idbPort"] = getSetting("idbPort", INFLUXDB_PORT);
root["idbDatabase"] = getSetting("idbDatabase", INFLUXDB_DATABASE);
root["idbUsername"] = getSetting("idbUsername", INFLUXDB_USERNAME);
root["idbPassword"] = getSetting("idbPassword", INFLUXDB_PASSWORD);
}
void _idbConfigure() {
_idb_enabled = getSetting("idbEnabled", INFLUXDB_ENABLED).toInt() == 1;
_idb_enabled = getSetting("idbEnabled", 1 == INFLUXDB_ENABLED);
if (_idb_enabled && (getSetting("idbHost", INFLUXDB_HOST).length() == 0)) {
_idb_enabled = false;
setSetting("idbEnabled", 0);
@ -196,7 +196,7 @@ void _idbFlush() {
host = mdnsResolve(host);
#endif
const uint16_t port = getSetting("idbPort", INFLUXDB_PORT).toInt();
const auto port = getSetting<uint16_t>("idbPort", INFLUXDB_PORT);
// TODO: should we always store specific pairs like tspk keeps relay / sensor readings?
// note that we also send heartbeat data, persistent values should be flagged


+ 8
- 8
code/espurna/led.ino View File

@ -89,8 +89,8 @@ void _ledWebSocketOnConnected(JsonObject& root) {
JsonArray& leds = root.createNestedArray("ledConfig");
for (byte i=0; i<_ledCount(); i++) {
JsonObject& led = leds.createNestedObject();
led["mode"] = getSetting("ledMode", i, "").toInt();
led["relay"] = getSetting("ledRelay", i, "").toInt();
led["mode"] = getSetting({"ledMode", i}, _ledMode(i));
led["relay"] = getSetting({"ledRelay", i}, _ledRelay(i));
}
}
@ -152,9 +152,9 @@ unsigned char _ledCount() {
}
void _ledConfigure() {
for (unsigned int i=0; i < _leds.size(); i++) {
_ledMode(i, getSetting("ledMode", i, _ledMode(i)).toInt());
_ledRelay(i, getSetting("ledRelay", i, _ledRelay(i)).toInt());
for (unsigned char i=0; i < _leds.size(); i++) {
_ledMode(i, getSetting({"ledMode", i}, _ledMode(i)));
_ledRelay(i, getSetting({"ledRelay", i}, _ledRelay(i)));
}
_led_update = true;
}
@ -192,9 +192,9 @@ void ledSetup() {
_leds.push_back((led_t) { LED8_PIN, LED8_PIN_INVERSE, LED8_MODE, LED8_RELAY - 1 });
#endif
for (unsigned int i=0; i < _leds.size(); i++) {
if (!hasSetting("ledMode", i)) setSetting("ledMode", i, _leds[i].mode);
if (!hasSetting("ledRelay", i)) setSetting("ledRelay", i, _leds[i].relay);
for (unsigned char i=0; i < _leds.size(); i++) {
if (!hasSetting({"ledMode", i})) setSetting({"ledMode", i}, _leds[i].mode);
if (!hasSetting({"ledRelay", i})) setSetting({"ledRelay", i}, _leds[i].relay);
pinMode(_leds[i].pin, OUTPUT);
_ledStatus(i, false);
}


+ 4
- 2
code/espurna/libs/EmbedisWrap.h View File

@ -4,13 +4,15 @@
#pragma once
#include "Embedis.h"
#include <Embedis.h>
class EmbedisWrap : public Embedis {
public:
EmbedisWrap(Stream& stream, size_t buflen = 128, size_t argvlen = 8): Embedis(stream, buflen, argvlen) {}
EmbedisWrap(Stream& stream, size_t buflen = 128, size_t argvlen = 8) :
Embedis(stream, buflen, argvlen)
{}
unsigned char getCommandCount() {
return commands.size();


+ 23
- 23
code/espurna/light.ino View File

@ -22,6 +22,8 @@ extern "C" {
#include "libs/fs_math.h"
}
#define ARRAYINIT(type, name, ...) type name[] = {__VA_ARGS__};
#if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER
#define PWM_CHANNEL_NUM_MAX LIGHT_CHANNELS
extern "C" {
@ -628,8 +630,8 @@ void _lightRestoreRtcmem() {
}
void _lightSaveSettings() {
for (unsigned int i=0; i < _light_channel.size(); i++) {
setSetting("ch", i, _light_channel[i].inputValue);
for (unsigned char i=0; i < _light_channel.size(); ++i) {
setSetting({"ch", i}, _light_channel[i].inputValue);
}
setSetting("brightness", _light_brightness);
setSetting("mireds", _light_mireds);
@ -637,11 +639,11 @@ void _lightSaveSettings() {
}
void _lightRestoreSettings() {
for (unsigned int i=0; i < _light_channel.size(); i++) {
_light_channel[i].inputValue = getSetting("ch", i, (i == 0) ? Light::VALUE_MAX : 0).toInt();
for (unsigned char i=0; i < _light_channel.size(); ++i) {
_light_channel[i].inputValue = getSetting({"ch", i}, (i == 0) ? Light::VALUE_MAX : 0);
}
_light_brightness = getSetting("brightness", Light::BRIGHTNESS_MAX).toInt();
_light_mireds = getSetting("mireds", _light_mireds).toInt();
_light_brightness = getSetting("brightness", Light::BRIGHTNESS_MAX);
_light_mireds = getSetting("mireds", _light_mireds);
}
// -----------------------------------------------------------------------------
@ -752,7 +754,7 @@ void lightMQTT() {
if (_light_has_color) {
// Color
if (getSetting("useCSS", LIGHT_USE_CSS).toInt() == 1) {
if (getSetting("useCSS", 1 == LIGHT_USE_CSS)) {
_toRGB(buffer, sizeof(buffer), true);
} else {
_toLong(buffer, sizeof(buffer), true);
@ -1003,7 +1005,7 @@ bool _lightWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
void _lightWebSocketStatus(JsonObject& root) {
if (_light_has_color) {
if (getSetting("useRGB", LIGHT_USE_RGB).toInt() == 1) {
if (getSetting("useRGB", 1 == LIGHT_USE_RGB)) {
root["rgb"] = lightColor(true);
} else {
root["hsv"] = lightColor(false);
@ -1033,8 +1035,8 @@ void _lightWebSocketOnConnected(JsonObject& root) {
root["useWhite"] = _light_use_white;
root["useGamma"] = _light_use_gamma;
root["useTransitions"] = _light_use_transitions;
root["useCSS"] = getSetting("useCSS", LIGHT_USE_CSS).toInt() == 1;
root["useRGB"] = getSetting("useRGB", LIGHT_USE_RGB).toInt() == 1;
root["useCSS"] = getSetting("useCSS", 1 == LIGHT_USE_CSS);
root["useRGB"] = getSetting("useRGB", 1 == LIGHT_USE_RGB);
root["lightTime"] = _light_transition_time;
_lightWebSocketStatus(root);
@ -1089,7 +1091,7 @@ void _lightAPISetup() {
apiRegister(MQTT_TOPIC_COLOR_RGB,
[](char * buffer, size_t len) {
if (getSetting("useCSS", LIGHT_USE_CSS).toInt() == 1) {
if (getSetting("useCSS", 1 == LIGHT_USE_CSS)) {
_toRGB(buffer, len, true);
} else {
_toLong(buffer, len, true);
@ -1260,13 +1262,13 @@ const unsigned long _light_iofunc[16] PROGMEM = {
void _lightConfigure() {
_light_has_color = getSetting("useColor", LIGHT_USE_COLOR).toInt() == 1;
_light_has_color = getSetting("useColor", 1 == LIGHT_USE_COLOR);
if (_light_has_color && (_light_channel.size() < 3)) {
_light_has_color = false;
setSetting("useColor", _light_has_color);
}
_light_use_white = getSetting("useWhite", LIGHT_USE_WHITE).toInt() == 1;
_light_use_white = getSetting("useWhite", 1 == LIGHT_USE_WHITE);
if (_light_use_white && (_light_channel.size() < 4) && (_light_channel.size() != 2)) {
_light_use_white = false;
setSetting("useWhite", _light_use_white);
@ -1282,22 +1284,20 @@ void _lightConfigure() {
_light_brightness_func = []() { _lightApplyBrightness(); };
}
_light_use_cct = getSetting("useCCT", LIGHT_USE_CCT).toInt() == 1;
_light_use_cct = getSetting("useCCT", 1 == LIGHT_USE_CCT);
if (_light_use_cct && (((_light_channel.size() < 5) && (_light_channel.size() != 2)) || !_light_use_white)) {
_light_use_cct = false;
setSetting("useCCT", _light_use_cct);
}
if (_light_use_cct) {
_light_cold_mireds = getSetting("lightColdMired", LIGHT_COLDWHITE_MIRED).toInt();
_light_warm_mireds = getSetting("lightWarmMired", LIGHT_WARMWHITE_MIRED).toInt();
_light_cold_kelvin = (1000000L / _light_cold_mireds);
_light_warm_kelvin = (1000000L / _light_warm_mireds);
}
_light_cold_mireds = getSetting("lightColdMired", LIGHT_COLDWHITE_MIRED);
_light_warm_mireds = getSetting("lightWarmMired", LIGHT_WARMWHITE_MIRED);
_light_cold_kelvin = (1000000L / _light_cold_mireds);
_light_warm_kelvin = (1000000L / _light_warm_mireds);
_light_use_gamma = getSetting("useGamma", LIGHT_USE_GAMMA).toInt() == 1;
_light_use_transitions = getSetting("useTransitions", LIGHT_USE_TRANSITIONS).toInt() == 1;
_light_transition_time = getSetting("lightTime", LIGHT_TRANSITION_TIME).toInt();
_light_use_gamma = getSetting("useGamma", 1 == LIGHT_USE_GAMMA);
_light_use_transitions = getSetting("useTransitions", 1 == LIGHT_USE_TRANSITIONS);
_light_transition_time = getSetting("lightTime", LIGHT_TRANSITION_TIME);
}


+ 1
- 1
code/espurna/mdns.ino View File

@ -40,7 +40,7 @@ void _mdnsServerStart() {
void mdnsServerSetup() {
#if WEB_SUPPORT
MDNS.addService("http", "tcp", getSetting("webPort", WEB_PORT).toInt());
MDNS.addService("http", "tcp", getSetting<uint16_t>("webPort", WEB_PORT));
#endif
#if TELNET_SUPPORT


+ 7
- 6
code/espurna/migrate.ino View File

@ -7,12 +7,13 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
void _cmpMoveIndexDown(const char * key, int offset = 0) {
if (hasSetting(key, 0)) return;
if (hasSetting({key, 0})) return;
for (unsigned char index = 1; index < SETTINGS_MAX_LIST_COUNT; index++) {
if (hasSetting(key, index)) {
setSetting(key, index - 1, getSetting(key, index).toInt() + offset);
const unsigned char prev = index - 1;
if (hasSetting({key, index})) {
setSetting({key, prev}, getSetting({key, index}).toInt() + offset);
} else {
delSetting(key, index - 1);
delSetting({key, prev});
}
}
}
@ -26,8 +27,8 @@ void _cmpMoveIndexDown(const char * key, int offset = 0) {
void migrate() {
// Get config version
unsigned int board = getSetting("board", 0).toInt();
unsigned int config_version = getSetting("cfg", board > 0 ? 2 : 1).toInt();
const auto board = getSetting("board", 0);
const auto config_version = getSetting("cfg", board > 0 ? 2 : 1);
// Update if not on latest version
if (config_version == CFG_VERSION) return;


+ 26
- 19
code/espurna/mqtt.ino View File

@ -61,9 +61,9 @@ unsigned long _mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
unsigned long _mqtt_last_connection = 0;
bool _mqtt_connected = false;
bool _mqtt_connecting = false;
unsigned char _mqtt_qos = MQTT_QOS;
bool _mqtt_retain = MQTT_RETAIN;
unsigned long _mqtt_keepalive = MQTT_KEEPALIVE;
int _mqtt_qos = MQTT_QOS;
int _mqtt_keepalive = MQTT_KEEPALIVE;
String _mqtt_topic;
String _mqtt_topic_json;
String _mqtt_setter;
@ -101,7 +101,7 @@ SecureClientConfig _mqtt_sc_config {
return _mqtt_server;
},
[]() -> int {
return getSetting("mqttScCheck", MQTT_SECURE_CLIENT_CHECK).toInt();
return getSetting("mqttScCheck", MQTT_SECURE_CLIENT_CHECK);
},
[]() -> String {
return getSetting("mqttFP", MQTT_SSL_FINGERPRINT);
@ -114,7 +114,7 @@ SecureClientConfig _mqtt_sc_config {
SecureClientConfig _mqtt_sc_config {
"MQTT",
[]() -> int {
return getSetting("mqttScCheck", MQTT_SECURE_CLIENT_CHECK).toInt();
return getSetting("mqttScCheck", MQTT_SECURE_CLIENT_CHECK);
},
[]() -> PGM_P {
return _mqtt_client_trusted_root_ca;
@ -123,7 +123,7 @@ SecureClientConfig _mqtt_sc_config {
return getSetting("mqttFP", MQTT_SSL_FINGERPRINT);
},
[]() -> uint16_t {
return getSetting("mqttScMFLN", MQTT_SECURE_CLIENT_MFLN).toInt();
return getSetting<uint16_t>("mqttScMFLN", MQTT_SECURE_CLIENT_MFLN);
},
true
};
@ -244,7 +244,7 @@ void _mqttConnect() {
_mqtt_connecting = true;
#if SECURE_CLIENT != SECURE_CLIENT_NONE
const bool secure = getSetting("mqttUseSSL", MQTT_SSL_ENABLED).toInt() == 1;
const bool secure = getSetting("mqttUseSSL", 1 == MQTT_SSL_ENABLED);
#else
const bool secure = false;
#endif
@ -304,11 +304,11 @@ void _mqttConfigure() {
// Enable only when server is set
{
String server = getSetting("mqttServer", MQTT_SERVER);
uint16_t port = getSetting("mqttPort", MQTT_PORT).toInt();
const String server = getSetting("mqttServer", MQTT_SERVER);
const auto port = getSetting<uint16_t>("mqttPort", MQTT_PORT);
bool enabled = false;
if (server.length()) {
enabled = getSetting("mqttEnabled", MQTT_ENABLED).toInt() == 1;
enabled = getSetting("mqttEnabled", 1 == MQTT_ENABLED);
}
_mqttApplySetting(_mqtt_server, server);
@ -348,9 +348,14 @@ void _mqttConfigure() {
String pass = getSetting("mqttPassword", MQTT_PASS);
unsigned char qos = getSetting("mqttQoS", MQTT_QOS).toInt();
bool retain = getSetting("mqttRetain", MQTT_RETAIN).toInt() == 1;
unsigned long keepalive = getSetting("mqttKeep", MQTT_KEEPALIVE).toInt();
const auto qos = getSetting("mqttQoS", MQTT_QOS);
const bool retain = getSetting("mqttRetain", 1 == MQTT_RETAIN);
// Note: MQTT spec defines this as 2 bytes
const auto keepalive = constrain(
getSetting("mqttKeep", MQTT_KEEPALIVE),
0, std::numeric_limits<uint16_t>::max()
);
String id = getSetting("mqttClientID", getIdentifier());
_mqttPlaceholders(id);
@ -370,7 +375,7 @@ void _mqttConfigure() {
// MQTT JSON
{
_mqttApplySetting(_mqtt_use_json, getSetting("mqttUseJson", MQTT_USE_JSON).toInt() == 1);
_mqttApplySetting(_mqtt_use_json, getSetting("mqttUseJson", 1 == MQTT_USE_JSON));
_mqttApplyTopic(_mqtt_topic_json, MQTT_TOPIC_JSON);
}
@ -462,11 +467,11 @@ void _mqttWebSocketOnConnected(JsonObject& root) {
root["mqttRetain"] = _mqtt_retain;
root["mqttQoS"] = _mqtt_qos;
#if SECURE_CLIENT != SECURE_CLIENT_NONE
root["mqttUseSSL"] = getSetting("mqttUseSSL", MQTT_SSL_ENABLED).toInt() == 1;
root["mqttUseSSL"] = getSetting("mqttUseSSL", 1 == MQTT_SSL_ENABLED);
root["mqttFP"] = getSetting("mqttFP", MQTT_SSL_FINGERPRINT);
#endif
root["mqttTopic"] = getSetting("mqttTopic", MQTT_TOPIC);
root["mqttUseJson"] = getSetting("mqttUseJson", MQTT_USE_JSON).toInt() == 1;
root["mqttUseJson"] = getSetting("mqttUseJson", 1 == MQTT_USE_JSON);
}
#endif
@ -887,18 +892,20 @@ void mqttRegister(mqtt_callback_f callback) {
_mqtt_callbacks.push_back(callback);
}
void mqttSetBroker(IPAddress ip, unsigned int port) {
void mqttSetBroker(IPAddress ip, uint16_t port) {
setSetting("mqttServer", ip.toString());
_mqtt_server = ip.toString();
setSetting("mqttPort", port);
_mqtt_port = port;
mqttEnabled(MQTT_AUTOCONNECT);
mqttEnabled(1 == MQTT_AUTOCONNECT);
}
void mqttSetBrokerIfNone(IPAddress ip, unsigned int port) {
if (getSetting("mqttServer", MQTT_SERVER).length() == 0) mqttSetBroker(ip, port);
void mqttSetBrokerIfNone(IPAddress ip, uint16_t port) {
if (getSetting("mqttServer", MQTT_SERVER).length() == 0) {
mqttSetBroker(ip, port);
}
}
const String& mqttPayloadOnline() {


+ 3
- 3
code/espurna/nofuss.ino View File

@ -30,7 +30,7 @@ void _nofussWebSocketOnVisible(JsonObject& root) {
}
void _nofussWebSocketOnConnected(JsonObject& root) {
root["nofussEnabled"] = getSetting("nofussEnabled", NOFUSS_ENABLED).toInt() == 1;
root["nofussEnabled"] = getSetting("nofussEnabled", 1 == NOFUSS_ENABLED);
root["nofussServer"] = getSetting("nofussServer", NOFUSS_SERVER);
}
@ -47,9 +47,9 @@ void _nofussConfigure() {
setSetting("nofussEnabled", 0);
_nofussEnabled = false;
} else {
_nofussEnabled = getSetting("nofussEnabled", NOFUSS_ENABLED).toInt() == 1;
_nofussEnabled = getSetting("nofussEnabled", 1 == NOFUSS_ENABLED);
}
_nofussInterval = getSetting("nofussInterval", NOFUSS_INTERVAL).toInt();
_nofussInterval = getSetting("nofussInterval", NOFUSS_INTERVAL);
_nofussLastCheck = 0;
if (!_nofussEnabled) {


+ 9
- 9
code/espurna/ntp.ino View File

@ -42,9 +42,9 @@ void _ntpWebSocketOnData(JsonObject& root) {
void _ntpWebSocketOnConnected(JsonObject& root) {
root["ntpServer"] = getSetting("ntpServer", NTP_SERVER);
root["ntpOffset"] = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
root["ntpDST"] = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1;
root["ntpRegion"] = getSetting("ntpRegion", NTP_DST_REGION).toInt();
root["ntpOffset"] = getSetting("ntpOffset", NTP_TIME_OFFSET);
root["ntpDST"] = getSetting("ntpDST", 1 == NTP_DAY_LIGHT);
root["ntpRegion"] = getSetting("ntpRegion", NTP_DST_REGION);
}
#endif
@ -90,7 +90,7 @@ void _ntpConfigure() {
_ntp_configure = false;
int offset = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
int offset = getSetting("ntpOffset", NTP_TIME_OFFSET);
int sign = offset > 0 ? 1 : -1;
offset = abs(offset);
int tz_hours = sign * (offset / 60);
@ -100,7 +100,7 @@ void _ntpConfigure() {
_ntp_report = true;
}
bool daylight = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1;
const bool daylight = getSetting("ntpDST", 1 == NTP_DAY_LIGHT);
if (NTPw.getDayLight() != daylight) {
NTPw.setDayLight(daylight);
_ntp_report = true;
@ -111,12 +111,12 @@ void _ntpConfigure() {
NTPw.setNtpServerName(server);
}
uint8_t dst_region = getSetting("ntpRegion", NTP_DST_REGION).toInt();
uint8_t dst_region = getSetting("ntpRegion", NTP_DST_REGION);
NTPw.setDSTZone(dst_region);
// Some remote servers can be slow to respond, increase accordingly
// TODO does this need upper constrain?
NTPw.setNTPTimeout(getSetting("ntpTimeout", NTP_TIMEOUT).toInt());
NTPw.setNTPTimeout(getSetting("ntpTimeout", NTP_TIMEOUT));
}
@ -174,7 +174,7 @@ void _ntpBackwards() {
moveSetting("ntpServer1", "ntpServer");
delSetting("ntpServer2");
delSetting("ntpServer3");
int offset = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
int offset = getSetting("ntpOffset", NTP_TIME_OFFSET);
if (-30 < offset && offset < 30) {
offset *= 60;
setSetting("ntpOffset", offset);
@ -209,7 +209,7 @@ String ntpDateTime() {
// XXX: returns garbage during DST switch
time_t ntpLocal2UTC(time_t local) {
int offset = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
int offset = getSetting("ntpOffset", NTP_TIME_OFFSET);
if (NTPw.isSummerTime()) offset += 60;
return local - offset * 60;
}


+ 1
- 1
code/espurna/ota_asynctcp.ino View File

@ -143,7 +143,7 @@ void _otaClientOnConnect(void* arg, AsyncClient* client) {
ota_client_t* ota_client = reinterpret_cast<ota_client_t*>(arg);
#if ASYNC_TCP_SSL_ENABLED
int check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt();
const auto check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK);
if ((check == SECURE_CLIENT_CHECK_FINGERPRINT) && (443 == _ota_url->port)) {
uint8_t fp[20] = {0};
sslFingerPrintArray(getSetting("otaFP", OTA_FINGERPRINT).c_str(), fp);


+ 3
- 3
code/espurna/ota_httpupdate.ino View File

@ -82,7 +82,7 @@ SecureClientConfig _ota_sc_config {
return String(); // NOTE: unused
},
[]() -> int {
return getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt();
return getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK);
},
[]() -> String {
return getSetting("otaFP", OTA_FINGERPRINT);
@ -95,7 +95,7 @@ SecureClientConfig _ota_sc_config {
SecureClientConfig _ota_sc_config {
"OTA",
[]() -> int {
return getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt();
return getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK);
},
[]() -> PGM_P {
return _ota_client_trusted_root_ca;
@ -104,7 +104,7 @@ SecureClientConfig _ota_sc_config {
return getSetting("otaFP", OTA_FINGERPRINT);
},
[]() -> uint16_t {
return getSetting("otaScMFLN", OTA_SECURE_CLIENT_MFLN).toInt();
return getSetting("otaScMFLN", OTA_SECURE_CLIENT_MFLN);
},
true
};


+ 34
- 33
code/espurna/relay.ino View File

@ -367,7 +367,7 @@ void _relayProcess(bool mode) {
// We will trigger a eeprom save only if
// we care about current relay status on boot
const auto boot_mode = getSetting("relayBoot", id, RELAY_BOOT_MODE).toInt();
const auto boot_mode = getSetting({"relayBoot", id}, RELAY_BOOT_MODE);
const bool save_eeprom = ((RELAY_BOOT_SAME == boot_mode) || (RELAY_BOOT_TOGGLE == boot_mode));
_relay_save_timer.once_ms(RELAY_SAVE_DELAY, relaySave, save_eeprom);
@ -469,8 +469,8 @@ void relayPulse(unsigned char id) {
DEBUG_MSG_P(PSTR("[RELAY] Scheduling relay #%d back in %lums (pulse)\n"), id, ms);
_relays[id].pulseTicker.once_ms(ms, relayToggle, id);
// Reconfigure after dynamic pulse
_relays[id].pulse = getSetting("relayPulse", id, RELAY_PULSE_MODE).toInt();
_relays[id].pulse_ms = 1000 * getSetting("relayTime", id, RELAY_PULSE_MODE).toFloat();
_relays[id].pulse = getSetting({"relayPulse", id}, RELAY_PULSE_MODE);
_relays[id].pulse_ms = 1000 * getSetting({"relayTime", id}, 0.);
}
}
@ -724,10 +724,11 @@ void _relayBackwards() {
}
#endif
for (unsigned int i=0; i<_relays.size(); i++) {
if (!hasSetting("mqttGroupInv", i)) continue;
setSetting("mqttGroupSync", i, getSetting("mqttGroupInv", i));
delSetting("mqttGroupInv", i);
for (unsigned char id = 0; id < _relays.size(); ++id) {
const settings_key_t key {"mqttGroupInv", id};
if (!hasSetting(key)) continue;
setSetting({"mqttGroupSync", id}, getSetting(key));
delSetting(key);
}
}
@ -749,8 +750,8 @@ void _relayBoot() {
bool status;
for (unsigned char i=0; i<relayCount(); ++i) {
unsigned char boot_mode = getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt();
DEBUG_MSG_P(PSTR("[RELAY] Relay #%u boot mode %u\n"), i, boot_mode);
const auto boot_mode = getSetting({"relayBoot", i}, RELAY_BOOT_MODE);
DEBUG_MSG_P(PSTR("[RELAY] Relay #%u boot mode %d\n"), i, boot_mode);
status = false;
lock = RELAY_LOCK_DISABLED;
@ -804,12 +805,12 @@ void _relayBoot() {
}
void _relayConfigure() {
for (unsigned int i=0; i<_relays.size(); i++) {
_relays[i].pulse = getSetting("relayPulse", i, RELAY_PULSE_MODE).toInt();
_relays[i].pulse_ms = 1000 * getSetting("relayTime", i, RELAY_PULSE_MODE).toFloat();
for (unsigned char i = 0, relays = _relays.size() ; (i < relays); ++i) {
_relays[i].pulse = getSetting({"relayPulse", i}, RELAY_PULSE_MODE);
_relays[i].pulse_ms = 1000 * getSetting({"relayTime", i}, 0.);
_relays[i].delay_on = getSetting("relayDelayOn", i, _relayDelayOn(i)).toInt();
_relays[i].delay_off = getSetting("relayDelayOff", i, _relayDelayOff(i)).toInt();
_relays[i].delay_on = getSetting({"relayDelayOn", i}, _relayDelayOn(i));
_relays[i].delay_off = getSetting({"relayDelayOff", i}, _relayDelayOff(i));
if (GPIO_NONE == _relays[i].pin) continue;
@ -823,11 +824,11 @@ void _relayConfigure() {
}
}
_relay_flood_window = (1000 * getSetting("relayFloodTime", RELAY_FLOOD_WINDOW).toInt());
_relay_flood_changes = getSetting("relayFloodChanges", RELAY_FLOOD_CHANGES).toInt();
_relay_flood_window = (1000 * getSetting("relayFloodTime", RELAY_FLOOD_WINDOW));
_relay_flood_changes = getSetting("relayFloodChanges", RELAY_FLOOD_CHANGES);
_relay_delay_interlock = getSetting("relayDelayInterlock", RELAY_DELAY_INTERLOCK).toInt();
_relay_sync_mode = getSetting("relaySync", RELAY_SYNC).toInt();
_relay_delay_interlock = getSetting("relayDelayInterlock", RELAY_DELAY_INTERLOCK);
_relay_sync_mode = getSetting("relaySync", RELAY_SYNC);
#if MQTT_SUPPORT
settingsProcessConfig({
@ -918,19 +919,19 @@ void _relayWebSocketSendRelays(JsonObject& root) {
type.add(_relays[i].type);
reset.add(_relays[i].reset_pin);
boot.add(getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt());
boot.add(getSetting({"relayBoot", i}, RELAY_BOOT_MODE));
pulse.add(_relays[i].pulse);
pulse_time.add(_relays[i].pulse_ms / 1000.0);
#if SCHEDULER_SUPPORT
sch_last.add(getSetting("relayLastSch", i, SCHEDULER_RESTORE_LAST_SCHEDULE).toInt());
sch_last.add(getSetting({"relayLastSch", i}, SCHEDULER_RESTORE_LAST_SCHEDULE));
#endif
#if MQTT_SUPPORT
group.add(getSetting("mqttGroup", i, ""));
group_sync.add(getSetting("mqttGroupSync", i, 0).toInt());
on_disconnect.add(getSetting("relayOnDisc", i, 0).toInt());
group.add(getSetting({"mqttGroup", i}));
group_sync.add(getSetting({"mqttGroupSync", i}, 0));
on_disconnect.add(getSetting({"relayOnDisc", i}, 0));
#endif
}
}
@ -1083,10 +1084,10 @@ const char* relayPayload(RelayStatus status) {
}
void _relayMQTTGroup(unsigned char id) {
String topic = getSetting("mqttGroup", id, "");
const String topic = getSetting({"mqttGroup", id});
if (!topic.length()) return;
unsigned char mode = getSetting("mqttGroupSync", id, RELAY_GROUP_SYNC_NORMAL).toInt();
const auto mode = getSetting({"mqttGroupSync", id}, RELAY_GROUP_SYNC_NORMAL);
if (mode == RELAY_GROUP_SYNC_RECEIVEONLY) return;
auto status = _relayStatusTyped(id);
@ -1167,8 +1168,8 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
#endif
// Subscribe to group topics
for (unsigned int i=0; i < _relays.size(); i++) {
String t = getSetting("mqttGroup", i, "");
for (unsigned char i=0; i < _relays.size(); i++) {
const auto t = getSetting({"mqttGroup", i});
if (t.length() > 0) mqttSubscribeRaw(t.c_str());
}
@ -1224,9 +1225,9 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
// Check group topics
for (unsigned int i=0; i < _relays.size(); i++) {
for (unsigned char i=0; i < _relays.size(); i++) {
String t = getSetting("mqttGroup", i, "");
const String t = getSetting({"mqttGroup", i});
if ((t.length() > 0) && t.equals(topic)) {
@ -1234,7 +1235,7 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
if (value == RelayStatus::UNKNOWN) return;
if ((value == RelayStatus::ON) || (value == RelayStatus::OFF)) {
if (getSetting("mqttGroupSync", i, RELAY_GROUP_SYNC_NORMAL).toInt() == RELAY_GROUP_SYNC_INVERSE) {
if (getSetting({"mqttGroupSync", i}, RELAY_GROUP_SYNC_NORMAL) == RELAY_GROUP_SYNC_INVERSE) {
value = _relayStatusInvert(value);
}
}
@ -1255,8 +1256,8 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
}
if (type == MQTT_DISCONNECT_EVENT) {
for (unsigned int i=0; i < _relays.size(); i++){
int reaction = getSetting("relayOnDisc", i, 0).toInt();
for (unsigned char i=0; i < _relays.size(); i++){
const auto reaction = getSetting({"relayOnDisc", i}, 0);
if (1 == reaction) { // switch relay OFF
DEBUG_MSG_P(PSTR("[RELAY] Reset relay (%d) due to MQTT disconnection\n"), i);
relayStatusWrap(i, RelayStatus::OFF, false);
@ -1422,7 +1423,7 @@ void relaySetup() {
_relaySetupAdhoc();
// Dummy (virtual) relays
relaySetupDummy(getSetting("relayDummy", DUMMY_RELAY_COUNT).toInt());
relaySetupDummy(getSetting("relayDummy", DUMMY_RELAY_COUNT));
_relaySetupProvider();
_relayBackwards();


+ 16
- 12
code/espurna/rfbridge.ino View File

@ -106,12 +106,12 @@ void _rfbWebSocketOnVisible(JsonObject& root) {
}
void _rfbWebSocketOnConnected(JsonObject& root) {
root["rfbRepeat"] = getSetting("rfbRepeat", RF_SEND_TIMES).toInt();
root["rfbRepeat"] = getSetting("rfbRepeat", RF_SEND_TIMES);
root["rfbCount"] = relayCount();
#if RFB_DIRECT
root["rfbdirectVisible"] = 1;
root["rfbRX"] = getSetting("rfbRX", RFB_RX_PIN).toInt();
root["rfbTX"] = getSetting("rfbTX", RFB_TX_PIN).toInt();
root["rfbRX"] = getSetting("rfbRX", RFB_RX_PIN);
root["rfbTX"] = getSetting("rfbTX", RFB_TX_PIN);
#endif
}
@ -666,15 +666,19 @@ void _rfbInitCommands() {
void rfbStore(unsigned char id, bool status, const char * code) {
DEBUG_MSG_P(PSTR("[RF] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
char key[RF_MAX_KEY_LENGTH] = {0};
snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
setSetting(key, code);
if (status) {
setSetting({"rfbON", id}, code);
} else {
setSetting({"rfbOFF", id}, code);
}
}
String rfbRetrieve(unsigned char id, bool status) {
char key[RF_MAX_KEY_LENGTH] = {0};
snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
return getSetting(key);
if (status) {
return getSetting({"rfbON", id});
} else {
return getSetting({"rfbOFF", id});
}
}
void rfbStatus(unsigned char id, bool status) {
@ -756,11 +760,11 @@ void rfbSetup() {
_rfbInitCommands();
#endif
_rfb_repeat = getSetting("rfbRepeat", RF_SEND_TIMES).toInt();
_rfb_repeat = getSetting("rfbRepeat", RF_SEND_TIMES);
#if RFB_DIRECT
unsigned char rx = getSetting("rfbRX", RFB_RX_PIN).toInt();
unsigned char tx = getSetting("rfbTX", RFB_TX_PIN).toInt();
const auto rx = getSetting("rfbRX", RFB_RX_PIN);
const auto tx = getSetting("rfbTX", RFB_TX_PIN);
_rfb_receive = gpioValid(rx);
_rfb_transmit = gpioValid(tx);


+ 9
- 9
code/espurna/rfm69.ino View File

@ -44,12 +44,12 @@ void _rfm69WebSocketOnConnected(JsonObject& root) {
root["nodeCount"] = _rfm69_node_count;
JsonArray& mappings = root.createNestedArray("mapping");
for (unsigned char i=0; i<RFM69_MAX_TOPICS; i++) {
unsigned char node = getSetting("node", i, 0).toInt();
auto node = getSetting({"node", i}, 0);
if (0 == node) break;
JsonObject& mapping = mappings.createNestedObject();
mapping["node"] = node;
mapping["key"] = getSetting("key", i, "");
mapping["topic"] = getSetting("topic", i, "");
mapping["key"] = getSetting({"key", i});
mapping["topic"] = getSetting({"topic", i});
}
}
@ -74,9 +74,9 @@ void _rfm69CleanNodes(unsigned char num) {
// Look for the last defined node
int i = 0;
while (i < num) {
if (getSetting("node", i, 0).toInt() == 0) break;
if (getSetting("key", i, "").length() == 0) break;
if (getSetting("topic", i, "").length() == 0) break;
if (0 == getSetting({"node", i}, 0)) break;
if (!getSetting({"key", i}).length()) break;
if (!getSetting({"topic", i}).length()) break;
++i;
}
@ -166,10 +166,10 @@ void _rfm69Process(packet_t * data) {
// Try to find a matching mapping
for (unsigned int i=0; i<RFM69_MAX_TOPICS; i++) {
unsigned char node = getSetting("node", i, 0).toInt();
auto node = getSetting({"node", i}, 0);
if (0 == node) break;
if ((node == data->senderID) && (getSetting("key", i, "").equals(data->key))) {
mqttSendRaw((char *) getSetting("topic", i, "").c_str(), (char *) String(data->value).c_str());
if ((node == data->senderID) && (getSetting({"key", i}).equals(data->key))) {
mqttSendRaw((char *) getSetting({"topic", i}).c_str(), (char *) String(data->value).c_str());
return;
}
}


+ 16
- 16
code/espurna/rpnrules.ino View File

@ -29,27 +29,27 @@ bool _rpnWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
void _rpnWebSocketOnConnected(JsonObject& root) {
root["rpnSticky"] = getSetting("rpnSticky", 1).toInt();
root["rpnDelay"] = getSetting("rpnDelay", RPN_DELAY).toInt();
root["rpnSticky"] = getSetting("rpnSticky", 1 == RPN_STICKY);
root["rpnDelay"] = getSetting("rpnDelay", RPN_DELAY);
JsonArray& rules = root.createNestedArray("rpnRules");
unsigned char i = 0;
String rule = getSetting("rpnRule", i, "");
String rule = getSetting({"rpnRule", i});
while (rule.length()) {
rules.add(rule);
rule = getSetting("rpnRule", ++i, "");
rule = getSetting({"rpnRule", ++i});
}
#if MQTT_SUPPORT
i=0;
JsonArray& topics = root.createNestedArray("rpnTopics");
JsonArray& names = root.createNestedArray("rpnNames");
String rpn_topic = getSetting("rpnTopic", i, "");
String rpn_topic = getSetting({"rpnTopic", i});
while (rpn_topic.length() > 0) {
String rpn_name = getSetting("rpnName", i, "");
String rpn_name = getSetting({"rpnName", i});
topics.add(rpn_topic);
names.add(rpn_name);
rpn_topic = getSetting("rpnTopic", ++i, "");
rpn_topic = getSetting({"rpnTopic", ++i});
}
#endif
@ -59,10 +59,10 @@ void _rpnWebSocketOnConnected(JsonObject& root) {
void _rpnMQTTSubscribe() {
unsigned char i = 0;
String rpn_topic = getSetting("rpnTopic", i, "");
String rpn_topic = getSetting({"rpnTopic", i});
while (rpn_topic.length()) {
mqttSubscribeRaw(rpn_topic.c_str());
rpn_topic = getSetting("rpnTopic", ++i, "");
rpn_topic = getSetting({"rpnTopic", ++i});
}
}
@ -74,10 +74,10 @@ void _rpnMQTTCallback(unsigned int type, const char * topic, const char * payloa
if (type == MQTT_MESSAGE_EVENT) {
unsigned char i = 0;
String rpn_topic = getSetting("rpnTopic", i, "");
String rpn_topic = getSetting({"rpnTopic", i});
while (rpn_topic.length()) {
if (rpn_topic.equals(topic)) {
String rpn_name = getSetting("rpnName", i, "");
String rpn_name = getSetting({"rpnName", i});
if (rpn_name.length()) {
rpn_variable_set(_rpn_ctxt, rpn_name.c_str(), atof(payload));
_rpn_last = millis();
@ -85,7 +85,7 @@ void _rpnMQTTCallback(unsigned int type, const char * topic, const char * payloa
break;
}
}
rpn_topic = getSetting("rpnTopic", ++i, "");
rpn_topic = getSetting({"rpnTopic", ++i});
}
}
@ -96,7 +96,7 @@ void _rpnConfigure() {
#if MQTT_SUPPORT
if (mqttConnected()) _rpnMQTTSubscribe();
#endif
_rpn_delay = getSetting("rpnDelay", RPN_DELAY).toInt();
_rpn_delay = getSetting("rpnDelay", RPN_DELAY);
}
void _rpnBrokerCallback(const String& topic, unsigned char id, double value, const char*) {
@ -256,16 +256,16 @@ void _rpnDump() {
void _rpnRun() {
unsigned char i = 0;
String rule = getSetting("rpnRule", i, "");
String rule = getSetting({"rpnRule", i});
while (rule.length()) {
//DEBUG_MSG_P(PSTR("[RPN] Running \"%s\"\n"), rule.c_str());
rpn_process(_rpn_ctxt, rule.c_str(), true);
//_rpnDump();
rule = getSetting("rpnRule", ++i, "");
rule = getSetting({"rpnRule", ++i});
rpn_stack_clear(_rpn_ctxt);
}
if (getSetting("rpnSticky", 1).toInt() == 0) {
if (getSetting("rpnSticky", 1 == RPN_STICKY)) {
rpn_variables_clear(_rpn_ctxt);
}


+ 38
- 37
code/espurna/scheduler.ino View File

@ -46,19 +46,19 @@ void _schWebSocketOnConnected(JsonObject &root){
uint8_t size = 0;
for (byte i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
if (!hasSetting("schSwitch", i)) break;
for (unsigned char i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
if (!getSetting({"schSwitch", i}).length()) break;
++size;
enabled.add<uint8_t>(getSetting("schEnabled", i, 0).toInt() == 1);
utc.add<uint8_t>(getSetting("schUTC", i, 0).toInt() == 1);
enabled.add(getSetting({"schEnabled", i}, false) ? 1 : 0);
utc.add(getSetting({"schUTC", i}, 0));
switch_.add(getSetting("schSwitch", i, 0).toInt());
action.add(getSetting("schAction", i, 0).toInt());
type.add(getSetting("schType", i, SCHEDULER_TYPE_SWITCH).toInt());
hour.add(getSetting("schHour", i, 0).toInt());
minute.add(getSetting("schMinute", i, 0).toInt());
weekdays.add(getSetting("schWDs", i, SCHEDULER_WEEKDAYS));
switch_.add(getSetting({"schSwitch", i}, 0));
action.add(getSetting({"schAction", i}, 0));
type.add(getSetting({"schType", i}, SCHEDULER_TYPE_SWITCH));
hour.add(getSetting({"schHour", i}, 0));
minute.add(getSetting({"schMinute", i}, 0));
weekdays.add(getSetting({"schWDs", i}, SCHEDULER_WEEKDAYS));
}
schedules["size"] = size;
@ -76,31 +76,31 @@ void _schConfigure() {
for (unsigned char i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
int sch_switch = getSetting("schSwitch", i, 0xFF).toInt();
int sch_switch = getSetting({"schSwitch", i}, 0xFF);
if (sch_switch == 0xFF) delete_flag = true;
if (delete_flag) {
delSetting("schEnabled", i);
delSetting("schSwitch", i);
delSetting("schAction", i);
delSetting("schHour", i);
delSetting("schMinute", i);
delSetting("schWDs", i);
delSetting("schType", i);
delSetting("schUTC", i);
delSetting({"schEnabled", i});
delSetting({"schSwitch", i});
delSetting({"schAction", i});
delSetting({"schHour", i});
delSetting({"schMinute", i});
delSetting({"schWDs", i});
delSetting({"schType", i});
delSetting({"schUTC", i});
} else {
#if DEBUG_SUPPORT
bool sch_enabled = getSetting("schEnabled", i, 0).toInt() == 1;
int sch_action = getSetting("schAction", i, 0).toInt();
int sch_hour = getSetting("schHour", i, 0).toInt();
int sch_minute = getSetting("schMinute", i, 0).toInt();
bool sch_utc = getSetting("schUTC", i, 0).toInt() == 1;
String sch_weekdays = getSetting("schWDs", i, SCHEDULER_WEEKDAYS);
unsigned char sch_type = getSetting("schType", i, SCHEDULER_TYPE_SWITCH).toInt();
bool sch_enabled = getSetting({"schEnabled", i}, false);
int sch_action = getSetting({"schAction", i}, 0);
int sch_hour = getSetting({"schHour", i}, 0);
int sch_minute = getSetting({"schMinute", i}, 0);
bool sch_utc = getSetting({"schUTC", i}, false);
String sch_weekdays = getSetting({"schWDs", i}, SCHEDULER_WEEKDAYS);
int sch_type = getSetting({"schType", i}, SCHEDULER_TYPE_SWITCH);
DEBUG_MSG_P(
PSTR("[SCH] Schedule #%d: %s #%d to %d at %02d:%02d %s on %s%s\n"),
@ -140,8 +140,8 @@ int _schMinutesLeft(time_t t, unsigned char schedule_hour, unsigned char schedul
return (schedule_hour - now_hour) * 60 + schedule_minute - now_minute;
}
void _schAction(int sch_id, int sch_action, int sch_switch) {
unsigned char sch_type = getSetting("schType", sch_id, SCHEDULER_TYPE_SWITCH).toInt();
void _schAction(unsigned char sch_id, int sch_action, int sch_switch) {
const auto sch_type = getSetting({"schType", sch_id}, SCHEDULER_TYPE_SWITCH);
if (SCHEDULER_TYPE_SWITCH == sch_type) {
DEBUG_MSG_P(PSTR("[SCH] Switching switch %d to %d\n"), sch_switch, sch_action);
@ -174,14 +174,14 @@ void _schCheck(int relay, int daybefore) {
// Check schedules
for (unsigned char i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
int sch_switch = getSetting("schSwitch", i, 0xFF).toInt();
int sch_switch = getSetting({"schSwitch", i}, 0xFF);
if (sch_switch == 0xFF) break;
// Skip disabled schedules
if (getSetting("schEnabled", i, 0).toInt() == 0) continue;
if (!getSetting({"schEnabled", i}, false)) continue;
// Get the datetime used for the calculation
bool sch_utc = getSetting("schUTC", i, 0).toInt() == 1;
const bool sch_utc = getSetting({"schUTC", i}, false);
time_t t = sch_utc ? utc_time : local_time;
if (daybefore > 0) {
@ -191,14 +191,14 @@ void _schCheck(int relay, int daybefore) {
t = t - ((now_hour * 3600) + ((now_minute + 1) * 60) + now_sec + (daybefore * 86400));
}
String sch_weekdays = getSetting("schWDs", i, SCHEDULER_WEEKDAYS);
String sch_weekdays = getSetting({"schWDs", i}, SCHEDULER_WEEKDAYS);
if (_schIsThisWeekday(t, sch_weekdays)) {
int sch_hour = getSetting("schHour", i, 0).toInt();
int sch_minute = getSetting("schMinute", i, 0).toInt();
int sch_hour = getSetting({"schHour", i}, 0);
int sch_minute = getSetting({"schMinute", i}, 0);
int minutes_to_trigger = _schMinutesLeft(t, sch_hour, sch_minute);
int sch_action = getSetting("schAction", i, 0).toInt();
unsigned char sch_type = getSetting("schType", i, SCHEDULER_TYPE_SWITCH).toInt();
int sch_action = getSetting({"schAction", i}, 0);
int sch_type = getSetting({"schType", i}, SCHEDULER_TYPE_SWITCH);
if (sch_type == SCHEDULER_TYPE_SWITCH && sch_switch == relay && sch_action != 2 && minutes_to_trigger < 0 && minutes_to_trigger > minimum_restore_time) {
minimum_restore_time = minutes_to_trigger;
@ -259,8 +259,9 @@ void _schLoop() {
if (_sch_restore == 0) {
for (unsigned char i = 0; i < relayCount(); i++){
if (getSetting("relayLastSch", i, SCHEDULER_RESTORE_LAST_SCHEDULE).toInt() == 1)
if (getSetting({"relayLastSch", i}, 1 == SCHEDULER_RESTORE_LAST_SCHEDULE)) {
_schCheck(i, 0);
}
}
_sch_restore = 1;
}


+ 81
- 73
code/espurna/sensor.ino View File

@ -68,7 +68,7 @@ unsigned char _magnitudeDecimals(unsigned char type) {
if (type == MAGNITUDE_ANALOG) return ANALOG_DECIMALS;
if (type == MAGNITUDE_ENERGY ||
type == MAGNITUDE_ENERGY_DELTA) {
_sensor_energy_units = getSetting("eneUnits", SENSOR_ENERGY_UNITS).toInt();
_sensor_energy_units = getSetting("eneUnits", (unsigned char)SENSOR_ENERGY_UNITS);
if (_sensor_energy_units == ENERGY_KWH) return 3;
}
if (type == MAGNITUDE_POWER_ACTIVE ||
@ -137,7 +137,7 @@ template<typename T> void _sensorWebSocketMagnitudes(JsonObject& root, T prefix)
//name.add(magnitudeName(i));
type.add(magnitudeType(i));
index.add(magnitudeIndex(i));
idx.add(getSetting(conf_name, i, 0).toInt());
idx.add(getSetting({conf_name, i}, 0));
}
}
@ -468,13 +468,13 @@ void _sensorResetTS() {
#endif
}
double _sensorEnergyTotal(unsigned int index) {
double _sensorEnergyTotal(unsigned char index) {
double value = 0;
if (rtcmemStatus() && (index < (sizeof(Rtcmem->energy) / sizeof(*Rtcmem->energy)))) {
value = Rtcmem->energy[index];
} else {
value = (_sensor_save_every > 0) ? getSetting("eneTotal", index, 0).toInt() : 0;
value = (_sensor_save_every > 0) ? getSetting<double>({"eneTotal", index}, 0.) : 0.;
}
return value;
@ -484,14 +484,14 @@ double _sensorEnergyTotal() {
return _sensorEnergyTotal(0);
}
void _sensorEnergyTotal(unsigned int index, double value) {
void _sensorEnergyTotal(unsigned char index, double value) {
static unsigned long save_count = 0;
// Save to EEPROM every '_sensor_save_every' readings
if (_sensor_save_every > 0) {
save_count = (save_count + 1) % _sensor_save_every;
if (0 == save_count) {
setSetting("eneTotal", index, value);
setSetting({"eneTotal", index}, value);
saveSettings();
}
}
@ -561,14 +561,14 @@ void _sensorLoad() {
#if BMX280_SUPPORT
{
// Support up to two sensors with full auto-discovery.
const unsigned char number = constrain(getSetting("bmx280Number", BMX280_NUMBER).toInt(), 1, 2);
const unsigned char number = constrain(getSetting<int>("bmx280Number", BMX280_NUMBER), 1, 2);
// For second sensor, if BMX280_ADDRESS is 0x00 then auto-discover
// otherwise choose the other unnamed sensor address
const unsigned char first = getSetting("bmx280Address", BMX280_ADDRESS).toInt();
const unsigned char second = (first == 0x00) ? 0x00 : (0x76 + 0x77 - first);
const auto first = getSetting("bmx280Address", BMX280_ADDRESS);
const auto second = (first == 0x00) ? 0x00 : (0x76 + 0x77 - first);
const unsigned char address_map[2] = { first, second };
const decltype(first) address_map[2] { first, second };
for (unsigned char n=0; n < number; ++n) {
BMX280Sensor * sensor = new BMX280Sensor();
@ -907,8 +907,9 @@ void _sensorLoad() {
MHZ19Sensor * sensor = new MHZ19Sensor();
sensor->setRX(MHZ19_RX_PIN);
sensor->setTX(MHZ19_TX_PIN);
if (getSetting("mhz19CalibrateAuto", 0).toInt() == 1)
if (getSetting("mhz19CalibrateAuto", false)) {
sensor->setCalibrateAuto(true);
}
_sensors.push_back(sensor);
}
#endif
@ -974,7 +975,7 @@ void _sensorLoad() {
#if PZEM004T_SUPPORT
{
String addresses = getSetting("pzemAddr", PZEM004T_ADDRESSES);
String addresses = getSetting("pzemAddr", F(PZEM004T_ADDRESSES));
if (!addresses.length()) {
DEBUG_MSG_P(PSTR("[SENSOR] PZEM004T Error: no addresses are configured\n"));
return;
@ -983,9 +984,9 @@ void _sensorLoad() {
PZEM004TSensor * sensor = pzem004t_sensor = new PZEM004TSensor();
sensor->setAddresses(addresses.c_str());
if (getSetting("pzemSoft", PZEM004T_USE_SOFT).toInt() == 1) {
sensor->setRX(getSetting("pzemRX", PZEM004T_RX_PIN).toInt());
sensor->setTX(getSetting("pzemTX", PZEM004T_TX_PIN).toInt());
if (getSetting("pzemSoft", 1 == PZEM004T_USE_SOFT)) {
sensor->setRX(getSetting("pzemRX", PZEM004T_RX_PIN));
sensor->setTX(getSetting("pzemTX", PZEM004T_TX_PIN));
} else {
sensor->setSerial(& PZEM004T_HW_PORT);
}
@ -1123,7 +1124,7 @@ void _sensorCallback(unsigned char i, unsigned char type, double value) {
void _sensorInit() {
_sensors_ready = true;
_sensor_save_every = getSetting("snsSave", 0).toInt();
_sensor_save_every = getSetting<int>("snsSave", 0);
for (unsigned char i=0; i<_sensors.size(); i++) {
@ -1159,11 +1160,11 @@ void _sensorInit() {
// TODO: find a proper way to extend this to min/max of any magnitude
if (MAGNITUDE_ENERGY == type) {
new_magnitude.max_change = getSetting("eneMaxDelta", ENERGY_MAX_CHANGE).toFloat();
new_magnitude.max_change = getSetting("eneMaxDelta", ENERGY_MAX_CHANGE);
} else if (MAGNITUDE_TEMPERATURE == type) {
new_magnitude.min_change = getSetting("tmpMinDelta", TEMPERATURE_MIN_CHANGE).toFloat();
new_magnitude.min_change = getSetting("tmpMinDelta", TEMPERATURE_MIN_CHANGE);
} else if (MAGNITUDE_HUMIDITY == type) {
new_magnitude.min_change = getSetting("humMinDelta", HUMIDITY_MIN_CHANGE).toFloat();
new_magnitude.min_change = getSetting("humMinDelta", HUMIDITY_MIN_CHANGE);
}
if (MAGNITUDE_ENERGY == type) {
@ -1195,14 +1196,14 @@ void _sensorInit() {
#if MICS2710_SUPPORT
if (_sensors[i]->getID() == SENSOR_MICS2710_ID) {
MICS2710Sensor * sensor = (MICS2710Sensor *) _sensors[i];
sensor->setR0(getSetting("snsR0", MICS2710_R0).toInt());
sensor->setR0(getSetting("snsR0", MICS2710_R0));
}
#endif // MICS2710_SUPPORT
#if MICS5525_SUPPORT
if (_sensors[i]->getID() == SENSOR_MICS5525_ID) {
MICS5525Sensor * sensor = (MICS5525Sensor *) _sensors[i];
sensor->setR0(getSetting("snsR0", MICS5525_R0).toInt());
sensor->setR0(getSetting("snsR0", MICS5525_R0));
}
#endif // MICS5525_SUPPORT
@ -1210,8 +1211,8 @@ void _sensorInit() {
if (_sensors[i]->getID() == SENSOR_EMON_ANALOG_ID) {
EmonAnalogSensor * sensor = (EmonAnalogSensor *) _sensors[i];
sensor->setCurrentRatio(0, getSetting("pwrRatioC", EMON_CURRENT_RATIO).toFloat());
sensor->setVoltage(getSetting("pwrVoltage", EMON_MAINS_VOLTAGE).toInt());
sensor->setCurrentRatio(0, getSetting("pwrRatioC", EMON_CURRENT_RATIO));
sensor->setVoltage(getSetting("pwrVoltage", EMON_MAINS_VOLTAGE));
double value = _sensorEnergyTotal();
@ -1228,13 +1229,13 @@ void _sensorInit() {
double value;
value = getSetting("pwrRatioC", HLW8012_CURRENT_RATIO).toFloat();
value = getSetting("pwrRatioC", HLW8012_CURRENT_RATIO);
if (value > 0) sensor->setCurrentRatio(value);
value = getSetting("pwrRatioV", HLW8012_VOLTAGE_RATIO).toFloat();
value = getSetting("pwrRatioV", HLW8012_VOLTAGE_RATIO);
if (value > 0) sensor->setVoltageRatio(value);
value = getSetting("pwrRatioP", HLW8012_POWER_RATIO).toFloat();
value = getSetting("pwrRatioP", HLW8012_POWER_RATIO);
if (value > 0) sensor->setPowerRatio(value);
value = _sensorEnergyTotal();
@ -1265,13 +1266,13 @@ void _sensorInit() {
double value;
value = getSetting("pwrRatioC", 0).toFloat();
value = getSetting("pwrRatioC", 0.0);
if (value > 0) sensor->setCurrentRatio(value);
value = getSetting("pwrRatioV", 0).toFloat();
value = getSetting("pwrRatioV", 0.0);
if (value > 0) sensor->setVoltageRatio(value);
value = getSetting("pwrRatioP", 0).toFloat();
value = getSetting("pwrRatioP", 0.0);
if (value > 0) sensor->setPowerRatio(value);
value = _sensorEnergyTotal();
@ -1284,7 +1285,7 @@ void _sensorInit() {
#if PULSEMETER_SUPPORT
if (_sensors[i]->getID() == SENSOR_PULSEMETER_ID) {
PulseMeterSensor * sensor = (PulseMeterSensor *) _sensors[i];
sensor->setEnergyRatio(getSetting("pwrRatioE", sensor->getEnergyRatio()).toInt());
sensor->setEnergyRatio(getSetting("pwrRatioE", sensor->getEnergyRatio()));
}
#endif // PULSEMETER_SUPPORT
@ -1294,18 +1295,25 @@ void _sensorInit() {
void _sensorConfigure() {
// General sensor settings
_sensor_read_interval = 1000 * constrain(getSetting("snsRead", SENSOR_READ_INTERVAL).toInt(), SENSOR_READ_MIN_INTERVAL, SENSOR_READ_MAX_INTERVAL);
_sensor_report_every = constrain(getSetting("snsReport", SENSOR_REPORT_EVERY).toInt(), SENSOR_REPORT_MIN_EVERY, SENSOR_REPORT_MAX_EVERY);
_sensor_save_every = getSetting("snsSave", SENSOR_SAVE_EVERY).toInt();
_sensor_realtime = getSetting("apiRealTime", API_REAL_TIME_VALUES).toInt() == 1;
_sensor_power_units = getSetting("pwrUnits", SENSOR_POWER_UNITS).toInt();
_sensor_energy_units = getSetting("eneUnits", SENSOR_ENERGY_UNITS).toInt();
_sensor_temperature_units = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt();
_sensor_temperature_correction = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION).toFloat();
_sensor_humidity_correction = getSetting("humCorrection", SENSOR_HUMIDITY_CORRECTION).toFloat();
_sensor_energy_reset_ts = getSetting("snsResetTS", "");
_sensor_lux_correction = getSetting("luxCorrection", SENSOR_LUX_CORRECTION).toFloat();
// General sensor settings for reporting and saving
_sensor_read_interval = 1000 * constrain(getSetting("snsRead", SENSOR_READ_INTERVAL), SENSOR_READ_MIN_INTERVAL, SENSOR_READ_MAX_INTERVAL);
_sensor_report_every = constrain(getSetting("snsReport", SENSOR_REPORT_EVERY), SENSOR_REPORT_MIN_EVERY, SENSOR_REPORT_MAX_EVERY);
_sensor_save_every = getSetting("snsSave", SENSOR_SAVE_EVERY);
_sensor_realtime = getSetting("apiRealTime", 1 == API_REAL_TIME_VALUES);
// Units are configured globally atm
_sensor_power_units = getSetting("pwrUnits", SENSOR_POWER_UNITS);
_sensor_energy_units = getSetting("eneUnits", SENSOR_ENERGY_UNITS);
_sensor_temperature_units = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS);
// ...same with corrections
_sensor_temperature_correction = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION);
_sensor_humidity_correction = getSetting("humCorrection", SENSOR_HUMIDITY_CORRECTION);
_sensor_lux_correction = getSetting("luxCorrection", SENSOR_LUX_CORRECTION);
// energy reset should be timestamped
_sensor_energy_reset_ts = getSetting("snsResetTS");
// Specific sensor settings
for (unsigned char i=0; i<_sensors.size(); i++) {
@ -1313,7 +1321,7 @@ void _sensorConfigure() {
#if MICS2710_SUPPORT
if (_sensors[i]->getID() == SENSOR_MICS2710_ID) {
if (getSetting("snsResetCalibration", 0).toInt() == 1) {
if (getSetting("snsResetCalibration", false)) {
MICS2710Sensor * sensor = (MICS2710Sensor *) _sensors[i];
sensor->calibrate();
setSetting("snsR0", sensor->getR0());
@ -1325,7 +1333,7 @@ void _sensorConfigure() {
#if MICS5525_SUPPORT
if (_sensors[i]->getID() == SENSOR_MICS5525_ID) {
if (getSetting("snsResetCalibration", 0).toInt() == 1) {
if (getSetting("snsResetCalibration", false)) {
MICS5525Sensor * sensor = (MICS5525Sensor *) _sensors[i];
sensor->calibrate();
setSetting("snsR0", sensor->getR0());
@ -1341,23 +1349,23 @@ void _sensorConfigure() {
double value;
EmonAnalogSensor * sensor = (EmonAnalogSensor *) _sensors[i];
if ((value = getSetting("pwrExpectedP", 0).toInt())) {
if ((value = getSetting("pwrExpectedP", 0))) {
sensor->expectedPower(0, value);
setSetting("pwrRatioC", sensor->getCurrentRatio(0));
}
if (getSetting("pwrResetCalibration", 0).toInt() == 1) {
if (getSetting("pwrResetCalibration", false)) {
sensor->setCurrentRatio(0, EMON_CURRENT_RATIO);
delSetting("pwrRatioC");
}
if (getSetting("pwrResetE", 0).toInt() == 1) {
if (getSetting("pwrResetE", false)) {
sensor->resetEnergy();
delSetting("eneTotal", 0);
delSetting({"eneTotal", 0});
_sensorResetTS();
}
sensor->setVoltage(getSetting("pwrVoltage", EMON_MAINS_VOLTAGE).toInt());
sensor->setVoltage(getSetting("pwrVoltage", EMON_MAINS_VOLTAGE));
}
@ -1366,9 +1374,9 @@ void _sensorConfigure() {
#if EMON_ADC121_SUPPORT
if (_sensors[i]->getID() == SENSOR_EMON_ADC121_ID) {
EmonADC121Sensor * sensor = (EmonADC121Sensor *) _sensors[i];
if (getSetting("pwrResetE", 0).toInt() == 1) {
if (getSetting("pwrResetE", false)) {
sensor->resetEnergy();
delSetting("eneTotal", 0);
delSetting({"eneTotal", 0});
_sensorResetTS();
}
}
@ -1377,9 +1385,9 @@ void _sensorConfigure() {
#if EMON_ADS1X15_SUPPORT
if (_sensors[i]->getID() == SENSOR_EMON_ADS1X15_ID) {
EmonADS1X15Sensor * sensor = (EmonADS1X15Sensor *) _sensors[i];
if (getSetting("pwrResetE", 0).toInt() == 1) {
if (getSetting("pwrResetE", false)) {
sensor->resetEnergy();
delSetting("eneTotal", 0);
delSetting({"eneTotal", 0});
_sensorResetTS();
}
}
@ -1393,28 +1401,28 @@ void _sensorConfigure() {
double value;
HLW8012Sensor * sensor = (HLW8012Sensor *) _sensors[i];
if ((value = getSetting("pwrExpectedC", 0).toFloat())) {
if ((value = getSetting("pwrExpectedC", 0.0))) {
sensor->expectedCurrent(value);
setSetting("pwrRatioC", sensor->getCurrentRatio());
}
if ((value = getSetting("pwrExpectedV", 0).toInt())) {
if ((value = getSetting("pwrExpectedV", 0.0))) {
sensor->expectedVoltage(value);
setSetting("pwrRatioV", sensor->getVoltageRatio());
}
if ((value = getSetting("pwrExpectedP", 0).toInt())) {
if ((value = getSetting("pwrExpectedP", 0.0))) {
sensor->expectedPower(value);
setSetting("pwrRatioP", sensor->getPowerRatio());
}
if (getSetting("pwrResetE", 0).toInt() == 1) {
if (getSetting("pwrResetE", false)) {
sensor->resetEnergy();
delSetting("eneTotal", 0);
delSetting({"eneTotal", 0});
_sensorResetTS();
}
if (getSetting("pwrResetCalibration", 0).toInt() == 1) {
if (getSetting("pwrResetCalibration", false)) {
sensor->resetRatios();
delSetting("pwrRatioC");
delSetting("pwrRatioV");
@ -1432,28 +1440,28 @@ void _sensorConfigure() {
double value;
CSE7766Sensor * sensor = (CSE7766Sensor *) _sensors[i];
if ((value = getSetting("pwrExpectedC", 0).toFloat())) {
if ((value = getSetting("pwrExpectedC", 0.0))) {
sensor->expectedCurrent(value);
setSetting("pwrRatioC", sensor->getCurrentRatio());
}
if ((value = getSetting("pwrExpectedV", 0).toInt())) {
if ((value = getSetting("pwrExpectedV", 0.0))) {
sensor->expectedVoltage(value);
setSetting("pwrRatioV", sensor->getVoltageRatio());
}
if ((value = getSetting("pwrExpectedP", 0).toInt())) {
if ((value = getSetting("pwrExpectedP", 0.0))) {
sensor->expectedPower(value);
setSetting("pwrRatioP", sensor->getPowerRatio());
}
if (getSetting("pwrResetE", 0).toInt() == 1) {
if (getSetting("pwrResetE", false)) {
sensor->resetEnergy();
delSetting("eneTotal", 0);
delSetting({"eneTotal", 0});
_sensorResetTS();
}
if (getSetting("pwrResetCalibration", 0).toInt() == 1) {
if (getSetting("pwrResetCalibration", false)) {
sensor->resetRatios();
delSetting("pwrRatioC");
delSetting("pwrRatioV");
@ -1467,13 +1475,13 @@ void _sensorConfigure() {
#if PULSEMETER_SUPPORT
if (_sensors[i]->getID() == SENSOR_PULSEMETER_ID) {
PulseMeterSensor * sensor = (PulseMeterSensor *) _sensors[i];
if (getSetting("pwrResetE", 0).toInt() == 1) {
if (getSetting("pwrResetE", false)) {
sensor->resetEnergy();
delSetting("eneTotal", 0);
delSetting({"eneTotal", 0});
_sensorResetTS();
}
sensor->setEnergyRatio(getSetting("pwrRatioE", sensor->getEnergyRatio()).toInt());
sensor->setEnergyRatio(getSetting<unsigned long>("pwrRatioE", sensor->getEnergyRatio()));
}
#endif // PULSEMETER_SUPPORT
@ -1481,11 +1489,11 @@ void _sensorConfigure() {
if (_sensors[i]->getID() == SENSOR_PZEM004T_ID) {
PZEM004TSensor * sensor = (PZEM004TSensor *) _sensors[i];
if (getSetting("pwrResetE", 0).toInt() == 1) {
if (getSetting("pwrResetE", false)) {
unsigned char dev_count = sensor->getAddressesCount();
for(unsigned char dev = 0; dev < dev_count; dev++) {
sensor->resetEnergy(dev, 0);
delSetting("eneTotal", dev);
delSetting({"eneTotal", dev});
}
_sensorResetTS();
}
@ -1497,11 +1505,11 @@ void _sensorConfigure() {
if (_sensors[i]->getID() == SENSOR_ADE7953_ID) {
ADE7953Sensor * sensor = (ADE7953Sensor *) _sensors[i];
if (getSetting("pwrResetE", 0).toInt() == 1) {
if (getSetting("pwrResetE", false)) {
unsigned char dev_count = sensor->getTotalDevices();
for(unsigned char dev = 0; dev < dev_count; dev++) {
sensor->resetEnergy(dev);
delSetting("eneTotal", dev);
delSetting({"eneTotal", dev});
}
_sensorResetTS();
}
@ -1517,7 +1525,7 @@ void _sensorConfigure() {
for (unsigned char i=0; i<_magnitudes.size(); i++) {
_magnitudes[i].filter->resize(_sensor_report_every);
if ((_magnitudes[i].type == MAGNITUDE_ENERGY) && reset_saved_energy) {
delSetting("eneTotal", _magnitudes[i].global);
delSetting({"eneTotal", _magnitudes[i].global});
}
}
}


+ 4
- 2
code/espurna/sensors/ADE7953Sensor.h View File

@ -11,10 +11,12 @@
#undef I2C_SUPPORT
#define I2C_SUPPORT 1 // Explicitly request I2C support.
#include "Arduino.h"
#include "I2CSensor.h"
#include <Arduino.h>
#include <Wire.h>
#include "I2CSensor.h"
#include "../utils.h"
// -----------------------------------------------------------------------------
// ADE7953 - Energy (Shelly 2.5)
//


+ 2
- 0
code/espurna/sensors/BMP180Sensor.h View File

@ -7,6 +7,8 @@
#pragma once
#include "../utils.h"
#undef I2C_SUPPORT
#define I2C_SUPPORT 1 // Explicitly request I2C support.


+ 3
- 1
code/espurna/sensors/BMX280Sensor.h View File

@ -10,7 +10,9 @@
#undef I2C_SUPPORT
#define I2C_SUPPORT 1 // Explicitly request I2C support.
#include "Arduino.h"
#include <Arduino.h>
#include "../utils.h"
#include "I2CSensor.h"
#define BMX280_CHIP_BMP280 0x58


+ 3
- 1
code/espurna/sensors/DHTSensor.h View File

@ -7,7 +7,9 @@
#pragma once
#include "Arduino.h"
#include <Arduino.h>
#include "../utils.h"
#include "BaseSensor.h"
constexpr const double DHT_DUMMY_VALUE = -255;


+ 3
- 1
code/espurna/sensors/EmonADC121Sensor.h View File

@ -7,7 +7,9 @@
#pragma once
#include "Arduino.h"
#include <Arduino.h>
#include "../utils.h"
#include "EmonSensor.h"
// ADC121 Registers


+ 3
- 1
code/espurna/sensors/GUVAS12SDSensor.h View File

@ -12,7 +12,9 @@
#undef ADC_MODE_VALUE
#define ADC_MODE_VALUE ADC_TOUT
#include "Arduino.h"
#include <Arduino.h>
#include "../utils.h"
#include "BaseSensor.h"
// http://www.eoc-inc.com/genicom/GUVA-S12SD.pdf


+ 2
- 1
code/espurna/sensors/SHT3XI2CSensor.h View File

@ -10,8 +10,9 @@
#undef I2C_SUPPORT
#define I2C_SUPPORT 1 // Explicitly request I2C support.
#include <Arduino.h>
#include "Arduino.h"
#include "../utils.h"
#include "I2CSensor.h"
class SHT3XI2CSensor : public I2CSensor {


+ 3
- 1
code/espurna/sensors/SI7021Sensor.h View File

@ -10,7 +10,9 @@
#undef I2C_SUPPORT
#define I2C_SUPPORT 1 // Explicitly request I2C support.
#include "Arduino.h"
#include <Arduino.h>
#include "../utils.h"
#include "I2CSensor.h"
#define SI7021_SCL_FREQUENCY 200


+ 105
- 0
code/espurna/settings.h View File

@ -0,0 +1,105 @@
/*
SETTINGS MODULE
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
#pragma once
#include <functional>
#include <utility>
#include <vector>
#include <ArduinoJson.h>
#include "libs/EmbedisWrap.h"
#include "settings_internal.h"
// --------------------------------------------------------------------------
class settings_key_t {
public:
settings_key_t(const char* value, unsigned char index) :
value(value), index(index)
{}
settings_key_t(const String& value, unsigned char index) :
value(value), index(index)
{}
settings_key_t(const char* value) :
value(value), index(-1)
{}
settings_key_t(const String& value) :
value(value), index(-1)
{}
settings_key_t(const __FlashStringHelper* value) :
value(value), index(-1)
{}
String toString() const;
explicit operator String () const {
return toString();
}
private:
const String value;
int index;
};
using settings_move_key_t = std::pair<settings_key_t, settings_key_t>;
using settings_filter_t = std::function<String(String& value)>;
// --------------------------------------------------------------------------
struct settings_cfg_t {
String& setting;
const char* key;
const char* default_value;
};
using settings_cfg_list_t = std::initializer_list<settings_cfg_t>;
// --------------------------------------------------------------------------
void moveSetting(const String& from, const String& to);
void moveSetting(const String& from, const String& to, unsigned int index);
void moveSettings(const String& from, const String& to);
template<typename R, settings::internal::convert_t<R> Rfunc = settings::internal::convert>
R getSetting(const settings_key_t& key, R defaultValue);
String getSetting(const settings_key_t& key);
template<typename T>
bool setSetting(const settings_key_t& key, const T& value);
bool delSetting(const settings_key_t& key);
bool hasSetting(const settings_key_t& key);
void saveSettings();
void resetSettings();
void settingsGetJson(JsonObject& data);
bool settingsRestoreJson(JsonObject& data);
void settingsProcessConfig(const settings_cfg_list_t& config, settings_filter_t filter = nullptr);
// --------------------------------------------------------------------------
template <typename T>
String getSetting(const String& key, unsigned char index, T defaultValue)
__attribute__((deprecated("getSetting({key, index}, default) should be used instead")));
template<typename T>
bool setSetting(const String& key, unsigned char index, T value)
__attribute__((deprecated("setSetting({key, index}, value) should be used instead")));
template<typename T>
bool hasSetting(const String& key, unsigned char index)
__attribute__((deprecated("hasSetting({key, index}) should be used instead")));
template<typename T>
bool delSetting(const String& key, unsigned char index)
__attribute__((deprecated("delSetting({key, index}) should be used instead")));

+ 85
- 35
code/espurna/settings.ino View File

@ -7,7 +7,11 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
#include <vector>
#include "libs/EmbedisWrap.h"
#include <ArduinoJson.h>
#include "settings_internal.h"
#include "settings.h"
// -----------------------------------------------------------------------------
// Reverse engineering EEPROM storage format
@ -97,65 +101,87 @@ std::vector<String> _settingsKeys() {
// Key-value API
// -----------------------------------------------------------------------------
void moveSetting(const char * from, const char * to) {
String value = getSetting(from);
String settings_key_t::toString() const {
if (index < 0) {
return value;
} else {
return value + index;
}
}
settings_move_key_t _moveKeys(const String& from, const String& to, unsigned char index) {
return settings_move_key_t {{from, index}, {to, index}};
}
void moveSetting(const String& from, const String& to) {
const auto value = getSetting(from);
if (value.length() > 0) setSetting(to, value);
delSetting(from);
}
void moveSetting(const char * from, const char * to, unsigned int index) {
String value = getSetting(from, index, "");
if (value.length() > 0) setSetting(to, index, value);
delSetting(from, index);
void moveSetting(const String& from, const String& to, unsigned char index) {
const auto keys = _moveKeys(from, to, index);
const auto value = getSetting(keys.first);
if (value.length() > 0) setSetting(keys.second, value);
delSetting(keys.first);
}
void moveSettings(const char * from, const char * to) {
unsigned int index = 0;
void moveSettings(const String& from, const String& to) {
unsigned char index = 0;
while (index < 100) {
String value = getSetting(from, index, "");
const auto keys = _moveKeys(from, to, index);
const auto value = getSetting(keys.first);
if (value.length() == 0) break;
setSetting(to, index, value);
delSetting(from, index);
index++;
setSetting(keys.second, value);
delSetting(keys.first);
++index;
}
}
template<typename T> String getSetting(const String& key, T defaultValue) {
template<typename R, settings::internal::convert_t<R> Rfunc = settings::internal::convert>
R getSetting(const settings_key_t& key, R defaultValue) {
String value;
if (!Embedis::get(key, value)) value = String(defaultValue);
return value;
}
template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue) {
return getSetting(key + String(index), defaultValue);
if (!Embedis::get(key.toString(), value)) {
return defaultValue;
}
return Rfunc(value);
}
String getSetting(const String& key) {
return getSetting(key, "");
template<>
String getSetting(const settings_key_t& key, String defaultValue) {
String value;
if (!Embedis::get(key.toString(), value)) {
value = defaultValue;
}
return value;
}
template<typename T> bool setSetting(const String& key, T value) {
return Embedis::set(key, String(value));
String getSetting(const settings_key_t& key) {
static const String defaultValue("");
return getSetting(key, defaultValue);
}
template<typename T> bool setSetting(const String& key, unsigned int index, T value) {
return setSetting(key + String(index), value);
String getSetting(const settings_key_t& key, const char* defaultValue) {
return getSetting(key, String(defaultValue));
}
bool delSetting(const String& key) {
return Embedis::del(key);
String getSetting(const settings_key_t& key, const __FlashStringHelper* defaultValue) {
return getSetting(key, String(defaultValue));
}
bool delSetting(const String& key, unsigned int index) {
return delSetting(key + String(index));
template<typename T>
bool setSetting(const settings_key_t& key, const T& value) {
return Embedis::set(key.toString(), String(value));
}
bool hasSetting(const String& key) {
return getSetting(key).length() != 0;
bool delSetting(const settings_key_t& key) {
return Embedis::del(key.toString());
}
bool hasSetting(const String& key, unsigned int index) {
return getSetting(key, index, "").length() != 0;
bool hasSetting(const settings_key_t& key) {
String value;
return Embedis::get(key.toString(), value);
}
void saveSettings() {
@ -165,12 +191,36 @@ void saveSettings() {
}
void resetSettings() {
for (unsigned int i = 0; i < SPI_FLASH_SEC_SIZE; i++) {
for (unsigned int i = 0; i < EEPROM_SIZE; i++) {
EEPROMr.write(i, 0xFF);
}
EEPROMr.commit();
}
// -----------------------------------------------------------------------------
// Deprecated implementation
// -----------------------------------------------------------------------------
template<typename T>
String getSetting(const String& key, unsigned char index, T defaultValue) {
return getSetting({key, index}, defaultValue);
}
template<typename T>
bool setSetting(const String& key, unsigned char index, T value) {
return setSetting({key, index}, value);
}
template<typename T>
bool hasSetting(const String& key, unsigned char index) {
return hasSetting({key, index});
}
template<typename T>
bool delSetting(const String& key, unsigned char index) {
return delSetting({key, index});
}
// -----------------------------------------------------------------------------
// API
// -----------------------------------------------------------------------------


+ 75
- 0
code/espurna/settings_internal.h View File

@ -0,0 +1,75 @@
/*
SETTINGS MODULE
*/
#pragma once
#include <cstdlib>
// --------------------------------------------------------------------------
namespace settings {
namespace internal {
template <typename T>
using convert_t = T(*)(const String& value);
template <typename T>
T convert(const String& value);
// --------------------------------------------------------------------------
template <>
float convert(const String& value) {
return value.toFloat();
}
template <>
double convert(const String& value) {
return value.toFloat();
}
template <>
int convert(const String& value) {
return value.toInt();
}
template <>
long convert(const String& value) {
return value.toInt();
}
template <>
bool convert(const String& value) {
return convert<int>(value) == 1;
}
template <>
unsigned long convert(const String& value) {
return strtoul(value.c_str(), nullptr, 10);
}
template <>
unsigned int convert(const String& value) {
return convert<unsigned long>(value);
}
template <>
unsigned short convert(const String& value) {
return convert<unsigned long>(value);
}
template <>
unsigned char convert(const String& value) {
return convert<unsigned long>(value);
}
template <>
String convert(const String& value) {
return value;
}
} // namespace settings::internal
} // namespace settings

+ 3045
- 3042
code/espurna/static/index.all.html.gz.h
File diff suppressed because it is too large
View File


+ 2174
- 2171
code/espurna/static/index.light.html.gz.h
File diff suppressed because it is too large
View File


+ 2419
- 2415
code/espurna/static/index.lightfox.html.gz.h
File diff suppressed because it is too large
View File


+ 2446
- 2443
code/espurna/static/index.rfbridge.html.gz.h
File diff suppressed because it is too large
View File


+ 3184
- 3181
code/espurna/static/index.rfm69.html.gz.h
File diff suppressed because it is too large
View File


+ 1898
- 1895
code/espurna/static/index.sensor.html.gz.h
File diff suppressed because it is too large
View File


+ 1793
- 1790
code/espurna/static/index.small.html.gz.h
File diff suppressed because it is too large
View File


+ 1829
- 1826
code/espurna/static/index.thermostat.html.gz.h
File diff suppressed because it is too large
View File


+ 3
- 3
code/espurna/system.ino View File

@ -14,7 +14,7 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
// -----------------------------------------------------------------------------
bool _system_send_heartbeat = false;
unsigned char _heartbeat_mode = HEARTBEAT_MODE;
int _heartbeat_mode = HEARTBEAT_MODE;
unsigned long _heartbeat_interval = HEARTBEAT_INTERVAL;
// Calculated load average 0 to 100;
@ -160,8 +160,8 @@ unsigned long systemLoadAverage() {
}
void _systemSetupHeartbeat() {
_heartbeat_mode = getSetting("hbMode", HEARTBEAT_MODE).toInt();
_heartbeat_interval = getSetting("hbInterval", HEARTBEAT_INTERVAL).toInt();
_heartbeat_mode = getSetting("hbMode", HEARTBEAT_MODE);
_heartbeat_interval = getSetting("hbInterval", HEARTBEAT_INTERVAL);
}
#if WEB_SUPPORT


+ 8
- 6
code/espurna/telnet.ino View File

@ -86,8 +86,8 @@ bool _telnetWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
}
void _telnetWebSocketOnConnected(JsonObject& root) {
root["telnetSTA"] = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
root["telnetAuth"] = getSetting("telnetAuth", TELNET_AUTHENTICATION).toInt() == 1;
root["telnetSTA"] = getSetting("telnetSTA", 1 == TELNET_STA);
root["telnetAuth"] = getSetting("telnetAuth", 1 == TELNET_AUTHENTICATION);
}
#endif
@ -345,12 +345,14 @@ void _telnetNotifyConnected(unsigned char i) {
DEBUG_MSG_P(PSTR("[TELNET] Client #%u connected\n"), i);
// If there is no terminal support automatically dump info and crash data
#if TERMINAL_SUPPORT == 0
#if DEBUG_SUPPORT
#if not TERMINAL_SUPPORT
info();
wifiDebug();
crashDump();
crashClear();
#endif
#endif
#ifdef ESPURNA_CORE
_telnetClientsAuth[i] = true;
@ -385,7 +387,7 @@ void _telnetLoop() {
#ifdef ESPURNA_CORE
bool telnetSTA = true;
#else
bool telnetSTA = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
bool telnetSTA = getSetting("telnetSTA", 1 == TELNET_STA);
#endif
if (!telnetSTA) {
@ -457,7 +459,7 @@ void _telnetNewClient(AsyncClient* client) {
#ifdef ESPURNA_CORE
bool telnetSTA = true;
#else
bool telnetSTA = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
bool telnetSTA = getSetting("telnetSTA", 1 == TELNET_STA);
#endif
if (!telnetSTA) {
@ -506,7 +508,7 @@ unsigned char telnetWrite(unsigned char ch) {
}
void _telnetConfigure() {
_telnetAuth = getSetting("telnetAuth", TELNET_AUTHENTICATION).toInt() == 1;
_telnetAuth = getSetting("telnetAuth", 1 == TELNET_AUTHENTICATION);
}
void telnetSetup() {


+ 5
- 6
code/espurna/terminal.ino View File

@ -8,9 +8,9 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if TERMINAL_SUPPORT
#include "settings.h"
#include "system.h"
#include "utils.h"
#include "libs/EmbedisWrap.h"
#include "libs/StreamInjector.h"
#include "libs/HeapStats.h"
@ -70,12 +70,11 @@ void _terminalKeysCommand() {
// Write key-values
DEBUG_MSG_P(PSTR("Current settings:\n"));
for (unsigned int i=0; i<keys.size(); i++) {
String value = getSetting(keys[i]);
const auto value = getSetting(keys[i]);
DEBUG_MSG_P(PSTR("> %s => \"%s\"\n"), (keys[i]).c_str(), value.c_str());
}
unsigned long freeEEPROM = SPI_FLASH_SEC_SIZE - settingsSize();
UNUSED(freeEEPROM);
unsigned long freeEEPROM [[gnu::unused]] = SPI_FLASH_SEC_SIZE - settingsSize();
DEBUG_MSG_P(PSTR("Number of keys: %d\n"), keys.size());
DEBUG_MSG_P(PSTR("Current EEPROM sector: %u\n"), EEPROMr.current());
DEBUG_MSG_P(PSTR("Free EEPROM: %d bytes (%d%%)\n"), freeEEPROM, 100 * freeEEPROM / SPI_FLASH_SEC_SIZE);
@ -388,8 +387,8 @@ Stream & terminalSerial() {
return (Stream &) _serial;
}
void terminalRegisterCommand(const String& name, void (*call)(Embedis*)) {
Embedis::command(name, call);
void terminalRegisterCommand(const String& name, embedis_command_f command) {
Embedis::command(name, command);
};
void terminalOK() {


+ 16
- 16
code/espurna/thermostat.ino View File

@ -252,25 +252,25 @@ void notifyRangeChanged(bool min) {
// Setup
//------------------------------------------------------------------------------
void commonSetup() {
_thermostat_enabled = getSetting(NAME_THERMOSTAT_ENABLED).toInt() == 1;
_thermostat_enabled = getSetting(NAME_THERMOSTAT_ENABLED, false);
DEBUG_MSG_P(PSTR("[THERMOSTAT] _thermostat_enabled = %d\n"), _thermostat_enabled);
_thermostat_mode_cooler = getSetting(NAME_THERMOSTAT_MODE).toInt() == 1;
_thermostat_mode_cooler = getSetting(NAME_THERMOSTAT_MODE, false);
DEBUG_MSG_P(PSTR("[THERMOSTAT] _thermostat_mode_cooler = %d\n"), _thermostat_mode_cooler);
_temp_range.min = getSetting(NAME_TEMP_RANGE_MIN, THERMOSTAT_TEMP_RANGE_MIN).toInt();
_temp_range.max = getSetting(NAME_TEMP_RANGE_MAX, THERMOSTAT_TEMP_RANGE_MAX).toInt();
_temp_range.min = getSetting(NAME_TEMP_RANGE_MIN, THERMOSTAT_TEMP_RANGE_MIN);
_temp_range.max = getSetting(NAME_TEMP_RANGE_MAX, THERMOSTAT_TEMP_RANGE_MAX);
DEBUG_MSG_P(PSTR("[THERMOSTAT] _temp_range.min = %d\n"), _temp_range.min);
DEBUG_MSG_P(PSTR("[THERMOSTAT] _temp_range.max = %d\n"), _temp_range.max);
_thermostat.remote_sensor_name = getSetting(NAME_REMOTE_SENSOR_NAME);
thermostat_remote_sensor_topic = _thermostat.remote_sensor_name + String("/") + String(MQTT_TOPIC_JSON);
_thermostat_remote_temp_max_wait = getSetting(NAME_REMOTE_TEMP_MAX_WAIT, THERMOSTAT_REMOTE_TEMP_MAX_WAIT).toInt() * MILLIS_IN_SEC;
_thermostat_alone_on_time = getSetting(NAME_ALONE_ON_TIME, THERMOSTAT_ALONE_ON_TIME).toInt() * MILLIS_IN_MIN;
_thermostat_alone_off_time = getSetting(NAME_ALONE_OFF_TIME, THERMOSTAT_ALONE_OFF_TIME).toInt() * MILLIS_IN_MIN;
_thermostat_max_on_time = getSetting(NAME_MAX_ON_TIME, THERMOSTAT_MAX_ON_TIME).toInt() * MILLIS_IN_MIN;
_thermostat_min_off_time = getSetting(NAME_MIN_OFF_TIME, THERMOSTAT_MIN_OFF_TIME).toInt() * MILLIS_IN_MIN;
_thermostat_remote_temp_max_wait = getSetting(NAME_REMOTE_TEMP_MAX_WAIT, THERMOSTAT_REMOTE_TEMP_MAX_WAIT) * MILLIS_IN_SEC;
_thermostat_alone_on_time = getSetting(NAME_ALONE_ON_TIME, THERMOSTAT_ALONE_ON_TIME) * MILLIS_IN_MIN;
_thermostat_alone_off_time = getSetting(NAME_ALONE_OFF_TIME, THERMOSTAT_ALONE_OFF_TIME) * MILLIS_IN_MIN;
_thermostat_max_on_time = getSetting(NAME_MAX_ON_TIME, THERMOSTAT_MAX_ON_TIME) * MILLIS_IN_MIN;
_thermostat_min_off_time = getSetting(NAME_MIN_OFF_TIME, THERMOSTAT_MIN_OFF_TIME) * MILLIS_IN_MIN;
}
//------------------------------------------------------------------------------
@ -278,13 +278,13 @@ void thermostatConfigure() {
commonSetup();
_thermostat.temperature_source = temp_none;
_thermostat_burn_total = getSetting(NAME_BURN_TOTAL).toInt();
_thermostat_burn_today = getSetting(NAME_BURN_TODAY).toInt();
_thermostat_burn_yesterday = getSetting(NAME_BURN_YESTERDAY).toInt();
_thermostat_burn_this_month = getSetting(NAME_BURN_THIS_MONTH).toInt();
_thermostat_burn_prev_month = getSetting(NAME_BURN_PREV_MONTH).toInt();
_thermostat_burn_day = getSetting(NAME_BURN_DAY).toInt();
_thermostat_burn_month = getSetting(NAME_BURN_MONTH).toInt();
_thermostat_burn_total = getSetting(NAME_BURN_TOTAL, 0);
_thermostat_burn_today = getSetting(NAME_BURN_TODAY, 0);
_thermostat_burn_yesterday = getSetting(NAME_BURN_YESTERDAY, 0);
_thermostat_burn_this_month = getSetting(NAME_BURN_THIS_MONTH, 0);
_thermostat_burn_prev_month = getSetting(NAME_BURN_PREV_MONTH, 0);
_thermostat_burn_day = getSetting(NAME_BURN_DAY, 0);
_thermostat_burn_month = getSetting(NAME_BURN_MONTH, 0);
}
//------------------------------------------------------------------------------


+ 8
- 8
code/espurna/thinkspeak.ino View File

@ -71,13 +71,13 @@ void _tspkWebSocketOnVisible(JsonObject& root) {
void _tspkWebSocketOnConnected(JsonObject& root) {
root["tspkEnabled"] = getSetting("tspkEnabled", THINGSPEAK_ENABLED).toInt() == 1;
root["tspkEnabled"] = getSetting("tspkEnabled", 1 == THINGSPEAK_ENABLED);
root["tspkKey"] = getSetting("tspkKey", THINGSPEAK_APIKEY);
root["tspkClear"] = getSetting("tspkClear", THINGSPEAK_CLEAR_CACHE).toInt() == 1;
root["tspkClear"] = getSetting("tspkClear", 1 == THINGSPEAK_CLEAR_CACHE);
JsonArray& relays = root.createNestedArray("tspkRelays");
for (byte i=0; i<relayCount(); i++) {
relays.add(getSetting("tspkRelay", i, 0).toInt());
relays.add(getSetting({"tspkRelay", i}, 0));
}
#if SENSOR_SUPPORT
@ -89,8 +89,8 @@ void _tspkWebSocketOnConnected(JsonObject& root) {
#endif
void _tspkConfigure() {
_tspk_clear = getSetting("tspkClear", THINGSPEAK_CLEAR_CACHE).toInt() == 1;
_tspk_enabled = getSetting("tspkEnabled", THINGSPEAK_ENABLED).toInt() == 1;
_tspk_clear = getSetting("tspkClear", 1 == THINGSPEAK_CLEAR_CACHE);
_tspk_enabled = getSetting("tspkEnabled", 1 == THINGSPEAK_ENABLED);
if (_tspk_enabled && (getSetting("tspkKey", THINGSPEAK_APIKEY).length() == 0)) {
_tspk_enabled = false;
setSetting("tspkEnabled", 0);
@ -340,7 +340,7 @@ void _tspkFlush() {
// POST data if any
if (_tspk_data.length()) {
_tspk_data.concat("&api_key=");
_tspk_data.concat(getSetting("tspkKey", THINGSPEAK_APIKEY));
_tspk_data.concat(getSetting<String>("tspkKey", THINGSPEAK_APIKEY));
--_tspk_tries;
_tspkPost();
}
@ -351,7 +351,7 @@ void _tspkFlush() {
bool tspkEnqueueRelay(unsigned char index, bool status) {
if (!_tspk_enabled) return true;
unsigned char id = getSetting("tspkRelay", index, 0).toInt();
unsigned char id = getSetting({"tspkRelay", index}, 0);
if (id > 0) {
_tspkEnqueue(id, status ? "1" : "0");
return true;
@ -361,7 +361,7 @@ bool tspkEnqueueRelay(unsigned char index, bool status) {
bool tspkEnqueueMeasurement(unsigned char index, const char * payload) {
if (!_tspk_enabled) return true;
unsigned char id = getSetting("tspkMagnitude", index, 0).toInt();
const auto id = getSetting({"tspkMagnitude", index}, 0);
if (id > 0) {
_tspkEnqueue(id, payload);
return true;


+ 8
- 8
code/espurna/tuya.ino View File

@ -467,8 +467,8 @@ namespace Tuya {
initBrokerCallback();
for (unsigned char n = 0; n < switchStates.capacity(); ++n) {
if (!hasSetting("tuyaSwitch", n)) break;
uint8_t dp = getSetting("tuyaSwitch", n, 0).toInt();
if (!hasSetting({"tuyaSwitch", n})) break;
const auto dp = getSetting({"tuyaSwitch", n}, 0);
switchStates.pushOrUpdate(dp, false);
}
relaySetupDummy(switchStates.size());
@ -489,8 +489,8 @@ namespace Tuya {
initBrokerCallback();
for (unsigned char n = 0; n < channelStates.capacity(); ++n) {
if (!hasSetting("tuyaChannel", n)) break;
uint8_t dp = getSetting("tuyaChannel", n, 0).toInt();
if (!hasSetting({"tuyaChannel", n})) break;
const auto dp = getSetting({"tuyaChannel", n}, 0);
channelStates.pushOrUpdate(dp, 0);
}
lightSetupChannels(channelStates.size());
@ -524,11 +524,11 @@ namespace Tuya {
terminalRegisterCommand(F("TUYA.SAVE"), [](Embedis* e) {
DEBUG_MSG_P(PSTR("[TUYA] Saving current configuration ...\n"));
for (unsigned char n=0; n < switchStates.size(); ++n) {
setSetting("tuyaSwitch", n, switchStates[n].dp);
setSetting({"tuyaSwitch", n}, switchStates[n].dp);
}
#if LIGHT_PROVIDER == LIGHT_PROVIDER_TUYA
for (unsigned char n=0; n < channelStates.size(); ++n) {
setSetting("tuyaChannel", n, channelStates[n].dp);
setSetting({"tuyaChannel", n}, channelStates[n].dp);
}
#endif
});
@ -536,12 +536,12 @@ namespace Tuya {
#endif
// Filtering for incoming data
unsigned char filter_raw = getSetting("tuyaFilter", dp_states_filter_t::NONE).toInt();
auto filter_raw = getSetting("tuyaFilter", dp_states_filter_t::NONE);
filterDP = dp_states_filter_t::clamp(filter_raw);
// Print all IN and OUT messages
transportDebug = getSetting("tuyaDebug", 1).toInt() == 1;
transportDebug = getSetting("tuyaDebug", true);
// Install main loop method and WiFiStatus ping (only works with specific mode)


+ 2
- 9
code/espurna/utils.h View File

@ -13,7 +13,6 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
extern "C" uint32_t _SPIFFS_start;
extern "C" uint32_t _SPIFFS_end;
String getIdentifier();
void setDefaultHostname();
void setBoardName();
@ -23,16 +22,10 @@ String getAdminPass();
const String& getCoreVersion();
const String& getCoreRevision();
unsigned char getHeartbeatMode();
unsigned char getHeartbeatInterval();
int getHeartbeatMode();
unsigned long getHeartbeatInterval();
void heartbeat();
String getEspurnaModules();
String getEspurnaOTAModules();
String getEspurnaSensors();
String getEspurnaWebUI();
String buildTime();
unsigned long getUptime();
bool haveRelaysOrSensors();


+ 9
- 40
code/espurna/utils.ino View File

@ -12,12 +12,6 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <Ticker.h>
#include <limits>
String getIdentifier() {
char buffer[20];
snprintf_P(buffer, sizeof(buffer), PSTR("%s-%06X"), APP_NAME, ESP.getChipId());
return String(buffer);
}
void setDefaultHostname() {
if (strlen(HOSTNAME) > 0) {
setSetting("hostname", HOSTNAME);
@ -33,11 +27,13 @@ void setBoardName() {
}
String getBoardName() {
return getSetting("boardName", DEVICE_NAME);
static const String defaultValue(DEVICE_NAME);
return getSetting("boardName", defaultValue);
}
String getAdminPass() {
return getSetting("adminPass", ADMIN_PASS);
static const String defaultValue(ADMIN_PASS);
return getSetting("adminPass", defaultValue);
}
const String& getCoreVersion() {
@ -72,30 +68,12 @@ const String& getCoreRevision() {
return revision;
}
unsigned char getHeartbeatMode() {
return getSetting("hbMode", HEARTBEAT_MODE).toInt();
}
unsigned char getHeartbeatInterval() {
return getSetting("hbInterval", HEARTBEAT_INTERVAL).toInt();
}
String getEspurnaModules() {
return FPSTR(espurna_modules);
}
String getEspurnaOTAModules() {
return FPSTR(espurna_ota_modules);
int getHeartbeatMode() {
return getSetting("hbMode", HEARTBEAT_MODE);
}
#if SENSOR_SUPPORT
String getEspurnaSensors() {
return FPSTR(espurna_sensors);
}
#endif
String getEspurnaWebUI() {
return FPSTR(espurna_webui);
unsigned long getHeartbeatInterval() {
return getSetting("hbInterval", HEARTBEAT_INTERVAL);
}
String buildTime() {
@ -125,15 +103,6 @@ unsigned long getUptime() {
}
bool haveRelaysOrSensors() {
bool result = false;
result = (relayCount() > 0);
#if SENSOR_SUPPORT
result = result || (magnitudeCount() > 0);
#endif
return result;
}
// -----------------------------------------------------------------------------
// Heartbeat helper
// -----------------------------------------------------------------------------
@ -453,7 +422,7 @@ void info(bool first) {
// -------------------------------------------------------------------------
FlashMode_t [[gnu::unused]] mode = ESP.getFlashChipMode();
FlashMode_t mode [[gnu::unused]] = ESP.getFlashChipMode();
DEBUG_MSG_P(PSTR("[MAIN] Flash chip ID: 0x%06X\n"), ESP.getFlashChipId());
DEBUG_MSG_P(PSTR("[MAIN] Flash speed: %u Hz\n"), ESP.getFlashChipSpeed());
DEBUG_MSG_P(PSTR("[MAIN] Flash mode: %s\n"), mode == FM_QIO ? "QIO" : mode == FM_QOUT ? "QOUT" : mode == FM_DIO ? "DIO" : mode == FM_DOUT ? "DOUT" : "UNKNOWN");


+ 3
- 2
code/espurna/web.ino View File

@ -492,11 +492,12 @@ void webRequestRegister(web_request_callback_f callback) {
_web_request_callbacks.push_back(callback);
}
unsigned int webPort() {
uint16_t webPort() {
#if WEB_SSL_ENABLED
return 443;
#else
return getSetting("webPort", WEB_PORT).toInt();
constexpr const uint16_t defaultValue(WEB_PORT);
return getSetting("webPort", defaultValue);
#endif
}


+ 29
- 0
code/espurna/wifi.h View File

@ -0,0 +1,29 @@
/*
WIFI MODULE
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
uint8_t wifiState();
void wifiReconnectCheck();
bool wifiConnected();
String getNetwork();
String getIP();
void wifiDebug();
void wifiDebug(WiFiMode_t modes);
void wifiStartAP();
void wifiStartSTA();
void wifiDisconnect();
void wifiStartWPS();
void wifiStartSmartConfig();
void wifiRegister(wifi_callback_f callback);
void wifiSetup();
void wifiLoop();

+ 77
- 104
code/espurna/wifi.ino View File

@ -11,10 +11,13 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include "ws.h"
#include "wifi.h"
#include "wifi_config.h"
bool _wifi_wps_running = false;
bool _wifi_smartconfig_running = false;
bool _wifi_smartconfig_initial = false;
uint8_t _wifi_ap_mode = WIFI_AP_FALLBACK;
int _wifi_ap_mode = WIFI_AP_FALLBACK;
#if WIFI_GRATUITOUS_ARP_SUPPORT
unsigned long _wifi_gratuitous_arp_interval = 0;
@ -55,6 +58,18 @@ void _wifiCheckAP() {
}
}
WiFiSleepType_t _wifiSleepModeConvert(const String& value) {
switch (value.toInt()) {
case 2:
return WIFI_MODEM_SLEEP;
case 1:
return WIFI_LIGHT_SLEEP;
case 0:
default:
return WIFI_NONE_SLEEP;
}
}
void _wifiConfigure() {
jw.setHostname(getSetting("hostname").c_str());
@ -66,7 +81,7 @@ void _wifiConfigure() {
jw.enableAPFallback(WIFI_FALLBACK_APMODE);
jw.cleanNetworks();
_wifi_ap_mode = getSetting("apmode", WIFI_AP_FALLBACK).toInt();
_wifi_ap_mode = getSetting("apmode", WIFI_AP_FALLBACK);
// If system is flagged unstable we do not init wifi networks
#if SYSTEM_CHECK_ENABLED
@ -76,22 +91,21 @@ void _wifiConfigure() {
// Clean settings
_wifiClean(WIFI_MAX_NETWORKS);
int i;
for (i = 0; i< WIFI_MAX_NETWORKS; i++) {
if (!hasSetting("ssid", i)) break;
if (!hasSetting("ip", i)) {
unsigned char i;
for (i = 0; i < WIFI_MAX_NETWORKS; i++) {
if (hasSetting({"ip", i}) || _wifiHasIP(i)) {
jw.addNetwork(
getSetting("ssid", i, "").c_str(),
getSetting("pass", i, "").c_str()
getSetting({"ssid", i}, _wifiSSID(i)).c_str(),
getSetting({"pass", i}, _wifiPass(i)).c_str(),
getSetting({"ip", i}, _wifiIP(i)).c_str(),
getSetting({"gw", i}, _wifiGateway(i)).c_str(),
getSetting({"mask", i}, _wifiNetmask(i)).c_str(),
getSetting({"dns", i}, _wifiDNS(i)).c_str()
);
} else {
} else if (hasSetting({"ssid", i}) || _wifiHasSSID(i)) {
jw.addNetwork(
getSetting("ssid", i, "").c_str(),
getSetting("pass", i, "").c_str(),
getSetting("ip", i, "").c_str(),
getSetting("gw", i, "").c_str(),
getSetting("mask", i, "").c_str(),
getSetting("dns", i, "").c_str()
getSetting({"ssid", i}, _wifiSSID(i)).c_str(),
getSetting({"pass", i}, _wifiPass(i)).c_str()
);
}
}
@ -99,22 +113,20 @@ void _wifiConfigure() {
#if JUSTWIFI_ENABLE_SMARTCONFIG
if (i == 0) _wifi_smartconfig_initial = true;
#endif
jw.enableScan(getSetting("wifiScan", WIFI_SCAN_NETWORKS).toInt() == 1);
unsigned char sleep_mode = getSetting("wifiSleep", WIFI_SLEEP_MODE).toInt();
sleep_mode = constrain(sleep_mode, 0, 2);
jw.enableScan(getSetting("wifiScan", 1 == WIFI_SCAN_NETWORKS));
WiFi.setSleepMode(static_cast<WiFiSleepType_t>(sleep_mode));
const auto sleep_mode = getSetting<WiFiSleepType_t, _wifiSleepModeConvert>("wifiSleep", WIFI_SLEEP_MODE);
WiFi.setSleepMode(sleep_mode);
#if WIFI_GRATUITOUS_ARP_SUPPORT
_wifi_gratuitous_arp_last = millis();
_wifi_gratuitous_arp_interval = getSetting("wifiGarpIntvl", secureRandom(
WIFI_GRATUITOUS_ARP_INTERVAL_MIN, WIFI_GRATUITOUS_ARP_INTERVAL_MAX
)).toInt();
));
#endif
const auto tx_power = getSetting("wifiTxPwr", WIFI_OUTPUT_POWER_DBM).toFloat();
const auto tx_power = getSetting("wifiTxPwr", WIFI_OUTPUT_POWER_DBM);
WiFi.setOutputPower(tx_power);
}
@ -166,23 +178,23 @@ void _wifiScan(wifi_scan_f callback = nullptr) {
bool _wifiClean(unsigned char num) {
bool changed = false;
int i = 0;
unsigned char i = 0;
// Clean defined settings
while (i < num) {
// Skip on first non-defined setting
if (!hasSetting("ssid", i)) {
delSetting("ssid", i);
if (!getSetting({"ssid", i}).length()) {
delSetting({"ssid", i});
break;
}
// Delete empty values
if (!hasSetting("pass", i)) delSetting("pass", i);
if (!hasSetting("ip", i)) delSetting("ip", i);
if (!hasSetting("gw", i)) delSetting("gw", i);
if (!hasSetting("mask", i)) delSetting("mask", i);
if (!hasSetting("dns", i)) delSetting("dns", i);
if (!getSetting({"pass", i}).length()) delSetting({"pass", i});
if (!getSetting({"ip", i}).length()) delSetting({"ip", i});
if (!getSetting({"gw", i}).length()) delSetting({"gw", i});
if (!getSetting({"mask", i}).length()) delSetting({"mask", i});
if (!getSetting({"dns", i}).length()) delSetting({"dns", i});
++i;
@ -190,13 +202,13 @@ bool _wifiClean(unsigned char num) {
// Delete all other settings
while (i < WIFI_MAX_NETWORKS) {
changed = hasSetting("ssid", i);
delSetting("ssid", i);
delSetting("pass", i);
delSetting("ip", i);
delSetting("gw", i);
delSetting("mask", i);
delSetting("dns", i);
changed = (getSetting({"ssid", i}).length() > 0);
delSetting({"ssid", i});
delSetting({"pass", i});
delSetting({"ip", i});
delSetting({"gw", i});
delSetting({"mask", i});
delSetting({"dns", i});
++i;
}
@ -204,55 +216,6 @@ bool _wifiClean(unsigned char num) {
}
// Inject hardcoded networks
void _wifiInject() {
if (strlen(WIFI1_SSID)) {
if (!hasSetting("ssid", 0)) {
setSetting("ssid", 0, F(WIFI1_SSID));
setSetting("pass", 0, F(WIFI1_PASS));
setSetting("ip", 0, F(WIFI1_IP));
setSetting("gw", 0, F(WIFI1_GW));
setSetting("mask", 0, F(WIFI1_MASK));
setSetting("dns", 0, F(WIFI1_DNS));
}
if (strlen(WIFI2_SSID)) {
if (!hasSetting("ssid", 1)) {
setSetting("ssid", 1, F(WIFI2_SSID));
setSetting("pass", 1, F(WIFI2_PASS));
setSetting("ip", 1, F(WIFI2_IP));
setSetting("gw", 1, F(WIFI2_GW));
setSetting("mask", 1, F(WIFI2_MASK));
setSetting("dns", 1, F(WIFI2_DNS));
}
if (strlen(WIFI3_SSID)) {
if (!hasSetting("ssid", 2)) {
setSetting("ssid", 2, F(WIFI3_SSID));
setSetting("pass", 2, F(WIFI3_PASS));
setSetting("ip", 2, F(WIFI3_IP));
setSetting("gw", 2, F(WIFI3_GW));
setSetting("mask", 2, F(WIFI3_MASK));
setSetting("dns", 2, F(WIFI3_DNS));
}
if (strlen(WIFI4_SSID)) {
if (!hasSetting("ssid", 3)) {
setSetting("ssid", 3, F(WIFI4_SSID));
setSetting("pass", 3, F(WIFI4_PASS));
setSetting("ip", 3, F(WIFI4_IP));
setSetting("gw", 3, F(WIFI4_GW));
setSetting("mask", 3, F(WIFI4_MASK));
setSetting("dns", 3, F(WIFI4_DNS));
}
}
}
}
}
}
void _wifiCallback(justwifi_messages_t code, char * parameter) {
if (MESSAGE_WPS_START == code) {
@ -276,16 +239,16 @@ void _wifiCallback(justwifi_messages_t code, char * parameter) {
// Look for the same SSID
uint8_t count = 0;
while (count < WIFI_MAX_NETWORKS) {
if (!hasSetting("ssid", count)) break;
if (ssid.equals(getSetting("ssid", count, ""))) break;
if (!hasSetting({"ssid", count})) break;
if (ssid.equals(getSetting({"ssid", count}))) break;
count++;
}
// If we have reached the max we overwrite the first one
if (WIFI_MAX_NETWORKS == count) count = 0;
setSetting("ssid", count, ssid);
setSetting("pass", count, pass);
setSetting({"ssid", count}, ssid);
setSetting({"pass", count}, pass);
_wifi_wps_running = false;
_wifi_smartconfig_running = false;
@ -486,18 +449,29 @@ bool _wifiWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
}
void _wifiWebSocketOnConnected(JsonObject& root) {
root["maxNetworks"] = WIFI_MAX_NETWORKS;
root["wifiScan"] = getSetting("wifiScan", WIFI_SCAN_NETWORKS).toInt() == 1;
JsonArray& wifi = root.createNestedArray("wifi");
for (byte i=0; i<WIFI_MAX_NETWORKS; i++) {
if (!hasSetting("ssid", i)) break;
JsonObject& network = wifi.createNestedObject();
network["ssid"] = getSetting("ssid", i, "");
network["pass"] = getSetting("pass", i, "");
network["ip"] = getSetting("ip", i, "");
network["gw"] = getSetting("gw", i, "");
network["mask"] = getSetting("mask", i, "");
network["dns"] = getSetting("dns", i, "");
root["wifiScan"] = getSetting("wifiScan", 1 == WIFI_SCAN_NETWORKS);
JsonObject& wifi = root.createNestedObject("wifi");
root["max"] = WIFI_MAX_NETWORKS;
const char* keys[] = {
"ssid", "pass", "ip", "gw", "mask", "dns", "stored"
};
JsonArray& schema = wifi.createNestedArray("schema");
schema.copyFrom(keys, 7);
JsonArray& networks = wifi.createNestedArray("networks");
for (unsigned char index = 0; index < WIFI_MAX_NETWORKS; ++index) {
if (!getSetting({"ssid", index}, _wifiSSID(index)).length()) break;
JsonArray& network = networks.createNestedArray();
network.add(getSetting({"ssid", index}, _wifiSSID(index)));
network.add(getSetting({"pass", index}, _wifiPass(index)));
network.add(getSetting({"ip", index}, _wifiIP(index)));
network.add(getSetting({"gw", index}, _wifiGateway(index)));
network.add(getSetting({"mask", index}, _wifiNetmask(index)));
network.add(getSetting({"dns", index}, _wifiDNS(index)));
network.add(_wifiHasSSID(index));
}
}
@ -725,13 +699,12 @@ void wifiRegister(wifi_callback_f callback) {
void wifiSetup() {
_wifiInject();
_wifiConfigure();
#if JUSTWIFI_ENABLE_SMARTCONFIG
if (_wifi_smartconfig_initial) jw.startSmartConfig();
#endif
// Message callbacks
wifiRegister(_wifiCallback);
#if WIFI_AP_CAPTIVE


+ 91
- 0
code/espurna/wifi_config.h View File

@ -0,0 +1,91 @@
/*
WIFI MODULE CONFIG
Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
*/
#pragma once
#include <pgmspace.h>
constexpr bool _wifiHasSSID(unsigned char index) {
return (
(index == 0) ? (strlen(WIFI1_SSID) > 0) :
(index == 1) ? (strlen(WIFI2_SSID) > 0) :
(index == 2) ? (strlen(WIFI3_SSID) > 0) :
(index == 3) ? (strlen(WIFI4_SSID) > 0) :
(index == 4) ? (strlen(WIFI5_SSID) > 0) : false
);
}
constexpr bool _wifiHasIP(unsigned char index) {
return (
(index == 0) ? (strlen(WIFI1_IP) > 0) :
(index == 1) ? (strlen(WIFI2_IP) > 0) :
(index == 2) ? (strlen(WIFI3_IP) > 0) :
(index == 3) ? (strlen(WIFI4_IP) > 0) :
(index == 4) ? (strlen(WIFI5_IP) > 0) : false
);
}
const __FlashStringHelper* _wifiSSID(unsigned char index) {
return (
(index == 0) ? F(WIFI1_SSID) :
(index == 1) ? F(WIFI2_SSID) :
(index == 2) ? F(WIFI3_SSID) :
(index == 3) ? F(WIFI4_SSID) :
(index == 4) ? F(WIFI5_SSID) : nullptr
);
}
const __FlashStringHelper* _wifiPass(unsigned char index) {
return (
(index == 0) ? F(WIFI1_PASS) :
(index == 1) ? F(WIFI2_PASS) :
(index == 2) ? F(WIFI3_PASS) :
(index == 3) ? F(WIFI4_PASS) :
(index == 4) ? F(WIFI5_PASS) : nullptr
);
}
const __FlashStringHelper* _wifiIP(unsigned char index) {
return (
(index == 0) ? F(WIFI1_IP) :
(index == 1) ? F(WIFI2_IP) :
(index == 2) ? F(WIFI3_IP) :
(index == 3) ? F(WIFI4_IP) :
(index == 4) ? F(WIFI5_IP) : nullptr
);
}
const __FlashStringHelper* _wifiGateway(unsigned char index) {
return (
(index == 0) ? F(WIFI1_GW) :
(index == 1) ? F(WIFI2_GW) :
(index == 2) ? F(WIFI3_GW) :
(index == 3) ? F(WIFI4_GW) :
(index == 4) ? F(WIFI5_GW) : nullptr
);
}
const __FlashStringHelper* _wifiNetmask(unsigned char index) {
return (
(index == 0) ? F(WIFI1_MASK) :
(index == 1) ? F(WIFI2_MASK) :
(index == 2) ? F(WIFI3_MASK) :
(index == 3) ? F(WIFI4_MASK) :
(index == 4) ? F(WIFI5_MASK) : nullptr
);
}
const __FlashStringHelper* _wifiDNS(unsigned char index) {
return (
(index == 0) ? F(WIFI1_DNS) :
(index == 1) ? F(WIFI2_DNS) :
(index == 2) ? F(WIFI3_DNS) :
(index == 3) ? F(WIFI4_DNS) :
(index == 4) ? F(WIFI5_DNS) : nullptr
);
}

+ 17
- 12
code/espurna/ws.ino View File

@ -155,7 +155,7 @@ bool _wsStore(const String& key, const String& value) {
}
}
if (value != getSetting(key)) {
if (!hasSetting(key) || value != getSetting(key)) {
return setSetting(key, value);
}
@ -167,19 +167,24 @@ bool _wsStore(const String& key, const String& value) {
// Store indexed key (key0, key1, etc.) from array
// -----------------------------------------------------------------------------
bool _wsStore(const String& key, JsonArray& value) {
bool _wsStore(const String& key, JsonArray& values) {
bool changed = false;
unsigned char index = 0;
for (auto element : value) {
if (_wsStore(key + index, element.as<String>())) changed = true;
index++;
for (auto& element : values) {
const auto value = element.as<String>();
const auto keyobj = settings_key_t {key, index};
if (!hasSetting(keyobj) || value != getSetting(keyobj)) {
setSetting(keyobj, value);
changed = true;
}
++index;
}
// Delete further values
for (unsigned char i=index; i<SETTINGS_MAX_LIST_COUNT; i++) {
if (!delSetting(key, index)) break;
for (unsigned char next_index=index; next_index < SETTINGS_MAX_LIST_COUNT; ++next_index) {
if (!delSetting({key, next_index})) break;
changed = true;
}
@ -405,11 +410,11 @@ void _wsOnConnected(JsonObject& root) {
root["sdk"] = ESP.getSdkVersion();
root["core"] = getCoreVersion();
root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt();
root["webPort"] = getSetting("webPort", WEB_PORT).toInt();
root["wsAuth"] = getSetting("wsAuth", WS_AUTHENTICATION).toInt() == 1;
root["hbMode"] = getSetting("hbMode", HEARTBEAT_MODE).toInt();
root["hbInterval"] = getSetting("hbInterval", HEARTBEAT_INTERVAL).toInt();
root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY);
root["webPort"] = getSetting("webPort", WEB_PORT);
root["wsAuth"] = getSetting("wsAuth", 1 == WS_AUTHENTICATION);
root["hbMode"] = getSetting("hbMode", HEARTBEAT_MODE);
root["hbInterval"] = getSetting("hbInterval", HEARTBEAT_INTERVAL);
}
void wsSend(JsonObject& root) {


+ 27
- 11
code/html/custom.js View File

@ -1018,7 +1018,7 @@ function moreNetwork() {
$(".more", parent).toggle();
}
function addNetwork(values) {
function addNetwork(network) {
var number = numNetworks();
if (number >= maxNetworks) {
@ -1026,8 +1026,8 @@ function addNetwork(values) {
return null;
}
if (values === undefined) {
values = {};
if (network === undefined) {
network = {};
}
var tabindex = 200 + number * 10;
@ -1041,10 +1041,18 @@ function addNetwork(values) {
$(line).find(".button-del-network").on("click", delNetwork);
$(line).find(".button-more-network").on("click", moreNetwork);
Object.entries(values).forEach(function(kv) {
$("input[name='" + kv[0] + "']", line).val(kv[1]);
Object.entries(network).forEach(function(pair) {
// XXX: UI deleting this network will only re-use stored values.
var key = pair[0],
val = pair[1];
if (key === "stored") {
$(line).find(".button-del-network").prop("disabled", val);
return;
}
$("input[name='" + key + "']", line).val(val);
});
line.appendTo("#networks");
return line;
@ -1780,13 +1788,21 @@ function processData(data) {
// WiFi
// ---------------------------------------------------------------------
if ("maxNetworks" === key) {
maxNetworks = parseInt(value, 10);
return;
}
if ("wifi" === key) {
value.forEach(addNetwork);
maxNetworks = parseInt(value["max"], 10);
value["networks"].forEach(function(network) {
var schema = value["schema"];
if (schema.length !== network.length) {
throw "WiFi schema mismatch!";
}
var _network = {};
schema.forEach(function(key, index) {
_network[key] = network[index];
});
addNetwork(_network);
});
return;
}


Loading…
Cancel
Save