Browse Source

AutoOTA first version

fastled
Xose Pérez 8 years ago
parent
commit
4854642b02
10 changed files with 309 additions and 91 deletions
  1. +145
    -0
      code/lib/AutoOTA/AutoOTA.cpp
  2. +65
    -0
      code/lib/AutoOTA/AutoOTA.h
  3. +2
    -1
      server/composer.json
  4. +50
    -2
      server/composer.lock
  5. +5
    -5
      server/data/versions.json
  6. +13
    -4
      server/src/dependencies.php
  7. +8
    -2
      server/src/middleware.php
  8. +13
    -13
      server/src/routes.php
  9. +8
    -7
      server/src/settings.php
  10. +0
    -57
      server/templates/index.phtml

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

@ -0,0 +1,145 @@
#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;

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

@ -0,0 +1,65 @@
#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 */

+ 2
- 1
server/composer.json View File

@ -16,6 +16,7 @@
"slim/slim": "^3.1", "slim/slim": "^3.1",
"slim/php-view": "^2.0", "slim/php-view": "^2.0",
"monolog/monolog": "^1.17", "monolog/monolog": "^1.17",
"slim/twig-view": "^2.1"
"slim/twig-view": "^2.1",
"akrabat/rka-ip-address-middleware": "^0.4.0"
} }
} }

+ 50
- 2
server/composer.lock View File

@ -4,9 +4,57 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "7cd735ef5dd6b16144dfd2a940224676",
"content-hash": "df0b70ace71b7e873d1119b4788805db",
"hash": "6cce6f8ddf6f041bbf61a8c85f1cb7a3",
"content-hash": "e9593bb6457c08a899298c347997dd68",
"packages": [ "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", "name": "container-interop/container-interop",
"version": "1.1.0", "version": "1.1.0",


+ 5
- 5
server/data/versions.json View File

@ -1,14 +1,14 @@
[ [
{ {
"model": "SONOFF",
"firmware": {
"origin": {
"model": "SONOFF",
"min": "*", "min": "*",
"max": "0.9.2" "max": "0.9.2"
}, },
"target": { "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"
} }
} }
] ]

+ 13
- 4
server/src/dependencies.php View File

@ -19,12 +19,21 @@ $container['view'] = function ($container) {
}; };
// monolog // 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 = 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; return $logger;
}; };
// version database // version database


+ 8
- 2
server/src/middleware.php View File

@ -1,4 +1,10 @@
<?php <?php
// Application middleware
// e.g: $app->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']
));

+ 13
- 13
server/src/routes.php View File

@ -1,7 +1,6 @@
<?php <?php
// Routes
// Check update entry point
$app->get('/{model}/{current}', function($request, $response, $args) { $app->get('/{model}/{current}', function($request, $response, $args) {
$found = false; $found = false;
@ -10,15 +9,11 @@ $app->get('/{model}/{current}', function($request, $response, $args) {
foreach ($this->get('data') as $version) { 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; $found = true;
break; break;
@ -26,11 +21,16 @@ $app->get('/{model}/{current}', function($request, $response, $args) {
}; };
if (!$found) { 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; return $response;
}); });

+ 8
- 7
server/src/settings.php View File

@ -1,19 +1,20 @@
<?php <?php
return [ return [
'settings' => [ 'settings' => [
'displayErrorDetails' => true, // set to false in production 'displayErrorDetails' => true, // set to false in production
'addContentLengthHeader' => false, // Allow the web server to send the content-length header '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 // Monolog settings
'logger' => [
'name' => 'espurna-update-server',
'path' => __DIR__ . '/../logs/app.log',
'devices' => [
'name' => 'espurna-update-server-devices',
'path' => __DIR__ . '/../logs/devices.log',
], ],
// Database // Database


+ 0
- 57
server/templates/index.phtml View File

@ -1,57 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>ESPurna Update Server</title>
<link href='//fonts.googleapis.com/css?family=Lato:300' rel='stylesheet' type='text/css'>
<style>
body {
margin: 50px 0 0 0;
padding: 0;
width: 100%;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
text-align: center;
color: #aaa;
font-size: 18px;
}
h1 {
color: #719e40;
letter-spacing: -3px;
font-family: 'Lato', sans-serif;
font-size: 100px;
font-weight: 200;
margin-bottom: 0;
}
</style>
</head>
<body>
<h1>ESPurna Update Server</h1>
<table>
<thead>
<tr>
<th>Device</th>
<th>Minimum Hardware Version</th>
<th>Maximum Hardware Version</th>
<th>Minimum Firmware Version</th>
<th>Maximum Firmware Version</th>
<th>Latest Firmware Version</th>
</tr>
</thead>
<tbody>
{% for version in versions %}
<tr>
<td>{{ version.model }}</td>
<td>{{ version.hardware_min }}</td>
<td>{{ version.hardware_max }}</td>
<td>{{ version.firmware_min }}</td>
<td>{{ version.firmware_max }}</td>
<td>{{ version.firmware_version }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>

Loading…
Cancel
Save