Browse Source

Merge branch 'dev' into sensors

rfm69
Xose Pérez 6 years ago
parent
commit
2cc3060507
23 changed files with 3748 additions and 3797 deletions
  1. +9
    -0
      code/espurna/config/general.h
  2. BIN
      code/espurna/data/index.html.gz
  3. +3077
    -3249
      code/espurna/static/index.html.gz.h
  4. +19
    -0
      code/espurna/web.ino
  5. +10
    -0
      code/espurna/ws.ino
  6. +9
    -0
      code/extra_scripts.py
  7. +35
    -1
      code/gulpfile.js
  8. +63
    -1
      code/html/custom.css
  9. +110
    -37
      code/html/custom.js
  10. +44
    -8
      code/html/index.html
  11. +0
    -120
      code/html/vendor/checkboxes.css
  12. +0
    -366
      code/html/vendor/checkboxes.js
  13. BIN
      code/html/vendor/images/border-off.png
  14. BIN
      code/html/vendor/images/border-on.png
  15. BIN
      code/html/vendor/images/handle-center.png
  16. BIN
      code/html/vendor/images/handle-left.png
  17. BIN
      code/html/vendor/images/handle-right.png
  18. BIN
      code/html/vendor/images/label-off.png
  19. BIN
      code/html/vendor/images/label-on.png
  20. +13
    -9
      code/ota.py
  21. +171
    -4
      code/package-lock.json
  22. +1
    -0
      code/package.json
  23. +187
    -2
      code/platformio.ini

+ 9
- 0
code/espurna/config/general.h View File

@ -377,6 +377,15 @@
#define WEB_PORT 80 // HTTP port
#endif
// Defining a WEB_REMOTE_DOMAIN will enable Cross-Origin Resource Sharing (CORS)
// so you will be able to login to this device from another domain. This will allow
// you to manage all ESPurna devices in your local network from a unique installation
// of the web UI. This installation could be in a local server (a Raspberry Pi, for instance)
// or in the Internet. Since the WebUI is just one compressed file with HTML, CSS and JS
// there are no special requirements. Any static web server will do (NGinx, Apache, Lighttpd,...).
// The only requirement is that the resource must be available under this domain.
#define WEB_REMOTE_DOMAIN "http://tinkerman.cat"
// -----------------------------------------------------------------------------
// WEBSOCKETS
// -----------------------------------------------------------------------------


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


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


+ 19
- 0
code/espurna/web.ino View File

@ -40,6 +40,24 @@ void _onReset(AsyncWebServerRequest *request) {
request->send(200);
}
void _onDiscover(AsyncWebServerRequest *request) {
webLog(request);
AsyncResponseStream *response = request->beginResponseStream("text/json");
DynamicJsonBuffer jsonBuffer;
JsonObject &root = jsonBuffer.createObject();
root["app"] = APP_NAME;
root["version"] = APP_VERSION;
root["hostname"] = getSetting("hostname");
root["device"] = getBoardName();
root.printTo(*response);
request->send(response);
}
void _onGetConfig(AsyncWebServerRequest *request) {
webLog(request);
@ -338,6 +356,7 @@ void webSetup() {
_server->on("/config", HTTP_GET, _onGetConfig);
_server->on("/config", HTTP_POST | HTTP_PUT, _onPostConfig, _onPostConfigData);
_server->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeData);
_server->on("/discover", HTTP_GET, _onDiscover);
// Serve static files
#if SPIFFS_SUPPORT


+ 10
- 0
code/espurna/ws.ino View File

@ -489,12 +489,22 @@ void wsReload() {
}
void wsSetup() {
_ws.onEvent(_wsEvent);
webServer()->addHandler(&_ws);
// CORS
#ifdef WEB_REMOTE_DOMAIN
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", WEB_REMOTE_DOMAIN);
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true");
#endif
webServer()->on("/auth", HTTP_GET, _onAuth);
#if MQTT_SUPPORT
mqttRegister(_wsMQTTCallback);
#endif
wsOnSendRegister(_wsOnStart);
wsOnReceiveRegister(_wsOnReceive);
espurnaRegisterLoop(_wsLoop);


+ 9
- 0
code/extra_scripts.py View File

@ -1,5 +1,6 @@
#!/usr/bin/env python
from subprocess import call
from platformio import util
import os
import time
@ -58,6 +59,13 @@ def check_size(source, target, env):
# print clr(Color.LIGHT_RED, "File too large for OTA!")
# Exit(1)
def build_webui(source, target, env):
config = util.load_project_config()
kv = dict(config.items("env:" + env.get('PIOENV')))
if 'modules' in kv:
os.environ['WEBUI_MODULES'] = kv["modules"]
call(["gulp"])
# ------------------------------------------------------------------------------
# Hooks
# ------------------------------------------------------------------------------
@ -65,4 +73,5 @@ def check_size(source, target, env):
remove_float_support()
#env.AddPreAction("buildprog", cpp_check)
env.AddPreAction("$BUILD_DIR/src/espurna.ino.cpp.o", build_webui)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", check_size)

+ 35
- 1
code/gulpfile.js View File

@ -39,6 +39,7 @@ const log = require('fancy-log');
const csslint = require('gulp-csslint');
const crass = require('gulp-crass');
const replace = require('gulp-replace');
const remover = require('gulp-remove-code');
const dataFolder = 'espurna/data/';
const staticFolder = 'espurna/static/';
@ -106,6 +107,27 @@ gulp.task('buildfs_embeded', ['buildfs_inline'], function() {
});
gulp.task('buildfs_inline', function() {
var remover_config = {
sensor: false,
light: false,
rfbridge: false
};
var modules = 'WEBUI_MODULES' in process.env ? process.env.WEBUI_MODULES : false;
if (false === modules || "all" === modules) {
for (var i in remover_config) {
remover_config[i] = true;
}
} else {
var list = modules.split(' ');
for (var i in list) {
if (list[i] != "") {
remover_config[list[i]] = true;
}
}
}
log.info("Modules: " + JSON.stringify(remover_config));
return gulp.src('html/*.html').
pipe(htmllint({
'failOnError': true,
@ -117,10 +139,11 @@ gulp.task('buildfs_inline', function() {
pipe(favicon()).
pipe(inline({
base: 'html/',
js: [uglify],
js: [],
css: [crass, inlineImages],
disabledTypes: ['svg', 'img']
})).
pipe(remover(remover_config)).
pipe(htmlmin({
collapseWhitespace: true,
removeComments: true,
@ -133,4 +156,15 @@ gulp.task('buildfs_inline', function() {
});
gulp.task('test', function() {
return gulp.src('html/custom.js').
pipe(remover({
sensor: false,
light: false,
rfbridge: false
})).
pipe(gulp.dest('/home/xose/tmp/'));
});
gulp.task('default', ['buildfs_embeded']);

+ 63
- 1
code/html/custom.css View File

@ -147,8 +147,12 @@ div.state {
color: white;
letter-spacing: 0;
margin-bottom: 10px;
padding: 8px 8px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
text-transform: uppercase;
font-family: 'FreeSans','Arimo','Droid Sans','Helvetica','Arial','sans-serif';
font-size: .7em;
height: 37px;
padding: 8px 8px;
}
.main-buttons {
@ -221,6 +225,64 @@ span.slider {
margin-top: 7px;
}
/* -----------------------------------------------------------------------------
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;
}
.inner-container:first-child {
background: #e9e9e9;
color: #a9a9a9;
}
.inner-container:nth-child(2) {
background: #c00000;
color: white;
clip-path: inset(0 50% 0 0);
transition: .3s cubic-bezier(0,0,0,1);
}
.toggle {
width: 50%;
position: absolute;
height: inherit;
display: flex;
box-sizing: border-box;
}
.toggle p {
margin: auto;
}
.toggle:nth-child(1) {
right: 0;
}
/* -----------------------------------------------------------------------------
Loading
-------------------------------------------------------------------------- */


+ 110
- 37
code/html/custom.js View File

@ -34,6 +34,7 @@ function initMessages() {
messages[10] = "Session expired, please reload page...";
}
<!-- removeIf(!sensor)-->
function sensorName(id) {
var names = [
"DHT", "Dallas", "Emon Analog", "Emon ADC121", "Emon ADS1X15",
@ -73,6 +74,7 @@ function magnitudeError(error) {
}
return "Error " + error;
}
<!-- endRemoveIf(!sensor)-->
// -----------------------------------------------------------------------------
// Utils
@ -452,12 +454,8 @@ function doUpdate() {
// Empty special fields
$(".pwrExpected").val(0);
$("input[name='pwrResetCalibration']").
prop("checked", false).
iphoneStyle("refresh");
$("input[name='pwrResetE']").
prop("checked", false).
iphoneStyle("refresh");
$("input[name='pwrResetCalibration']").prop("checked", false);
$("input[name='pwrResetE']").prop("checked", false);
// Change handling
numChanged = 0;
@ -539,8 +537,7 @@ function doFactoryReset() {
return false;
}
function doToggle(element, value) {
var id = parseInt(element.attr("data"), 10);
function doToggle(id, value) {
sendAction("relay", {id: id, status: value ? 1 : 0 });
return false;
}
@ -584,10 +581,7 @@ function toggleMenu() {
function showPanel() {
$(".panel").hide();
if ($("#layout").hasClass("active")) { toggleMenu(); }
$("#" + $(this).attr("data")).show().
find("input[type='checkbox']").
iphoneStyle("calculateDimensions").
iphoneStyle("refresh");
$("#" + $(this).attr("data")).show();
}
// -----------------------------------------------------------------------------
@ -609,6 +603,7 @@ function createRelayList(data, container, template_name) {
}
<!-- removeIf(!sensor)-->
function createMagnitudeList(data, container, template_name) {
var current = $("#" + container + " > div").length;
@ -625,6 +620,7 @@ function createMagnitudeList(data, container, template_name) {
}
}
<!-- endRemoveIf(!sensor)-->
// -----------------------------------------------------------------------------
// Wifi
@ -666,6 +662,7 @@ function addNetwork() {
// -----------------------------------------------------------------------------
// Relays scheduler
// -----------------------------------------------------------------------------
function delSchedule() {
var parent = $(this).parents(".pure-g");
$(parent).remove();
@ -677,6 +674,7 @@ function moreSchedule() {
}
function addSchedule(event) {
var numSchedules = $("#schedules > div").length;
if (numSchedules >= maxSchedules) {
alert("Max number of schedules reached");
@ -699,13 +697,12 @@ function addSchedule(event) {
$(line).find(".button-del-schedule").on("click", delSchedule);
$(line).find(".button-more-schedule").on("click", moreSchedule);
line.appendTo("#schedules");
$(line).find("input[type='checkbox']").prop("checked", false);
$(line).find("input[type='checkbox']").
prop("checked", false).
iphoneStyle("calculateDimensions").
iphoneStyle("refresh");
initCheckboxes();
return line;
}
// -----------------------------------------------------------------------------
@ -723,15 +720,8 @@ function initRelays(data) {
// Add relay fields
var line = $(template).clone();
$(".id", line).html(i);
$("input", line).attr("data", i);
$(":checkbox", line).prop('checked', data[i]).attr("data", i);
line.appendTo("#relays");
$("input[type='checkbox']", line).iphoneStyle({
onChange: doToggle,
resizeContainer: true,
resizeHandle: true,
checkedLabel: "ON",
uncheckedLabel: "OFF"
});
// Populate the relay SELECTs
$("select.isrelay").append(
@ -739,6 +729,59 @@ function initRelays(data) {
}
}
function initCheckboxes() {
var setCheckbox = function(element, value) {
var container = $(".toggle-container", $(element));
if (value) {
container.css("clipPath", "inset(0 0 0 50%)");
container.css("backgroundColor", "#00c000");
} else {
container.css("clipPath", "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 = checkbox.prop('data');
doToggle(id, status);
}
});
}
function createCheckboxes() {
$("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();
});
}
@ -768,6 +811,7 @@ function initRelayConfig(data) {
// Sensors & Magnitudes
// -----------------------------------------------------------------------------
<!-- removeIf(!sensor)-->
function initMagnitudes(data) {
// check if already initialized
@ -786,11 +830,14 @@ function initMagnitudes(data) {
}
}
<!-- endRemoveIf(!sensor)-->
// -----------------------------------------------------------------------------
// Lights
// -----------------------------------------------------------------------------
<!-- removeIf(!light)-->
function initColorRGB() {
// check if already initialized
@ -909,11 +956,14 @@ function initChannels(num) {
}
}
<!-- endRemoveIf(!light)-->
// -----------------------------------------------------------------------------
// RFBridge
// -----------------------------------------------------------------------------
<!-- removeIf(!rfbridge)-->
function rfbLearn() {
var parent = $(this).parents(".pure-g");
var input = $("input", parent);
@ -952,6 +1002,7 @@ function addRfbNode() {
return line;
}
<!-- endRemoveIf(!rfbridge)-->
// -----------------------------------------------------------------------------
// Processing
@ -1000,6 +1051,8 @@ function processData(data) {
// RFBridge
// ---------------------------------------------------------------------
<!-- removeIf(!rfbridge)-->
if ("rfbCount" === key) {
for (i=0; i<data.rfbCount; i++) { addRfbNode(); }
return;
@ -1017,11 +1070,14 @@ function processData(data) {
}
return;
}
<!-- endRemoveIf(!rfbridge)-->
// ---------------------------------------------------------------------
// Lights
// ---------------------------------------------------------------------
<!-- removeIf(!light)-->
if ("rgb" === key) {
initColorRGB();
$("input[name='color']").wheelColorPicker("setValue", value, true);
@ -1072,10 +1128,14 @@ function processData(data) {
useCCT = value;
}
<!-- endRemoveIf(!light)-->
// ---------------------------------------------------------------------
// Sensors & Magnitudes
// ---------------------------------------------------------------------
<!-- removeIf(!sensor)-->
if ("magnitudes" === key) {
initMagnitudes(value);
for (i in value) {
@ -1091,6 +1151,8 @@ function processData(data) {
return;
}
<!-- endRemoveIf(!sensor)-->
// ---------------------------------------------------------------------
// WiFi
// ---------------------------------------------------------------------
@ -1142,9 +1204,7 @@ function processData(data) {
var sch_value = schedule[key];
$("input[name='" + key + "']", sch_line).val(sch_value);
$("select[name='" + key + "']", sch_line).prop("value", sch_value);
$("input[type='checkbox'][name='" + key + "']", sch_line).
prop("checked", sch_value).
iphoneStyle("refresh");
$("input[type='checkbox'][name='" + key + "']", sch_line).prop("checked", sch_value);
});
}
return;
@ -1157,12 +1217,7 @@ function processData(data) {
if ("relayStatus" === key) {
initRelays(value);
for (i in value) {
// Set the status for each relay
$("input.relayStatus[data='" + i + "']").
prop("checked", value[i]).
iphoneStyle("refresh");
$("input[name='relay'][data='" + i + "']").prop("checked", value[i]);
}
return;
}
@ -1184,10 +1239,12 @@ function processData(data) {
}
// Domoticz - Magnitudes
<!-- removeIf(!sensor)-->
if ("dczMagnitudes" === key) {
createMagnitudeList(value, "dczMagnitudes", "dczMagnitudeTemplate");
return;
}
<!-- endRemoveIf(!sensor)-->
// ---------------------------------------------------------------------
// Thingspeak
@ -1200,10 +1257,12 @@ function processData(data) {
}
// Thingspeak - Magnitudes
<!-- removeIf(!sensor)-->
if ("tspkMagnitudes" === key) {
createMagnitudeList(value, "tspkMagnitudes", "tspkMagnitudeTemplate");
return;
}
<!-- endRemoveIf(!sensor)-->
// ---------------------------------------------------------------------
// General
@ -1273,9 +1332,7 @@ function processData(data) {
var input = $("input[name='" + key + "']");
if (input.length > 0) {
if (input.attr("type") === "checkbox") {
input.
prop("checked", value).
iphoneStyle("refresh");
input.prop("checked", value);
} else if (input.attr("type") === "radio") {
input.val([value]);
} else {
@ -1307,6 +1364,7 @@ function processData(data) {
}
resetOriginals();
initCheckboxes();
}
@ -1374,6 +1432,7 @@ function connectToURL(url) {
$.ajax({
'method': 'GET',
'crossDomain': true,
'url': urls.auth.href,
'xhrFields': { 'withCredentials': true }
}).done(function(data) {
@ -1402,10 +1461,16 @@ function connectToCurrentURL() {
connectToURL(new URL(window.location));
}
function getParameterByName(name) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
$(function() {
initMessages();
loadTimeZones();
createCheckboxes();
setInterval(function() { keepTime(); }, 1000);
$("#menuLink").on("click", toggleMenu);
@ -1440,13 +1505,21 @@ $(function() {
$(".more", addNetwork()).toggle();
});
$(".button-add-switch-schedule").on("click", { schType: 1 }, addSchedule);
<!-- removeIf(!light)-->
$(".button-add-light-schedule").on("click", { schType: 2 }, addSchedule);
<!-- endRemoveIf(!light)-->
$(document).on("change", "input", hasChanged);
$(document).on("change", "select", hasChanged);
// don't autoconnect when opening from filesystem
if (window.location.protocol === "file:") { return; }
connectToCurrentURL();
// Check host param in query string
if (host = getParameterByName('host')) {
connect(host);
} else {
connectToCurrentURL();
}
});

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

@ -11,8 +11,9 @@
<link rel="stylesheet" href="vendor/pure-1.0.0.min.css" />
<link rel="stylesheet" href="vendor/pure-grids-responsive-1.0.0.min.css" />
<link rel="stylesheet" href="vendor/side-menu.css" />
<link rel="stylesheet" href="vendor/checkboxes.css" />
<!-- removeIf(!light)-->
<link rel="stylesheet" href="vendor/jquery.wheelcolorpicker-3.0.3.css" />
<!-- endRemoveIf(!light)-->
<link rel="stylesheet" href="custom.css" />
<!-- endbuild -->
@ -98,9 +99,11 @@
<a href="#" class="pure-menu-link" data="panel-idb">INFLUXDB</a>
</li>
<!-- removeIf(!light)-->
<li class="pure-menu-item module module-color">
<a href="#" class="pure-menu-link" data="panel-color">LIGHTS</a>
</li>
<!-- endRemoveIf(!light)-->
<li class="pure-menu-item module module-mqtt">
<a href="#" class="pure-menu-link" data="panel-mqtt">MQTT</a>
@ -110,17 +113,21 @@
<a href="#" class="pure-menu-link" data="panel-ntp">NTP</a>
</li>
<!-- removeIf(!rfbridge)-->
<li class="pure-menu-item module module-rfb">
<a href="#" class="pure-menu-link" data="panel-rfb">RF</a>
</li>
<!-- endRemoveIf(!rfbridge)-->
<li class="pure-menu-item module module-sch">
<a href="#" class="pure-menu-link" data="panel-schedule">SCHEDULE</a>
</li>
<!-- removeIf(!sensor)-->
<li class="pure-menu-item module module-sensors">
<a href="#" class="pure-menu-link" data="panel-sensors">SENSORS</a>
</li>
<!-- endRemoveIf(!sensor)-->
<li class="pure-menu-item module module-relay">
<a href="#" class="pure-menu-link" data="panel-relay">SWITCHES</a>
@ -177,13 +184,15 @@
<div id="relays"></div>
<!-- removeIf(!light)-->
<div id="colors"></div>
<div id="cct"></div>
<div id="channels"></div>
<!-- endRemoveIf(!light)-->
<!-- removeIf(!sensor)-->
<div id="magnitudes"></div>
<!-- endRemoveIf(!sensor)-->
<div class="pure-u-1 pure-u-lg-1-2 state">
@ -343,7 +352,7 @@
<div class="pure-g module module-alexa">
<label class="pure-u-1 pure-u-lg-1-4">Alexa integration</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="alexaEnabled" tabindex="13" /></div>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="alexaEnabled" /></div>
</div>
</fieldset>
@ -382,6 +391,7 @@
</div>
</div>
<!-- removeIf(!light)-->
<div class="panel" id="panel-color">
<div class="header">
@ -467,6 +477,7 @@
</fieldset>
</div>
</div>
<!-- endRemoveIf(!light)-->
<div class="panel" id="panel-admin">
@ -638,7 +649,9 @@
<div id="schedules"></div>
<button type="button" class="pure-button button-add-switch-schedule module module-relay">Add switch schedule</button>
<!-- removeIf(!light)-->
<button type="button" class="pure-button button-add-light-schedule module module-color">Add channel schedule</button>
<!-- endRemoveIf(!light)-->
</fieldset>
@ -735,11 +748,13 @@
<div class="pure-u-1 pure-u-lg-3-4 hint">
This is the root topic for this device. The {hostname} and {mac} placeholders will be replaced by the device hostname and MAC address.<br />
- <strong>&lt;root&gt;/relay/#/set</strong> Send a 0 or a 1 as a payload to this topic to switch it on or off. You can also send a 2 to toggle its current state. Replace # with the switch ID (starting from 0). If the board has only one switch it will be 0.<br />
<!-- removeIf(!light)-->
<span class="module module-color">- <strong>&lt;root&gt;/rgb/set</strong> Set the color using this topic, your can either send an "#RRGGBB" value or "RRR,GGG,BBB" (0-255 each).<br /></span>
<span class="module module-color">- <strong>&lt;root&gt;/hsv/set</strong> Set the color using hue (0-360), saturation (0-100) and value (0-100) values, comma separated.<br /></span>
<span class="module module-color">- <strong>&lt;root&gt;/brightness/set</strong> Set the brighness (0-255).<br /></span>
<span class="module module-color">- <strong>&lt;root&gt;/channel/#/set</strong> Set the value for a single color channel (0-255). Replace # with the channel ID (starting from 0 and up to 4 for RGBWC lights).<br /></span>
<span class="module module-color">- <strong>&lt;root&gt;/mired/set</strong> Set the temperature color in mired.<br /></span>
<!-- endRemoveIf(!light)-->
- <strong>&lt;root&gt;/status</strong> The device will report a 1 to this topic every few minutes. Upon MQTT disconnecting this will be set to 0.<br />
- Other values reported (depending on the build) are: <strong>firmware</strong> and <strong>version</strong>, <strong>hostname</strong>, <strong>IP</strong>, <strong>MAC</strong>, signal strenth (<strong>RSSI</strong>), <strong>uptime</strong> (in seconds), <strong>free heap</strong> and <strong>power supply</strong>.
</div>
@ -844,7 +859,9 @@
<div id="dczRelays"></div>
<!-- removeIf(!sensor)-->
<div id="dczMagnitudes"></div>
<!-- endRemoveIf(!sensor)-->
</fieldset>
</div>
@ -872,7 +889,9 @@
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">
Home Assistant auto-discovery feature. Enable and save to add the device to your HA console.
<!-- removeIf(!light)-->
When using a colour light you might want to disable CSS style so Home Assistant can parse the color.
<!-- endRemoveIf(!light)-->
</div>
</div>
@ -903,8 +922,6 @@
</div>
<div class="panel" id="panel-thingspeak">
<div class="header">
@ -938,7 +955,9 @@
<div id="tspkRelays"></div>
<!-- removeIf(!sensor)-->
<div id="tspkMagnitudes"></div>
<!-- endRemoveIf(!sensor)-->
</fieldset>
</div>
@ -1025,6 +1044,7 @@
</div>
<!-- removeIf(!sensor)-->
<div class="panel" id="panel-sensors">
<div class="header">
@ -1161,7 +1181,9 @@
</div>
</div>
<!-- endRemoveIf(!sensor)-->
<!-- removeIf(!rfbridge)-->
<div class="panel" id="panel-rfb">
<div class="header">
@ -1182,6 +1204,7 @@
</fieldset>
</div>
</div>
<!-- endRemoveIf(!rfbridge)-->
</form>
@ -1191,6 +1214,7 @@
<!-- Templates -->
<!-- removeIf(!rfbridge)-->
<div id="rfbNodeTemplate" class="template">
<legend>Switch #<span></span></legend>
@ -1212,6 +1236,7 @@
</div>
</div>
<!-- endRemoveIf(!rfbridge)-->
<div id="networkTemplate" class="template">
@ -1299,6 +1324,7 @@
<input type="hidden" name="schType" value="1">
</div>
<!-- removeIf(!light)-->
<div id="lightActionTemplate" class="template">
<label class="pure-u-1 pure-u-lg-1-4">Brightness</label>
<div class="pure-u-1 pure-u-lg-1-5">
@ -1307,11 +1333,12 @@
<select class="pure-u-1 pure-u-lg-1-5 islight" name="schSwitch"></select>
<input type="hidden" name="schType" value="2">
</div>
<!-- endRemoveIf(!light)-->
<div id="relayTemplate" class="template">
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Switch #<span class="id"></span></label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" class="relayStatus pure-u-1 pure-u-lg-1-4" data="0" /></div>
<input name="relay" type="checkbox" on="ON" off="OFF" />
</div>
</div>
@ -1366,6 +1393,7 @@
</div>
</div>
<!-- removeIf(!sensor)-->
<div id="dczMagnitudeTemplate" class="template">
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Magnitude</label>
@ -1373,6 +1401,7 @@
<div class="pure-u-1 pure-u-lg-1-2 hint center"></div>
</div>
</div>
<!-- endRemoveIf(!sensor)-->
<div id="tspkRelayTemplate" class="template">
<div class="pure-g">
@ -1381,6 +1410,7 @@
</div>
</div>
<!-- removeIf(!sensor)-->
<div id="tspkMagnitudeTemplate" class="template">
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Magnitude</label>
@ -1388,7 +1418,9 @@
<div class="pure-u-1 pure-u-lg-1-2 hint center"></div>
</div>
</div>
<!-- endRemoveIf(!sensor)-->
<!-- removeIf(!light)-->
<div id="colorRGBTemplate" class="template">
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Color</label>
@ -1424,7 +1456,9 @@
<span class="slider mireds pure-u-lg-1-4"></span>
</div>
</div>
<!-- endRemoveIf(!light)-->
<!-- removeIf(!sensor)-->
<div id="magnitudeTemplate" class="template">
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4"></label>
@ -1434,6 +1468,7 @@
<div class="pure-u-1 pure-u-lg-1-2 hint center"></div>
</div>
</div>
<!-- endRemoveIf(!sensor)-->
<iframe id="downloader"></iframe>
<input id="uploader" type="file" />
@ -1443,8 +1478,9 @@
<!-- build:js script.js -->
<script src="vendor/jquery-3.2.1.min.js"></script>
<script src="custom.js"></script>
<script src="vendor/checkboxes.js"></script>
<!-- removeIf(!light)-->
<script src="vendor/jquery.wheelcolorpicker-3.0.3.min.js"></script>
<!-- endRemoveIf(!light)-->
<!-- endbuild -->
</html>

+ 0
- 120
code/html/vendor/checkboxes.css View File

@ -1,120 +0,0 @@
.iPhoneCheckContainer {
-webkit-transform:translate3d(0,0,0);
position: relative;
height: 30px;
cursor: pointer;
overflow: hidden;
margin: 5px 0 10px 0;
}
.iPhoneCheckContainer input {
position: absolute;
top: 5px;
left: 30px;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
opacity: 0;
}
.iPhoneCheckContainer label {
white-space: nowrap;
font-size: 17px;
line-height: 17px;
font-weight: bold;
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
cursor: pointer;
display: block;
height: 27px;
position: absolute;
width: auto;
top: 0;
padding-top: 5px;
overflow: hidden;
}
.iPhoneCheckContainer, .iPhoneCheckContainer label {
user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
}
.iPhoneCheckDisabled {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);
opacity: 0.5;
}
div.iPhoneCheckBorderOn {
position: absolute;
left: 0;
width: 4px;
height: 100%;
background-image: url('images/border-on.png');
background-repeat: no-repeat;
}
label.iPhoneCheckLabelOn {
color: white;
background-image: url('images/label-on.png');
background-repeat: repeat-x;
text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.6);
left: 0;
padding-top: 5px;
margin-left: 4px;
margin-top: 0px;
}
label.iPhoneCheckLabelOn span {
padding-left: 4px;
}
label.iPhoneCheckLabelOff {
color: #8b8b8b;
background-image: url('images/label-off.png');
background-repeat: repeat-x;
text-shadow: 0px 0px 2px rgba(255, 255, 255, 0.6);
text-align: right;
margin-right: 4px;
margin-top: 0px;
right: 0;
}
label.iPhoneCheckLabelOff span {
padding-right: 4px;
}
div.iPhoneCheckBorderOff {
position: absolute;
width: 4px;
height: 100%;
background-image: url('images/border-off.png');
background-repeat: no-repeat;
}
.iPhoneCheckHandle {
display: block;
height: 27px;
cursor: pointer;
position: absolute;
top: 0;
left: 0;
width: 0;
background-image: url('images/handle-left.png');
background-repeat: no-repeat;
padding-left: 4px;
}
.iPhoneCheckHandleCenter {
position: absolute;
width: 100%;
height: 100%;
background-image: url('images/handle-center.png');
background-repeat: repeat-x;
}
.iPhoneCheckHandleRight {
position: absolute;
height: 100%;
width: 4px;
right: 0px;
background-image: url('images/handle-right.png');
background-repeat: no-repeat;
}

+ 0
- 366
code/html/vendor/checkboxes.js View File

@ -1,366 +0,0 @@
// Generated by CoffeeScript 1.6.2
/*eslint quotes: ["error", "double"]*/
/*eslint-env es6*/
(function() {
var iOSCheckbox, matched, userAgent,
__slice = [].slice;
if ($.browser == null) {
userAgent = navigator.userAgent || "";
jQuery.uaMatch = function(ua) {
var match;
ua = ua.toLowerCase();
match = /(chrome)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version)?[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+))?/.exec(ua) || [];
return {
browser: match[1] || "",
version: match[2] || "0"
};
};
matched = jQuery.uaMatch(userAgent);
jQuery.browser = {};
if (matched.browser) {
jQuery.browser[matched.browser] = true;
jQuery.browser.version = matched.version;
}
if (jQuery.browser.webkit) {
jQuery.browser.safari = true;
}
}
iOSCheckbox = (function() {
function iOSCheckbox(elem, options) {
var key, opts, value;
this.elem = $(elem);
opts = $.extend({}, iOSCheckbox.defaults, options);
for (key in opts) {
if ({}.hasOwnProperty.call(opts, key)) {
value = opts[key];
this[key] = value;
}
}
this.elem.data(this.dataName, this);
this.wrapCheckboxWithDivs();
this.attachEvents();
this.disableTextSelection();
this.calculateDimensions();
}
iOSCheckbox.prototype.calculateDimensions = function() {
if (this.resizeHandle) {
this.optionallyResize("handle");
}
if (this.resizeContainer) {
this.optionallyResize("container");
}
return this.initialPosition();
};
iOSCheckbox.prototype.isDisabled = function() {
return this.elem.is(":disabled");
};
iOSCheckbox.prototype.wrapCheckboxWithDivs = function() {
this.elem.wrap("<div class='" + this.containerClass + "' />");
this.container = this.elem.parent();
this.offLabel = $("<label class='" + this.labelOffClass + "'>\n <span>" + this.uncheckedLabel + "</span>\n</label>").appendTo(this.container);
this.offSpan = this.offLabel.children("span");
this.onLabel = $("<label class='" + this.labelOnClass + "'>\n <span>" + this.checkedLabel + "</span>\n</label>").appendTo(this.container);
this.onBorder = $("<div class='iPhoneCheckBorderOn'</div>").appendTo(this.container);
this.offBorder = $("<div class='iPhoneCheckBorderOff'</div>").appendTo(this.container);
this.onSpan = this.onLabel.children("span");
this.handle = $("<div class='" + this.handleClass + "'></div>").appendTo(this.container);
this.handleCenter = $("<div class='" + this.handleCenterClass + "'></div>").appendTo(this.handle);
this.handleRight = $("<div class='" + this.handleRightClass + "'></div>").appendTo(this.handle);
return true;
};
iOSCheckbox.prototype.disableTextSelection = function() {
if ($.browser.msie) {
return $([this.handle, this.offLabel, this.onLabel, this.container]).attr("unselectable", "on");
}
};
iOSCheckbox.prototype._getDimension = function(elem, dimension) {
if ($.fn.actual != null) {
return elem.actual(dimension);
} else {
return elem[dimension]();
}
};
iOSCheckbox.prototype.optionallyResize = function(mode) {
var newWidth, offLabelWidth, offSpan, onLabelWidth, onSpan;
onSpan = this.onLabel.find("span");
onLabelWidth = this._getDimension(onSpan, "width");
onLabelWidth += parseInt(onSpan.css("padding-left"), 10);
offSpan = this.offLabel.find("span");
offLabelWidth = this._getDimension(offSpan, "width");
offLabelWidth += parseInt(offSpan.css("padding-right"), 10);
if (mode === "container") {
newWidth = onLabelWidth > offLabelWidth ? onLabelWidth : offLabelWidth;
newWidth += this._getDimension(this.handle, "width") + this.handleMargin;
return this.container.css({
width: newWidth
});
} else {
newWidth = onLabelWidth > offLabelWidth ? onLabelWidth : offLabelWidth;
this.handleCenter.css({
width: newWidth + 4
});
return this.handle.css({
width: newWidth + 7
});
}
};
iOSCheckbox.prototype.onMouseDown = function(event) {
var x;
event.preventDefault();
if (this.isDisabled()) {
return;
}
x = event.pageX || event.originalEvent.changedTouches[0].pageX;
iOSCheckbox.currentlyClicking = this.handle;
iOSCheckbox.dragStartPosition = x;
return iOSCheckbox.handleLeftOffset = parseInt(this.handle.css("left"), 10) || 0;
};
iOSCheckbox.prototype.onDragMove = function(event, x) {
var newWidth, p;
if (iOSCheckbox.currentlyClicking !== this.handle) {
return;
}
p = (x + iOSCheckbox.handleLeftOffset - iOSCheckbox.dragStartPosition) / this.rightSide;
if (p < 0) {
p = 0;
}
if (p > 1) {
p = 1;
}
newWidth = p * this.rightSide;
this.handle.css({
left: newWidth
});
this.onLabel.css({
width: newWidth + this.handleRadius
});
this.offSpan.css({
marginRight: -newWidth
});
return this.onSpan.css({
marginLeft: -(1 - p) * this.rightSide
});
};
iOSCheckbox.prototype.onDragEnd = function(event, x) {
var p;
if (iOSCheckbox.currentlyClicking !== this.handle) {
return;
}
if (this.isDisabled()) {
return;
}
if (iOSCheckbox.dragging) {
p = (x - iOSCheckbox.dragStartPosition) / this.rightSide;
this.elem.prop("checked", p >= 0.5).change();
} else {
this.elem.prop("checked", !this.elem.prop("checked")).change();
}
iOSCheckbox.currentlyClicking = null;
iOSCheckbox.dragging = null;
if (typeof this.onChange === "function") {
this.onChange(this.elem, this.elem.prop("checked"));
}
return this.didChange();
};
iOSCheckbox.prototype.refresh = function() {
return this.didChange();
};
iOSCheckbox.prototype.didChange = function() {
var newLeft;
if (this.isDisabled()) {
this.container.addClass(this.disabledClass);
return false;
} else {
this.container.removeClass(this.disabledClass);
}
newLeft = this.elem.prop("checked") ? this.rightSide + 2 : 0;
this.handle.animate({
left: newLeft
}, this.duration);
this.onLabel.animate({
width: newLeft + this.handleRadius
}, this.duration);
this.offSpan.animate({
marginRight: - newLeft
}, this.duration);
return this.onSpan.animate({
marginLeft: newLeft - this.rightSide
}, this.duration);
};
iOSCheckbox.prototype.attachEvents = function() {
var localMouseMove, localMouseUp, self;
self = this;
localMouseMove = function(event) {
return self.onGlobalMove.apply(self, arguments);
};
localMouseUp = function(event) {
self.onGlobalUp.apply(self, arguments);
$(document).unbind("mousemove touchmove", localMouseMove);
return $(document).unbind("mouseup touchend", localMouseUp);
};
this.elem.change(function() {
return self.refresh();
});
return this.container.bind("mousedown touchstart", function(event) {
self.onMouseDown.apply(self, arguments);
$(document).bind("mousemove touchmove", localMouseMove);
return $(document).bind("mouseup touchend", localMouseUp);
});
};
iOSCheckbox.prototype.initialPosition = function() {
var containerWidth, offset;
containerWidth = this._getDimension(this.container, "width");
this.offLabel.css({
width: containerWidth - this.containerRadius - 4
});
this.offBorder.css({
left: containerWidth - 4
});
offset = this.containerRadius + 1;
if ($.browser.msie && $.browser.version < 7) {
offset -= 3;
}
this.rightSide = containerWidth - this._getDimension(this.handle, "width") - offset;
if (this.elem.is(":checked")) {
this.handle.css({
left: this.rightSide
});
this.onLabel.css({
width: this.rightSide + this.handleRadius
});
this.offSpan.css({
marginRight: -this.rightSide,
});
} else {
this.onLabel.css({
width: 0
});
this.onSpan.css({
marginLeft: -this.rightSide
});
}
if (this.isDisabled()) {
return this.container.addClass(this.disabledClass);
}
};
iOSCheckbox.prototype.onGlobalMove = function(event) {
var x;
if (!(!this.isDisabled() && iOSCheckbox.currentlyClicking)) {
return;
}
event.preventDefault();
x = event.pageX || event.originalEvent.changedTouches[0].pageX;
if (!iOSCheckbox.dragging && (Math.abs(iOSCheckbox.dragStartPosition - x) > this.dragThreshold)) {
iOSCheckbox.dragging = true;
}
return this.onDragMove(event, x);
};
iOSCheckbox.prototype.onGlobalUp = function(event) {
var x;
if (!iOSCheckbox.currentlyClicking) {
return;
}
event.preventDefault();
x = event.pageX || event.originalEvent.changedTouches[0].pageX;
this.onDragEnd(event, x);
return false;
};
iOSCheckbox.defaults = {
duration: 200,
checkedLabel: "ON",
uncheckedLabel: "OFF",
resizeHandle: true,
resizeContainer: true,
disabledClass: "iPhoneCheckDisabled",
containerClass: "iPhoneCheckContainer",
labelOnClass: "iPhoneCheckLabelOn",
labelOffClass: "iPhoneCheckLabelOff",
handleClass: "iPhoneCheckHandle",
handleCenterClass: "iPhoneCheckHandleCenter",
handleRightClass: "iPhoneCheckHandleRight",
dragThreshold: 5,
handleMargin: 15,
handleRadius: 4,
containerRadius: 5,
dataName: "iphoneStyle",
onChange: function() {}
};
return iOSCheckbox;
})();
$.iphoneStyle = this.iOSCheckbox = iOSCheckbox;
$.fn.iphoneStyle = function() {
var args, checkbox, dataName, existingControl, method, params, _i, _len, _ref, _ref1, _ref2, _ref3;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
dataName = (_ref = (_ref1 = args[0]) != null ? _ref1.dataName : void 0) != null ? _ref : iOSCheckbox.defaults.dataName;
_ref2 = this.filter(":checkbox");
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
checkbox = _ref2[_i];
existingControl = $(checkbox).data(dataName);
if (existingControl != null) {
method = args[0], params = 2 <= args.length ? __slice.call(args, 1) : [];
if ((_ref3 = existingControl[method]) != null) {
_ref3.apply(existingControl, params);
}
} else {
new iOSCheckbox(checkbox, args[0]);
}
}
return this;
};
$.fn.iOSCheckbox = function(options) {
var opts;
if (options == null) {
options = {};
}
opts = $.extend({}, options, {
resizeHandle: false,
disabledClass: "iOSCheckDisabled",
containerClass: "iOSCheckContainer",
labelOnClass: "iOSCheckLabelOn",
labelOffClass: "iOSCheckLabelOff",
handleClass: "iOSCheckHandle",
handleCenterClass: "iOSCheckHandleCenter",
handleRightClass: "iOSCheckHandleRight",
dataName: "iOSCheckbox"
});
return this.iphoneStyle(opts);
};
}).call(this);

BIN
code/html/vendor/images/border-off.png View File

Before After
Width: 4  |  Height: 27  |  Size: 308 B

BIN
code/html/vendor/images/border-on.png View File

Before After
Width: 4  |  Height: 27  |  Size: 302 B

BIN
code/html/vendor/images/handle-center.png View File

Before After
Width: 4  |  Height: 27  |  Size: 190 B

BIN
code/html/vendor/images/handle-left.png View File

Before After
Width: 4  |  Height: 27  |  Size: 264 B

BIN
code/html/vendor/images/handle-right.png View File

Before After
Width: 4  |  Height: 27  |  Size: 264 B

BIN
code/html/vendor/images/label-off.png View File

Before After
Width: 4  |  Height: 27  |  Size: 211 B

BIN
code/html/vendor/images/label-on.png View File

Before After
Width: 4  |  Height: 27  |  Size: 214 B

+ 13
- 9
code/ota.py View File

@ -121,7 +121,7 @@ def get_empty_board():
"""
Returns the empty structure of a board to flash
"""
board = {'board': '', 'ip': '', 'size': 0, 'auth': '', 'flags': ''}
board = {'board': '', 'ip': '', 'size': 0, 'auth': '', 'flags': '', 'modules': ''}
return board
def get_board_by_index(index):
@ -230,8 +230,8 @@ def store(device, env):
def run(device, env):
print("Building and flashing image over-the-air...")
command = "export ESPURNA_IP=\"%s\"; export ESPURNA_BOARD=\"%s\"; export ESPURNA_AUTH=\"%s\"; export ESPURNA_FLAGS=\"%s\"; platformio run --silent --environment %s -t upload"
command = command % (device['ip'], device['board'], device['auth'], device['flags'], env)
command = "ESPURNA_IP=\"%s\" ESPURNA_BOARD=\"%s\" ESPURNA_AUTH=\"%s\" ESPURNA_FLAGS=\"%s\" WEBUI_MODULES=\"%s\" platformio run --environment %s -t upload"
command = command % (device['ip'], device['board'], device['auth'], device['flags'], device['modules'], env)
subprocess.check_call(command, shell=True)
store(device, env)
@ -245,6 +245,7 @@ if __name__ == '__main__':
parser.add_argument("-f", "--flash", help="flash device", default=0, action='count')
parser.add_argument("-o", "--flags", help="extra flags", default='')
parser.add_argument("-p", "--password", help="auth password", default='')
parser.add_argument("-m", "--modules", help="webui modules", default='')
parser.add_argument("-s", "--sort", help="sort devices list by field", default='hostname')
parser.add_argument("-y", "--yes", help="do not ask for confirmation", default=0, action='count')
parser.add_argument("hostnames", nargs='*', help="Hostnames to update")
@ -288,6 +289,7 @@ if __name__ == '__main__':
if board:
board['auth'] = args.password
board['flags'] = args.flags
board['modules'] = args.modules
queue.append(board)
# If no boards ask the user
@ -296,6 +298,7 @@ if __name__ == '__main__':
if board:
board['auth'] = args.password or input("Authorization key of the device to flash: ")
board['flags'] = args.flags or input("Extra flags for the build: ")
board['modules'] = args.modules or input("WebUI modules to build: ")
queue.append(board)
# If still no boards quit
@ -313,12 +316,13 @@ if __name__ == '__main__':
# Summary
print()
print("HOST = %s" % boardname(board))
print("IP = %s" % board['ip'])
print("BOARD = %s" % board['board'])
print("AUTH = %s" % board['auth'])
print("FLAGS = %s" % board['flags'])
print("ENV = %s" % env)
print("HOST = %s" % boardname(board))
print("IP = %s" % board['ip'])
print("BOARD = %s" % board['board'])
print("AUTH = %s" % board['auth'])
print("FLAGS = %s" % board['flags'])
print("MODULES = %s" % board['modules'])
print("ENV = %s" % env)
response = True
if args.yes == 0:


+ 171
- 4
code/package-lock.json View File

@ -841,6 +841,16 @@
"clone": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz"
}
},
"define-properties": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
"integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
"dev": true,
"requires": {
"foreach": "2.0.5",
"object-keys": "1.0.11"
}
},
"define-property": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
@ -981,6 +991,30 @@
"integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=",
"dev": true
},
"es-abstract": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
"integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
"dev": true,
"requires": {
"es-to-primitive": "1.1.1",
"function-bind": "1.1.1",
"has": "1.0.3",
"is-callable": "1.1.3",
"is-regex": "1.0.4"
}
},
"es-to-primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
"integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
"dev": true,
"requires": {
"is-callable": "1.1.3",
"is-date-object": "1.0.1",
"is-symbol": "1.0.1"
}
},
"escape-string-regexp": {
"version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
@ -1273,6 +1307,12 @@
"for-in": "1.0.2"
}
},
"foreach": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
"integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
"dev": true
},
"forever-agent": {
"version": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
@ -1307,6 +1347,12 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"gaze": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz",
@ -1579,7 +1625,7 @@
},
"gulp-csslint": {
"version": "https://registry.npmjs.org/gulp-csslint/-/gulp-csslint-1.0.1.tgz",
"integrity": "sha1-ESqQj3rvmO/Ce3vQCAHxOne+y5M=",
"integrity": "sha512-Rec56+RpCGg7feK3d/S45oqgxyLV3end0ed+UjWFv6YziQae2Bp4DNSDobwEvJdfCAsOhOSExEEB+jcfMx430w==",
"dev": true,
"requires": {
"csslint": "https://registry.npmjs.org/csslint/-/csslint-1.0.5.tgz",
@ -1602,7 +1648,7 @@
},
"plugin-error": {
"version": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
"integrity": "sha1-dwFr2JGdCsN3/c3QMiMolTyleBw=",
"integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==",
"dev": true,
"requires": {
"ansi-colors": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
@ -1658,8 +1704,9 @@
}
},
"gulp-htmllint": {
"version": "https://registry.npmjs.org/gulp-htmllint/-/gulp-htmllint-0.0.14.tgz",
"integrity": "sha1-fobrpgVmjfsVstLVAZX+VqdVhaI=",
"version": "0.0.14",
"resolved": "https://registry.npmjs.org/gulp-htmllint/-/gulp-htmllint-0.0.14.tgz",
"integrity": "sha512-D5FfR2G4SUwxPd/ypE5glZSXXddl3bnGz9JlY3gS5e8AXSvYJco+baIWtpT8fq5aqyr5IELf2I+VgCe6b+yq1A==",
"dev": true,
"requires": {
"gulp-util": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz",
@ -1720,6 +1767,60 @@
"through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz"
}
},
"gulp-remove-code": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gulp-remove-code/-/gulp-remove-code-3.0.2.tgz",
"integrity": "sha512-Ptrwce+Nq8B+zPMomMzjfkEpldBpbHDOtE4Py0jyxh3eE7scfzEjxAs0XojwFw4xBkLWuNdqetZhPk7zsuyxAw==",
"dev": true,
"requires": {
"bufferstreams": "2.0.1",
"escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"object.entries": "1.0.4",
"plugin-error": "1.0.1",
"through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz"
},
"dependencies": {
"bufferstreams": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-2.0.1.tgz",
"integrity": "sha512-ZswyIoBfFb3cVDsnZLLj2IDJ/0ppYdil/v2EGlZXvoefO689FokEmFEldhN5dV7R2QBxFneqTJOMIpfqhj+n0g==",
"dev": true,
"requires": {
"readable-stream": "2.3.6"
}
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
"core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"isarray": "1.0.0",
"process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"string_decoder": "1.1.1",
"util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz"
}
}
}
},
"gulp-replace": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz",
@ -1847,6 +1948,15 @@
"har-schema": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz"
}
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "1.1.1"
}
},
"has-ansi": {
"version": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
@ -2103,6 +2213,12 @@
"integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=",
"dev": true
},
"is-callable": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
"integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
"dev": true
},
"is-data-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
@ -2112,6 +2228,12 @@
"kind-of": "6.0.2"
}
},
"is-date-object": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
"integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
"dev": true
},
"is-descriptor": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
@ -2218,6 +2340,15 @@
"isobject": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz"
}
},
"is-regex": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
"integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
"dev": true,
"requires": {
"has": "1.0.3"
}
},
"is-relative": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
@ -2227,6 +2358,12 @@
"is-unc-path": "1.0.0"
}
},
"is-symbol": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
"integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=",
"dev": true
},
"is-typedarray": {
"version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
@ -2846,6 +2983,12 @@
}
}
},
"object-keys": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
"integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=",
"dev": true
},
"object-visit": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
@ -2867,6 +3010,18 @@
"isobject": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz"
}
},
"object.entries": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz",
"integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=",
"dev": true,
"requires": {
"define-properties": "1.1.2",
"es-abstract": "1.12.0",
"function-bind": "1.1.1",
"has": "1.0.3"
}
},
"object.map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
@ -3038,6 +3193,18 @@
"pinkie": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz"
}
},
"plugin-error": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
"integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==",
"dev": true,
"requires": {
"ansi-colors": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
"arr-diff": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
"arr-union": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
"extend-shallow": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz"
}
},
"posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",


+ 1
- 0
code/package.json View File

@ -17,6 +17,7 @@
"gulp-htmllint": "0.0.14",
"gulp-htmlmin": "^2.0.0",
"gulp-inline": "^0.1.1",
"gulp-remove-code": "^3.0.2",
"gulp-replace": "^1.0.0",
"gulp-uglify": "^1.5.3"
}


+ 187
- 2
code/platformio.ini
File diff suppressed because it is too large
View File


Loading…
Cancel
Save