Browse Source

Merge remote-tracking branch 'origin/dev' into mqtt/group-receive-only

rules-rpn
Max Prokhorov 5 years ago
parent
commit
65af8373cb
31 changed files with 16109 additions and 15767 deletions
  1. +4
    -2
      code/espurna/config/arduino.h
  2. +63
    -6
      code/espurna/config/hardware.h
  3. +3
    -0
      code/espurna/config/prototypes.h
  4. BIN
      code/espurna/data/index.all.html.gz
  5. BIN
      code/espurna/data/index.light.html.gz
  6. BIN
      code/espurna/data/index.rfbridge.html.gz
  7. BIN
      code/espurna/data/index.rfm69.html.gz
  8. BIN
      code/espurna/data/index.sensor.html.gz
  9. BIN
      code/espurna/data/index.small.html.gz
  10. +32
    -29
      code/espurna/debug.ino
  11. +10
    -13
      code/espurna/domoticz.ino
  12. +17
    -14
      code/espurna/light.ino
  13. +13
    -0
      code/espurna/migrate.ino
  14. +11
    -12
      code/espurna/mqtt.ino
  15. +0
    -1
      code/espurna/ntp.ino
  16. +46
    -33
      code/espurna/relay.ino
  17. +23
    -9
      code/espurna/rfbridge.ino
  18. +33
    -18
      code/espurna/scheduler.ino
  19. +48
    -11
      code/espurna/sensor.ino
  20. +1173
    -1168
      code/espurna/static/index.all.html.gz.h
  21. +3003
    -2999
      code/espurna/static/index.light.html.gz.h
  22. +2032
    -2027
      code/espurna/static/index.rfbridge.html.gz.h
  23. +4080
    -4076
      code/espurna/static/index.rfm69.html.gz.h
  24. +2668
    -2664
      code/espurna/static/index.sensor.html.gz.h
  25. +2562
    -2558
      code/espurna/static/index.small.html.gz.h
  26. +8
    -4
      code/espurna/telnet.ino
  27. +2
    -9
      code/espurna/thinkspeak.ino
  28. +126
    -65
      code/espurna/ws.ino
  29. +64
    -40
      code/html/custom.js
  30. +5
    -0
      code/html/index.html
  31. +83
    -9
      code/platformio.ini

+ 4
- 2
code/espurna/config/arduino.h View File

@ -73,6 +73,7 @@
//#define LINGAN_SWA1 //#define LINGAN_SWA1
//#define HEYGO_HY02 //#define HEYGO_HY02
//#define MAXCIO_WUS002S //#define MAXCIO_WUS002S
//#define OUKITEL_P1
//#define YIDIAN_XSSSA05 //#define YIDIAN_XSSSA05
//#define TONBUX_XSSSA06 //#define TONBUX_XSSSA06
//#define TONBUX_XSSSA01 //#define TONBUX_XSSSA01
@ -91,7 +92,7 @@
//#define NEO_COOLCAM_NAS_WR01W //#define NEO_COOLCAM_NAS_WR01W
//#define ESTINK_WIFI_POWER_STRIP //#define ESTINK_WIFI_POWER_STRIP
//#define PILOTAK_ESP_DIN_V1 //#define PILOTAK_ESP_DIN_V1
//#define BLITZWOLF_BWSHP2
//#define BLITZWOLF_BWSHPX
//#define BH_ONOFRE //#define BH_ONOFRE
//#define ITEAD_SONOFF_IFAN02 //#define ITEAD_SONOFF_IFAN02
//#define GENERIC_AG_L4 //#define GENERIC_AG_L4
@ -112,8 +113,9 @@
//#define GBLIFE_RGBW_SOCKET //#define GBLIFE_RGBW_SOCKET
//#define SMARTLIFE_MINI_SMART_SOCKET //#define SMARTLIFE_MINI_SMART_SOCKET
//#define GOSUND_SP1_V23 //#define GOSUND_SP1_V23
//#define GOSUND_WS1
//#define ARILUX_AL_LC02_V14 //#define ARILUX_AL_LC02_V14
//#define BLITZWOLF_BWSHP2_V23
//#define BLITZWOLF_BWSHPX_V23
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Features (values below are non-default values) // Features (values below are non-default values)


+ 63
- 6
code/espurna/config/hardware.h View File

@ -2101,6 +2101,38 @@
#define LED1_PIN 13 #define LED1_PIN 13
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// -----------------------------------------------------------------------------
// Oukitel - P1
// -----------------------------------------------------------------------------
#elif defined(OUKITEL_P1)
// -----------------------------------------------------------------------------
// Oukitel P1 Smart Plug
// https://www.amazon.com/Docooler-OUKITEL-Control-Wireless-Adaptor/dp/B07J3BYFJX/ref=sr_1_fkmrnull_2?keywords=oukitel+p1+smart+switch&qid=1550424399&s=gateway&sr=8-2-fkmrnull
// -----------------------------------------------------------------------------
// Info
#define MANUFACTURER "Oukitel"
#define DEVICE "P1"
// Buttons
#define BUTTON1_PIN 13
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_RELAY 1
// Relays
// Right
#define RELAY1_PIN 12
#define RELAY1_TYPE RELAY_TYPE_NORMAL
// Left
#define RELAY2_PIN 15
#define RELAY2_TYPE RELAY_TYPE_NORMAL
// LEDs
#define LED1_PIN 0 // blue
#define LED1_PIN_INVERSE 1
#define LED1_MODE LED_MODE_WIFI
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// YiDian XS-SSA05 // YiDian XS-SSA05
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -2299,7 +2331,9 @@
// Relays // Relays
#define RELAY1_PIN 0 #define RELAY1_PIN 0
#define RELAY1_TYPE RELAY_TYPE_NORMAL
#ifndef RELAY1_TYPE
#define RELAY1_TYPE RELAY_TYPE_NORMAL // See #1504 and #1554
#endif
// LEDs // LEDs
#define LED1_PIN 2 #define LED1_PIN 2
@ -2689,7 +2723,8 @@
#define RELAY2_TYPE RELAY_TYPE_NORMAL #define RELAY2_TYPE RELAY_TYPE_NORMAL
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Several boards under different names uing a power chip labelled BL0937 or HJL-01
// BlitzWolf SHP2 and SHP6
// Also several boards under different names uing a power chip labelled BL0937 or HJL-01
// * Blitzwolf (https://www.amazon.es/Inteligente-Temporización-Dispositivos-Cualquier-BlitzWolf/dp/B07BMQP142) // * Blitzwolf (https://www.amazon.es/Inteligente-Temporización-Dispositivos-Cualquier-BlitzWolf/dp/B07BMQP142)
// * HomeCube (https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG) // * HomeCube (https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG)
// * Coosa (https://www.amazon.com/COOSA-Monitoring-Function-Campatible-Assiatant/dp/B0788W9TDR) // * Coosa (https://www.amazon.com/COOSA-Monitoring-Function-Campatible-Assiatant/dp/B0788W9TDR)
@ -2697,11 +2732,11 @@
// * Ablue (https://www.amazon.de/Intelligente-Steckdose-Ablue-Funktioniert-Assistant/dp/B076DRFRZC) // * Ablue (https://www.amazon.de/Intelligente-Steckdose-Ablue-Funktioniert-Assistant/dp/B076DRFRZC)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#elif defined(BLITZWOLF_BWSHP2)
#elif defined(BLITZWOLF_BWSHPX)
// Info // Info
#define MANUFACTURER "BLITZWOLF" #define MANUFACTURER "BLITZWOLF"
#define DEVICE "BWSHP2"
#define DEVICE "BWSHPX"
// Buttons // Buttons
#define BUTTON1_PIN 13 #define BUTTON1_PIN 13
@ -2736,13 +2771,15 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Same as the above but new board version marked V2.3 // Same as the above but new board version marked V2.3
// BlitzWolf SHP2 V2.3
// Gosund SP1 V2.3
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#elif defined(BLITZWOLF_BWSHP2_V23)
#elif defined(BLITZWOLF_BWSHPX_V23)
// Info // Info
#define MANUFACTURER "BLITZWOLF" #define MANUFACTURER "BLITZWOLF"
#define DEVICE "BWSHP2V2.3"
#define DEVICE "BWSHPX_V23"
// Buttons // Buttons
#define BUTTON1_PIN 3 #define BUTTON1_PIN 3
@ -2820,6 +2857,26 @@
// Several boards under different names uing a power chip labelled BL0937 or HJL-01 // Several boards under different names uing a power chip labelled BL0937 or HJL-01
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#elif defined(GOSUND_WS1)
// Info
#define MANUFACTURER "GOSUND"
#define DEVICE "WS1"
// Buttons
#define BUTTON1_PIN 0
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_RELAY 1
// Relays
#define RELAY1_PIN 14
#define RELAY1_TYPE RELAY_TYPE_NORMAL
// LEDs
#define LED1_PIN 1
#define LED1_PIN_INVERSE 1
// This one is the same as the BLITZWOLF_BWSHPX_V23
#elif defined(GOSUND_SP1_V23) #elif defined(GOSUND_SP1_V23)
// Info // Info


+ 3
- 0
code/espurna/config/prototypes.h View File

@ -184,6 +184,8 @@ void webRequestRegister(web_request_callback_f callback);
#if WEB_SUPPORT #if WEB_SUPPORT
typedef std::function<void(JsonObject&)> ws_on_send_callback_f; typedef std::function<void(JsonObject&)> ws_on_send_callback_f;
void wsOnSendRegister(ws_on_send_callback_f callback); void wsOnSendRegister(ws_on_send_callback_f callback);
void wsSend(uint32_t, JsonObject& root);
void wsSend(JsonObject& root);
void wsSend(ws_on_send_callback_f sender); void wsSend(ws_on_send_callback_f sender);
typedef std::function<void(uint32_t, const char *, JsonObject&)> ws_on_action_callback_f; typedef std::function<void(uint32_t, const char *, JsonObject&)> ws_on_action_callback_f;
@ -194,6 +196,7 @@ void webRequestRegister(web_request_callback_f callback);
bool wsConnected(); bool wsConnected();
bool wsConnected(uint32_t); bool wsConnected(uint32_t);
bool wsDebugSend(const char*, const char*);
#else #else
#define ws_on_send_callback_f void * #define ws_on_send_callback_f void *
#define ws_on_action_callback_f void * #define ws_on_action_callback_f void *


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.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


+ 32
- 29
code/espurna/debug.ino View File

@ -16,22 +16,43 @@ char _udp_syslog_header[40] = {0};
#endif #endif
#endif #endif
void _debugSend(char * message) {
#if DEBUG_SERIAL_SUPPORT
void _debugSendSerial(const char* prefix, const char* data) {
if (prefix && (prefix[0] != '\0')) {
Serial.print(prefix);
}
Serial.print(data);
}
#endif
#if DEBUG_TELNET_SUPPORT
void _debugSendTelnet(const char* prefix, const char* data) {
if (prefix && (prefix[0] != '\0')) {
_telnetWrite(prefix);
}
_telnetWrite(data);
}
#endif
void _debugSend(const char * message) {
const size_t msg_len = strlen(message);
bool pause = false; bool pause = false;
char timestamp[10] = {0};
#if DEBUG_ADD_TIMESTAMP #if DEBUG_ADD_TIMESTAMP
static bool add_timestamp = true; static bool add_timestamp = true;
char timestamp[10] = {0};
if (add_timestamp) snprintf_P(timestamp, sizeof(timestamp), PSTR("[%06lu] "), millis() % 1000000);
add_timestamp = (message[strlen(message)-1] == 10) || (message[strlen(message)-1] == 13);
if (add_timestamp) {
snprintf(timestamp, sizeof(timestamp), "[%06lu] ", millis() % 1000000);
}
add_timestamp = (message[msg_len - 1] == 10) || (message[msg_len - 1] == 13);
#endif #endif
#if DEBUG_SERIAL_SUPPORT #if DEBUG_SERIAL_SUPPORT
#if DEBUG_ADD_TIMESTAMP
DEBUG_PORT.printf(timestamp);
#endif
DEBUG_PORT.printf(message);
_debugSendSerial(timestamp, message);
#endif #endif
#if DEBUG_UDP_SUPPORT #if DEBUG_UDP_SUPPORT
@ -51,31 +72,13 @@ void _debugSend(char * message) {
#endif #endif
#if DEBUG_TELNET_SUPPORT #if DEBUG_TELNET_SUPPORT
#if DEBUG_ADD_TIMESTAMP
_telnetWrite(timestamp, strlen(timestamp));
#endif
_telnetWrite(message, strlen(message));
_debugSendTelnet(timestamp, message);
pause = true; pause = true;
#endif #endif
#if DEBUG_WEB_SUPPORT #if DEBUG_WEB_SUPPORT
if (wsConnected() && (getFreeHeap() > 10000)) {
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(1) + strlen(message) + 17);
JsonObject &root = jsonBuffer.createObject();
#if DEBUG_ADD_TIMESTAMP
char buffer[strlen(timestamp) + strlen(message) + 1];
snprintf_P(buffer, sizeof(buffer), "%s%s", timestamp, message);
root.set("weblog", buffer);
#else
root.set("weblog", message);
#endif
String out;
root.printTo(out);
jsonBuffer.clear();
wsSend(out.c_str());
pause = true;
}
wsDebugSend(timestamp, message);
pause = true;
#endif #endif
if (pause) optimistic_yield(100); if (pause) optimistic_yield(100);


+ 10
- 13
code/espurna/domoticz.ino View File

@ -166,8 +166,10 @@ void _domoticzBrokerCallback(const unsigned char type, const char * topic, unsig
if (BROKER_MSG_TYPE_STATUS != type) return; if (BROKER_MSG_TYPE_STATUS != type) return;
if (strcmp(MQTT_TOPIC_RELAY, topic) == 0) { if (strcmp(MQTT_TOPIC_RELAY, topic) == 0) {
unsigned char value = atoi(payload);
domoticzSendRelay(id, value == 1);
bool status = atoi(payload) == 1;
if (_domoticzStatus(id) == status) return;
_dcz_relay_state[id] = status;
domoticzSendRelay(id, status);
} }
} }
@ -181,7 +183,7 @@ bool _domoticzWebSocketOnReceive(const char * key, JsonVariant& value) {
void _domoticzWebSocketOnSend(JsonObject& root) { void _domoticzWebSocketOnSend(JsonObject& root) {
root["dczVisible"] = 1;
unsigned char visible = 0;
root["dczEnabled"] = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1; root["dczEnabled"] = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1;
root["dczTopicIn"] = getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC); root["dczTopicIn"] = getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC);
root["dczTopicOut"] = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC); root["dczTopicOut"] = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
@ -190,18 +192,15 @@ void _domoticzWebSocketOnSend(JsonObject& root) {
for (unsigned char i=0; i<relayCount(); i++) { for (unsigned char i=0; i<relayCount(); i++) {
relays.add(domoticzIdx(i)); relays.add(domoticzIdx(i));
} }
visible = (relayCount() > 0);
#if SENSOR_SUPPORT #if SENSOR_SUPPORT
JsonArray& list = root.createNestedArray("dczMagnitudes");
for (byte i=0; i<magnitudeCount(); i++) {
JsonObject& element = list.createNestedObject();
element["name"] = magnitudeName(i);
element["type"] = magnitudeType(i);
element["index"] = magnitudeIndex(i);
element["idx"] = getSetting("dczMagnitude", i, 0).toInt();
}
_sensorWebSocketMagnitudes(root, "dcz");
visible = visible || (magnitudeCount() > 0);
#endif #endif
root["dczVisible"] = visible;
} }
#endif // WEB_SUPPORT #endif // WEB_SUPPORT
@ -238,8 +237,6 @@ template<typename T> void domoticzSend(const char * key, T nvalue) {
void domoticzSendRelay(unsigned char relayID, bool status) { void domoticzSendRelay(unsigned char relayID, bool status) {
if (!_dcz_enabled) return; if (!_dcz_enabled) return;
if (_domoticzStatus(relayID) == status) return;
_dcz_relay_state[relayID] = status;
char buffer[15]; char buffer[15];
snprintf_P(buffer, sizeof(buffer), PSTR("dczRelayIdx%u"), relayID); snprintf_P(buffer, sizeof(buffer), PSTR("dczRelayIdx%u"), relayID);
domoticzSend(buffer, status ? "1" : "0"); domoticzSend(buffer, status ? "1" : "0");


+ 17
- 14
code/espurna/light.ino View File

@ -669,7 +669,7 @@ void _lightComms(unsigned char mask) {
// Report color to WS clients (using current brightness setting) // Report color to WS clients (using current brightness setting)
#if WEB_SUPPORT #if WEB_SUPPORT
wsSend(_lightWebSocketOnSend);
wsSend(_lightWebSocketStatus);
#endif #endif
// Report channels to local broker // Report channels to local broker
@ -824,23 +824,13 @@ bool _lightWebSocketOnReceive(const char * key, JsonVariant& value) {
return false; return false;
} }
void _lightWebSocketOnSend(JsonObject& root) {
root["colorVisible"] = 1;
root["mqttGroupColor"] = getSetting("mqttGroupColor");
root["useColor"] = _light_has_color;
root["useWhite"] = _light_use_white;
root["useGamma"] = _light_use_gamma;
root["useTransitions"] = _light_use_transitions;
root["lightTime"] = _light_transition_time;
root["useCSS"] = getSetting("useCSS", LIGHT_USE_CSS).toInt() == 1;
bool useRGB = getSetting("useRGB", LIGHT_USE_RGB).toInt() == 1;
root["useRGB"] = useRGB;
void _lightWebSocketStatus(JsonObject& root) {
if (_light_has_color) { if (_light_has_color) {
if (_light_use_cct) { if (_light_use_cct) {
root["useCCT"] = _light_use_cct; root["useCCT"] = _light_use_cct;
root["mireds"] = _light_mireds; root["mireds"] = _light_mireds;
} }
if (useRGB) {
if (getSetting("useRGB", LIGHT_USE_RGB).toInt() == 1) {
root["rgb"] = lightColor(true); root["rgb"] = lightColor(true);
} else { } else {
root["hsv"] = lightColor(false); root["hsv"] = lightColor(false);
@ -850,7 +840,20 @@ void _lightWebSocketOnSend(JsonObject& root) {
for (unsigned char id=0; id < _light_channel.size(); id++) { for (unsigned char id=0; id < _light_channel.size(); id++) {
channels.add(lightChannel(id)); channels.add(lightChannel(id));
} }
root["brightness"] = lightBrightness();
}
void _lightWebSocketOnSend(JsonObject& root) {
root["colorVisible"] = 1;
root["mqttGroupColor"] = getSetting("mqttGroupColor");
root["useColor"] = _light_has_color;
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["lightTime"] = _light_transition_time;
_lightWebSocketStatus(root);
} }
void _lightWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) { void _lightWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {


+ 13
- 0
code/espurna/migrate.ino View File

@ -1256,6 +1256,19 @@ void migrate() {
setSetting("relayGPIO", 0, 4); setSetting("relayGPIO", 0, 4);
setSetting("relayType", 0, RELAY_TYPE_NORMAL); setSetting("relayType", 0, RELAY_TYPE_NORMAL);
#elif defined(OUKITEL_P1)
setSetting("board", 94);
setSetting("ledGPIO", 0, 0); // Blue LED
setSetting("ledLogic", 0, 0);
setSetting("btnGPIO", 0, 13);
setSetting("btnRelay", 0, 0);
setSetting("relayGPIO", 0, 12); // Right outlet
setSetting("relayType", 0, RELAY_TYPE_NORMAL);
setSetting("relayGPIO", 1, 15); // Left outlet
setSetting("relayType", 1, RELAY_TYPE_NORMAL);
#else #else
// Allow users to define new settings without migration config // Allow users to define new settings without migration config


+ 11
- 12
code/espurna/mqtt.ino View File

@ -36,6 +36,8 @@ WiFiClientSecure _mqtt_client_secure;
bool _mqtt_enabled = MQTT_ENABLED; bool _mqtt_enabled = MQTT_ENABLED;
bool _mqtt_use_json = false; bool _mqtt_use_json = false;
unsigned long _mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN; unsigned long _mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
unsigned long _mqtt_last_connection = 0;
bool _mqtt_connecting = false;
unsigned char _mqtt_qos = MQTT_QOS; unsigned char _mqtt_qos = MQTT_QOS;
bool _mqtt_retain = MQTT_RETAIN; bool _mqtt_retain = MQTT_RETAIN;
unsigned long _mqtt_keepalive = MQTT_KEEPALIVE; unsigned long _mqtt_keepalive = MQTT_KEEPALIVE;
@ -48,10 +50,6 @@ char *_mqtt_user = 0;
char *_mqtt_pass = 0; char *_mqtt_pass = 0;
char *_mqtt_will; char *_mqtt_will;
char *_mqtt_clientid; char *_mqtt_clientid;
#if MQTT_SKIP_RETAINED
unsigned long _mqtt_connected_at = 0;
#endif
unsigned long _mqtt_disconnected_at = 0;
std::vector<mqtt_callback_f> _mqtt_callbacks; std::vector<mqtt_callback_f> _mqtt_callbacks;
@ -72,11 +70,11 @@ void _mqttConnect() {
// Do not connect if disabled // Do not connect if disabled
if (!_mqtt_enabled) return; if (!_mqtt_enabled) return;
// Do not connect if already connected
if (_mqtt.connected()) return;
// Do not connect if already connected or still trying to connect
if (_mqtt.connected() || _mqtt_connecting) return;
// Check reconnect interval // Check reconnect interval
if (millis() - _mqtt_disconnected_at < _mqtt_reconnect_delay) return;
if (millis() - _mqtt_last_connection < _mqtt_reconnect_delay) return;
// Increase the reconnect delay // Increase the reconnect delay
_mqtt_reconnect_delay += MQTT_RECONNECT_DELAY_STEP; _mqtt_reconnect_delay += MQTT_RECONNECT_DELAY_STEP;
@ -109,6 +107,7 @@ void _mqttConnect() {
DEBUG_MSG_P(PSTR("[MQTT] Connecting to broker at %s:%d\n"), host, port); DEBUG_MSG_P(PSTR("[MQTT] Connecting to broker at %s:%d\n"), host, port);
#if MQTT_USE_ASYNC #if MQTT_USE_ASYNC
_mqtt_connecting = true;
_mqtt.setServer(host, port); _mqtt.setServer(host, port);
_mqtt.setClientId(_mqtt_clientid); _mqtt.setClientId(_mqtt_clientid);
@ -397,9 +396,7 @@ void _mqttOnConnect() {
DEBUG_MSG_P(PSTR("[MQTT] Connected!\n")); DEBUG_MSG_P(PSTR("[MQTT] Connected!\n"));
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN; _mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
#if MQTT_SKIP_RETAINED
_mqtt_connected_at = millis();
#endif
_mqtt_last_connection = millis();
// Clean subscriptions // Clean subscriptions
mqttUnsubscribeRaw("#"); mqttUnsubscribeRaw("#");
@ -413,7 +410,9 @@ void _mqttOnConnect() {
void _mqttOnDisconnect() { void _mqttOnDisconnect() {
_mqtt_disconnected_at = millis();
// Reset reconnection delay
_mqtt_last_connection = millis();
_mqtt_connecting = false;
DEBUG_MSG_P(PSTR("[MQTT] Disconnected!\n")); DEBUG_MSG_P(PSTR("[MQTT] Disconnected!\n"));
@ -432,7 +431,7 @@ void _mqttOnMessage(char* topic, char* payload, unsigned int len) {
strlcpy(message, (char *) payload, len + 1); strlcpy(message, (char *) payload, len + 1);
#if MQTT_SKIP_RETAINED #if MQTT_SKIP_RETAINED
if (millis() - _mqtt_connected_at < MQTT_SKIP_TIME) {
if (millis() - _mqtt_last_connection < MQTT_SKIP_TIME) {
DEBUG_MSG_P(PSTR("[MQTT] Received %s => %s - SKIPPED\n"), topic, message); DEBUG_MSG_P(PSTR("[MQTT] Received %s => %s - SKIPPED\n"), topic, message);
return; return;
} }


+ 0
- 1
code/espurna/ntp.ino View File

@ -34,7 +34,6 @@ void _ntpWebSocketOnSend(JsonObject& root) {
root["ntpOffset"] = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt(); root["ntpOffset"] = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
root["ntpDST"] = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1; root["ntpDST"] = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1;
root["ntpRegion"] = getSetting("ntpRegion", NTP_DST_REGION).toInt(); root["ntpRegion"] = getSetting("ntpRegion", NTP_DST_REGION).toInt();
if (ntpSynced()) root["now"] = now();
} }
#endif #endif


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

@ -592,65 +592,78 @@ bool _relayWebSocketOnReceive(const char * key, JsonVariant& value) {
void _relayWebSocketUpdate(JsonObject& root) { void _relayWebSocketUpdate(JsonObject& root) {
JsonArray& relay = root.createNestedArray("relayStatus"); JsonArray& relay = root.createNestedArray("relayStatus");
for (unsigned char i=0; i<relayCount(); i++) { for (unsigned char i=0; i<relayCount(); i++) {
relay.add(_relays[i].target_status);
relay.add<uint8_t>(_relays[i].target_status);
} }
} }
void _relayWebSocketSendRelay(unsigned char i) {
String _relayFriendlyName(unsigned char i) {
String res = String("GPIO") + String(_relays[i].pin);
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonArray& config = root.createNestedArray("relayConfig");
JsonObject& line = config.createNestedObject();
line["id"] = i;
if (GPIO_NONE == _relays[i].pin) { if (GPIO_NONE == _relays[i].pin) {
#if (RELAY_PROVIDER == RELAY_PROVIDER_LIGHT) #if (RELAY_PROVIDER == RELAY_PROVIDER_LIGHT)
uint8_t physical = _relays.size() - DUMMY_RELAY_COUNT; uint8_t physical = _relays.size() - DUMMY_RELAY_COUNT;
if (i >= physical) { if (i >= physical) {
if (DUMMY_RELAY_COUNT == lightChannels()) { if (DUMMY_RELAY_COUNT == lightChannels()) {
line["gpio"] = String("CH") + String(i-physical);
res = String("CH") + String(i-physical);
} else if (DUMMY_RELAY_COUNT == (lightChannels() + 1u)) { } else if (DUMMY_RELAY_COUNT == (lightChannels() + 1u)) {
if (physical == i) { if (physical == i) {
line["gpio"] = String("Light");
res = String("Light");
} else { } else {
line["gpio"] = String("CH") + String(i-1-physical);
res = String("CH") + String(i-1-physical);
} }
} else { } else {
line["gpio"] = String("Light");
res = String("Light");
} }
} else { } else {
line["gpio"] = String("?");
res = String("?");
} }
#else #else
line["gpio"] = String("SW") + String(i);
res = String("SW") + String(i);
#endif #endif
} else {
line["gpio"] = String("GPIO") + String(_relays[i].pin);
} }
line["type"] = _relays[i].type;
line["reset"] = _relays[i].reset_pin;
line["boot"] = getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt();
line["pulse"] = _relays[i].pulse;
line["pulse_ms"] = _relays[i].pulse_ms / 1000.0;
#if MQTT_SUPPORT
line["group"] = getSetting("mqttGroup", i, "");
line["group_sync"] = getSetting("mqttGroupSync", i, RELAY_GROUP_SYNC_NORMAL).toInt();
line["on_disc"] = getSetting("relayOnDisc", i, 0).toInt();
#endif
String output;
root.printTo(output);
jsonBuffer.clear();
wsSend((char *) output.c_str());
return res;
} }
void _relayWebSocketSendRelays() { void _relayWebSocketSendRelays() {
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonObject& relays = root.createNestedObject("relayConfig");
relays["size"] = relayCount();
relays["start"] = 0;
JsonArray& gpio = relays.createNestedArray("gpio");
JsonArray& type = relays.createNestedArray("type");
JsonArray& reset = relays.createNestedArray("reset");
JsonArray& boot = relays.createNestedArray("boot");
JsonArray& pulse = relays.createNestedArray("pulse");
JsonArray& pulse_time = relays.createNestedArray("pulse_time");
#if MQTT_SUPPORT
JsonArray& group = relays.createNestedArray("group");
JsonArray& group_sync = relays.createNestedArray("group_sync");
JsonArray& on_disconnect = relays.createNestedArray("on_disc");
#endif
for (unsigned char i=0; i<relayCount(); i++) { for (unsigned char i=0; i<relayCount(); i++) {
_relayWebSocketSendRelay(i);
gpio.add(_relayFriendlyName(i));
type.add(_relays[i].type);
reset.add(_relays[i].reset_pin);
boot.add(getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt());
pulse.add(_relays[i].pulse);
pulse_time.add(_relays[i].pulse_ms / 1000.0);
#if MQTT_SUPPORT
group.add(getSetting("mqttGroup", i, ""));
group_sync.add(getSetting("mqttGroupSync", i, 0).toInt() == 1);
on_disconnect.add(getSetting("relayOnDisc", i, 0).toInt());
#endif
} }
wsSend(root);
} }
void _relayWebSocketOnStart(JsonObject& root) { void _relayWebSocketOnStart(JsonObject& root) {


+ 23
- 9
code/espurna/rfbridge.ino View File

@ -88,17 +88,31 @@ static bool _rfbToChar(byte * in, char * out, int n = RF_MESSAGE_SIZE) {
#if WEB_SUPPORT #if WEB_SUPPORT
void _rfbWebSocketSendCode(unsigned char id, bool status, const char * code) {
char wsb[192]; // (32 * 5): 46 bytes for json , 116 bytes raw code, reserve
snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"%s\"}]}"), id, status ? 1 : 0, code);
wsSend(wsb);
void _rfbWebSocketSendCodeArray(unsigned char start, unsigned char size) {
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonObject& rfb = root.createNestedObject("rfb");
rfb["size"] = size;
rfb["start"] = start;
JsonArray& on = rfb.createNestedArray("on");
JsonArray& off = rfb.createNestedArray("off");
for (byte id=start; id<start+size; id++) {
on.add(rfbRetrieve(id, true));
off.add(rfbRetrieve(id, false));
}
wsSend(rfb);
}
void _rfbWebSocketSendCode(unsigned char id) {
_rfbWebSocketSendCodeArray(id, 1);
} }
void _rfbWebSocketSendCodes() { void _rfbWebSocketSendCodes() {
for (unsigned char id=0; id<relayCount(); id++) {
_rfbWebSocketSendCode(id, true, rfbRetrieve(id, true).c_str());
_rfbWebSocketSendCode(id, false, rfbRetrieve(id, false).c_str());
}
_rfbWebSocketSendCodeArray(0, relayCount());
} }
void _rfbWebSocketOnSend(JsonObject& root) { void _rfbWebSocketOnSend(JsonObject& root) {
@ -337,7 +351,7 @@ void _rfbDecode() {
// Websocket update // Websocket update
#if WEB_SUPPORT #if WEB_SUPPORT
_rfbWebSocketSendCode(_learnId, _learnStatus, buffer);
_rfbWebSocketSendCode(_learnId);
#endif #endif
} }


+ 33
- 18
code/espurna/scheduler.ino View File

@ -21,26 +21,41 @@ bool _schWebSocketOnReceive(const char * key, JsonVariant& value) {
void _schWebSocketOnSend(JsonObject &root){ void _schWebSocketOnSend(JsonObject &root){
if (relayCount() > 0) {
root["schVisible"] = 1;
root["maxSchedules"] = SCHEDULER_MAX_SCHEDULES;
JsonArray &sch = root.createNestedArray("schedule");
for (byte i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
if (!hasSetting("schSwitch", i)) break;
JsonObject &scheduler = sch.createNestedObject();
scheduler["schEnabled"] = getSetting("schEnabled", i, 1).toInt() == 1;
scheduler["schSwitch"] = getSetting("schSwitch", i, 0).toInt();
scheduler["schAction"] = getSetting("schAction", i, 0).toInt();
scheduler["schType"] = getSetting("schType", i, 0).toInt();
scheduler["schHour"] = getSetting("schHour", i, 0).toInt();
scheduler["schMinute"] = getSetting("schMinute", i, 0).toInt();
scheduler["schUTC"] = getSetting("schUTC", i, 0).toInt() == 1;
scheduler["schWDs"] = getSetting("schWDs", i, "");
}
if (!relayCount()) return;
root["schVisible"] = 1;
root["maxSchedules"] = SCHEDULER_MAX_SCHEDULES;
JsonObject &schedules = root.createNestedObject("schedules");
uint8_t size = 0;
JsonArray& enabled = schedules.createNestedArray("schEnabled");
JsonArray& switch_ = schedules.createNestedArray("schSwitch");
JsonArray& action = schedules.createNestedArray("schAction");
JsonArray& type = schedules.createNestedArray("schType");
JsonArray& hour = schedules.createNestedArray("schHour");
JsonArray& minute = schedules.createNestedArray("schMinute");
JsonArray& utc = schedules.createNestedArray("schUTC");
JsonArray& weekdays = schedules.createNestedArray("schWDs");
for (byte i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
if (!hasSetting("schSwitch", i)) break;
++size;
enabled.add<uint8_t>(getSetting("schEnabled", i, 1).toInt() == 1);
utc.add<uint8_t>(getSetting("schUTC", i, 0).toInt() == 1);
switch_.add(getSetting("schSwitch", i, 0).toInt());
action.add(getSetting("schAction", i, 0).toInt());
type.add(getSetting("schType", i, 0).toInt());
hour.add(getSetting("schHour", i, 0).toInt());
minute.add(getSetting("schMinute", i, 0).toInt());
weekdays.add(getSetting("schWDs", i, ""));
} }
schedules["size"] = size;
schedules["start"] = 0;
} }
#endif // WEB_SUPPORT #endif // WEB_SUPPORT


+ 48
- 11
code/espurna/sensor.ino View File

@ -102,6 +102,32 @@ double _magnitudeProcess(unsigned char type, double value) {
#if WEB_SUPPORT #if WEB_SUPPORT
template<typename T>
void _sensorWebSocketMagnitudes(JsonObject& root, T prefix) {
// ws produces flat list <prefix>Magnitudes
String ws_name = String(prefix);
ws_name.concat("Magnitudes");
// config uses <prefix>Magnitude<index> (cut 's')
String conf_name = ws_name.substring(0, ws_name.length() - 1);
JsonObject& list = root.createNestedObject(ws_name);
list["size"] = magnitudeCount();
JsonArray& name = list.createNestedArray("name");
JsonArray& type = list.createNestedArray("type");
JsonArray& index = list.createNestedArray("index");
JsonArray& idx = list.createNestedArray("idx");
for (unsigned char i=0; i<magnitudeCount(); ++i) {
name.add(magnitudeName(i));
type.add(magnitudeType(i));
index.add(magnitudeIndex(i));
idx.add(getSetting(conf_name, i, 0).toInt());
}
}
bool _sensorWebSocketOnReceive(const char * key, JsonVariant& value) { bool _sensorWebSocketOnReceive(const char * key, JsonVariant& value) {
if (strncmp(key, "pwr", 3) == 0) return true; if (strncmp(key, "pwr", 3) == 0) return true;
if (strncmp(key, "sns", 3) == 0) return true; if (strncmp(key, "sns", 3) == 0) return true;
@ -118,27 +144,36 @@ void _sensorWebSocketSendData(JsonObject& root) {
bool hasHumidity = false; bool hasHumidity = false;
bool hasMICS = false; bool hasMICS = false;
JsonArray& list = root.createNestedArray("magnitudes");
for (unsigned char i=0; i<_magnitudes.size(); i++) {
JsonObject& magnitudes = root.createNestedObject("magnitudes");
uint8_t size = 0;
JsonArray& index = magnitudes.createNestedArray("index");
JsonArray& type = magnitudes.createNestedArray("type");
JsonArray& value = magnitudes.createNestedArray("value");
JsonArray& units = magnitudes.createNestedArray("units");
JsonArray& error = magnitudes.createNestedArray("error");
JsonArray& description = magnitudes.createNestedArray("description");
for (unsigned char i=0; i<magnitudeCount(); i++) {
sensor_magnitude_t magnitude = _magnitudes[i]; sensor_magnitude_t magnitude = _magnitudes[i];
if (magnitude.type == MAGNITUDE_EVENT) continue; if (magnitude.type == MAGNITUDE_EVENT) continue;
++size;
unsigned char decimals = _magnitudeDecimals(magnitude.type); unsigned char decimals = _magnitudeDecimals(magnitude.type);
dtostrf(magnitude.current, 1-sizeof(buffer), decimals, buffer); dtostrf(magnitude.current, 1-sizeof(buffer), decimals, buffer);
JsonObject& element = list.createNestedObject();
element["index"] = int(magnitude.global);
element["type"] = int(magnitude.type);
element["value"] = String(buffer);
element["units"] = magnitudeUnits(magnitude.type);
element["error"] = magnitude.sensor->error();
index.add<uint8_t>(magnitude.global);
type.add<uint8_t>(magnitude.type);
value.add(buffer);
units.add(magnitudeUnits(magnitude.type));
error.add(magnitude.sensor->error());
if (magnitude.type == MAGNITUDE_ENERGY) { if (magnitude.type == MAGNITUDE_ENERGY) {
if (_sensor_energy_reset_ts.length() == 0) _sensorResetTS(); if (_sensor_energy_reset_ts.length() == 0) _sensorResetTS();
element["description"] = magnitude.sensor->slot(magnitude.local) + String(" (since ") + _sensor_energy_reset_ts + String(")");
description.add(magnitude.sensor->slot(magnitude.local) + String(" (since ") + _sensor_energy_reset_ts + String(")"));
} else { } else {
element["description"] = magnitude.sensor->slot(magnitude.local);
description.add(magnitude.sensor->slot(magnitude.local));
} }
if (magnitude.type == MAGNITUDE_TEMPERATURE) hasTemperature = true; if (magnitude.type == MAGNITUDE_TEMPERATURE) hasTemperature = true;
@ -148,6 +183,8 @@ void _sensorWebSocketSendData(JsonObject& root) {
#endif #endif
} }
magnitudes["size"] = size;
if (hasTemperature) root["temperatureVisible"] = 1; if (hasTemperature) root["temperatureVisible"] = 1;
if (hasHumidity) root["humidityVisible"] = 1; if (hasHumidity) root["humidityVisible"] = 1;
if (hasMICS) root["micsVisible"] = 1; if (hasMICS) root["micsVisible"] = 1;
@ -210,7 +247,7 @@ void _sensorWebSocketStart(JsonObject& root) {
} }
if (_magnitudes.size() > 0) {
if (magnitudeCount()) {
root["snsVisible"] = 1; root["snsVisible"] = 1;
//root["apiRealTime"] = _sensor_realtime; //root["apiRealTime"] = _sensor_realtime;
root["pwrUnits"] = _sensor_power_units; root["pwrUnits"] = _sensor_power_units;


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


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


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


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


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


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


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

@ -45,14 +45,14 @@ void _telnetDisconnect(unsigned char clientId) {
DEBUG_MSG_P(PSTR("[TELNET] Client #%d disconnected\n"), clientId); DEBUG_MSG_P(PSTR("[TELNET] Client #%d disconnected\n"), clientId);
} }
bool _telnetWrite(unsigned char clientId, void *data, size_t len) {
bool _telnetWrite(unsigned char clientId, const char *data, size_t len) {
if (_telnetClients[clientId] && _telnetClients[clientId]->connected()) { if (_telnetClients[clientId] && _telnetClients[clientId]->connected()) {
return (_telnetClients[clientId]->write((const char*) data, len) > 0);
return (_telnetClients[clientId]->write(data, len) > 0);
} }
return false; return false;
} }
unsigned char _telnetWrite(void *data, size_t len) {
unsigned char _telnetWrite(const char *data, size_t len) {
unsigned char count = 0; unsigned char count = 0;
for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) { for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
// Do not send broadcast messages to unauthenticated clients // Do not send broadcast messages to unauthenticated clients
@ -65,8 +65,12 @@ unsigned char _telnetWrite(void *data, size_t len) {
return count; return count;
} }
unsigned char _telnetWrite(const char *data) {
return _telnetWrite(data, strlen(data));
}
bool _telnetWrite(unsigned char clientId, const char * message) { bool _telnetWrite(unsigned char clientId, const char * message) {
return _telnetWrite(clientId, (void *) message, strlen(message));
return _telnetWrite(clientId, message, strlen(message));
} }
void _telnetData(unsigned char clientId, void *data, size_t len) { void _telnetData(unsigned char clientId, void *data, size_t len) {


+ 2
- 9
code/espurna/thinkspeak.ino View File

@ -75,15 +75,8 @@ void _tspkWebSocketOnSend(JsonObject& root) {
if (relayCount() > 0) visible = 1; if (relayCount() > 0) visible = 1;
#if SENSOR_SUPPORT #if SENSOR_SUPPORT
JsonArray& list = root.createNestedArray("tspkMagnitudes");
for (byte i=0; i<magnitudeCount(); i++) {
JsonObject& element = list.createNestedObject();
element["name"] = magnitudeName(i);
element["type"] = magnitudeType(i);
element["index"] = magnitudeIndex(i);
element["idx"] = getSetting("tspkMagnitude", i, 0).toInt();
}
if (magnitudeCount() > 0) visible = 1;
_sensorWebSocketMagnitudes(root, "tspk");
visible = visible || (magnitudeCount() > 0);
#endif #endif
root["tspkVisible"] = visible; root["tspkVisible"] = visible;


+ 126
- 65
code/espurna/ws.ino View File

@ -73,6 +73,27 @@ bool _wsAuth(AsyncWebSocketClient * client) {
} }
#if DEBUG_WEB_SUPPORT
bool wsDebugSend(const char* prefix, const char* message) {
if (!wsConnected()) return false;
if (getFreeHeap() < (strlen(message) * 3)) return false;
DynamicJsonBuffer jsonBuffer;
JsonObject &root = jsonBuffer.createObject();
JsonObject &weblog = root.createNestedObject("weblog");
weblog.set("message", message);
if (prefix && (prefix[0] != '\0')) {
weblog.set("prefix", prefix);
}
wsSend(root);
return true;
}
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if MQTT_SUPPORT #if MQTT_SUPPORT
@ -289,6 +310,20 @@ void _wsUpdate(JsonObject& root) {
#endif #endif
} }
void _wsDoUpdate(bool reset = false) {
static unsigned long last = 0;
if (reset) {
last = 0;
return;
}
if (millis() - last > WS_UPDATE_INTERVAL) {
last = millis();
wsSend(_wsUpdate);
}
}
bool _wsOnReceive(const char * key, JsonVariant& value) { bool _wsOnReceive(const char * key, JsonVariant& value) {
if (strncmp(key, "ws", 2) == 0) return true; if (strncmp(key, "ws", 2) == 0) return true;
if (strncmp(key, "admin", 5) == 0) return true; if (strncmp(key, "admin", 5) == 0) return true;
@ -299,70 +334,95 @@ bool _wsOnReceive(const char * key, JsonVariant& value) {
} }
void _wsOnStart(JsonObject& root) { void _wsOnStart(JsonObject& root) {
char chipid[7];
snprintf_P(chipid, sizeof(chipid), PSTR("%06X"), ESP.getChipId());
uint8_t * bssid = WiFi.BSSID();
char bssid_str[20];
snprintf_P(bssid_str, sizeof(bssid_str),
PSTR("%02X:%02X:%02X:%02X:%02X:%02X"),
bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]
);
root["webMode"] = WEB_MODE_NORMAL;
root["app_name"] = APP_NAME;
root["app_version"] = APP_VERSION;
root["app_build"] = buildTime();
#if defined(APP_REVISION)
root["app_revision"] = APP_REVISION;
#endif
root["manufacturer"] = MANUFACTURER;
root["chipid"] = String(chipid);
root["mac"] = WiFi.macAddress();
root["bssid"] = String(bssid_str);
root["channel"] = WiFi.channel();
root["device"] = DEVICE;
root["hostname"] = getSetting("hostname");
root["desc"] = getSetting("desc");
root["network"] = getNetwork();
root["deviceip"] = getIP();
root["sketch_size"] = ESP.getSketchSize();
root["free_size"] = ESP.getFreeSketchSpace();
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;
#if TERMINAL_SUPPORT
root["cmdVisible"] = 1;
#endif
root["hbMode"] = getSetting("hbMode", HEARTBEAT_MODE).toInt();
root["hbInterval"] = getSetting("hbInterval", HEARTBEAT_INTERVAL).toInt();
_wsDoUpdate(true);
}
void wsSend(JsonObject& root) {
size_t len = root.measureLength();
AsyncWebSocketMessageBuffer* buffer = _ws.makeBuffer(len);
if (buffer) {
root.printTo(reinterpret_cast<char*>(buffer->get()), len + 1);
_ws.textAll(buffer);
}
}
void wsSend(uint32_t client_id, JsonObject& root) {
AsyncWebSocketClient* client = _ws.client(client_id);
if (client == nullptr) return;
size_t len = root.measureLength();
AsyncWebSocketMessageBuffer* buffer = _ws.makeBuffer(len);
if (buffer) {
root.printTo(reinterpret_cast<char*>(buffer->get()), len + 1);
client->text(buffer);
}
}
void _wsStart(uint32_t client_id) {
#if USE_PASSWORD && WEB_FORCE_PASS_CHANGE #if USE_PASSWORD && WEB_FORCE_PASS_CHANGE
bool changePassword = getAdminPass().equals(ADMIN_PASS); bool changePassword = getAdminPass().equals(ADMIN_PASS);
#else #else
bool changePassword = false; bool changePassword = false;
#endif #endif
if (changePassword) {
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
if (changePassword) {
root["webMode"] = WEB_MODE_PASSWORD; root["webMode"] = WEB_MODE_PASSWORD;
} else {
char chipid[7];
snprintf_P(chipid, sizeof(chipid), PSTR("%06X"), ESP.getChipId());
uint8_t * bssid = WiFi.BSSID();
char bssid_str[20];
snprintf_P(bssid_str, sizeof(bssid_str),
PSTR("%02X:%02X:%02X:%02X:%02X:%02X"),
bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]
);
root["webMode"] = WEB_MODE_NORMAL;
root["app_name"] = APP_NAME;
root["app_version"] = APP_VERSION;
root["app_build"] = buildTime();
#if defined(APP_REVISION)
root["app_revision"] = APP_REVISION;
#endif
root["manufacturer"] = MANUFACTURER;
root["chipid"] = String(chipid);
root["mac"] = WiFi.macAddress();
root["bssid"] = String(bssid_str);
root["channel"] = WiFi.channel();
root["device"] = DEVICE;
root["hostname"] = getSetting("hostname");
root["desc"] = getSetting("desc");
root["network"] = getNetwork();
root["deviceip"] = getIP();
root["sketch_size"] = ESP.getSketchSize();
root["free_size"] = ESP.getFreeSketchSpace();
root["sdk"] = ESP.getSdkVersion();
root["core"] = getCoreVersion();
_wsUpdate(root);
root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt();
root["webPort"] = getSetting("webPort", WEB_PORT).toInt();
root["wsAuth"] = getSetting("wsAuth", WS_AUTHENTICATION).toInt() == 1;
#if TERMINAL_SUPPORT
root["cmdVisible"] = 1;
#endif
root["hbMode"] = getSetting("hbMode", HEARTBEAT_MODE).toInt();
root["hbInterval"] = getSetting("hbInterval", HEARTBEAT_INTERVAL).toInt();
wsSend(root);
return;
} }
}
void _wsStart(uint32_t client_id) {
for (unsigned char i = 0; i < _ws_on_send_callbacks.size(); i++) {
wsSend(client_id, _ws_on_send_callbacks[i]);
for (auto& callback : _ws_on_send_callbacks) {
callback(root);
} }
wsSend(client_id, root);
} }
void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
@ -410,12 +470,8 @@ void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTy
} }
void _wsLoop() { void _wsLoop() {
static unsigned long last = 0;
if (!wsConnected()) return; if (!wsConnected()) return;
if (millis() - last > WS_UPDATE_INTERVAL) {
last = millis();
wsSend(_wsUpdate);
}
_wsDoUpdate();
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -447,10 +503,8 @@ void wsSend(ws_on_send_callback_f callback) {
DynamicJsonBuffer jsonBuffer; DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject(); JsonObject& root = jsonBuffer.createObject();
callback(root); callback(root);
String output;
root.printTo(output);
jsonBuffer.clear();
_ws.textAll((char *) output.c_str());
wsSend(root);
} }
} }
@ -469,13 +523,20 @@ void wsSend_P(PGM_P payload) {
} }
void wsSend(uint32_t client_id, ws_on_send_callback_f callback) { void wsSend(uint32_t client_id, ws_on_send_callback_f callback) {
AsyncWebSocketClient* client = _ws.client(client_id);
if (client == nullptr) return;
DynamicJsonBuffer jsonBuffer; DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject(); JsonObject& root = jsonBuffer.createObject();
callback(root); callback(root);
String output;
root.printTo(output);
jsonBuffer.clear();
_ws.text(client_id, (char *) output.c_str());
size_t len = root.measureLength();
AsyncWebSocketMessageBuffer* buffer = _ws.makeBuffer(len);
if (buffer) {
root.printTo(reinterpret_cast<char*>(buffer->get()), len + 1);
client->text(buffer);
}
} }
void wsSend(uint32_t client_id, const char * payload) { void wsSend(uint32_t client_id, const char * payload) {


+ 64
- 40
code/html/custom.js View File

@ -770,12 +770,13 @@ function createMagnitudeList(data, container, template_name) {
if (current > 0) { return; } if (current > 0) { return; }
var template = $("#" + template_name + " .pure-g")[0]; var template = $("#" + template_name + " .pure-g")[0];
for (var i in data) {
var magnitude = data[i];
var size = data.size;
for (var i=0; i<size; ++i) {
var line = $(template).clone(); var line = $(template).clone();
$("label", line).html(magnitudeType(magnitude.type) + " #" + parseInt(magnitude.index, 10));
$("div.hint", line).html(magnitude.name);
$("input", line).attr("tabindex", 40 + i).val(magnitude.idx);
$("label", line).html(magnitudeType(data.type[i]) + " #" + parseInt(data.index[i], 10));
$("div.hint", line).html(data.name[i]);
$("input", line).attr("tabindex", 40 + i).val(data.idx[i]);
line.appendTo("#" + container); line.appendTo("#" + container);
} }
@ -937,24 +938,34 @@ function createCheckboxes() {
function initRelayConfig(data) { function initRelayConfig(data) {
var current = $("#relayConfig > div").length / 6; // there are 6 divs per each relay
var current = $("#relayConfig > legend").length; // there is a legend per relay
if (current > 0) { return; }
var size = data.size;
var start = data.start;
var template = $("#relayConfigTemplate").children(); var template = $("#relayConfigTemplate").children();
for (var i in data) {
var relay = data[i];
if (current > relay.id) continue;
for (var i=start; i<size; ++i) {
var line = $(template).clone(); var line = $(template).clone();
$("span.gpio", line).html(relay.gpio);
$("span.id", line).html(relay.id);
$("select[name='relayBoot']", line).val(relay.boot);
$("select[name='relayPulse']", line).val(relay.pulse);
$("input[name='relayTime']", line).val(relay.pulse_ms);
$("input[name='mqttGroup']", line).val(relay.group);
$("select[name='mqttGroupSync']", line).val(relay.group_sync);
$("select[name='relayOnDisc']", line).val(relay.on_disc);
line.appendTo("#relayConfig");
$("span.id", line).html(i);
$("span.gpio", line).html(data.gpio[i]);
$("select[name='relayBoot']", line).val(data.boot[i]);
$("select[name='relayPulse']", line).val(data.pulse[i]);
$("input[name='relayTime']", line).val(data.pulse_time[i]);
if ("group" in data) {
$("input[name='mqttGroup']", line).val(data.group[i]);
}
if ("group_sync" in data) {
$("input[name='mqttGroupSync']", line).val(data.group_sync[i]);
}
if ("on_disc" in data) {
$("input[name='relayOnDisc']", line).val(data.on_disc[i]);
}
line.appendTo("#relayConfig");
} }
} }
@ -966,17 +977,19 @@ function initRelayConfig(data) {
<!-- removeIf(!sensor)--> <!-- removeIf(!sensor)-->
function initMagnitudes(data) { function initMagnitudes(data) {
// check if already initialized
// check if already initialized (each magnitude is inside div.pure-g)
var done = $("#magnitudes > div").length; var done = $("#magnitudes > div").length;
if (done > 0) { return; } if (done > 0) { return; }
var size = data.size;
// add templates // add templates
var template = $("#magnitudeTemplate").children(); var template = $("#magnitudeTemplate").children();
for (var i in data) {
var magnitude = data[i];
for (var i=0; i<size; ++i) {
var line = $(template).clone(); var line = $(template).clone();
$("label", line).html(magnitudeType(magnitude.type) + " #" + parseInt(magnitude.index, 10));
$("div.hint", line).html(magnitude.description);
$("label", line).html(magnitudeType(data.type[i]) + " #" + parseInt(data.index[i], 10));
$("div.hint", line).html(data.description[i]);
$("input", line).attr("data", i); $("input", line).attr("data", i);
line.appendTo("#magnitudes"); line.appendTo("#magnitudes");
} }
@ -1205,11 +1218,18 @@ function processData(data) {
} }
if ("rfb" === key) { if ("rfb" === key) {
var nodes = data.rfb;
for (i in nodes) {
var node = nodes[i];
$("input[name='rfbcode'][data-id='" + node.id + "'][data-status='" + node.status + "']").val(node.data);
var rfb = data.rfb;
var size = data.size;
var start = data.start;
var on = rfb["on"];
var off = rfb["off"];
for (var i=start; i<start+size; ++i) {
$("input[name='rfbcode'][data-id='" + i + "'][data-status='1']").val(on[i]);
$("input[name='rfbcode'][data-id='" + i + "'][data-status='0']").val(off[i]);
} }
return; return;
} }
<!-- endRemoveIf(!rfbridge)--> <!-- endRemoveIf(!rfbridge)-->
@ -1324,15 +1344,13 @@ function processData(data) {
if ("magnitudes" === key) { if ("magnitudes" === key) {
initMagnitudes(value); initMagnitudes(value);
for (i in value) {
var magnitude = value[i];
var error = magnitude.error || 0;
for (var i=0; i<value.size; ++i) {
var error = value.error[i] || 0;
var text = (0 === error) ? var text = (0 === error) ?
magnitude.value + magnitude.units :
value.value[i] + value.units[i] :
magnitudeError(error); magnitudeError(error);
var element = $("input[name='magnitude'][data='" + i + "']"); var element = $("input[name='magnitude'][data='" + i + "']");
element.val(text); element.val(text);
$("div.hint", element.parent().parent()).html(magnitude.description);
} }
return; return;
} }
@ -1385,13 +1403,13 @@ function processData(data) {
return; return;
} }
if ("schedule" === key) {
for (i in value) {
var schedule = value[i];
var sch_line = addSchedule({ data: {schType: schedule["schType"] }});
if ("schedules" === key) {
for (var i=0; i<value.size; ++i) {
var sch_line = addSchedule({ data: {schType: value.schType[i] }});
Object.keys(schedule).forEach(function(key) {
var sch_value = schedule[key];
Object.keys(value).forEach(function(key) {
if ("size" == key) return;
var sch_value = value[key][i];
$("input[name='" + key + "']", sch_line).val(sch_value); $("input[name='" + key + "']", sch_line).val(sch_value);
$("select[name='" + key + "']", sch_line).prop("value", sch_value); $("select[name='" + key + "']", sch_line).prop("value", sch_value);
$("input[type='checkbox'][name='" + key + "']", sch_line).prop("checked", sch_value); $("input[type='checkbox'][name='" + key + "']", sch_line).prop("checked", sch_value);
@ -1466,7 +1484,13 @@ function processData(data) {
// Web log // Web log
if ("weblog" === key) { if ("weblog" === key) {
$("#weblog").append(new Text(value));
websock.send("{}");
if (value.prefix) {
$("#weblog").append(new Text(value.prefix));
}
$("#weblog").append(new Text(value.message));
$("#weblog").scrollTop($("#weblog")[0].scrollHeight - $("#weblog").height()); $("#weblog").scrollTop($("#weblog")[0].scrollHeight - $("#weblog").height());
return; return;
} }


+ 5
- 0
code/html/index.html View File

@ -686,6 +686,11 @@
<legend>General</legend> <legend>General</legend>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Device hostname can be configured on the GENERAL tab.</div>
</div>
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Scan networks</label> <label class="pure-u-1 pure-u-lg-1-4">Scan networks</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="wifiScan" tabindex="1" /></div> <div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="wifiScan" tabindex="1" /></div>


+ 83
- 9
code/platformio.ini View File

@ -2208,6 +2208,30 @@ upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags} upload_flags = ${common.upload_flags}
extra_scripts = ${common.extra_scripts} extra_scripts = ${common.extra_scripts}
[env:oukitel-p1]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DOUKITEL_P1
monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts}
[env:oukitel-p1-ota]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DOUKITEL_P1
upload_speed = ${common.upload_speed}
upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags}
extra_scripts = ${common.extra_scripts}
[env:tonbux-xsssa01] [env:tonbux-xsssa01]
platform = ${common.platform} platform = ${common.platform}
framework = ${common.framework} framework = ${common.framework}
@ -2517,6 +2541,17 @@ build_flags = ${common.build_flags_1m0m} -DGENERIC_ESP01S_RELAY_V40
monitor_speed = ${common.monitor_speed} monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts} extra_scripts = ${common.extra_scripts}
[env:generic-esp01s-relay-40-inv]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DGENERIC_ESP01S_RELAY_V40 -DRELAY1_TYPE=1
monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts}
[env:generic-esp01s-relay-40-ota] [env:generic-esp01s-relay-40-ota]
platform = ${common.platform} platform = ${common.platform}
framework = ${common.framework} framework = ${common.framework}
@ -2530,6 +2565,19 @@ upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags} upload_flags = ${common.upload_flags}
extra_scripts = ${common.extra_scripts} extra_scripts = ${common.extra_scripts}
[env:generic-esp01s-relay-40-inv-ota]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DGENERIC_ESP01S_RELAY_V40 -DRELAY1_TYPE=1
upload_speed = ${common.upload_speed}
upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags}
extra_scripts = ${common.extra_scripts}
[env:generic-esp01s-rgbled-10] [env:generic-esp01s-rgbled-10]
platform = ${common.platform} platform = ${common.platform}
framework = ${common.framework} framework = ${common.framework}
@ -2724,52 +2772,52 @@ upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags} upload_flags = ${common.upload_flags}
extra_scripts = ${common.extra_scripts} extra_scripts = ${common.extra_scripts}
[env:blitzwolf-bwshp2]
[env:blitzwolf-bwshpx]
platform = ${common.platform} platform = ${common.platform}
framework = ${common.framework} framework = ${common.framework}
board = ${common.board_1m} board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode} board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore} lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DBLITZWOLF_BWSHP2
build_flags = ${common.build_flags_1m0m} -DBLITZWOLF_BWSHPX
upload_speed = ${common.upload_speed} upload_speed = ${common.upload_speed}
monitor_speed = ${common.monitor_speed} monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts} extra_scripts = ${common.extra_scripts}
[env:blitzwolf-bwshp2-ota]
[env:blitzwolf-bwshpx-ota]
platform = ${common.platform} platform = ${common.platform}
framework = ${common.framework} framework = ${common.framework}
board = ${common.board_1m} board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode} board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore} lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DBLITZWOLF_BWSHP2
build_flags = ${common.build_flags_1m0m} -DBLITZWOLF_BWSHPX
upload_speed = ${common.upload_speed} upload_speed = ${common.upload_speed}
monitor_speed = ${common.monitor_speed} monitor_speed = ${common.monitor_speed}
upload_port = ${common.upload_port} upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags} upload_flags = ${common.upload_flags}
extra_scripts = ${common.extra_scripts} extra_scripts = ${common.extra_scripts}
[env:blitzwolf-bwshp2-v23]
[env:blitzwolf-bwshpx-v23]
platform = ${common.platform} platform = ${common.platform}
framework = ${common.framework} framework = ${common.framework}
board = ${common.board_1m} board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode} board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore} lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DBLITZWOLF_BWSHP2_V23
build_flags = ${common.build_flags_1m0m} -DBLITZWOLF_BWSHPX_V23
upload_speed = ${common.upload_speed} upload_speed = ${common.upload_speed}
monitor_speed = ${common.monitor_speed} monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts} extra_scripts = ${common.extra_scripts}
[env:blitzwolf-bwshp2-v23-ota]
[env:blitzwolf-bwshpx-v23-ota]
platform = ${common.platform} platform = ${common.platform}
framework = ${common.framework} framework = ${common.framework}
board = ${common.board_1m} board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode} board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore} lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DBLITZWOLF_BWSHP2_V23
build_flags = ${common.build_flags_1m0m} -DBLITZWOLF_BWSHPX_V23
upload_speed = ${common.upload_speed} upload_speed = ${common.upload_speed}
monitor_speed = ${common.monitor_speed} monitor_speed = ${common.monitor_speed}
upload_port = ${common.upload_port} upload_port = ${common.upload_port}
@ -2802,6 +2850,32 @@ upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags} upload_flags = ${common.upload_flags}
extra_scripts = ${common.extra_scripts} extra_scripts = ${common.extra_scripts}
[env:gosund-ws1]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DGOSUND_WS1
upload_speed = ${common.upload_speed}
monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts}
[env:gosund-ws1-ota]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DGOSUND_WS1
upload_speed = ${common.upload_speed}
monitor_speed = ${common.monitor_speed}
upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags}
extra_scripts = ${common.extra_scripts}
[env:gosund-sp1-v23] [env:gosund-sp1-v23]
platform = ${common.platform} platform = ${common.platform}
framework = ${common.framework} framework = ${common.framework}
@ -3103,4 +3177,4 @@ upload_speed = ${common.upload_speed}
upload_port = ${common.upload_port} upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags} upload_flags = ${common.upload_flags}
monitor_speed = ${common.monitor_speed} monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts}
extra_scripts = ${common.extra_scripts}

Loading…
Cancel
Save