Ported framework from Stairs project
This commit is contained in:
parent
2ff24cb099
commit
0ad50a20b6
|
@ -1 +1,2 @@
|
|||
node_modules
|
||||
.pio
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(bodyParser.json());
|
||||
app.use(express.static('web'));
|
||||
app.use(express.static('web/dist'));
|
||||
|
||||
app.get('/api/status', function(req, res)
|
||||
{
|
||||
res.send({
|
||||
systemID: 'dev-server',
|
||||
version: 'dev-server',
|
||||
resetReason: 2,
|
||||
stackTrace: true
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/connection', function(req, res)
|
||||
{
|
||||
res.send({
|
||||
hostname: 'dev-server',
|
||||
accesspoint: true,
|
||||
station: true,
|
||||
ssid: 'MyWiFiSSID',
|
||||
password: 'supersecret',
|
||||
dhcp: true,
|
||||
ip: '192.168.1.234',
|
||||
subnetmask: '255.255.255.0',
|
||||
gateway: '192.168.1.0'
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/connection/status', function(req, res)
|
||||
{
|
||||
res.send({
|
||||
"ap": {
|
||||
"enabled": true,
|
||||
"ip": "192.168.4.1"
|
||||
},
|
||||
"station": {
|
||||
"enabled": true,
|
||||
"status": 1,
|
||||
"ip": "0.0.0.0"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/api/connection', function(req, res)
|
||||
{
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
app.post('/api/firmware', function(req, res)
|
||||
{
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
|
||||
var system = {
|
||||
pins: {
|
||||
ledAP: 4,
|
||||
ledSTA: 5,
|
||||
apButton: 2,
|
||||
}
|
||||
};
|
||||
|
||||
app.get('/api/system', function(req, res)
|
||||
{
|
||||
res.send(system);
|
||||
});
|
||||
|
||||
app.post('/api/system', function(req, res)
|
||||
{
|
||||
var body = req.body;
|
||||
if (body)
|
||||
{
|
||||
system.pins.ledAP = body.pins.ledAP || system.pins.ledAP;
|
||||
system.pins.ledSTA = body.pins.ledSTA || system.pins.ledSTA;
|
||||
system.pins.apButton = body.pins.apButton || system.pins.apButton;
|
||||
|
||||
res.sendStatus(200);
|
||||
}
|
||||
else
|
||||
res.sendStatus(400);
|
||||
});
|
||||
|
||||
|
||||
|
||||
app.get('/api/stacktrace/get', function(req, res)
|
||||
{
|
||||
res.send("Nothing to see here, move along!");
|
||||
});
|
||||
|
||||
app.get('/api/stacktrace/delete', function(req, res)
|
||||
{
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
|
||||
app.listen(3000, function()
|
||||
{
|
||||
console.log('Development server listening on port 3000')
|
||||
console.log('Press Ctrl-C to stop')
|
||||
});
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
// Borrowed heavily from gulp-concat:
|
||||
// https://github.com/contra/gulp-concat/
|
||||
//
|
||||
// It's very much hardcoded for the ESP8266 Arduino at the moment,
|
||||
// but feel free to hack away at it if you need it for other purposes!
|
||||
var through = require('through2');
|
||||
var path = require('path');
|
||||
var File = require('vinyl');
|
||||
var _ = require('lodash');
|
||||
var Readable = require('stream').Readable;
|
||||
|
||||
|
||||
function escapeContent(content, lineLength)
|
||||
{
|
||||
var lineRegexp = new RegExp('(.{1,' + (lineLength - 1) + '}[^\\\\])', 'g');
|
||||
|
||||
return content
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/\r?\n/g, '\\r\\n')
|
||||
.replace(lineRegexp, ' "$1"\r\n')
|
||||
.replace(/\r\n$/, '');
|
||||
};
|
||||
|
||||
|
||||
function escapeContentAsByteArray(content, lineLength)
|
||||
{
|
||||
var bytesPerLine = Math.floor(lineLength / 5);
|
||||
var lineRegexp = new RegExp('((?:0x..,){1,' + bytesPerLine + '})', 'g');
|
||||
|
||||
return content
|
||||
.replace(/(.{2})/g, '0x$1,')
|
||||
.replace(lineRegexp, ' $1\r\n')
|
||||
.replace(/,\r\n$/, '');
|
||||
};
|
||||
|
||||
|
||||
function encodeFile(file, opts)
|
||||
{
|
||||
var variableName;
|
||||
|
||||
if (opts.map.hasOwnProperty(file.relative))
|
||||
variableName = opts.map[file.relative];
|
||||
else
|
||||
variableName = file.relative.replace(/[^a-zA-Z0-9_]/g, '_');
|
||||
|
||||
if (variableName === null)
|
||||
return '';
|
||||
|
||||
variableName = opts.variablePrefix + variableName;
|
||||
|
||||
var escapedContent;
|
||||
var output;
|
||||
|
||||
if (opts.byteArray)
|
||||
{
|
||||
escapedContent = escapeContentAsByteArray(file.contents.toString('hex'), opts.lineLength);
|
||||
output = "const uint8_t " + variableName + "[] PROGMEM = {\r\n" + escapedContent + "};\r\n\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
escapedContent = escapeContent(file.contents.toString('utf-8'), opts.lineLength);
|
||||
output = "const char " + variableName + "[] PROGMEM = \r\n" + escapedContent + ";\r\n\r\n";
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
module.exports = function(file, opts)
|
||||
{
|
||||
if (!file)
|
||||
throw new Error('gulp-cppstringify: Missing file option');
|
||||
|
||||
opts = _.extend({
|
||||
map: [],
|
||||
headerDefineName: '__Embedded',
|
||||
variablePrefix: 'Embedded',
|
||||
lineLength: 100,
|
||||
byteArray: false
|
||||
}, opts || {});
|
||||
|
||||
var fileName;
|
||||
var latestFile = false;
|
||||
var latestMod = 0;
|
||||
var output = null;
|
||||
|
||||
|
||||
if (typeof file === 'string')
|
||||
fileName = file;
|
||||
else if (typeof file.path === 'string')
|
||||
fileName = path.basename(file.path);
|
||||
else
|
||||
throw new Error('gulp-cppstringify: Missing path in file options');
|
||||
|
||||
|
||||
function bufferContents(file, enc, cb)
|
||||
{
|
||||
if (file.isNull())
|
||||
{
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.isStream())
|
||||
{
|
||||
this.emit('error', new Error('gulp-cppstringify: Streaming not supported'));
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!latestMod || file.stat && file.stat.mtime > latestMod)
|
||||
{
|
||||
latestFile = file;
|
||||
latestMod = file.stat && file.stat.mtime;
|
||||
}
|
||||
|
||||
if (output == null)
|
||||
{
|
||||
output = new Readable();
|
||||
output._read = function noop() {};
|
||||
|
||||
output.push("#ifndef " + opts.headerDefineName + "\r\n");
|
||||
output.push("#define " + opts.headerDefineName + "\r\n\r\n");
|
||||
output.push("#include <pgmspace.h>\r\n\r\n");
|
||||
}
|
||||
|
||||
output.push(encodeFile(file, opts));
|
||||
cb();
|
||||
}
|
||||
|
||||
|
||||
function endStream(cb)
|
||||
{
|
||||
if (!latestFile)
|
||||
{
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
var headerFile;
|
||||
|
||||
if (typeof file === 'string')
|
||||
{
|
||||
headerFile = latestFile.clone({contents: false});
|
||||
headerFile.path = path.join(latestFile.base, file);
|
||||
}
|
||||
else
|
||||
headerFile = new File(file);
|
||||
|
||||
output.push("#endif\r\n");
|
||||
output.push(null);
|
||||
headerFile.contents = output;
|
||||
|
||||
this.push(headerFile);
|
||||
cb();
|
||||
}
|
||||
|
||||
return through.obj(bufferContents, endStream);
|
||||
};
|
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const htmlmin = require('gulp-htmlmin');
|
||||
const cppstringify = require('./gulp-cppstringify');
|
||||
const fs = require('fs');
|
||||
const plumber = require('gulp-plumber');
|
||||
const sass = require('gulp-sass');
|
||||
const cleanCSS = require('gulp-clean-css');
|
||||
const watch = require('gulp-debounced-watch');
|
||||
const uglify = require('gulp-uglify');
|
||||
const concat = require('gulp-concat');
|
||||
const print = require('gulp-print');
|
||||
const path = require('path');
|
||||
const gzip = require('gulp-gzip');
|
||||
|
||||
|
||||
const config = {
|
||||
assetsPath: 'web/',
|
||||
distPath: 'web/dist/',
|
||||
outputPath: 'src/assets/',
|
||||
|
||||
firmwareArtifact: '.pioenvs/esp12e/firmware.bin',
|
||||
firmwareOutputPath: 'bin/'
|
||||
};
|
||||
|
||||
|
||||
const HTMLMap = {
|
||||
'index.html': 'Index'
|
||||
};
|
||||
|
||||
const JSMap = {
|
||||
'bundle.js': 'BundleJS'
|
||||
};
|
||||
|
||||
const CSSMap = {
|
||||
'bundle.css': 'BundleCSS'
|
||||
};
|
||||
|
||||
|
||||
// There is an issue in the AsyncWebServer where it's apparantly running
|
||||
// out of memory on simultaneous requests. We'll work around it by
|
||||
// merging all the JS into one big file.
|
||||
//
|
||||
// https://github.com/me-no-dev/ESPAsyncWebServer/issues/256
|
||||
const JSSrc = [
|
||||
'node_modules/axios/dist/axios.min.js',
|
||||
'node_modules/vue/dist/vue.min.js',
|
||||
'node_modules/vue-i18n/dist/vue-i18n.min.js',
|
||||
'web/lang.js',
|
||||
'web/app.js'
|
||||
];
|
||||
|
||||
const SCSSSrc = [
|
||||
'web/site.scss'
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
gulp.task('embedHTML', () =>
|
||||
{
|
||||
return gulp.src(config.assetsPath + '*.html')
|
||||
.pipe(print(filepath => { return 'HTML: ' + filepath; }))
|
||||
.pipe(htmlmin({
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
minifyCSS: true,
|
||||
minifyJS: true
|
||||
}))
|
||||
.pipe(gzip({ append: false }))
|
||||
.pipe(cppstringify('html.h', {
|
||||
headerDefineName: '__assets_html',
|
||||
map: HTMLMap,
|
||||
byteArray: true
|
||||
}))
|
||||
.pipe(gulp.dest(config.outputPath));
|
||||
});
|
||||
|
||||
|
||||
gulp.task('compileScss', () =>
|
||||
{
|
||||
return gulp.src(SCSSSrc)
|
||||
.pipe(plumber({
|
||||
errorHandler: error =>
|
||||
{
|
||||
console.log(error.toString());
|
||||
this.emit('end');
|
||||
}}))
|
||||
.pipe(print(filepath => { return 'SCSS: ' + filepath }))
|
||||
.pipe(sass({
|
||||
includePaths: ['node_modules/milligram/src']
|
||||
}))
|
||||
.pipe(cleanCSS({compatibility: 'ie9'}))
|
||||
.pipe(concat('bundle.css'))
|
||||
.pipe(gulp.dest(config.distPath));
|
||||
});
|
||||
|
||||
|
||||
gulp.task('compileJS', () =>
|
||||
{
|
||||
return gulp.src(JSSrc)
|
||||
.pipe(plumber({
|
||||
errorHandler: error =>
|
||||
{
|
||||
console.log(error.toString());
|
||||
this.emit('end');
|
||||
}}))
|
||||
.pipe(print(filepath => { return 'JS: ' + filepath }))
|
||||
.pipe(concat('bundle.js'))
|
||||
.pipe(uglify())
|
||||
.pipe(gulp.dest(config.distPath));
|
||||
});
|
||||
|
||||
|
||||
gulp.task('embedJS', gulp.series('compileJS', () =>
|
||||
{
|
||||
return gulp.src([config.distPath + 'bundle.js'])
|
||||
.pipe(gzip({ append: false }))
|
||||
.pipe(cppstringify('js.h', {
|
||||
headerDefineName: '__assets_js',
|
||||
map: JSMap,
|
||||
byteArray: true
|
||||
}))
|
||||
.pipe(gulp.dest(config.outputPath));
|
||||
}));
|
||||
|
||||
|
||||
gulp.task('embedCSS', gulp.series('compileScss', () =>
|
||||
{
|
||||
return gulp.src([config.distPath + 'bundle.css'])
|
||||
.pipe(gzip({ append: false }))
|
||||
.pipe(cppstringify('css.h', {
|
||||
headerDefineName: '__embed_css',
|
||||
map: CSSMap,
|
||||
byteArray: true
|
||||
}))
|
||||
.pipe(gulp.dest(config.outputPath));
|
||||
}));
|
||||
|
||||
|
||||
gulp.task('watch', gulp.series(
|
||||
'compileScss',
|
||||
'compileJS',
|
||||
() =>
|
||||
{
|
||||
watch(config.assetsPath + '*.scss', () => { gulp.series('compileScss')(); });
|
||||
watch(config.assetsPath + '*.js', () => { gulp.series('compileJS')(); });
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
|
||||
var version = false;
|
||||
function getVersion(callback)
|
||||
{
|
||||
return new Promise((resolve, reject) =>
|
||||
{
|
||||
if (version !== false)
|
||||
{
|
||||
resolve(version);
|
||||
return;
|
||||
}
|
||||
|
||||
var versionData = '';
|
||||
const cmd = spawn('gitversion');
|
||||
|
||||
cmd.stdout.on('data', data =>
|
||||
{
|
||||
versionData += data;
|
||||
});
|
||||
|
||||
cmd.stderr.on('data', data =>
|
||||
{
|
||||
console.log(data.toString().trim());
|
||||
});
|
||||
|
||||
cmd.on('exit', code =>
|
||||
{
|
||||
if (code != 0)
|
||||
{
|
||||
reject(code);
|
||||
return;
|
||||
}
|
||||
|
||||
var version = JSON.parse(versionData);
|
||||
Promise.resolve(callback(version))
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
gulp.task('embedVersion', () =>
|
||||
{
|
||||
return getVersion(version =>
|
||||
{
|
||||
return new Promise((resolve, reject) =>
|
||||
{
|
||||
var headerFile = "#ifndef __assets_version\r\n";
|
||||
headerFile += "#define __assets_version\r\n\r\n";
|
||||
headerFile += "const uint8_t VersionMajor = " + version.Major + ";\r\n";
|
||||
headerFile += "const uint8_t VersionMinor = " + version.Minor + ";\r\n";
|
||||
headerFile += "const uint8_t VersionPatch = " + version.Patch + ";\r\n";
|
||||
headerFile += "const uint8_t VersionMetadata = " + (version.BuildMetaData ? version.BuildMetaData : '0') + ";\r\n";
|
||||
|
||||
headerFile += "const char VersionBranch[] = \"" + version.BranchName + "\";\r\n";
|
||||
|
||||
headerFile += "const char VersionSemVer[] = \"" + version.SemVer + "\";\r\n";
|
||||
headerFile += "const char VersionFullSemVer[] = \"" + version.FullSemVer + "\";\r\n";
|
||||
|
||||
headerFile += "const char VersionCommitDate[] = \"" + version.CommitDate + "\";\r\n";
|
||||
|
||||
headerFile += "\r\n#endif\r\n";
|
||||
|
||||
fs.writeFile(config.outputPath + 'version.h', headerFile, err =>
|
||||
{
|
||||
if (err)
|
||||
reject(err);
|
||||
else
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
gulp.task('copyBinary', () =>
|
||||
{
|
||||
if (!fs.existsSync(config.firmwareOutputPath))
|
||||
fs.mkdirSync(config.firmwareOutputPath, '0777', true);
|
||||
|
||||
return getVersion(version =>
|
||||
{
|
||||
return new Promise((resolve, reject) =>
|
||||
{
|
||||
var target = path.join(config.firmwareOutputPath, version.FullSemVer + '.bin');
|
||||
console.log('Target: ' + target);
|
||||
|
||||
var reader = fs.createReadStream(config.firmwareArtifact);
|
||||
reader.pipe(fs.createWriteStream(target));
|
||||
reader.on('end', resolve);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
gulp.task('embedAssets', gulp.series('embedHTML', 'embedJS', 'embedCSS', 'embedVersion'));
|
||||
|
||||
|
||||
|
||||
// PlatformIO
|
||||
const spawn = require('child_process').spawn;
|
||||
const argv = require('yargs').argv;
|
||||
|
||||
var platformio = function(target)
|
||||
{
|
||||
var args = ['run'];
|
||||
if ("e" in argv)
|
||||
{
|
||||
args.push('-e');
|
||||
args.push(argv.e);
|
||||
}
|
||||
|
||||
if ("p" in argv)
|
||||
{
|
||||
args.push('--upload-port');
|
||||
args.push(argv.p);
|
||||
}
|
||||
|
||||
if (target)
|
||||
{
|
||||
args.push('-t');
|
||||
args.push(target);
|
||||
}
|
||||
|
||||
const cmd = spawn('platformio', args);
|
||||
cmd.stdout.on('data', data =>
|
||||
{
|
||||
console.log(data.toString().trim());
|
||||
});
|
||||
|
||||
cmd.stderr.on('data', data =>
|
||||
{
|
||||
console.log(data.toString().trim());
|
||||
});
|
||||
|
||||
cmd.on('exit', code =>
|
||||
{
|
||||
if (code != 0) return;
|
||||
gulp.start('copyBinary');
|
||||
});
|
||||
}
|
||||
|
||||
gulp.task('upload', gulp.series('embedAssets', () => { platformio('upload'); }));
|
||||
gulp.task('build', gulp.series('embedAssets', () => { platformio(false); }));
|
||||
gulp.task('default', gulp.series('embedAssets'));
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"name": "rgbwifi",
|
||||
"version": "1.0.0",
|
||||
"description": "RGBWifi",
|
||||
"main": "gulpfile.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "node devserver.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.x2software.net/pub/RGBWifi.git"
|
||||
},
|
||||
"author": "Mark van Renswoude <mark@x2software.net>",
|
||||
"license": "Unlicense",
|
||||
"devDependencies": {
|
||||
"axios": "^0.20.0",
|
||||
"body-parser": "^1.18.2",
|
||||
"child_process": "^1.0.2",
|
||||
"express": "^4.16.2",
|
||||
"gulp-clean-css": "^3.9.0",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-debounced-watch": "^1.0.4",
|
||||
"gulp-gzip": "^1.4.1",
|
||||
"gulp-htmlmin": "^3.0.0",
|
||||
"gulp-plumber": "^1.1.0",
|
||||
"gulp-print": "^2.0.1",
|
||||
"gulp-sass": "^3.1.0",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"path": "^0.12.7",
|
||||
"through2": "^2.0.3",
|
||||
"vinyl": "^2.1.0",
|
||||
"vue": "^2.5.13",
|
||||
"vue-i18n": "^7.3.3",
|
||||
"yargs": "^16.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"gulp": "^4.0.2",
|
||||
"npm": "^6.14.8"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; http://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:esp12e]
|
||||
platform = https://github.com/platformio/platform-espressif8266.git
|
||||
board = esp12e
|
||||
framework = arduino
|
||||
upload_speed = 115200
|
||||
lib_deps =
|
||||
ArduinoJson
|
||||
ESP Async WebServer
|
||||
EspSaveCrash
|
||||
NeoPixelBus
|
|
@ -0,0 +1,106 @@
|
|||
#ifndef __embed_css
|
||||
#define __embed_css
|
||||
|
||||
#include <pgmspace.h>
|
||||
|
||||
const uint8_t EmbeddedBundleCSS[] PROGMEM = {
|
||||
0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xad,0x59,0xdb,0x6e,0xa3,0x38,0x18,0x7e,0x95,0x48,
|
||||
0xd5,0x48,0xd3,0x15,0x20,0x92,0x94,0xb4,0x05,0xed,0x6a,0x57,0xfb,0x06,0x7b,0xb1,0x37,0xa3,0x5e,0x18,
|
||||
0x30,0xc1,0x2a,0xc1,0xc8,0x38,0x4d,0x5b,0xc4,0xbb,0xef,0xef,0x13,0xb1,0xc1,0xc9,0x64,0x66,0x47,0xa8,
|
||||
0x4d,0xb0,0xfd,0x1f,0xfd,0x1f,0x3e,0x3b,0x35,0x3f,0x34,0x43,0x4e,0xdf,0xc3,0x9e,0x7c,0x92,0x76,0x9f,
|
||||
0xe6,0x94,0x95,0x98,0x85,0x30,0x92,0x55,0xb4,0xe5,0x62,0x18,0xa7,0xbb,0x4d,0x94,0x7c,0x19,0x7f,0x0b,
|
||||
0x52,0x54,0x71,0xcc,0x82,0x34,0xc7,0x15,0x65,0xd8,0x26,0x23,0x6d,0x8d,0x19,0xe1,0x63,0x4e,0xcb,0x8f,
|
||||
0x21,0x47,0xc5,0xeb,0x9e,0xd1,0x63,0x5b,0x86,0x05,0x6d,0x28,0x4b,0xef,0xe2,0x38,0xce,0xf4,0xd7,0xaa,
|
||||
0xaa,0x14,0xe7,0x0a,0x1d,0x48,0xf3,0x91,0xfe,0x8b,0x59,0x89,0x5a,0x14,0xfc,0xc5,0x08,0x6a,0x82,0x1e,
|
||||
0xb5,0x7d,0xd8,0x03,0xab,0xca,0x12,0xbf,0x8e,0xb6,0xf8,0xa0,0xde,0x4f,0x98,0xec,0x6b,0x9e,0x6e,0x81,
|
||||
0x5f,0x83,0x39,0x28,0x13,0xf6,0x1d,0x2a,0x84,0x06,0x51,0xbc,0x86,0x45,0x0d,0x69,0x71,0x58,0xab,0x45,
|
||||
0x40,0x96,0x75,0xa8,0x2c,0x61,0x16,0xec,0xe1,0x9c,0x1e,0xd2,0x2d,0xc3,0x87,0xf1,0xcf,0x03,0x2e,0x09,
|
||||
0x5a,0xf5,0x05,0xc3,0xb8,0x5d,0xa1,0xb6,0x5c,0x7d,0x3d,0x90,0x36,0x3c,0x91,0x92,0xd7,0xe9,0xe3,0xee,
|
||||
0xa9,0x7b,0xbf,0x1f,0xa4,0x1d,0x86,0x98,0xd3,0x4e,0x51,0x8e,0x68,0xe0,0xf8,0x9d,0x87,0x25,0x2e,0x28,
|
||||
0x43,0x9c,0xd0,0x36,0x6d,0x69,0x8b,0xc7,0x6f,0x6f,0x61,0xd1,0x50,0xf4,0xfa,0x32,0x94,0xa4,0xef,0x1a,
|
||||
0xf4,0xa1,0x86,0xef,0x0a,0x50,0x19,0x81,0x46,0xcc,0x72,0x49,0x7a,0xb7,0x89,0xc5,0x93,0x1d,0x10,0xdb,
|
||||
0x83,0x58,0xc1,0x7c,0x03,0xcc,0x8d,0xaa,0xe9,0x5a,0xbc,0x48,0xd7,0xd6,0xa8,0xa4,0xa7,0x34,0x5e,0xc5,
|
||||
0xab,0x24,0xee,0xde,0x57,0x77,0x55,0x51,0xed,0x8a,0x2a,0x53,0x5b,0x94,0xf6,0xb4,0x21,0xe5,0x6a,0x2d,
|
||||
0x26,0xc0,0xbd,0x37,0x59,0x65,0x29,0x64,0x8d,0x1b,0x4d,0x1a,0x5c,0xf1,0x14,0x1d,0x39,0x35,0x03,0x4c,
|
||||
0xba,0x51,0x8c,0x8c,0x63,0x54,0x63,0x04,0x52,0x87,0x8e,0xf6,0x44,0x1a,0xce,0x70,0x03,0x1e,0x78,0xc3,
|
||||
0x66,0x66,0x45,0x0e,0xfb,0xa1,0x02,0x2f,0xf0,0x54,0x30,0x72,0x79,0xac,0x2f,0xf9,0x1d,0xbd,0x4f,0x1a,
|
||||
0x3e,0x0a,0x0d,0x0d,0xb3,0xe8,0x44,0x2a,0xd2,0x73,0xc4,0x8f,0xfd,0x50,0x34,0x18,0x31,0x08,0x4c,0x5e,
|
||||
0xdb,0x3e,0x53,0x1b,0x72,0x8b,0xd5,0x3e,0x9e,0x93,0x19,0x28,0x07,0x3f,0x1e,0x39,0xce,0x94,0xa2,0x71,
|
||||
0x26,0x78,0xc7,0x93,0xbd,0x36,0xd1,0x2a,0x22,0x6d,0x49,0x0a,0xc4,0x29,0x9b,0xf6,0x99,0xb4,0x32,0xe2,
|
||||
0xf2,0x86,0x16,0xaf,0x99,0x92,0x2a,0xf7,0xcf,0x84,0xa0,0xda,0x4b,0x99,0x52,0x0c,0x95,0xe4,0xd8,0xa7,
|
||||
0x49,0xfc,0xc5,0xf5,0x4d,0x94,0x08,0x4b,0xae,0xcb,0xfb,0x56,0x22,0x8e,0x42,0x35,0xfc,0x3b,0x6c,0x62,
|
||||
0x8b,0x0b,0x8e,0xcb,0x17,0x4f,0xa6,0x6d,0x9f,0x77,0x3f,0xc2,0x0b,0xec,0xb0,0xd9,0xcd,0x23,0x6b,0xcf,
|
||||
0xf0,0xc7,0x4f,0xa8,0x06,0x61,0xec,0xd3,0xad,0x7a,0xde,0xfe,0x08,0x33,0xcc,0x18,0x65,0x3e,0x3e,0x05,
|
||||
0x84,0x7b,0x94,0x1f,0x21,0xa3,0xdb,0x20,0x2a,0x6a,0x5c,0xbc,0xae,0x22,0x11,0xd9,0x8c,0x36,0x41,0xd4,
|
||||
0x52,0x0e,0x9c,0x0b,0x99,0x9d,0x41,0xd4,0xa1,0x16,0x37,0x2b,0xf5,0x11,0x8a,0xa4,0x9e,0x0d,0x29,0x6d,
|
||||
0x82,0x48,0x6c,0x0e,0xb5,0xb8,0x9c,0x10,0x6b,0xc1,0x8a,0x40,0x4b,0xa9,0xb7,0x01,0x69,0xbb,0x23,0xff,
|
||||
0xc6,0x3f,0x3a,0xfc,0x7b,0x7f,0xcc,0x0f,0x84,0xbf,0x04,0x3d,0x6e,0xc0,0x58,0xe3,0x35,0xe1,0x2f,0xe5,
|
||||
0xb9,0xbb,0xf5,0x7a,0x3d,0xdb,0xf5,0x2d,0xa4,0x99,0x95,0xd3,0xa4,0xed,0x31,0x87,0xbc,0x16,0x34,0x6c,
|
||||
0x9f,0xa3,0xaf,0x9b,0x24,0x09,0xcc,0x5f,0xb4,0xbe,0x0f,0xcc,0x82,0x50,0xac,0xd8,0x9a,0x55,0x71,0x20,
|
||||
0x9e,0x68,0x7b,0x9e,0x8f,0x2f,0x32,0x89,0x9f,0xee,0x03,0x35,0xb7,0x99,0x91,0xaf,0x93,0x7b,0xe3,0xbe,
|
||||
0x08,0x15,0x22,0x87,0x03,0xfd,0x9a,0xea,0x57,0x77,0xd2,0x9d,0x93,0x7e,0xd0,0x53,0xae,0x3b,0x2c,0x0f,
|
||||
0xb5,0xc7,0x43,0x8e,0x99,0x33,0xd4,0xa1,0xbe,0x3f,0x81,0x4f,0x5e,0x3c,0x9e,0x74,0x78,0xab,0x19,0x51,
|
||||
0x67,0x5f,0x02,0xf1,0x1f,0x31,0x8c,0xae,0xfb,0xf8,0xdc,0x64,0xe4,0xb0,0x99,0xf3,0x7a,0x7b,0xee,0x8c,
|
||||
0x4d,0x62,0xdc,0xe4,0x75,0xe1,0xa8,0x03,0x40,0x6a,0x36,0xdc,0xd2,0xb7,0x46,0xb5,0x14,0x3a,0x55,0xfe,
|
||||
0x4a,0x78,0x88,0xba,0x0e,0xaa,0x17,0x6a,0x0b,0x2c,0x7b,0x42,0x16,0x1e,0xe8,0xe7,0x62,0x70,0xf6,0x3e,
|
||||
0x05,0xb7,0x2d,0xdc,0x71,0x98,0xbf,0xfe,0x98,0x0e,0x02,0xf6,0x80,0x99,0xa6,0xe1,0x96,0x65,0x99,0xd9,
|
||||
0xed,0xe7,0x21,0x16,0x4f,0x56,0x1c,0x59,0x0f,0xd3,0x1d,0x25,0x2d,0xb4,0x51,0xa7,0x71,0xca,0xd2,0x6a,
|
||||
0x22,0xa4,0xa2,0xc5,0xb1,0x9f,0x02,0xc4,0x7d,0xab,0xe9,0x1b,0x24,0x8f,0xb3,0xd0,0x59,0xe7,0x2c,0x5b,
|
||||
0x5a,0xa1,0x49,0x3c,0xf1,0x70,0x71,0x42,0xb2,0x1a,0x2e,0x58,0x96,0xc4,0xe2,0xc9,0xe8,0x91,0x0b,0x6b,
|
||||
0xd2,0xf8,0xff,0x46,0xb9,0xab,0xec,0xc5,0x19,0x4d,0x63,0xb4,0x2a,0x8a,0xc2,0xd1,0x6a,0xf3,0x24,0x1e,
|
||||
0xa3,0x4b,0xd8,0x31,0x02,0xe5,0xff,0xc3,0xb7,0xa9,0x0e,0xd5,0x66,0x97,0xa0,0xf5,0x9c,0xca,0xdd,0x0d,
|
||||
0x33,0x9a,0xfa,0x47,0x7f,0xb9,0xdb,0x1d,0xfd,0x76,0x8f,0x9b,0x7c,0x37,0x22,0x2d,0xd4,0x8f,0x8b,0xa2,
|
||||
0x16,0xbd,0x91,0xbd,0x1c,0xb8,0xd6,0xc5,0x23,0x0e,0x5d,0xf8,0x0f,0xc3,0xc9,0x46,0x23,0xa2,0xfc,0xcd,
|
||||
0x2a,0x69,0xec,0x2e,0x4f,0x2b,0xc2,0x7a,0x1e,0x16,0x35,0x69,0x4a,0x87,0x34,0x5e,0x56,0x60,0x59,0x2d,
|
||||
0xe1,0x73,0xc6,0xa1,0x41,0x13,0x83,0x99,0x28,0x59,0x79,0x25,0xe1,0x5c,0xa8,0x70,0xd4,0x12,0x0b,0x65,
|
||||
0x9f,0x21,0xb4,0x32,0xfc,0x9e,0xc2,0xc6,0x81,0xc3,0x7a,0x69,0xb7,0x8c,0x0a,0xd1,0x48,0x2d,0x44,0xfb,
|
||||
0xd4,0xf1,0x4c,0x7a,0x0c,0x35,0x64,0xdf,0xa6,0x05,0x96,0x39,0x38,0x43,0x84,0xa3,0xd3,0xcb,0xfe,0x9e,
|
||||
0xa0,0xdb,0x24,0xb6,0x22,0xef,0xb8,0xcc,0x26,0x00,0x69,0x84,0xef,0x76,0xbb,0xdb,0x70,0x91,0x9f,0xbd,
|
||||
0x5a,0x93,0xc8,0x12,0x22,0x3d,0x09,0x88,0x65,0x74,0x75,0x71,0x23,0xe1,0xf9,0x11,0xe5,0x4f,0x73,0xc4,
|
||||
0xba,0x8e,0x35,0x30,0xb5,0x71,0xff,0xac,0xe6,0x98,0x7a,0x15,0x25,0xa0,0xbd,0x36,0x5e,0x03,0x75,0x85,
|
||||
0x88,0x17,0x58,0xf3,0x87,0xcd,0x1a,0x54,0x18,0x2d,0x4d,0x58,0x45,0x07,0xdc,0xf7,0x68,0x8f,0x87,0x53,
|
||||
0x4d,0x38,0x96,0xc7,0x08,0x9c,0x76,0x0c,0xbb,0xcb,0x22,0x09,0x42,0x1c,0x7b,0x9f,0x1f,0xb7,0x68,0x0b,
|
||||
0xf9,0x2c,0x81,0x87,0x46,0x0e,0xfe,0x7a,0x3c,0x33,0xf7,0xd8,0x8b,0xe3,0x8a,0xc4,0x0c,0xaa,0xec,0xdb,
|
||||
0x92,0x5b,0x7a,0x62,0xa8,0xb3,0x43,0xc0,0xe3,0x15,0x31,0x34,0x2e,0x10,0x8f,0x8b,0x5d,0x06,0x4f,0xad,
|
||||
0xbf,0x86,0x56,0x77,0xb0,0xcd,0x06,0xad,0x8a,0xef,0x1e,0x80,0xaf,0x05,0x36,0x28,0xc7,0x67,0x71,0xf2,
|
||||
0xcd,0x6f,0xb7,0x9d,0x86,0xd2,0x0a,0xc8,0x05,0x0e,0x0e,0x6d,0x74,0xb8,0x83,0x75,0x9a,0xa9,0xfa,0x8f,
|
||||
0xcb,0xb9,0x35,0x8b,0x71,0xc7,0xaa,0x5d,0x2c,0x1e,0xc3,0x02,0x54,0x40,0x79,0x83,0x4b,0x43,0x6a,0xde,
|
||||
0x07,0xed,0x7e,0xd8,0x4e,0x90,0xdb,0xd0,0x13,0x2e,0xe7,0x24,0xae,0x4d,0xf3,0x61,0x2b,0x75,0xc7,0x05,
|
||||
0x40,0x74,0xdf,0x05,0x84,0x95,0xc7,0xbc,0x39,0xd4,0x9f,0x13,0x9a,0x85,0xd6,0xb9,0x78,0x79,0x14,0x11,
|
||||
0xbb,0xff,0x60,0xb2,0x4f,0x7c,0x51,0x5b,0x65,0xed,0xd4,0x4e,0x14,0x31,0xbf,0xa7,0x26,0x55,0x2c,0x87,
|
||||
0xc9,0x96,0x64,0xe7,0xe7,0x7a,0x86,0x82,0x04,0x22,0x74,0xc3,0xca,0xb0,0xf1,0x6b,0x97,0x5c,0xd5,0x4e,
|
||||
0x96,0x58,0xff,0xfe,0xba,0x8e,0xd2,0x27,0x8d,0x25,0xf2,0x7a,0xba,0x37,0xd5,0x5b,0x31,0x17,0x15,0x65,
|
||||
0xa3,0xd0,0x5b,0xc6,0x01,0x28,0xf5,0x15,0x65,0x87,0x94,0x51,0x38,0x28,0xe0,0xaf,0xe1,0x43,0x52,0xe2,
|
||||
0xfd,0xbd,0x6d,0xa1,0x84,0xcd,0xb1,0x0b,0xf4,0x1c,0x9c,0x67,0xd9,0x2d,0x58,0x85,0x26,0xcc,0xac,0xfc,
|
||||
0x5b,0x43,0xae,0xfd,0x04,0xa4,0x9d,0x03,0x57,0x0f,0xf2,0x3a,0x17,0x44,0xa7,0x02,0xea,0x84,0x8c,0x21,
|
||||
0x6a,0xcc,0xc9,0xe2,0x2a,0xf1,0x4d,0xb7,0x28,0xb6,0x04,0xdb,0x1c,0x70,0xe2,0x1e,0xbf,0x38,0xf6,0xb2,
|
||||
0x45,0xbd,0x91,0xc7,0xf6,0x7a,0x3d,0x9c,0x1b,0xd7,0xe6,0xbc,0x08,0x3a,0x71,0xbd,0xd1,0x81,0xdc,0x93,
|
||||
0x06,0x72,0xdc,0xb9,0xb2,0x99,0xad,0xdc,0xda,0x8d,0x70,0x09,0x8f,0x96,0xa4,0x67,0xc5,0xa5,0x12,0x0f,
|
||||
0x83,0xbd,0xe2,0x81,0x4d,0xd6,0x98,0xa4,0x7d,0xf1,0xe4,0x7b,0x76,0x49,0xe6,0x4e,0x3c,0xa3,0x5b,0xc2,
|
||||
0x9c,0xda,0x75,0xb5,0x00,0x4b,0xba,0x50,0x95,0xbc,0xc1,0x39,0xcb,0x6f,0x6e,0xbd,0x5f,0x8a,0x6a,0xca,
|
||||
0xc8,0xa7,0xe8,0xb9,0x8d,0x85,0x8c,0x46,0x6b,0x78,0x75,0xb9,0xc0,0x3a,0xcb,0x3c,0x21,0x7a,0x61,0xfa,
|
||||
0x1c,0xae,0x17,0x16,0xa8,0xd0,0xb5,0x27,0xa7,0x30,0xf6,0xd6,0x79,0x75,0xdb,0x23,0x2d,0xd7,0xc1,0x2b,
|
||||
0x2b,0xde,0x99,0x5e,0xdd,0x17,0xda,0x06,0xc2,0x2c,0x34,0xc3,0x99,0xcf,0x5d,0x5c,0x64,0xed,0xd9,0x2c,
|
||||
0x1a,0xf5,0x4d,0x09,0x48,0x15,0x91,0x31,0xcc,0x60,0xe4,0x12,0x50,0x8d,0x11,0xe0,0x57,0xda,0x89,0xf2,
|
||||
0xd5,0x3b,0xd8,0x30,0xb1,0x0e,0x38,0xce,0xfd,0xd2,0x55,0x4c,0x26,0xb3,0x21,0xea,0xa1,0x62,0x01,0xb4,
|
||||
0x1b,0x16,0x60,0xad,0xe7,0xb8,0x73,0xa4,0x6c,0xad,0x84,0x62,0xd3,0xc1,0xca,0xd7,0x60,0x05,0xe9,0x4a,
|
||||
0x73,0x3e,0x5f,0xd1,0x39,0xa4,0x0f,0x46,0xc4,0x2a,0x7a,0x43,0xcd,0x11,0x7f,0xe7,0x06,0x2b,0x92,0xd9,
|
||||
0x6c,0x37,0xb0,0x0b,0xdc,0xe7,0x86,0x5d,0x3c,0xb8,0x9e,0xab,0x93,0x29,0xf3,0x72,0x3b,0x66,0x28,0x1b,
|
||||
0xce,0xd3,0x72,0x70,0x59,0xb8,0xac,0x53,0x99,0x92,0x94,0xa6,0x46,0x94,0x7a,0x0f,0x79,0x0d,0xf1,0x7b,
|
||||
0x51,0xbe,0x5f,0x9f,0x8d,0x75,0xd9,0xb6,0xf1,0x5f,0xb6,0xd9,0xba,0xe8,0x1b,0x54,0x17,0x98,0x59,0x0a,
|
||||
0x89,0xc3,0xb9,0xac,0x8c,0x5a,0x99,0x5f,0x29,0x44,0x5f,0x29,0x79,0xb0,0xe4,0x77,0x81,0xf0,0x62,0xaf,
|
||||
0x5a,0x2a,0x2e,0xca,0xec,0x92,0xea,0x09,0x7e,0x19,0xd7,0x4e,0x75,0x91,0x77,0x5e,0x83,0x0f,0x69,0x9b,
|
||||
0x9b,0x83,0xd1,0x77,0x3d,0x36,0x2c,0x4f,0x52,0xfa,0x34,0x95,0x4d,0x3f,0x17,0x08,0x5e,0xa6,0x59,0xbb,
|
||||
0xf6,0x5c,0x6d,0x62,0x5e,0x79,0xba,0xf4,0x59,0x05,0x5f,0xd8,0xec,0x5b,0x28,0x0f,0xe5,0x22,0x87,0xad,
|
||||
0x52,0x74,0x61,0xa5,0x46,0x7a,0xbe,0x39,0xe3,0x48,0x8f,0x42,0xea,0xf7,0x8c,0xd9,0xa9,0x70,0x3a,0x17,
|
||||
0x3a,0xb6,0x6d,0x63,0xf1,0x4c,0xb6,0xab,0xa2,0x20,0xb9,0xe8,0xab,0x83,0xb9,0x53,0x6d,0xda,0xfc,0x01,
|
||||
0x25,0x4f,0x8e,0x5f,0x74,0x7b,0xb9,0x02,0xdd,0xe5,0xc5,0x7c,0x74,0xc2,0xf8,0xb5,0x44,0x1f,0xfd,0x32,
|
||||
0xa1,0xcd,0x8c,0x81,0xb3,0x8a,0xea,0x49,0x4c,0x55,0xa0,0x41,0x28,0xc3,0x24,0x34,0x77,0x23,0x72,0x08,
|
||||
0x62,0xe5,0x0d,0xeb,0xa1,0x41,0x02,0x2d,0x55,0x63,0xa8,0xf8,0x91,0x85,0x7f,0xac,0xa2,0xa4,0xb7,0x89,
|
||||
0x1d,0x2a,0x4e,0x07,0xbd,0x4c,0x84,0x91,0xcc,0x22,0x27,0xf8,0xe4,0x08,0x54,0x39,0x68,0x28,0xdc,0x73,
|
||||
0x74,0xbe,0x62,0xe8,0xc3,0xf3,0x17,0x97,0xfc,0x96,0x5a,0xe9,0xae,0xbf,0xa5,0x66,0xaa,0xfe,0x92,0xb9,
|
||||
0xa8,0x5f,0x32,0xc1,0x6d,0x79,0xab,0xc6,0x17,0x7a,0xe2,0x99,0xd1,0x45,0xdd,0x15,0x9e,0xb6,0x54,0x97,
|
||||
0x8b,0x2f,0x29,0xae,0xef,0x38,0xae,0xe9,0xbd,0xec,0xbe,0x11,0xc3,0x3d,0xe6,0xff,0x60,0xd4,0xcf,0xee,
|
||||
0x59,0x36,0x20,0xf6,0x3f,0x57,0x7a,0x1c,0xe6,0x0a,0x1c,0x00,0x00};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,108 @@
|
|||
#ifndef __assets_html
|
||||
#define __assets_html
|
||||
|
||||
#include <pgmspace.h>
|
||||
|
||||
const uint8_t EmbeddedIndex[] PROGMEM = {
|
||||
0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xcd,0x58,0xd9,0x7a,0xe2,0x38,0x16,0x7e,0x15,0x97,
|
||||
0x7b,0xa6,0xa9,0xfa,0xd2,0x61,0x0b,0x95,0xaa,0x64,0x20,0xd3,0x66,0xdf,0xc3,0x1e,0xe0,0x4e,0xb6,0x85,
|
||||
0xad,0x20,0x2f,0x91,0x04,0x84,0xaa,0xce,0xbb,0x8f,0xe4,0x05,0x8c,0xe3,0x6c,0xd5,0x7d,0x31,0x5c,0x00,
|
||||
0xd2,0xd9,0xfe,0x73,0x7c,0x16,0x59,0xc5,0x4f,0xd5,0xdb,0xca,0x64,0x31,0xa8,0x49,0x26,0xb3,0xf0,0x4d,
|
||||
0x31,0xf8,0x86,0x40,0xbf,0x29,0x5a,0x90,0x01,0x49,0x33,0x01,0xa1,0x90,0x95,0xe4,0xe9,0xa4,0x7e,0xfe,
|
||||
0x5d,0xbe,0x29,0x32,0xc4,0x30,0xbc,0x19,0x35,0xca,0x77,0x68,0x85,0x8a,0x19,0x7f,0xe9,0xf3,0xda,0xc0,
|
||||
0x82,0x25,0x99,0x99,0xd0,0x82,0xe7,0x9a,0x83,0x1d,0x22,0x4b,0x9a,0x63,0x33,0x68,0x73,0xf1,0xdf,0xb2,
|
||||
0xde,0x47,0x3e,0x61,0xdd,0x22,0xb8,0x73,0x1d,0xc2,0x22,0x7c,0x3b,0xa4,0x33,0xb3,0xa4,0xc3,0x2d,0xd2,
|
||||
0xe0,0xb9,0xb7,0xf8,0x03,0xd9,0x88,0x21,0x80,0xcf,0xa9,0x06,0x30,0x2c,0xe5,0xb8,0x0a,0x8c,0xec,0xb5,
|
||||
0x44,0x20,0x2e,0xc9,0x94,0xed,0x31,0xa4,0x26,0x84,0x5c,0x87,0x49,0xe0,0xaa,0x24,0xab,0x1b,0x5b,0xc7,
|
||||
0x30,0xad,0x51,0xca,0x19,0xa9,0x46,0x90,0xcb,0x24,0x4a,0xb4,0x03,0xe1,0x5e,0xec,0x67,0x7c,0x02,0xff,
|
||||
0xe3,0xbb,0xaa,0x3a,0xfa,0xfe,0xa6,0xa8,0xa3,0xad,0x84,0xf4,0x92,0x0c,0x5c,0x57,0xf6,0x57,0xdb,0x73,
|
||||
0x0d,0x3b,0x60,0xed,0x2f,0x34,0x0c,0x28,0x2d,0xc9,0xb6,0xc3,0xb8,0xe7,0x1a,0x60,0xc8,0xb1,0x2b,0x1c,
|
||||
0x36,0x40,0x36,0x24,0xf2,0x8b,0x2c,0xb2,0x74,0x1d,0xec,0xfe,0x94,0x20,0x21,0x0e,0xb9,0x96,0xa2,0x64,
|
||||
0xe9,0x53,0x49,0xb2,0x37,0x18,0x4b,0xbf,0xff,0x7e,0xb2,0x9f,0xf6,0x78,0xa5,0x27,0x99,0x63,0x40,0xab,
|
||||
0x53,0x95,0xa1,0x8c,0x2c,0xfd,0xa9,0x61,0xa4,0xad,0xd3,0x2e,0x81,0x5b,0x2f,0x7a,0x26,0xd2,0x61,0x3f,
|
||||
0x6a,0x9c,0x87,0xc0,0x05,0x76,0x88,0xcb,0x82,0x94,0x02,0x03,0xca,0x37,0x3f,0x7f,0x9e,0x1a,0x0b,0x08,
|
||||
0xd2,0xd3,0x13,0x0f,0x0d,0x17,0xe0,0x81,0xe1,0xee,0x84,0xdf,0x61,0x5c,0xb4,0x64,0x6f,0x45,0x08,0xbd,
|
||||
0x3d,0x64,0x19,0x7e,0xa8,0x75,0xc0,0xc0,0x35,0xb2,0xb8,0xc6,0x8c,0x6b,0x1b,0xff,0x51,0x01,0x85,0x97,
|
||||
0x85,0x3f,0xd0,0xac,0x7c,0x3b,0xda,0x65,0x3b,0x0d,0xc3,0x51,0xf8,0xa7,0x3f,0x9e,0x9a,0xb5,0xa9,0xc1,
|
||||
0xff,0x55,0xc5,0x52,0xd9,0x55,0x94,0x05,0xff,0x29,0xcf,0x95,0xad,0xd5,0x14,0x1b,0x8d,0xf9,0xa8,0x7e,
|
||||
0xd7,0x1c,0x4d,0xd4,0xfc,0x32,0xab,0xe7,0xeb,0xfb,0xe5,0xb0,0x5c,0x5e,0x36,0xae,0xd0,0x72,0x5c,0x6e,
|
||||
0xab,0x77,0x75,0x7b,0x39,0x6b,0xe3,0xc5,0xdd,0xe8,0xab,0xa6,0x61,0x3c,0x10,0x02,0xf3,0x72,0x7b,0x54,
|
||||
0xab,0x4f,0x61,0x9f,0xd0,0x3b,0xbd,0xd6,0x37,0xee,0x95,0x61,0x57,0x5b,0x94,0x35,0x65,0xa0,0x29,0x15,
|
||||
0x7d,0xd8,0x2f,0x28,0xfd,0x7c,0xaf,0x52,0x30,0x46,0x74,0xd1,0xbe,0xaa,0xf5,0x75,0x65,0xb0,0x50,0xaa,
|
||||
0x40,0xa9,0x42,0x57,0x9f,0x9a,0xbd,0xdc,0x43,0xfd,0x7e,0x43,0x0c,0xf7,0x6a,0xac,0xf5,0x9a,0x86,0xfe,
|
||||
0x2d,0x77,0x31,0xbb,0x58,0xb1,0xa9,0xfb,0x15,0x36,0x8d,0x5e,0x3d,0x47,0x48,0xa3,0x06,0x36,0x97,0xb3,
|
||||
0x66,0x35,0xdf,0xec,0xa9,0xcd,0xaf,0x0f,0xed,0xdb,0x6e,0x93,0x80,0xb3,0xd5,0xfa,0x87,0x4a,0x17,0x23,
|
||||
0x6a,0xf6,0xbe,0xbb,0xdd,0x89,0x31,0x6d,0x19,0x63,0x63,0xbb,0xe9,0xf5,0x9c,0xc5,0xee,0x0c,0xf5,0x16,
|
||||
0x13,0x72,0x39,0x34,0xfb,0x8b,0x1e,0xe9,0xa3,0xfe,0x7e,0xd7,0xea,0xe2,0xfd,0xac,0xa3,0x6b,0xfb,0xfd,
|
||||
0x80,0x5a,0xda,0x88,0xee,0xa7,0x5f,0xb3,0x6b,0xa3,0xc9,0x86,0xc3,0x4d,0x5e,0xd1,0xfb,0xed,0xba,0x5b,
|
||||
0x5d,0x2b,0x9d,0x42,0x2b,0xd3,0x6d,0xdd,0xf5,0xd4,0xbc,0x42,0x5b,0x65,0xed,0x21,0x8b,0x46,0x0d,0x38,
|
||||
0x6c,0x0c,0x26,0xcb,0xd5,0xec,0x72,0x58,0xcb,0x9e,0x19,0xd5,0x46,0x3d,0x4f,0x1c,0xda,0xa8,0x19,0xbd,
|
||||
0xe1,0x63,0x4b,0x31,0xed,0xa5,0x82,0x06,0xfd,0xef,0x85,0x8d,0x3b,0x5a,0x65,0x33,0xb7,0xd8,0xa5,0xdd,
|
||||
0x4a,0xd9,0xbd,0xd8,0x3f,0x64,0x35,0xd3,0x60,0x95,0xe9,0x74,0x49,0x46,0xbb,0xcb,0x61,0xf5,0xf6,0xa2,
|
||||
0x76,0xd7,0x1c,0x3f,0xd4,0xaf,0x18,0x20,0x4b,0x30,0xee,0xb4,0xe7,0xb0,0x5d,0xd5,0xd5,0x21,0xa6,0xb5,
|
||||
0x6c,0xa7,0x7a,0xd9,0xee,0x67,0x3a,0xce,0x88,0x36,0xcc,0xc7,0x79,0xa7,0x82,0x2b,0x9d,0x66,0xbb,0xb5,
|
||||
0x5a,0x4f,0xcc,0x5d,0xef,0xce,0x54,0x2e,0xf5,0xf2,0xd8,0xc1,0x23,0x74,0xbf,0x6e,0xdf,0xea,0xb9,0xe5,
|
||||
0x74,0x7b,0xb5,0x1f,0x5e,0xdd,0xba,0x0f,0x6a,0xd3,0x45,0x60,0x3a,0x03,0x35,0x75,0x59,0xfb,0xc6,0x5a,
|
||||
0xad,0x7b,0xa7,0xdc,0x99,0xef,0xa9,0x43,0x73,0x5a,0x61,0xf6,0x1d,0xaa,0xdd,0x9a,0xae,0x6e,0xf3,0xaa,
|
||||
0xd6,0xa3,0xb5,0x6f,0xc6,0xfd,0xa6,0xac,0x6f,0xe7,0xa3,0x71,0xbb,0x50,0x3f,0xcb,0xec,0x1e,0x5a,0xf3,
|
||||
0x39,0x69,0x35,0x76,0xd6,0xfc,0xe2,0xc7,0x0e,0x68,0xdd,0xaa,0x09,0xfb,0xb7,0x57,0xb9,0xdb,0xfb,0xee,
|
||||
0xb0,0xa3,0xe7,0x0a,0xb3,0x5e,0xb5,0x62,0x2f,0x8c,0xca,0xe3,0xec,0xbe,0x75,0xd1,0x9f,0xc0,0x9c,0x35,
|
||||
0x76,0x06,0xd5,0xc2,0xd5,0x63,0x61,0x4c,0x78,0x72,0x5c,0x3d,0x0c,0xec,0x02,0x74,0xb6,0x95,0x9e,0x97,
|
||||
0x3d,0x35,0x5c,0x9f,0xac,0xc7,0x9b,0xa1,0x55,0xa9,0xf0,0x4c,0x34,0x73,0x22,0xc5,0xff,0xc5,0x3e,0xa7,
|
||||
0xbc,0xfe,0x94,0xfa,0xe2,0x65,0x36,0xdf,0x2d,0x9a,0x79,0x41,0xa1,0x0c,0xb0,0x0d,0x4d,0xd3,0x3d,0x65,
|
||||
0xd0,0x6a,0x55,0x79,0x41,0x05,0x55,0xf8,0x5f,0x4f,0x28,0xdc,0xe7,0x72,0x67,0x52,0xea,0x5a,0x4a,0xf1,
|
||||
0x9f,0xb8,0x08,0xdf,0x4d,0xf9,0x5a,0xf3,0x27,0xd5,0xb0,0xe3,0x55,0xe5,0xf3,0x9e,0x56,0x09,0x2f,0x1e,
|
||||
0x1b,0x6a,0x41,0x51,0x46,0xf6,0x91,0xad,0x8b,0x2a,0x14,0xcd,0xf2,0x5a,0xd4,0xcd,0xb9,0x2f,0xec,0x2b,
|
||||
0x1a,0xfb,0x46,0x81,0x9b,0x86,0x36,0x50,0x31,0xd4,0x39,0xc0,0x54,0xa0,0x09,0xea,0x29,0x01,0x42,0x47,
|
||||
0xf4,0xb8,0x21,0x07,0x25,0x1b,0x38,0x1f,0x55,0xa1,0x69,0xbc,0xc6,0x5d,0x07,0xd9,0x2c,0x7d,0x8c,0x89,
|
||||
0xc4,0x19,0x5f,0xb2,0x73,0xba,0x8f,0x5c,0x6e,0xec,0x15,0x9d,0x1c,0x86,0x27,0x18,0x84,0xfa,0xd0,0x37,
|
||||
0x7e,0xd5,0x7b,0x03,0xb2,0x3b,0x54,0xf7,0x2c,0x71,0x21,0xdf,0xe0,0xe7,0x2f,0x2f,0xfb,0x47,0x7d,0x46,
|
||||
0xcb,0xd1,0x61,0xcc,0xbf,0x24,0x4d,0x13,0xf8,0xc8,0x3e,0x47,0x90,0xc6,0xbb,0x9d,0xdf,0x73,0x79,0xeb,
|
||||
0xd7,0x91,0x6d,0xc8,0x21,0xd8,0x70,0x1d,0x9a,0x0f,0xd6,0x07,0x4b,0xc1,0xba,0x15,0xfa,0x74,0x1a,0x09,
|
||||
0x5f,0xe7,0xa7,0x80,0x49,0xf4,0x7b,0x3e,0xcf,0x90,0x4a,0xfc,0x96,0x5e,0x0a,0x32,0xf0,0x34,0x3c,0x3b,
|
||||
0x40,0x6c,0x0f,0x81,0x2f,0x6c,0x02,0x3a,0x82,0x7c,0x1a,0xd7,0xc4,0x6c,0xe0,0x9c,0x6e,0x88,0xc4,0x1b,
|
||||
0x16,0x69,0x72,0xa0,0x05,0x4f,0xc1,0xe5,0x2c,0xa1,0x2a,0x8f,0x38,0x82,0x80,0x8a,0x47,0xf0,0x5c,0xcc,
|
||||
0xa7,0xa4,0x23,0x99,0x1e,0xd9,0x8e,0x68,0xf3,0x81,0xd0,0x43,0xd0,0xb5,0xf5,0x84,0x00,0x0d,0xc6,0x54,
|
||||
0x1e,0x09,0x47,0x24,0x20,0x44,0xa2,0x6e,0x18,0xe3,0x1e,0xfb,0x3f,0xe7,0x2e,0xe1,0x83,0x82,0xec,0xc3,
|
||||
0xf1,0x9d,0x01,0x2e,0xca,0x78,0xe2,0x4c,0x88,0x67,0x0c,0x31,0xd9,0x3f,0x6a,0xb4,0xea,0xec,0x6c,0x11,
|
||||
0xe7,0xc0,0x38,0xb8,0x91,0xe2,0xd6,0xc3,0xd1,0xc9,0x67,0x15,0xc4,0x90,0xc1,0xf1,0x3b,0x94,0x7a,0x8c,
|
||||
0x07,0x95,0xcf,0x73,0xdc,0x06,0x5b,0x64,0xf8,0x4f,0x93,0x01,0x55,0x94,0xff,0x33,0xa3,0xc7,0xa3,0x40,
|
||||
0x0a,0xf0,0x6a,0xd8,0x42,0xde,0x5c,0xfc,0x3f,0x13,0xa0,0xf2,0x1c,0x90,0x52,0xbe,0x93,0x29,0x31,0xf8,
|
||||
0x43,0x84,0x11,0x86,0x03,0xfd,0x80,0x32,0x08,0x0a,0x37,0x38,0x89,0x74,0x3a,0xf0,0x4b,0xb6,0x8f,0x45,
|
||||
0xfa,0xb2,0xfd,0x08,0xcf,0x01,0xc3,0x71,0xef,0x9f,0xc1,0xe1,0xf7,0xd8,0x57,0x62,0xe0,0xd3,0x8f,0x31,
|
||||
0xf0,0xd6,0x09,0xb6,0xe3,0xc5,0x97,0x18,0x6a,0x31,0x2e,0x2e,0xe2,0xe1,0x8c,0x4e,0x8d,0x8b,0xb7,0x14,
|
||||
0x9d,0xc4,0xa4,0xb8,0x72,0x88,0x25,0xfd,0x49,0x37,0xaa,0x85,0xd8,0xf1,0x64,0xc6,0xcf,0x94,0x78,0x5f,
|
||||
0x89,0x76,0xc1,0xa3,0xd1,0x68,0xfc,0x62,0x86,0x35,0x13,0x6a,0x6b,0x6e,0x54,0xf4,0x35,0x9c,0x56,0x1d,
|
||||
0x07,0x43,0x60,0x47,0xdb,0x69,0xb4,0x09,0xf3,0xd8,0x7a,0xf2,0x25,0x39,0xa6,0x35,0xc2,0x93,0xf2,0x7a,
|
||||
0xa8,0xa7,0xf6,0xf4,0x6c,0x68,0x0a,0x05,0x09,0x88,0x22,0xb2,0x4d,0x4f,0x3e,0x72,0x48,0x7c,0x1b,0x5d,
|
||||
0xd0,0x96,0x5f,0x44,0x16,0x69,0xdb,0x1f,0x46,0x16,0x91,0x8d,0x23,0x93,0x8a,0x18,0xa8,0x10,0x4b,0xfc,
|
||||
0x59,0xf0,0xbe,0x41,0x91,0x9e,0x28,0xcf,0xf7,0x03,0x21,0x8f,0x9b,0x4b,0x21,0xdb,0xdd,0x30,0x89,0xed,
|
||||
0x5d,0xf1,0x1e,0xc3,0x67,0x84,0xec,0x9d,0x7b,0x3d,0x05,0xa1,0x9b,0xa7,0xee,0x79,0x94,0xeb,0x70,0xf8,
|
||||
0xf1,0xf6,0x9e,0xe0,0xfb,0x29,0x1a,0x97,0xfb,0xb4,0x73,0x48,0x22,0xa2,0x90,0xf6,0x0a,0xaa,0x83,0xb8,
|
||||
0x87,0xec,0xb8,0x4a,0x42,0x77,0xa4,0xbe,0x85,0xf0,0x1d,0x4f,0x52,0x37,0x35,0xf7,0x4d,0x45,0x2f,0x3d,
|
||||
0x66,0x21,0xcc,0x9f,0x6f,0xf8,0x44,0x45,0x89,0x9c,0x8b,0xb7,0x09,0xe2,0xe0,0x0f,0x3e,0x74,0xa1,0xe9,
|
||||
0x59,0x1e,0x46,0x9a,0x30,0xaf,0x3b,0xc7,0x15,0x9c,0xa2,0xff,0x46,0xc2,0x8e,0xdc,0x24,0x6d,0xc8,0x05,
|
||||
0xba,0xce,0x07,0x1d,0x7d,0x57,0x1e,0x70,0x1d,0x89,0x71,0x46,0x6f,0x06,0x46,0xfa,0xeb,0x2f,0x29,0x1e,
|
||||
0xcb,0x58,0x92,0x6e,0x54,0x1b,0x32,0x0b,0xd0,0x75,0x62,0xaa,0x1e,0xa8,0xef,0x4b,0xd8,0xa3,0xb2,0xe4,
|
||||
0xb4,0x8d,0xd0,0xff,0x2e,0x70,0x3e,0xf1,0xe0,0x0e,0xec,0x93,0x50,0x07,0xa4,0x77,0x41,0x0e,0xd5,0x24,
|
||||
0xe2,0x3d,0x10,0x7f,0x01,0x6c,0xd0,0xbb,0x23,0x88,0x4d,0x87,0x32,0x71,0xff,0x90,0x04,0x39,0xa4,0xbd,
|
||||
0x89,0xf9,0xda,0xc5,0xfc,0x4c,0x60,0x3a,0x98,0xbf,0xfa,0x3e,0x4b,0xf7,0x50,0xcb,0xe0,0xc8,0x23,0xb2,
|
||||
0x5f,0xf8,0x79,0x30,0x9e,0xe8,0xe8,0x91,0xfa,0x66,0xc5,0x46,0x72,0xde,0x9f,0xac,0x22,0xe1,0xa3,0x38,
|
||||
0xfd,0x09,0x74,0xa2,0x89,0xf2,0x13,0x8a,0x38,0x4d,0x5e,0x6f,0x01,0xde,0xc0,0x70,0x1d,0xbc,0xf4,0x78,
|
||||
0x13,0xaa,0xec,0xa9,0x1a,0x7b,0xfb,0x3c,0x06,0xd7,0x71,0x4a,0xea,0x70,0x04,0x2f,0x66,0x44,0x15,0xbf,
|
||||
0x39,0x63,0xc3,0x51,0xfd,0xc2,0x58,0xdc,0xb8,0xe2,0x9c,0x56,0x47,0xc4,0xe2,0x47,0x5d,0x78,0x3a,0x8a,
|
||||
0xfd,0xa9,0xbe,0x0a,0x68,0x93,0xd8,0x64,0x8c,0xba,0xba,0x42,0x18,0xfa,0xe1,0x0d,0xb9,0xeb,0x62,0xe7,
|
||||
0xff,0x20,0x48,0xc7,0xb8,0xf8,0x9e,0x0e,0x88,0x63,0x88,0x7e,0xe3,0xbd,0x71,0xae,0x00,0xa6,0x7e,0x16,
|
||||
0xc6,0x88,0x4f,0x4f,0xff,0x3e,0x0d,0xf2,0x2b,0x67,0x8a,0xb1,0x17,0xa6,0xa4,0xc8,0xb9,0xc8,0xa6,0xf1,
|
||||
0xa8,0x45,0xaf,0x6d,0x1c,0x82,0x7e,0x88,0x1b,0x1d,0x7c,0xda,0x29,0xb9,0x58,0xb7,0x56,0x55,0x06,0xf1,
|
||||
0xe3,0x55,0xb8,0xff,0x4a,0x69,0xd8,0x1b,0x4b,0x85,0x24,0x18,0x4d,0xa1,0x9a,0xc3,0x54,0xf1,0xa9,0x3c,
|
||||
0xa0,0x47,0x78,0x69,0x1e,0x71,0x61,0xe9,0xf9,0x61,0xfa,0x75,0x74,0xe3,0x89,0x92,0x0c,0x8f,0x13,0x3e,
|
||||
0x84,0x4f,0x28,0x7a,0x0b,0xa0,0x67,0xec,0x43,0x08,0x95,0x81,0x9f,0x08,0x09,0x18,0x43,0xd2,0xfb,0x51,
|
||||
0x1e,0x94,0xbd,0x8a,0x13,0xb8,0xa1,0xc9,0xa4,0xf2,0x7c,0xfe,0x3e,0xce,0xe7,0x3b,0x59,0xa1,0x47,0xf9,
|
||||
0x45,0x8e,0x2d,0x24,0x14,0x45,0x5c,0xd0,0x1c,0x77,0x4f,0x90,0x61,0x06,0x93,0x57,0x25,0x91,0x7b,0x94,
|
||||
0x80,0x37,0x7e,0x8d,0x12,0x16,0xe3,0xcc,0x27,0x7b,0xb7,0x29,0x31,0x89,0xc3,0x2d,0xca,0xb3,0xd7,0xf0,
|
||||
0xe0,0xea,0x16,0x03,0xdb,0xd8,0x00,0x83,0x87,0xe5,0x1e,0x6c,0x81,0xbf,0x29,0xdf,0x70,0x2d,0x84,0x29,
|
||||
0xae,0xfb,0xf9,0x4b,0xe4,0x2a,0xd7,0xbf,0xc4,0xcd,0x78,0x57,0xd8,0xff,0x03,0x5f,0x4f,0x1f,0xc2,0xd8,
|
||||
0x16,0x00,0x00};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,13 @@
|
|||
#ifndef __assets_version
|
||||
#define __assets_version
|
||||
|
||||
const uint8_t VersionMajor = 0;
|
||||
const uint8_t VersionMinor = 1;
|
||||
const uint8_t VersionPatch = 0;
|
||||
const uint8_t VersionMetadata = 0;
|
||||
const char VersionBranch[] = "master";
|
||||
const char VersionSemVer[] = "0.1.0";
|
||||
const char VersionFullSemVer[] = "0.1.0+0";
|
||||
const char VersionCommitDate[] = "2020-09-19";
|
||||
|
||||
#endif
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./charproperties.h"
|
||||
#include <cstddef>
|
||||
#include <string.h>
|
||||
#include "./debug.h"
|
||||
|
||||
void assignChar(char** field, const char* newValue)
|
||||
{
|
||||
if (*field != nullptr)
|
||||
delete *field;
|
||||
|
||||
if (newValue != nullptr)
|
||||
{
|
||||
// Include the terminating null character
|
||||
size_t length = strlen(newValue) + 1;
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
*field = new char[length];
|
||||
strncpy(*field, newValue, length);
|
||||
}
|
||||
else
|
||||
*field = nullptr;
|
||||
}
|
||||
else
|
||||
*field = nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool sameStr(const char* value1, const char* value2)
|
||||
{
|
||||
if ((value1 == nullptr) != (value2 == nullptr))
|
||||
return true;
|
||||
|
||||
if (value1 == nullptr)
|
||||
return false;
|
||||
|
||||
return strcmp(value1, value2) == 0;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __charproperties
|
||||
#define __charproperties
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void assignChar(char** field, const char* newValue);
|
||||
bool sameStr(const char* value1, const char* value2);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./config.h"
|
||||
|
||||
#ifdef SerialDebug
|
||||
const uint32_t SerialDebugBaudrate = 115200;
|
||||
const uint32_t SerialDebugStartupDelay = 2000;
|
||||
#endif
|
||||
|
||||
|
||||
const char* ConnectionSettingsFile = "/connection.json";
|
||||
const char* SystemSettingsFile = "/system.json";
|
||||
|
||||
|
||||
const char* DefaultAPSSIDPrefix = "RGBWifi-";
|
||||
|
||||
// Timeout when in AP + station mode (otherwise trying to connect
|
||||
// to the STA will block the AP)
|
||||
const uint32_t StationModeTimeout = 30000;
|
||||
|
||||
const uint16_t APButtonHoldTime = 2000;
|
||||
|
||||
|
||||
const uint8_t InitialisationBrightness = 128;
|
||||
const uint8_t InitialisationFadeTime = 250;
|
||||
const uint8_t InitialisationBlinkCount = 2;
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __config
|
||||
#define __config
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// Enables debug information to be output through the standard
|
||||
// Serial connection, disable in production units to improve performance
|
||||
#define SerialDebug
|
||||
|
||||
// Enables the crash API methods to cause crashes, you probably never
|
||||
// want to leave this on unless you're debugging the exception handler
|
||||
//#define EnableCrashAPI
|
||||
|
||||
|
||||
#ifdef SerialDebug
|
||||
extern const uint32_t SerialDebugBaudrate;
|
||||
extern const uint32_t SerialDebugStartupDelay;
|
||||
#endif
|
||||
|
||||
|
||||
extern const char* ConnectionSettingsFile;
|
||||
extern const char* SystemSettingsFile;
|
||||
|
||||
|
||||
extern const char* DefaultAPSSIDPrefix;
|
||||
|
||||
extern const uint32_t StationModeTimeout;
|
||||
extern const uint16_t APButtonHoldTime;
|
||||
|
||||
|
||||
extern const uint32_t TimezoneRetryInterval;
|
||||
|
||||
|
||||
extern const uint8_t InitialisationBrightness;
|
||||
extern const uint8_t InitialisationFadeTime;
|
||||
extern const uint8_t InitialisationBlinkCount;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./debug.h"
|
||||
|
||||
|
||||
void _dinit()
|
||||
{
|
||||
#ifdef SerialDebug
|
||||
Serial.begin(SerialDebugBaudrate);
|
||||
// Enable if you want detailed WiFi state logging
|
||||
//Serial.setDebugOutput(true);
|
||||
delay(SerialDebugStartupDelay);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __serialdebug
|
||||
#define __serialdebug
|
||||
|
||||
#include "./config.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
void _dinit();
|
||||
|
||||
#ifdef SerialDebug
|
||||
#define _d(msg) Serial.print(msg)
|
||||
#define _dln(msg) Serial.println(msg)
|
||||
#else
|
||||
#define _d(msg) do { } while (0)
|
||||
#define _dln(msg) do { } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./global.h"
|
||||
|
||||
ConnectionSettings* connectionSettings = new ConnectionSettings();
|
||||
bool connectionSettingsChanged = false;
|
||||
|
||||
SystemSettings* systemSettings = new SystemSettings();
|
||||
bool systemSettingsChanged = false;
|
||||
|
||||
|
||||
bool shouldReboot = false;
|
||||
|
||||
uint32_t currentTime;
|
||||
|
||||
IPAddress emptyIP(0, 0, 0, 0);
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __global
|
||||
#define __global
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <IPAddress.h>
|
||||
#include "./settings/connection.h"
|
||||
#include "./settings/system.h"
|
||||
|
||||
extern ConnectionSettings* connectionSettings;
|
||||
extern bool connectionSettingsChanged;
|
||||
|
||||
extern SystemSettings* systemSettings;
|
||||
extern bool systemSettingsChanged;
|
||||
|
||||
extern bool shouldReboot;
|
||||
|
||||
extern uint32_t currentTime;
|
||||
|
||||
extern IPAddress emptyIP;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <NeoPixelBus.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
#include "./config.h"
|
||||
#include "./debug.h"
|
||||
#include "./global.h"
|
||||
|
||||
#include "./main.wifi.h"
|
||||
#include "./main.led.h"
|
||||
#include "./main.debug.h"
|
||||
|
||||
#include "./server/static.h"
|
||||
#include "./server/settings.h"
|
||||
#include "./server/firmware.h"
|
||||
#include "./server/api.h"
|
||||
|
||||
|
||||
// Forward declarations
|
||||
void handleNotFound(AsyncWebServerRequest* request);
|
||||
|
||||
|
||||
AsyncWebServer server(80);
|
||||
NeoPixelBus<NeoGrbwFeature, Neo800KbpsMethod>* strip = NULL;
|
||||
|
||||
|
||||
#define colorSaturation 128
|
||||
RgbwColor red(colorSaturation, 0, 0, 0);
|
||||
RgbwColor green(0, colorSaturation, 0, 0);
|
||||
RgbwColor blue(0, 0, colorSaturation, 0);
|
||||
RgbwColor white(colorSaturation);
|
||||
RgbwColor black(0);
|
||||
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
_dinit();
|
||||
|
||||
_dln("Initializing LED strip");
|
||||
|
||||
strip = new NeoPixelBus<NeoGrbwFeature, Neo800KbpsMethod>(5);
|
||||
strip->Begin();
|
||||
strip->Show();
|
||||
|
||||
|
||||
|
||||
currentTime = millis();
|
||||
|
||||
if (!LittleFS.begin())
|
||||
_dln("Setup :: failed to mount file system");
|
||||
|
||||
connectionSettings->read();
|
||||
systemSettings->read();
|
||||
/*
|
||||
stepsSettings->read();
|
||||
timeTriggerSettings->read();
|
||||
motionTriggerSettings->read();
|
||||
*/
|
||||
|
||||
pinMode(systemSettings->pinAPButton(), INPUT_PULLUP);
|
||||
pinMode(systemSettings->pinLEDAP(), OUTPUT);
|
||||
pinMode(systemSettings->pinLEDSTA(), OUTPUT);
|
||||
|
||||
|
||||
_dln("Setup :: starting initialization sequence");
|
||||
/*
|
||||
uint8_t bottomStep = stepsSettings->count() - 1;
|
||||
|
||||
for (uint8_t i = 0; i < InitialisationBlinkCount; i++)
|
||||
{
|
||||
stairs->set(bottomStep, InitialisationBrightness, InitialisationFadeTime);
|
||||
waitForTransition();
|
||||
|
||||
stairs->set(bottomStep, 0, InitialisationFadeTime);
|
||||
waitForTransition();
|
||||
}
|
||||
*/
|
||||
|
||||
_dln("Setup :: initializing WiFi");
|
||||
WiFi.persistent(false);
|
||||
WiFi.mode(WIFI_OFF);
|
||||
|
||||
initDebug();
|
||||
initWiFi();
|
||||
|
||||
_dln("Setup :: registering routes");
|
||||
registerStaticRoutes(&server);
|
||||
registerAPIRoutes(&server);
|
||||
registerSettingsRoutes(&server);
|
||||
registerFirmwareRoutes(&server);
|
||||
|
||||
_dln("Setup :: starting HTTP server");
|
||||
server.onNotFound(handleNotFound);
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
/*
|
||||
_dln("Looping");
|
||||
delay(1000);
|
||||
|
||||
// set the colors,
|
||||
// if they don't match in order, you need to use NeoGrbFeature feature
|
||||
strip->SetPixelColor(0, red);
|
||||
strip->SetPixelColor(1, green);
|
||||
strip->SetPixelColor(2, blue);
|
||||
strip->SetPixelColor(3, white);
|
||||
strip->SetPixelColor(4, black);
|
||||
// the following line demonstrates rgbw color support
|
||||
// if the NeoPixels are rgbw types the following line will compile
|
||||
// if the NeoPixels are anything else, the following line will give an error
|
||||
//strip->SetPixelColor(3, RgbwColor(colorSaturation));
|
||||
strip->Show();
|
||||
|
||||
|
||||
delay(1000);
|
||||
|
||||
// turn off the pixels
|
||||
strip->SetPixelColor(0, black);
|
||||
strip->SetPixelColor(1, black);
|
||||
strip->SetPixelColor(2, black);
|
||||
strip->SetPixelColor(3, black);
|
||||
strip->SetPixelColor(4, black);
|
||||
strip->Show();
|
||||
|
||||
delay(1000);
|
||||
|
||||
|
||||
return;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
if (shouldReboot || systemSettingsChanged)
|
||||
{
|
||||
_dln("Loop :: reboot requested, so long and thanks for all the fish!");
|
||||
delay(100);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
currentTime = millis();
|
||||
updateDebugStatus();
|
||||
|
||||
|
||||
if (connectionSettingsChanged)
|
||||
{
|
||||
_dln("Loop :: connection settings changed");
|
||||
initWiFi();
|
||||
connectionSettingsChanged = false;
|
||||
}
|
||||
|
||||
|
||||
updateWiFi();
|
||||
updateLED();
|
||||
//updateNTPClient();
|
||||
//checkTriggers();
|
||||
|
||||
//stairs->tick();
|
||||
}
|
||||
|
||||
|
||||
void handleNotFound(AsyncWebServerRequest *request)
|
||||
{
|
||||
_d("HTTP :: not found: "); _dln(request->url());
|
||||
request->send(404);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiUDP.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <TimeLib.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <EspSaveCrash.h>
|
||||
|
||||
extern "C" {
|
||||
#include <user_interface.h>
|
||||
}
|
||||
|
||||
#include "./components/PCA9685.h"
|
||||
#include "./settings/connection.h"
|
||||
|
||||
#include "./main.triggers.h"
|
||||
|
||||
|
||||
ADC_MODE(ADC_VCC);
|
||||
|
||||
|
||||
|
||||
|
||||
inline void waitForTransition()
|
||||
{
|
||||
while (stairs->inTransition())
|
||||
{
|
||||
currentTime = millis();
|
||||
stairs->tick();
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
*/
|
|
@ -0,0 +1,68 @@
|
|||
#ifdef SerialDebug
|
||||
void wifiEvent(WiFiEvent_t event);
|
||||
|
||||
|
||||
void initDebug()
|
||||
{
|
||||
// onEvent is already deprecated, but since I'm only using it
|
||||
// for debug purposes we'll see how long it lasts...
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
WiFi.onEvent(wifiEvent);
|
||||
_d("WiFi :: MAC address: ");
|
||||
_dln(WiFi.macAddress());
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
|
||||
void wifiEvent(WiFiEvent_t event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case WIFI_EVENT_STAMODE_CONNECTED:
|
||||
_dln("WiFi:: station mode: connected"); break;
|
||||
|
||||
case WIFI_EVENT_STAMODE_DISCONNECTED:
|
||||
_dln("WiFi:: station mode: disconnected"); break;
|
||||
|
||||
case WIFI_EVENT_STAMODE_AUTHMODE_CHANGE:
|
||||
_dln("WiFi:: station mode: authmode change"); break;
|
||||
|
||||
case WIFI_EVENT_STAMODE_GOT_IP:
|
||||
_dln("WiFi:: station mode: got IP");
|
||||
_dln(WiFi.localIP());
|
||||
break;
|
||||
|
||||
case WIFI_EVENT_STAMODE_DHCP_TIMEOUT:
|
||||
_dln("WiFi:: station mode: DHCP timeout"); break;
|
||||
|
||||
case WIFI_EVENT_SOFTAPMODE_STACONNECTED:
|
||||
_dln("WiFi:: soft AP mode: station connected"); break;
|
||||
|
||||
case WIFI_EVENT_SOFTAPMODE_STADISCONNECTED:
|
||||
_dln("WiFi:: soft AP mode: station disconnected"); break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t debugStatusTime = 0;
|
||||
|
||||
void updateDebugStatus()
|
||||
{
|
||||
if (currentTime - debugStatusTime < 5000) return;
|
||||
debugStatusTime = currentTime;
|
||||
|
||||
|
||||
_d("Status :: available heap: ");
|
||||
_dln(ESP.getFreeHeap());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define initDebug() do { } while (0)
|
||||
#define updateDebugStatus() do { } while (0)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,70 @@
|
|||
enum LEDState
|
||||
{
|
||||
Off,
|
||||
BlinkLow,
|
||||
BlinkHigh,
|
||||
On
|
||||
};
|
||||
|
||||
bool ledAP = false;
|
||||
LEDState ledWiFi = Off;
|
||||
uint32_t blinkOnTime = 0;
|
||||
|
||||
|
||||
void updateLED()
|
||||
{
|
||||
uint8_t value = (currentTime - blinkOnTime >= 1000) ? LOW : HIGH;
|
||||
|
||||
WiFiMode_t mode = WiFi.getMode();
|
||||
if (mode == WIFI_AP_STA || mode == WIFI_AP)
|
||||
{
|
||||
if (!ledAP)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDAP(), HIGH);
|
||||
ledAP = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ledAP)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDAP(), LOW);
|
||||
ledAP = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == WIFI_AP_STA || mode == WIFI_STA)
|
||||
{
|
||||
wl_status_t status = WiFi.status();
|
||||
|
||||
if (status == WL_CONNECTED)
|
||||
{
|
||||
if (ledWiFi != On)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDSTA(), HIGH);
|
||||
ledWiFi = On;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LEDState expectedState = value == HIGH ? BlinkHigh : BlinkLow;
|
||||
if (ledWiFi != expectedState)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDSTA(), value);
|
||||
ledWiFi = expectedState;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ledWiFi != Off)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDSTA(), LOW);
|
||||
ledWiFi = Off;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (currentTime - blinkOnTime >= 2000)
|
||||
blinkOnTime = currentTime;
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
bool accessPoint = false;
|
||||
bool stationMode = false;
|
||||
|
||||
uint32_t stationModeStart = 0;
|
||||
uint32_t apButtonStart = 0;
|
||||
|
||||
|
||||
void startAccessPoint();
|
||||
void startStationMode();
|
||||
|
||||
|
||||
void initWiFi()
|
||||
{
|
||||
WiFi.disconnect();
|
||||
WiFi.softAPdisconnect();
|
||||
|
||||
accessPoint = connectionSettings->flag(AccessPoint);
|
||||
stationMode = connectionSettings->flag(StationMode) && connectionSettings->ssid() != nullptr;
|
||||
|
||||
WiFi.mode(accessPoint && stationMode ? WIFI_AP_STA :
|
||||
accessPoint ? WIFI_AP :
|
||||
stationMode ? WIFI_STA :
|
||||
WIFI_OFF);
|
||||
|
||||
if (accessPoint)
|
||||
startAccessPoint();
|
||||
|
||||
if (stationMode)
|
||||
startStationMode();
|
||||
}
|
||||
|
||||
|
||||
void updateWiFi()
|
||||
{
|
||||
if (stationModeStart > 0)
|
||||
{
|
||||
bool isConnected = WiFi.status() == WL_CONNECTED;
|
||||
|
||||
if (isConnected)
|
||||
{
|
||||
_d("WiFi :: connected, IP address: ");
|
||||
_dln(WiFi.localIP());
|
||||
|
||||
stationModeStart = 0;
|
||||
}
|
||||
else if (stationMode && accessPoint &&
|
||||
currentTime - stationModeStart >= StationModeTimeout)
|
||||
{
|
||||
_dln("WiFi :: unable to connect, switching off station mode, status:");
|
||||
_dln(WiFi.status());
|
||||
|
||||
#ifdef SerialDebug
|
||||
WiFi.printDiag(Serial);
|
||||
#endif
|
||||
|
||||
// Connecting to access point is taking too long and is blocking
|
||||
// the access point mode, stop trying
|
||||
stationMode = false;
|
||||
WiFi.disconnect();
|
||||
WiFi.mode(WIFI_AP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!accessPoint)
|
||||
{
|
||||
if (digitalRead(systemSettings->pinAPButton()) == LOW)
|
||||
{
|
||||
if (apButtonStart == 0)
|
||||
apButtonStart = currentTime;
|
||||
else if (currentTime - apButtonStart >= APButtonHoldTime)
|
||||
{
|
||||
connectionSettings->flag(AccessPoint, true);
|
||||
connectionSettings->write();
|
||||
|
||||
startAccessPoint();
|
||||
apButtonStart = 0;
|
||||
}
|
||||
}
|
||||
else if (apButtonStart > 0)
|
||||
apButtonStart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void startAccessPoint()
|
||||
{
|
||||
_dln("WiFi :: starting access point");
|
||||
String ssidString = DefaultAPSSIDPrefix + String(ESP.getChipId(), HEX);
|
||||
if (WiFi.softAP((const char *)ssidString.c_str()))
|
||||
{
|
||||
_d("WiFi :: IP address: ");
|
||||
_dln(WiFi.softAPIP());
|
||||
}
|
||||
else
|
||||
_d("WiFi :: failed to start soft access point");
|
||||
}
|
||||
|
||||
|
||||
void startStationMode()
|
||||
{
|
||||
_d("WiFi :: starting station mode to: ");
|
||||
_dln(connectionSettings->ssid());
|
||||
|
||||
stationModeStart = currentTime;
|
||||
|
||||
if (connectionSettings->hostname() != nullptr)
|
||||
WiFi.hostname(connectionSettings->hostname());
|
||||
|
||||
if (WiFi.begin(connectionSettings->ssid(), connectionSettings->password()))
|
||||
{
|
||||
if (connectionSettings->flag(DHCP))
|
||||
// I've had the same issue as described here with config(0, 0, 0):
|
||||
// https://stackoverflow.com/questions/40069654/how-to-clear-static-ip-configuration-and-start-dhcp
|
||||
wifi_station_dhcpc_start();
|
||||
else
|
||||
WiFi.config(connectionSettings->ip(), connectionSettings->gateway(), connectionSettings->subnetMask());
|
||||
}
|
||||
else
|
||||
_d("WiFi :: failed to start station mode");
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./api.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <IPAddress.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include "./shared.h"
|
||||
#include "../assets/version.h"
|
||||
#include "../debug.h"
|
||||
#include "../global.h"
|
||||
#include "../settings/connection.h"
|
||||
|
||||
|
||||
/*
|
||||
void handleSet(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: set");
|
||||
|
||||
AsyncWebParameter* param;
|
||||
uint8_t value = 0;
|
||||
|
||||
param = request->getParam("value");
|
||||
if (param != nullptr)
|
||||
{
|
||||
value = param->value().toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
param = request->getParam("percent");
|
||||
if (param != nullptr)
|
||||
{
|
||||
value = map(param->value().toInt(), 0, 100, 0, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t time = 0;
|
||||
uint8_t from = 0;
|
||||
|
||||
param = request->getParam("time");
|
||||
if (param != nullptr)
|
||||
time = param->value().toInt();
|
||||
|
||||
param = request->getParam("from");
|
||||
if (param != nullptr)
|
||||
{
|
||||
if (param->value() == "top")
|
||||
from = 1;
|
||||
else if (param->value() == "bottom")
|
||||
from = 2;
|
||||
}
|
||||
|
||||
|
||||
if (from == 0 || time == 0)
|
||||
stairs->setAll(value, time, 0);
|
||||
else
|
||||
stairs->sweep(value, time, from == 1);
|
||||
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
|
||||
void handleGetStepValues(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: get steps");
|
||||
|
||||
DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(17));
|
||||
|
||||
bool target = !request->hasParam("current");
|
||||
|
||||
JsonArray& root = jsonBuffer.createArray();
|
||||
for (uint8_t step = 0; step < stepsSettings->count(); step++)
|
||||
root.add(stairs->get(step, target));
|
||||
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||
root.printTo(*response);
|
||||
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
void handlePostStepValues(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
|
||||
{
|
||||
_dln("API :: post steps");
|
||||
|
||||
DynamicJsonBuffer jsonBuffer(2*JSON_ARRAY_SIZE(17) + JSON_OBJECT_SIZE(3) + 130);
|
||||
JsonObject& root = jsonBuffer.parseObject((char*)data);
|
||||
if (!root.success())
|
||||
{
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t transitionTime = root["transitionTime"];
|
||||
JsonArray& values = root["values"];
|
||||
|
||||
JsonArray& startTime = root["startTime"];
|
||||
size_t startTimeCount = startTime.size();
|
||||
|
||||
size_t valueCount = values.size();
|
||||
if (valueCount > stepsSettings->count())
|
||||
valueCount = stepsSettings->count();
|
||||
|
||||
|
||||
for (uint8_t step = 0; step < valueCount; step++)
|
||||
stairs->set(step, values[step], transitionTime, step < startTimeCount ? startTime[step] : 0);
|
||||
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
|
||||
void handleGetTimeTriggers(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: get time triggers");
|
||||
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||
timeTriggerSettings->toJson(*response);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
void handlePostTimeTriggers(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
|
||||
{
|
||||
_dln("API :: post time triggers");
|
||||
|
||||
bool changed;
|
||||
if (timeTriggerSettings->fromJson((char*)data, &changed))
|
||||
{
|
||||
timeTriggerSettings->write();
|
||||
|
||||
if (changed)
|
||||
timeTriggerSettingsChanged = true;
|
||||
|
||||
request->send(200);
|
||||
}
|
||||
else
|
||||
request->send(400);
|
||||
}
|
||||
|
||||
|
||||
void handleGetMotionTriggers(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: get motion triggers");
|
||||
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||
motionTriggerSettings->toJson(*response);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
void handlePostMotionTriggers(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
|
||||
{
|
||||
_dln("API :: post motion triggers");
|
||||
|
||||
bool changed;
|
||||
if (motionTriggerSettings->fromJson((char*)data, &changed))
|
||||
{
|
||||
motionTriggerSettings->write();
|
||||
|
||||
if (changed)
|
||||
motionTriggerSettingsChanged = true;
|
||||
|
||||
request->send(200);
|
||||
}
|
||||
else
|
||||
request->send(400);
|
||||
}
|
||||
*/
|
||||
|
||||
void registerAPIRoutes(AsyncWebServer* server)
|
||||
{
|
||||
/*
|
||||
server->on("/api/set", HTTP_GET, handleSet);
|
||||
|
||||
server->on("/api/steps/values", HTTP_GET, handleGetStepValues);
|
||||
server->on("/api/steps/values", HTTP_POST, devNullRequest, devNullFileUpload, handlePostStepValues);
|
||||
|
||||
server->on("/api/triggers/time", HTTP_GET, handleGetTimeTriggers);
|
||||
server->on("/api/triggers/time", HTTP_POST, devNullRequest, devNullFileUpload, handlePostTimeTriggers);
|
||||
|
||||
server->on("/api/triggers/motion", HTTP_GET, handleGetMotionTriggers);
|
||||
server->on("/api/triggers/motion", HTTP_POST, devNullRequest, devNullFileUpload, handlePostMotionTriggers);
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __server_api
|
||||
#define __server_api
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
void registerAPIRoutes(AsyncWebServer* server);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./firmware.h"
|
||||
#include "../config.h"
|
||||
#include "../debug.h"
|
||||
#include "../global.h"
|
||||
|
||||
|
||||
|
||||
void handleFirmware(AsyncWebServerRequest *request)
|
||||
{
|
||||
shouldReboot = !Update.hasError();
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", shouldReboot ? "OK" : "FAIL");
|
||||
response->addHeader("Connection", "close");
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
void handleFirmwareFile(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
|
||||
{
|
||||
_d("Firmware :: file upload: index = "); _d(index);
|
||||
_d(", len = "); _d(len);
|
||||
_d(", final = "); _dln(final);
|
||||
|
||||
if (!index)
|
||||
{
|
||||
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||
_d("Firmware :: update start, max sketch space: ");
|
||||
_dln(maxSketchSpace);
|
||||
|
||||
Update.runAsync(true);
|
||||
if (!Update.begin(maxSketchSpace))
|
||||
{
|
||||
#ifdef SerialDebug
|
||||
Update.printError(Serial);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (!Update.hasError())
|
||||
{
|
||||
if (Update.write(data, len) != len)
|
||||
{
|
||||
#ifdef SerialDebug
|
||||
Update.printError(Serial);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (final)
|
||||
{
|
||||
if (Update.end(true))
|
||||
{
|
||||
_dln("Firmware :: success");
|
||||
}
|
||||
else
|
||||
{
|
||||
_dln("Firmware :: failed");
|
||||
#ifdef SerialDebug
|
||||
Update.printError(Serial);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void registerFirmwareRoutes(AsyncWebServer* server)
|
||||
{
|
||||
server->on("/api/firmware", HTTP_POST, handleFirmware, handleFirmwareFile);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __server_firmware
|
||||
#define __server_firmware
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
void registerFirmwareRoutes(AsyncWebServer* server);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./settings.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <IPAddress.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <EspSaveCrash.h>
|
||||
#include "./shared.h"
|
||||
#include "../assets/version.h"
|
||||
#include "../config.h"
|
||||
#include "../debug.h"
|
||||
#include "../global.h"
|
||||
#include "../settings/connection.h"
|
||||
|
||||
extern "C" {
|
||||
#include <user_interface.h>
|
||||
}
|
||||
|
||||
|
||||
bool clearedResetReason = false;
|
||||
|
||||
|
||||
/*
|
||||
void handleStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: status");
|
||||
|
||||
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(6));
|
||||
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
root["systemID"] = String(ESP.getChipId(), HEX);
|
||||
root["version"] = String(VersionFullSemVer);
|
||||
|
||||
if (ntpClient != nullptr && hasTimezone)
|
||||
{
|
||||
root["time"] = ntpClient->getEpochTime();
|
||||
root["timeOffset"] = timezoneOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
root["time"] = false;
|
||||
root["timeOffset"] = 0;
|
||||
}
|
||||
|
||||
root["resetReason"] = clearedResetReason ? 0 : ESP.getResetInfoPtr()->reason;
|
||||
root["stackTrace"] = SaveCrash.count() > 0;
|
||||
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||
root.printTo(*response);
|
||||
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
void handleConnectionStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: connection status");
|
||||
|
||||
WiFiMode_t mode = WiFi.getMode();
|
||||
|
||||
|
||||
DynamicJsonBuffer jsonBuffer((2 * JSON_OBJECT_SIZE(2)) + JSON_OBJECT_SIZE(3));
|
||||
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
JsonObject& ap = root.createNestedObject("ap");
|
||||
ap["enabled"] = (mode == WIFI_AP || mode == WIFI_AP_STA);
|
||||
ap["ip"] = WiFi.softAPIP().toString();
|
||||
|
||||
JsonObject& station = root.createNestedObject("station");
|
||||
station["enabled"] = (mode == WIFI_STA || mode == WIFI_AP_STA);
|
||||
station["status"] = (uint8_t)WiFi.status();
|
||||
station["ip"] = WiFi.localIP().toString();
|
||||
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||
root.printTo(*response);
|
||||
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
void handleGetConnection(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: get connection");
|
||||
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||
connectionSettings->toJson(*response);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
void handlePostConnection(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
|
||||
{
|
||||
_dln("API :: post connection");
|
||||
|
||||
bool changed;
|
||||
if (connectionSettings->fromJson((char*)data, &changed))
|
||||
{
|
||||
connectionSettings->write();
|
||||
|
||||
if (changed)
|
||||
connectionSettingsChanged = true;
|
||||
|
||||
request->send(200);
|
||||
}
|
||||
else
|
||||
request->send(400);
|
||||
}
|
||||
|
||||
|
||||
void handleGetSystem(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: get system");
|
||||
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||
systemSettings->toJson(*response);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
void handlePostSystem(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
|
||||
{
|
||||
_dln("API :: post system");
|
||||
|
||||
bool changed;
|
||||
if (systemSettings->fromJson((char*)data, &changed))
|
||||
{
|
||||
systemSettings->write();
|
||||
|
||||
if (changed)
|
||||
systemSettingsChanged = true;
|
||||
|
||||
request->send(200);
|
||||
}
|
||||
else
|
||||
request->send(400);
|
||||
}
|
||||
|
||||
|
||||
void handleGetSteps(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: get steps");
|
||||
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||
stepsSettings->toJson(*response);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
void handlePostSteps(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
|
||||
{
|
||||
_dln("API :: post steps");
|
||||
|
||||
bool changed;
|
||||
if (stepsSettings->fromJson((char*)data, &changed))
|
||||
{
|
||||
stepsSettings->write();
|
||||
|
||||
if (changed)
|
||||
stepsSettingsChanged = true;
|
||||
|
||||
request->send(200);
|
||||
}
|
||||
else
|
||||
request->send(400);
|
||||
}
|
||||
|
||||
|
||||
void handleGetStackTrace(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: get stack trace");
|
||||
|
||||
if (SaveCrash.count() == 0)
|
||||
{
|
||||
request->send(404);
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/octet-stream");
|
||||
response->addHeader("Content-Disposition", "attachment; filename=\"stacktrace.txt\"");
|
||||
SaveCrash.print(*response);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
void handleDeleteStackTrace(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: delete stack trace");
|
||||
|
||||
clearedResetReason = true;
|
||||
SaveCrash.clear();
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
|
||||
#ifdef EnableCrashAPI
|
||||
#pragma "!!! Crash API is enabled on this build !!!"
|
||||
|
||||
void handleCrashException(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: crash exception");
|
||||
|
||||
int* i = nullptr;
|
||||
*i = 42;
|
||||
}
|
||||
|
||||
void handleCrashSoftWDT(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: crash soft WDT");
|
||||
|
||||
while (true);
|
||||
}
|
||||
|
||||
void handleCrashWDT(AsyncWebServerRequest *request)
|
||||
{
|
||||
_dln("API :: crash WDT");
|
||||
|
||||
ESP.wdtDisable();
|
||||
while (true);
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
void registerSettingsRoutes(AsyncWebServer* server)
|
||||
{
|
||||
/*
|
||||
server->on("/api/status", HTTP_GET, handleStatus);
|
||||
|
||||
server->on("/api/connection/status", HTTP_GET, handleConnectionStatus);
|
||||
|
||||
server->on("/api/connection", HTTP_GET, handleGetConnection);
|
||||
server->on("/api/connection", HTTP_POST, devNullRequest, devNullFileUpload, handlePostConnection);
|
||||
|
||||
server->on("/api/system", HTTP_GET, handleGetSystem);
|
||||
server->on("/api/system", HTTP_POST, devNullRequest, devNullFileUpload, handlePostSystem);
|
||||
|
||||
server->on("/api/steps", HTTP_GET, handleGetSteps);
|
||||
server->on("/api/steps", HTTP_POST, devNullRequest, devNullFileUpload, handlePostSteps);
|
||||
|
||||
server->on("/api/stacktrace/get", HTTP_GET, handleGetStackTrace);
|
||||
server->on("/api/stacktrace/delete", HTTP_GET, handleDeleteStackTrace);
|
||||
|
||||
#ifdef EnableCrashAPI
|
||||
server->on("/api/crash/exception", HTTP_GET, handleCrashException);
|
||||
server->on("/api/crash/softwdt", HTTP_GET, handleCrashSoftWDT);
|
||||
server->on("/api/crash/wdt", HTTP_GET, handleCrashWDT);
|
||||
#endif
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __server_settings
|
||||
#define __server_settings
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
void registerSettingsRoutes(AsyncWebServer* server);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./shared.h"
|
||||
|
||||
void devNullRequest(AsyncWebServerRequest *request) {}
|
||||
void devNullFileUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {}
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __server_shared
|
||||
#define __server_shared
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
void devNullRequest(AsyncWebServerRequest *request);
|
||||
void devNullFileUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./static.h"
|
||||
#include "../debug.h"
|
||||
#include "../assets/html.h"
|
||||
#include "../assets/js.h"
|
||||
#include "../assets/css.h"
|
||||
|
||||
|
||||
void handleGzipped(AsyncWebServerRequest *request, const String& contentType, const uint8_t * content, size_t len)
|
||||
{
|
||||
_d("HTTP :: static: "); _dln(request->url());
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, contentType, content, len);
|
||||
response->addHeader("Content-Encoding", "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
void registerStaticRoutes(AsyncWebServer* server)
|
||||
{
|
||||
server->on("/", HTTP_GET, [](AsyncWebServerRequest *request) { handleGzipped(request, "text/html", EmbeddedIndex, sizeof(EmbeddedIndex)); });
|
||||
|
||||
server->on("/bundle.js", HTTP_GET, [](AsyncWebServerRequest *request) { handleGzipped(request, "text/javascript", EmbeddedBundleJS, sizeof(EmbeddedBundleJS)); });
|
||||
server->on("/bundle.css", HTTP_GET, [](AsyncWebServerRequest *request) { handleGzipped(request, "text/css", EmbeddedBundleCSS, sizeof(EmbeddedBundleCSS)); });
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __server_static
|
||||
#define __server_static
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
void registerStaticRoutes(AsyncWebServer* server);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./abstractjson.h"
|
||||
#include <LittleFS.h>
|
||||
|
||||
|
||||
void AbstractJsonSettings::read()
|
||||
{
|
||||
_d(getDebugPrefix()); _dln(" :: opening file");
|
||||
File settingsFile = LittleFS.open(getFilename(), "r");
|
||||
if (!settingsFile)
|
||||
{
|
||||
_d(getDebugPrefix()); _dln(" :: failed to open file");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size = settingsFile.size();
|
||||
if (size > 1024)
|
||||
{
|
||||
_d(getDebugPrefix()); _dln(" :: file size is too large");
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
_d(getDebugPrefix()); _dln(" :: zero size file");
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
settingsFile.readBytes(buf.get(), size);
|
||||
settingsFile.close();
|
||||
|
||||
_dln(buf.get());
|
||||
|
||||
if (fromJson(buf.get()))
|
||||
{
|
||||
_d(getDebugPrefix());
|
||||
_dln(" :: read from file");
|
||||
}
|
||||
else
|
||||
{
|
||||
_d(getDebugPrefix());
|
||||
_dln(" :: failed to parse file");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AbstractJsonSettings::write()
|
||||
{
|
||||
_d(getDebugPrefix()); _dln(" :: opening file for writing");
|
||||
File settingsFile = LittleFS.open(getFilename(), "w");
|
||||
if (!settingsFile)
|
||||
{
|
||||
_d(getDebugPrefix()); _dln(" :: failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
toJson(settingsFile);
|
||||
settingsFile.close();
|
||||
_d(getDebugPrefix()); _dln(" :: written to file");
|
||||
}
|
||||
|
||||
|
||||
bool AbstractJsonSettings::fromJson(char* data)
|
||||
{
|
||||
return fromJson(data, nullptr);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __settingsjson
|
||||
#define __settingsjson
|
||||
|
||||
#include "../debug.h"
|
||||
|
||||
class AbstractJsonSettings
|
||||
{
|
||||
protected:
|
||||
virtual const char* getFilename() = 0;
|
||||
virtual const char* getDebugPrefix() = 0;
|
||||
|
||||
public:
|
||||
void read();
|
||||
void write();
|
||||
|
||||
virtual void toJson(Print &print) = 0;
|
||||
virtual bool fromJson(char* data, bool* changed) = 0;
|
||||
|
||||
bool fromJson(char* data);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./connection.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include "./abstractjson.h"
|
||||
#include "../debug.h"
|
||||
#include "../config.h"
|
||||
#include "../global.h"
|
||||
|
||||
|
||||
|
||||
void ConnectionSettings::toJson(Print &print)
|
||||
{
|
||||
/*
|
||||
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(9));
|
||||
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
root["hostname"] = hostname();
|
||||
root["accesspoint"] = flag(AccessPoint);
|
||||
root["station"] = flag(StationMode);
|
||||
root["ssid"] = ssid();
|
||||
root["password"] = password();
|
||||
root["dhcp"] = flag(DHCP);
|
||||
root["ip"] = ip() != 0 ? ip().toString() : "";
|
||||
root["subnetmask"] = subnetMask() != 0 ? subnetMask().toString() : "";
|
||||
root["gateway"] = gateway() != 0 ? gateway().toString() : "";
|
||||
|
||||
root.printTo(print);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
bool ConnectionSettings::fromJson(char* data, bool* changed)
|
||||
{
|
||||
/*
|
||||
if (changed != nullptr)
|
||||
*changed = false;
|
||||
|
||||
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(9) + 250);
|
||||
JsonObject& root = jsonBuffer.parseObject(data);
|
||||
|
||||
if (!root.success())
|
||||
return false;
|
||||
|
||||
IPAddress jsonIP;
|
||||
IPAddress jsonSubnetMask;
|
||||
IPAddress jsonGateway;
|
||||
|
||||
const char* jsonHostname = root["hostname"];
|
||||
bool jsonAccessPoint = root["accesspoint"];
|
||||
bool jsonStation = root["station"];
|
||||
const char* jsonSSID = root["ssid"];
|
||||
const char* jsonPassword = root["password"];
|
||||
bool jsonDHCP = root["dhcp"];
|
||||
const char* jsonIPText = root["ip"];
|
||||
const char* jsonSubnetMaskText = root["subnetmask"];
|
||||
const char* jsonGatewayText = root["gateway"];
|
||||
|
||||
if (jsonIPText == nullptr || !jsonIP.fromString(jsonIPText)) jsonIP = emptyIP;
|
||||
if (jsonSubnetMaskText == nullptr || !jsonSubnetMask.fromString(jsonSubnetMaskText)) jsonSubnetMask = emptyIP;
|
||||
if (jsonGatewayText == nullptr || !jsonGateway.fromString(jsonGatewayText)) jsonGateway = emptyIP;
|
||||
|
||||
|
||||
if (!(jsonAccessPoint || jsonStation))
|
||||
jsonAccessPoint = true;
|
||||
|
||||
if ((!sameStr(jsonHostname, hostname())) ||
|
||||
(jsonAccessPoint != flag(AccessPoint)) ||
|
||||
(jsonStation != flag(StationMode)) ||
|
||||
(!sameStr(jsonSSID, ssid())) ||
|
||||
(!sameStr(jsonPassword, password())) ||
|
||||
(jsonDHCP != flag(DHCP)) ||
|
||||
(jsonIP != ip()) ||
|
||||
(jsonSubnetMask != subnetMask()) ||
|
||||
(jsonGateway != gateway()))
|
||||
{
|
||||
hostname(jsonHostname);
|
||||
flag(AccessPoint, jsonAccessPoint);
|
||||
flag(StationMode, jsonStation);
|
||||
ssid(jsonSSID);
|
||||
password(jsonPassword);
|
||||
flag(DHCP, jsonDHCP);
|
||||
ip(jsonIP);
|
||||
subnetMask(jsonSubnetMask);
|
||||
gateway(jsonGateway);
|
||||
|
||||
if (changed != nullptr)
|
||||
*changed = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __settingsconnection
|
||||
#define __settingsconnection
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <IPAddress.h>
|
||||
#include "./abstractjson.h"
|
||||
#include "../charproperties.h"
|
||||
#include "../config.h"
|
||||
|
||||
|
||||
enum ConnectionSettingsFlags
|
||||
{
|
||||
AccessPoint = 1,
|
||||
StationMode = 2,
|
||||
DHCP = 4
|
||||
};
|
||||
|
||||
|
||||
class ConnectionSettings : public AbstractJsonSettings
|
||||
{
|
||||
private:
|
||||
char* mHostname = nullptr;
|
||||
uint8_t mFlags = AccessPoint | DHCP;
|
||||
char* mSSID = nullptr;
|
||||
char* mPassword = nullptr;
|
||||
IPAddress mIP = (uint32_t)0;
|
||||
IPAddress mSubnetMask = (uint32_t)0;
|
||||
IPAddress mGateway = (uint32_t)0;
|
||||
|
||||
protected:
|
||||
virtual const char* getFilename() { return ConnectionSettingsFile; };
|
||||
virtual const char* getDebugPrefix() { return "ConnectionSettings"; };
|
||||
|
||||
public:
|
||||
void toJson(Print &print);
|
||||
bool fromJson(char* data, bool* changed);
|
||||
|
||||
|
||||
char* hostname() { return mHostname; }
|
||||
void hostname(const char* value) { assignChar(&mHostname, value); }
|
||||
|
||||
bool flag(ConnectionSettingsFlags flag) { return (mFlags & flag) != 0; }
|
||||
void flag(ConnectionSettingsFlags flag, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
mFlags |= flag;
|
||||
else
|
||||
mFlags &= ~flag;
|
||||
}
|
||||
|
||||
char* ssid() { return mSSID; }
|
||||
void ssid(const char* value) { assignChar(&mSSID, value); }
|
||||
|
||||
char* password() { return mPassword; }
|
||||
void password(const char* value) { assignChar(&mPassword, value); }
|
||||
|
||||
IPAddress ip() { return mIP; }
|
||||
void ip(IPAddress value) { mIP = value; }
|
||||
|
||||
IPAddress subnetMask() { return mSubnetMask; }
|
||||
void subnetMask(IPAddress value) { mSubnetMask = value; }
|
||||
|
||||
IPAddress gateway() { return mGateway; }
|
||||
void gateway(IPAddress value) { mGateway = value; }
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#include "./system.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <FS.h>
|
||||
#include "../debug.h"
|
||||
#include "../global.h"
|
||||
#include "../config.h"
|
||||
|
||||
|
||||
|
||||
void SystemSettings::toJson(Print &print)
|
||||
{
|
||||
/*
|
||||
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(8) + JSON_OBJECT_SIZE(5));
|
||||
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
|
||||
JsonObject& pins = root.createNestedObject("pins");
|
||||
pins["ledAP"] = pinLEDAP();
|
||||
pins["ledSTA"] = pinLEDSTA();
|
||||
pins["apButton"] = pinAPButton();
|
||||
pins["pwmSDA"] = pinPWMDriverSDA();
|
||||
pins["pwmSCL"] = pinPWMDriverSCL();
|
||||
|
||||
root["pwmAddress"] = pwmDriverAddress();
|
||||
root["pwmFrequency"] = pwmDriverFrequency();
|
||||
root["mapsAPIKey"] = mapsAPIKey();
|
||||
|
||||
root.printTo(print);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
bool SystemSettings::fromJson(char* data, bool* changed)
|
||||
{
|
||||
/*
|
||||
if (changed != nullptr)
|
||||
*changed = false;
|
||||
|
||||
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(8) + JSON_OBJECT_SIZE(5) + 500);
|
||||
JsonObject& root = jsonBuffer.parseObject(data);
|
||||
|
||||
if (!root.success())
|
||||
return false;
|
||||
|
||||
const char* jsonNTPServer = root["ntpServer"];
|
||||
uint32_t jsonNTPInterval = root["ntpInterval"];
|
||||
|
||||
double jsonLat = root["lat"];
|
||||
double jsonLng = root["lng"];
|
||||
|
||||
JsonObject& pins = root["pins"];
|
||||
uint8_t jsonPinLEDAP = pins["ledAP"];
|
||||
uint8_t jsonPinLEDSTA = pins["ledSTA"];
|
||||
uint8_t jsonPinAPButton = pins["apButton"];
|
||||
uint8_t jsonPinPWMDriverSDA = pins["pwmSDA"];
|
||||
uint8_t jsonPinPWMDriverSCL = pins["pwmSCL"];
|
||||
|
||||
uint8_t jsonPWMDriverAddress = root["pwmAddress"];
|
||||
uint16_t jsonPWMDriverFrequency = root["pwmFrequency"];
|
||||
const char* jsonMapAPIKey = root["mapsAPIKey"];
|
||||
|
||||
if (jsonNTPServer == nullptr) jsonNTPServer = DefaultNTPServer;
|
||||
if (jsonNTPInterval == 0) jsonNTPInterval = 5;
|
||||
|
||||
if (jsonPinLEDAP == 0) jsonPinLEDAP = pinLEDAP();
|
||||
if (jsonPinLEDSTA == 0) jsonPinLEDSTA = pinLEDSTA();
|
||||
if (jsonPinAPButton == 0) jsonPinAPButton = pinAPButton();
|
||||
if (jsonPinPWMDriverSDA == 0) jsonPinPWMDriverSDA = pinPWMDriverSDA();
|
||||
if (jsonPinPWMDriverSCL == 0) jsonPinPWMDriverSCL = pinPWMDriverSCL();
|
||||
|
||||
if (jsonPWMDriverAddress == 0) jsonPWMDriverAddress = pwmDriverAddress();
|
||||
if (jsonPWMDriverFrequency == 0) jsonPWMDriverFrequency = pwmDriverFrequency();
|
||||
|
||||
|
||||
if ((jsonPinLEDAP != pinLEDAP()) ||
|
||||
(jsonPinLEDSTA != pinLEDSTA()) ||
|
||||
(jsonPinAPButton != pinAPButton()) ||
|
||||
(jsonPinPWMDriverSDA != pinPWMDriverSDA()) ||
|
||||
(jsonPinPWMDriverSCL != pinPWMDriverSCL()) ||
|
||||
(jsonPWMDriverAddress != pwmDriverAddress()) ||
|
||||
(jsonPWMDriverFrequency != pwmDriverFrequency()) ||
|
||||
(!sameStr(jsonMapAPIKey, mapsAPIKey())) ||
|
||||
(jsonLat != latitude()) ||
|
||||
(jsonLng != longitude()) ||
|
||||
(!sameStr(jsonNTPServer, ntpServer())) ||
|
||||
(jsonNTPInterval != ntpInterval()))
|
||||
{
|
||||
latitude(jsonLat);
|
||||
longitude(jsonLng);
|
||||
pinLEDAP(jsonPinLEDAP);
|
||||
pinLEDSTA(jsonPinLEDSTA);
|
||||
pinAPButton(jsonPinAPButton);
|
||||
pinPWMDriverSDA(jsonPinPWMDriverSDA);
|
||||
pinPWMDriverSCL(jsonPinPWMDriverSCL);
|
||||
pwmDriverAddress(jsonPWMDriverAddress);
|
||||
pwmDriverFrequency(jsonPWMDriverFrequency);
|
||||
mapsAPIKey(jsonMapAPIKey);
|
||||
ntpServer(jsonNTPServer);
|
||||
ntpInterval(jsonNTPInterval);
|
||||
|
||||
if (changed != nullptr)
|
||||
*changed = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* ESP8266 RGBW controller
|
||||
* Copyright 2020 (c) Mark van Renswoude
|
||||
*
|
||||
* https://git.x2software.net/pub/RGBWifi
|
||||
*/
|
||||
#ifndef __settingssystem
|
||||
#define __settingssystem
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "../charproperties.h"
|
||||
#include "./abstractjson.h"
|
||||
|
||||
|
||||
class SystemSettings : public AbstractJsonSettings
|
||||
{
|
||||
private:
|
||||
uint8_t mPinLEDAP = 4;
|
||||
uint8_t mPinLEDSTA = 5;
|
||||
uint8_t mPinAPButton = 2;
|
||||
|
||||
protected:
|
||||
virtual const char* getFilename() { return SystemSettingsFile; };
|
||||
virtual const char* getDebugPrefix() { return "SystemSettings"; };
|
||||
|
||||
public:
|
||||
SystemSettings()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void toJson(Print &print);
|
||||
bool fromJson(char* data, bool* changed);
|
||||
|
||||
uint8_t pinLEDAP() { return mPinLEDAP; }
|
||||
void pinLEDAP(uint8_t value) { mPinLEDAP = value; }
|
||||
|
||||
uint8_t pinLEDSTA() { return mPinLEDSTA; }
|
||||
void pinLEDSTA(uint8_t value) { mPinLEDSTA = value; }
|
||||
|
||||
uint8_t pinAPButton() { return mPinAPButton; }
|
||||
void pinAPButton(uint8_t value) { mPinAPButton = value; }
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,555 @@
|
|||
function startApp()
|
||||
{
|
||||
// Source: https://github.com/axios/axios/issues/164
|
||||
axios.interceptors.response.use(undefined, function axiosRetryInterceptor(err) {
|
||||
var config = err.config;
|
||||
// If config does not exist or the retry option is not set, reject
|
||||
if(!config || !config.retry) return Promise.reject(err);
|
||||
|
||||
// Set the variable for keeping track of the retry count
|
||||
config.__retryCount = config.__retryCount || 0;
|
||||
|
||||
// Check if we've maxed out the total number of retries
|
||||
if(config.__retryCount >= config.retry) {
|
||||
// Reject with the error
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
// Increase the retry count
|
||||
config.__retryCount += 1;
|
||||
|
||||
// Create new promise to handle exponential backoff
|
||||
var backoff = new Promise(function(resolve) {
|
||||
setTimeout(function() {
|
||||
resolve();
|
||||
}, config.retryDelay || 1);
|
||||
});
|
||||
|
||||
// Return the promise in which recalls axios to retry the request
|
||||
return backoff.then(function() {
|
||||
return axios(config);
|
||||
});
|
||||
});
|
||||
|
||||
Vue.component('check', {
|
||||
template: '<div class="check" :class="{ checked: value, disabled: disabled }" @keydown="handleKeyDown" @click="handleClick" tabindex="0"><div class="control"><div class="inner"></div></div><div class="label">{{ title }}</div></div>',
|
||||
props: {
|
||||
title: String,
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleClick: function()
|
||||
{
|
||||
if (this.disabled) return;
|
||||
this.value = !this.value;
|
||||
this.$emit('input', this.value);
|
||||
},
|
||||
|
||||
handleKeyDown: function(event)
|
||||
{
|
||||
if (event.keyCode == 32)
|
||||
{
|
||||
this.handleClick();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Vue.component('radio', {
|
||||
template: '<div class="radio" :class="{ checked: value == id, disabled: disabled }" @keydown="handleKeyDown" @click="handleClick" tabindex="0"><div class="control"><div class="inner"></div></div><div class="label">{{ title }}</div></div>',
|
||||
props: {
|
||||
title: String,
|
||||
value: null,
|
||||
id: null,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleClick: function()
|
||||
{
|
||||
if (this.disabled) return;
|
||||
this.value = this.id;
|
||||
this.$emit('input', this.value);
|
||||
},
|
||||
|
||||
handleKeyDown: function(event)
|
||||
{
|
||||
if (event.keyCode == 32)
|
||||
{
|
||||
this.handleClick();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Vue.component('range', {
|
||||
template: '<div>' +
|
||||
'<div class="start">' +
|
||||
'<span class="value">{{ value.start }}</span>' +
|
||||
'<div class="slidercontainer">' +
|
||||
'<input type="range" min="0" max="4094" class="slider" v-model.number="value.start">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
|
||||
'<div class="end">' +
|
||||
'<span class="value">{{ value.end }}</span>' +
|
||||
'<div class="slidercontainer">' +
|
||||
'<input type="range" min="1" max="4095" class="slider" v-model.number="value.end">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
props: ['value'],
|
||||
|
||||
mounted: function()
|
||||
{
|
||||
this.oldValue = { start: this.value.start, end: this.value.end };
|
||||
},
|
||||
|
||||
watch: {
|
||||
value: {
|
||||
handler: function(newValue)
|
||||
{
|
||||
if (newValue.start != this.oldValue.start)
|
||||
{
|
||||
if (newValue.start > newValue.end)
|
||||
{
|
||||
newValue.end = newValue.start + 1;
|
||||
this.$emit('input', newValue);
|
||||
}
|
||||
}
|
||||
else if (newValue.end != this.oldValue.end)
|
||||
{
|
||||
if (newValue.end < newValue.start)
|
||||
{
|
||||
newValue.start = newValue.end - 1;
|
||||
this.$emit('input', newValue);
|
||||
}
|
||||
}
|
||||
|
||||
this.oldValue.start = newValue.start;
|
||||
this.oldValue.end = newValue.end;
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var i18n = new VueI18n({
|
||||
locale: navigator.language.split('-')[0],
|
||||
fallbackLocale: 'en',
|
||||
messages: messages
|
||||
});
|
||||
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
|
||||
i18n: i18n,
|
||||
|
||||
data: {
|
||||
notification: null,
|
||||
|
||||
loading: true,
|
||||
saving: false,
|
||||
loadingIndicator: '|',
|
||||
uploadProgress: false,
|
||||
|
||||
activeTab: 'status',
|
||||
|
||||
status: {
|
||||
systemID: 'loading...',
|
||||
version: 'loading...',
|
||||
resetReason: null,
|
||||
stackTrace: false
|
||||
},
|
||||
|
||||
wifiStatus: {
|
||||
ap: {
|
||||
enabled: false,
|
||||
ip: '0.0.0.0'
|
||||
},
|
||||
station: {
|
||||
enabled: false,
|
||||
status: 0,
|
||||
ip: '0.0.0.0'
|
||||
}
|
||||
},
|
||||
|
||||
connection: {
|
||||
hostname: null,
|
||||
accesspoint: true,
|
||||
station: false,
|
||||
ssid: null,
|
||||
password: null,
|
||||
dhcp: true,
|
||||
ip: null,
|
||||
subnetmask: null,
|
||||
gateway: null
|
||||
},
|
||||
|
||||
system: {
|
||||
pins: {
|
||||
ledAP: null,
|
||||
ledSTA: null,
|
||||
apButton: null,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
created: function()
|
||||
{
|
||||
var self = this;
|
||||
|
||||
self.notificationTimer = null;
|
||||
|
||||
document.title = i18n.t('title');
|
||||
var hash = window.location.hash.substr(1);
|
||||
if (hash)
|
||||
self.activeTab = hash;
|
||||
|
||||
self.startLoadingIndicator();
|
||||
self.updateWiFiStatus();
|
||||
|
||||
|
||||
// Sequential loading of all the settings makes sure
|
||||
// we don't overload the ESP8266 with requests, as that
|
||||
// can cause it to run out of memory easily.
|
||||
// This is a horrible way to implement it, but I don't feel like
|
||||
// including a big library or working out a clean short solution
|
||||
// at the moment, and it works :)
|
||||
self.loadStatus().then(function()
|
||||
{
|
||||
self.loadConnection().then(function()
|
||||
{
|
||||
self.loadSystem().then(function()
|
||||
{
|
||||
self.stopLoadingIndicator();
|
||||
self.loading = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
showNotification: function(message, error)
|
||||
{
|
||||
var self = this;
|
||||
self.notification = {
|
||||
message: message,
|
||||
error: error
|
||||
};
|
||||
|
||||
if (self.notificationTimer != null)
|
||||
clearTimeout(self.notificationTimer);
|
||||
|
||||
self.notificationTimer = setTimeout(function()
|
||||
{
|
||||
self.notification = null;
|
||||
self.notificationTimer = null;
|
||||
}, 5000);
|
||||
},
|
||||
|
||||
hideNotification: function()
|
||||
{
|
||||
var self = this;
|
||||
self.notification = null;
|
||||
|
||||
if (self.notificationTimer != null)
|
||||
{
|
||||
clearTimeout(self.notificationTimer);
|
||||
self.notificationTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
handleAPIError: function(messageId, error)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
console.log(error);
|
||||
var errorMessage = '';
|
||||
|
||||
if (error.response)
|
||||
{
|
||||
errorMessage = 'HTTP response code ' + error.response.status;
|
||||
}
|
||||
else if (error.request)
|
||||
{
|
||||
errorMessage = 'No response';
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = error.message;
|
||||
}
|
||||
|
||||
self.showNotification(i18n.t(messageId) + '\n\n' + errorMessage, true);
|
||||
},
|
||||
|
||||
loadStatus: function()
|
||||
{
|
||||
var self = this;
|
||||
return axios.get('/api/status', { retry: 10, retryDelay: 1000 })
|
||||
.then(function(response)
|
||||
{
|
||||
if (typeof response.data == 'object')
|
||||
self.status = response.data;
|
||||
})
|
||||
.catch(self.handleAPIError.bind(self, 'error.loadStatus'));
|
||||
},
|
||||
|
||||
loadConnection: function()
|
||||
{
|
||||
var self = this;
|
||||
return axios.get('/api/connection', { retry: 10, retryDelay: 1000 })
|
||||
.then(function(response)
|
||||
{
|
||||
if (typeof response.data == 'object')
|
||||
self.connection = response.data;
|
||||
})
|
||||
.catch(self.handleAPIError.bind(self, 'error.loadConnection'));
|
||||
},
|
||||
|
||||
loadSystem: function()
|
||||
{
|
||||
var self = this;
|
||||
return axios.get('/api/system', { retry: 10, retryDelay: 1000 })
|
||||
.then(function(response)
|
||||
{
|
||||
if (typeof response.data == 'object')
|
||||
self.system = response.data;
|
||||
})
|
||||
.catch(self.handleAPIError.bind(self, 'error.loadSystem'));
|
||||
},
|
||||
|
||||
|
||||
applyConnection: function()
|
||||
{
|
||||
var self = this;
|
||||
if (self.saving) return;
|
||||
|
||||
self.saving = true;
|
||||
|
||||
axios.post('/api/connection', {
|
||||
hostname: self.connection.hostname,
|
||||
accesspoint: self.connection.accesspoint,
|
||||
station: self.connection.station,
|
||||
ssid: self.connection.ssid,
|
||||
password: self.connection.password,
|
||||
dhcp: self.connection.dhcp,
|
||||
ip: self.connection.ip,
|
||||
subnetmask: self.connection.subnetmask,
|
||||
gateway: self.connection.gateway,
|
||||
}, { retry: 10, retryDelay: 1000 })
|
||||
.then(function(response)
|
||||
{
|
||||
})
|
||||
.catch(self.handleAPIError.bind(self, 'error.applyConnection'))
|
||||
.then(function()
|
||||
{
|
||||
self.saving = false;
|
||||
});
|
||||
},
|
||||
|
||||
applySystem: function()
|
||||
{
|
||||
var self = this;
|
||||
if (self.saving) return;
|
||||
|
||||
self.saving = true;
|
||||
|
||||
axios.post('/api/system', self.system, { retry: 10, retryDelay: 1000 })
|
||||
.then(function(response)
|
||||
{
|
||||
self.showNotification(i18n.t('rebootPending'));
|
||||
})
|
||||
.catch(self.handleAPIError.bind(self, 'error.applySystem'))
|
||||
.then(function()
|
||||
{
|
||||
self.saving = false;
|
||||
});
|
||||
},
|
||||
|
||||
startLoadingIndicator: function()
|
||||
{
|
||||
var self = this;
|
||||
|
||||
self.loadingStage = 0;
|
||||
self.loadingTimer = setInterval(function()
|
||||
{
|
||||
self.loadingStage++;
|
||||
switch (self.loadingStage)
|
||||
{
|
||||
case 1: self.loadingIndicator = '/'; break;
|
||||
case 2: self.loadingIndicator = '-'; break;
|
||||
case 3: self.loadingIndicator = '\\'; break;
|
||||
case 4: self.loadingIndicator = '|'; self.loadingStage = 0; break;
|
||||
}
|
||||
}, 250);
|
||||
},
|
||||
|
||||
stopLoadingIndicator: function()
|
||||
{
|
||||
clearInterval(this.loadingTimer);
|
||||
},
|
||||
|
||||
getWiFiStationStatus: function()
|
||||
{
|
||||
if (!this.wifiStatus.station.enabled)
|
||||
return 'disconnected';
|
||||
|
||||
switch (this.wifiStatus.station.status)
|
||||
{
|
||||
case 0: // WL_IDLE_STATUS
|
||||
case 2: // WL_SCAN_COMPLETED
|
||||
return 'connecting';
|
||||
|
||||
case 1: // WL_NO_SSID_AVAIL
|
||||
case 4: // WL_CONNECT_FAILED
|
||||
case 5: // WL_CONNECTION_LOST
|
||||
return 'error';
|
||||
|
||||
case 3: // WL_CONNECTED
|
||||
return 'connected';
|
||||
|
||||
case 6: // WL_DISCONNECTED
|
||||
default:
|
||||
return 'disconnected';
|
||||
}
|
||||
},
|
||||
|
||||
getWiFiStationStatusText: function()
|
||||
{
|
||||
if (!this.wifiStatus.station.enabled)
|
||||
return i18n.t('wifiStatus.stationmode.disabled');
|
||||
|
||||
switch (this.wifiStatus.station.status)
|
||||
{
|
||||
case 0: // WL_IDLE_STATUS
|
||||
return i18n.t('wifiStatus.stationmode.idle');
|
||||
|
||||
case 1: // WL_NO_SSID_AVAIL
|
||||
return i18n.t('wifiStatus.stationmode.noSSID');
|
||||
|
||||
case 2: // WL_SCAN_COMPLETED
|
||||
return i18n.t('wifiStatus.stationmode.scanCompleted');
|
||||
|
||||
case 3: // WL_CONNECTED
|
||||
return this.wifiStatus.station.ip;
|
||||
|
||||
case 4: // WL_CONNECT_FAILED
|
||||
return i18n.t('wifiStatus.stationmode.connectFailed');
|
||||
|
||||
case 5: // WL_CONNECTION_LOST
|
||||
return i18n.t('wifiStatus.stationmode.connectionLost');
|
||||
|
||||
case 6: // WL_DISCONNECTED
|
||||
default:
|
||||
return i18n.t('wifiStatus.stationmode.disconnected');
|
||||
}
|
||||
},
|
||||
|
||||
updateWiFiStatus: function()
|
||||
{
|
||||
var self = this;
|
||||
if (!self.saving)
|
||||
{
|
||||
axios.get('/api/connection/status', { retry: 10, retryDelay: 1000 })
|
||||
.then(function(response)
|
||||
{
|
||||
if (typeof response.data == 'object')
|
||||
self.wifiStatus = response.data;
|
||||
})
|
||||
.catch(self.handleAPIError.bind(self, 'error.updateWiFiStatus'))
|
||||
.then(function()
|
||||
{
|
||||
setTimeout(self.updateWiFiStatus, 5000);
|
||||
});
|
||||
}
|
||||
else
|
||||
setTimeout(self.updateWiFiStatus, 5000);
|
||||
},
|
||||
|
||||
uploadFirmware: function()
|
||||
{
|
||||
var self = this;
|
||||
if (self.saving) return;
|
||||
|
||||
self.saving = true;
|
||||
self.uploadProgress = 0;
|
||||
|
||||
|
||||
var data = new FormData();
|
||||
data.append('file', document.getElementById('firmwareFile').files[0]);
|
||||
|
||||
var config = {
|
||||
timeout: 360000,
|
||||
onUploadProgress: function(progressEvent)
|
||||
{
|
||||
self.uploadProgress = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
||||
}
|
||||
};
|
||||
|
||||
axios.post('/api/firmware', data, config)
|
||||
.then(function(response)
|
||||
{
|
||||
self.showNotification(i18n.t('rebootPending'));
|
||||
})
|
||||
.catch(self.handleAPIError.bind(self, 'error.uploadFirmware'))
|
||||
.then(function()
|
||||
{
|
||||
self.uploadProgress = false;
|
||||
self.saving = false;
|
||||
|
||||
document.getElementById('firmware').reset();
|
||||
});
|
||||
},
|
||||
|
||||
deleteStackTrace: function()
|
||||
{
|
||||
var self = this;
|
||||
|
||||
return axios.get('/api/stacktrace/delete', { retry: 10, retryDelay: 1000 })
|
||||
.then(function(response)
|
||||
{
|
||||
self.status.resetReason = 0;
|
||||
self.status.stackTrace = false;
|
||||
})
|
||||
.catch(self.handleAPIError.bind(self, 'error.stackTraceDeleteError'));
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
hasResetError: function()
|
||||
{
|
||||
var self = this;
|
||||
|
||||
/*
|
||||
REASON_DEFAULT_RST = 0 normal startup by power on
|
||||
REASON_WDT_RST = 1 hardware watch dog reset
|
||||
REASON_EXCEPTION_RST = 2 exception reset, GPIO status won’t change
|
||||
REASON_SOFT_WDT_RST = 3 software watch dog reset, GPIO status won’t change
|
||||
REASON_SOFT_RESTART = 4 software restart ,system_restart , GPIO status won’t change
|
||||
REASON_DEEP_SLEEP_AWAKE = 5 wake up from deep-sleep
|
||||
REASON_EXT_SYS_RST = 6 system reset
|
||||
*/
|
||||
return (self.status.resetReason === 1 ||
|
||||
self.status.resetReason === 2 ||
|
||||
self.status.resetReason === 3 ||
|
||||
self.status.stackTrace);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,172 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>RGBWifi</title>
|
||||
<meta name="theme-color" content="#000000">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="bundle.css">
|
||||
<script src="bundle.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div v-cloak>
|
||||
<div class="notificationContainer">
|
||||
<div class="notification" :class="{ error: notification != null && notification.error }" v-if="notification != null" @click.prevent="hideNotification">
|
||||
<span class="message">{{ notification.message }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="container">
|
||||
<div class="header">
|
||||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAXBJREFUeNrsWdENgjAQLcYBcAPcACdQN4AN2MC4gRsYJ9ENdAPYADaADepdUhM1qFjurgp9ScMHgd713V3ftUp5eHgMF1rrGEau6VHD2HMbH5qJOLHra+fkzbsYRshM8pLTgUIgSgvuMMoYw+iMYTr6QhNYMrNiNywILlyVKdcyyPsmcRsyU50kgHtQQu2AdNJFpDkAK4I/LIWMb2AsIBcq0iRGeQGPTZfV6QE0+gDGF2rosGEgMQxIAhnZAiPN84upRf0/OlpsLCBp3yq0chgtCUUZrRw6QDO3EWHSqF9tarZaSKJXeJDdbQlsE0KD6JN/KoRsGhxXKClCKHJIfkThwMWhA6dBSolRijkJOd1ZUv9yQ9OpqbHpiaUVaEbZE7tIIjoBKXysos1c4V8ebLEdbv2bcMsE7gjuBdvXRSJ4F+/wqIXXrIGwmX3zwacLDheNO91OjLQKd14VMDCnYgCxVjI3NTe1mSoPD49x4SrAAG9qPn4eovCMAAAAAElFTkSuQmCC" />
|
||||
<h1>{{ $t('title') }}</h1>
|
||||
<h2>{{ status.systemID !== null ? $t('systemID') + ': ' + status.systemID : '' }}</h2>
|
||||
|
||||
<div class="wifistatus">
|
||||
<div class="connection">
|
||||
<div class="indicator" :data-status="wifiStatus.ap.enabled ? 'connected' : 'disconnected'"></div> {{ $t('wifiStatus.accesspoint.title') }} {{ wifiStatus.ap.enabled ? wifiStatus.ap.ip : $t('wifiStatus.accesspoint.disabled') }}
|
||||
</div>
|
||||
<div class="connection">
|
||||
<div class="indicator" :data-status="getWiFiStationStatus()"></div> {{ $t('wifiStatus.stationmode.title') }} {{ getWiFiStationStatusText() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="loading">
|
||||
{{ $t('loading') }} {{ loadingIndicator }}
|
||||
</div>
|
||||
|
||||
<div v-if="!loading">
|
||||
<div class="warning" v-if="hasResetError">
|
||||
<p>
|
||||
{{ $t('error.resetError') }}
|
||||
</p>
|
||||
|
||||
<p class="resetReason">
|
||||
{{ $t('error.resetReason.' + status.resetReason) }}
|
||||
</p>
|
||||
|
||||
<p v-if="status.stackTrace">
|
||||
{{ $t('error.stackTrace') }}
|
||||
</p>
|
||||
|
||||
<a class="button button-primary" href="/api/stacktrace/get" v-if="status.stackTrace">{{ $t('error.stackTraceDownload') }}</a>
|
||||
<a class="button" @click="deleteStackTrace">{{ $t('error.stackTraceDelete') }}</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="navigation tabs">
|
||||
<a class="button" :class="{ 'active': activeTab == 'status' }" @click="activeTab = 'status'">{{ $t('status.tabTitle') }}</a><a class="button" :class="{ 'active': activeTab == 'connection' }" @click="activeTab = 'connection'">{{ $t('connection.tabTitle') }}</a><a class="button" :class="{ 'active': activeTab == 'system' }" @click="activeTab = 'system'">{{ $t('system.tabTitle') }}</a>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab == 'status'">
|
||||
<!--
|
||||
|
||||
Status tab
|
||||
|
||||
-->
|
||||
<h3>{{ $t('status.title') }}</h3>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab == 'connection'">
|
||||
<!--
|
||||
|
||||
Connection tab
|
||||
|
||||
-->
|
||||
<form @submit.prevent="applyConnection">
|
||||
<h3>{{ $t('connection.title') }}</h3>
|
||||
|
||||
<check v-model.boolean="connection.accesspoint" :title="$t('connection.accesspoint')"></check>
|
||||
<span class="hint">{{ $t('connection.accesspointHint') }}</span>
|
||||
|
||||
<check v-model.boolean="connection.station" :title="$t('connection.stationmode')"></check>
|
||||
<span class="hint">{{ $t('connection.stationmodeHint') }}</span>
|
||||
|
||||
<label for="ssid">{{ $t('connection.ssid') }}</label>
|
||||
<input type="text" id="ssid" v-model="connection.ssid" :disabled="!connection.station">
|
||||
|
||||
<label for="password">{{ $t('connection.password') }}</label>
|
||||
<input type="password" id="password" v-model="connection.password" :disabled="!connection.station">
|
||||
|
||||
<check v-model.boolean="connection.dhcp" :disabled="!connection.station" :title="$t('connection.dhcp')" class="form-control"></check>
|
||||
<span class="hint">{{ $t('connection.dhcpHint') }}</span>
|
||||
|
||||
|
||||
<div class="suboptions">
|
||||
<label for="ip">{{ $t('connection.ipaddress') }}</label>
|
||||
<input type="text" id="ip" v-model="connection.ip" :disabled="!connection.station || connection.dhcp">
|
||||
|
||||
<label for="subnetmask">{{ $t('connection.subnetmask') }}</label>
|
||||
<input type="text" id="subnetmask" v-model="connection.subnetmask" :disabled="!connection.station || connection.dhcp">
|
||||
|
||||
<label for="gateway">{{ $t('connection.gateway') }}</label>
|
||||
<input type="text" id="gateway" v-model="connection.gateway" :disabled="!connection.station || connection.dhcp">
|
||||
</div>
|
||||
|
||||
|
||||
<label for="hostname">{{ $t('connection.hostname') }}</label>
|
||||
<input type="text" :placeholder="$t('connection.hostnamePlaceholder')" id="hostname" v-model="connection.hostname" :disabled="!connection.station">
|
||||
|
||||
<div class="buttons">
|
||||
<input type="submit" :disabled="saving" :value="saving ? $t('applyButtonSaving') : $t('applyButton')">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab == 'system'">
|
||||
<!--
|
||||
|
||||
System tab
|
||||
|
||||
-->
|
||||
<form @submit.prevent="uploadFirmware">
|
||||
<h3>{{ $t('system.firmwareTitle') }}</h3>
|
||||
|
||||
<input type="file" id="firmwareFile">
|
||||
|
||||
<div class="buttons">
|
||||
<input type="submit" :disabled="saving" :value="saving ? $t('applyButtonSaving') : $t('applyButton')">
|
||||
</div>
|
||||
|
||||
<div v-if="uploadProgress !== false">
|
||||
{{ uploadProgress }}%
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form @submit.prevent="applySystem">
|
||||
<h3>{{ $t('system.pinsTitle') }}</h3>
|
||||
|
||||
<div class="horizontal">
|
||||
<label for="pinLEDAP">{{ $t('system.pinLEDAP') }}</label>
|
||||
<input type="number" id="pinLEDAP" v-model.number="system.pins.ledAP">
|
||||
</div>
|
||||
|
||||
<div class="horizontal">
|
||||
<label for="pinLEDSTA">{{ $t('system.pinLEDSTA') }}</label>
|
||||
<input type="number" id="pinLEDSTA" v-model.number="system.pins.ledSTA">
|
||||
</div>
|
||||
|
||||
<div class="horizontal">
|
||||
<label for="pinAPButton">{{ $t('system.pinAPButton') }}</label>
|
||||
<input type="number" id="pinAPButton" v-model.number="system.pins.apButton">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="version">
|
||||
{{ $t('copyright') }}<br>
|
||||
{{ status.version !== null ? $t('firmwareVersion') + status.version : '' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script language="javascript">
|
||||
startApp();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,191 @@
|
|||
var messages = {
|
||||
en: {
|
||||
title: 'RGBWifi',
|
||||
systemID: 'System ID',
|
||||
firmwareVersion: 'Firmware version: ',
|
||||
copyright: 'Copyright © 2020 Mark van Renswoude',
|
||||
loading: 'Please wait, loading configuration...',
|
||||
rebootPending: 'The system will be rebooted, please refresh this page afterwards',
|
||||
|
||||
applyButton: 'Apply',
|
||||
applyButtonSaving: 'Saving...',
|
||||
deviceTime: 'Time: ',
|
||||
|
||||
wifiStatus: {
|
||||
accesspoint: {
|
||||
title: 'AP: ',
|
||||
disabled: 'Disabled'
|
||||
},
|
||||
|
||||
stationmode: {
|
||||
title: 'WiFi: ',
|
||||
disabled: 'Disabled',
|
||||
idle: 'Idle',
|
||||
noSSID: 'SSID not found',
|
||||
scanCompleted: 'Scan completed',
|
||||
connectFailed: 'Failed to connect',
|
||||
connectionLost: 'Connection lost',
|
||||
disconnected: 'Disconnected'
|
||||
}
|
||||
},
|
||||
|
||||
status: {
|
||||
tabTitle: 'Status',
|
||||
title: 'Current status'
|
||||
},
|
||||
|
||||
connection: {
|
||||
tabTitle: 'Connection',
|
||||
title: 'Connection parameters',
|
||||
|
||||
accesspoint: 'Enable access point',
|
||||
accesspointHint: 'Allows for a direct connection from your device to this RGBWifi module for configuration purposes. The RGBWifi configuration is available on http://192.168.1.4/ when you are connected to it. Turn it off as soon as station mode is configured, as it is not secured in any way. You can always turn this option back on by pushing the access point button until the LED lights up.',
|
||||
|
||||
stationmode: 'Enable station mode',
|
||||
stationmodeHint: 'Connect this RGBWifi module to your own WiFi router. Please enter the SSID, password and further configuration below.',
|
||||
|
||||
ssid: 'SSID',
|
||||
password: 'Password',
|
||||
|
||||
dhcp: 'Use DHCP',
|
||||
dhcpHint: 'Automatically assigns an IP address to this RGBWifi module. You probably want to keep this on unless you know what you\'re doing.',
|
||||
|
||||
ipaddress: 'IP address',
|
||||
subnetmask: 'Subnet mask',
|
||||
gateway: 'Gateway',
|
||||
hostname: 'Hostname',
|
||||
hostnamePlaceholder: 'Default: mac address'
|
||||
},
|
||||
|
||||
system: {
|
||||
tabTitle: 'System',
|
||||
pinsTitle: 'Hardware pinout',
|
||||
firmwareTitle: 'Firmware update',
|
||||
|
||||
pinLEDAP: 'Access Point status LED pin (+3.3v)',
|
||||
pinLEDSTA: 'Station Mode status LED pin (+3.3v)',
|
||||
pinAPButton: 'Enable Access Point button pin (active low)'
|
||||
},
|
||||
|
||||
error: {
|
||||
loadStatus: 'Could not load system status',
|
||||
loadConnection: 'Could not load connection settings',
|
||||
loadSystem: 'Could not load system settings',
|
||||
applyConnection: 'Could not save connection settings',
|
||||
applySystem: 'Could not save system settings',
|
||||
updateWiFiStatus: 'Could not retrieve WiFi status',
|
||||
uploadFirmware: 'Error while uploading firmware',
|
||||
|
||||
resetError: 'The system reports that it has been reset unexpectedly. The last power up status is:',
|
||||
resetReason: {
|
||||
0: 'Normal startup',
|
||||
1: 'Unresponsive, reset by hardware watchdog',
|
||||
2: 'Unhandled exception',
|
||||
3: 'Unresponsive, reset by software watchdog',
|
||||
4: 'System restart requested',
|
||||
5: 'Wake up from deep sleep',
|
||||
6: 'System reset'
|
||||
},
|
||||
stackTrace: 'A stack trace is available. Please send it to your nearest developer and/or delete it from this RGBWifi module to remove this message.',
|
||||
stackTraceDownload: 'Download',
|
||||
stackTraceDelete: 'Hide',
|
||||
|
||||
stackTraceDeleteError: 'Could not remove stack trace'
|
||||
}
|
||||
},
|
||||
|
||||
nl: {
|
||||
title: 'RGBWifi',
|
||||
systemID: 'Systeem ID',
|
||||
firmwareVersion: 'Firmware versie: ',
|
||||
copyright: 'Copyright © 2020 Mark van Renswoude',
|
||||
loading: 'Een ogenblik geduld, bezig met laden van configuratie...',
|
||||
rebootPending: 'Het systeem wordt opnieuw opgestart, ververse deze pagina nadien',
|
||||
|
||||
applyButton: 'Opslaan',
|
||||
applyButtonSaving: 'Bezig met opslaan...',
|
||||
deviceTime: 'Tijd: ',
|
||||
|
||||
wifiStatus: {
|
||||
accesspoint: {
|
||||
title: 'AP: ',
|
||||
disabled: 'Uitgeschakeld'
|
||||
},
|
||||
|
||||
stationmode: {
|
||||
title: 'WiFi: ',
|
||||
disabled: 'Uitgeschakeld',
|
||||
idle: 'Slaapstand',
|
||||
noSSID: 'SSID niet gevonden',
|
||||
scanCompleted: 'Scan afgerond',
|
||||
connectFailed: 'Kan geen verbinding maken',
|
||||
connectionLost: 'Verbinding verloren',
|
||||
disconnected: 'Niet verbonden'
|
||||
}
|
||||
},
|
||||
|
||||
status: {
|
||||
tabTitle: 'Status',
|
||||
title: 'Huidige status'
|
||||
},
|
||||
|
||||
connection: {
|
||||
tabTitle: 'Verbinding',
|
||||
title: 'Verbinding configuratie',
|
||||
|
||||
accesspoint: 'Access point inschakelen',
|
||||
accesspointHint: 'Maakt het mogelijk om een directe connectie vanaf een apparaat naar deze RGBWifi module te maken om de module te configureren. De RGBWifi module is te benaderen via http://192.168.1.4/ nadat je connectie hebt gemaakt. Schakel deze optie uit na het configureren, aangezien deze niet beveiligd is. Je kunt deze optie ook inschakelen door op de Access point knop te drukken totdat de LED aan gaat.',
|
||||
|
||||
stationmode: 'Verbinding met WiFi maken',
|
||||
stationmodeHint: 'Verbind deze RGBWifi module aan je eigen WiFi router. Vul hieronder het SSID en wachtwoord in, en configureer eventuel de overige opties.',
|
||||
|
||||
ssid: 'SSID',
|
||||
password: 'Wachtwoord',
|
||||
|
||||
dhcp: 'Gebruik DHCP',
|
||||
dhcpHint: 'Automatisch een IP adres toewijzen aan deze RGBWifi module. Waarschijnlijk wil je deze optie aan laten, tenzij je weet waar je mee bezig bent.',
|
||||
|
||||
ipaddress: 'IP adres',
|
||||
subnetmask: 'Subnet masker',
|
||||
gateway: 'Gateway',
|
||||
hostname: 'Hostnaam',
|
||||
hostnamePlaceholder: 'Standaard: mac adres'
|
||||
},
|
||||
|
||||
system: {
|
||||
tabTitle: 'Systeem',
|
||||
pinsTitle: 'Hardware aansluitingen',
|
||||
firmwareTitle: 'Firmware bijwerken',
|
||||
|
||||
pinLEDAP: 'Access Point status LED pin (+3.3v)',
|
||||
pinLEDSTA: 'WiFi status LED pin (+3.3v)',
|
||||
pinAPButton: 'Access Point inschakelen knop pin (actief laag)'
|
||||
},
|
||||
|
||||
error: {
|
||||
loadStatus: 'Kan systeemstatus niet ophalen',
|
||||
loadConnection: 'Kan verbinding instellingen niet ophalen',
|
||||
loadSystem: 'Kan systeem instellingen niet ophalen',
|
||||
applyConnection: 'Kan verbinding instellingen niet opslaan',
|
||||
applySystem: 'Kan systeem instellingen niet opslaan',
|
||||
updateWiFiStatus: 'Kan WiFi status niet ophalen',
|
||||
uploadFirmware: 'Fout tijdens bijwerken van firmware',
|
||||
|
||||
resetError: 'Het systeem is onverwachts herstart. De laatste status is:',
|
||||
resetReason: {
|
||||
0: 'Normaal opgestart',
|
||||
1: 'Reageert niet, herstart door hardware watchdog',
|
||||
2: 'Onafgehandelde fout',
|
||||
3: 'Reageert niet, herstart door software watchdog',
|
||||
4: 'Herstart verzoek door systeem',
|
||||
5: 'Wakker geworden uit diepe slaap',
|
||||
6: 'Systeem gereset'
|
||||
},
|
||||
stackTrace: 'Een stack trace is beschikbaar. Stuur het naar de dichtsbijzijnde ontwikkelaar en/of verwijder het van deze RGBWifi module om dit bericht te verbergen.',
|
||||
stackTraceDownload: 'Downloaden',
|
||||
stackTraceDelete: 'Verbergen',
|
||||
|
||||
stackTraceDeleteError: 'Kan stack trace niet verwijderen'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,333 @@
|
|||
%PDF-1.5
%âãÏÓ
|
||||
1 0 obj
<</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R 21 0 R]/Order 22 0 R/RBGroups[]>>/OCGs[5 0 R 21 0 R]>>/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<</Length 9212/Subtype/XML/Type/Metadata>>stream
|
||||
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 ">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
|
||||
xmlns:xmpGImg="http://ns.adobe.com/xap/1.0/g/img/">
|
||||
<xmp:CreatorTool>Adobe Illustrator CS6 (Windows)</xmp:CreatorTool>
|
||||
<xmp:CreateDate>2017-12-30T16:13+01:00</xmp:CreateDate>
|
||||
<xmp:MetadataDate>2017-12-30T16:17:16+01:00</xmp:MetadataDate>
|
||||
<xmp:ModifyDate>2017-12-30T16:17:16+01:00</xmp:ModifyDate>
|
||||
<xmp:Thumbnails>
|
||||
<rdf:Alt>
|
||||
<rdf:li rdf:parseType="Resource">
|
||||
<xmpGImg:width>248</xmpGImg:width>
|
||||
<xmpGImg:height>256</xmpGImg:height>
|
||||
<xmpGImg:format>JPEG</xmpGImg:format>
|
||||
<xmpGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAD4AwER
AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FX//2Q==</xmpGImg:image>
|
||||
</rdf:li>
|
||||
</rdf:Alt>
|
||||
</xmp:Thumbnails>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/"
|
||||
xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
|
||||
xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/">
|
||||
<xmpTPg:NPages>1</xmpTPg:NPages>
|
||||
<xmpTPg:HasVisibleTransparency>False</xmpTPg:HasVisibleTransparency>
|
||||
<xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint>
|
||||
<xmpTPg:MaxPageSize rdf:parseType="Resource">
|
||||
<stDim:w>512.000000</stDim:w>
|
||||
<stDim:h>512.000000</stDim:h>
|
||||
<stDim:unit>Points</stDim:unit>
|
||||
</xmpTPg:MaxPageSize>
|
||||
<xmpTPg:SwatchGroups>
|
||||
<rdf:Seq>
|
||||
<rdf:li rdf:parseType="Resource">
|
||||
<xmpG:groupName>Default Swatch Group</xmpG:groupName>
|
||||
<xmpG:groupType>0</xmpG:groupType>
|
||||
</rdf:li>
|
||||
</rdf:Seq>
|
||||
</xmpTPg:SwatchGroups>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<dc:format>application/pdf</dc:format>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/">
|
||||
<illustrator:Type>Document</illustrator:Type>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
|
||||
xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#">
|
||||
<xmpMM:DocumentID>xmp.did:89DEF0EC73EDE7119CCADB31B93B2005</xmpMM:DocumentID>
|
||||
<xmpMM:InstanceID>uuid:b03a2483-4f32-4465-b083-e0550ab05b88</xmpMM:InstanceID>
|
||||
<xmpMM:OriginalDocumentID>xmp.did:89DEF0EC73EDE7119CCADB31B93B2005</xmpMM:OriginalDocumentID>
|
||||
<xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass>
|
||||
<xmpMM:DerivedFrom rdf:parseType="Resource"/>
|
||||
<xmpMM:History>
|
||||
<rdf:Seq>
|
||||
<rdf:li rdf:parseType="Resource">
|
||||
<stEvt:action>saved</stEvt:action>
|
||||
<stEvt:instanceID>xmp.iid:89DEF0EC73EDE7119CCADB31B93B2005</stEvt:instanceID>
|
||||
<stEvt:when>2017-12-30T16:13:01+01:00</stEvt:when>
|
||||
<stEvt:softwareAgent>Adobe Illustrator CS6 (Windows)</stEvt:softwareAgent>
|
||||
<stEvt:changed>/</stEvt:changed>
|
||||
</rdf:li>
|
||||
</rdf:Seq>
|
||||
</xmpMM:History>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<?xpacket end="w"?>
|
||||
endstream
endobj
3 0 obj
<</Count 1/Kids[7 0 R]/Type/Pages>>
endobj
7 0 obj
<</ArtBox[72.1367 53.875 458.887 458.125]/BleedBox[0.0 0.0 512.0 512.0]/Contents 23 0 R/LastModified(D:20171230161716+02'00')/MediaBox[0.0 0.0 512.0 512.0]/Parent 3 0 R/PieceInfo<</Illustrator 24 0 R>>/Resources<</ExtGState<</GS0 25 0 R>>/Properties<</MC0 21 0 R>>>>/Thumb 26 0 R/TrimBox[0.0 0.0 512.0 512.0]/Type/Page>>
endobj
23 0 obj
<</Filter/FlateDecode/Length 177>>stream
|
||||
H‰”‘Á
|
||||
Â0†ïyŠ¼Àº&M¶õj•<6A>0dîàˆxqÂôàëÛ
|
||||
º¦ ¡¥)ßßÐr°ì‚ÅÕ: L`Q‰_«HÛí¼aª}*†±Ù"”í`ñ|‡)¶6VdÄáºFfÂãévnØ8'ñ|<7C>úEÞÿàûÿó]•ûDyšó¿L9{¼7\Q<>âr
¥g¹…1ÄŠñ)–4ãV5º€¿íM.Wÿ!7]üŠž |