Updated with latest changes from Tree v2

Added support for dynamic fields, updated web app, etc.
This commit is contained in:
Jason Coon
2016-12-18 16:02:44 -06:00
parent f1d2c4dd94
commit d8149e2fd3
29 changed files with 2415 additions and 776 deletions

6
data/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

1
data/css/jquery.minicolors.min.css vendored Normal file

File diff suppressed because one or more lines are too long

18
data/css/simple.css Normal file
View File

@ -0,0 +1,18 @@
/*body {
padding-bottom: 70px;
}*/
.grid-item-color {
width: 4%;
height: 32px;
cursor: pointer;
}
.grid-item-pattern {
width: 100px;
height: 100px;
margin: 5px;
padding: 6px;
white-space: normal;
cursor: pointer;
}

File diff suppressed because one or more lines are too long

3
data/edit.htm Normal file

File diff suppressed because one or more lines are too long

View File

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

@ -5,110 +5,228 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ESP8266 + FastLED</title>
<title>ESP8266 + FastLED by Evil Genius Labs</title>
<!-- request CSS from internet CDN -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-minicolors/2.2.4/jquery.minicolors.min.css" integrity="sha256-4wnSkPYU5B4yngAlx/rEb8LdfMah4teUth4AfhGEuaY=" crossorigin="anonymous" />
<!-- request CSS from the ESP8266 web server -->
<!-- <link rel="stylesheet" href="css/bootstrap.min.css"> -->
<!-- <link rel="stylesheet" href="css/jquery.minicolors.min.css"> -->
<link rel="stylesheet" href="css/styles.css">
<link rel="icon" href="images/atom196.png">
</head>
<body>
<header class="navbar navbar-default navbar-static-top" id="top" role="banner">
<nav class="navbar navbar-default navbar-static-top" id="top" role="banner">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">ESP8266 + FastLED</a>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="https://www.evilgeniuslabs.org" target="_blank"><img src="https://evilgeniuslabs.org/images/atom.svg" style="width: 24px; height: 24px;" /></a>
<a class="navbar-brand" href="https://www.evilgeniuslabs.org" target="_blank">Evil Genius Labs</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="/">Tree v2 <span class="sr-only">(current)</span></a></li>
<li><a href="/simple.htm" target="_blank" title="Simple Mode">Simple</a></li>
<li><a href="/edit.htm" target="_blank" title="Edit Files">Files</a></li>
<li><a href="/update" target="_blank" title="Update Firmware">Firmware</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>
<img style="height: 16px;" src="https://assets-cdn.github.com/favicon.ico">
</a>
</li>
</ul>
</div>
</div>
</header>
<div class="container">
<form class="form-horizontal">
<div class="form-group">
<div class="col-sm-1 col-sm-offset-2">
<button type="button" class="btn btn-default">
<span class="glyphicon glyphicon-refresh" id="btnRefresh"></span>
</nav>
<div id="container" class="container">
<form class="form-horizontal" id="form">
</form>
</div>
<div id="templates" style="display: none">
<div id="sectionTemplate" class="form-group">
<div class="col-sm-12">
<hr style="margin-bottom: 5px;margin-top: 5px;" />
</div>
</div>
<div id="numberTemplate" class="form-group">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-2">
<input class="form-control input" type="number" step="1" min="0" max="255" />
</div>
<div class="col-sm-8">
<input class="form-control slider" type="range" step="1" min="0" max="255" />
</div>
</div>
<div id="booleanTemplate" class="form-group">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-10">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default" id="btnOn">On</button>
<button type="button" class="btn btn-default" id="btnOff">Off</button>
</div>
</div>
</div>
<div id="selectTemplate" class="form-group">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-8">
<select class="form-control"><select>
</div>
<div class="col-sm-2">
<div class="btn-group" role="group" aria-label="...">
<button type="button" class="btn btn-default btn-previous"
aria-label="Previous" title="Previous">
<span class="glyphicon glyphicon-chevron-left"></span>
</button>
<button type="button" class="btn btn-default btn-next"
aria-label="Next" title="Next">
<span class="glyphicon glyphicon-chevron-right"></span>
</button>
</div>
<div class="col-sm-4">
<p id="status" class="form-control-static">Status</p>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Power</label>
<div class="col-sm-6">
<div class="btn-group" role="group" aria-label="Power">
<button type="button" class="btn btn-default" id="btnPowerOn">On</button>
<button type="button" class="btn btn-default" id="btnPowerOff">Off</button>
</div>
<div id="colorPaletteTemplate" class="form-group">
<label class="col-sm-2 control-label color-label"></label>
<div class="col-sm-10">
<div class="btn-group btn-group-justified" role="group">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FF0000;" title="Red">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FF8000;" title="Orange">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FFFF00;" title="Yellow">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #80FF00;" title="Chartreuse">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #00FF00;" title="Green">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #00FF80;" title="Spring Green">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #00FFFF;" title="Cyan">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #0080FF;" title="Azure">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #0000FF;" title="Blue">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #8000FF;" title="Violet">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FF00FF;" title="Magenta">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FF0080;" title="Rose">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FFFFFF;" title="White">&nbsp;</button>
</div>
</div>
</div>
</div>
<div id="colorTemplate">
<div class="form-group">
<label for="inputBrightness" class="col-sm-2 control-label">Brightness</label>
<div class="col-sm-6">
<div class="input-group">
<span class="input-group-addon" id="spanBrightness">128</span>
<input class="form-control" id="inputBrightness" type="range" step="1" min="0" max="255" />
</div>
<!-- <label class="col-sm-2 control-label color-label"></label> -->
<div class="col-sm-12 col-sm-offset-2">
<input type="text" class="form-control minicolors" />
</div>
</div>
<div class="form-group">
<label for="inputPattern" class="col-sm-2 control-label">Pattern</label>
<div class="col-sm-6">
<select class="form-control" id="inputPattern">
<option>Rainbow</option>
<option>Sinelon</option>
<option>Juggle</option>
</select>
<label class="col-sm-2 control-label">Red</label>
<div class="col-sm-2">
<input class="form-control color-red-input" type="number" step="1" min="0" max="255" />
</div>
<div class="col-sm-8">
<input class="form-control color-red-slider" type="range" step="1" min="0" max="255" />
</div>
</div>
<div class="form-group">
<label for="inputColor" class="col-sm-2 control-label">Color</label>
<div class="col-sm-6">
<input id="inputColor" type="text" class="form-control">
<label class="col-sm-2 control-label">Green</label>
<div class="col-sm-2">
<input class="form-control color-green-input" type="number" step="1" min="0" max="255" />
</div>
<div class="col-sm-8">
<input class="form-control color-green-slider" type="range" step="1" min="0" max="255" />
</div>
</div>
<div class="form-group">
<div class="col-sm-6 col-sm-offset-2">
<div class="btn-group btn-group-justified" role="group">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FF0000;" title="Red">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FF8000;" title="Orange">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FFFF00;" title="Yellow">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #80FF00;" title="Chartreuse">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #00FF00;" title="Green">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #00FF80;" title="Spring Green">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #00FFFF;" title="Cyan">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #0080FF;" title="Azure">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #0000FF;" title="Blue">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #8000FF;" title="Violet">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FF00FF;" title="Magenta">&nbsp;</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-color" style="background: #FF0080;" title="Rose">&nbsp;</button>
</div>
</div>
<label class="col-sm-2 control-label">Blue</label>
<div class="col-sm-2">
<input class="form-control color-blue-input" type="number" step="1" min="0" max="255" />
</div>
<div class="col-sm-8">
<input class="form-control color-blue-slider" type="range" step="1" min="0" max="255" />
</div>
</div>
</form>
</div>
</div>
<script src="js/scripts.js"></script>
<nav class="navbar navbar-default navbar-fixed-bottom">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse-2" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse-2">
<ul class="nav navbar-nav">
<li>
<a href="/" aria-label="Refresh" title="Refresh">
<span class="glyphicon glyphicon-refresh" id="btnRefresh"></span>
</a>
</li>
<li><p class="navbar-text" id="status">Loading, please wait...</p></li>
</ul>
</div>
</div>
</nav>
<!-- request js from internet CDN -->
<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-minicolors/2.2.4/jquery.minicolors.min.js" integrity="sha256-XAFQ9dZ6hy8p/GRhU8h/8pMvM1etymiJLZW1CiHV3bQ=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.min.js" integrity="sha256-A4JwlcDvqO4JXpvEtvWY1RH8JAEMu5W21wP8GUXLUNs=" crossorigin="anonymous"></script>
<!-- request js from the ESP8266 web server -->
<!-- <script src="js/jquery-3.1.1.min.js"></script> -->
<!-- <script src="js/bootstrap.min.js"></script> -->
<!-- <script src="js/jquery.minicolors.min.js"></script> -->
<!-- <script src="js/r-websocket.min.js"></script> -->
<script src="js/app.js"></script>
</body>
</html>

462
data/js/app.js Normal file
View File

@ -0,0 +1,462 @@
// used when hosting the site on the ESP8266
var address = location.hostname;
var urlBase = "";
// used when hosting the site somewhere other than the ESP8266 (handy for testing without waiting forever to upload to SPIFFS)
// var address = "esp8266-1920f7.local";
// var urlBase = "http://" + address + "/";
var postColorTimer = {};
var postValueTimer = {};
var ignoreColorChange = false;
var ws = new ReconnectingWebSocket('ws://' + address + ':81/', ['arduino']);
ws.debug = true;
ws.onmessage = function(evt) {
if(evt.data != null)
{
var data = JSON.parse(evt.data);
if(data == null) return;
updateFieldValue(data.name, data.value);
}
}
$(document).ready(function() {
$("#status").html("Connecting, please wait...");
$.get(urlBase + "all", function(data) {
$("#status").html("Loading, please wait...");
$.each(data, function(index, field) {
if (field.type == "Number") {
addNumberField(field);
} else if (field.type == "Boolean") {
addBooleanField(field);
} else if (field.type == "Select") {
addSelectField(field);
} else if (field.type == "Color") {
addColorFieldPalette(field);
addColorFieldPicker(field);
} else if (field.type == "Section") {
addSectionField(field);
}
});
$(".minicolors").minicolors({
theme: "bootstrap",
changeDelay: 200,
control: "wheel",
format: "rgb",
inline: true
});
$("#status").html("Ready");
})
.fail(function(errorThrown) {
console.log("error: " + errorThrown);
});
});
function addNumberField(field) {
var template = $("#numberTemplate").clone();
template.attr("id", "form-group-" + field.name);
template.attr("data-field-type", field.type);
var label = template.find(".control-label");
label.attr("for", "input-" + field.name);
label.text(field.label);
var input = template.find(".input");
var slider = template.find(".slider");
slider.attr("id", "input-" + field.name);
if (field.min) {
input.attr("min", field.min);
slider.attr("min", field.min);
}
if (field.max) {
input.attr("max", field.max);
slider.attr("max", field.max);
}
if (field.step) {
input.attr("step", field.step);
slider.attr("step", field.step);
}
input.val(field.value);
slider.val(field.value);
slider.on("change mousemove", function() {
input.val($(this).val());
});
slider.on("change", function() {
var value = $(this).val();
input.val(value);
field.value = value;
delayPostValue(field.name, value);
});
input.on("change", function() {
var value = $(this).val();
slider.val(value);
field.value = value;
delayPostValue(field.name, value);
});
$("#form").append(template);
}
function addBooleanField(field) {
var template = $("#booleanTemplate").clone();
template.attr("id", "form-group-" + field.name);
template.attr("data-field-type", field.type);
var label = template.find(".control-label");
label.attr("for", "btn-group-" + field.name);
label.text(field.label);
var btngroup = template.find(".btn-group");
btngroup.attr("id", "btn-group-" + field.name);
var btnOn = template.find("#btnOn");
var btnOff = template.find("#btnOff");
btnOn.attr("id", "btnOn" + field.name);
btnOff.attr("id", "btnOff" + field.name);
btnOn.attr("class", field.value ? "btn btn-primary" : "btn btn-default");
btnOff.attr("class", !field.value ? "btn btn-primary" : "btn btn-default");
btnOn.click(function() {
setBooleanFieldValue(field, btnOn, btnOff, 1)
});
btnOff.click(function() {
setBooleanFieldValue(field, btnOn, btnOff, 0)
});
$("#form").append(template);
}
function addSelectField(field) {
var template = $("#selectTemplate").clone();
template.attr("id", "form-group-" + field.name);
template.attr("data-field-type", field.type);
var id = "input-" + field.name;
var label = template.find(".control-label");
label.attr("for", id);
label.text(field.label);
var select = template.find(".form-control");
select.attr("id", id);
for (var i = 0; i < field.options.length; i++) {
var optionText = field.options[i];
var option = $("<option></option>");
option.text(optionText);
option.attr("value", i);
select.append(option);
}
select.val(field.value);
select.change(function() {
var value = template.find("#" + id + " option:selected").index();
postValue(field.name, value);
});
var previousButton = template.find(".btn-previous");
var nextButton = template.find(".btn-next");
previousButton.click(function() {
var value = template.find("#" + id + " option:selected").index();
var count = select.find("option").length;
value--;
if(value < 0)
value = count - 1;
select.val(value);
postValue(field.name, value);
});
nextButton.click(function() {
var value = template.find("#" + id + " option:selected").index();
var count = select.find("option").length;
value++;
if(value >= count)
value = 0;
select.val(value);
postValue(field.name, value);
});
$("#form").append(template);
}
function addColorFieldPicker(field) {
var template = $("#colorTemplate").clone();
template.attr("id", "form-group-" + field.name);
template.attr("data-field-type", field.type);
var id = "input-" + field.name;
var input = template.find(".minicolors");
input.attr("id", id);
if(!field.value.startsWith("rgb("))
field.value = "rgb(" + field.value;
if(!field.value.endsWith(")"))
field.value += ")";
input.val(field.value);
var components = rgbToComponents(field.value);
var redInput = template.find(".color-red-input");
var greenInput = template.find(".color-green-input");
var blueInput = template.find(".color-blue-input");
var redSlider = template.find(".color-red-slider");
var greenSlider = template.find(".color-green-slider");
var blueSlider = template.find(".color-blue-slider");
redInput.attr("id", id + "-red");
greenInput.attr("id", id + "-green");
blueInput.attr("id", id + "-blue");
redSlider.attr("id", id + "-red-slider");
greenSlider.attr("id", id + "-green-slider");
blueSlider.attr("id", id + "-blue-slider");
redInput.val(components.r);
greenInput.val(components.g);
blueInput.val(components.b);
redSlider.val(components.r);
greenSlider.val(components.g);
blueSlider.val(components.b);
redInput.on("change", function() {
var value = $("#" + id).val();
var r = $(this).val();
var components = rgbToComponents(value);
field.value = r + "," + components.g + "," + components.b;
$("#" + id).minicolors("value", "rgb(" + field.value + ")");
redSlider.val(r);
});
greenInput.on("change", function() {
var value = $("#" + id).val();
var g = $(this).val();
var components = rgbToComponents(value);
field.value = components.r + "," + g + "," + components.b;
$("#" + id).minicolors("value", "rgb(" + field.value + ")");
greenSlider.val(g);
});
blueInput.on("change", function() {
var value = $("#" + id).val();
var b = $(this).val();
var components = rgbToComponents(value);
field.value = components.r + "," + components.g + "," + b;
$("#" + id).minicolors("value", "rgb(" + field.value + ")");
blueSlider.val(b);
});
redSlider.on("change", function() {
var value = $("#" + id).val();
var r = $(this).val();
var components = rgbToComponents(value);
field.value = r + "," + components.g + "," + components.b;
$("#" + id).minicolors("value", "rgb(" + field.value + ")");
redInput.val(r);
});
greenSlider.on("change", function() {
var value = $("#" + id).val();
var g = $(this).val();
var components = rgbToComponents(value);
field.value = components.r + "," + g + "," + components.b;
$("#" + id).minicolors("value", "rgb(" + field.value + ")");
greenInput.val(g);
});
blueSlider.on("change", function() {
var value = $("#" + id).val();
var b = $(this).val();
var components = rgbToComponents(value);
field.value = components.r + "," + components.g + "," + b;
$("#" + id).minicolors("value", "rgb(" + field.value + ")");
blueInput.val(b);
});
redSlider.on("change mousemove", function() {
redInput.val($(this).val());
});
greenSlider.on("change mousemove", function() {
greenInput.val($(this).val());
});
blueSlider.on("change mousemove", function() {
blueInput.val($(this).val());
});
input.on("change", function() {
if (ignoreColorChange) return;
var value = $(this).val();
var components = rgbToComponents(value);
redInput.val(components.r);
greenInput.val(components.g);
blueInput.val(components.b);
redSlider.val(components.r);
greenSlider.val(components.g);
blueSlider.val(components.b);
field.value = components.r + "," + components.g + "," + components.b;
delayPostColor(field.name, components);
});
$("#form").append(template);
}
function addColorFieldPalette(field) {
var template = $("#colorPaletteTemplate").clone();
var buttons = template.find(".btn-color");
var label = template.find(".control-label");
label.text(field.label);
buttons.each(function(index, button) {
$(button).click(function() {
var rgb = $(this).css('backgroundColor');
var components = rgbToComponents(rgb);
field.value = components.r + "," + components.g + "," + components.b;
postColor(field.name, components);
ignoreColorChange = true;
var id = "#input-" + field.name;
$(id).minicolors("value", "rgb(" + field.value + ")");
$(id + "-red").val(components.r);
$(id + "-green").val(components.g);
$(id + "-blue").val(components.b);
$(id + "-red-slider").val(components.r);
$(id + "-green-slider").val(components.g);
$(id + "-blue-slider").val(components.b);
ignoreColorChange = false;
});
});
$("#form").append(template);
}
function addSectionField(field) {
var template = $("#sectionTemplate").clone();
template.attr("id", "form-group-section-" + field.name);
template.attr("data-field-type", field.type);
$("#form").append(template);
}
function updateFieldValue(name, value) {
var group = $("#form-group-" + name);
var type = group.attr("data-field-type");
if (type == "Number") {
var input = group.find(".form-control");
input.val(value);
} else if (type == "Boolean") {
var btnOn = group.find("#btnOn" + name);
var btnOff = group.find("#btnOff" + name);
btnOn.attr("class", value ? "btn btn-primary" : "btn btn-default");
btnOff.attr("class", !value ? "btn btn-primary" : "btn btn-default");
} else if (type == "Select") {
var select = group.find(".form-control");
select.val(value);
} else if (type == "Color") {
var input = group.find(".form-control");
input.val("rgb(" + value + ")");
}
};
function setBooleanFieldValue(field, btnOn, btnOff, value) {
field.value = value;
btnOn.attr("class", field.value ? "btn btn-primary" : "btn btn-default");
btnOff.attr("class", !field.value ? "btn btn-primary" : "btn btn-default");
postValue(field.name, field.value);
}
function postValue(name, value) {
$("#status").html("Setting " + name + ": " + value + ", please wait...");
var body = { name: name, value: value };
$.post(urlBase + name + "?value=" + value, body, function(data) {
if (data.name != null) {
$("#status").html("Set " + name + ": " + data.name);
} else {
$("#status").html("Set " + name + ": " + data);
}
});
}
function delayPostValue(name, value) {
clearTimeout(postValueTimer);
postValueTimer = setTimeout(function() {
postValue(name, value);
}, 300);
}
function postColor(name, value) {
$("#status").html("Setting " + name + ": " + value.r + "," + value.g + "," + value.b + ", please wait...");
var body = { name: name, r: value.r, g: value.g, b: value.b };
$.post(urlBase + name + "?r=" + value.r + "&g=" + value.g + "&b=" + value.b, body, function(data) {
$("#status").html("Set " + name + ": " + data);
})
.fail(function(textStatus, errorThrown) { $("#status").html("Fail: " + textStatus + " " + errorThrown); });
}
function delayPostColor(name, value) {
clearTimeout(postColorTimer);
postColorTimer = setTimeout(function() {
postColor(name, value);
}, 300);
}
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function rgbToComponents(rgb) {
var components = {};
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
components.r = parseInt(rgb[1]);
components.g = parseInt(rgb[2]);
components.b = parseInt(rgb[3]);
return components;
}

7
data/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

4
data/js/jquery-3.1.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

11
data/js/jquery.minicolors.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
data/js/r-websocket.min.js vendored Normal file
View File

@ -0,0 +1 @@
!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a});

File diff suppressed because one or more lines are too long

239
data/js/simple.js Normal file
View File

@ -0,0 +1,239 @@
// used when hosting the site on the ESP8266
var address = location.hostname;
var urlBase = "";
// used when hosting the site somewhere other than the ESP8266 (handy for testing without waiting forever to upload to SPIFFS)
// var address = "192.168.1.13";
// var urlBase = "http://" + address + "/";
var postColorTimer = {};
var postValueTimer = {};
var ignoreColorChange = false;
var patterns = [
"Pride",
"Color Waves",
"Rainbow Twinkles",
"Snow Twinkles",
"Cloud Twinkles",
"Incandescent Twinkles",
"Retro C9 Twinkles",
"Red & White Twinkles",
"Blue & White Twinkles",
"Red, Green & White Twinkles",
"Fairy Light Twinkles",
"Snow 2 Twinkles",
"Holly Twinkles",
"Ice Twinkles",
"Party Twinkles",
"Forest Twinkles",
"Lava Twinkles",
"Fire Twinkles",
"Cloud 2 Twinkles",
"Ocean Twinkles",
"Rainbow",
"Rainbow With Glitter",
"Solid Rainbow",
"Confetti",
"Sinelon",
"Beat",
"Juggle",
"Fire",
"Water"
];
var ws = new ReconnectingWebSocket('ws://' + address + ':81/', ['arduino']);
ws.debug = true;
ws.onmessage = function(evt) {
if(evt.data != null)
{
var data = JSON.parse(evt.data);
if(data == null) return;
switch(data.name) {
case "power":
if(data.value == 1) {
$("#btnOn").attr("class", "btn btn-primary");
$("#btnOff").attr("class", "btn btn-default");
} else {
$("#btnOff").attr("class", "btn btn-primary");
$("#btnOn").attr("class", "btn btn-default");
}
break;
case "pattern":
$(".grid-item-pattern").attr("class", "grid-item-pattern btn btn-default");
$("#pattern-button-" + data.value).attr("class", "grid-item-pattern btn btn-primary");
break;
}
}
}
$(document).ready(function() {
$("#status").html("Connecting, please wait...");
$.get(urlBase + "all", function(data) {
$("#status").html("Loading, please wait...");
$.each(data, function(index, field) {
switch (field.name) {
case "power":
if(field.value == 1) {
$("#btnOn").attr("class", "btn btn-primary");
} else {
$("#btnOff").attr("class", "btn btn-primary");
}
break;
case "pattern":
addPatternButtons(field);
break;
}
});
});
addColorButtons();
$("#btnOn").click(function() {
postValue("power", 1);
$("#btnOn").attr("class", "btn btn-primary");
$("#btnOff").attr("class", "btn btn-default");
});
$("#btnOff").click(function() {
postValue("power", 0);
$("#btnOff").attr("class", "btn btn-primary");
$("#btnOn").attr("class", "btn btn-default");
});
$("#status").html("Ready");
});
function addColorButtons() {
var hues = 25;
var hueStep = 360 / hues;
var levels = 10;
var levelStep = 60 / levels;
for(var l = 20; l < 80; l += levelStep) {
for(var h = 0; h < hues; h++) {
addColorButton(h * hueStep, 100, l);
}
}
$('.grid-color').isotope({
itemSelector: '.grid-item-color',
layoutMode: 'fitRows'
});
}
var colorButtonIndex = 0;
function addColorButton(h, s, l) {
var color = "hsla(" + h + ", " + s + "%, " + l + "%, 1)"
var template = $("#colorButtonTemplate").clone();
template.attr("id", "color-button-" + colorButtonIndex++);
template.css("background-color", color);
template.click(function() {
var rgb = $(this).css('backgroundColor');
var components = rgbToComponents(rgb);
$(".grid-item-color").css("border", "none");
$(this).css("border", "1px solid");
postColor("solidColor", components);
});
$("#colorButtonsRow").append(template);
}
function addPatternButtons(patternField) {
$.each(patternField.options, function(index, pattern) {
if($.inArray(pattern, patterns) == -1)
return;
var template = $("#patternButtonTemplate").clone();
template.attr("id", "pattern-button-" + index);
template.text(pattern);
template.click(function() {
postValue("patternName", pattern);
$(".grid-item-color").css("border", "none");
$(".grid-item-pattern").attr("class", "grid-item-pattern btn btn-default");
$(this).attr("class", "grid-item-pattern btn btn-primary");
});
$("#patternGrid").append(template);
});
$('.grid-pattern').isotope({
itemSelector: '.grid-item-pattern',
layoutMode: 'fitRows'
});
$("#pattern-button-" + patternField.value).attr("class", "grid-item-pattern btn btn-primary");
}
function postValue(name, value) {
$("#status").html("Setting " + name + ": " + value + ", please wait...");
var body = { name: name, value: value };
$.post(urlBase + name, body, function(data) {
if (data.name != null) {
$("#status").html("Set " + name + ": " + data.name);
} else {
$("#status").html("Set " + name + ": " + data);
}
});
}
function delayPostValue(name, value) {
clearTimeout(postValueTimer);
postValueTimer = setTimeout(function() {
postValue(name, value);
}, 300);
}
function postColor(name, value) {
$("#status").html("Setting " + name + ": " + value.r + "," + value.g + "," + value.b + ", please wait...");
var body = { name: name, r: value.r, g: value.g, b: value.b };
$.post(urlBase + name + "?r=" + value.r + "&g=" + value.g + "&b=" + value.b, body, function(data) {
$("#status").html("Set " + name + ": " + data);
})
.fail(function(textStatus, errorThrown) { $("#status").html("Fail: " + textStatus + " " + errorThrown); });
}
function delayPostColor(name, value) {
clearTimeout(postColorTimer);
postColorTimer = setTimeout(function() {
postColor(name, value);
}, 300);
}
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function rgbToComponents(rgb) {
var components = {};
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
components.r = parseInt(rgb[1]);
components.g = parseInt(rgb[2]);
components.b = parseInt(rgb[3]);
return components;
}

58
data/simple.htm Normal file
View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tree v2 by Evil Genius Labs</title>
<!-- request CSS from internet CDN -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- request CSS from the ESP8266 web server -->
<!-- <link rel="stylesheet" href="css/bootstrap.min.css"> -->
<link rel="stylesheet" href="css/simple.css">
<link rel="icon" href="images/atom196.png">
</head>
<body>
<div id="container" class="container">
<div style="margin: 5px;">
<button type="button" class="btn btn-default" id="btnOn">On</button>
<button type="button" class="btn btn-default" id="btnOff">Off</button>
</div>
<div id="patternGrid" class="grid-pattern"></div>
<div id="colorButtonsRow" class="grid-color"></div>
</div>
<div id="templates" style="display: none">
<div id="colorButtonTemplate" class="grid-item-color"></div>
<button id="patternButtonTemplate" class="grid-item-pattern btn btn-default"></button>
</div>
<!-- request js from internet CDN -->
<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.min.js" integrity="sha256-A4JwlcDvqO4JXpvEtvWY1RH8JAEMu5W21wP8GUXLUNs=" crossorigin="anonymous"></script>
<!-- request js from the ESP8266 web server -->
<!-- <script src="js/jquery-3.1.1.min.js"></script> -->
<!-- <script src="js/bootstrap.min.js"></script> -->
<script src="js/simple.js"></script>
</body>
</html>