@ -0,0 +1,8 @@ | |||||
#!/bin/bash | |||||
rm -rf data/*.gz | |||||
cp html/*min.css data/ | |||||
cp html/*min.js data/ | |||||
cp html/index.html data/ | |||||
gzip data/*.js | |||||
gzip data/*.css | |||||
gzip data/*.html |
@ -1 +1 @@ | |||||
0.9.5 | |||||
0.9.7 |
@ -1,392 +0,0 @@ | |||||
<!DOCTYPE html> | |||||
<html> | |||||
<head> | |||||
<title>ESPurna</title> | |||||
<meta charset="utf-8" /> | |||||
<link rel="stylesheet" href="spectre.min.css" /> | |||||
<script src="jquery-1.12.3.min.js"></script> | |||||
<style> | |||||
html, body { | |||||
font-size: 12px; | |||||
margin: 0; | |||||
padding: 0; | |||||
width: 100%; | |||||
height: 100%; | |||||
} | |||||
div.page { | |||||
display: none; | |||||
} | |||||
.bg-grey { | |||||
background-color: #efefef; | |||||
padding: 1rem; | |||||
border-radius: .3rem; | |||||
} | |||||
div.hint { | |||||
background-color: #efefef; | |||||
padding: 2rem; | |||||
margin: 20px 0; | |||||
border-left: 2px solid blue; | |||||
} | |||||
</style> | |||||
<script> | |||||
$(function() { | |||||
var timer = null; | |||||
$("form").submit(function(event) { | |||||
button = $(":submit", this); | |||||
button.addClass("loading"); | |||||
$.ajax({ | |||||
'method': 'POST', | |||||
'url': '/save', | |||||
'dataType': 'json', | |||||
'data': $(this).serializeArray() | |||||
}).done(function(data) { | |||||
button.removeClass("loading"); | |||||
}).fail(function() { | |||||
button.removeClass("loading"); | |||||
}); | |||||
event.preventDefault(); | |||||
setTimeout(update, 200); | |||||
}); | |||||
function init() { | |||||
$.ajax({ | |||||
'method': 'GET', | |||||
'url': '/init', | |||||
'dataType': 'json' | |||||
}).done(function(data) { | |||||
keys = Object.keys(data); | |||||
for (index in keys) { | |||||
key = "#" + keys[index]; | |||||
value = data[keys[index]]; | |||||
try { | |||||
if ($(key).prop('tagName') == 'INPUT') { | |||||
$(key).val(value); | |||||
} else { | |||||
$(key).html(value); | |||||
} | |||||
} catch(err) { | |||||
// nope | |||||
}; | |||||
}; | |||||
timer = setInterval(update, data.updateInterval); | |||||
document.title = data.hostname; | |||||
$("[name='pwMainsVoltage']").val(data.pwMainsVoltage); | |||||
$("[name='rfDevice']").val(data.rfDevice); | |||||
}); | |||||
} | |||||
function update() { | |||||
$.ajax({ | |||||
'method': 'GET', | |||||
'url': '/status', | |||||
'dataType': 'json' | |||||
}).done(function(data) { | |||||
$("#mqtt").val(data.mqtt ? "CONNECTED" : "NOT CONNECTED"); | |||||
$("#power").val((data.power | 0) + "W"); | |||||
$("#temperature").val((data.temperature | 0) + "ºC"); | |||||
$("#humidity").val((data.humidity | 0) + "%"); | |||||
if (data.relay) { | |||||
$("#relay").addClass('btn-primary').html("ON"); | |||||
$("#status").val(1); | |||||
} else { | |||||
$("#relay").removeClass('btn-primary').html("OFF"); | |||||
$("#status").val(0); | |||||
} | |||||
}); | |||||
} | |||||
$("#btn-admin").click(function() { | |||||
$("#panel-status").hide(); | |||||
$("#panel-admin").show(); | |||||
$("#btn-admin").addClass('btn-primary'); | |||||
$("#btn-status").removeClass('btn-primary'); | |||||
}); | |||||
$("#btn-status").click(function() { | |||||
$("#panel-admin").hide(); | |||||
$("#panel-status").show(); | |||||
$("#btn-admin").removeClass('btn-primary'); | |||||
$("#btn-status").addClass('btn-primary'); | |||||
}); | |||||
$("#relay").click(function(event) { | |||||
var status = parseInt($("#status").val()); | |||||
if (status == 1) { | |||||
$.ajax({'method': 'GET', 'url': '/relay/off'}); | |||||
} else { | |||||
$.ajax({'method': 'GET', 'url': '/relay/on'}); | |||||
} | |||||
setTimeout(update, 200); | |||||
event.preventDefault(); | |||||
}) | |||||
init(); | |||||
update(); | |||||
}); | |||||
</script> | |||||
</head> | |||||
<body> | |||||
<div class="container"> | |||||
<header class="navbar bg-grey"> | |||||
<section class="navbar-section"> | |||||
<a href="#" class="navbar-brand"><span id="appname"></span></a> | |||||
</section> | |||||
<section class="navbar-section"> | |||||
<button class="btn" id="btn-admin">Administration</button> | |||||
<button class="btn btn-primary" id="btn-status">Status</button> | |||||
</section> | |||||
</header> | |||||
<div class="page" id="panel-status" style="display: block;"> | |||||
<div class="columns"> | |||||
<div class="column col-12"> | |||||
<form class="form-horizontal"> | |||||
<div class="form-group"> | |||||
<div class="col-xs-3"> | |||||
<label class="form-label" for="manufacturer">Manufacturer</label> | |||||
</div> | |||||
<div class="col-xs-9"> | |||||
<input type="text" class="form-input" id="manufacturer" value="" disabled> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-xs-3"> | |||||
<label class="form-label" for="device">Device</label> | |||||
</div> | |||||
<div class="col-xs-9"> | |||||
<input type="text" class="form-input" id="device" value="" disabled> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-xs-3"> | |||||
<label class="form-label" for="hostname">Hostname</label> | |||||
</div> | |||||
<div class="col-xs-9"> | |||||
<input type="text" class="form-input" id="hostname" value="" disabled> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-xs-3"> | |||||
<label class="form-label" for="network">Network</label> | |||||
</div> | |||||
<div class="col-xs-9"> | |||||
<input type="text" class="form-input" id="network" value="" disabled> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-xs-3"> | |||||
<label class="form-label" for="ip">IP</label> | |||||
</div> | |||||
<div class="col-xs-9"> | |||||
<input type="text" class="form-input" id="ip" value="" disabled> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-xs-3"> | |||||
<label class="form-label" for="mqtt">MQTT Status</label> | |||||
</div> | |||||
<div class="col-xs-9"> | |||||
<input type="text" class="form-input" id="mqtt" disabled> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-xs-3"> | |||||
<label class="form-label" for="power">Power</label> | |||||
</div> | |||||
<div class="col-xs-6"> | |||||
<input type="text" class="form-input" id="power" disabled> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-xs-3"> | |||||
<label class="form-label" for="temperature">Temperature</label> | |||||
</div> | |||||
<div class="col-xs-6"> | |||||
<input type="text" class="form-input" id="temperature" disabled> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-xs-3"> | |||||
<label class="form-label" for="humidity">Humidity</label> | |||||
</div> | |||||
<div class="col-xs-6"> | |||||
<input type="text" class="form-input" id="humidity" disabled> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-xs-3"> | |||||
<label class="form-label" for="relay">Relay Status</label> | |||||
</div> | |||||
<div class="col-xs-6"> | |||||
<input type="hidden" class="form-input" name="status" id="status" value="0" /> | |||||
<a class="btn" data="0" id="relay">OFF</a> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="page" id="panel-admin"> | |||||
<form action="/" method="post"> | |||||
<div class="hint"> | |||||
You can configure up to 3 different WiFi networks. The device will try to connect to any of them starting with the first one. | |||||
</div> | |||||
<div class="columns"> | |||||
<div class="column col-4"> | |||||
<h5>Network 1</h5> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="ssid0">Network SSID</label> | |||||
<input name="ssid0" id="ssid0" type="text" class="form-input" size="8" tabindex="1" placeholder="Network SSID"> | |||||
</div> | |||||
<div> | |||||
<label class="form-label" for="pass0">Network Password</label> | |||||
<input name="pass0" id="pass0" type="text" class="form-input" maxlength="255" tabindex="2" placeholder="Network password"> | |||||
</div> | |||||
</div> | |||||
<div class="column col-4"> | |||||
<h5>Network 2</h5> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="ssid1">Network SSID</label> | |||||
<input name="ssid1" id="ssid1" type="text" class="form-input" size="8" tabindex="3" placeholder="Network SSID"> | |||||
</div> | |||||
<div> | |||||
<label class="form-label" for="pass1">Network Password</label> | |||||
<input name="pass1" id="pass1" type="text" class="form-input" maxlength="255" tabindex="4" placeholder="Network password"> | |||||
</div> | |||||
</div> | |||||
<div class="column col-4"> | |||||
<h5>Network 3</h5> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="ssid2">Network SSID</label> | |||||
<input name="ssid2" id="ssid2" type="text" class="form-input" size="8" tabindex="5" placeholder="Network SSID"> | |||||
</div> | |||||
<div> | |||||
<label class="form-label" for="pass2">Network Password</label> | |||||
<input name="pass2" id="pass2" type="text" class="form-input" maxlength="255" tabindex="6" placeholder="Network password"> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="clearfix"></div> | |||||
<div class="hint"> | |||||
Configure an <strong>MQTT broker</strong> in your network and you will be able to change the switch status via an MQTT message. Just send a 0 or a 1 as a payload to the provided topic below. | |||||
The switch will also report its current open/close status to the same topic and its IP address to the topic you define plus "<code>/ip</code>". Leave the server field empty to disable MQTT. | |||||
</div> | |||||
<div class="columns"> | |||||
<div class="column col-4"> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="mqttServer">MQTT Server</label> | |||||
<input name="mqttServer" id="mqttServer" type="text" class="form-input" size="8" tabindex="8" placeholder="MQTT Server"> | |||||
</div> | |||||
</div> | |||||
<div class="column col-2"> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="mqttPort">MQTT Port</label> | |||||
<input name="mqttPort" id="mqttPort" type="text" class="form-input" size="8" tabindex="9" placeholder="1883"> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="columns"> | |||||
<div class="column col-4"> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="mqttUser">MQTT User</label> | |||||
<input name="mqttUser" id="mqttUser" type="text" class="form-input" size="8" tabindex="10" placeholder="Leave blank if no user/pass"> | |||||
</div> | |||||
</div> | |||||
<div class="column col-4"> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="mqttPassword">MQTT Password</label> | |||||
<input name="mqttPassword" id="mqttPassword" type="text" class="form-input" size="8" tabindex="11" placeholder="Leave blank if no user/pass"> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="columns"> | |||||
<div class="column col-4"> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="mqttTopic">MQTT Topic</label> | |||||
<input name="mqttTopic" id="mqttTopic" type="text" class="form-input" size="8" tabindex="12" maxlength="35" placeholder="MQTT Topic"> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="clearfix"></div> | |||||
<div class="hint"> | |||||
If your device supports RF switching you can configure here the channel and device ID. | |||||
</div> | |||||
<div class="columns"> | |||||
<div class="column col-2"> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="rfChannel">RF Channel</label> | |||||
<input name="rfChannel" id="rfChannel" type="number" min="0" max="31" step="1" class="form-input" tabindex="13"/> | |||||
</div> | |||||
</div> | |||||
<div class="column col-2"> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="rfDevice">RF Device</label> | |||||
<select name="rfDevice" class="form-input" tabindex="14"> | |||||
<option value="0">A</a> | |||||
<option value="1">B</a> | |||||
<option value="2">C</a> | |||||
<option value="3">D</a> | |||||
<option value="4">E</a> | |||||
</select> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="clearfix"></div> | |||||
<div class="hint"> | |||||
If your device supports power measurement, here you can configure line potential and current ratio. | |||||
</div> | |||||
<div class="columns"> | |||||
<div class="column col-2"> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="pwMainsVoltage">AC RMS Voltage</label> | |||||
<select name="pwMainsVoltage" class="form-input" tabindex="15"> | |||||
<option value="125">125</a> | |||||
<option value="220">220</a> | |||||
<option value="230">230</a> | |||||
<option value="240">240</a> | |||||
</select> | |||||
</div> | |||||
</div> | |||||
<div class="column col-2"> | |||||
<div class="form-group"> | |||||
<label class="form-label" for="pwCurrentRatio">Current ratio</label> | |||||
<input name="pwCurrentRatio" id="pwCurrentRatio" type="text" class="form-input" size="8" tabindex="16" placeholder="30"> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div> | |||||
<div> | |||||
<button class="btn btn-primary float-right">Update</button> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
</div> | |||||
</div> | |||||
</body> | |||||
</html> |
@ -0,0 +1 @@ | |||||
.iPhoneCheckContainer{-webkit-transform:translate3d(0,0,0);position:relative;height:27px;cursor:pointer;overflow:hidden}.iPhoneCheckContainer input{position:absolute;top:5px;left:30px;filter:alpha(Opacity=0);opacity:0}.iPhoneCheckContainer label,.iPhoneCheckHandle{display:block;height:27px;cursor:pointer;position:absolute;top:0}.iPhoneCheckContainer label{white-space:nowrap;font-size:17px;line-height:17px;font-weight:700;font-family:"Helvetica Neue",Arial,Helvetica,sans-serif;width:auto;padding-top:5px;overflow:hidden}.iPhoneCheckContainer,.iPhoneCheckContainer label{user-select:none;-moz-user-select:none;-khtml-user-select:none}.iPhoneCheckDisabled{filter:alpha(Opacity=50);opacity:.5}label.iPhoneCheckLabelOn{color:#fff;background:url(images/on.png) no-repeat;text-shadow:0 0 2px rgba(0,0,0,.6);left:0;padding-top:5px}label.iPhoneCheckLabelOn span{padding-left:8px}label.iPhoneCheckLabelOff{color:#8b8b8b;background:url(images/off.png) right 0 no-repeat;text-shadow:0 0 2px rgba(255,255,255,.6);text-align:right;right:0}label.iPhoneCheckLabelOff span{padding-right:8px}.iPhoneCheckHandle{left:0;width:0;background:url(images/slider_left.png) no-repeat;padding-left:3px}.iPhoneCheckHandleRight{height:100%;width:100%;padding-right:3px;background:url(images/slider_right.png) right 0 no-repeat}.iPhoneCheckHandleCenter{height:100%;width:100%;background:url(images/slider_center.png)} |
@ -0,0 +1,73 @@ | |||||
.iPhoneCheckContainer { | |||||
-webkit-transform:translate3d(0,0,0); | |||||
position: relative; | |||||
height: 27px; | |||||
cursor: pointer; | |||||
overflow: hidden; } | |||||
.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; } | |||||
label.iPhoneCheckLabelOn { | |||||
color: white; | |||||
background: url('images/on.png') no-repeat; | |||||
text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.6); | |||||
left: 0; | |||||
padding-top: 5px; } | |||||
label.iPhoneCheckLabelOn span { | |||||
padding-left: 8px; } | |||||
label.iPhoneCheckLabelOff { | |||||
color: #8b8b8b; | |||||
background: url('images/off.png') no-repeat right 0; | |||||
text-shadow: 0px 0px 2px rgba(255, 255, 255, 0.6); | |||||
text-align: right; | |||||
right: 0; } | |||||
label.iPhoneCheckLabelOff span { | |||||
padding-right: 8px; } | |||||
.iPhoneCheckHandle { | |||||
display: block; | |||||
height: 27px; | |||||
cursor: pointer; | |||||
position: absolute; | |||||
top: 0; | |||||
left: 0; | |||||
width: 0; | |||||
background: url('images/slider_left.png') no-repeat; | |||||
padding-left: 3px; } | |||||
.iPhoneCheckHandleRight { | |||||
height: 100%; | |||||
width: 100%; | |||||
padding-right: 3px; | |||||
background: url('images/slider_right.png') no-repeat right 0; } | |||||
.iPhoneCheckHandleCenter { | |||||
height: 100%; | |||||
width: 100%; | |||||
background: url('images/slider_center.png'); } |
@ -0,0 +1,350 @@ | |||||
// Generated by CoffeeScript 1.6.2 | |||||
(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) { | |||||
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.onSpan = this.onLabel.children('span'); | |||||
return this.handle = $("<div class='" + this.handleClass + "'>\n <div class='" + this.handleRightClass + "'>\n <div class='" + this.handleCenterClass + "' />\n </div>\n</div>").appendTo(this.container); | |||||
}; | |||||
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; | |||||
return this.handle.css({ | |||||
width: newWidth | |||||
}); | |||||
} | |||||
}; | |||||
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 new_left; | |||||
if (this.isDisabled()) { | |||||
this.container.addClass(this.disabledClass); | |||||
return false; | |||||
} else { | |||||
this.container.removeClass(this.disabledClass); | |||||
} | |||||
new_left = this.elem.prop('checked') ? this.rightSide : 0; | |||||
this.handle.animate({ | |||||
left: new_left | |||||
}, this.duration); | |||||
this.onLabel.animate({ | |||||
width: new_left + this.handleRadius | |||||
}, this.duration); | |||||
this.offSpan.animate({ | |||||
marginRight: -new_left | |||||
}, this.duration); | |||||
return this.onSpan.animate({ | |||||
marginLeft: new_left - 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 | |||||
}); | |||||
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); |
@ -0,0 +1,495 @@ | |||||
<!DOCTYPE html> | |||||
<html> | |||||
<head> | |||||
<title>ESPurna 0.0.0</title> | |||||
<meta charset="utf-8" /> | |||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" /> | |||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||||
<link rel="stylesheet" href="pure-min.css" /> | |||||
<link rel="stylesheet" href="side-menu-min.css" /> | |||||
<link rel="stylesheet" href="grids-responsive-min.css" /> | |||||
<link rel="stylesheet" href="checkboxes-min.css" /> | |||||
<script src="jquery-1.12.3.min.js"></script> | |||||
<script src="checkboxes-min.js"></script> | |||||
<style> | |||||
#menu .pure-menu-heading { | |||||
font-size: 100%; | |||||
padding: .5em .5em; | |||||
} | |||||
.header h2 { | |||||
font-size: 1em; | |||||
} | |||||
.panel { | |||||
display: none; | |||||
} | |||||
.content { | |||||
margin: 0px; | |||||
} | |||||
.page { | |||||
margin-top: 40px; | |||||
} | |||||
.center { | |||||
text-align: center; | |||||
} | |||||
.pure-button { | |||||
color: white; | |||||
padding: 8px 16px; | |||||
border-radius: 4px; | |||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); | |||||
} | |||||
.button-update { | |||||
width: 100px; | |||||
margin: 50px auto; | |||||
background: #1f8dd6; | |||||
} | |||||
.pure-g { | |||||
margin-bottom: 20px; | |||||
} | |||||
legend { | |||||
font-weight: bold; | |||||
} | |||||
.l-box { | |||||
padding-right: 1px; | |||||
} | |||||
#topics .pure-g { | |||||
padding-bottom: 5px; | |||||
margin-bottom: 5px; | |||||
border-bottom: 1px dashed #e5e5e5; | |||||
} | |||||
.pure-form input[type=text][disabled] { | |||||
color: #777777; | |||||
} | |||||
div.hint { | |||||
font-size: 80%; | |||||
color: #ccc; | |||||
margin-top: -5px; | |||||
} | |||||
.iPhoneCheckContainer { | |||||
letter-spacing: 0em; | |||||
height: 36px; | |||||
} | |||||
.iPhoneCheckHandle { | |||||
top: 8px; | |||||
} | |||||
</style> | |||||
<script> | |||||
var update_timer = null; | |||||
var relaySlider; | |||||
function doUpdate() { | |||||
var self = $(this); | |||||
self.addClass("loading"); | |||||
$.ajax({ | |||||
'method': 'POST', | |||||
'url': '/post', | |||||
'dataType': 'json', | |||||
'data': $("#formSave").serializeArray() | |||||
}).done(function(data) { | |||||
self.removeClass("loading"); | |||||
}).fail(function() { | |||||
self.removeClass("loading"); | |||||
}); | |||||
} | |||||
function showPanel() { | |||||
$(".panel").hide(); | |||||
$("#" + $(this).attr("data")).show(); | |||||
if ($("#layout").hasClass('active')) toggleMenu(); | |||||
}; | |||||
function toggleMenu() { | |||||
$("#layout").toggleClass('active'); | |||||
$("#menu").toggleClass('active'); | |||||
$("#menuLink").toggleClass('active'); | |||||
} | |||||
function parseResponse(data) { | |||||
// pre-process | |||||
if ("network" in data) data.network = data.network.toUpperCase(); | |||||
if ("mqttStatus" in data) data.mqttStatus = data.mqttStatus ? "CONNECTED" : "NOT CONNECTED"; | |||||
// relay | |||||
if ("relayStatus" in data) { | |||||
$("input[name='relayStatus']") | |||||
.prop("checked", data.relayStatus) | |||||
.iphoneStyle("refresh"); | |||||
} | |||||
// title | |||||
if ("app" in data) { | |||||
document.title = data.app; | |||||
$(".pure-menu-heading").html(data.app); | |||||
} | |||||
// automatic assign | |||||
Object.keys(data).forEach(function(key) { | |||||
var id = "input[name=" + key + "]"; | |||||
if ($(id).length) $(id).val(data[key]); | |||||
}); | |||||
// WIFI | |||||
var groups = $("#panel-wifi .pure-g"); | |||||
for (var i in data.wifi) { | |||||
var wifi = data.wifi[i]; | |||||
Object.keys(wifi).forEach(function(key) { | |||||
var id = "input[name=" + key + "]"; | |||||
if ($(id, groups[i]).length) $(id, groups[i]).val(wifi[key]); | |||||
}); | |||||
}; | |||||
if ("updateInterval" in data) { | |||||
if (update_timer) clearInterval(update_timer); | |||||
if (data.updateInterval > 0) { | |||||
update_timer = setInterval(update, data.updateInterval); | |||||
} | |||||
} | |||||
} | |||||
function update() { | |||||
$.ajax({ | |||||
'method': 'GET', | |||||
'url': '/status', | |||||
'dataType': 'json' | |||||
}).done(parseResponse); | |||||
} | |||||
function init() { | |||||
$.ajax({ | |||||
'method': 'GET', | |||||
'url': '/get', | |||||
'dataType': 'json' | |||||
}).done(parseResponse); | |||||
} | |||||
$(function() { | |||||
$("#menuLink").on('click', toggleMenu); | |||||
$(".button-update").on('click', doUpdate); | |||||
$(".pure-menu-link").on('click', showPanel); | |||||
relaySlider = $('#relayStatus').iphoneStyle({ | |||||
checkedLabel: 'ON', | |||||
uncheckedLabel: 'OFF', | |||||
onChange: function(elem, value) { | |||||
$.ajax({ | |||||
'method': 'GET', | |||||
'url': value ? '/relay/on' : '/relay/off', | |||||
'dataType': 'json' | |||||
}); | |||||
setTimeout(update, 200); | |||||
} | |||||
}); | |||||
init(); | |||||
}); | |||||
</script> | |||||
</head> | |||||
<body> | |||||
<div id="layout"> | |||||
<a href="#menu" id="menuLink" class="menu-link"> | |||||
<span></span> | |||||
</a> | |||||
<div id="menu"> | |||||
<div class="pure-menu"> | |||||
<span class="pure-menu-heading">ESPurna 0.0.0</span> | |||||
<ul class="pure-menu-list"> | |||||
<li class="pure-menu-item"> | |||||
<a href="#" class="pure-menu-link" data="panel-status">STATUS</a> | |||||
</li> | |||||
<li class="pure-menu-item"> | |||||
<a href="#" class="pure-menu-link" data="panel-wifi">WIFI</a> | |||||
</li> | |||||
<li class="pure-menu-item"> | |||||
<a href="#" class="pure-menu-link" data="panel-mqtt">MQTT</a> | |||||
</li> | |||||
<li class="pure-menu-item"> | |||||
<a href="#" class="pure-menu-link" data="panel-rf">RF</a> | |||||
</li> | |||||
<li class="pure-menu-item"> | |||||
<a href="#" class="pure-menu-link" data="panel-power">POWER</a> | |||||
</li> | |||||
</ul> | |||||
<div class="center"> | |||||
<button class="pure-button button-update">Update</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="content"> | |||||
<div class="panel" id="panel-status" style="display: block;"> | |||||
<div class="header"> | |||||
<h1>STATUS</h1> | |||||
<h2>Current configuration</h2> | |||||
</div> | |||||
<div class="page"> | |||||
<form class="pure-form pure-form-aligned"> | |||||
<fieldset> | |||||
<div class="pure-g"> | |||||
<label class="pure-u-1 pure-u-sm-1-4" for="manufacturer">Manufacturer</label> | |||||
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="manufacturer" disabled> | |||||
</div> | |||||
<div class="pure-g"> | |||||
<label class="pure-u-1 pure-u-sm-1-4" for="device">Device</label> | |||||
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="device" disabled> | |||||
</div> | |||||
<div class="pure-g"> | |||||
<label class="pure-u-1 pure-u-sm-1-4" for="hostname">Hostname</label> | |||||
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="hostname" disabled> | |||||
</div> | |||||
<div class="pure-g"> | |||||
<label class="pure-u-1 pure-u-sm-1-4" for="network">Network</label> | |||||
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="network" disabled> | |||||
</div> | |||||
<div class="pure-g"> | |||||
<label class="pure-u-1 pure-u-sm-1-4" for="ip">IP</label> | |||||
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="ip" disabled> | |||||
</div> | |||||
<div class="pure-g"> | |||||
<label class="pure-u-1 pure-u-sm-1-4" for="mqtt">MQTT Status</label> | |||||
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="mqttStatus" disabled> | |||||
</div> | |||||
<div class="pure-g"> | |||||
<label class="pure-u-1 pure-u-sm-1-4" for="temperature">Temperature</label> | |||||
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="temperature" disabled> | |||||
</div> | |||||
<div class="pure-g"> | |||||
<label class="pure-u-1 pure-u-sm-1-4" for="humidity">Humidity</label> | |||||
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="humidity" disabled> | |||||
</div> | |||||
<div class="pure-g"> | |||||
<label class="pure-u-1 pure-u-sm-1-4" for="power">Power</label> | |||||
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="power" disabled> | |||||
</div> | |||||
<div class="pure-g"> | |||||
<label class="pure-u-1 pure-u-sm-1-4" for="relayStatus">Relay Status</label> | |||||
<input class="pure-u-1 pure-u-sm-3-4" type="checkbox" checked="checked" name="relayStatus" id="relayStatus"/> | |||||
</div> | |||||
</fieldset> | |||||
</form> | |||||
</div> | |||||
</div> | |||||
<form id="formSave" class="pure-form pure-form-stacked" action="/" method="post"> | |||||
<div class="panel" id="panel-wifi"> | |||||
<div class="header"> | |||||
<h1>WIFI</h1> | |||||
<h2>You can configure up to 3 different WiFi networks. The device will try to connect to any of them starting with the first one.</h2> | |||||
</div> | |||||
<div class="page"> | |||||
<fieldset> | |||||
<legend>First network</legend> | |||||
<div class="pure-g"> | |||||
<div class="pure-u-1 pure-u-md-1-2"> | |||||
<label for="ssid">SSID</label> | |||||
<input type="text" class="pure-u-23-24" tabindex="10" name="ssid"> | |||||
</div> | |||||
<div class="pure-u-1 pure-u-md-1-2"> | |||||
<label for="pass">Password</label> | |||||
<input type="text" class="pure-u-23-24" tabindex="11" name="pass"> | |||||
</div> | |||||
</div> | |||||
<legend>Second network</legend> | |||||
<div class="pure-g"> | |||||
<div class="pure-u-1 pure-u-md-1-2"> | |||||
<label for="ssid">SSID</label> | |||||
<input type="text" class="pure-u-23-24" tabindex="12" name="ssid"> | |||||
</div> | |||||
<div class="pure-u-1 pure-u-md-1-2"> | |||||
<label for="pass">Password</label> | |||||
<input type="text" class="pure-u-23-24" tabindex="13" name="pass"> | |||||
</div> | |||||
</div> | |||||
<legend>Third network</legend> | |||||
<div class="pure-g"> | |||||
<div class="pure-u-1 pure-u-md-1-2"> | |||||
<label for="ssid">SSID</label> | |||||
<input type="text" class="pure-u-23-24" tabindex="14" name="ssid"> | |||||
</div> | |||||
<div class="pure-u-1 pure-u-md-1-2"> | |||||
<label for="pass">Password</label> | |||||
<input type="text" class="pure-u-23-24" tabindex="15" name="pass"> | |||||
</div> | |||||
</div> | |||||
</fieldset> | |||||
</div> | |||||
</div> | |||||
<div class="panel" id="panel-mqtt"> | |||||
<div class="header"> | |||||
<h1>MQTT</h1> | |||||
<h2>Configure an <strong>MQTT broker</strong> in your network and you will be able to change the switch status via an MQTT message. Leave the server field empty to disable MQTT.</h2> | |||||
</div> | |||||
<div class="page"> | |||||
<fieldset> | |||||
<div class="pure-g"> | |||||
<div class="pure-u-1"> | |||||
<label class="form-label" for="mqttServer">MQTT Server</label> | |||||
<input name="mqttServer" type="text" class="pure-u-23-24" value="" size="20" tabindex="10" placeholder="MQTT Server"> | |||||
</div> | |||||
<div class="pure-u-1"> | |||||
<label class="form-label" for="mqttPort">MQTT Port</label> | |||||
<input name="mqttPort" type="text" class="pure-u-23-24" value="" size="21" tabindex="11" placeholder="1883"> | |||||
</div> | |||||
<div class="pure-u-1"> | |||||
<label class="form-label" for="mqttUser">MQTT User</label> | |||||
<input name="mqttUser" type="text" class="pure-u-23-24" value="" size="22" tabindex="12" placeholder="Leave blank if no user/pass"> | |||||
</div> | |||||
<div class="pure-u-1"> | |||||
<label class="form-label" for="mqttPassword">MQTT Password</label> | |||||
<input name="mqttPassword" type="text" class="pure-u-23-24" value="" size="23" tabindex="13" placeholder="Leave blank if no user/pass"> | |||||
</div> | |||||
<div class="pure-u-1"> | |||||
<label class="form-label" for="mqttTopic">MQTT Topic</label> | |||||
<div class="hint">Send a 0 or a 1 as a payload to the provided topic below to switch it on or off. You can also send a 2 to toggle its current state. The switch will also report its current open/close status to the same topic and its IP address, hertbeat, firmware version and file system version to the topic you define plus "/ip", "/heartbeat", "/version" and "/fsversion" respectively. </div> | |||||
<input name="mqttTopic" type="text" class="pure-u-23-24" value="" size="24" tabindex="13" placeholder="Leave blank if no user/pass"> | |||||
</div> | |||||
</div> | |||||
</fieldset> | |||||
</div> | |||||
</div> | |||||
<div class="panel" id="panel-rf"> | |||||
<div class="header"> | |||||
<h1>RADIO</h1> | |||||
<h2> | |||||
Configure your radio channel and device ID. | |||||
</h2> | |||||
</div> | |||||
<div class="page"> | |||||
<fieldset> | |||||
<div class="pure-g"> | |||||
<div class="pure-u-1"> | |||||
<label class="form-label" for="rfChannel">Channel</label> | |||||
<div class="hint">This is the 5 bits code (0-31) you define on your remote, usually from a DIP switch.</div> | |||||
<input name="rfChannel" type="number" class="pure-u-1-4" min="0" max="30" step="1" tabindex="13"/> | |||||
</div> | |||||
<div class="pure-u-1"> | |||||
<label class="form-label" for="rfDevice">Device</label> | |||||
<div class="hint">This is the button in your remote that will trigger the switch</div> | |||||
<select name="rfDevice" class="pure-u-1-4" tabindex="31"> | |||||
<option value="0">A</a> | |||||
<option value="1">B</a> | |||||
<option value="2">C</a> | |||||
<option value="3">D</a> | |||||
<option value="4">E</a> | |||||
</select> | |||||
</div> | |||||
</div> | |||||
</fieldset> | |||||
</div> | |||||
</div> | |||||
<div class="panel" id="panel-power"> | |||||
<div class="header"> | |||||
<h1>POWER</h1> | |||||
<h2> | |||||
Configure your power monitor variables. | |||||
</h2> | |||||
</div> | |||||
<div class="page"> | |||||
<fieldset> | |||||
<div class="pure-g"> | |||||
<div class="pure-u-1"> | |||||
<label class="form-label" for="pwMainsVoltage">AC RMS Voltage</label> | |||||
<div class="hint">This is your house nominal voltage, you probably know this or you wont be playing with this device...</div> | |||||
<select name="pwMainsVoltage" class="pure-u-1-4" tabindex="40"> | |||||
<option value="125">125</a> | |||||
<option value="220">220</a> | |||||
<option value="230">230</a> | |||||
<option value="240">240</a> | |||||
</select> | |||||
</div> | |||||
<div class="pure-u-1"> | |||||
<label class="form-label" for="pwCurrentRatio">Current Ratio</label> | |||||
<div class="hint">This is the value in amps for a 1V output for your sensor. Some current sensors like the YHDC SCT-013-030 have it written in the enclosure: 30A 1V. If you are using a current sensor that outputs a current (no built in burden resistor) it will depend on the turns ratio between the primary and secondary coils in the sensor and the burden resistor you use. Check about this constant in the <a href="https://openenergymonitor.org/emon/buildingblocks/calibration" target="_blank">post about calibration</a> in the Open Energy Monitor site.</div> | |||||
<input name="pwCurrentRatio" type="text" class="pure-u-1-4" value="" size="8" tabindex="41" placeholder="0"> | |||||
</div> | |||||
</div> | |||||
</fieldset> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
</div> <!-- content --> | |||||
</div> <!-- layout --> | |||||
</body> | |||||
</html> |
@ -0,0 +1 @@ | |||||
body{color:#777}.pure-img-responsive{max-width:100%;height:auto}#layout,#menu,.menu-link{-webkit-transition:all 0.2s ease-out;-moz-transition:all 0.2s ease-out;-ms-transition:all 0.2s ease-out;-o-transition:all 0.2s ease-out;transition:all 0.2s ease-out}#layout{position:relative;padding-left:0}#layout.active #menu{left:150px;width:150px}#layout.active .menu-link{left:150px}.content{margin:0 auto;padding:0 2em;max-width:800px;margin-bottom:50px;line-height:1.6em}.header{margin:0;color:#333;text-align:center;padding:2.5em 2em 0;border-bottom:1px solid #eee}.header h1{margin:0.2em 0;font-size:3em;font-weight:300}.header h2{font-weight:300;color:#ccc;padding:0;margin-top:0}.content-subhead{margin:50px 0 20px 0;font-weight:300;color:#888}#menu{margin-left:-150px;width:150px;position:fixed;top:0;left:0;bottom:0;z-index:1000;background:#191818;overflow-y:auto;-webkit-overflow-scrolling:touch}#menu a{color:#999;border:none;padding:0.6em 0 0.6em 0.6em}#menu .pure-menu,#menu .pure-menu ul{border:none;background:transparent}#menu .pure-menu ul,#menu .pure-menu .menu-item-divided{border-top:1px solid #333}#menu .pure-menu li a:hover,#menu .pure-menu li a:focus{background:#333}#menu .pure-menu-selected,#menu .pure-menu-heading{background:#1f8dd6}#menu .pure-menu-selected a{color:#fff}#menu .pure-menu-heading{font-size:110%;color:#fff;margin:0}.menu-link{position:fixed;display:block;top:0;left:0;background:#000;background:rgba(0,0,0,0.7);font-size:10px;z-index:10;width:2em;height:auto;padding:2.1em 1.6em}.menu-link:hover,.menu-link:focus{background:#000}.menu-link span{position:relative;display:block}.menu-link span,.menu-link span:before,.menu-link span:after{background-color:#fff;width:100%;height:0.2em}.menu-link span:before,.menu-link span:after{position:absolute;margin-top:-0.6em;content:" "}.menu-link span:after{margin-top:0.6em}@media (min-width: 48em){.header,.content{padding-left:2em;padding-right:2em}#layout{padding-left:150px;left:0}#menu{left:150px}.menu-link{position:fixed;left:150px;display:none}#layout.active .menu-link{left:150px}}@media (max-width: 48em){#layout.active{position:relative;left:150px}} |
@ -0,0 +1,248 @@ | |||||
body { | |||||
color: #777; | |||||
} | |||||
.pure-img-responsive { | |||||
max-width: 100%; | |||||
height: auto; | |||||
} | |||||
/* | |||||
Add transition to containers so they can push in and out. | |||||
*/ | |||||
#layout, | |||||
#menu, | |||||
.menu-link { | |||||
-webkit-transition: all 0.2s ease-out; | |||||
-moz-transition: all 0.2s ease-out; | |||||
-ms-transition: all 0.2s ease-out; | |||||
-o-transition: all 0.2s ease-out; | |||||
transition: all 0.2s ease-out; | |||||
} | |||||
/* | |||||
This is the parent `<div>` that contains the menu and the content area. | |||||
*/ | |||||
#layout { | |||||
position: relative; | |||||
padding-left: 0; | |||||
} | |||||
#layout.active #menu { | |||||
left: 150px; | |||||
width: 150px; | |||||
} | |||||
#layout.active .menu-link { | |||||
left: 150px; | |||||
} | |||||
/* | |||||
The content `<div>` is where all your content goes. | |||||
*/ | |||||
.content { | |||||
margin: 0 auto; | |||||
padding: 0 2em; | |||||
max-width: 800px; | |||||
margin-bottom: 50px; | |||||
line-height: 1.6em; | |||||
} | |||||
.header { | |||||
margin: 0; | |||||
color: #333; | |||||
text-align: center; | |||||
padding: 2.5em 2em 0; | |||||
border-bottom: 1px solid #eee; | |||||
} | |||||
.header h1 { | |||||
margin: 0.2em 0; | |||||
font-size: 3em; | |||||
font-weight: 300; | |||||
} | |||||
.header h2 { | |||||
font-weight: 300; | |||||
color: #ccc; | |||||
padding: 0; | |||||
margin-top: 0; | |||||
} | |||||
.content-subhead { | |||||
margin: 50px 0 20px 0; | |||||
font-weight: 300; | |||||
color: #888; | |||||
} | |||||
/* | |||||
The `#menu` `<div>` is the parent `<div>` that contains the `.pure-menu` that | |||||
appears on the left side of the page. | |||||
*/ | |||||
#menu { | |||||
margin-left: -150px; /* "#menu" width */ | |||||
width: 150px; | |||||
position: fixed; | |||||
top: 0; | |||||
left: 0; | |||||
bottom: 0; | |||||
z-index: 1000; /* so the menu or its navicon stays above all content */ | |||||
background: #191818; | |||||
overflow-y: auto; | |||||
-webkit-overflow-scrolling: touch; | |||||
} | |||||
/* | |||||
All anchors inside the menu should be styled like this. | |||||
*/ | |||||
#menu a { | |||||
color: #999; | |||||
border: none; | |||||
padding: 0.6em 0 0.6em 0.6em; | |||||
} | |||||
/* | |||||
Remove all background/borders, since we are applying them to #menu. | |||||
*/ | |||||
#menu .pure-menu, | |||||
#menu .pure-menu ul { | |||||
border: none; | |||||
background: transparent; | |||||
} | |||||
/* | |||||
Add that light border to separate items into groups. | |||||
*/ | |||||
#menu .pure-menu ul, | |||||
#menu .pure-menu .menu-item-divided { | |||||
border-top: 1px solid #333; | |||||
} | |||||
/* | |||||
Change color of the anchor links on hover/focus. | |||||
*/ | |||||
#menu .pure-menu li a:hover, | |||||
#menu .pure-menu li a:focus { | |||||
background: #333; | |||||
} | |||||
/* | |||||
This styles the selected menu item `<li>`. | |||||
*/ | |||||
#menu .pure-menu-selected, | |||||
#menu .pure-menu-heading { | |||||
background: #1f8dd6; | |||||
} | |||||
/* | |||||
This styles a link within a selected menu item `<li>`. | |||||
*/ | |||||
#menu .pure-menu-selected a { | |||||
color: #fff; | |||||
} | |||||
/* | |||||
This styles the menu heading. | |||||
*/ | |||||
#menu .pure-menu-heading { | |||||
font-size: 110%; | |||||
color: #fff; | |||||
margin: 0; | |||||
} | |||||
/* -- Dynamic Button For Responsive Menu -------------------------------------*/ | |||||
/* | |||||
The button to open/close the Menu is custom-made and not part of Pure. Here's | |||||
how it works: | |||||
*/ | |||||
/* | |||||
`.menu-link` represents the responsive menu toggle that shows/hides on | |||||
small screens. | |||||
*/ | |||||
.menu-link { | |||||
position: fixed; | |||||
display: block; /* show this only on small screens */ | |||||
top: 0; | |||||
left: 0; /* "#menu width" */ | |||||
background: #000; | |||||
background: rgba(0,0,0,0.7); | |||||
font-size: 10px; /* change this value to increase/decrease button size */ | |||||
z-index: 10; | |||||
width: 2em; | |||||
height: auto; | |||||
padding: 2.1em 1.6em; | |||||
} | |||||
.menu-link:hover, | |||||
.menu-link:focus { | |||||
background: #000; | |||||
} | |||||
.menu-link span { | |||||
position: relative; | |||||
display: block; | |||||
} | |||||
.menu-link span, | |||||
.menu-link span:before, | |||||
.menu-link span:after { | |||||
background-color: #fff; | |||||
width: 100%; | |||||
height: 0.2em; | |||||
} | |||||
.menu-link span:before, | |||||
.menu-link span:after { | |||||
position: absolute; | |||||
margin-top: -0.6em; | |||||
content: " "; | |||||
} | |||||
.menu-link span:after { | |||||
margin-top: 0.6em; | |||||
} | |||||
/* -- Responsive Styles (Media Queries) ------------------------------------- */ | |||||
/* | |||||
Hides the menu at `48em`, but modify this based on your app's needs. | |||||
*/ | |||||
@media (min-width: 48em) { | |||||
.header, | |||||
.content { | |||||
padding-left: 2em; | |||||
padding-right: 2em; | |||||
} | |||||
#layout { | |||||
padding-left: 150px; /* left col width "#menu" */ | |||||
left: 0; | |||||
} | |||||
#menu { | |||||
left: 150px; | |||||
} | |||||
.menu-link { | |||||
position: fixed; | |||||
left: 150px; | |||||
display: none; | |||||
} | |||||
#layout.active .menu-link { | |||||
left: 150px; | |||||
} | |||||
} | |||||
@media (max-width: 48em) { | |||||
/* Only apply this when the window is small. Otherwise, the following | |||||
case results in extra padding on the left: | |||||
* Make the window small. | |||||
* Tap the menu to trigger the active state. | |||||
* Make the window large again. | |||||
*/ | |||||
#layout.active { | |||||
position: relative; | |||||
left: 150px; | |||||
} | |||||
} | |||||