Browse Source

Merge branch 'dev' into sensors

ech1560
Xose Pérez 6 years ago
parent
commit
f9d7390b89
29 changed files with 17723 additions and 17597 deletions
  1. +2
    -2
      .travis.yml
  2. +1
    -0
      code/espurna/config/arduino.h
  3. +1
    -1
      code/espurna/config/general.h
  4. +53
    -4
      code/espurna/config/hardware.h
  5. BIN
      code/espurna/data/index.all.html.gz
  6. BIN
      code/espurna/data/index.light.html.gz
  7. BIN
      code/espurna/data/index.rfbridge.html.gz
  8. BIN
      code/espurna/data/index.rfm69.html.gz
  9. BIN
      code/espurna/data/index.sensor.html.gz
  10. BIN
      code/espurna/data/index.small.html.gz
  11. +23
    -0
      code/espurna/migrate.ino
  12. +1
    -1
      code/espurna/rfm69.ino
  13. +3045
    -3042
      code/espurna/static/index.all.html.gz.h
  14. +2961
    -2957
      code/espurna/static/index.light.html.gz.h
  15. +2551
    -2548
      code/espurna/static/index.rfbridge.html.gz.h
  16. +4029
    -4027
      code/espurna/static/index.rfm69.html.gz.h
  17. +2310
    -2306
      code/espurna/static/index.sensor.html.gz.h
  18. +2514
    -2511
      code/espurna/static/index.small.html.gz.h
  19. +5
    -1
      code/espurna/utils.ino
  20. +78
    -87
      code/gulpfile.js
  21. +91
    -49
      code/html/custom.css
  22. +22
    -53
      code/html/custom.js
  23. +8
    -4
      code/html/index.html
  24. +28
    -4
      code/platformio.ini
  25. BIN
      images/devices/generic-ag-l4-1.jpg
  26. BIN
      images/devices/generic-ag-l4-2.jpg
  27. BIN
      images/devices/generic-ag-l4-3.jpg
  28. BIN
      images/devices/generic-ag-l4-4.jpg
  29. BIN
      images/devices/generic-ag-l4-5.jpg

+ 2
- 2
.travis.yml View File

@ -2,6 +2,7 @@ language: python
python: python:
- '2.7' - '2.7'
sudo: false sudo: false
conditions: v1
cache: cache:
directories: directories:
- "~/.npm" - "~/.npm"
@ -19,7 +20,7 @@ script:
stages: stages:
- name: Test - name: Test
- name: Release - name: Release
if: tag IS present AND branch = master
if: tag IS present AND tag =~ ^\d+\.\d+\.\d+$
jobs: jobs:
include: include:
- stage: Test - stage: Test
@ -42,7 +43,6 @@ deploy:
skip_cleanup: true skip_cleanup: true
on: on:
tags: true tags: true
branch: master
repo: xoseperez/espurna repo: xoseperez/espurna
condition: $TRAVIS_BUILD_STAGE_NAME = Release condition: $TRAVIS_BUILD_STAGE_NAME = Release
notifications: notifications:


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

@ -89,6 +89,7 @@
//#define BLITZWOLF_BWSHP2 //#define BLITZWOLF_BWSHP2
//#define BH_ONOFRE //#define BH_ONOFRE
//#define ITEAD_SONOFF_IFAN02 //#define ITEAD_SONOFF_IFAN02
//#define GENERIC_AG_L4
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Features (values below are non-default values) // Features (values below are non-default values)


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

@ -784,7 +784,7 @@
#endif #endif
#if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER #if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER
#define LIGHT_MAX_PWM 10000 // 5000 * 200ns => 1 kHz
#define LIGHT_MAX_PWM 10000 // 10000 * 200ns => 2 kHz
#endif #endif
#endif // LIGHT_MAX_PWM #endif // LIGHT_MAX_PWM


+ 53
- 4
code/espurna/config/hardware.h View File

@ -15,7 +15,7 @@
// RELAY#_TYPE: Relay can be RELAY_TYPE_NORMAL, RELAY_TYPE_INVERSE, RELAY_TYPE_LATCHED or RELAY_TYPE_LATCHED_INVERSE // RELAY#_TYPE: Relay can be RELAY_TYPE_NORMAL, RELAY_TYPE_INVERSE, RELAY_TYPE_LATCHED or RELAY_TYPE_LATCHED_INVERSE
// LED#_PIN: GPIO for the n-th LED (1-based, up to 8 LEDs) // LED#_PIN: GPIO for the n-th LED (1-based, up to 8 LEDs)
// LED#_PIN_INVERSE: LED has inversed logic (lit when pulled down) // LED#_PIN_INVERSE: LED has inversed logic (lit when pulled down)
// LED#_MODE: Check general.h for LED_MODE_%
// LED#_MODE: Check types.h for LED_MODE_%
// LED#_RELAY: Linked relay (1-based) // LED#_RELAY: Linked relay (1-based)
// //
// Besides, other hardware specific information should be stated here // Besides, other hardware specific information should be stated here
@ -1580,7 +1580,7 @@
// LEDs // LEDs
#define LED1_PIN 13 #define LED1_PIN 13
#define LED1_PIN_INVERSE 0
#define LED1_PIN_INVERSE 1
// HLW8012 // HLW8012
#ifndef HLW8012_SUPPORT #ifndef HLW8012_SUPPORT
@ -2174,13 +2174,19 @@
// Buttons // Buttons
#define BUTTON1_PIN 12 #define BUTTON1_PIN 12
#define BUTTON1_RELAY 1 #define BUTTON1_RELAY 1
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_MODE BUTTON_SWITCH | BUTTON_DEFAULT_HIGH //Hardware Pullup
#define BUTTON1_PRESS BUTTON_MODE_NONE
#define BUTTON1_CLICK BUTTON_MODE_TOGGLE
#define BUTTON1_DBLCLICK BUTTON_MODE_NONE #define BUTTON1_DBLCLICK BUTTON_MODE_NONE
#define BUTTON1_LNGCLICK BUTTON_MODE_NONE
#define BUTTON1_LNGLNGCLICK BUTTON_MODE_NONE
#define BUTTON2_PIN 13 #define BUTTON2_PIN 13
#define BUTTON2_RELAY 2 #define BUTTON2_RELAY 2
#define BUTTON2_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON2_MODE BUTTON_SWITCH | BUTTON_DEFAULT_HIGH //Hardware Pullup
#define BUTTON2_CLICK BUTTON_MODE_TOGGLE
// Relays // Relays
#define RELAY1_PIN 4 #define RELAY1_PIN 4
@ -2423,7 +2429,50 @@
#define HLW8012_POWER_RATIO 3414290 #define HLW8012_POWER_RATIO 3414290
#define HLW8012_INTERRUPT_ON FALLING #define HLW8012_INTERRUPT_ON FALLING
#elif defined(GENERIC_AG_L4)
// Info
#define MANUFACTURER "GENERIC"
#define DEVICE "AG_L4"
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER
#define DUMMY_RELAY_COUNT 1
// button 1: "power" button
#define BUTTON1_PIN 4
#define BUTTON1_RELAY 1
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_SET_PULLUP | BUTTON_DEFAULT_HIGH
#define BUTTON1_PRESS BUTTON_MODE_TOGGLE
#define BUTTON1_CLICK BUTTON_MODE_NONE
#define BUTTON1_DBLCLICK BUTTON_MODE_NONE
#define BUTTON1_LNGCLICK BUTTON_MODE_NONE
#define BUTTON1_LNGLNGCLICK BUTTON_MODE_RESET
// button 2: "wifi" button
#define BUTTON2_PIN 2
#define BUTTON2_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON2_PRESS BUTTON_MODE_TOGGLE
#define BUTTON2_CLICK BUTTON_MODE_NONE
#define BUTTON2_DBLCLICK BUTTON_MODE_NONE
#define BUTTON2_LNGCLICK BUTTON_MODE_NONE
#define BUTTON2_LNGLNGCLICK BUTTON_MODE_NONE
// LEDs
#define LED1_PIN 5 // red status led
#define LED1_PIN_INVERSE 0
#define LED2_PIN 16 // master light power
#define LED2_PIN_INVERSE 1
#define LED2_MODE LED_MODE_RELAY
// Light
#define LIGHT_CHANNELS 3
#define LIGHT_CH1_PIN 14 // RED
#define LIGHT_CH2_PIN 13 // GREEN
#define LIGHT_CH3_PIN 12 // BLUE
#define LIGHT_CH1_INVERSE 0
#define LIGHT_CH2_INVERSE 0
#define LIGHT_CH3_INVERSE 0
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// TEST boards (do not use!!) // TEST boards (do not use!!)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


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


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


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


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


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


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


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

@ -1064,6 +1064,29 @@ void migrate() {
setSetting("relayType", 2, RELAY_TYPE_NORMAL); setSetting("relayType", 2, RELAY_TYPE_NORMAL);
setSetting("relayType", 3, RELAY_TYPE_NORMAL); setSetting("relayType", 3, RELAY_TYPE_NORMAL);
#elif defined(GENERIC_AG_L4)
setSetting("board", 82);
setSetting("btnGPIO", 0, 4);
setSetting("btnGPIO", 1, 2);
setSetting("btnRelay", 0, 0);
setSetting("ledGPIO", 0, 5);
setSetting("ledGPIO", 1, 16);
setSetting("ledLogic", 0, 0);
setSetting("ledLogic", 1, 1);
setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
setSetting("lightProvider", LIGHT_PROVIDER_DIMMER);
setSetting("chGPIO", 0, 14);
setSetting("chGPIO", 1, 13);
setSetting("chGPIO", 2, 12);
setSetting("chLogic", 0, 0);
setSetting("chLogic", 1, 0);
setSetting("chLogic", 2, 0);
setSetting("relays", 1);
#else #else
// Allow users to define new settings without migration config // Allow users to define new settings without migration config


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

@ -257,13 +257,13 @@ void rfm69Setup() {
_rfm69_radio->initialize(RFM69_FREQUENCY, RFM69_NODE_ID, RFM69_NETWORK_ID); _rfm69_radio->initialize(RFM69_FREQUENCY, RFM69_NODE_ID, RFM69_NETWORK_ID);
_rfm69_radio->encrypt(RFM69_ENCRYPTKEY); _rfm69_radio->encrypt(RFM69_ENCRYPTKEY);
_rfm69_radio->promiscuous(RFM69_PROMISCUOUS); _rfm69_radio->promiscuous(RFM69_PROMISCUOUS);
_rfm69_radio->enableAutoPower(0);
if (RFM69_IS_RFM69HW) _rfm69_radio->setHighPower(); if (RFM69_IS_RFM69HW) _rfm69_radio->setHighPower();
DEBUG_MSG_P(PSTR("[RFM69] Worning at %u MHz\n"), RFM69_FREQUENCY == RF69_433MHZ ? 433 : RFM69_FREQUENCY == RF69_868MHZ ? 868 : 915); DEBUG_MSG_P(PSTR("[RFM69] Worning at %u MHz\n"), RFM69_FREQUENCY == RF69_433MHZ ? 433 : RFM69_FREQUENCY == RF69_868MHZ ? 868 : 915);
DEBUG_MSG_P(PSTR("[RFM69] Node %u\n"), RFM69_NODE_ID); DEBUG_MSG_P(PSTR("[RFM69] Node %u\n"), RFM69_NODE_ID);
DEBUG_MSG_P(PSTR("[RFM69] Network %u\n"), RFM69_NETWORK_ID); DEBUG_MSG_P(PSTR("[RFM69] Network %u\n"), RFM69_NETWORK_ID);
DEBUG_MSG_P(PSTR("[RFM69] Promiscuous mode %s\n"), RFM69_PROMISCUOUS ? "ON" : "OFF"); DEBUG_MSG_P(PSTR("[RFM69] Promiscuous mode %s\n"), RFM69_PROMISCUOUS ? "ON" : "OFF");
DEBUG_MSG_P(PSTR("[RFM69] Auto Transmission Control (ATC) enabled\n"));
#if WEB_SUPPORT #if WEB_SUPPORT
wsOnSendRegister(_rfm69WebSocketOnSend); wsOnSendRegister(_rfm69WebSocketOnSend);


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


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


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


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


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


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


+ 5
- 1
code/espurna/utils.ino View File

@ -260,7 +260,11 @@ void _info_print_memory_layout_line(const char * name, unsigned long bytes) {
void info() { void info() {
DEBUG_MSG_P(PSTR("\n\n")); DEBUG_MSG_P(PSTR("\n\n"));
DEBUG_MSG_P(PSTR("[INIT] %s %s\n"), (char *) APP_NAME, (char *) APP_VERSION);
if (strlen(APP_REVISION) > 0) {
DEBUG_MSG_P(PSTR("[INIT] %s %s (%s)\n"), (char *) APP_NAME, (char *) APP_VERSION, (char *) APP_REVISION);
} else {
DEBUG_MSG_P(PSTR("[INIT] %s %s\n"), (char *) APP_NAME, (char *) APP_VERSION);
}
DEBUG_MSG_P(PSTR("[INIT] %s\n"), (char *) APP_AUTHOR); DEBUG_MSG_P(PSTR("[INIT] %s\n"), (char *) APP_AUTHOR);
DEBUG_MSG_P(PSTR("[INIT] %s\n\n"), (char *) APP_WEBSITE); DEBUG_MSG_P(PSTR("[INIT] %s\n\n"), (char *) APP_WEBSITE);
DEBUG_MSG_P(PSTR("[INIT] CPU chip ID: 0x%06X\n"), ESP.getChipId()); DEBUG_MSG_P(PSTR("[INIT] CPU chip ID: 0x%06X\n"), ESP.getChipId());


+ 78
- 87
code/gulpfile.js View File

@ -19,34 +19,39 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*eslint quotes: ["error", "single"]*/
/*eslint quotes: ['error', 'single']*/
/*eslint-env es6*/ /*eslint-env es6*/
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Dependencies // Dependencies
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
const fs = require('fs');
const gulp = require('gulp'); const gulp = require('gulp');
const runSequence = require('run-sequence');
const through = require('through2');
const htmlmin = require('gulp-htmlmin'); const htmlmin = require('gulp-htmlmin');
const uglify = require('gulp-uglify'); const uglify = require('gulp-uglify');
const gzip = require('gulp-gzip');
const inline = require('gulp-inline'); const inline = require('gulp-inline');
const inlineImages = require('gulp-css-base64'); const inlineImages = require('gulp-css-base64');
const favicon = require('gulp-base64-favicon'); const favicon = require('gulp-base64-favicon');
const crass = require('gulp-crass');
const htmllint = require('gulp-htmllint'); const htmllint = require('gulp-htmllint');
const csslint = require('gulp-csslint'); const csslint = require('gulp-csslint');
const crass = require('gulp-crass');
const rename = require('gulp-rename');
const replace = require('gulp-replace'); const replace = require('gulp-replace');
const remover = require('gulp-remove-code'); const remover = require('gulp-remove-code');
const map = require('map-stream');
const rename = require('gulp-rename');
const runSequence = require('run-sequence');
const gzip = require('gulp-gzip');
const path = require('path');
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Configuration // Configuration
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
const htmlFolder = 'html/';
const configFolder = 'espurna/config/';
const dataFolder = 'espurna/data/'; const dataFolder = 'espurna/data/';
const staticFolder = 'espurna/static/'; const staticFolder = 'espurna/static/';
@ -54,51 +59,39 @@ const staticFolder = 'espurna/static/';
// Methods // Methods
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
var buildHeaderFile = function() {
String.prototype.replaceAll = function(search, replacement) {
var target = this;
return target.split(search).join(replacement);
};
var toHeader = function(name, debug) {
return map(function(file, cb) {
return through.obj(function (source, encoding, callback) {
var parts = file.path.split("/");
var parts = source.path.split(path.sep);
var filename = parts[parts.length - 1]; var filename = parts[parts.length - 1];
var destination = staticFolder + filename + ".h";
var safename = "webui_image";
var wstream = fs.createWriteStream(destination);
wstream.on('error', function (err) {
console.error(err);
});
var data = fs.readFileSync(file.path);
wstream.write('#define ' + safename + '_len ' + data.length + '\n');
wstream.write('const uint8_t ' + safename + '[] PROGMEM = {');
for (var i=0; i<data.length; i++) {
if (0 === (i % 20)) {
wstream.write('\n');
}
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
if (i < (data.length - 1)) {
wstream.write(',');
}
var safename = name || filename.split('.').join('_');
// Generate output
var output = '';
output += '#define ' + safename + '_len ' + source.contents.length + '\n';
output += 'const uint8_t ' + safename + '[] PROGMEM = {';
for (var i=0; i<source.contents.length; i++) {
if (i > 0) output += ',';
if (0 === (i % 20)) output += '\n';
output += '0x' + ('00' + source.contents[i].toString(16)).slice(-2);
} }
output += '\n};';
wstream.write('\n};');
wstream.end();
// clone the contents
var destination = source.clone();
destination.path = source.path + '.h';
destination.contents = Buffer.from(output);
var fstat = fs.statSync(file.path);
console.log("Created '" + filename + "' size: " + fstat.size + " bytes");
if (debug) {
console.info('Image ' + filename + ' \tsize: ' + source.contents.length + ' bytes');
}
cb(0, destination);
callback(null, destination);
}); });
}
};
var htmllintReporter = function(filepath, issues) { var htmllintReporter = function(filepath, issues) {
if (issues.length > 0) { if (issues.length > 0) {
@ -118,17 +111,17 @@ var htmllintReporter = function(filepath, issues) {
var buildWebUI = function(module) { var buildWebUI = function(module) {
var modules = {"light": false, "sensor": false, "rfbridge": false, "rfm69": false};
if ("all" == module) {
modules["light"] = true;
modules["sensor"] = true;
modules["rfbridge"] = false; // we will never be adding this except when building RFBRIDGE
modules["rfm69"] = false; // we will never be adding this except when building RFM69GW
} else if ("small" != module) {
var modules = {'light': false, 'sensor': false, 'rfbridge': false, 'rfm69': false};
if ('all' === module) {
modules['light'] = true;
modules['sensor'] = true;
modules['rfbridge'] = false; // we will never be adding this except when building RFBRIDGE
modules['rfm69'] = false; // we will never be adding this except when building RFM69GW
} else if ('small' !== module) {
modules[module] = true; modules[module] = true;
} }
return gulp.src('html/*.html').
return gulp.src(htmlFolder + '*.html').
pipe(htmllint({ pipe(htmllint({
'failOnError': true, 'failOnError': true,
'rules': { 'rules': {
@ -138,7 +131,7 @@ var buildWebUI = function(module) {
}, htmllintReporter)). }, htmllintReporter)).
pipe(favicon()). pipe(favicon()).
pipe(inline({ pipe(inline({
base: 'html/',
base: htmlFolder,
js: [], js: [],
css: [crass, inlineImages], css: [crass, inlineImages],
disabledTypes: ['svg', 'img'] disabledTypes: ['svg', 'img']
@ -152,8 +145,10 @@ var buildWebUI = function(module) {
})). })).
pipe(replace('pure-', 'p-')). pipe(replace('pure-', 'p-')).
pipe(gzip()). pipe(gzip()).
pipe(rename("index." + module + ".html.gz")).
pipe(gulp.dest(dataFolder));
pipe(rename('index.' + module + '.html.gz')).
pipe(gulp.dest(dataFolder)).
pipe(toHeader('webui_image', true)).
pipe(gulp.dest(staticFolder));
}; };
@ -161,55 +156,51 @@ var buildWebUI = function(module) {
// Tasks // Tasks
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
gulp.task('build_certs', function() {
gulp.task('certs', function() {
gulp.src(dataFolder + 'server.*'). gulp.src(dataFolder + 'server.*').
pipe(buildHeaderFile());
pipe(toHeader(debug=false)).
pipe(gulp.dest(staticFolder));
}); });
gulp.task('csslint', function() { gulp.task('csslint', function() {
gulp.src('html/*.css').
gulp.src(htmlFolder + '*.css').
pipe(csslint({ids: false})). pipe(csslint({ids: false})).
pipe(csslint.formatter()); pipe(csslint.formatter());
}); });
gulp.task('build_webui_small', function() {
return buildWebUI("small");
})
gulp.task('webui_small', function() {
return buildWebUI('small');
});
gulp.task('build_webui_sensor', function() {
return buildWebUI("sensor");
})
gulp.task('webui_sensor', function() {
return buildWebUI('sensor');
});
gulp.task('build_webui_light', function() {
return buildWebUI("light");
})
gulp.task('webui_light', function() {
return buildWebUI('light');
});
gulp.task('build_webui_rfbridge', function() {
return buildWebUI("rfbridge");
})
gulp.task('webui_rfbridge', function() {
return buildWebUI('rfbridge');
});
gulp.task('build_webui_rfm69', function() {
return buildWebUI("rfm69");
})
gulp.task('webui_rfm69', function() {
return buildWebUI('rfm69');
});
gulp.task('build_webui_all', function() {
return buildWebUI("all");
})
gulp.task('webui_all', function() {
return buildWebUI('all');
});
gulp.task('buildfs_inline', function(cb) {
gulp.task('webui', function(cb) {
runSequence([ runSequence([
'build_webui_small',
'build_webui_sensor',
'build_webui_light',
'build_webui_rfbridge',
'build_webui_rfm69',
'build_webui_all'
'webui_small',
'webui_sensor',
'webui_light',
'webui_rfbridge',
'webui_rfm69',
'webui_all'
], cb); ], cb);
}); });
gulp.task('buildfs_embeded', ['buildfs_inline'], function() {
gulp.src(dataFolder + 'index.*').
pipe(buildHeaderFile());
});
gulp.task('default', ['buildfs_embeded']);
gulp.task('default', ['webui']);

+ 91
- 49
code/html/custom.css View File

@ -158,6 +158,9 @@ div.state {
.main-buttons button { .main-buttons button {
width: 100px; width: 100px;
} }
.button-del-schedule {
margin-top: 15px;
}
.button-reboot, .button-reboot,
.button-reconnect, .button-reconnect,
@ -233,59 +236,98 @@ span.slider {
Checkboxes Checkboxes
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
.checkbox-container {
width: 130px;
height: 30px;
margin: 3px 0 10px 0px;
position: relative;
border-radius: 4px;
overflow: hidden;
user-select: none;
cursor: pointer;
}
.checkbox-container input {
display: none;
}
.inner-container {
position: absolute;
left: 0;
top: 0;
width: inherit;
height: inherit;
text-transform: uppercase;
font-size: .7em;
letter-spacing: .2em;
.toggleWrapper {
overflow: hidden;
width: auto;
height: 30px;
margin: 0px 0px 10px 0px;
padding: 0px;
border-radius: 4px;
box-shadow: inset 1px 1px #CCC;
} }
.inner-container:first-child {
background: #e9e9e9;
color: #a9a9a9;
.toggleWrapper input {
position: absolute;
left: -99em;
} }
.inner-container:nth-child(2) {
background: #c00000;
color: white;
-webkit-clip-path: inset(0 50% 0 0);
clip-path: inset(0 50% 0 0);
transition: .3s cubic-bezier(0,0,0,1);
label[for].toggle {
margin: 0px;
padding: 0px;
} }
.toggle { .toggle {
width: 50%;
position: absolute;
height: inherit;
display: flex;
box-sizing: border-box;
}
.toggle p {
margin: auto;
}
.toggle:nth-child(1) {
right: 0;
letter-spacing:normal;
cursor: pointer;
display: inline-block;
position: relative;
width: 130px;
height: 100%;
background: #e9e9e9;
color: #a9a9a9;
border-radius: 4px;
-webkit-transition: all 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition: all 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
}
.toggle:before,
.toggle:after {
position: absolute;
line-height: 30px;
font-size: .7em;
z-index: 2;
-webkit-transition: all 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition: all 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
}
.toggle:before {
content: "NO";
left: 20px;
}
input[name="relay"] + .toggle:before {
content: "OFF";
}
.toggle:after{
content: "YES";
right: 20px;
}
input[name="relay"] + .toggle:after {
content: "ON";
}
.toggle__handler {
display: inline-block;
position: relative;
z-index: 1;
background: #c00000;
width: 50%;
height: 100%;
border-radius: 4px;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
top: 0px;
left: 0px;
-webkit-transition: all 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition: all 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
-webkit-transform: translateX(0px);
transform: translateX(0px);
}
input:checked + .toggle:after {
color: #fff;
}
input:checked + .toggle:before {
color: #a9a9a9;
}
input + .toggle:before {
color: #fff;
}
input:checked + .toggle .toggle__handler {
width: 50%;
background: #00c000;
-webkit-transform: translateX(65px);
transform: translateX(65px);
border-color: #000;
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
input[disabled] + .toggle .toggle__handler {
background: #ccc;
} }
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------


+ 22
- 53
code/html/custom.js View File

@ -177,8 +177,13 @@ function validateForm(form) {
var re_hostname = new RegExp('^(?!-)[A-Za-z0-9-]{0,31}[A-Za-z0-9]$'); var re_hostname = new RegExp('^(?!-)[A-Za-z0-9-]{0,31}[A-Za-z0-9]$');
var hostname = $("input[name='hostname']", form).val();
if (!re_hostname.test(hostname)) {
var hostname = $("input[name='hostname']", form);
var hasChanged = hostname.attr("hasChanged") || 0;
if (0 === hasChanged) {
return true;
}
if (!re_hostname.test(hostname.val())) {
alert("Hostname cannot be empty and may only contain the ASCII letters ('A' through 'Z' and 'a' through 'z'), the digits '0' through '9', and the hyphen ('-')! They can neither start or end with an hyphen."); alert("Hostname cannot be empty and may only contain the ASCII letters ('A' through 'Z' and 'a' through 'z'), the digits '0' through '9', and the hyphen ('-')! They can neither start or end with an hyphen.");
return false; return false;
} }
@ -781,11 +786,13 @@ function addSchedule(event) {
}); });
$(line).find(".button-del-schedule").on("click", delSchedule); $(line).find(".button-del-schedule").on("click", delSchedule);
$(line).find(".button-more-schedule").on("click", moreSchedule); $(line).find(".button-more-schedule").on("click", moreSchedule);
$(line).find("input[name='schUTC']").prop("id", "schUTC" + (numSchedules + 1))
.next().prop("for", "schUTC" + (numSchedules + 1));
$(line).find("input[name='schEnabled']").prop("id", "schEnabled" + (numSchedules + 1))
.next().prop("for", "schEnabled" + (numSchedules + 1));
line.appendTo("#schedules"); line.appendTo("#schedules");
$(line).find("input[type='checkbox']").prop("checked", false); $(line).find("input[type='checkbox']").prop("checked", false);
initCheckboxes();
return line; return line;
} }
@ -805,7 +812,14 @@ function initRelays(data) {
// Add relay fields // Add relay fields
var line = $(template).clone(); var line = $(template).clone();
$(".id", line).html(i); $(".id", line).html(i);
$(":checkbox", line).prop('checked', data[i]).attr("data", i);
$(":checkbox", line).prop('checked', data[i]).attr("data", i)
.prop("id", "relay" + i)
.on("change", function (event) {
var id = parseInt($(event.target).attr("data"), 10);
var status = $(event.target).prop("checked");
doToggle(id, status);
});
$("label.toggle", line).prop("for", "relay" + i)
line.appendTo("#relays"); line.appendTo("#relays");
// Populate the relay SELECTs // Populate the relay SELECTs
@ -816,57 +830,13 @@ function initRelays(data) {
} }
function initCheckboxes() {
var setCheckbox = function(element, value) {
var container = $(".toggle-container", $(element));
if (value) {
container.css("-webkit-clip-path", "inset(0 0 0 50%)");
container.css("clip-path", "inset(0 0 0 50%)");
container.css("backgroundColor", "#00c000");
} else {
container.css("-webkit-clip-path", "inset(0 50% 0 0)");
container.css("clip-path", "inset(0 50% 0 0)");
container.css("backgroundColor", "#c00000");
}
}
$(".checkbox-container")
.each(function() {
var status = $(this).next().prop('checked');
setCheckbox(this, status);
})
.off('click')
.on('click', function() {
var checkbox = $(this).next();
var status = checkbox.prop('checked');
status = !status;
checkbox.prop('checked', status);
setCheckbox(this, status);
if ("relay" == checkbox.attr('name')) {
var id = parseInt(checkbox.attr('data'), 10);
doToggle(id, status);
}
});
}
function createCheckboxes() { function createCheckboxes() {
$("input[type='checkbox']").each(function() { $("input[type='checkbox']").each(function() {
var text_on = $(this).attr("on") || "YES";
var text_off = $(this).attr("off") || "NO";
var toggles = "<div class=\"toggle\"><p>" + text_on + "</p></div><div class=\"toggle\"><p>" + text_off + "</p></div>";
var content = "<div class=\"checkbox-container\"><div class=\"inner-container\">" + toggles
+ "</div><div class=\"inner-container toggle-container\">" + toggles + "</div></div>";
$(this).before(content).hide();
if($(this).prop("name"))$(this).prop("id", $(this).prop("name"));
$(this).parent().addClass("toggleWrapper");
$(this).after('<label for="' + $(this).prop("name") + '" class="toggle"><span class="toggle__handler"></span></label>')
}); });
@ -1495,7 +1465,6 @@ function processData(data) {
} }
resetOriginals(); resetOriginals();
initCheckboxes();
} }


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

@ -245,6 +245,9 @@
<div class="pure-u-1-2">Firmware version</div> <div class="pure-u-1-2">Firmware version</div>
<div class="pure-u-11-24"><span class="right" name="app_version"></span></div> <div class="pure-u-11-24"><span class="right" name="app_version"></span></div>
<div class="pure-u-1-2">Firmware revision</div>
<div class="pure-u-11-24"><span class="right" name="app_revision"></span></div>
<div class="pure-u-1-2">Firmware build date</div> <div class="pure-u-1-2">Firmware build date</div>
<div class="pure-u-11-24"><span class="right" name="app_build"></span></div> <div class="pure-u-11-24"><span class="right" name="app_build"></span></div>
@ -634,7 +637,6 @@
ESPurna will scan for visible WiFi SSIDs and try to connect to networks defined below in order of <strong>signal strength</strong>, even if multiple AP share the same SSID. ESPurna will scan for visible WiFi SSIDs and try to connect to networks defined below in order of <strong>signal strength</strong>, even if multiple AP share the same SSID.
When disabled, ESPurna will try to connect to the networks in the same order they are listed below. When disabled, ESPurna will try to connect to the networks in the same order they are listed below.
Disable this option if you are <strong>connecting to a single access point</strong> (or router) or to a <strong>hidden SSID</strong>. Disable this option if you are <strong>connecting to a single access point</strong> (or router) or to a <strong>hidden SSID</strong>.
</thead>
</div> </div>
<div class="pure-u-0 pure-u-lg-1-4"></div> <div class="pure-u-0 pure-u-lg-1-4"></div>
<button class="pure-button button-wifi-scan" type="button">Scan now</button> <button class="pure-button button-wifi-scan" type="button">Scan now</button>
@ -855,7 +857,8 @@
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Use JSON payload</label> <label class="pure-u-1 pure-u-lg-1-4">Use JSON payload</label>
<div class="pure-u-1 pure-u-lg-3-4"><input type="checkbox" name="mqttUseJson" tabindex="32" /></div>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="mqttUseJson" tabindex="32" /></div>
<div class="pure-u-1 pure-u-lg-1-2"></div>
<div class="pure-u-1 pure-u-lg-1-4"></div> <div class="pure-u-1 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint"> <div class="pure-u-1 pure-u-lg-3-4 hint">
All messages (except the device status) will be included in a JSON payload along with the timestamp and hostname All messages (except the device status) will be included in a JSON payload along with the timestamp and hostname
@ -1387,6 +1390,7 @@
<label class="pure-u-1 pure-u-lg-1-4">Use UTC time</label> <label class="pure-u-1 pure-u-lg-1-4">Use UTC time</label>
<div class="pure-u-1 pure-u-lg-3-4"><input type="checkbox" name="schUTC" /></div> <div class="pure-u-1 pure-u-lg-3-4"><input type="checkbox" name="schUTC" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<label class="pure-u-1 pure-u-lg-1-4">And weekday is one of</label> <label class="pure-u-1 pure-u-lg-1-4">And weekday is one of</label>
<div class="pure-u-2-5 pure-u-lg-1-5"> <div class="pure-u-2-5 pure-u-lg-1-5">
<input class="pure-u-23-24 pure-u-lg-23-24" name="schWDs" type="text" maxlength="15" tabindex="0" value="1,2,3,4,5,6,7" /> <input class="pure-u-23-24 pure-u-lg-23-24" name="schWDs" type="text" maxlength="15" tabindex="0" value="1,2,3,4,5,6,7" />
@ -1399,7 +1403,7 @@
<label class="pure-u-1 pure-u-lg-1-4">Enabled</label> <label class="pure-u-1 pure-u-lg-1-4">Enabled</label>
<div class="pure-u-1 pure-u-lg-3-4"><input type="checkbox" name="schEnabled" /></div> <div class="pure-u-1 pure-u-lg-3-4"><input type="checkbox" name="schEnabled" /></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-1-2"></div>
<button class="pure-button button-del-schedule" type="button">Delete schedule</button> <button class="pure-button button-del-schedule" type="button">Delete schedule</button>
</div> </div>
</div> </div>
@ -1431,7 +1435,7 @@
<div id="relayTemplate" class="template"> <div id="relayTemplate" class="template">
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Switch #<span class="id"></span></label> <label class="pure-u-1 pure-u-lg-1-4">Switch #<span class="id"></span></label>
<input name="relay" type="checkbox" on="ON" off="OFF" />
<div><input name="relay" type="checkbox" on="ON" off="OFF" /></div>
</div> </div>
</div> </div>


+ 28
- 4
code/platformio.ini View File

@ -2040,22 +2040,22 @@ extra_scripts = ${common.extra_scripts}
[env:luani-hvio] [env:luani-hvio]
platform = ${common.platform} platform = ${common.platform}
framework = ${common.framework} framework = ${common.framework}
board = ${common.board_4m}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode} board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore} lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_4m1m} -DLUANI_HVIO
build_flags = ${common.build_flags_1m0m} -DLUANI_HVIO
monitor_speed = ${common.monitor_speed} monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts} extra_scripts = ${common.extra_scripts}
[env:luani-hvio-ota] [env:luani-hvio-ota]
platform = ${common.platform} platform = ${common.platform}
framework = ${common.framework} framework = ${common.framework}
board = ${common.board_4m}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode} board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore} lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_4m1m} -DLUANI_HVIO
build_flags = ${common.build_flags_1m0m} -DLUANI_HVIO
upload_speed = ${common.upload_speed} upload_speed = ${common.upload_speed}
upload_port = ${common.upload_port} upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags} upload_flags = ${common.upload_flags}
@ -2383,3 +2383,27 @@ monitor_speed = ${common.monitor_speed}
upload_port = ${common.upload_port} upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags} upload_flags = ${common.upload_flags}
extra_scripts = ${common.extra_scripts} extra_scripts = ${common.extra_scripts}
[env:generic-ag-l4]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DGENERIC_AG_L4
monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts}
[env:generic-ag-l4-ota]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DGENERIC_AG_L4
upload_speed = ${common.upload_speed}
upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags}
extra_scripts = ${common.extra_scripts}

BIN
images/devices/generic-ag-l4-1.jpg View File

Before After
Width: 960  |  Height: 1280  |  Size: 200 KiB

BIN
images/devices/generic-ag-l4-2.jpg View File

Before After
Width: 960  |  Height: 1280  |  Size: 234 KiB

BIN
images/devices/generic-ag-l4-3.jpg View File

Before After
Width: 960  |  Height: 1280  |  Size: 232 KiB

BIN
images/devices/generic-ag-l4-4.jpg View File

Before After
Width: 960  |  Height: 1280  |  Size: 166 KiB

BIN
images/devices/generic-ag-l4-5.jpg View File

Before After
Width: 1280  |  Height: 960  |  Size: 141 KiB

Loading…
Cancel
Save