Browse Source

Using NoFUSS library for autoota

fastled
Xose Pérez 8 years ago
parent
commit
ceb73afb47
17 changed files with 137 additions and 1112 deletions
  1. +34
    -32
      README.md
  2. +0
    -145
      code/lib/AutoOTA/AutoOTA.cpp
  3. +0
    -65
      code/lib/AutoOTA/AutoOTA.h
  4. +4
    -4
      code/src/Config.cpp
  5. +4
    -4
      code/src/Config.h
  6. +95
    -76
      code/src/main.cpp
  7. +0
    -4
      server/.gitignore
  8. +0
    -8
      server/README.md
  9. +0
    -22
      server/composer.json
  10. +0
    -580
      server/composer.lock
  11. +0
    -14
      server/data/versions.json
  12. +0
    -10
      server/public/.htaccess
  13. +0
    -30
      server/public/index.php
  14. +0
    -45
      server/src/dependencies.php
  15. +0
    -10
      server/src/middleware.php
  16. +0
    -36
      server/src/routes.php
  17. +0
    -27
      server/src/settings.php

+ 34
- 32
README.md View File

@ -1,29 +1,38 @@
# ESPurna # ESPurna
ESPurna ("spark" in Catalan) is a custom C firmware for [ITead Sonoff][1] Smart WiFi Switch.
This device has an ESP8266 on board with a 8Mbit flash memory chip, a mains to 3V3 transformer
and a relay (GPIO12). It also features a button (GPIO0), an LED (GPIO13) and an unpopulated header you can use to reprogram it.
ESPurna ("spark" in Catalan) is a custom C firmware for ESP8266 based smart switches. It was originally developed with the **[ITead Sonoff][1]** in mind. This device has an ESP8266 on board with a 8Mbit flash memory chip, a mains to 3V3 transformer and a relay (GPIO12). It also features a button (GPIO0), an LED (GPIO13) and an unpopulated header you can use to reprogram it.
You can read about this board and firmware in [my blog][2]. You can read about this board and firmware in [my blog][2].
![Sonoff board - front view](/images/pinout_front.jpg) ![Sonoff board - front view](/images/pinout_front.jpg)
![Sonoff board - back view](/images/pinout_back.jpg) ![Sonoff board - back view](/images/pinout_back.jpg)
## Hardware support
* [ITead Sonoff TH][1]
* [ITead Sonoff RF][8]
* [ITead Slampher][9]
* [ITead S20 Smart Socket][10]
* Tinkerman ESPurna board
## Features ## Features
* WebServer for configuration and simple relay toggle
* Flashing firmware Over-The-Air (OTA)
* Up to 3 configurable WIFI networks
* MQTT support with configurable host and topic
* **WebServer for configuration** and simple relay toggle
* **Flashing firmware Over-The-Air** (OTA)
* Up to **3 configurable WIFI networks**
* **MQTT support** with configurable host and topic
* Manual switch ON/OFF with button (single click the button) * Manual switch ON/OFF with button (single click the button)
* AP mode backup (double click the button) * AP mode backup (double click the button)
* Support for custom RF module (check blog post)
* Visual status of the connection via the LED * Visual status of the connection via the LED
* Support for custom **[RF module][2]**
* Support for **automatic over-the-air updates** through the [NoFUSS Library][6]
* Support for **current monitoring** through then [EmonLiteESP Library][7]
* Support for **DHT22** sensors
## Flashing ## Flashing
The unpopulated header has all the required pins. My board has a 5 pins header
in-line with the button. They are (from the button outwards):
The unpopulated header has all the required pins. My board has a 5 pins header in-line with the button. They are (from the button outwards):
* 3V3 * 3V3
* RX * RX
@ -31,14 +40,10 @@ in-line with the button. They are (from the button outwards):
* GND * GND
* GPIO14 * GPIO14
Last one is not necessary. Mind it's a **3V3 device**, if connected to 5V you will
probably fry it. Button is connected to GPIO0 on the ESP8266 chip, so to enter
flash mode you have to hold the button pressed while powering on the board, then
you can realease it again.
Last one is not necessary. Mind it's a **3V3 device**, if connected to 5V you will probably fry it. Button is connected to GPIO0 on the ESP8266 chip, so to enter flash mode you have to hold the button pressed while powering on the board, then you can realease it again.
The project is ready to be build using [PlatformIO][3]. The project is ready to be build using [PlatformIO][3].
Please refer to their web page for instructions on how to install the builder.
Once installed:
Please refer to their web page for instructions on how to install the builder. Once installed:
```bash ```bash
> platformio run --target upload -e wire > platformio run --target upload -e wire
@ -59,36 +64,28 @@ When using OTA environment it defaults to the IP address of the device in SoftAP
> platformio run --target uploadfs -e ota --upload-port 192.168.1.151 > platformio run --target uploadfs -e ota --upload-port 192.168.1.151
``` ```
You can also use the automatic OTA update feature. Check the [NoFUSS library][6] for more info.
Library dependencies are automatically managed via PlatformIO Library Manager. Library dependencies are automatically managed via PlatformIO Library Manager.
## Usage ## Usage
On normal boot (i.e. button not pressed) it will execute the firmware.
It configures the hardware (button, LED, relay), the SPIFFS memory access, the
WIFI, the WebServer and MQTT connection.
On normal boot (i.e. button not pressed) it will execute the firmware. It configures the hardware (button, LED, relay), the SPIFFS memory access, the WIFI, the WebServer and MQTT connection.
Obviously the default values for WIFI network and MQTT will probably not match
your requirements. The device will start in Soft AP creating a WIFI SSID named "SONOFF_XXXXXX", where XXXXXX are the last 3 bytes of the radio MAC. Connect with
phone, PC, laptop, whatever to that network, password is "fibonacci". Once connected
browse to 192.168.4.1 and you will be presented a configuration page where you will
be able to define up to 3 possible WIFI networks and the MQTT configuration parameters.
Obviously the default values for WIFI network and MQTT will probably not match your requirements. The device will start in Soft AP creating a WIFI SSID named "SONOFF_XXXXXX", where XXXXXX are the last 3 bytes of the radio MAC. Connect with phone, PC, laptop, whatever to that network, password is "fibonacci". Once connected
browse to 192.168.4.1 and you will be presented a configuration page where you will be able to define up to 3 possible WIFI networks and the MQTT configuration parameters.
It will then try to connect to the first WIFI network. If fail it will try the second
in 30 seconds, and so on. Once connected it will try to connect the MQTT server. If there are no configured networks or none of the configured ones is reachable it defaults to SoftAP mode. You can also switch to SoftAP mode by double clicking the on board button.
It will then try to connect to the configure WIFI networks one after the other. If none of the 3 attempts succeed it will default to SoftAP mode again. Once connected it will try to connect the MQTT server. You can also switch to SoftAP mode by long clicking the on board button or reset the board double clicking the it.
The device will publish the relay state to the given topic and it will subscribe to
the same topic plus "/set" for remote switching. So if your topic is "/home/living/switch"
you will be able to switch it on/off sending "1"/"0" to "/home/living/switch/set".
The device will publish the relay state to the given topic and it will subscribe to the same topic for remote switching. Don't worry, it avoids infinite loops.
You can also use "{identifier}" as place holder in the topic. It will be translated to
your device ID (same as the soft AP network it creates).
You can also use "{identifier}" as place holder in the topic. It will be translated to your device ID (same as the soft AP network it creates).
## Troubleshooting ## Troubleshooting
After flashing the firmware via serial do a hard reset of the device (unplug & plug). There is an issue with the ESP.reset() method. Check [https://github.com/esp8266/Arduino/issues/1017][4] for more info. After flashing the firmware via serial do a hard reset of the device (unplug & plug). There is an issue with the ESP.reset() method. Check [https://github.com/esp8266/Arduino/issues/1017][4] for more info.
Current version of ESP8266httpUpdate restarts the modules after SPIFFS update, thus preventing the firmware to be updated too. There is a recent commit fixing that which is not yet pushed to PLatformIO. Check [Fix example for ESP8266httpUpdate][5] for more info.
Current version of ESP8266httpUpdate restarts the modules after SPIFFS update, thus preventing the firmware to be updated too. There is a recent commit fixing that which is not yet pushed to PLatformIO. Check [Fix example for ESP8266httpUpdate][5] for more info. Anyway, current expected behaviour is to not resetting the board from the ESP8266httpUpdate class (comment out line 300 in ```ESP8266httpUpdate.cpp```).
[1]: https://www.itead.cc/sonoff-wifi-wireless-switch.html [1]: https://www.itead.cc/sonoff-wifi-wireless-switch.html
@ -96,3 +93,8 @@ Current version of ESP8266httpUpdate restarts the modules after SPIFFS update, t
[3]: http://www.platformio.org [3]: http://www.platformio.org
[4]: https://github.com/esp8266/Arduino/issues/1017 [4]: https://github.com/esp8266/Arduino/issues/1017
[5]: https://github.com/esp8266/Arduino/pull/2251 [5]: https://github.com/esp8266/Arduino/pull/2251
[6]: https://bitbucket.org/xoseperez/nofuss
[7]: https://bitbucket.org/xoseperez
[8]: https://www.itead.cc/sonoff-rf.html
[9]: https://www.itead.cc/slampher-wifi-wireless-light-holder.html
[10]: https://www.itead.cc/smart-socket-eu.html

+ 0
- 145
code/lib/AutoOTA/AutoOTA.cpp View File

@ -1,145 +0,0 @@
#include "AutoOTA.h"
#include <functional>
#include <ArduinoJson.h>
#include <ESP8266httpUpdate.h>
void AutoOTAClass::setServer(String server) {
_server = server;
}
void AutoOTAClass::setModel(String model) {
_model = model;
}
void AutoOTAClass::setVersion(String version) {
_version = version;
}
void AutoOTAClass::onMessage(TMessageFunction fn) {
_callback = fn;
}
String AutoOTAClass::getNewVersion() {
return _newVersion;
}
String AutoOTAClass::getNewFirmware() {
return _newFirmware;
}
String AutoOTAClass::getNewFileSystem() {
return _newFileSystem;
}
int AutoOTAClass::getErrorNumber() {
return _errorNumber;
}
String AutoOTAClass::getErrorString() {
return _errorString;
}
String AutoOTAClass::_getPayload() {
HTTPClient http;
char url[100];
String payload = "";
_callback(AUTO_OTA_START);
sprintf(url, "%s/%s/%s", _server.c_str(), _model.c_str(), _version.c_str());
http.begin(url);
int httpCode = http.GET();
if (httpCode > 0) payload = http.getString();
http.end();
return payload;
}
bool AutoOTAClass::_checkUpdates() {
String payload = _getPayload();
if (payload.length() == 0) {
_callback(AUTO_OTA_NO_RESPONSE_ERROR);
return false;
}
StaticJsonBuffer<500> jsonBuffer;
JsonObject& response = jsonBuffer.parseObject(payload);
if (!response.success()) {
_callback(AUTO_OTA_PARSE_ERROR);
return false;
}
if (response.size() == 0) {
_callback(AUTO_OTA_UPTODATE);
return false;
}
_newVersion = response.get<String>("version");
_newFileSystem = response.get<String>("spiffs");
_newFirmware = response.get<String>("firmware");
_callback(AUTO_OTA_UPDATING);
return true;
}
void AutoOTAClass::_doUpdate() {
char url[100];
bool error = false;
uint8_t updates = 0;
if (_newFileSystem.length() > 0) {
// Update SPIFFS
sprintf(url, "%s/%s", _server.c_str(), _newFileSystem.c_str());
t_httpUpdate_return ret = ESPhttpUpdate.updateSpiffs(url);
if (ret == HTTP_UPDATE_FAILED) {
error = true;
_errorNumber = ESPhttpUpdate.getLastError();
_errorString = ESPhttpUpdate.getLastErrorString();
_callback(AUTO_OTA_FILESYSTEM_UPDATE_ERROR);
} else if (ret == HTTP_UPDATE_OK) {
updates++;
_callback(AUTO_OTA_FILESYSTEM_UPDATED);
}
}
if (!error && (_newFirmware.length() > 0)) {
// Update binary
sprintf(url, "%s%s", _server.c_str(), _newFirmware.c_str());
t_httpUpdate_return ret = ESPhttpUpdate.update(url);
if (ret == HTTP_UPDATE_FAILED) {
error = true;
_errorNumber = ESPhttpUpdate.getLastError();
_errorString = ESPhttpUpdate.getLastErrorString();
_callback(AUTO_OTA_FIRMWARE_UPDATE_ERROR);
} else if (ret == HTTP_UPDATE_OK) {
updates++;
_callback(AUTO_OTA_FIRMWARE_UPDATED);
}
}
if (!error && (updates > 0)) {
_callback(AUTO_OTA_RESET);
ESP.restart();
}
}
void AutoOTAClass::handle() {
_callback(AUTO_OTA_START);
if (_checkUpdates()) _doUpdate();
_callback(AUTO_OTA_END);
}
AutoOTAClass AutoOTA;

+ 0
- 65
code/lib/AutoOTA/AutoOTA.h View File

@ -1,65 +0,0 @@
#ifndef _AUTO_OTA_h
#define _AUTO_OTA_h
#include <functional>
#include <ArduinoJson.h>
#include <ESP8266httpUpdate.h>
typedef enum {
AUTO_OTA_START,
AUTO_OTA_UPTODATE,
AUTO_OTA_UPDATING,
AUTO_OTA_FILESYSTEM_UPDATED,
AUTO_OTA_FIRMWARE_UPDATED,
AUTO_OTA_RESET,
AUTO_OTA_END,
AUTO_OTA_NO_RESPONSE_ERROR,
AUTO_OTA_PARSE_ERROR,
AUTO_OTA_FILESYSTEM_UPDATE_ERROR,
AUTO_OTA_FIRMWARE_UPDATE_ERROR
} auto_ota_t;
class AutoOTAClass {
public:
typedef std::function<void(auto_ota_t)> TMessageFunction;
void setServer(String server);
void setModel(String model);
void setVersion(String version);
String getNewVersion();
String getNewFirmware();
String getNewFileSystem();
int getErrorNumber();
String getErrorString();
void onMessage(TMessageFunction fn);
void handle();
private:
String _server;
String _model;
String _version;
String _newVersion;
String _newFirmware;
String _newFileSystem;
int _errorNumber;
String _errorString;
TMessageFunction _callback;
String _getPayload();
bool _checkUpdates();
void _doUpdate();
};
extern AutoOTAClass AutoOTA;
#endif /* _AUTO_OTA_h */

+ 4
- 4
code/src/Config.cpp View File

@ -29,8 +29,8 @@ bool ConfigClass::save() {
content += "mqttTopic=" + mqttTopic + "|"; content += "mqttTopic=" + mqttTopic + "|";
content += "rfChannel=" + rfChannel + "|"; content += "rfChannel=" + rfChannel + "|";
content += "rfDevice=" + rfDevice + "|"; content += "rfDevice=" + rfDevice + "|";
content += "otaServer=" + otaServer + "|";
content += "otaInterval=" + otaInterval + "|";
content += "nofussServer=" + nofussServer + "|";
content += "nofussInterval=" + nofussInterval + "|";
content += "pwMainsVoltage=" + pwMainsVoltage + "|"; content += "pwMainsVoltage=" + pwMainsVoltage + "|";
content += "pwCurrentRatio=" + pwCurrentRatio + "|"; content += "pwCurrentRatio=" + pwCurrentRatio + "|";
@ -92,8 +92,8 @@ bool ConfigClass::load() {
else if (line.startsWith("mqttTopic=")) mqttTopic = line.substring(10); else if (line.startsWith("mqttTopic=")) mqttTopic = line.substring(10);
else if (line.startsWith("rfChannel=")) rfChannel = line.substring(10); else if (line.startsWith("rfChannel=")) rfChannel = line.substring(10);
else if (line.startsWith("rfDevice=")) rfDevice = line.substring(9); else if (line.startsWith("rfDevice=")) rfDevice = line.substring(9);
else if (line.startsWith("otaServer=")) otaServer = line.substring(10);
else if (line.startsWith("otaInterval=")) otaInterval = line.substring(12);
else if (line.startsWith("nofussServer=")) nofussServer = line.substring(13);
else if (line.startsWith("nofussInterval=")) nofussInterval = line.substring(15);
else if (line.startsWith("pwMainsVoltage=")) pwMainsVoltage = line.substring(15); else if (line.startsWith("pwMainsVoltage=")) pwMainsVoltage = line.substring(15);
else if (line.startsWith("pwCurrentRatio=")) pwCurrentRatio = line.substring(15); else if (line.startsWith("pwCurrentRatio=")) pwCurrentRatio = line.substring(15);


+ 4
- 4
code/src/Config.h View File

@ -22,8 +22,8 @@
#define MQTT_PORT 1883 #define MQTT_PORT 1883
#define MQTT_TOPIC "/test/switch/{identifier}" #define MQTT_TOPIC "/test/switch/{identifier}"
#define AUTOOTA_SERVER "http://192.168.1.100"
#define AUTOOTA_INTERVAL 600000
#define NOFUSS_SERVER "http://192.168.1.100"
#define NOFUSS_INTERVAL 600000
#define MAINS_VOLTAGE 230 #define MAINS_VOLTAGE 230
#define CURRENT_RATIO 180 #define CURRENT_RATIO 180
@ -53,8 +53,8 @@ class ConfigClass {
String rfChannel = String(RF_CHANNEL); String rfChannel = String(RF_CHANNEL);
String rfDevice = String(RF_DEVICE); String rfDevice = String(RF_DEVICE);
String otaServer = String(AUTOOTA_SERVER);
String otaInterval = String(AUTOOTA_INTERVAL);
String nofussServer = String(NOFUSS_SERVER);
String nofussInterval = String(NOFUSS_INTERVAL);
String pwMainsVoltage = String(MAINS_VOLTAGE); String pwMainsVoltage = String(MAINS_VOLTAGE);
String pwCurrentRatio = String(CURRENT_RATIO); String pwCurrentRatio = String(CURRENT_RATIO);


+ 95
- 76
code/src/main.cpp View File

@ -30,7 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "FS.h" #include "FS.h"
#include "Config.h" #include "Config.h"
#include "AutoOTA.h"
#include "NoFUSSClient.h"
#include <DebounceEvent.h> #include <DebounceEvent.h>
#include <EmonLiteESP.h> #include <EmonLiteESP.h>
@ -52,7 +52,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define ENABLE_RF 0 #define ENABLE_RF 0
#define ENABLE_OTA 1 #define ENABLE_OTA 1
#define ENABLE_OTA_AUTO 0
#define ENABLE_NOFUSS 1
#define ENABLE_MQTT 1 #define ENABLE_MQTT 1
#define ENABLE_WEBSERVER 1 #define ENABLE_WEBSERVER 1
#define ENABLE_ENERGYMONITOR 0 #define ENABLE_ENERGYMONITOR 0
@ -273,6 +273,85 @@ void toggleRelay() {
} }
} }
// -----------------------------------------------------------------------------
// NoFUSS
// -----------------------------------------------------------------------------
#if ENABLE_NOFUSS
void nofussSetup() {
NoFUSSClient.setServer(config.nofussServer);
NoFUSSClient.setDevice(MODEL);
NoFUSSClient.setVersion(APP_VERSION);
NoFUSSClient.onMessage([](nofuss_t code) {
if (code == NOFUSS_START) {
Serial.println(F("[NoFUSS] Start"));
}
if (code == NOFUSS_UPTODATE) {
Serial.println(F("[NoFUSS] Already in the last version"));
}
if (code == NOFUSS_PARSE_ERROR) {
Serial.println(F("[NoFUSS] Error parsing server response"));
}
if (code == NOFUSS_UPDATING) {
Serial.println(F("[NoFUSS] Updating"));
Serial.print( F(" New version: "));
Serial.println(NoFUSSClient.getNewVersion());
Serial.print( F(" Firmware: "));
Serial.println(NoFUSSClient.getNewFirmware());
Serial.print( F(" File System: "));
Serial.println(NoFUSSClient.getNewFileSystem());
}
if (code == NOFUSS_FILESYSTEM_UPDATE_ERROR) {
Serial.print(F("[NoFUSS] File System Update Error: "));
Serial.println(NoFUSSClient.getErrorString());
}
if (code == NOFUSS_FILESYSTEM_UPDATED) {
Serial.println(F("[NoFUSS] File System Updated"));
}
if (code == NOFUSS_FIRMWARE_UPDATE_ERROR) {
Serial.print(F("[NoFUSS] Firmware Update Error: "));
Serial.println(NoFUSSClient.getErrorString());
}
if (code == NOFUSS_FIRMWARE_UPDATED) {
Serial.println(F("[NoFUSS] Firmware Updated"));
}
if (code == NOFUSS_RESET) {
Serial.println(F("[NoFUSS] Resetting board"));
}
if (code == NOFUSS_END) {
Serial.println(F("[NoFUSS] End"));
}
});
}
void nofussLoop() {
static unsigned long last_check = 0;
if (WiFi.status() != WL_CONNECTED) return;
if ((last_check > 0) && ((millis() - last_check) < config.nofussInterval.toInt())) return;
last_check = millis();
NoFUSSClient.handle();
}
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// OTA // OTA
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -320,83 +399,11 @@ void toggleRelay() {
#endif #endif
}); });
#if ENABLE_OTA_AUTO
AutoOTA.setServer(config.otaServer);
AutoOTA.setModel(MODEL);
AutoOTA.setVersion(APP_VERSION);
AutoOTA.onMessage([](auto_ota_t code) {
if (code == AUTO_OTA_FILESYSTEM_UPDATED) {
#ifdef DEBUG
Serial.print(F("[AUTOOTA] File System Updated"));
#endif
config.save();
}
#ifdef DEBUG
if (code == AUTO_OTA_START) {
Serial.println(F("[AUTOOTA] Start"));
}
if (code == AUTO_OTA_UPTODATE) {
Serial.println(F("[AUTOOTA] Already in the last version"));
}
if (code == AUTO_OTA_PARSE_ERROR) {
Serial.println(F("[AUTOOTA] Error parsing server response"));
}
if (code == AUTO_OTA_UPDATING) {
Serial.println(F("[AUTOOTA] Updating"));
Serial.print( F(" New version: "));
Serial.println(AutoOTA.getNewVersion());
Serial.print( F(" Firmware: "));
Serial.println(AutoOTA.getNewFirmware());
Serial.print( F(" File System: "));
Serial.println(AutoOTA.getNewFileSystem());
}
if (code == AUTO_OTA_FILESYSTEM_UPDATE_ERROR) {
Serial.print(F("[AUTOOTA] File System Update Error: "));
Serial.println(AutoOTA.getErrorString());
}
if (code == AUTO_OTA_FIRMWARE_UPDATE_ERROR) {
Serial.print(F("[AUTOOTA] Firmware Update Error: "));
Serial.println(AutoOTA.getErrorString());
}
if (code == AUTO_OTA_FIRMWARE_UPDATED) {
Serial.print(F("[AUTOOTA] Firmware Updated"));
}
if (code == AUTO_OTA_RESET) {
Serial.println(F("[AUTOOTA] Resetting board"));
}
if (code == AUTO_OTA_END) {
Serial.println(F("[AUTOOTA] End"));
}
#endif
});
#endif
ArduinoOTA.begin(); ArduinoOTA.begin();
} }
void OTALoop() { void OTALoop() {
#if ENABLE_OTA_AUTO
static unsigned long last_check = 0;
if (WiFi.status() != WL_CONNECTED) return;
if ((last_check > 0) && ((millis() - last_check) < config.otaInterval.toInt())) return;
last_check = millis();
AutoOTA.handle();
#endif
ArduinoOTA.handle(); ArduinoOTA.handle();
} }
@ -481,21 +488,27 @@ void wifiSetupSTA() {
#endif #endif
if (WiFi.status() == WL_CONNECTED) { if (WiFi.status() == WL_CONNECTED) {
WiFi.setAutoConnect(true); WiFi.setAutoConnect(true);
status = WIFI_STATUS_CONNECTED; status = WIFI_STATUS_CONNECTED;
#ifdef DEBUG #ifdef DEBUG
Serial.print(F("[WIFI] STATION Mode, SSID: ")); Serial.print(F("[WIFI] STATION Mode, SSID: "));
Serial.print(WiFi.SSID()); Serial.print(WiFi.SSID());
Serial.print(F(", IP address: ")); Serial.print(F(", IP address: "));
Serial.println(WiFi.localIP()); Serial.println(WiFi.localIP());
#endif #endif
#if ENABLE_OTA_AUTO
AutoOTA.handle();
#if ENABLE_NOFUSS
NoFUSSClient.handle();
#endif #endif
} else { } else {
#ifdef DEBUG #ifdef DEBUG
Serial.println(F("[WIFI] Not connected")); Serial.println(F("[WIFI] Not connected"));
#endif #endif
} }
} }
@ -1180,6 +1193,9 @@ void setup() {
#if ENABLE_OTA #if ENABLE_OTA
OTASetup(); OTASetup();
#endif #endif
#if ENABLE_NOFUSS
nofussSetup();
#endif
#if ENABLE_MQTT #if ENABLE_MQTT
mqttSetup(); mqttSetup();
#endif #endif
@ -1206,6 +1222,9 @@ void loop() {
#if ENABLE_OTA #if ENABLE_OTA
OTALoop(); OTALoop();
#endif #endif
#if ENABLE_NOFUSS
nofussLoop();
#endif
#if ENABLE_MQTT #if ENABLE_MQTT
mqttLoop(); mqttLoop();
#endif #endif


+ 0
- 4
server/.gitignore View File

@ -1,4 +0,0 @@
/cache/
/vendor/
/logs/*
!/logs/README.md

+ 0
- 8
server/README.md View File

@ -1,8 +0,0 @@
# ESPurna Update Server
First version of an ESPurna update server, an API that responfs to ESPurna devices with information about the last available firmware.
## Use
1. Modify ```data/versions.js``` with info about available firmware versions depending on current model (device type) and firmware version.
1. Perform GET queries against http://<server>/<model>/<firmware_version>, for instance: ```http://192.168.1.105/espurna/0.9.1```

+ 0
- 22
server/composer.json View File

@ -1,22 +0,0 @@
{
"name": "tinkerman/espurna-update-server",
"description": "Update server that listen to ESPurna devices queries and returns last available firmware",
"keywords": ["esp8266", "ota", "espurna", "firmware"],
"homepage": "http://tinkerman.cat",
"license": "GPLv3",
"authors": [
{
"name": "Xose Pérez",
"email": "xose.perez@gmail.com",
"homepage": "http://tinkerman.cat/"
}
],
"require": {
"php": ">=5.5.0",
"slim/slim": "^3.1",
"slim/php-view": "^2.0",
"monolog/monolog": "^1.17",
"slim/twig-view": "^2.1",
"akrabat/rka-ip-address-middleware": "^0.4.0"
}
}

+ 0
- 580
server/composer.lock View File

@ -1,580 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "6cce6f8ddf6f041bbf61a8c85f1cb7a3",
"content-hash": "e9593bb6457c08a899298c347997dd68",
"packages": [
{
"name": "akrabat/rka-ip-address-middleware",
"version": "0.4",
"source": {
"type": "git",
"url": "https://github.com/akrabat/rka-ip-address-middleware.git",
"reference": "1c9947fdbaad04614e8b15d55f191f11c39293d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/akrabat/rka-ip-address-middleware/zipball/1c9947fdbaad04614e8b15d55f191f11c39293d1",
"reference": "1c9947fdbaad04614e8b15d55f191f11c39293d1",
"shasum": ""
},
"require": {
"psr/http-message": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8",
"squizlabs/php_codesniffer": "^2.3",
"zendframework/zend-diactoros": "^1.1"
},
"type": "library",
"autoload": {
"psr-4": {
"RKA\\Middleware\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "http://akrabat.com"
}
],
"description": "PSR-7 Middleware that determines the client IP address and stores it as an ServerRequest attribute",
"homepage": "http://github.com/akrabat/rka-ip-address-middleware",
"keywords": [
"IP",
"middleware",
"psr7"
],
"time": "2015-11-06 10:38:17"
},
{
"name": "container-interop/container-interop",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e",
"reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-4": {
"Interop\\Container\\": "src/Interop/Container/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"time": "2014-12-30 15:22:37"
},
{
"name": "monolog/monolog",
"version": "1.20.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "55841909e2bcde01b5318c35f2b74f8ecc86e037"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/55841909e2bcde01b5318c35f2b74f8ecc86e037",
"reference": "55841909e2bcde01b5318c35f2b74f8ecc86e037",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/log": "~1.0"
},
"provide": {
"psr/log-implementation": "1.0.0"
},
"require-dev": {
"aws/aws-sdk-php": "^2.4.9",
"doctrine/couchdb": "~1.0@dev",
"graylog2/gelf-php": "~1.0",
"jakub-onderka/php-parallel-lint": "0.9",
"php-amqplib/php-amqplib": "~2.4",
"php-console/php-console": "^3.1.3",
"phpunit/phpunit": "~4.5",
"phpunit/phpunit-mock-objects": "2.3.0",
"ruflin/elastica": ">=0.90 <3.0",
"sentry/sentry": "^0.13",
"swiftmailer/swiftmailer": "~5.3"
},
"suggest": {
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
"ext-mongo": "Allow sending log messages to a MongoDB server",
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
"mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
"php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
"php-console/php-console": "Allow sending log messages to Google Chrome",
"rollbar/rollbar": "Allow sending log messages to Rollbar",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server",
"sentry/sentry": "Allow sending log messages to a Sentry server"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Monolog\\": "src/Monolog"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
"homepage": "http://github.com/Seldaek/monolog",
"keywords": [
"log",
"logging",
"psr-3"
],
"time": "2016-07-02 14:02:10"
},
{
"name": "nikic/fast-route",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/FastRoute.git",
"reference": "8ea928195fa9b907f0d6e48312d323c1a13cc2af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/8ea928195fa9b907f0d6e48312d323c1a13cc2af",
"reference": "8ea928195fa9b907f0d6e48312d323c1a13cc2af",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"type": "library",
"autoload": {
"psr-4": {
"FastRoute\\": "src/"
},
"files": [
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Nikita Popov",
"email": "nikic@php.net"
}
],
"description": "Fast request router for PHP",
"keywords": [
"router",
"routing"
],
"time": "2016-06-12 19:08:51"
},
{
"name": "pimple/pimple",
"version": "v3.0.2",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
"reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a",
"reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2015-09-11 15:10:35"
},
{
"name": "psr/http-message",
"version": "1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2015-05-04 20:22:00"
},
{
"name": "psr/log",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-0": {
"Psr\\Log\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2012-12-21 11:40:51"
},
{
"name": "slim/php-view",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/slimphp/PHP-View.git",
"reference": "8bae5b10d10c51596ef8d8113b3b63678718adcb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/PHP-View/zipball/8bae5b10d10c51596ef8d8113b3b63678718adcb",
"reference": "8bae5b10d10c51596ef8d8113b3b63678718adcb",
"shasum": ""
},
"require": {
"psr/http-message": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^5.0",
"slim/slim": "^3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Slim\\Views\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Glenn Eggleton",
"email": "geggleto@gmail.com"
}
],
"description": "Render PHP view scripts into a PSR-7 Response object.",
"keywords": [
"framework",
"php",
"phtml",
"renderer",
"slim",
"template",
"view"
],
"time": "2016-03-04 09:48:50"
},
{
"name": "slim/slim",
"version": "3.4.2",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
"reference": "a132385f736063d00632b52b3f8a389fe66fe4fa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/a132385f736063d00632b52b3f8a389fe66fe4fa",
"reference": "a132385f736063d00632b52b3f8a389fe66fe4fa",
"shasum": ""
},
"require": {
"container-interop/container-interop": "^1.1",
"nikic/fast-route": "^1.0",
"php": ">=5.5.0",
"pimple/pimple": "^3.0",
"psr/http-message": "^1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0",
"squizlabs/php_codesniffer": "^2.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Slim\\": "Slim"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "http://akrabat.com"
},
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "https://joshlockhart.com"
},
{
"name": "Gabriel Manricks",
"email": "gmanricks@me.com",
"homepage": "http://gabrielmanricks.com"
},
{
"name": "Andrew Smith",
"email": "a.smith@silentworks.co.uk",
"homepage": "http://silentworks.co.uk"
}
],
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
"homepage": "http://slimframework.com",
"keywords": [
"api",
"framework",
"micro",
"router"
],
"time": "2016-05-25 11:23:38"
},
{
"name": "slim/twig-view",
"version": "2.1.1",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Twig-View.git",
"reference": "16fded26a44b8e8e0e041f1cff32afa21daeb284"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Twig-View/zipball/16fded26a44b8e8e0e041f1cff32afa21daeb284",
"reference": "16fded26a44b8e8e0e041f1cff32afa21daeb284",
"shasum": ""
},
"require": {
"php": ">=5.5.0",
"psr/http-message": "^1.0",
"twig/twig": "^1.18"
},
"require-dev": {
"phpunit/phpunit": "^4.8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Slim\\Views\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "http://joshlockhart.com"
}
],
"description": "Slim Framework 3 view helper built on top of the Twig templating component",
"homepage": "http://slimframework.com",
"keywords": [
"framework",
"slim",
"template",
"twig",
"view"
],
"time": "2016-03-13 20:58:41"
},
{
"name": "twig/twig",
"version": "v1.24.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "3566d311a92aae4deec6e48682dc5a4528c4a512"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/3566d311a92aae4deec6e48682dc5a4528c4a512",
"reference": "3566d311a92aae4deec6e48682dc5a4528c4a512",
"shasum": ""
},
"require": {
"php": ">=5.2.7"
},
"require-dev": {
"symfony/debug": "~2.7",
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.24-dev"
}
},
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
},
{
"name": "Twig Team",
"homepage": "http://twig.sensiolabs.org/contributors",
"role": "Contributors"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
],
"time": "2016-05-30 09:11:59"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.5.0"
},
"platform-dev": []
}

+ 0
- 14
server/data/versions.json View File

@ -1,14 +0,0 @@
[
{
"origin": {
"model": "SONOFF",
"min": "*",
"max": "0.9.2"
},
"target": {
"version": "0.9.3",
"firmware": "/firmware/espurna-0.9.3.bin",
"spiffs": "/firmware/espurna-0.9.3-spiffs.bin"
}
}
]

+ 0
- 10
server/public/.htaccess View File

@ -1,10 +0,0 @@
RewriteEngine On
# Some hosts may require you to use the `RewriteBase` directive.
# If you need to use the `RewriteBase` directive, it should be the
# absolute physical path to the directory that contains this htaccess file.
#
# RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

+ 0
- 30
server/public/index.php View File

@ -1,30 +0,0 @@
<?php
if (PHP_SAPI == 'cli-server') {
// To help the built-in PHP dev server, check if the request was actually for
// something which should probably be served as a static file
$url = parse_url($_SERVER['REQUEST_URI']);
$file = __DIR__ . $url['path'];
if (is_file($file)) {
return false;
}
}
require __DIR__ . '/../vendor/autoload.php';
session_start();
// Instantiate the app
$settings = require __DIR__ . '/../src/settings.php';
$app = new \Slim\App($settings);
// Set up dependencies
require __DIR__ . '/../src/dependencies.php';
// Register middleware
require __DIR__ . '/../src/middleware.php';
// Register routes
require __DIR__ . '/../src/routes.php';
// Run app
$app->run();

+ 0
- 45
server/src/dependencies.php View File

@ -1,45 +0,0 @@
<?php
// DIC configuration
$container = $app->getContainer();
// view renderer
// Register component on container
$container['view'] = function ($container) {
$settings = $container->get('settings')['renderer'];
$view = new \Slim\Views\Twig($settings['template_path'], [
'cache' => $settings['cache_path']
]);
$view->addExtension(new \Slim\Views\TwigExtension(
$container['router'],
$container['request']->getUri()
));
return $view;
};
// monolog
$container['devices'] = function ($container) {
$settings = $container->get('settings')['devices'];
$dateFormat = "[Y/m/d H:i:s]";
$output = "%datetime% %message%\n";
$formatter = new Monolog\Formatter\LineFormatter($output, $dateFormat);
$stream = new Monolog\Handler\StreamHandler($settings['path'], Monolog\Logger::INFO);
$stream->setFormatter($formatter);
$logger = new Monolog\Logger($settings['name']);
$logger->pushHandler($stream);
return $logger;
};
// version database
$container['data'] = function($container) {
$settings = $container->get('settings')['database'];
$json_data = file_get_contents($settings['path']);
$data = json_decode($json_data, true);
return $data;
};

+ 0
- 10
server/src/middleware.php View File

@ -1,10 +0,0 @@
<?php
$container = $app->getContainer();
$settings = $container->get('settings');
$app->add(new RKA\Middleware\IpAddress(
$settings['rka']['check_proxy_headers'],
$settings['rka']['trusted_proxies']
));

+ 0
- 36
server/src/routes.php View File

@ -1,36 +0,0 @@
<?php
// Check update entry point
$app->get('/{model}/{current}', function($request, $response, $args) {
$found = false;
$model = $request->getAttribute('model');
$current = $request->getAttribute('current');
foreach ($this->get('data') as $version) {
if (($model == $version['origin']['model'])
&& (($version['origin']['min'] == '*' || version_compare($version['origin']['min'], $current, '<=')))
&& (($version['origin']['max'] == '*' || version_compare($version['origin']['max'], $current, '>=')))) {
$response->getBody()->write(stripslashes(json_encode($version['target'])));
$found = true;
break;
}
};
if (!$found) {
$response->getBody()->write("{}");
}
$this->get('devices')->info(
"from:"
. $request->getAttribute('ip_address')
. " model:$model version:$current update:"
. ($found ? $version['target']['version'] : "none")
);
return $response;
});

+ 0
- 27
server/src/settings.php View File

@ -1,27 +0,0 @@
<?php
return [
'settings' => [
'displayErrorDetails' => true, // set to false in production
'addContentLengthHeader' => false, // Allow the web server to send the content-length header
// RKA IP Address middleware
'rka' => [
'check_proxy_headers' => false,
'trusted_proxies' => [],
],
// Monolog settings
'devices' => [
'name' => 'espurna-update-server-devices',
'path' => __DIR__ . '/../logs/devices.log',
],
// Database
'database' => [
'type' => 'json',
'path' => __DIR__ . '/../data/versions.json',
],
],
];

Loading…
Cancel
Save