From 4854642b02850ac6b79b0d6ea418da5ed7b2b3f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xose=20P=C3=A9rez?= Date: Tue, 26 Jul 2016 16:51:51 +0200 Subject: [PATCH] AutoOTA first version --- code/lib/AutoOTA/AutoOTA.cpp | 145 +++++++++++++++++++++++++++++++++++ code/lib/AutoOTA/AutoOTA.h | 65 ++++++++++++++++ server/composer.json | 3 +- server/composer.lock | 52 ++++++++++++- server/data/versions.json | 10 +-- server/src/dependencies.php | 17 +++- server/src/middleware.php | 10 ++- server/src/routes.php | 26 +++---- server/src/settings.php | 15 ++-- server/templates/index.phtml | 57 -------------- 10 files changed, 309 insertions(+), 91 deletions(-) create mode 100644 code/lib/AutoOTA/AutoOTA.cpp create mode 100644 code/lib/AutoOTA/AutoOTA.h delete mode 100644 server/templates/index.phtml diff --git a/code/lib/AutoOTA/AutoOTA.cpp b/code/lib/AutoOTA/AutoOTA.cpp new file mode 100644 index 00000000..d96e58bd --- /dev/null +++ b/code/lib/AutoOTA/AutoOTA.cpp @@ -0,0 +1,145 @@ +#include "AutoOTA.h" +#include +#include +#include + +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("version"); + _newFileSystem = response.get("spiffs"); + _newFirmware = response.get("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; diff --git a/code/lib/AutoOTA/AutoOTA.h b/code/lib/AutoOTA/AutoOTA.h new file mode 100644 index 00000000..8c0a3744 --- /dev/null +++ b/code/lib/AutoOTA/AutoOTA.h @@ -0,0 +1,65 @@ +#ifndef _AUTO_OTA_h +#define _AUTO_OTA_h + +#include +#include +#include + +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 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 */ diff --git a/server/composer.json b/server/composer.json index 29310bdd..9fb9927f 100644 --- a/server/composer.json +++ b/server/composer.json @@ -16,6 +16,7 @@ "slim/slim": "^3.1", "slim/php-view": "^2.0", "monolog/monolog": "^1.17", - "slim/twig-view": "^2.1" + "slim/twig-view": "^2.1", + "akrabat/rka-ip-address-middleware": "^0.4.0" } } diff --git a/server/composer.lock b/server/composer.lock index 4e2704de..4b3198e9 100644 --- a/server/composer.lock +++ b/server/composer.lock @@ -4,9 +4,57 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "7cd735ef5dd6b16144dfd2a940224676", - "content-hash": "df0b70ace71b7e873d1119b4788805db", + "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", diff --git a/server/data/versions.json b/server/data/versions.json index 3806122c..528adbef 100644 --- a/server/data/versions.json +++ b/server/data/versions.json @@ -1,14 +1,14 @@ [ { - "model": "SONOFF", - "firmware": { + "origin": { + "model": "SONOFF", "min": "*", "max": "0.9.2" }, "target": { - "version": "0.9.2", - "firmware": "/firmware/espurna-0.9.2.bin", - "spiffs": "/firmware/espurna-0.9.1-spiffs.bin" + "version": "0.9.3", + "firmware": "/firmware/espurna-0.9.3.bin", + "spiffs": "/firmware/espurna-0.9.3-spiffs.bin" } } ] diff --git a/server/src/dependencies.php b/server/src/dependencies.php index f188a741..2e7af52c 100644 --- a/server/src/dependencies.php +++ b/server/src/dependencies.php @@ -19,12 +19,21 @@ $container['view'] = function ($container) { }; // monolog -$container['logger'] = function ($container) { - $settings = $container->get('settings')['logger']; +$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->pushProcessor(new Monolog\Processor\UidProcessor()); - $logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], Monolog\Logger::DEBUG)); + $logger->pushHandler($stream); + return $logger; + }; // version database diff --git a/server/src/middleware.php b/server/src/middleware.php index 116a0ffe..f4fbcdd5 100644 --- a/server/src/middleware.php +++ b/server/src/middleware.php @@ -1,4 +1,10 @@ add(new \Slim\Csrf\Guard); +$container = $app->getContainer(); + +$settings = $container->get('settings'); + +$app->add(new RKA\Middleware\IpAddress( + $settings['rka']['check_proxy_headers'], + $settings['rka']['trusted_proxies'] +)); diff --git a/server/src/routes.php b/server/src/routes.php index 40368edc..7ec55209 100644 --- a/server/src/routes.php +++ b/server/src/routes.php @@ -1,7 +1,6 @@ get('/{model}/{current}', function($request, $response, $args) { $found = false; @@ -10,15 +9,11 @@ $app->get('/{model}/{current}', function($request, $response, $args) { foreach ($this->get('data') as $version) { - if (($model == $version['model']) - && (($version['firmware']['min'] == "*" || version_compare($version['firmware']['min'], $current, "<="))) - && (($version['firmware']['max'] == "*" || version_compare($version['firmware']['max'], $current, ">")))) { - - $response->getBody()->write(stripslashes(json_encode(array( - 'action' => 'update', - 'target' => $version["target"] - )))); + 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; @@ -26,11 +21,16 @@ $app->get('/{model}/{current}', function($request, $response, $args) { }; if (!$found) { - $response->getBody()->write(stripslashes(json_encode(array( - 'action' => 'none', - )))); + $response->getBody()->write("{}"); } + $this->get('devices')->info( + "from:" + . $request->getAttribute('ip_address') + . " model:$model version:$current update:" + . ($found ? $version['target']['version'] : "none") + ); + return $response; }); diff --git a/server/src/settings.php b/server/src/settings.php index 4751d57f..8e4e168d 100644 --- a/server/src/settings.php +++ b/server/src/settings.php @@ -1,19 +1,20 @@ [ + 'displayErrorDetails' => true, // set to false in production 'addContentLengthHeader' => false, // Allow the web server to send the content-length header - // Renderer settings - 'renderer' => [ - 'template_path' => __DIR__ . '/../templates/', - 'cache_path' => __DIR__ . '/../cache/', + // RKA IP Address middleware + 'rka' => [ + 'check_proxy_headers' => false, + 'trusted_proxies' => [], ], // Monolog settings - 'logger' => [ - 'name' => 'espurna-update-server', - 'path' => __DIR__ . '/../logs/app.log', + 'devices' => [ + 'name' => 'espurna-update-server-devices', + 'path' => __DIR__ . '/../logs/devices.log', ], // Database diff --git a/server/templates/index.phtml b/server/templates/index.phtml deleted file mode 100644 index 59e4dfe9..00000000 --- a/server/templates/index.phtml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - ESPurna Update Server - - - - -

ESPurna Update Server

- - - - - - - - - - - - - - {% for version in versions %} - - - - - - - - - {% endfor %} - -
DeviceMinimum Hardware VersionMaximum Hardware VersionMinimum Firmware VersionMaximum Firmware VersionLatest Firmware Version
{{ version.model }}{{ version.hardware_min }}{{ version.hardware_max }}{{ version.firmware_min }}{{ version.firmware_max }}{{ version.firmware_version }}
- - -