Browse Source

merge dev branch

fastled
Pawel Raszewski 7 years ago
parent
commit
548f6edd44
40 changed files with 5005 additions and 4757 deletions
  1. +1
    -0
      code/.gitignore
  2. +51
    -0
      code/core_version.py
  3. +15
    -0
      code/espurna/alexa.ino
  4. +15
    -4
      code/espurna/analog.ino
  5. +181
    -0
      code/espurna/api.ino
  6. +2
    -4
      code/espurna/button.ino
  7. +5
    -1
      code/espurna/config/all.h
  8. +1
    -0
      code/espurna/config/arduino.h
  9. +22
    -8
      code/espurna/config/general.h
  10. +42
    -30
      code/espurna/config/hardware.h
  11. +15
    -5
      code/espurna/config/prototypes.h
  12. +1
    -1
      code/espurna/config/sensors.h
  13. +1
    -1
      code/espurna/config/version.h
  14. +15
    -4
      code/espurna/counter.ino
  15. BIN
      code/espurna/data/index.html.gz
  16. +22
    -20
      code/espurna/debug.ino
  17. +4
    -3
      code/espurna/dht.ino
  18. +21
    -0
      code/espurna/domoticz.ino
  19. +1
    -1
      code/espurna/ds18b20.ino
  20. +45
    -26
      code/espurna/espurna.ino
  21. +11
    -3
      code/espurna/hardware.ino
  22. +18
    -0
      code/espurna/homeassitant.ino
  23. +37
    -18
      code/espurna/influxdb.ino
  24. +12
    -21
      code/espurna/light.ino
  25. +0
    -1
      code/espurna/light_ir.ino
  26. +18
    -0
      code/espurna/llmnr.ino
  27. +21
    -8
      code/espurna/mqtt.ino
  28. +1
    -2
      code/espurna/ota.ino
  29. +9
    -9
      code/espurna/power.ino
  30. +1
    -1
      code/espurna/relay.ino
  31. +37
    -39
      code/espurna/settings.ino
  32. +3459
    -3470
      code/espurna/static/index.html.gz.h
  33. +66
    -11
      code/espurna/utils.ino
  34. +26
    -997
      code/espurna/web.ino
  35. +7
    -1
      code/espurna/wifi.ino
  36. +18
    -12
      code/espurna/ws.h
  37. +755
    -0
      code/espurna/ws.ino
  38. +1
    -35
      code/html/custom.js
  39. +17
    -14
      code/html/index.html
  40. +31
    -7
      code/platformio.ini

+ 1
- 0
code/.gitignore View File

@ -3,3 +3,4 @@
.pioenvs .pioenvs
.piolibdeps .piolibdeps
.vscode/c_cpp_properties.json .vscode/c_cpp_properties.json
core_version.h

+ 51
- 0
code/core_version.py View File

@ -0,0 +1,51 @@
#!/bin/python
import json
import commands
import subprocess
import os
import sys
def core_version(env):
# Get the core folder
fwdir = env["FRAMEWORK_ARDUINOESP8266_DIR"]
# Get the core version
with open(fwdir + '/package.json') as data_file:
data = json.load(data_file)
core_version = data["version"].upper().replace(".", "_").replace("-", "_")
print "CORE VERSION: %s" % core_version
# Get git version
pr = subprocess.Popen(
"git --git-dir .git rev-parse --short=8 HEAD 2>/dev/null || echo ffffffff",
cwd = fwdir,
shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE )
(out, error) = pr.communicate()
git_version = str(out).replace('\n', "")
print "GIT VERSION: %s" % git_version
#env["BUILD_FLAGS"][0] += str(" -DARDUINO_ESP8266_RELEASE=" + core_version)
#env["BUILD_FLAGS"][0] += str(" -DARDUINO_ESP8266_RELEASE_" + core_version)
#env["BUILD_FLAGS"][0] += str(" -DARDUINO_ESP8266_GIT_VER=" + git_version)
with open('espurna/config/core_version.h', 'w') as the_file:
the_file.write('#define ARDUINO_ESP8266_RELEASE "%s"\n' % core_version)
the_file.write('#define ARDUINO_ESP8266_RELEASE_%s\n' % core_version)
the_file.write('#define ARDUINO_ESP8266_GIT_VER "%s"\n' % git_version)
#env.Append(
# CFLAGS = [
# str("-DARDUINO_ESP8266_RELEASE=" + core_version),
# str("-DARDUINO_ESP8266_RELEASE_" + core_version),
# str("-DARDUINO_ESP8266_GIT_VER=" + git_version)
# ]
#)
#print " -DARDUINO_ESP8266_RELEASE=" + core_version +
# " -DARDUINO_ESP8266_RELEASE_" + core_version +
# " -DARDUINO_ESP8266_GIT_VER=" + git_version
Import('env')
core_version(env)

+ 15
- 0
code/espurna/alexa.ino View File

@ -20,6 +20,15 @@ bool _alexa_change = false;
unsigned int _alexa_device_id = 0; unsigned int _alexa_device_id = 0;
bool _alexa_state = false; bool _alexa_state = false;
#if WEB_SUPPORT
void _alexaWSSend(JsonObject& root) {
root["alexaVisible"] = 1;
root["alexaEnabled"] = getSetting("alexaEnabled", ALEXA_ENABLED).toInt() == 1;
}
#endif
// -----------------------------------------------------------------------------
void alexaConfigure() { void alexaConfigure() {
alexa.enable(getSetting("alexaEnabled", ALEXA_ENABLED).toInt() == 1); alexa.enable(getSetting("alexaEnabled", ALEXA_ENABLED).toInt() == 1);
} }
@ -29,8 +38,14 @@ void alexaSetup() {
// Backwards compatibility // Backwards compatibility
moveSetting("fauxmoEnabled", "alexaEnabled"); moveSetting("fauxmoEnabled", "alexaEnabled");
// Load & cache settings
alexaConfigure(); alexaConfigure();
#if WEB_SUPPORT
// Websockets
wsRegister(_alexaWSSend);
#endif
unsigned int relays = relayCount(); unsigned int relays = relayCount();
String hostname = getSetting("hostname"); String hostname = getSetting("hostname");
if (relays == 1) { if (relays == 1) {


+ 15
- 4
code/espurna/analog.ino View File

@ -12,6 +12,13 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
// ANALOG // ANALOG
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void _analogWSSend(JsonObject& root) {
root["analogVisible"] = 1;
root["analogValue"] = getAnalog();
}
// -----------------------------------------------------------------------------
unsigned int getAnalog() { unsigned int getAnalog() {
return analogRead(ANALOG_PIN); return analogRead(ANALOG_PIN);
} }
@ -21,9 +28,15 @@ void analogSetup() {
pinMode(ANALOG_PIN, INPUT); pinMode(ANALOG_PIN, INPUT);
#if WEB_SUPPORT #if WEB_SUPPORT
// Websocket register
wsRegister(_analogWSSend);
// API register
apiRegister(ANALOG_TOPIC, ANALOG_TOPIC, [](char * buffer, size_t len) { apiRegister(ANALOG_TOPIC, ANALOG_TOPIC, [](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), getAnalog()); snprintf_P(buffer, len, PSTR("%d"), getAnalog());
}); });
#endif #endif
DEBUG_MSG_P(PSTR("[ANALOG] Monitoring analog values\n")); DEBUG_MSG_P(PSTR("[ANALOG] Monitoring analog values\n"));
@ -51,14 +64,12 @@ void analogLoop() {
// Send to InfluxDB // Send to InfluxDB
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
influxDBSend(MQTT_TOPIC_ANALOG, analog);
idbSend(MQTT_TOPIC_ANALOG, analog);
#endif #endif
// Update websocket clients // Update websocket clients
#if WEB_SUPPORT #if WEB_SUPPORT
char buffer[100];
snprintf_P(buffer, sizeof(buffer), PSTR("{\"analogVisible\": 1, \"analogValue\": %d}"), analog);
wsSend(buffer);
wsSend(_analogWSSend);
#endif #endif
} }


+ 181
- 0
code/espurna/api.ino View File

@ -0,0 +1,181 @@
/*
API MODULE
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if WEB_SUPPORT
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
#include <vector>
typedef struct {
char * url;
char * key;
api_get_callback_f getFn = NULL;
api_put_callback_f putFn = NULL;
} web_api_t;
std::vector<web_api_t> _apis;
// -----------------------------------------------------------------------------
// API
// -----------------------------------------------------------------------------
bool _authAPI(AsyncWebServerRequest *request) {
if (getSetting("apiEnabled", API_ENABLED).toInt() == 0) {
DEBUG_MSG_P(PSTR("[WEBSERVER] HTTP API is not enabled\n"));
request->send(403);
return false;
}
if (!request->hasParam("apikey", (request->method() == HTTP_PUT))) {
DEBUG_MSG_P(PSTR("[WEBSERVER] Missing apikey parameter\n"));
request->send(403);
return false;
}
AsyncWebParameter* p = request->getParam("apikey", (request->method() == HTTP_PUT));
if (!p->value().equals(getSetting("apiKey"))) {
DEBUG_MSG_P(PSTR("[WEBSERVER] Wrong apikey parameter\n"));
request->send(403);
return false;
}
return true;
}
bool _asJson(AsyncWebServerRequest *request) {
bool asJson = false;
if (request->hasHeader("Accept")) {
AsyncWebHeader* h = request->getHeader("Accept");
asJson = h->value().equals("application/json");
}
return asJson;
}
ArRequestHandlerFunction _bindAPI(unsigned int apiID) {
return [apiID](AsyncWebServerRequest *request) {
webLog(request);
if (!_authAPI(request)) return;
web_api_t api = _apis[apiID];
// Check if its a PUT
if (api.putFn != NULL) {
if (request->hasParam("value", request->method() == HTTP_PUT)) {
AsyncWebParameter* p = request->getParam("value", request->method() == HTTP_PUT);
(api.putFn)((p->value()).c_str());
}
}
// Get response from callback
char value[API_BUFFER_SIZE];
(api.getFn)(value, API_BUFFER_SIZE);
// The response will be a 404 NOT FOUND if the resource is not available
if (!value) {
DEBUG_MSG_P(PSTR("[API] Sending 404 response\n"));
request->send(404);
return;
}
DEBUG_MSG_P(PSTR("[API] Sending response '%s'\n"), value);
// Format response according to the Accept header
if (_asJson(request)) {
char buffer[64];
snprintf_P(buffer, sizeof(buffer), PSTR("{ \"%s\": %s }"), api.key, value);
request->send(200, "application/json", buffer);
} else {
request->send(200, "text/plain", value);
}
};
}
void _onAPIs(AsyncWebServerRequest *request) {
webLog(request);
if (!_authAPI(request)) return;
bool asJson = _asJson(request);
String output;
if (asJson) {
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
for (unsigned int i=0; i < _apis.size(); i++) {
root[_apis[i].key] = _apis[i].url;
}
root.printTo(output);
request->send(200, "application/json", output);
} else {
for (unsigned int i=0; i < _apis.size(); i++) {
output += _apis[i].key + String(" -> ") + _apis[i].url + String("\n");
}
request->send(200, "text/plain", output);
}
}
void _onRPC(AsyncWebServerRequest *request) {
webLog(request);
if (!_authAPI(request)) return;
//bool asJson = _asJson(request);
int response = 404;
if (request->hasParam("action")) {
AsyncWebParameter* p = request->getParam("action");
String action = p->value();
DEBUG_MSG_P(PSTR("[RPC] Action: %s\n"), action.c_str());
if (action.equals("reset")) {
response = 200;
deferredReset(100, CUSTOM_RESET_RPC);
}
}
request->send(response);
}
// -----------------------------------------------------------------------------
void apiRegister(const char * url, const char * key, api_get_callback_f getFn, api_put_callback_f putFn) {
// Store it
web_api_t api;
char buffer[40];
snprintf_P(buffer, sizeof(buffer), PSTR("/api/%s"), url);
api.url = strdup(buffer);
api.key = strdup(key);
api.getFn = getFn;
api.putFn = putFn;
_apis.push_back(api);
// Bind call
unsigned int methods = HTTP_GET;
if (putFn != NULL) methods += HTTP_PUT;
webServer()->on(buffer, methods, _bindAPI(_apis.size() - 1));
}
void apiSetup() {
webServer()->on("/apis", HTTP_GET, _onAPIs);
webServer()->on("/rpc", HTTP_GET, _onRPC);
}
#endif // WEB_SUPPORT

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

@ -104,15 +104,13 @@ void buttonEvent(unsigned int id, unsigned char event) {
} }
if (action == BUTTON_MODE_AP) createAP(); if (action == BUTTON_MODE_AP) createAP();
if (action == BUTTON_MODE_RESET) { if (action == BUTTON_MODE_RESET) {
customReset(CUSTOM_RESET_HARDWARE);
ESP.restart();
deferredReset(100, CUSTOM_RESET_HARDWARE);
} }
if (action == BUTTON_MODE_PULSE) relayPulseToggle(); if (action == BUTTON_MODE_PULSE) relayPulseToggle();
if (action == BUTTON_MODE_FACTORY) { if (action == BUTTON_MODE_FACTORY) {
DEBUG_MSG_P(PSTR("\n\nFACTORY RESET\n\n")); DEBUG_MSG_P(PSTR("\n\nFACTORY RESET\n\n"));
settingsFactoryReset(); settingsFactoryReset();
customReset(CUSTOM_RESET_FACTORY);
ESP.restart();
deferredReset(100, CUSTOM_RESET_FACTORY);
} }
} }


+ 5
- 1
code/espurna/config/all.h View File

@ -5,9 +5,13 @@
#include "sensors.h" #include "sensors.h"
#include "prototypes.h" #include "prototypes.h"
#ifdef USE_CORE_VERSION_H
#include "core_version.h"
#endif
/* /*
If you want to modify the stock configuration but you don't want to touch If you want to modify the stock configuration but you don't want to touch
the repo files you can either define USE_CUSTOM_H or remove the
the repo files you can either define USE_CUSTOM_H or remove the
"#ifdef USE_CUSTOM_H" & "#endif" lines and add a "custom.h" "#ifdef USE_CUSTOM_H" & "#endif" lines and add a "custom.h"
file to this same folder. file to this same folder.
Check https://bitbucket.org/xoseperez/espurna/issues/104/general_customh Check https://bitbucket.org/xoseperez/espurna/issues/104/general_customh


+ 1
- 0
code/espurna/config/arduino.h View File

@ -69,6 +69,7 @@
//#define I2C_SUPPORT 1 //#define I2C_SUPPORT 1
//#define INFLUXDB_SUPPORT 0 //#define INFLUXDB_SUPPORT 0
//#define IR_SUPPORT 1 //#define IR_SUPPORT 1
//#define LLMNR_SUPPORT 1
//#define MDNS_SUPPORT 0 //#define MDNS_SUPPORT 0
//#define NOFUSS_SUPPORT 1 //#define NOFUSS_SUPPORT 1
//#define NTP_SUPPORT 0 //#define NTP_SUPPORT 0


+ 22
- 8
code/espurna/config/general.h View File

@ -9,6 +9,10 @@
#define ADMIN_PASS "fibonacci" // Default password (WEB, OTA, WIFI) #define ADMIN_PASS "fibonacci" // Default password (WEB, OTA, WIFI)
#define DEVICE_NAME MANUFACTURER "_" DEVICE // Concatenate both to get a unique device name #define DEVICE_NAME MANUFACTURER "_" DEVICE // Concatenate both to get a unique device name
#define LOOP_DELAY_TIME 10 // Delay for this millis in the main loop [0-250]
#define ARRAYINIT(type, name, ...) \
type name[] = {__VA_ARGS__};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// TELNET // TELNET
@ -59,10 +63,9 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// General debug options and macros // General debug options and macros
#define DEBUG_MESSAGE_MAX_LENGTH 80
#define DEBUG_FORMAT_MAX_LENGTH 80
#define DEBUG_SUPPORT DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT || DEBUG_TELNET_SUPPORT #define DEBUG_SUPPORT DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT || DEBUG_TELNET_SUPPORT
#if DEBUG_SUPPORT #if DEBUG_SUPPORT
#define DEBUG_MSG(...) debugSend(__VA_ARGS__) #define DEBUG_MSG(...) debugSend(__VA_ARGS__)
#define DEBUG_MSG_P(...) debugSend_P(__VA_ARGS__) #define DEBUG_MSG_P(...) debugSend_P(__VA_ARGS__)
@ -82,11 +85,15 @@
#endif #endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// CRASH
// SYSTEM CHECK
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#define CRASH_SAFE_TIME 60000 // The system is considered stable after these many millis
#define CRASH_COUNT_MAX 5 // After this many crashes on boot
#ifndef SYSTEM_CHECK_ENABLED
#define SYSTEM_CHECK_ENABLED 1 // Enable crash check by default
#endif
#define SYSTEM_CHECK_TIME 60000 // The system is considered stable after these many millis
#define SYSTEM_CHECK_MAX 5 // After this many crashes on boot
// the system is flagged as unstable // the system is flagged as unstable
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -263,6 +270,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define WIFI_RECONNECT_INTERVAL 180000 // If could not connect to WIFI, retry after this time in ms #define WIFI_RECONNECT_INTERVAL 180000 // If could not connect to WIFI, retry after this time in ms
#define WIFI_MAX_NETWORKS 5 // Max number of WIFI connection configurations #define WIFI_MAX_NETWORKS 5 // Max number of WIFI connection configurations
#define WIFI_AP_MODE AP_MODE_ALONE #define WIFI_AP_MODE AP_MODE_ALONE
#define WIFI_SLEEP_ENABLED 1 // Enable WiFi light sleep
// Optional hardcoded configuration (up to 2 different networks) // Optional hardcoded configuration (up to 2 different networks)
//#define WIFI1_SSID "..." //#define WIFI1_SSID "..."
@ -317,13 +325,17 @@ PROGMEM const char* const custom_reset_string[] = {
#define API_REAL_TIME_VALUES 0 // Show filtered/median values by default (0 => median, 1 => real time) #define API_REAL_TIME_VALUES 0 // Show filtered/median values by default (0 => median, 1 => real time)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// MDNS
// MDNS & LLMNR
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#ifndef MDNS_SUPPORT #ifndef MDNS_SUPPORT
#define MDNS_SUPPORT 1 // Publish services using mDNS by default #define MDNS_SUPPORT 1 // Publish services using mDNS by default
#endif #endif
#ifndef LLMNR_SUPPORT
#define LLMNR_SUPPORT 0 // Publish device using LLMNR protocol by default - requires 2.4.0
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// SPIFFS // SPIFFS
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -475,7 +487,7 @@ PROGMEM const char* const custom_reset_string[] = {
// Available light providers (do not change) // Available light providers (do not change)
#define LIGHT_PROVIDER_NONE 0 #define LIGHT_PROVIDER_NONE 0
#define LIGHT_PROVIDER_MY9192 1 // works with MY9231 also (Sonoff B1)
#define LIGHT_PROVIDER_MY92XX 1 // works with MY9291 and MY9231
#define LIGHT_PROVIDER_DIMMER 2 #define LIGHT_PROVIDER_DIMMER 2
// LIGHT_PROVIDER_DIMMER can have from 1 to 5 different channels. // LIGHT_PROVIDER_DIMMER can have from 1 to 5 different channels.
@ -500,7 +512,7 @@ PROGMEM const char* const custom_reset_string[] = {
#ifndef LIGHT_MAX_PWM #ifndef LIGHT_MAX_PWM
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX
#define LIGHT_MAX_PWM 255 #define LIGHT_MAX_PWM 255
#endif #endif
@ -664,6 +676,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define DOMOTICZ_ENABLED 0 // Disable domoticz by default #define DOMOTICZ_ENABLED 0 // Disable domoticz by default
#define DOMOTICZ_IN_TOPIC "domoticz/in" // Default subscription topic #define DOMOTICZ_IN_TOPIC "domoticz/in" // Default subscription topic
#define DOMOTICZ_OUT_TOPIC "domoticz/out" // Default publication topic #define DOMOTICZ_OUT_TOPIC "domoticz/out" // Default publication topic
#define DOMOTICZ_SKIP_TIME 2 // Avoid recursion skipping messages to same IDX within 2 seconds
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// HOME ASSISTANT // HOME ASSISTANT
@ -673,6 +686,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define HOMEASSISTANT_SUPPORT 1 // Build with home assistant support #define HOMEASSISTANT_SUPPORT 1 // Build with home assistant support
#endif #endif
#define HOMEASSISTANT_ENABLED 0 // Integration not enabled by default
#define HOMEASSISTANT_PREFIX "homeassistant" // Default MQTT prefix #define HOMEASSISTANT_PREFIX "homeassistant" // Default MQTT prefix
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 42
- 30
code/espurna/config/hardware.h View File

@ -321,7 +321,6 @@
#define RELAY_PROVIDER RELAY_PROVIDER_DUAL #define RELAY_PROVIDER RELAY_PROVIDER_DUAL
#define DUMMY_RELAY_COUNT 2 #define DUMMY_RELAY_COUNT 2
#define DEBUG_SERIAL_SUPPORT 0 #define DEBUG_SERIAL_SUPPORT 0
#define TERMINAL_SUPPORT 0
// Buttons // Buttons
#define BUTTON3_RELAY 1 #define BUTTON3_RELAY 1
@ -464,7 +463,7 @@
#define LED1_PIN 13 #define LED1_PIN 13
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels
// Light
#define LIGHT_CHANNELS 1 #define LIGHT_CHANNELS 1
#define LIGHT_CH1_PIN 12 #define LIGHT_CH1_PIN 12
#define LIGHT_CH1_INVERSE 0 #define LIGHT_CH1_INVERSE 0
@ -497,12 +496,17 @@
#define MANUFACTURER "ITEAD" #define MANUFACTURER "ITEAD"
#define DEVICE "SONOFF_B1" #define DEVICE "SONOFF_B1"
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY92XX
#define DUMMY_RELAY_COUNT 1 #define DUMMY_RELAY_COUNT 1
#define MY9291_DI_PIN 12
#define MY9291_DCKI_PIN 14
#define MY9291_COMMAND MY9291_COMMAND_DEFAULT
#define MY9291_CHANNELS 5
// Light
#define LIGHT_CHANNELS 5
#define MY92XX_MODEL MY92XX_MODEL_MY9231
#define MY92XX_CHIPS 2
#define MY92XX_DI_PIN 12
#define MY92XX_DCKI_PIN 14
#define MY92XX_COMMAND MY92XX_COMMAND_DEFAULT
#define MY92XX_MAPPING 4, 3, 5, 0, 1
#elif defined(ITEAD_SONOFF_LED) #elif defined(ITEAD_SONOFF_LED)
@ -517,7 +521,7 @@
#define LED1_PIN 13 #define LED1_PIN 13
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels
// Light
#define LIGHT_CHANNELS 2 #define LIGHT_CHANNELS 2
#define LIGHT_CH1_PIN 12 // Cold white #define LIGHT_CH1_PIN 12 // Cold white
#define LIGHT_CH2_PIN 14 // Warm white #define LIGHT_CH2_PIN 14 // Warm white
@ -531,12 +535,12 @@
#define DEVICE "SONOFF_T1_1CH" #define DEVICE "SONOFF_T1_1CH"
// Buttons // Buttons
#define BUTTON1_PIN 9
#define BUTTON1_PIN 0
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH #define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_RELAY 1 #define BUTTON1_RELAY 1
// Relays // Relays
#define RELAY1_PIN 5
#define RELAY1_PIN 12
#define RELAY1_TYPE RELAY_TYPE_NORMAL #define RELAY1_TYPE RELAY_TYPE_NORMAL
// LEDs // LEDs
@ -551,7 +555,7 @@
// Buttons // Buttons
#define BUTTON1_PIN 0 #define BUTTON1_PIN 0
#define BUTTON2_PIN 10
#define BUTTON2_PIN 9
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH #define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON2_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH #define BUTTON2_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
@ -561,7 +565,7 @@
// Relays // Relays
#define RELAY1_PIN 12 #define RELAY1_PIN 12
#define RELAY2_PIN 4
#define RELAY2_PIN 5
#define RELAY1_TYPE RELAY_TYPE_NORMAL #define RELAY1_TYPE RELAY_TYPE_NORMAL
#define RELAY2_TYPE RELAY_TYPE_NORMAL #define RELAY2_TYPE RELAY_TYPE_NORMAL
@ -700,12 +704,17 @@
#define MANUFACTURER "AITHINKER" #define MANUFACTURER "AITHINKER"
#define DEVICE "AI_LIGHT" #define DEVICE "AI_LIGHT"
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY92XX
#define DUMMY_RELAY_COUNT 1 #define DUMMY_RELAY_COUNT 1
#define MY9291_DI_PIN 13
#define MY9291_DCKI_PIN 15
#define MY9291_COMMAND MY9291_COMMAND_DEFAULT
#define MY9291_CHANNELS 4
// Light
#define LIGHT_CHANNELS 4
#define MY92XX_MODEL MY92XX_MODEL_MY9291
#define MY92XX_CHIPS 1
#define MY92XX_DI_PIN 13
#define MY92XX_DCKI_PIN 15
#define MY92XX_COMMAND MY92XX_COMMAND_DEFAULT
#define MY92XX_MAPPING 0, 1, 2, 3
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// LED Controller // LED Controller
@ -724,7 +733,7 @@
#define LED1_PIN 2 #define LED1_PIN 2
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels
// Light
#define LIGHT_CHANNELS 4 #define LIGHT_CHANNELS 4
#define LIGHT_CH1_PIN 14 // RED #define LIGHT_CH1_PIN 14 // RED
#define LIGHT_CH2_PIN 5 // GREEN #define LIGHT_CH2_PIN 5 // GREEN
@ -755,7 +764,7 @@
#define LED1_PIN 2 #define LED1_PIN 2
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels
// Light
#define LIGHT_CHANNELS 4 #define LIGHT_CHANNELS 4
#define LIGHT_CH1_PIN 5 // RED #define LIGHT_CH1_PIN 5 // RED
#define LIGHT_CH2_PIN 12 // GREEN #define LIGHT_CH2_PIN 12 // GREEN
@ -788,7 +797,7 @@
#define LED1_PIN 5 #define LED1_PIN 5
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels
// Light
#define LIGHT_CHANNELS 5 #define LIGHT_CHANNELS 5
#define LIGHT_CH1_PIN 15 // RED #define LIGHT_CH1_PIN 15 // RED
#define LIGHT_CH2_PIN 13 // GREEN #define LIGHT_CH2_PIN 13 // GREEN
@ -810,7 +819,7 @@
#define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER #define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER
#define DUMMY_RELAY_COUNT 1 #define DUMMY_RELAY_COUNT 1
// Channels
// Light
#define LIGHT_CHANNELS 4 #define LIGHT_CHANNELS 4
#define LIGHT_CH1_PIN 12 // RED #define LIGHT_CH1_PIN 12 // RED
#define LIGHT_CH2_PIN 14 // GREEN #define LIGHT_CH2_PIN 14 // GREEN
@ -1047,7 +1056,7 @@
#define LED1_PIN 5 #define LED1_PIN 5
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels
// Light
#define LIGHT_CHANNELS 2 #define LIGHT_CHANNELS 2
#define LIGHT_CH1_PIN 0 #define LIGHT_CH1_PIN 0
#define LIGHT_CH2_PIN 2 #define LIGHT_CH2_PIN 2
@ -1067,7 +1076,7 @@
#define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER #define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER
#define DUMMY_RELAY_COUNT 1 #define DUMMY_RELAY_COUNT 1
// Channels
// Light
#define LIGHT_CHANNELS 5 #define LIGHT_CHANNELS 5
#define LIGHT_CH1_PIN 14 // RED #define LIGHT_CH1_PIN 14 // RED
#define LIGHT_CH2_PIN 12 // GREEN #define LIGHT_CH2_PIN 12 // GREEN
@ -1087,14 +1096,17 @@
#define MANUFACTURER "ARILUX" #define MANUFACTURER "ARILUX"
#define DEVICE "E27" #define DEVICE "E27"
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY92XX
#define DUMMY_RELAY_COUNT 1 #define DUMMY_RELAY_COUNT 1
// Channels
#define MY9291_CHANNELS 4
#define MY9291_DI_PIN 13
#define MY9291_DCKI_PIN 15
#define MY9291_COMMAND MY9291_COMMAND_DEFAULT
// Light
#define LIGHT_CHANNELS 4
#define MY92XX_MODEL MY92XX_MODEL_MY9291
#define MY92XX_CHIPS 1
#define MY92XX_DI_PIN 13
#define MY92XX_DCKI_PIN 15
#define MY92XX_COMMAND MY92XX_COMMAND_DEFAULT
#define MY92XX_MAPPING 0, 1, 2, 3
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// XENON SM-PW701U // XENON SM-PW701U
@ -1133,7 +1145,7 @@
#define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER #define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER
#define DUMMY_RELAY_COUNT 1 #define DUMMY_RELAY_COUNT 1
// Channels
// Light
#define LIGHT_CHANNELS 4 #define LIGHT_CHANNELS 4
#define LIGHT_CH1_PIN 13 // RED #define LIGHT_CH1_PIN 13 // RED
#define LIGHT_CH2_PIN 12 // GREEN #define LIGHT_CH2_PIN 12 // GREEN


+ 15
- 5
code/espurna/config/prototypes.h View File

@ -2,22 +2,32 @@
#include <NtpClientLib.h> #include <NtpClientLib.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <AsyncMqttClient.h> #include <AsyncMqttClient.h>
#include <ArduinoJson.h>
#include <functional> #include <functional>
#include <FastLED.h>
typedef std::function<void(char *, size_t)> apiGetCallbackFunction;
typedef std::function<void(const char *)> apiPutCallbackFunction;
void apiRegister(const char * url, const char * key, apiGetCallbackFunction getFn, apiPutCallbackFunction putFn = NULL);
AsyncWebServer * webServer();
void mqttRegister(void (*callback)(unsigned int, const char *, const char *));
typedef std::function<void(char *, size_t)> api_get_callback_f;
typedef std::function<void(const char *)> api_put_callback_f;
void apiRegister(const char * url, const char * key, api_get_callback_f getFn, api_put_callback_f putFn = NULL);
typedef std::function<void(unsigned int, const char *, const char *)> mqtt_callback_f;
void mqttRegister(mqtt_callback_f callback);
String mqttSubtopic(char * topic); String mqttSubtopic(char * topic);
typedef std::function<void(JsonObject&)> ws_callback_f;
void wsRegister(ws_callback_f sender, ws_callback_f receiver = NULL);
void wsSend(ws_callback_f sender);
template<typename T> bool setSetting(const String& key, T value); 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> 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, T defaultValue);
template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue); template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue);
template<typename T> void domoticzSend(const char * key, T value); template<typename T> void domoticzSend(const char * key, T value);
template<typename T> void domoticzSend(const char * key, T nvalue, const char * svalue); template<typename T> void domoticzSend(const char * key, T nvalue, const char * svalue);
template<typename T> bool influxDBSend(const char * topic, T payload);
template<typename T> bool idbSend(const char * topic, T payload);
char * ltrim(char * s); char * ltrim(char * s);

+ 1
- 1
code/espurna/config/sensors.h View File

@ -36,9 +36,9 @@
#define DHT_UPDATE_INTERVAL 60000 #define DHT_UPDATE_INTERVAL 60000
#endif #endif
#define DHT_TIMING 11
#define DHT_TEMPERATURE_TOPIC "temperature" #define DHT_TEMPERATURE_TOPIC "temperature"
#define DHT_HUMIDITY_TOPIC "humidity" #define DHT_HUMIDITY_TOPIC "humidity"
#define DHT_TEMPERATURE_DECIMALS 1 // Decimals for temperature values
#define HUMIDITY_NORMAL 0 #define HUMIDITY_NORMAL 0
#define HUMIDITY_COMFORTABLE 1 #define HUMIDITY_COMFORTABLE 1


+ 1
- 1
code/espurna/config/version.h View File

@ -1,4 +1,4 @@
#define APP_NAME "ESPURNA" #define APP_NAME "ESPURNA"
#define APP_VERSION "1.9.9"
#define APP_VERSION "1.9.10b"
#define APP_AUTHOR "xose.perez@gmail.com" #define APP_AUTHOR "xose.perez@gmail.com"
#define APP_WEBSITE "http://tinkerman.cat" #define APP_WEBSITE "http://tinkerman.cat"

+ 15
- 4
code/espurna/counter.ino View File

@ -26,6 +26,13 @@ void ICACHE_RAM_ATTR _counterISR() {
} }
} }
#if WEB_SUPPORT
void _counterWSSend(JsonObject& root) {
root["counterVisible"] = 1;
root["counterValue"] = getCounter();
}
#endif
unsigned long getCounter() { unsigned long getCounter() {
return _counterValue; return _counterValue;
} }
@ -36,9 +43,15 @@ void counterSetup() {
attachInterrupt(COUNTER_PIN, _counterISR, COUNTER_INTERRUPT_MODE); attachInterrupt(COUNTER_PIN, _counterISR, COUNTER_INTERRUPT_MODE);
#if WEB_SUPPORT #if WEB_SUPPORT
// Websockets
wsRegister(_counterWSSend);
// API
apiRegister(COUNTER_TOPIC, COUNTER_TOPIC, [](char * buffer, size_t len) { apiRegister(COUNTER_TOPIC, COUNTER_TOPIC, [](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), getCounter()); snprintf_P(buffer, len, PSTR("%d"), getCounter());
}); });
#endif #endif
DEBUG_MSG_P(PSTR("[COUNTER] Counter on GPIO %d\n"), COUNTER_PIN); DEBUG_MSG_P(PSTR("[COUNTER] Counter on GPIO %d\n"), COUNTER_PIN);
@ -62,9 +75,7 @@ void counterLoop() {
// Update websocket clients // Update websocket clients
#if WEB_SUPPORT #if WEB_SUPPORT
char buffer[100];
snprintf_P(buffer, sizeof(buffer), PSTR("{\"counterVisible\": 1, \"counterValue\": %d}"), _counterValue);
wsSend(buffer);
wsSend(_counterWSSend);
#endif #endif
// Do we have to report? // Do we have to report?
@ -80,7 +91,7 @@ void counterLoop() {
// Send to InfluxDB // Send to InfluxDB
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
influxDBSend(COUNTER_TOPIC, _counterValue);
idbSend(COUNTER_TOPIC, _counterValue);
#endif #endif
} }


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


+ 22
- 20
code/espurna/debug.ino View File

@ -18,73 +18,75 @@ WiFiUDP udpDebug;
void debugSend(const char * format, ...) { void debugSend(const char * format, ...) {
char buffer[DEBUG_MESSAGE_MAX_LENGTH+1];
va_list args; va_list args;
va_start(args, format); va_start(args, format);
int len = ets_vsnprintf(buffer, DEBUG_MESSAGE_MAX_LENGTH, format, args);
char test[1];
int len = ets_vsnprintf(test, 1, format, args) + 1;
char * buffer = new char[len];
ets_vsnprintf(buffer, len, format, args);
va_end(args); va_end(args);
#if DEBUG_SERIAL_SUPPORT #if DEBUG_SERIAL_SUPPORT
DEBUG_PORT.printf(buffer); DEBUG_PORT.printf(buffer);
if (len > DEBUG_MESSAGE_MAX_LENGTH) {
DEBUG_PORT.printf(" (...)\n");
}
#endif #endif
#if DEBUG_UDP_SUPPORT #if DEBUG_UDP_SUPPORT
#if SYSTEM_CHECK_ENABLED
if (systemCheck()) { if (systemCheck()) {
#endif
udpDebug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT); udpDebug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT);
udpDebug.write(buffer); udpDebug.write(buffer);
if (len > DEBUG_MESSAGE_MAX_LENGTH) {
udpDebug.write(" (...)\n");
}
udpDebug.endPacket(); udpDebug.endPacket();
delay(1); delay(1);
#if SYSTEM_CHECK_ENABLED
} }
#endif
#endif #endif
#if DEBUG_TELNET_SUPPORT #if DEBUG_TELNET_SUPPORT
_telnetWrite(buffer, strlen(buffer)); _telnetWrite(buffer, strlen(buffer));
#endif #endif
free(buffer);
} }
void debugSend_P(PGM_P format, ...) { void debugSend_P(PGM_P format, ...) {
char f[DEBUG_MESSAGE_MAX_LENGTH+1];
memcpy_P(f, format, DEBUG_MESSAGE_MAX_LENGTH);
char buffer[DEBUG_MESSAGE_MAX_LENGTH+1];
char f[DEBUG_FORMAT_MAX_LENGTH+1];
memcpy_P(f, format, DEBUG_FORMAT_MAX_LENGTH);
va_list args; va_list args;
va_start(args, format); va_start(args, format);
int len = ets_vsnprintf(buffer, DEBUG_MESSAGE_MAX_LENGTH, f, args);
char test[1];
int len = ets_vsnprintf(test, 1, f, args) + 1;
char * buffer = new char[len];
ets_vsnprintf(buffer, len, f, args);
va_end(args); va_end(args);
#if DEBUG_SERIAL_SUPPORT #if DEBUG_SERIAL_SUPPORT
DEBUG_PORT.printf(buffer); DEBUG_PORT.printf(buffer);
if (len > DEBUG_MESSAGE_MAX_LENGTH) {
DEBUG_PORT.printf(" (...)\n");
}
#endif #endif
#if DEBUG_UDP_SUPPORT #if DEBUG_UDP_SUPPORT
#if SYSTEM_CHECK_ENABLED
if (systemCheck()) { if (systemCheck()) {
#endif
udpDebug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT); udpDebug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT);
udpDebug.write(buffer); udpDebug.write(buffer);
if (len > DEBUG_MESSAGE_MAX_LENGTH) {
udpDebug.write(" (...)\n");
}
udpDebug.endPacket(); udpDebug.endPacket();
delay(1); delay(1);
#if SYSTEM_CHECK_ENABLED
} }
#endif
#endif #endif
#if DEBUG_TELNET_SUPPORT #if DEBUG_TELNET_SUPPORT
_telnetWrite(buffer, strlen(buffer)); _telnetWrite(buffer, strlen(buffer));
#endif #endif
free(buffer);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 4
- 3
code/espurna/dht.ino View File

@ -136,7 +136,8 @@ int readDHT() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
double getDHTTemperature(bool celsius) { double getDHTTemperature(bool celsius) {
return celsius ? _dhtTemperature : _dhtTemperature * 1.8 + 32;
double value = celsius ? _dhtTemperature : _dhtTemperature * 1.8 + 32;
return roundTo(value, DHT_TEMPERATURE_DECIMALS);
} }
double getDHTTemperature() { double getDHTTemperature() {
@ -208,8 +209,8 @@ void dhtLoop() {
#endif #endif
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
influxDBSend(getSetting("dhtTmpTopic", DHT_TEMPERATURE_TOPIC).c_str(), temperature);
influxDBSend(getSetting("dhtHumTopic", DHT_HUMIDITY_TOPIC).c_str(), humidity);
idbSend(getSetting("dhtTmpTopic", DHT_TEMPERATURE_TOPIC).c_str(), temperature);
idbSend(getSetting("dhtHumTopic", DHT_HUMIDITY_TOPIC).c_str(), humidity);
#endif #endif
// Update websocket clients // Update websocket clients


+ 21
- 0
code/espurna/domoticz.ino View File

@ -11,6 +11,9 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#include <ArduinoJson.h> #include <ArduinoJson.h>
bool _dcz_enabled = false; bool _dcz_enabled = false;
unsigned long _dcz_skip_time = 0;
unsigned long _dcz_last_idx = 0;
unsigned long _dcz_last_time = 0;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Private methods // Private methods
@ -25,6 +28,13 @@ int _domoticzRelay(unsigned int idx) {
return -1; return -1;
} }
bool _domoticzSkip(unsigned long idx) {
if (idx == _dcz_last_idx && (millis() - _dcz_last_time < _dcz_skip_time)) return true;
_dcz_last_idx = idx;
_dcz_last_time = millis();
return false;
}
void _domoticzMqtt(unsigned int type, const char * topic, const char * payload) { void _domoticzMqtt(unsigned int type, const char * topic, const char * payload) {
String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC); String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
@ -52,9 +62,14 @@ void _domoticzMqtt(unsigned int type, const char * topic, const char * payload)
unsigned long idx = root["idx"]; unsigned long idx = root["idx"];
int relayID = _domoticzRelay(idx); int relayID = _domoticzRelay(idx);
if (relayID >= 0) { if (relayID >= 0) {
// Skip message if recursive
if (_domoticzSkip(idx)) return;
unsigned long value = root["nvalue"]; unsigned long value = root["nvalue"];
DEBUG_MSG_P(PSTR("[DOMOTICZ] Received value %d for IDX %d\n"), value, idx); DEBUG_MSG_P(PSTR("[DOMOTICZ] Received value %d for IDX %d\n"), value, idx);
relayStatus(relayID, value == 1); relayStatus(relayID, value == 1);
} }
} }
@ -71,9 +86,14 @@ template<typename T> void domoticzSend(const char * key, T nvalue, const char *
if (!_dcz_enabled) return; if (!_dcz_enabled) return;
unsigned int idx = getSetting(key).toInt(); unsigned int idx = getSetting(key).toInt();
if (idx > 0) { if (idx > 0) {
// Skip message if recursive
if (_domoticzSkip(idx)) return;
char payload[128]; char payload[128];
snprintf(payload, sizeof(payload), "{\"idx\": %d, \"nvalue\": %s, \"svalue\": \"%s\"}", idx, String(nvalue).c_str(), svalue); snprintf(payload, sizeof(payload), "{\"idx\": %d, \"nvalue\": %s, \"svalue\": \"%s\"}", idx, String(nvalue).c_str(), svalue);
mqttSendRaw(getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC).c_str(), payload); mqttSendRaw(getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC).c_str(), payload);
} }
} }
@ -96,6 +116,7 @@ int domoticzIdx(unsigned int relayID) {
void domoticzConfigure() { void domoticzConfigure() {
_dcz_enabled = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1; _dcz_enabled = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1;
_dcz_skip_time = 1000 * getSetting("dczSkip", DOMOTICZ_SKIP_TIME).toInt();
} }
void domoticzSetup() { void domoticzSetup() {


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

@ -119,7 +119,7 @@ void dsLoop() {
#endif #endif
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
influxDBSend(getSetting("dsTmpTopic", DS18B20_TEMPERATURE_TOPIC).c_str(), _dsTemperatureStr);
idbSend(getSetting("dsTmpTopic", DS18B20_TEMPERATURE_TOPIC).c_str(), _dsTemperatureStr);
#endif #endif
} }


+ 45
- 26
code/espurna/espurna.ino View File

@ -21,12 +21,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "config/all.h" #include "config/all.h"
#include <EEPROM.h> #include <EEPROM.h>
#include <FastLED.h> // W.T.H. is this include ALSO needed here ?!?!!
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// METHODS // METHODS
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
unsigned long _loopDelay = 0;
void hardwareSetup() { void hardwareSetup() {
EEPROM.begin(EEPROM_SIZE); EEPROM.begin(EEPROM_SIZE);
@ -49,18 +50,11 @@ void hardwareSetup() {
pinMode(16, OUTPUT); pinMode(16, OUTPUT);
digitalWrite(16, HIGH); //Defualt CT input (pin B, solder jumper B) digitalWrite(16, HIGH); //Defualt CT input (pin B, solder jumper B)
#endif #endif
} }
void hardwareLoop() { void hardwareLoop() {
// System check
static bool checked = false;
if (!checked && (millis() > CRASH_SAFE_TIME)) {
// Check system as stable
systemCheck(true);
checked = true;
}
// Heartbeat // Heartbeat
static unsigned long last_uptime = 0; static unsigned long last_uptime = 0;
if ((millis() - last_uptime > HEARTBEAT_INTERVAL) || (last_uptime == 0)) { if ((millis() - last_uptime > HEARTBEAT_INTERVAL) || (last_uptime == 0)) {
@ -87,7 +81,8 @@ void welcome() {
DEBUG_MSG_P(PSTR("[INIT] CPU chip ID: 0x%06X\n"), ESP.getChipId()); DEBUG_MSG_P(PSTR("[INIT] CPU chip ID: 0x%06X\n"), ESP.getChipId());
DEBUG_MSG_P(PSTR("[INIT] CPU frequency: %d MHz\n"), ESP.getCpuFreqMHz()); DEBUG_MSG_P(PSTR("[INIT] CPU frequency: %d MHz\n"), ESP.getCpuFreqMHz());
DEBUG_MSG_P(PSTR("[INIT] SDK version: %s\n"), ESP.getSdkVersion()); DEBUG_MSG_P(PSTR("[INIT] SDK version: %s\n"), ESP.getSdkVersion());
DEBUG_MSG_P(PSTR("[INIT] Core version: %s\n"), ESP.getCoreVersion().c_str());
DEBUG_MSG_P(PSTR("[INIT] Core version: %s\n"), getCoreVersion().c_str());
DEBUG_MSG_P(PSTR("[INIT] Core revision: %s\n"), getCoreRevision().c_str());
DEBUG_MSG_P(PSTR("\n")); DEBUG_MSG_P(PSTR("\n"));
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -173,6 +168,9 @@ void welcome() {
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
DEBUG_MSG_P(PSTR(" INFLUXDB")); DEBUG_MSG_P(PSTR(" INFLUXDB"));
#endif #endif
#if LLMNR_SUPPORT
DEBUG_MSG_P(PSTR(" LLMNR"));
#endif
#if MDNS_SUPPORT #if MDNS_SUPPORT
DEBUG_MSG_P(PSTR(" MDNS")); DEBUG_MSG_P(PSTR(" MDNS"));
#endif #endif
@ -202,10 +200,10 @@ void welcome() {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
unsigned char custom_reset = customReset();
if (custom_reset > 0) {
unsigned char reason = resetReason();
if (reason > 0) {
char buffer[32]; char buffer[32];
strcpy_P(buffer, custom_reset_string[custom_reset-1]);
strcpy_P(buffer, custom_reset_string[reason-1]);
DEBUG_MSG_P(PSTR("[INIT] Last reset reason: %s\n"), buffer); DEBUG_MSG_P(PSTR("[INIT] Last reset reason: %s\n"), buffer);
} else { } else {
DEBUG_MSG_P(PSTR("[INIT] Last reset reason: %s\n"), (char *) ESP.getResetReason().c_str()); DEBUG_MSG_P(PSTR("[INIT] Last reset reason: %s\n"), (char *) ESP.getResetReason().c_str());
@ -215,6 +213,8 @@ void welcome() {
#if ADC_VCC_ENABLED #if ADC_VCC_ENABLED
DEBUG_MSG_P(PSTR("[INIT] Power: %d mV\n"), ESP.getVcc()); DEBUG_MSG_P(PSTR("[INIT] Power: %d mV\n"), ESP.getVcc());
#endif #endif
DEBUG_MSG_P(PSTR("[INIT] Power saving delay value: %lu ms\n"), _loopDelay);
DEBUG_MSG_P(PSTR("\n")); DEBUG_MSG_P(PSTR("\n"));
} }
@ -225,10 +225,9 @@ void setup() {
hardwareSetup(); hardwareSetup();
// Question system stability // Question system stability
systemCheck(false);
// Show welcome message and system configuration
welcome();
#if SYSTEM_CHECK_ENABLED
systemCheck(false);
#endif
// Init persistance and terminal features // Init persistance and terminal features
settingsSetup(); settingsSetup();
@ -236,6 +235,13 @@ void setup() {
setSetting("hostname", getIdentifier()); setSetting("hostname", getIdentifier());
} }
// Cache loop delay value to speed things (recommended max 250ms)
_loopDelay = atol(getSetting("loopDelay", LOOP_DELAY_TIME).c_str());
// Show welcome message and system configuration
welcome();
// Basic modules, will always run
wifiSetup(); wifiSetup();
otaSetup(); otaSetup();
#if TELNET_SUPPORT #if TELNET_SUPPORT
@ -243,10 +249,15 @@ void setup() {
#endif #endif
// Do not run the next services if system is flagged stable // Do not run the next services if system is flagged stable
if (!systemCheck()) return;
#if SYSTEM_CHECK_ENABLED
if (!systemCheck()) return;
#endif
// Init webserver required before any module that uses API
#if WEB_SUPPORT #if WEB_SUPPORT
webSetup(); webSetup();
wsSetup();
apiSetup();
#endif #endif
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
@ -260,7 +271,6 @@ void setup() {
#ifdef ITEAD_SONOFF_RFBRIDGE #ifdef ITEAD_SONOFF_RFBRIDGE
rfbSetup(); rfbSetup();
#endif #endif
#if POWER_PROVIDER != POWER_PROVIDER_NONE #if POWER_PROVIDER != POWER_PROVIDER_NONE
powerSetup(); powerSetup();
#endif #endif
@ -277,7 +287,7 @@ void setup() {
nofussSetup(); nofussSetup();
#endif #endif
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
influxDBSetup();
idbSetup();
#endif #endif
#if DS18B20_SUPPORT #if DS18B20_SUPPORT
dsSetup(); dsSetup();
@ -300,6 +310,12 @@ void setup() {
#if DOMOTICZ_SUPPORT #if DOMOTICZ_SUPPORT
domoticzSetup(); domoticzSetup();
#endif #endif
#if HOMEASSISTANT_SUPPORT
haSetup();
#endif
#if LLMNR_SUPPORT
llmnrSetup();
#endif
// Prepare configuration for version 2.0 // Prepare configuration for version 2.0
hwUpwardsCompatibility(); hwUpwardsCompatibility();
@ -315,23 +331,23 @@ void loop() {
wifiLoop(); wifiLoop();
otaLoop(); otaLoop();
// Do not run the next services if system is flagged stable
if (!systemCheck()) return;
#if SYSTEM_CHECK_ENABLED
systemCheckLoop();
// Do not run the next services if system is flagged stable
if (!systemCheck()) return;
#endif
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
lightLoop(); lightLoop();
#endif #endif
buttonLoop();
relayLoop(); relayLoop();
buttonLoop();
ledLoop(); ledLoop();
mqttLoop(); mqttLoop();
#ifdef ITEAD_SONOFF_RFBRIDGE #ifdef ITEAD_SONOFF_RFBRIDGE
rfbLoop(); rfbLoop();
#endif #endif
#if POWER_PROVIDER != POWER_PROVIDER_NONE #if POWER_PROVIDER != POWER_PROVIDER_NONE
powerLoop(); powerLoop();
#endif #endif
@ -363,4 +379,7 @@ void loop() {
irLoop(); irLoop();
#endif #endif
// Power saving delay
delay(_loopDelay);
} }

+ 11
- 3
code/espurna/hardware.ino View File

@ -13,6 +13,8 @@ the migration to future version 2 will be straigh forward.
*/ */
#include <my92xx.h>
void hwUpwardsCompatibility() { void hwUpwardsCompatibility() {
unsigned int board = getSetting("board", 0).toInt(); unsigned int board = getSetting("board", 0).toInt();
@ -222,7 +224,9 @@ void hwUpwardsCompatibility() {
setSetting("board", 20); setSetting("board", 20);
setSetting("relayProvider", RELAY_PROVIDER_LIGHT); setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
setSetting("lightProvider", LIGHT_PROVIDER_MY9192);
setSetting("lightProvider", LIGHT_PROVIDER_MY92XX);
setSetting("myModel", MY92XX_MODEL_MY9291);
setSetting("myChips", 1);
setSetting("myDIGPIO", 13); setSetting("myDIGPIO", 13);
setSetting("myDCKIGPIO", 15); setSetting("myDCKIGPIO", 15);
setSetting("relays", 1); setSetting("relays", 1);
@ -349,7 +353,9 @@ void hwUpwardsCompatibility() {
setSetting("board", 28); setSetting("board", 28);
setSetting("relayProvider", RELAY_PROVIDER_LIGHT); setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
setSetting("lightProvider", LIGHT_PROVIDER_MY9192);
setSetting("lightProvider", LIGHT_PROVIDER_MY92XX);
setSetting("myModel", MY92XX_MODEL_MY9231);
setSetting("myChips", 2);
setSetting("myDIGPIO", 12); setSetting("myDIGPIO", 12);
setSetting("myDCKIGPIO", 14); setSetting("myDCKIGPIO", 14);
setSetting("relays", 1); setSetting("relays", 1);
@ -561,7 +567,9 @@ void hwUpwardsCompatibility() {
setSetting("board", 46); setSetting("board", 46);
setSetting("relayProvider", RELAY_PROVIDER_LIGHT); setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
setSetting("lightProvider", LIGHT_PROVIDER_MY9192);
setSetting("lightProvider", LIGHT_PROVIDER_MY92XX);
setSetting("myModel", MY92XX_MODEL_MY9291);
setSetting("myChips", 1);
setSetting("myDIGPIO", 13); setSetting("myDIGPIO", 13);
setSetting("myDCKIGPIO", 15); setSetting("myDCKIGPIO", 15);
setSetting("relays", 1); setSetting("relays", 1);


+ 18
- 0
code/espurna/homeassitant.ino View File

@ -10,6 +10,8 @@ Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
#include <ArduinoJson.h> #include <ArduinoJson.h>
bool _haEnabled = false;
void haSend(bool add) { void haSend(bool add) {
DEBUG_MSG_P(PSTR("[HA] Sending autodiscovery MQTT message\n")); DEBUG_MSG_P(PSTR("[HA] Sending autodiscovery MQTT message\n"));
@ -29,6 +31,9 @@ void haSend(bool add) {
root["command_topic"] = getTopic(MQTT_TOPIC_RELAY, 0, true); root["command_topic"] = getTopic(MQTT_TOPIC_RELAY, 0, true);
root["payload_on"] = String("1"); root["payload_on"] = String("1");
root["payload_off"] = String("0"); root["payload_off"] = String("0");
root["availability_topic"] = getTopic(MQTT_TOPIC_STATUS, false);
root["payload_available"] = String("1");
root["payload_not_available"] = String("0");
} }
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
@ -63,8 +68,21 @@ void haSend(bool add) {
"/config"; "/config";
mqttSendRaw(topic.c_str(), output.c_str()); mqttSendRaw(topic.c_str(), output.c_str());
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
}
void haConfigure() {
bool enabled = getSetting("haEnabled", HOMEASSISTANT_ENABLED).toInt() == 1;
if (enabled != _haEnabled) haSend(enabled);
_haEnabled = enabled;
} }
void haSetup() {
haConfigure();
mqttRegister([](unsigned int type, const char * topic, const char * payload) {
if (type == MQTT_CONNECT_EVENT) haSend(_haEnabled);
});
}
#endif // HOMEASSISTANT_SUPPORT #endif // HOMEASSISTANT_SUPPORT

+ 37
- 18
code/espurna/influxdb.ino View File

@ -11,18 +11,33 @@ Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
#include "ESPAsyncTCP.h" #include "ESPAsyncTCP.h"
#include "SyncClient.h" #include "SyncClient.h"
bool _influxdb_enabled = false;
SyncClient _influx_client;
bool _idb_enabled = false;
SyncClient _idb_client;
// -----------------------------------------------------------------------------
#if WEB_SUPPORT
void _idbWSSend(JsonObject& root) {
root["idbVisible"] = 1;
root["idbHost"] = getSetting("idbHost");
root["idbPort"] = getSetting("idbPort", INFLUXDB_PORT).toInt();
root["idbDatabase"] = getSetting("idbDatabase");
root["idbUsername"] = getSetting("idbUsername");
root["idbPassword"] = getSetting("idbPassword");
}
#endif
template<typename T> bool influxDBSend(const char * topic, T payload) {
// -----------------------------------------------------------------------------
if (!_influxdb_enabled) return true;
template<typename T> bool idbSend(const char * topic, T payload) {
if (!_idb_enabled) return true;
if (!wifiConnected() || (WiFi.getMode() != WIFI_STA)) return true; if (!wifiConnected() || (WiFi.getMode() != WIFI_STA)) return true;
DEBUG_MSG("[INFLUXDB] Sending\n"); DEBUG_MSG("[INFLUXDB] Sending\n");
_influx_client.setTimeout(2);
if (!_influx_client.connect(getSetting("idbHost").c_str(), getSetting("idbPort", INFLUXDB_PORT).toInt())) {
_idb_client.setTimeout(2);
if (!_idb_client.connect(getSetting("idbHost").c_str(), getSetting("idbPort", INFLUXDB_PORT).toInt())) {
DEBUG_MSG("[INFLUXDB] Connection failed\n"); DEBUG_MSG("[INFLUXDB] Connection failed\n");
return false; return false;
} }
@ -37,29 +52,33 @@ template<typename T> bool influxDBSend(const char * topic, T payload) {
getSetting("idbHost").c_str(), getSetting("idbPort", INFLUXDB_PORT).toInt(), getSetting("idbHost").c_str(), getSetting("idbPort", INFLUXDB_PORT).toInt(),
strlen(data), data); strlen(data), data);
if (_influx_client.printf(request) > 0) {
while (_influx_client.connected() && _influx_client.available() == 0) delay(1);
while (_influx_client.available()) _influx_client.read();
if (_influx_client.connected()) _influx_client.stop();
if (_idb_client.printf(request) > 0) {
while (_idb_client.connected() && _idb_client.available() == 0) delay(1);
while (_idb_client.available()) _idb_client.read();
if (_idb_client.connected()) _idb_client.stop();
return true; return true;
} }
_influx_client.stop();
_idb_client.stop();
DEBUG_MSG("[INFLUXDB] Sent failed\n"); DEBUG_MSG("[INFLUXDB] Sent failed\n");
while (_influx_client.connected()) delay(0);
while (_idb_client.connected()) delay(0);
return false; return false;
} }
bool influxdbEnabled() {
return _influxdb_enabled;
bool idbEnabled() {
return _idb_enabled;
} }
void influxDBConfigure() {
_influxdb_enabled = getSetting("idbHost").length() > 0;
void idbConfigure() {
_idb_enabled = getSetting("idbHost").length() > 0;
} }
void influxDBSetup() {
influxDBConfigure();
void idbSetup() {
idbConfigure();
#if WEB_SUPPORT
wsRegister(_idbWSSend);
#endif
} }
#endif #endif

+ 12
- 21
code/espurna/light.ino View File

@ -31,9 +31,9 @@ std::vector<channel_t> _channels;
bool _lightState = false; bool _lightState = false;
unsigned int _brightness = LIGHT_MAX_BRIGHTNESS; unsigned int _brightness = LIGHT_MAX_BRIGHTNESS;
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
#include <my9291.h>
my9291 * _my9291;
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX
#include <my92xx.h>
my92xx * _my92xx;
#endif #endif
// Gamma Correction lookup table (8 bit) // Gamma Correction lookup table (8 bit)
@ -387,24 +387,15 @@ void _lightProviderUpdate() {
digitalWrite(LIGHT_ENABLE_PIN, _lightState); digitalWrite(LIGHT_ENABLE_PIN, _lightState);
#endif #endif
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX
if (_lightState) {
unsigned int red = _toPWM(0);
unsigned int green = _toPWM(1);
unsigned int blue = _toPWM(2);
unsigned int white = _toPWM(3);
unsigned int warm = _toPWM(4);
_my9291->setColor((my9291_color_t) { red, green, blue, white, warm });
_my9291->setState(true);
} else {
_my9291->setColor((my9291_color_t) { 0, 0, 0, 0, 0 });
_my9291->setState(false);
ARRAYINIT(unsigned char, channels, MY92XX_MAPPING);
for (unsigned char i=0; i<_channels.size(); i++) {
_my92xx->setChannel(channels[i], _toPWM(i));
} }
_my92xx->setState(_lightState);
_my92xx->update();
#endif #endif
@ -803,10 +794,10 @@ void lightSetup() {
pinMode(LIGHT_ENABLE_PIN, OUTPUT); pinMode(LIGHT_ENABLE_PIN, OUTPUT);
#endif #endif
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX
_my9291 = new my9291(MY9291_DI_PIN, MY9291_DCKI_PIN, MY9291_COMMAND, MY9291_CHANNELS);
for (unsigned char i=0; i<MY9291_CHANNELS; i++) {
_my92xx = new my92xx(MY92XX_MODEL, MY92XX_CHIPS, MY92XX_DI_PIN, MY92XX_DCKI_PIN, MY92XX_COMMAND);
for (unsigned char i=0; i<LIGHT_CHANNELS; i++) {
_channels.push_back((channel_t) {0, false, 0}); _channels.push_back((channel_t) {0, false, 0});
} }


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

@ -21,7 +21,6 @@ Not currently Implemented :
*/ */
#ifdef LIGHT_PROVIDER_EXPERIMENTAL_RGB_ONLY_HSV_IR #ifdef LIGHT_PROVIDER_EXPERIMENTAL_RGB_ONLY_HSV_IR
#include <FastLED.h> #include <FastLED.h>


+ 18
- 0
code/espurna/llmnr.ino View File

@ -0,0 +1,18 @@
/*
LLMNR MODULE
Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if LLMNR_SUPPORT
#include <ESP8266LLMNR.h>
void llmnrSetup() {
LLMNR.begin(getSetting("hostname").c_str());
DEBUG_MSG_P(PSTR("[LLMNR] Configured\n"));
}
#endif // LLMNR_SUPPORT

+ 21
- 8
code/espurna/mqtt.ino View File

@ -44,7 +44,7 @@ char *_mqtt_will;
unsigned long _mqtt_connected_at = 0; unsigned long _mqtt_connected_at = 0;
#endif #endif
std::vector<void (*)(unsigned int, const char *, const char *)> _mqtt_callbacks;
std::vector<mqtt_callback_f> _mqtt_callbacks;
typedef struct { typedef struct {
char * topic; char * topic;
@ -180,7 +180,19 @@ void mqttSubscribe(const char * topic) {
mqttSubscribeRaw(path.c_str()); mqttSubscribeRaw(path.c_str());
} }
void mqttRegister(void (*callback)(unsigned int, const char *, const char *)) {
void mqttUnsubscribeRaw(const char * topic) {
if (_mqtt.connected() && (strlen(topic) > 0)) {
#if MQTT_USE_ASYNC
unsigned int packetId = _mqtt.unsubscribe(topic);
DEBUG_MSG_P(PSTR("[MQTT] Unsubscribing to %s (PID %d)\n"), topic, packetId);
#else
_mqtt.unsubscribe(topic);
DEBUG_MSG_P(PSTR("[MQTT] Unsubscribing to %s\n"), topic);
#endif
}
}
void mqttRegister(mqtt_callback_f callback) {
_mqtt_callbacks.push_back(callback); _mqtt_callbacks.push_back(callback);
} }
@ -190,7 +202,6 @@ void mqttRegister(void (*callback)(unsigned int, const char *, const char *)) {
void _mqttCallback(unsigned int type, const char * topic, const char * payload) { void _mqttCallback(unsigned int type, const char * topic, const char * payload) {
if (type == MQTT_CONNECT_EVENT) { if (type == MQTT_CONNECT_EVENT) {
mqttSubscribe(MQTT_TOPIC_ACTION); mqttSubscribe(MQTT_TOPIC_ACTION);
@ -205,8 +216,7 @@ void _mqttCallback(unsigned int type, const char * topic, const char * payload)
// Actions // Actions
if (t.equals(MQTT_TOPIC_ACTION)) { if (t.equals(MQTT_TOPIC_ACTION)) {
if (strcmp(payload, MQTT_ACTION_RESET) == 0) { if (strcmp(payload, MQTT_ACTION_RESET) == 0) {
customReset(CUSTOM_RESET_MQTT);
ESP.restart();
deferredReset(100, CUSTOM_RESET_MQTT);
} }
} }
@ -226,9 +236,12 @@ void _mqttOnConnect() {
// Send first Heartbeat // Send first Heartbeat
heartbeat(); heartbeat();
// Clean subscriptions
mqttUnsubscribeRaw("#");
// Send connect event to subscribers // Send connect event to subscribers
for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) { for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) {
(*_mqtt_callbacks[i])(MQTT_CONNECT_EVENT, NULL, NULL);
(_mqtt_callbacks[i])(MQTT_CONNECT_EVENT, NULL, NULL);
} }
} }
@ -239,7 +252,7 @@ void _mqttOnDisconnect() {
// Send disconnect event to subscribers // Send disconnect event to subscribers
for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) { for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) {
(*_mqtt_callbacks[i])(MQTT_DISCONNECT_EVENT, NULL, NULL);
(_mqtt_callbacks[i])(MQTT_DISCONNECT_EVENT, NULL, NULL);
} }
} }
@ -261,7 +274,7 @@ void _mqttOnMessage(char* topic, char* payload, unsigned int len) {
// Send message event to subscribers // Send message event to subscribers
for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) { for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) {
(*_mqtt_callbacks[i])(MQTT_MESSAGE_EVENT, topic, message);
(_mqtt_callbacks[i])(MQTT_MESSAGE_EVENT, topic, message);
} }
} }


+ 1
- 2
code/espurna/ota.ino View File

@ -30,12 +30,11 @@ void otaSetup() {
}); });
ArduinoOTA.onEnd([]() { ArduinoOTA.onEnd([]() {
customReset(CUSTOM_RESET_OTA);
DEBUG_MSG_P(PSTR("\n[OTA] End\n")); DEBUG_MSG_P(PSTR("\n[OTA] End\n"));
#if WEB_SUPPORT #if WEB_SUPPORT
wsSend_P(PSTR("{\"action\": \"reload\"}")); wsSend_P(PSTR("{\"action\": \"reload\"}"));
#endif #endif
delay(100);
deferredReset(100, CUSTOM_RESET_OTA);
}); });
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {


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

@ -234,16 +234,16 @@ void _powerReport() {
#endif #endif
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
if (influxdbEnabled()) {
influxDBSend(MQTT_TOPIC_CURRENT, buf_current);
influxDBSend(MQTT_TOPIC_POWER_APPARENT, String((int) _power_apparent).c_str());
influxDBSend(MQTT_TOPIC_ENERGY_DELTA, buf_energy_delta);
influxDBSend(MQTT_TOPIC_ENERGY_TOTAL, buf_energy_total);
if (idbEnabled()) {
idbSend(MQTT_TOPIC_CURRENT, buf_current);
idbSend(MQTT_TOPIC_POWER_APPARENT, String((int) _power_apparent).c_str());
idbSend(MQTT_TOPIC_ENERGY_DELTA, buf_energy_delta);
idbSend(MQTT_TOPIC_ENERGY_TOTAL, buf_energy_total);
#if POWER_HAS_ACTIVE #if POWER_HAS_ACTIVE
influxDBSend(MQTT_TOPIC_POWER_ACTIVE, String((int) _power_active).c_str());
influxDBSend(MQTT_TOPIC_POWER_REACTIVE, String((int) _power_reactive).c_str());
influxDBSend(MQTT_TOPIC_VOLTAGE, String((int) _power_voltage).c_str());
influxDBSend(MQTT_TOPIC_POWER_FACTOR, String((int) 100 * _power_factor).c_str());
idbSend(MQTT_TOPIC_POWER_ACTIVE, String((int) _power_active).c_str());
idbSend(MQTT_TOPIC_POWER_REACTIVE, String((int) _power_reactive).c_str());
idbSend(MQTT_TOPIC_VOLTAGE, String((int) _power_voltage).c_str());
idbSend(MQTT_TOPIC_POWER_FACTOR, String((int) 100 * _power_factor).c_str());
#endif #endif
} }
#endif #endif


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

@ -493,7 +493,7 @@ void relayInfluxDB(unsigned char id) {
if (id >= _relays.size()) return; if (id >= _relays.size()) return;
char buffer[10]; char buffer[10];
snprintf_P(buffer, sizeof(buffer), PSTR("%s,id=%d"), MQTT_TOPIC_RELAY, id); snprintf_P(buffer, sizeof(buffer), PSTR("%s,id=%d"), MQTT_TOPIC_RELAY, id);
influxDBSend(buffer, relayStatus(id) ? "1" : "0");
idbSend(buffer, relayStatus(id) ? "1" : "0");
} }
#endif #endif


+ 37
- 39
code/espurna/settings.ino View File

@ -118,7 +118,7 @@ void settingsSetup() {
Embedis::hardware( F("WIFI"), [](Embedis* e) { Embedis::hardware( F("WIFI"), [](Embedis* e) {
StreamString s; StreamString s;
WiFi.printDiag(s); WiFi.printDiag(s);
e->response(s);
DEBUG_MSG(s.c_str());
}, 0); }, 0);
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -126,58 +126,57 @@ void settingsSetup() {
Embedis::command( F("RESET.WIFI"), [](Embedis* e) { Embedis::command( F("RESET.WIFI"), [](Embedis* e) {
wifiConfigure(); wifiConfigure();
wifiDisconnect(); wifiDisconnect();
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
Embedis::command( F("RESET.MQTT"), [](Embedis* e) { Embedis::command( F("RESET.MQTT"), [](Embedis* e) {
mqttConfigure(); mqttConfigure();
mqttDisconnect(); mqttDisconnect();
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
Embedis::command( F("INFO"), [](Embedis* e) { Embedis::command( F("INFO"), [](Embedis* e) {
welcome(); welcome();
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
Embedis::command( F("UPTIME"), [](Embedis* e) { Embedis::command( F("UPTIME"), [](Embedis* e) {
e->stream->printf("Uptime: %d seconds\n", getUptime());
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("Uptime: %d seconds\n"), getUptime());
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
Embedis::command( F("RESET"), [](Embedis* e) { Embedis::command( F("RESET"), [](Embedis* e) {
e->response(Embedis::OK);
customReset(CUSTOM_RESET_TERMINAL);
ESP.restart();
DEBUG_MSG_P(PSTR("+OK\n"));
deferredReset(100, CUSTOM_RESET_TERMINAL);
}); });
Embedis::command( F("ERASE.CONFIG"), [](Embedis* e) { Embedis::command( F("ERASE.CONFIG"), [](Embedis* e) {
e->response(Embedis::OK);
customReset(CUSTOM_RESET_TERMINAL);
DEBUG_MSG_P(PSTR("+OK\n"));
resetReason(CUSTOM_RESET_TERMINAL);
ESP.eraseConfig(); ESP.eraseConfig();
*((int*) 0) = 0; // see https://github.com/esp8266/Arduino/issues/1494 *((int*) 0) = 0; // see https://github.com/esp8266/Arduino/issues/1494
}); });
#if NOFUSS_SUPPORT #if NOFUSS_SUPPORT
Embedis::command( F("NOFUSS"), [](Embedis* e) { Embedis::command( F("NOFUSS"), [](Embedis* e) {
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("+OK\n"));
nofussRun(); nofussRun();
}); });
#endif #endif
Embedis::command( F("FACTORY.RESET"), [](Embedis* e) { Embedis::command( F("FACTORY.RESET"), [](Embedis* e) {
settingsFactoryReset(); settingsFactoryReset();
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
Embedis::command( F("HEAP"), [](Embedis* e) { Embedis::command( F("HEAP"), [](Embedis* e) {
e->stream->printf("Free HEAP: %d bytes\n", ESP.getFreeHeap());
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("Free HEAP: %d bytes\n"), ESP.getFreeHeap());
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
Embedis::command( F("RELAY"), [](Embedis* e) { Embedis::command( F("RELAY"), [](Embedis* e) {
if (e->argc < 2) { if (e->argc < 2) {
return e->response(Embedis::ARGS_ERROR);
DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
} }
int id = String(e->argv[1]).toInt(); int id = String(e->argv[1]).toInt();
if (e->argc > 2) { if (e->argc > 2) {
@ -188,8 +187,8 @@ void settingsSetup() {
relayStatus(id, value == 1); relayStatus(id, value == 1);
} }
} }
e->stream->printf("Status: %s\n", relayStatus(id) ? "true" : "false");
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("Status: %s\n"), relayStatus(id) ? "true" : "false");
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
@ -202,8 +201,8 @@ void settingsSetup() {
lightColor(color.c_str()); lightColor(color.c_str());
lightUpdate(true, true); lightUpdate(true, true);
} }
e->stream->printf("Color: %s\n", lightColor().c_str());
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str());
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
Embedis::command( F("BRIGHTNESS"), [](Embedis* e) { Embedis::command( F("BRIGHTNESS"), [](Embedis* e) {
@ -211,8 +210,8 @@ void settingsSetup() {
lightBrightness(String(e->argv[1]).toInt()); lightBrightness(String(e->argv[1]).toInt());
lightUpdate(true, true); lightUpdate(true, true);
} }
e->stream->printf("Brightness: %d\n", lightBrightness());
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("Brightness: %d\n"), lightBrightness());
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
Embedis::command( F("MIRED"), [](Embedis* e) { Embedis::command( F("MIRED"), [](Embedis* e) {
@ -221,8 +220,8 @@ void settingsSetup() {
lightColor(color.c_str()); lightColor(color.c_str());
lightUpdate(true, true); lightUpdate(true, true);
} }
e->stream->printf("Color: %s\n", lightColor().c_str());
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str());
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
Embedis::command( F("KELVIN"), [](Embedis* e) { Embedis::command( F("KELVIN"), [](Embedis* e) {
@ -231,15 +230,15 @@ void settingsSetup() {
lightColor(color.c_str()); lightColor(color.c_str());
lightUpdate(true, true); lightUpdate(true, true);
} }
e->stream->printf("Color: %s\n", lightColor().c_str());
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str());
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
} }
Embedis::command( F("CHANNEL"), [](Embedis* e) { Embedis::command( F("CHANNEL"), [](Embedis* e) {
if (e->argc < 2) { if (e->argc < 2) {
return e->response(Embedis::ARGS_ERROR);
DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
} }
int id = String(e->argv[1]).toInt(); int id = String(e->argv[1]).toInt();
if (e->argc > 2) { if (e->argc > 2) {
@ -247,17 +246,17 @@ void settingsSetup() {
lightChannel(id, value); lightChannel(id, value);
lightUpdate(true, true); lightUpdate(true, true);
} }
e->stream->printf("Channel #%d: %d\n", id, lightChannel(id));
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("Channel #%d: %d\n"), id, lightChannel(id));
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
#endif #endif
Embedis::command( F("EEPROM"), [](Embedis* e) { Embedis::command( F("EEPROM"), [](Embedis* e) {
unsigned long freeEEPROM = SPI_FLASH_SEC_SIZE - settingsSize(); unsigned long freeEEPROM = SPI_FLASH_SEC_SIZE - settingsSize();
e->stream->printf("Number of keys: %d\n", settingsKeyCount());
e->stream->printf("Free EEPROM: %d bytes (%d%%)\n", freeEEPROM, 100 * freeEEPROM / SPI_FLASH_SEC_SIZE);
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("Number of keys: %d\n"), settingsKeyCount());
DEBUG_MSG_P(PSTR("Free EEPROM: %d bytes (%d%%)\n"), freeEEPROM, 100 * freeEEPROM / SPI_FLASH_SEC_SIZE);
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
Embedis::command( F("DUMP"), [](Embedis* e) { Embedis::command( F("DUMP"), [](Embedis* e) {
@ -265,25 +264,24 @@ void settingsSetup() {
for (unsigned int i=0; i<size; i++) { for (unsigned int i=0; i<size; i++) {
String key = settingsKeyName(i); String key = settingsKeyName(i);
String value = getSetting(key); String value = getSetting(key);
e->stream->printf("+%s => %s\n", key.c_str(), value.c_str());
DEBUG_MSG_P(PSTR("+%s => %s\n"), key.c_str(), value.c_str());
} }
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
#if DEBUG_SUPPORT #if DEBUG_SUPPORT
Embedis::command( F("CRASH"), [](Embedis* e) { Embedis::command( F("CRASH"), [](Embedis* e) {
debugDumpCrashInfo(); debugDumpCrashInfo();
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("+OK\n"));
}); });
#endif #endif
Embedis::command( F("DUMP.RAW"), [](Embedis* e) { Embedis::command( F("DUMP.RAW"), [](Embedis* e) {
for (unsigned int i = 0; i < SPI_FLASH_SEC_SIZE; i++) { for (unsigned int i = 0; i < SPI_FLASH_SEC_SIZE; i++) {
if (i % 16 == 0) e->stream->printf("\n[%04X] ", i);
e->stream->printf("%02X ", EEPROM.read(i));
if (i % 16 == 0) DEBUG_MSG_P(PSTR("\n[%04X] "), i);
DEBUG_MSG_P(PSTR("%02X "), EEPROM.read(i));
} }
e->stream->printf("\n");
e->response(Embedis::OK);
DEBUG_MSG_P(PSTR("\n+OK\n"));
}); });
DEBUG_MSG_P(PSTR("[SETTINGS] EEPROM size: %d bytes\n"), SPI_FLASH_SEC_SIZE); DEBUG_MSG_P(PSTR("[SETTINGS] EEPROM size: %d bytes\n"), SPI_FLASH_SEC_SIZE);


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


+ 66
- 11
code/espurna/utils.ino View File

@ -6,12 +6,33 @@ Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
*/ */
#include <Ticker.h>
Ticker _defer_reset;
String getIdentifier() { String getIdentifier() {
char buffer[20]; char buffer[20];
snprintf_P(buffer, sizeof(buffer), PSTR("%s_%06X"), DEVICE, ESP.getChipId()); snprintf_P(buffer, sizeof(buffer), PSTR("%s_%06X"), DEVICE, ESP.getChipId());
return String(buffer); return String(buffer);
} }
String getCoreVersion() {
String version = ESP.getCoreVersion();
#ifdef ARDUINO_ESP8266_RELEASE
if (version.equals("00000000")) {
version = String(ARDUINO_ESP8266_RELEASE);
}
#endif
return version;
}
String getCoreRevision() {
#ifdef ARDUINO_ESP8266_GIT_VER
return String(ARDUINO_ESP8266_GIT_VER);
#else
return String("");
#endif
}
String buildTime() { String buildTime() {
const char time_now[] = __TIME__; // hh:mm:ss const char time_now[] = __TIME__; // hh:mm:ss
@ -96,13 +117,13 @@ void heartbeat() {
#if (HEARTBEAT_REPORT_UPTIME) #if (HEARTBEAT_REPORT_UPTIME)
mqttSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str()); mqttSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
influxDBSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
idbSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
#endif #endif
#endif #endif
#if (HEARTBEAT_REPORT_FREEHEAP) #if (HEARTBEAT_REPORT_FREEHEAP)
mqttSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str()); mqttSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
influxDBSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
idbSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
#endif #endif
#endif #endif
#if (HEARTBEAT_REPORT_RELAY) #if (HEARTBEAT_REPORT_RELAY)
@ -120,30 +141,53 @@ void heartbeat() {
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true); mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
#endif #endif
// Send info to websocket clients
{
char buffer[200];
snprintf_P(
buffer,
sizeof(buffer) - 1,
PSTR("{\"time\": \"%s\", \"uptime\": %lu, \"heap\": %lu}"),
ntpDateTime().c_str(), uptime_seconds, free_heap
);
wsSend(buffer);
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void customReset(unsigned char status) {
EEPROM.write(EEPROM_CUSTOM_RESET, status);
EEPROM.commit();
}
unsigned char customReset() {
unsigned char resetReason() {
static unsigned char status = 255; static unsigned char status = 255;
if (status == 255) { if (status == 255) {
status = EEPROM.read(EEPROM_CUSTOM_RESET); status = EEPROM.read(EEPROM_CUSTOM_RESET);
if (status > 0) customReset(0);
if (status > 0) resetReason(0);
if (status > CUSTOM_RESET_MAX) status = 0; if (status > CUSTOM_RESET_MAX) status = 0;
} }
return status; return status;
} }
void resetReason(unsigned char reason) {
EEPROM.write(EEPROM_CUSTOM_RESET, reason);
EEPROM.commit();
}
void reset(unsigned char reason) {
resetReason(reason);
ESP.restart();
}
void deferredReset(unsigned long delay, unsigned char reason) {
_defer_reset.once_ms(delay, reset, reason);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if SYSTEM_CHECK_ENABLED
// Call this method on boot with start=true to increase the crash counter // Call this method on boot with start=true to increase the crash counter
// Call it again once the system is stable to decrease the counter // Call it again once the system is stable to decrease the counter
// If the counter reaches CRASH_COUNT_MAX then the system is flagged as unstable
// If the counter reaches SYSTEM_CHECK_MAX then the system is flagged as unstable
// setting _systemOK = false; // setting _systemOK = false;
// //
// An unstable system will only have serial access, WiFi in AP mode and OTA // An unstable system will only have serial access, WiFi in AP mode and OTA
@ -156,7 +200,7 @@ void systemCheck(bool stable) {
value = 0; value = 0;
DEBUG_MSG_P(PSTR("[MAIN] System OK\n")); DEBUG_MSG_P(PSTR("[MAIN] System OK\n"));
} else { } else {
if (++value > CRASH_COUNT_MAX) {
if (++value > SYSTEM_CHECK_MAX) {
_systemStable = false; _systemStable = false;
value = 0; value = 0;
DEBUG_MSG_P(PSTR("[MAIN] System UNSTABLE\n")); DEBUG_MSG_P(PSTR("[MAIN] System UNSTABLE\n"));
@ -170,6 +214,17 @@ bool systemCheck() {
return _systemStable; return _systemStable;
} }
void systemCheckLoop() {
static bool checked = false;
if (!checked && (millis() > SYSTEM_CHECK_TIME)) {
// Check system as stable
systemCheck(true);
checked = true;
}
}
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
char * ltrim(char * s) { char * ltrim(char * s) {


+ 26
- 997
code/espurna/web.ino
File diff suppressed because it is too large
View File


+ 7
- 1
code/espurna/wifi.ino View File

@ -66,7 +66,9 @@ void wifiConfigure() {
jw.cleanNetworks(); jw.cleanNetworks();
// If system is flagged unstable we do not init wifi networks // If system is flagged unstable we do not init wifi networks
if (!systemCheck()) return;
#if SYSTEM_CHECK_ENABLED
if (!systemCheck()) return;
#endif
int i; int i;
for (i = 0; i< WIFI_MAX_NETWORKS; i++) { for (i = 0; i< WIFI_MAX_NETWORKS; i++) {
@ -172,6 +174,10 @@ void wifiInject() {
void wifiSetup() { void wifiSetup() {
#if WIFI_SLEEP_ENABLED
wifi_set_sleep_type(LIGHT_SLEEP_T);
#endif
wifiInject(); wifiInject();
wifiConfigure(); wifiConfigure();


code/espurna/web.h → code/espurna/ws.h View File

@ -19,8 +19,8 @@ class WebSocketIncommingBuffer {
public: public:
WebSocketIncommingBuffer(AwsMessageHandler cb, bool terminate_string = true, bool cb_on_fragments = false) : WebSocketIncommingBuffer(AwsMessageHandler cb, bool terminate_string = true, bool cb_on_fragments = false) :
_cb(cb), _cb(cb),
_cb_on_fragments(cb_on_fragments),
_terminate_string(terminate_string), _terminate_string(terminate_string),
_cb_on_fragments(cb_on_fragments),
_buffer(0) _buffer(0)
{} {}
@ -30,19 +30,24 @@ class WebSocketIncommingBuffer {
void data_event(AsyncWebSocketClient *client, AwsFrameInfo *info, uint8_t *data, size_t len) { void data_event(AsyncWebSocketClient *client, AwsFrameInfo *info, uint8_t *data, size_t len) {
if((info->final || _cb_on_fragments) &&
!_terminate_string && info->index == 0 && info->len == len) {
if ((info->final || _cb_on_fragments)
&& !_terminate_string
&& info->index == 0
&& info->len == len) {
/* The whole message is in a single frame and we got all of it's /* The whole message is in a single frame and we got all of it's
data therefore we can parse it without copying the data first.*/ data therefore we can parse it without copying the data first.*/
_cb(client, data, len); _cb(client, data, len);
} else { } else {
if (info->len > MAX_WS_MSG_SIZE) return; if (info->len > MAX_WS_MSG_SIZE) return;
/* Check if previous fragment was discarded because it was too long. */ /* Check if previous fragment was discarded because it was too long. */
if (!_cb_on_fragments && info->num > 0 && !_buffer) return;
//if (!_cb_on_fragments && info->num > 0 && !_buffer) return;
if (!_buffer) _buffer = new std::vector<uint8_t>();
if (!_buffer) {
_buffer = new std::vector<uint8_t>();
}
if (info->index == 0) { if (info->index == 0) {
//New frame => preallocate memory //New frame => preallocate memory
if (_cb_on_fragments) { if (_cb_on_fragments) {
@ -59,16 +64,17 @@ class WebSocketIncommingBuffer {
} }
} }
} }
//assert(_buffer->size() == info->index); //assert(_buffer->size() == info->index);
_buffer->insert(_buffer->end(), data, data+len); _buffer->insert(_buffer->end(), data, data+len);
if (info->index + len == info->len &&
(info->final || _cb_on_fragments)) {
if (info->index + len == info->len
&& (info->final || _cb_on_fragments)) {
// Frame/message complete // Frame/message complete
if (_terminate_string) {
_buffer->push_back(0);
}
if (_terminate_string) _buffer->push_back(0);
_cb(client, _buffer->data(), _buffer->size()); _cb(client, _buffer->data(), _buffer->size());
_buffer->clear(); _buffer->clear();
} }
} }
} }

+ 755
- 0
code/espurna/ws.ino View File

@ -0,0 +1,755 @@
/*
WEBSOCKET MODULE
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if WEB_SUPPORT
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
#include <Ticker.h>
#include <vector>
#include "ws.h"
AsyncWebSocket _ws("/ws");
Ticker _web_defer;
std::vector<ws_callback_f> _ws_sender_callbacks;
std::vector<ws_callback_f> _ws_receiver_callbacks;
// -----------------------------------------------------------------------------
// Private methods
// -----------------------------------------------------------------------------
void _wsMQTTCallback(unsigned int type, const char * topic, const char * payload) {
if (type == MQTT_CONNECT_EVENT) {
wsSend_P(PSTR("{\"mqttStatus\": true}"));
}
if (type == MQTT_DISCONNECT_EVENT) {
wsSend_P(PSTR("{\"mqttStatus\": false}"));
}
}
void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) {
//DEBUG_MSG_P(PSTR("[WEBSOCKET] Parsing: %s\n"), length ? (char*) payload : "");
// Get client ID
uint32_t client_id = client->id();
// Parse JSON input
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject((char *) payload);
if (!root.success()) {
DEBUG_MSG_P(PSTR("[WEBSOCKET] Error parsing data\n"));
wsSend_P(client_id, PSTR("{\"message\": 3}"));
return;
}
// Check actions
if (root.containsKey("action")) {
String action = root["action"];
DEBUG_MSG_P(PSTR("[WEBSOCKET] Requested action: %s\n"), action.c_str());
if (action.equals("reset")) {
deferredReset(100, CUSTOM_RESET_WEB);
}
#ifdef ITEAD_SONOFF_RFBRIDGE
if (action.equals("rfblearn") && root.containsKey("data")) {
JsonObject& data = root["data"];
rfbLearn(data["id"], data["status"]);
}
if (action.equals("rfbforget") && root.containsKey("data")) {
JsonObject& data = root["data"];
rfbForget(data["id"], data["status"]);
}
if (action.equals("rfbsend") && root.containsKey("data")) {
JsonObject& data = root["data"];
rfbStore(data["id"], data["status"], data["data"].as<const char*>());
}
#endif
if (action.equals("restore") && root.containsKey("data")) {
JsonObject& data = root["data"];
if (!data.containsKey("app") || (data["app"] != APP_NAME)) {
wsSend_P(client_id, PSTR("{\"message\": 4}"));
return;
}
for (unsigned int i = EEPROM_DATA_END; i < SPI_FLASH_SEC_SIZE; i++) {
EEPROM.write(i, 0xFF);
}
for (auto element : data) {
if (strcmp(element.key, "app") == 0) continue;
if (strcmp(element.key, "version") == 0) continue;
setSetting(element.key, element.value.as<char*>());
}
saveSettings();
wsSend_P(client_id, PSTR("{\"message\": 5}"));
}
if (action.equals("reconnect")) {
// Let the HTTP request return and disconnect after 100ms
_web_defer.once_ms(100, wifiDisconnect);
}
if (action.equals("relay") && root.containsKey("data")) {
JsonObject& data = root["data"];
if (data.containsKey("status")) {
unsigned char value = relayParsePayload(data["status"]);
if (value == 3) {
relayWS();
} else if (value < 3) {
unsigned int relayID = 0;
if (data.containsKey("id")) {
String value = data["id"];
relayID = value.toInt();
}
// Action to perform
if (value == 0) {
relayStatus(relayID, false);
} else if (value == 1) {
relayStatus(relayID, true);
} else if (value == 2) {
relayToggle(relayID);
}
}
}
}
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
if (lightHasColor()) {
if (action.equals("rgb") && root.containsKey("data")) {
lightColor((const char *) root["data"], true);
lightUpdate(true, true);
}
if (action.equals("brightness") && root.containsKey("data")) {
lightBrightness(root["data"]);
lightUpdate(true, true);
}
if (action.equals("hsv") && root.containsKey("data")) {
lightColor((const char *) root["data"], false);
lightUpdate(true, true);
}
}
if (action.equals("channel") && root.containsKey("data")) {
JsonObject& data = root["data"];
if (data.containsKey("id") && data.containsKey("value")) {
lightChannel(data["id"], data["value"]);
lightUpdate(true, true);
}
}
#ifdef LIGHT_PROVIDER_EXPERIMENTAL_RGB_ONLY_HSV_IR
if (action.equals("anim_mode") && root.containsKey("data")) {
lightAnimMode(root["data"]);
lightUpdate(true, true);
}
if (action.equals("anim_speed") && root.containsKey("data")) {
lightAnimSpeed(root["data"]);
lightUpdate(true, true);
}
#endif //LIGHT_PROVIDER_EXPERIMENTAL_RGB_ONLY_HSV_IR
#endif //LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
};
// Check config
if (root.containsKey("config") && root["config"].is<JsonArray&>()) {
JsonArray& config = root["config"];
DEBUG_MSG_P(PSTR("[WEBSOCKET] Parsing configuration data\n"));
unsigned char webMode = WEB_MODE_NORMAL;
bool save = false;
bool changed = false;
bool changedMQTT = false;
bool changedNTP = false;
unsigned int network = 0;
unsigned int dczRelayIdx = 0;
String adminPass;
for (unsigned int i=0; i<config.size(); i++) {
String key = config[i]["name"];
String value = config[i]["value"];
// Skip firmware filename
if (key.equals("filename")) continue;
#if POWER_PROVIDER != POWER_PROVIDER_NONE
if (key == "pwrExpectedP") {
powerCalibrate(POWER_MAGNITUDE_ACTIVE, value.toFloat());
changed = true;
continue;
}
if (key == "pwrExpectedV") {
powerCalibrate(POWER_MAGNITUDE_VOLTAGE, value.toFloat());
changed = true;
continue;
}
if (key == "pwrExpectedC") {
powerCalibrate(POWER_MAGNITUDE_CURRENT, value.toFloat());
changed = true;
continue;
}
if (key == "pwrExpectedF") {
powerCalibrate(POWER_MAGNITUDE_POWER_FACTOR, value.toFloat());
changed = true;
continue;
}
if (key == "pwrResetCalibration") {
if (value.toInt() == 1) {
powerResetCalibration();
changed = true;
}
continue;
}
#endif
#if DOMOTICZ_SUPPORT
if (key == "dczRelayIdx") {
if (dczRelayIdx >= relayCount()) continue;
key = key + String(dczRelayIdx);
++dczRelayIdx;
}
#else
if (key.startsWith("dcz")) continue;
#endif
// Web portions
if (key == "webPort") {
if ((value.toInt() == 0) || (value.toInt() == 80)) {
save = changed = true;
delSetting(key);
continue;
}
}
if (key == "webMode") {
webMode = value.toInt();
continue;
}
// Check password
if (key == "adminPass1") {
adminPass = value;
continue;
}
if (key == "adminPass2") {
if (!value.equals(adminPass)) {
wsSend_P(client_id, PSTR("{\"message\": 7}"));
return;
}
if (value.length() == 0) continue;
wsSend_P(client_id, PSTR("{\"action\": \"reload\"}"));
key = String("adminPass");
}
if (key == "ssid") {
key = key + String(network);
}
if (key == "pass") {
key = key + String(network);
}
if (key == "ip") {
key = key + String(network);
}
if (key == "gw") {
key = key + String(network);
}
if (key == "mask") {
key = key + String(network);
}
if (key == "dns") {
key = key + String(network);
++network;
}
if (value != getSetting(key)) {
//DEBUG_MSG_P(PSTR("[WEBSOCKET] Storing %s = %s\n", key.c_str(), value.c_str()));
setSetting(key, value);
save = changed = true;
if (key.startsWith("mqtt")) changedMQTT = true;
#if NTP_SUPPORT
if (key.startsWith("ntp")) changedNTP = true;
#endif
}
}
if (webMode == WEB_MODE_NORMAL) {
// Clean wifi networks
int i = 0;
while (i < network) {
if (!hasSetting("ssid", i)) {
delSetting("ssid", i);
break;
}
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);
++i;
}
while (i < WIFI_MAX_NETWORKS) {
if (hasSetting("ssid", i)) {
save = changed = true;
}
delSetting("ssid", i);
delSetting("pass", i);
delSetting("ip", i);
delSetting("gw", i);
delSetting("mask", i);
delSetting("dns", i);
++i;
}
}
// Save settings
if (save) {
wsConfigure();
saveSettings();
wifiConfigure();
otaConfigure();
if (changedMQTT) {
mqttConfigure();
mqttDisconnect();
}
#if ALEXA_SUPPORT
alexaConfigure();
#endif
#if INFLUXDB_SUPPORT
idbConfigure();
#endif
#if DOMOTICZ_SUPPORT
domoticzConfigure();
#endif
#if NOFUSS_SUPPORT
nofussConfigure();
#endif
#if RF_SUPPORT
rfBuildCodes();
#endif
#if POWER_PROVIDER != POWER_PROVIDER_NONE
powerConfigure();
#endif
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
#if LIGHT_SAVE_ENABLED == 0
lightSave();
#endif
#endif
#if NTP_SUPPORT
if (changedNTP) ntpConfigure();
#endif
#if HOMEASSISTANT_SUPPORT
haConfigure();
#endif
}
if (changed) {
wsSend_P(client_id, PSTR("{\"message\": 8}"));
} else {
wsSend_P(client_id, PSTR("{\"message\": 9}"));
}
}
}
void _wsStart(uint32_t client_id) {
char chipid[7];
snprintf_P(chipid, sizeof(chipid), PSTR("%06X"), ESP.getChipId());
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
bool changePassword = false;
#if WEB_FORCE_PASS_CHANGE
String adminPass = getSetting("adminPass", ADMIN_PASS);
if (adminPass.equals(ADMIN_PASS)) changePassword = true;
#endif
if (changePassword) {
root["webMode"] = WEB_MODE_PASSWORD;
} else {
root["webMode"] = WEB_MODE_NORMAL;
root["app_name"] = APP_NAME;
root["app_version"] = APP_VERSION;
root["app_build"] = buildTime();
root["manufacturer"] = MANUFACTURER;
root["chipid"] = chipid;
root["mac"] = WiFi.macAddress();
root["device"] = DEVICE;
root["hostname"] = getSetting("hostname");
root["network"] = getNetwork();
root["deviceip"] = getIP();
root["time"] = ntpDateTime();
root["uptime"] = getUptime();
root["heap"] = ESP.getFreeHeap();
root["sketch_size"] = ESP.getSketchSize();
root["free_size"] = ESP.getFreeSketchSpace();
#if NTP_SUPPORT
root["ntpVisible"] = 1;
root["ntpStatus"] = ntpConnected();
root["ntpServer1"] = getSetting("ntpServer1", NTP_SERVER);
root["ntpServer2"] = getSetting("ntpServer2");
root["ntpServer3"] = getSetting("ntpServer3");
root["ntpOffset"] = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
root["ntpDST"] = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1;
#endif
root["mqttStatus"] = mqttConnected();
root["mqttEnabled"] = mqttEnabled();
root["mqttServer"] = getSetting("mqttServer", MQTT_SERVER);
root["mqttPort"] = getSetting("mqttPort", MQTT_PORT);
root["mqttUser"] = getSetting("mqttUser");
root["mqttPassword"] = getSetting("mqttPassword");
#if ASYNC_TCP_SSL_ENABLED
root["mqttsslVisible"] = 1;
root["mqttUseSSL"] = getSetting("mqttUseSSL", 0).toInt() == 1;
root["mqttFP"] = getSetting("mqttFP");
#endif
root["mqttTopic"] = getSetting("mqttTopic", MQTT_TOPIC);
root["mqttUseJson"] = getSetting("mqttUseJson", MQTT_USE_JSON).toInt() == 1;
JsonArray& relay = root.createNestedArray("relayStatus");
for (unsigned char relayID=0; relayID<relayCount(); relayID++) {
relay.add(relayStatus(relayID));
}
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
root["colorVisible"] = 1;
root["useColor"] = getSetting("useColor", LIGHT_USE_COLOR).toInt() == 1;
root["useWhite"] = getSetting("useWhite", LIGHT_USE_WHITE).toInt() == 1;
root["useGamma"] = getSetting("useGamma", LIGHT_USE_GAMMA).toInt() == 1;
root["useCSS"] = getSetting("useCSS", LIGHT_USE_CSS).toInt() == 1;
bool useRGB = getSetting("useRGB", LIGHT_USE_RGB).toInt() == 1;
root["useRGB"] = useRGB;
if (lightHasColor()) {
if (useRGB) {
root["rgb"] = lightColor(true);
root["brightness"] = lightBrightness();
} else {
root["hsv"] = lightColor(false);
}
#ifdef LIGHT_PROVIDER_EXPERIMENTAL_RGB_ONLY_HSV_IR
root["anim_mode"] = lightAnimMode();
root["anim_speed"] = lightAnimSpeed();
#endif // LIGHT_PROVIDER_EXPERIMENTAL_RGB_ONLY_HSV_IR
}
JsonArray& channels = root.createNestedArray("channels");
for (unsigned char id=0; id < lightChannels(); id++) {
channels.add(lightChannel(id));
}
#endif
root["relayMode"] = getSetting("relayMode", RELAY_MODE);
root["relayPulseMode"] = getSetting("relayPulseMode", RELAY_PULSE_MODE);
root["relayPulseTime"] = getSetting("relayPulseTime", RELAY_PULSE_TIME).toFloat();
if (relayCount() > 1) {
root["multirelayVisible"] = 1;
root["relaySync"] = getSetting("relaySync", RELAY_SYNC);
}
root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt();
root["webPort"] = getSetting("webPort", WEB_PORT).toInt();
root["apiEnabled"] = getSetting("apiEnabled", API_ENABLED).toInt() == 1;
root["apiKey"] = getSetting("apiKey");
root["apiRealTime"] = getSetting("apiRealTime", API_REAL_TIME_VALUES).toInt() == 1;
root["tmpUnits"] = getSetting("tmpUnits", TMP_UNITS).toInt();
root["tmpCorrection"] = getSetting("tmpCorrection", TMP_CORRECTION).toFloat();
#if HOMEASSISTANT_SUPPORT
root["haVisible"] = 1;
root["haPrefix"] = getSetting("haPrefix", HOMEASSISTANT_PREFIX);
#endif // HOMEASSISTANT_SUPPORT
#if DOMOTICZ_SUPPORT
root["dczVisible"] = 1;
root["dczEnabled"] = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1;
root["dczSkip"] = getSetting("dczSkip", DOMOTICZ_SKIP_TIME);
root["dczTopicIn"] = getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC);
root["dczTopicOut"] = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
JsonArray& dczRelayIdx = root.createNestedArray("dczRelayIdx");
for (byte i=0; i<relayCount(); i++) {
dczRelayIdx.add(domoticzIdx(i));
}
#if DHT_SUPPORT
root["dczTmpIdx"] = getSetting("dczTmpIdx").toInt();
root["dczHumIdx"] = getSetting("dczHumIdx").toInt();
#endif
#if DS18B20_SUPPORT
root["dczTmpIdx"] = getSetting("dczTmpIdx").toInt();
#endif
#if ANALOG_SUPPORT
root["dczAnaIdx"] = getSetting("dczAnaIdx").toInt();
#endif
#if POWER_PROVIDER != POWER_PROVIDER_NONE
root["dczPowIdx"] = getSetting("dczPowIdx").toInt();
root["dczEnergyIdx"] = getSetting("dczEnergyIdx").toInt();
root["dczCurrentIdx"] = getSetting("dczCurrentIdx").toInt();
#if POWER_HAS_ACTIVE
root["dczVoltIdx"] = getSetting("dczVoltIdx").toInt();
#endif
#endif
#endif
#if DS18B20_SUPPORT
root["dsVisible"] = 1;
root["dsTmp"] = getDSTemperatureStr();
#endif
#if DHT_SUPPORT
root["dhtVisible"] = 1;
root["dhtTmp"] = getDHTTemperature();
root["dhtHum"] = getDHTHumidity();
#endif
#if RF_SUPPORT
root["rfVisible"] = 1;
root["rfChannel"] = getSetting("rfChannel", RF_CHANNEL);
root["rfDevice"] = getSetting("rfDevice", RF_DEVICE);
#endif
#if POWER_PROVIDER != POWER_PROVIDER_NONE
root["pwrVisible"] = 1;
root["pwrCurrent"] = getCurrent();
root["pwrVoltage"] = getVoltage();
root["pwrApparent"] = getApparentPower();
root["pwrEnergy"] = getPowerEnergy();
root["pwrReadEvery"] = powerReadInterval();
root["pwrReportEvery"] = powerReportInterval();
#if POWER_HAS_ACTIVE
root["pwrActive"] = getActivePower();
root["pwrReactive"] = getReactivePower();
root["pwrFactor"] = int(100 * getPowerFactor());
#endif
#if (POWER_PROVIDER == POWER_PROVIDER_EMON_ANALOG) || (POWER_PROVIDER == POWER_PROVIDER_EMON_ADC121)
root["emonVisible"] = 1;
#endif
#if POWER_PROVIDER == POWER_PROVIDER_HLW8012
root["hlwVisible"] = 1;
#endif
#if POWER_PROVIDER == POWER_PROVIDER_V9261F
root["v9261fVisible"] = 1;
#endif
#if POWER_PROVIDER == POWER_PROVIDER_ECH1560
root["ech1560fVisible"] = 1;
#endif
#endif
#if NOFUSS_SUPPORT
root["nofussVisible"] = 1;
root["nofussEnabled"] = getSetting("nofussEnabled", NOFUSS_ENABLED).toInt() == 1;
root["nofussServer"] = getSetting("nofussServer", NOFUSS_SERVER);
#endif
#ifdef ITEAD_SONOFF_RFBRIDGE
root["rfbVisible"] = 1;
root["rfbCount"] = relayCount();
JsonArray& rfb = root.createNestedArray("rfb");
for (byte id=0; id<relayCount(); id++) {
for (byte status=0; status<2; status++) {
JsonObject& node = rfb.createNestedObject();
node["id"] = id;
node["status"] = status;
node["data"] = rfbRetrieve(id, status == 1);
}
}
#endif
#if TELNET_SUPPORT
root["telnetVisible"] = 1;
root["telnetSTA"] = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
#endif
root["maxNetworks"] = WIFI_MAX_NETWORKS;
JsonArray& wifi = root.createNestedArray("wifi");
for (byte i=0; i<WIFI_MAX_NETWORKS; i++) {
if (getSetting("ssid" + String(i)).length() == 0) break;
JsonObject& network = wifi.createNestedObject();
network["ssid"] = getSetting("ssid" + String(i));
network["pass"] = getSetting("pass" + String(i));
network["ip"] = getSetting("ip" + String(i));
network["gw"] = getSetting("gw" + String(i));
network["mask"] = getSetting("mask" + String(i));
network["dns"] = getSetting("dns" + String(i));
}
// Module setters
for (unsigned char i = 0; i < _ws_sender_callbacks.size(); i++) {
(_ws_sender_callbacks[i])(root);
}
}
String output;
root.printTo(output);
wsSend(client_id, (char *) output.c_str());
}
void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
if (type == WS_EVT_CONNECT) {
IPAddress ip = client->remoteIP();
DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u connected, ip: %d.%d.%d.%d, url: %s\n"), client->id(), ip[0], ip[1], ip[2], ip[3], server->url());
_wsStart(client->id());
client->_tempObject = new WebSocketIncommingBuffer(&_wsParse, true);
wifiReconnectCheck();
} else if(type == WS_EVT_DISCONNECT) {
DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u disconnected\n"), client->id());
if (client->_tempObject) {
delete (WebSocketIncommingBuffer *) client->_tempObject;
}
wifiReconnectCheck();
} else if(type == WS_EVT_ERROR) {
DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u error(%u): %s\n"), client->id(), *((uint16_t*)arg), (char*)data);
} else if(type == WS_EVT_PONG) {
DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u pong(%u): %s\n"), client->id(), len, len ? (char*) data : "");
} else if(type == WS_EVT_DATA) {
//DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u data(%u): %s\n"), client->id(), len, len ? (char*) data : "");
WebSocketIncommingBuffer *buffer = (WebSocketIncommingBuffer *)client->_tempObject;
AwsFrameInfo * info = (AwsFrameInfo*)arg;
buffer->data_event(client, info, data, len);
}
}
// -----------------------------------------------------------------------------
// Piblic API
// -----------------------------------------------------------------------------
bool wsConnected() {
return (_ws.count() > 0);
}
void wsRegister(ws_callback_f sender, ws_callback_f receiver) {
_ws_sender_callbacks.push_back(sender);
if (receiver) _ws_receiver_callbacks.push_back(receiver);
}
void wsSend(ws_callback_f sender) {
if (_ws.count() > 0) {
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
sender(root);
String output;
root.printTo(output);
wsSend((char *) output.c_str());
}
}
void wsSend(const char * payload) {
if (_ws.count() > 0) {
_ws.textAll(payload);
}
}
void wsSend_P(PGM_P payload) {
if (_ws.count() > 0) {
char buffer[strlen_P(payload)];
strcpy_P(buffer, payload);
_ws.textAll(buffer);
}
}
void wsSend(uint32_t client_id, const char * payload) {
_ws.text(client_id, payload);
}
void wsSend_P(uint32_t client_id, PGM_P payload) {
char buffer[strlen_P(payload)];
strcpy_P(buffer, payload);
_ws.text(client_id, buffer);
}
void wsConfigure() {
_ws.setAuthentication(WEB_USERNAME, (const char *) getSetting("adminPass", ADMIN_PASS).c_str());
}
void wsSetup() {
_ws.onEvent(_wsEvent);
wsConfigure();
webServer()->addHandler(&_ws);
mqttRegister(_wsMQTTCallback);
}
#endif // WEB_SUPPORT

+ 1
- 35
code/html/custom.js View File

@ -21,7 +21,6 @@ function initMessages() {
messages[03] = "Error parsing data!"; messages[03] = "Error parsing data!";
messages[04] = "The file does not look like a valid configuration backup or is corrupted"; messages[04] = "The file does not look like a valid configuration backup or is corrupted";
messages[05] = "Changes saved. You should reboot your board now"; messages[05] = "Changes saved. You should reboot your board now";
messages[06] = "Home Assistant auto-discovery message sent";
messages[07] = "Passwords do not match!"; messages[07] = "Passwords do not match!";
messages[08] = "Changes saved"; messages[08] = "Changes saved";
messages[09] = "No changes detected"; messages[09] = "No changes detected";
@ -91,24 +90,6 @@ function generateAPIKey() {
return false; return false;
} }
function forgetCredentials() {
$.ajax({
'method': 'GET',
'url': '/',
'async': false,
'username': "logmeout",
'password': "123456",
'headers': { "Authorization": "Basic xxx" }
}).done(function(data) {
return false;
// If we don't get an error, we actually got an error as we expect an 401!
}).fail(function(){
// We expect to get an 401 Unauthorized error! In this case we are successfully
// logged out and we redirect the user.
return true;
});
}
function getJson(str) { function getJson(str) {
try { try {
return JSON.parse(str); return JSON.parse(str);
@ -626,14 +607,12 @@ function processData(data) {
password = data.webMode == 1; password = data.webMode == 1;
$("#layout").toggle(data.webMode == 0); $("#layout").toggle(data.webMode == 0);
$("#password").toggle(data.webMode == 1); $("#password").toggle(data.webMode == 1);
$("#credentials").hide();
} }
// Actions // Actions
if (key == "action") { if (key == "action") {
if (data.action == "reload") { if (data.action == "reload") {
if (password) forgetCredentials();
doReload(1000); doReload(1000);
} }
@ -954,24 +933,11 @@ function init() {
$(".button-add-network").on('click', function() { $(".button-add-network").on('click', function() {
$("div.more", addNetwork()).toggle(); $("div.more", addNetwork()).toggle();
}); });
$(".button-ha-add").on('click', function() {
websock.send(JSON.stringify({'action': 'ha_add', 'data': $("input[name='haPrefix']").val()}));
});
$(".button-ha-del").on('click', function() {
websock.send(JSON.stringify({'action': 'ha_del', 'data': $("input[name='haPrefix']").val()}));
});
$(document).on('change', 'input', hasChanged); $(document).on('change', 'input', hasChanged);
$(document).on('change', 'select', hasChanged); $(document).on('change', 'select', hasChanged);
$.ajax({
'method': 'GET',
'url': window.location.href + 'auth'
}).done(function(data) {
connect();
}).fail(function(){
$("#credentials").show();
});
connect();
} }


+ 17
- 14
code/html/index.html View File

@ -21,10 +21,6 @@
<body> <body>
<div id="credentials" class="webmode">
Wrong credentials
</div>
<div id="password" class="webmode"> <div id="password" class="webmode">
<div class="content"> <div class="content">
@ -416,24 +412,25 @@
</div> </div>
<div class="pure-g module module-ha"> <div class="pure-g module module-ha">
<label class="pure-u-1 pure-u-md-1-4" for="haPrefix">Home Assistant Prefix</label>
<input class="pure-u-1 pure-u-md-1-4" name="haPrefix" type="text" tabindex="13" />
<div class="pure-u-1-2 pure-u-md-1-8"><button type="button" class="pure-u-23-24 pure-button button-ha-add">Add</button></div>
<div class="pure-u-1-2 pure-u-md-1-8"><button type="button" class="pure-u-23-24 pure-button button-ha-del">Delete</button></div>
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-sm-1-4"><label for="haEnabled">Home Assistant</label></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="checkbox" name="haEnabled" tabindex="13" /></div>
<div class="pure-u-0 pure-u-md-1-2">&nbsp;</div>
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div> <div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint"> <div class="pure-u-1 pure-u-md-3-4 hint">
Home Assistant auto-discovery feature.<br />
Add should immediately add the device to your HA console. Messages are retained so the device should be there even after a HA reboot<br />
To remove the device click on the Del button (retained message will be deleted) and reboot HA.<br />
Home Assistant auto-discovery feature. Enable and save to add the device to your HA console.
You might want to disable CSS style (above) so Home Assistant can parse the color. You might want to disable CSS style (above) so Home Assistant can parse the color.
</div> </div>
</div> </div>
<div class="pure-g module module-ha">
<label class="pure-u-1 pure-u-md-1-4" for="haPrefix">Home Assistant Prefix</label>
<input class="pure-u-1 pure-u-md-1-4" name="haPrefix" type="text" tabindex="14" />
</div>
<div class="pure-g module module-ds module-dht"> <div class="pure-g module module-ds module-dht">
<label class="pure-u-1 pure-u-sm-1-4" for="tmpUnits">Temperature units</label> <label class="pure-u-1 pure-u-sm-1-4" for="tmpUnits">Temperature units</label>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="14" value="0"> Celsius (&deg;C)</input></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="15" value="1"> Fahrenheit (&deg;F)</input></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="15" value="0"> Celsius (&deg;C)</input></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="16" value="1"> Fahrenheit (&deg;F)</input></div>
</div> </div>
<div class="pure-g module module-ds module-dht"> <div class="pure-g module module-ds module-dht">
@ -719,6 +716,12 @@
<div class="pure-u-1 pure-u-sm-1-4"><input type="checkbox" name="dczEnabled" tabindex="30" /></div> <div class="pure-u-1 pure-u-sm-1-4"><input type="checkbox" name="dczEnabled" tabindex="30" /></div>
</div> </div>
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="dczSkip">Anti-recursion time</label>
<div class="pure-u-1 pure-u-sm-1-8"><input class="pure-u-sm-23-24" name="dczSkip" type="number" min="0" max="10" tabindex="31" /></div>
<div class="pure-u-1 pure-u-sm-5-8 hint center">Skips in/out messages from the same IDX within this time in seconds</div>
</div>
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="dczTopicIn">Domoticz IN Topic</label> <label class="pure-u-1 pure-u-md-1-4" for="dczTopicIn">Domoticz IN Topic</label>
<input class="pure-u-1 pure-u-md-3-4" name="dczTopicIn" type="text" tabindex="31" /> <input class="pure-u-1 pure-u-md-3-4" name="dczTopicIn" type="text" tabindex="31" />


+ 31
- 7
code/platformio.ini View File

@ -1,21 +1,19 @@
[platformio] [platformio]
env_default = nodemcu-lolin
env_default = wemos-d1mini-relayshield
src_dir = espurna src_dir = espurna
data_dir = espurna/data data_dir = espurna/data
[common] [common]
platform = espressif8266
#platform = espressif8266_stage
#platform = espressif8266
platform = espressif8266_stage
build_flags = -g -DMQTT_MAX_PACKET_SIZE=400 ${env.ESPURNA_FLAGS} build_flags = -g -DMQTT_MAX_PACKET_SIZE=400 ${env.ESPURNA_FLAGS}
debug_flags = -DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM debug_flags = -DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM
build_flags_512k = ${common.build_flags} -Wl,-Tesp8266.flash.512k0.ld build_flags_512k = ${common.build_flags} -Wl,-Tesp8266.flash.512k0.ld
build_flags_1m = ${common.build_flags} -Wl,-Tesp8266.flash.1m0.ld build_flags_1m = ${common.build_flags} -Wl,-Tesp8266.flash.1m0.ld
#https://github.com/me-no-dev/ESPAsyncTCP#9b0cc37 // 2.3.0 compatible
#https://github.com/me-no-dev/ESPAsyncTCP#289a681 // 2.4.0-rc2 compatible
lib_deps = lib_deps =
https://github.com/xoseperez/Time https://github.com/xoseperez/Time
ArduinoJson ArduinoJson
https://github.com/me-no-dev/ESPAsyncTCP#9b0cc37
https://github.com/me-no-dev/ESPAsyncTCP#a57560d
https://github.com/me-no-dev/ESPAsyncWebServer#313f337 https://github.com/me-no-dev/ESPAsyncWebServer#313f337
https://github.com/marvinroger/async-mqtt-client#v0.8.1 https://github.com/marvinroger/async-mqtt-client#v0.8.1
PubSubClient PubSubClient
@ -31,11 +29,13 @@ lib_deps =
https://bitbucket.org/xoseperez/nofuss.git#0.2.5 https://bitbucket.org/xoseperez/nofuss.git#0.2.5
https://bitbucket.org/xoseperez/emonliteesp.git#0.2.0 https://bitbucket.org/xoseperez/emonliteesp.git#0.2.0
https://bitbucket.org/xoseperez/debounceevent.git#2.0.1 https://bitbucket.org/xoseperez/debounceevent.git#2.0.1
https://github.com/xoseperez/my9291#2.0.0
https://github.com/xoseperez/my92xx#3.0.0
https://github.com/xoseperez/RemoteSwitch-arduino-library.git https://github.com/xoseperez/RemoteSwitch-arduino-library.git
https://github.com/FastLED/FastLED#v3.1.6 https://github.com/FastLED/FastLED#v3.1.6
https://github.com/markszabo/IRremoteESP8266#v2.2.0 https://github.com/markszabo/IRremoteESP8266#v2.2.0
lib_ignore = lib_ignore =
#extra_scripts = post:core_version.py
extra_scripts =
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -48,6 +48,7 @@ lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags} -DWEMOS_D1_MINI_RELAYSHIELD -DDEBUG_FAUXMO=Serial -DNOWSAUTH build_flags = ${common.build_flags} -DWEMOS_D1_MINI_RELAYSHIELD -DDEBUG_FAUXMO=Serial -DNOWSAUTH
upload_speed = 460800 upload_speed = 460800
monitor_baud = 115200 monitor_baud = 115200
extra_scripts = ${common.extra_scripts}
[env:wemos-d1mini-relayshield-ssl] [env:wemos-d1mini-relayshield-ssl]
platform = espressif8266_stage platform = espressif8266_stage
@ -229,6 +230,29 @@ upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266 upload_flags = --auth=fibonacci --port 8266
monitor_baud = 115200 monitor_baud = 115200
[env:itead-sonoff-th]
platform = ${common.platform}
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DITEAD_SONOFF_TH
monitor_baud = 115200
[env:itead-sonoff-th-ota]
platform = ${common.platform}
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DITEAD_SONOFF_TH
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
monitor_baud = 115200
[env:itead-sonoff-pow] [env:itead-sonoff-pow]
platform = ${common.platform} platform = ${common.platform}
framework = arduino framework = arduino


Loading…
Cancel
Save