Browse Source

Merge pull request #3 from xoseperez/dev

synching to upstream
ota
ColinShorts 6 years ago
committed by GitHub
parent
commit
466bc06ab7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 3819 additions and 3351 deletions
  1. +1
    -1
      README.md
  2. +41
    -0
      code/.github/stale.yml
  3. +3
    -0
      code/espurna/config/arduino.h
  4. +1
    -1
      code/espurna/config/defaults.h
  5. +11
    -7
      code/espurna/config/general.h
  6. +128
    -2
      code/espurna/config/hardware.h
  7. +4
    -0
      code/espurna/config/sensors.h
  8. +1
    -1
      code/espurna/config/version.h
  9. BIN
      code/espurna/data/index.html.gz
  10. +12
    -0
      code/espurna/domoticz.ino
  11. +1
    -1
      code/espurna/influxdb.ino
  12. +1
    -1
      code/espurna/mdns.ino
  13. +51
    -0
      code/espurna/migrate.ino
  14. +21
    -6
      code/espurna/relay.ino
  15. +9
    -3
      code/espurna/sensors/CSE7766Sensor.h
  16. +5
    -0
      code/espurna/settings.ino
  17. +3246
    -3278
      code/espurna/static/index.html.gz.h
  18. +3
    -1
      code/espurna/uartmqtt.ino
  19. +7
    -3
      code/espurna/utils.ino
  20. +1
    -1
      code/espurna/wifi.ino
  21. +10
    -3
      code/espurna/ws.ino
  22. +2
    -0
      code/gulpfile.js
  23. +32
    -11
      code/html/custom.js
  24. +6
    -4
      code/html/index.html
  25. +2
    -2
      code/html/vendor/pure-1.0.0.min.css
  26. +2
    -2
      code/html/vendor/pure-grids-responsive-1.0.0.min.css
  27. +28
    -21
      code/ota.py
  28. +115
    -0
      code/package-lock.json
  29. +1
    -0
      code/package.json
  30. +74
    -2
      code/platformio.ini

+ 1
- 1
README.md View File

@ -3,7 +3,7 @@
ESPurna ("spark" in Catalan) is a custom firmware for ESP8285/ESP8266 based smart switches, lights and sensors.
It uses the Arduino Core for ESP8266 framework and a number of 3rd party libraries.
[![version](https://img.shields.io/badge/version-1.12.6-brightgreen.svg)](CHANGELOG.md)
[![version](https://img.shields.io/badge/version-1.12.7a-brightgreen.svg)](CHANGELOG.md)
[![branch](https://img.shields.io/badge/branch-dev-orange.svg)](https://github.org/xoseperez/espurna/tree/dev/)
[![travis](https://travis-ci.org/xoseperez/espurna.svg?branch=dev)](https://travis-ci.org/xoseperez/espurna)
[![codacy](https://img.shields.io/codacy/grade/c9496e25cf07434cba786b462cb15f49/dev.svg)](https://www.codacy.com/app/xoseperez/espurna/dashboard)


+ 41
- 0
code/.github/stale.yml View File

@ -0,0 +1,41 @@
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 120
# Number of days of inactivity before a stale Issue or Pull Request is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 30
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- enhancement
- bug
- staged for release
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Label to use when marking as stale
staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed in 30 days if no further activity occurs.
Thank you for your contributions.
# Comment to post when removing the stale label.
# unmarkComment: >
# Your comment here.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This issue will be auto-closed because there hasn't been any activity for a few months. Feel free to open a new one if you still experience this problem.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Limit to only `issues` or `pulls`
only: issues

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

@ -82,6 +82,9 @@
//#define LUANI_HVIO
//#define ALLNET_4DUINO_IOT_WLAN_RELAIS
//#define TONBUX_MOSQUITO_KILLER
//#define NEO_COOLCAM_POWER_PLUG_WIFI
//#define ESTINK_WIFI_POWER_STRIP
//#define PILOTAK_ESP_DIN_V1
//--------------------------------------------------------------------------------
// Features (values below are non-default values)


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

@ -399,7 +399,7 @@
// General
// -----------------------------------------------------------------------------
// Default hostname will be ESPURNA_XXXXXX, where XXXXXX is last 3 octets of chipID
// Default hostname will be ESPURNA-XXXXXX, where XXXXXX is last 3 octets of chipID
#ifndef HOSTNAME
#define HOSTNAME ""
#endif


+ 11
- 7
code/espurna/config/general.h View File

@ -511,6 +511,10 @@
#define UART_MQTT_BAUDRATE 115200 // Serial speed
#endif
#ifndef UART_MQTT_TERMINATION
#define UART_MQTT_TERMINATION '\n' // Termination character
#endif
#define UART_MQTT_BUFFER_SIZE 100 // UART buffer size
// -----------------------------------------------------------------------------
@ -558,9 +562,9 @@
#ifndef MQTT_AUTOCONNECT
#define MQTT_AUTOCONNECT 1 // If enabled and MDNS_SERVER_SUPPORT=1 will perform an autodiscover and
// autoconnect to the first MQTT broker found if none defined
#endif
// autoconnect to the first MQTT broker found if none defined
#ifndef MQTT_SERVER
#define MQTT_SERVER "" // Default MQTT broker address
#endif
@ -590,7 +594,7 @@
#endif
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 30 // MQTT keepalive value
#define MQTT_KEEPALIVE 300 // MQTT keepalive value
#endif
@ -1148,13 +1152,13 @@
#if IR_BUTTON_SET == 3
/*
+------+------+------+
| 1 | 2 | 3 |
| 1 | 2 | 3 |
+------+------+------+
| 4 | 5 | 6 |
| 4 | 5 | 6 |
+------+------+------+
| 7 | 8 | 9 |
| 7 | 8 | 9 |
+------+------+------+
| | 0 | |
| | 0 | |
+------+------+------+
*/
#define IR_BUTTON_COUNT 10
@ -1164,7 +1168,7 @@
{ 0xE0E020DF, IR_BUTTON_MODE_TOGGLE, 0 }, // Toggle Relay #0
{ 0xE0E0A05F, IR_BUTTON_MODE_TOGGLE, 1 }, // Toggle Relay #1
{ 0xE0E0609F, IR_BUTTON_MODE_TOGGLE, 2 }, // Toggle Relay #2
{ 0xE0E010EF, IR_BUTTON_MODE_TOGGLE, 3 }, // Toggle Relay #3
{ 0xE0E0906F, IR_BUTTON_MODE_TOGGLE, 4 }, // Toggle Relay #4
{ 0xE0E050AF, IR_BUTTON_MODE_TOGGLE, 5 }, // Toggle Relay #5


+ 128
- 2
code/espurna/config/hardware.h View File

@ -395,8 +395,11 @@
#define RELAY1_TYPE RELAY_TYPE_NORMAL
// LEDs
#define LED1_PIN 15
#define LED1_PIN_INVERSE 0
#define LED1_PIN 13
#define LED1_PIN_INVERSE 1
// Disable UART noise
#define DEBUG_SERIAL_SUPPORT 0
// CSE7766
#ifndef CSE7766_SUPPORT
@ -1942,6 +1945,52 @@
#endif
#define DALLAS_PIN 2
// -----------------------------------------------------------------------------
// ESP-DIN relay board V1
// https://github.com/pilotak/esp_din
// -----------------------------------------------------------------------------
#elif defined(PILOTAK_ESP_DIN_V1)
// Info
#define MANUFACTURER "PILOTAK"
#define DEVICE "ESP_DIN_V1"
// Buttons
#define BUTTON1_PIN 0
#define BUTTON1_RELAY 1
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
// Relays
#define RELAY1_PIN 4
#define RELAY1_TYPE RELAY_TYPE_NORMAL
#define RELAY2_PIN 5
#define RELAY2_TYPE RELAY_TYPE_NORMAL
// LEDs
#define LED1_PIN 15
#define LED1_PIN_INVERSE 0
#define I2C_SDA_PIN 12
#define I2C_SCL_PIN 13
#ifndef DALLAS_SUPPORT
#define DALLAS_SUPPORT 1
#endif
#define DALLAS_PIN 2
#ifndef RF_SUPPORT
#define RF_SUPPORT 1
#endif
#define RF_PIN 14
#ifndef DIGITAL_SUPPORT
#define DIGITAL_SUPPORT 1
#endif
#define DIGITAL_PIN 16
#define DIGITAL_PIN_MODE INPUT
// -----------------------------------------------------------------------------
// Heltec Touch Relay
// https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20180408043114&SearchText=esp8266+touch+relay
@ -2108,6 +2157,83 @@
#define LED4_PIN_INVERSE 0
#define LED4_RELAY 1
// -----------------------------------------------------------------------------
// NEO Coolcam Power Plug
// https://es.aliexpress.com/item/-/32854589733.html?spm=a219c.12010608.0.0.6d084e68xX0y5N
// -----------------------------------------------------------------------------
#elif defined(NEO_COOLCAM_POWER_PLUG_WIFI)
// Info
#define MANUFACTURER "NEO_COOLCAM"
#define DEVICE "POWER_PLUG_WIFI"
// Buttons
#define BUTTON1_PIN 13
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_RELAY 1
// Relays
#define RELAY1_PIN 12
#define RELAY1_TYPE RELAY_TYPE_NORMAL
// LEDs
#define LED1_PIN 4
#define LED1_PIN_INVERSE 1
// ------------------------------------------------------------------------------
// Estink Wifi Power Strip
// https://www.amazon.de/Steckdosenleiste-Ladeger%C3%A4t-Sprachsteuerung-SmartphonesTablets-Android/dp/B0796W5FZY
// Fornorm Wi-Fi USB Extension Socket (ZLD-34EU)
// https://www.aliexpress.com/item/Fornorm-WiFi-Extension-Socket-with-Surge-Protector-Smart-Power-Strip-3-Outlets-and-4-USB-Charging/32849743948.html
// -----------------------------------------------------------------------------
#elif defined(ESTINK_WIFI_POWER_STRIP)
// Info
#define MANUFACTURER "ESTINK"
#define DEVICE "WIFI_POWER_STRIP"
// Disable UART noise since this board uses GPIO3
#define DEBUG_SERIAL_SUPPORT 0
// Buttons
#define BUTTON1_PIN 16
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_RELAY 4
// Relays
#define RELAY1_PIN 14 // USB power
#define RELAY2_PIN 13 // power plug 1
#define RELAY3_PIN 4 // power plug 2
#define RELAY4_PIN 15 // power plug 3
#define RELAY1_TYPE RELAY_TYPE_NORMAL
#define RELAY2_TYPE RELAY_TYPE_NORMAL
#define RELAY3_TYPE RELAY_TYPE_NORMAL
#define RELAY4_TYPE RELAY_TYPE_NORMAL
// LEDs
#define LED1_PIN 0 // power led
#define LED2_PIN 12 // power plug 1
#define LED3_PIN 3 // power plug 2
#define LED4_PIN 5 // power plug 3
#define LED1_PIN_INVERSE 1
#define LED2_PIN_INVERSE 1
#define LED3_PIN_INVERSE 1
#define LED4_PIN_INVERSE 1
#define LED1_MODE LED_MODE_FINDME
#define LED2_MODE LED_MODE_FOLLOW
#define LED3_MODE LED_MODE_FOLLOW
#define LED4_MODE LED_MODE_FOLLOW
#define LED2_RELAY 2
#define LED3_RELAY 3
#define LED4_RELAY 4
// -----------------------------------------------------------------------------
// TEST boards (do not use!!)
// -----------------------------------------------------------------------------


+ 4
- 0
code/espurna/config/sensors.h View File

@ -579,6 +579,10 @@
#if SENSOR_SUPPORT
#if SENSOR_DEBUG
#include "../config/debug.h"
#endif
#include "../sensors/BaseSensor.h"
#if AM2320_SUPPORT


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

@ -1,5 +1,5 @@
#define APP_NAME "ESPURNA"
#define APP_VERSION "1.12.6"
#define APP_VERSION "1.12.7a"
#define APP_REVISION "db84006"
#define APP_AUTHOR "xose.perez@gmail.com"
#define APP_WEBSITE "http://tinkerman.cat"


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


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

@ -43,7 +43,13 @@ void _domoticzMqtt(unsigned int type, const char * topic, const char * payload)
String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
if (type == MQTT_CONNECT_EVENT) {
// Subscribe to domoticz action topics
mqttSubscribeRaw(dczTopicOut.c_str());
// Send relays state on connection
domoticzSendRelays();
}
if (type == MQTT_MESSAGE_EVENT) {
@ -138,6 +144,12 @@ void domoticzSendRelay(unsigned char relayID) {
domoticzSend(buffer, relayStatus(relayID) ? "1" : "0");
}
void domoticzSendRelays() {
for (uint8_t relayID=0; relayID < relayCount(); relayID++) {
domoticzSendRelay(relayID);
}
}
unsigned int domoticzIdx(unsigned char relayID) {
char buffer[15];
snprintf_P(buffer, sizeof(buffer), PSTR("dczRelayIdx%u"), relayID);


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

@ -92,7 +92,7 @@ bool idbSend(const char * topic, const char * payload) {
bool idbSend(const char * topic, unsigned char id, const char * payload) {
char measurement[64];
snprintf(measurement, sizeof(measurement), "%s,id=%d", topic, id);
return idbSend(topic, payload);
return idbSend(measurement, payload);
}
bool idbEnabled() {


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

@ -28,7 +28,7 @@ void _mdnsFindMQTT() {
#endif
void _mdnsServerStart() {
if (MDNS.begin(WiFi.getMode() == WIFI_AP ? APP_NAME : (char *) WiFi.hostname().c_str())) {
if (MDNS.begin((char *) getSetting("hostname").c_str())) {
DEBUG_MSG_P(PSTR("[MDNS] OK\n"));
} else {
DEBUG_MSG_P(PSTR("[MDNS] FAIL\n"));


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

@ -953,6 +953,57 @@ void migrate() {
setSetting("relayGPIO", 0, 5);
setSetting("relayType", 0, RELAY_TYPE_NORMAL);
#elif defined(NEO_COOLCAM_POWER_PLUG_WIFI)
setSetting("board", 75);
setSetting("ledGPIO", 0, 4);
setSetting("ledLogic", 0, 1);
setSetting("btnGPIO", 0, 13);
setSetting("btnRelay", 0, 0);
setSetting("relayGPIO", 0, 12);
setSetting("relayType", 0, RELAY_TYPE_NORMAL);
#elif defined(PILOTAK_ESP_DIN_V1)
setSetting("board", 76);
setSetting("ledGPIO", 0, 16);
setSetting("ledLogic", 0, 0);
setSetting("btnGPIO", 0, 0);
setSetting("btnRelay", 0, 0);
setSetting("relayGPIO", 0, 4);
setSetting("relayGPIO", 1, 5);
setSetting("relayType", 0, RELAY_TYPE_NORMAL);
setSetting("relayType", 1, RELAY_TYPE_NORMAL);
#elif defined(ESTINK_WIFI_POWER_STRIP)
setSetting("board", 76);
setSetting("btnGPIO", 0, 16);
setSetting("btnRelay", 0, 3);
setSetting("ledGPIO", 0, 0);
setSetting("ledGPIO", 1, 12);
setSetting("ledGPIO", 2, 3);
setSetting("ledGPIO", 3, 5);
setSetting("ledLogic", 0, 1);
setSetting("ledLogic", 1, 1);
setSetting("ledLogic", 2, 1);
setSetting("ledLogic", 3, 1);
setSetting("ledMode", 0, LED_MODE_FINDME);
setSetting("ledMode", 1, LED_MODE_FOLLOW);
setSetting("ledMode", 2, LED_MODE_FOLLOW);
setSetting("ledMode", 3, LED_MODE_FOLLOW);
setSetting("ledRelay", 1, 1);
setSetting("ledRelay", 2, 2);
setSetting("ledRelay", 3, 3);
setSetting("relayGPIO", 0, 14);
setSetting("relayGPIO", 1, 13);
setSetting("relayGPIO", 2, 4);
setSetting("relayGPIO", 3, 15);
setSetting("relayType", 0, RELAY_TYPE_NORMAL);
setSetting("relayType", 1, RELAY_TYPE_NORMAL);
setSetting("relayType", 2, RELAY_TYPE_NORMAL);
setSetting("relayType", 3, RELAY_TYPE_NORMAL);
#else
// Allow users to define new settings without migration config


+ 21
- 6
code/espurna/relay.ino View File

@ -23,6 +23,7 @@ typedef struct {
unsigned long delay_off; // Delay to turn relay OFF
unsigned char pulse; // RELAY_PULSE_NONE, RELAY_PULSE_OFF or RELAY_PULSE_ON
unsigned long pulse_ms; // Pulse length in millis
unsigned long pulse_start; // Current pulse start (millis), 0 means no pulse
// Status variables
@ -34,10 +35,6 @@ typedef struct {
bool report; // Whether to report to own topic
bool group_report; // Whether to report to group topic
// Helping objects
Ticker pulseTicker; // Holds the pulse back timer
} relay_t;
std::vector<relay_t> _relays;
bool _relayRecursive = false;
@ -203,6 +200,21 @@ void _relayProcess(bool mode) {
}
/**
* Walks the relay vector check if any relay has to pulse back
*/
void _relayPulseCheck() {
unsigned long current_time = millis();
for (unsigned char id = 0; id < _relays.size(); id++) {
if (_relays[id].pulse_start > 0) {
if (current_time - _relays[id].pulse_start > _relays[id].pulse_ms) {
_relays[id].pulse_start = 0;
relayToggle(id);
}
}
}
}
// -----------------------------------------------------------------------------
// RELAY
// -----------------------------------------------------------------------------
@ -218,9 +230,10 @@ void relayPulse(unsigned char id) {
bool pulseStatus = (mode == RELAY_PULSE_ON);
if (pulseStatus == status) {
_relays[id].pulseTicker.detach();
_relays[id].pulse_start = 0;
} else {
_relays[id].pulseTicker.once_ms(ms, relayToggle, id);
DEBUG_MSG_P(PSTR("[RELAY] Scheduling relay #%d back in %lums (pulse)\n"), id, ms);
_relays[id].pulse_start = millis();
}
}
@ -463,6 +476,7 @@ void _relayBoot() {
}
_relays[i].current_status = !status;
_relays[i].target_status = status;
_relays[i].pulse_start = 0;
#if RELAY_PROVIDER == RELAY_PROVIDER_STM
_relays[i].change_time = millis() + 3000 + 1000 * i;
#else
@ -820,6 +834,7 @@ void _relayInitCommands() {
//------------------------------------------------------------------------------
void _relayLoop() {
_relayPulseCheck();
_relayProcess(false);
_relayProcess(true);
}


+ 9
- 3
code/espurna/sensors/CSE7766Sensor.h View File

@ -270,10 +270,16 @@ class CSE7766Sensor : public BaseSensor {
}
// Calculate energy
static unsigned long cf_pulses_last = 0;
unsigned long cf_pulses = _data[21] << 8 | _data[22];
unsigned int difference;
static unsigned int cf_pulses_last = 0;
unsigned int cf_pulses = _data[21] << 8 | _data[22];
if (0 == cf_pulses_last) cf_pulses_last = cf_pulses;
_energy += (cf_pulses - cf_pulses_last) * (float) _coefP / 1000000.0;
if (cf_pulses < cf_pulses_last) {
difference = cf_pulses + (0xFFFF - cf_pulses_last) + 1;
} else {
difference = cf_pulses - cf_pulses_last;
}
_energy += difference * (float) _coefP / 1000000.0;
cf_pulses_last = cf_pulses;
}


+ 5
- 0
code/espurna/settings.ino View File

@ -269,6 +269,11 @@ void _settingsInitCommands() {
DEBUG_MSG_P(PSTR("+OK\n"));
});
settingsRegisterCommand(F("RELOAD"), [](Embedis* e) {
wsReload();
DEBUG_MSG_P(PSTR("+OK\n"));
});
settingsRegisterCommand(F("RESET"), [](Embedis* e) {
DEBUG_MSG_P(PSTR("+OK\n"));
deferredReset(100, CUSTOM_RESET_TERMINAL);


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


+ 3
- 1
code/espurna/uartmqtt.ino View File

@ -32,15 +32,17 @@ void _uartmqttReceiveUART() {
char rc = UART_MQTT_PORT.read();
if (rc != '\n') {
if (rc != UART_MQTT_TERMINATION) {
_uartmqttBuffer[ndx] = rc;
if (ndx < UART_MQTT_BUFFER_SIZE - 1) ndx++;
} else {
_uartmqttBuffer[ndx] = '\0';
_uartmqttNewData = true;
ndx = 0;
}
}


+ 7
- 3
code/espurna/utils.ino View File

@ -11,7 +11,7 @@ Ticker _defer_reset;
String getIdentifier() {
char buffer[20];
snprintf_P(buffer, sizeof(buffer), PSTR("%s_%06X"), APP_NAME, ESP.getChipId());
snprintf_P(buffer, sizeof(buffer), PSTR("%s-%06X"), APP_NAME, ESP.getChipId());
return String(buffer);
}
@ -52,6 +52,10 @@ String getCoreRevision() {
#endif
}
unsigned long maxSketchSpace() {
return (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
}
// WTF
// Calling ESP.getFreeHeap() is making the system crash on a specific
// AiLight bulb, but anywhere else...
@ -235,9 +239,9 @@ void info() {
DEBUG_MSG_P(PSTR("[INIT] Flash size (CHIP): %8u bytes\n"), ESP.getFlashChipRealSize());
DEBUG_MSG_P(PSTR("[INIT] Flash size (SDK): %8u bytes / %4d sectors\n"), ESP.getFlashChipSize(), sectors(ESP.getFlashChipSize()));
DEBUG_MSG_P(PSTR("[INIT] Firmware size: %8u bytes / %4d sectors\n"), ESP.getSketchSize(), sectors(ESP.getSketchSize()));
DEBUG_MSG_P(PSTR("[INIT] OTA size: %8u bytes / %4d sectors\n"), ESP.getFreeSketchSpace(), sectors(ESP.getFreeSketchSpace()));
DEBUG_MSG_P(PSTR("[INIT] Max OTA size: %8u bytes / %4d sectors\n"), maxSketchSpace(), sectors(maxSketchSpace()));
DEBUG_MSG_P(PSTR("[INIT] EEPROM size: %8u bytes / %4d sectors\n"), settingsMaxSize(), sectors(settingsMaxSize()));
DEBUG_MSG_P(PSTR("[INIT] Empty space: %8u bytes / 4 sectors\n"), 4 * SPI_FLASH_SEC_SIZE);
DEBUG_MSG_P(PSTR("[INIT] Empty space: %8u bytes / 4 sectors\n"), 4 * SPI_FLASH_SEC_SIZE);
DEBUG_MSG_P(PSTR("\n"));
// -------------------------------------------------------------------------


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

@ -417,7 +417,7 @@ void wifiStatus() {
DEBUG_MSG_P(PSTR("[WIFI] GW %s\n"), WiFi.gatewayIP().toString().c_str());
DEBUG_MSG_P(PSTR("[WIFI] DNS %s\n"), WiFi.dnsIP().toString().c_str());
DEBUG_MSG_P(PSTR("[WIFI] MASK %s\n"), WiFi.subnetMask().toString().c_str());
DEBUG_MSG_P(PSTR("[WIFI] HOST %s\n"), WiFi.hostname().c_str());
DEBUG_MSG_P(PSTR("[WIFI] HOST http://%s.local\n"), WiFi.hostname().c_str());
DEBUG_MSG_P(PSTR("[WIFI] BSSID %02X:%02X:%02X:%02X:%02X:%02X\n"),
bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], bssid[6]
);


+ 10
- 3
code/espurna/ws.ino View File

@ -205,9 +205,7 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) {
if (save) {
// Callbacks
for (unsigned char i = 0; i < _ws_on_after_parse_callbacks.size(); i++) {
(_ws_on_after_parse_callbacks[i])();
}
wsReload();
// This should got to callback as well
// but first change management has to be in place
@ -434,6 +432,15 @@ void wsConfigure() {
#endif
}
// This method being public makes
// _ws_on_after_parse_callbacks strange here,
// it should belong somewhere else.
void wsReload() {
for (unsigned char i = 0; i < _ws_on_after_parse_callbacks.size(); i++) {
(_ws_on_after_parse_callbacks[i])();
}
}
void wsSetup() {
_ws.onEvent(_wsEvent);
wsConfigure();


+ 2
- 0
code/gulpfile.js View File

@ -38,6 +38,7 @@ const htmllint = require('gulp-htmllint');
const log = require('fancy-log');
const csslint = require('gulp-csslint');
const crass = require('gulp-crass');
const replace = require('gulp-replace');
const dataFolder = 'espurna/data/';
const staticFolder = 'espurna/static/';
@ -126,6 +127,7 @@ gulp.task('buildfs_inline', function() {
minifyCSS: true,
minifyJS: true
})).
pipe(replace('pure-', 'p-')).
pipe(gzip()).
pipe(gulp.dest(dataFolder));
});


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

@ -102,14 +102,6 @@ function keepTime() {
}
// http://www.the-art-of-web.com/javascript/validate-password/
function checkPassword(str) {
// at least one lowercase and one uppercase letter or number
// at least five characters (letters, numbers or special characters)
var re = /^(?=.*[A-Z\d])(?=.*[a-z])[\w~!@#$%^&*\(\)<>,.\?;:{}\[\]\\|]{5,}$/;
return re.test(str);
}
function zeroPad(number, positions) {
var zeros = "";
for (var i = 0; i < positions; i++) {
@ -147,9 +139,14 @@ function loadTimeZones() {
function validateForm(form) {
// http://www.the-art-of-web.com/javascript/validate-password/
// at least one lowercase and one uppercase letter or number
// at least five characters (letters, numbers or special characters)
var re_password = /^(?=.*[A-Z\d])(?=.*[a-z])[\w~!@#$%^&*\(\)<>,.\?;:{}\[\]\\|]{5,}$/;
// password
var adminPass1 = $("input[name='adminPass']", form).first().val();
if (adminPass1.length > 0 && !checkPassword(adminPass1)) {
if (adminPass1.length > 0 && !re_password.test(adminPass1)) {
alert("The password you have entered is not valid, it must have at least 5 characters, 1 lowercase and 1 uppercase or number!");
return false;
}
@ -160,6 +157,23 @@ function validateForm(form) {
return false;
}
// RFCs mandate that a hostname's labels may contain only
// the ASCII letters 'a' through 'z' (case-insensitive),
// the digits '0' through '9', and the hyphen.
// Hostname labels cannot begin or end with a hyphen.
// No other symbols, punctuation characters, or blank spaces are permitted.
// Negative lookbehind does not work in Javascript
// var re_hostname = new RegExp('^(?!-)[A-Za-z0-9-]{1,32}(?<!-)$');
var re_hostname = new RegExp('^(?!-)[A-Za-z0-9-]{0,31}[A-Za-z0-9]$');
var hostname = $("input[name='hostname']", form).val();
if (!re_hostname.test(hostname)) {
alert("Hostname cannot be empty and may only contain the ASCII letters ('A' through 'Z' and 'a' through 'z'), the digits '0' through '9', and the hyphen ('-')! They can neither start or end with an hyphen.");
return false;
}
return true;
}
@ -874,8 +888,9 @@ function initChannels(num) {
};
// add templates
var i = 0;
var template = $("#channelTemplate").children();
for (var i=0; i<max; i++) {
for (i=0; i<max; i++) {
var channel_id = start + i;
var line = $(template).clone();
@ -887,7 +902,7 @@ function initChannels(num) {
}
for (var i=0; i<num; i++) {
for (i=0; i<num; i++) {
$("select.islight").append(
$("<option></option>").attr("value",i).text("Channel #" + i));
}
@ -1214,6 +1229,12 @@ function processData(data) {
return;
}
if ("deviceip" === key) {
var a_href = $("span[name='" + key + "']").parent();
a_href.attr("href", "http://" + value);
a_href.next().attr("href", "telnet://" + value);
}
if ("now" === key) {
now = value;
return;


+ 6
- 4
code/html/index.html View File

@ -242,7 +242,7 @@
<div class="pure-u-11-24"><span class="right" name="rssi"></span></div>
<div class="pure-u-1-2">IP</div>
<div class="pure-u-11-24"><span class="right" name="deviceip"></span></div>
<div class="pure-u-11-24"><a href=""><span class="right" name="deviceip"></span></a> (<a href=""><span class="right">telnet</span></a>)</div>
<div class="pure-u-1-2">Free heap</div>
<div class="pure-u-11-24"><span class="right" name="heap" post=" bytes"></span></div>
@ -295,7 +295,9 @@
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">
This name will identify this device in your network (http://&lt;hostname&gt;.local). For this setting to take effect you should restart the wifi interface by clicking the "Reconnect" button.
This name will identify this device in your network (http://&lt;hostname&gt;.local).<br />
Hostname may contain only the ASCII letters 'a' through 'z' (in a case-insensitive manner), the digits '0' through '9', and the hyphen ('-'). They can neither start or end with an hyphen.<br />
For this setting to take effect you should restart the wifi interface by clicking the "Reconnect" button.
</div>
</div>
@ -1334,7 +1336,7 @@
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Pulse time (s)</label></div>
<div class="pure-u-1 pure-u-lg-1-4"><input name="relayTime" class="pure-u-1" type="number" min="0" step="0.1" max="3600" /></div>
<div class="pure-u-1 pure-u-lg-1-4"><input name="relayTime" class="pure-u-1" type="number" min="0" step="0.1" max="86400" /></div>
</div>
<div class="pure-g module module-mqtt">
<div class="pure-u-1 pure-u-lg-1-4"><label>MQTT group</label></div>
@ -1349,7 +1351,7 @@
</div>
<div class="pure-g module module-mqtt">
<div class="pure-u-1 pure-u-lg-1-4"><label>On MQTT disconnect</label></div>
<select class="pure-u-1 pure-u-lg-3-4" name="mqttOnDisc">
<select class="pure-u-1 pure-u-lg-3-4" name="relayOnDisc">
<option value="0">Don't change</option>
<option value="1">Turn the switch OFF</option>
<option value="2">Turn the switch ON</option>


+ 2
- 2
code/html/vendor/pure-1.0.0.min.css
File diff suppressed because it is too large
View File


+ 2
- 2
code/html/vendor/pure-grids-responsive-1.0.0.min.css
File diff suppressed because it is too large
View File


+ 28
- 21
code/ota.py View File

@ -13,7 +13,7 @@ import re
import socket
import subprocess
import sys
from time import sleep
import time
from zeroconf import ServiceBrowser, ServiceStateChange, Zeroconf
@ -25,9 +25,11 @@ except NameError:
# -------------------------------------------------------------------------------
devices = []
description = "ESPurna OTA Manager v0.1"
DISCOVER_TIMEOUT = 2
description = "ESPurna OTA Manager v0.2"
devices = []
discover_last = 0
# -------------------------------------------------------------------------------
@ -37,23 +39,26 @@ def on_service_state_change(zeroconf, service_type, name, state_change):
"""
if state_change is ServiceStateChange.Added:
discover_last = time.time()
info = zeroconf.get_service_info(service_type, name)
if info:
hostname = info.server.split(".")[0]
device = {
'hostname': hostname.upper(),
'ip': socket.inet_ntoa(info.address)
'ip': socket.inet_ntoa(info.address),
'mac': '',
'app_name': '',
'app_version': '',
'target_board': '',
'mem_size': '',
'sdk_size': '',
'free_space': '',
}
device['mac'] = info.properties.get('mac', '')
device['app'] = info.properties.get('app_name', '')
device['version'] = info.properties.get('app_version', '')
device['device'] = info.properties.get('target_board', '')
if 'mem_size' in info.properties:
device['mem_size'] = info.properties.get('mem_size')
if 'sdk_size' in info.properties:
device['sdk_size'] = info.properties.get('sdk_size')
if 'free_space' in info.properties:
device['free_space'] = info.properties.get('free_space')
for key, item in info.properties.items():
device[key.decode('UTF-8')] = item.decode('UTF-8');
devices.append(device)
@ -84,9 +89,9 @@ def list_devices():
device.get('hostname', ''),
device.get('ip', ''),
device.get('mac', ''),
device.get('app', ''),
device.get('version', ''),
device.get('device', ''),
device.get('app_name', ''),
device.get('app_version', ''),
device.get('target_board', ''),
device.get('mem_size', ''),
device.get('sdk_size', ''),
device.get('free_space', ''),
@ -121,7 +126,7 @@ def get_board_by_index(index):
if 1 <= index and index <= len(devices):
device = devices[index - 1]
board['hostname'] = device.get('hostname')
board['board'] = device.get('device', '')
board['board'] = device.get('target_board', '')
board['ip'] = device.get('ip', '')
board['size'] = int(device.get('mem_size', 0) if device.get('mem_size', 0) == device.get('sdk_size', 0) else 0) / 1024
return board
@ -135,7 +140,7 @@ def get_board_by_hostname(hostname):
if device.get('hostname', '').lower() == hostname:
board = {}
board['hostname'] = device.get('hostname')
board['board'] = device.get('device')
board['board'] = device.get('target_board')
if not board['board']:
return None
board['ip'] = device.get('ip')
@ -226,8 +231,10 @@ if __name__ == '__main__':
# Look for sevices
zeroconf = Zeroconf()
browser = ServiceBrowser(zeroconf, "_arduino._tcp.local.", handlers=[on_service_state_change])
sleep(5)
zeroconf.close()
discover_last = time.time()
while time.time() < discover_last + DISCOVER_TIMEOUT:
None
#zeroconf.close()
if len(devices) == 0:
print("Nothing found!\n")


+ 115
- 0
code/package-lock.json View File

@ -238,6 +238,12 @@
"integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=",
"dev": true
},
"binaryextensions": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.1.1.tgz",
"integrity": "sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA==",
"dev": true
},
"boolbase": {
"version": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
@ -944,6 +950,12 @@
"jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz"
}
},
"editions": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz",
"integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==",
"dev": true
},
"end-of-stream": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz",
@ -1708,6 +1720,49 @@
"through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz"
}
},
"gulp-replace": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz",
"integrity": "sha512-lgdmrFSI1SdhNMXZQbrC75MOl1UjYWlOWNbNRnz+F/KHmgxt3l6XstBoAYIdadwETFyG/6i+vWUSCawdC3pqOw==",
"dev": true,
"requires": {
"istextorbinary": "2.2.1",
"readable-stream": "2.3.6",
"replacestream": "4.0.3"
},
"dependencies": {
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
"core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"isarray": "1.0.0",
"process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"string_decoder": "1.1.1",
"util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz"
}
}
}
},
"gulp-uglify": {
"version": "https://registry.npmjs.org/gulp-uglify/-/gulp-uglify-1.5.4.tgz",
"integrity": "sha1-UkeI2HZm0J+dDCH7IXf5ADmmWMk=",
@ -2227,6 +2282,17 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
},
"istextorbinary": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz",
"integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==",
"dev": true,
"requires": {
"binaryextensions": "2.1.1",
"editions": "1.3.4",
"textextensions": "2.2.0"
}
},
"js-yaml": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz",
@ -3136,6 +3202,49 @@
"integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=",
"dev": true
},
"replacestream": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz",
"integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==",
"dev": true,
"requires": {
"escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"readable-stream": "2.3.6"
},
"dependencies": {
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
"core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"isarray": "1.0.0",
"process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"string_decoder": "1.1.1",
"util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz"
}
}
}
},
"request": {
"version": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
"integrity": "sha1-ygtl2gLtYpNYh4COb1EDgQNOM1Y=",
@ -3675,6 +3784,12 @@
"upper-case": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz"
}
},
"textextensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.2.0.tgz",
"integrity": "sha512-j5EMxnryTvKxwH2Cq+Pb43tsf6sdEgw6Pdwxk83mPaq0ToeFJt6WE4J3s5BqY7vmjlLgkgXvhtXUxo80FyBhCA==",
"dev": true
},
"through": {
"version": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",


+ 1
- 0
code/package.json View File

@ -17,6 +17,7 @@
"gulp-htmllint": "0.0.14",
"gulp-htmlmin": "^2.0.0",
"gulp-inline": "^0.1.1",
"gulp-replace": "^1.0.0",
"gulp-uglify": "^1.5.3"
}
}

+ 74
- 2
code/platformio.ini View File

@ -64,7 +64,7 @@ lib_deps =
https://bitbucket.org/xoseperez/fauxmoesp.git#2.4.2
https://github.com/xoseperez/hlw8012.git#1.1.0
https://github.com/markszabo/IRremoteESP8266#v2.2.0
https://bitbucket.org/xoseperez/justwifi.git#1.1.9
https://github.com/xoseperez/justwifi.git#1.2.0
https://github.com/madpilot/mDNSResolver#4cfcda1
https://github.com/xoseperez/my92xx#3.0.1
https://bitbucket.org/xoseperez/nofuss.git#0.2.5
@ -1981,6 +1981,54 @@ upload_flags = --auth=${env.ESPURNA_AUTH} --port 8266
monitor_baud = 115200
extra_scripts = ${common.extra_scripts}
[env:neo-coolcam-power-plug-wifi]
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} -DNEO_COOLCAM_POWER_PLUG_WIFI
monitor_baud = 115200
extra_scripts = ${common.extra_scripts}
[env:neo-coolcam-power-plug-wifi-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} -DNEO_COOLCAM_POWER_PLUG_WIFI
upload_speed = 115200
upload_port = "${env.ESPURNA_IP}"
upload_flags = --auth=${env.ESPURNA_AUTH} --port 8266
extra_scripts = ${common.extra_scripts}
[env:estink-wifi-power-strip]
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} -DESTINK_WIFI_POWER_STRIP
monitor_baud = 115200
extra_scripts = ${common.extra_scripts}
[env:estink-wifi-power-strip-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} -DESTINK_WIFI_POWER_STRIP
upload_speed = 115200
upload_port = "${env.ESPURNA_IP}"
upload_flags = --auth=${env.ESPURNA_AUTH} --port 8266
extra_scripts = ${common.extra_scripts}
# ------------------------------------------------------------------------------
# GENERIC OTA ENVIRONMENTS
# ------------------------------------------------------------------------------
@ -2151,4 +2199,28 @@ build_flags = ${common.build_flags_1m} -DTONBUX_MOSQUITO_KILLER
upload_speed = 115200
upload_port = "${env.ESPURNA_IP}"
upload_flags = --auth=${env.ESPURNA_AUTH} --port 8266
extra_scripts = ${common.extra_scripts}
extra_scripts = ${common.extra_scripts}
[env:pilotak-esp-din-v1]
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} -DPILOTAK_ESP_DIN_V1
monitor_baud = 115200
extra_scripts = ${common.extra_scripts}
[env:pilotak-esp-din-v1-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} -DPILOTAK_ESP_DIN_V1
upload_speed = 115200
upload_port = "${env.ESPURNA_IP}"
upload_flags = --auth=${env.ESPURNA_AUTH} --port 8266
extra_scripts = ${common.extra_scripts}

Loading…
Cancel
Save