mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-03-26 22:22:16 +03:00
61
.vscode/settings.json
vendored
61
.vscode/settings.json
vendored
@@ -1,66 +1,7 @@
|
||||
{
|
||||
"terminal.integrated.env.windows": {
|
||||
"PATH": "C:\\Users\\ytrikoz\\.platformio\\penv\\Scripts;C:\\Users\\ytrikoz\\.platformio\\penv;C:\\Program Files (x86)\\Borland\\Delphi7\\Bin;C:\\Program Files (x86)\\Borland\\Delphi7\\Projects\\Bpl\\;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;C:\\WINDOWS\\System32\\OpenSSH\\;C:\\Program Files\\Git\\cmd\\;c:\\android-studio\\jre\\jre\\bin\\;C:\\Program Files (x86)\\Microsoft SQL Server\\150\\DTS\\Binn\\;C:\\Front\\Shared\\;C:\\Program Files (x86)\\Plantronics\\Spokes3G\\;C:\\Program Files\\Microsoft SQL Server\\130\\Tools\\Binn\\;C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\170\\Tools\\Binn\\;C:\\Program Files\\nodejs\\;C:\\ProgramData\\chocolatey\\bin;C:\\Program Files\\dotnet\\;C:\\Users\\ytrikoz\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\ytrikoz\\AppData\\Local\\Programs\\Microsoft VS Code\\bin;C:\\Users\\ytrikoz\\AppData\\Roaming\\npm;C:\\MinGW\\bin;;C:\\Program Files (x86)\\West Wind Html Help Builder;C:\\Program Files (x86)\\Borland\\Delphi7\\Bin;C:\\Program Files (x86)\\Borland\\Delphi7\\Projects\\Bpl\\;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;C:\\WINDOWS\\System32\\OpenSSH\\;C:\\Program Files\\Git\\cmd\\;c:\\android-studio\\jre\\jre\\bin\\;C:\\Program Files (x86)\\Microsoft SQL Server\\150\\DTS\\Binn\\;C:\\Front\\Shared\\;C:\\Program Files (x86)\\Plantronics\\Spokes3G\\;C:\\Program Files\\Microsoft SQL Server\\130\\Tools\\Binn\\;C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\170\\Tools\\Binn\\;C:\\Program Files\\nodejs\\;C:\\ProgramData\\chocolatey\\bin;C:\\Program Files\\dotnet\\;C:\\Users\\ytrikoz\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\ytrikoz\\AppData\\Local\\Programs\\Microsoft VS Code\\bin;C:\\Users\\ytrikoz\\AppData\\Roaming\\npm;C:\\MinGW\\bin;;C:\\Program Files (x86)\\West Wind Html Help Builder",
|
||||
"PLATFORMIO_CALLER": "vscode"
|
||||
},
|
||||
"files.associations": {
|
||||
"*.cpp": "cpp",
|
||||
"functional": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"optional": "cpp",
|
||||
"ratio": "cpp",
|
||||
"system_error": "cpp",
|
||||
"array": "cpp",
|
||||
"regex": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"bitset": "cpp",
|
||||
"chrono": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"random": "cpp",
|
||||
"atomic": "cpp",
|
||||
"cctype": "cpp",
|
||||
"cerrno": "cpp",
|
||||
"cfloat": "cpp",
|
||||
"climits": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"map": "cpp",
|
||||
"set": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"numeric": "cpp",
|
||||
"fstream": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"ios": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"locale": "cpp",
|
||||
"new": "cpp",
|
||||
"ostream": "cpp",
|
||||
"queue": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stack": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"cstdbool": "cpp",
|
||||
"typeinfo": "cpp"
|
||||
"algorithm": "cpp"
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
"chipID": "",
|
||||
"apssid": "IoTmanager",
|
||||
"appass": "",
|
||||
"routerssid": "rise",
|
||||
"routerpass": "hostel3333",
|
||||
"routerssid": "home",
|
||||
"routerpass": "kisa3333",
|
||||
"timezone": 2,
|
||||
"ntp": "pool.ntp.org",
|
||||
"mqttServer": "91.204.228.124",
|
||||
@@ -17,6 +17,6 @@
|
||||
"weblogin": "admin",
|
||||
"webpass": "admin",
|
||||
"udponoff": "1",
|
||||
"blink": "1",
|
||||
"blink": "0",
|
||||
"oneWirePin": "2"
|
||||
}
|
||||
Binary file not shown.
@@ -1,12 +0,0 @@
|
||||
button 1 na Включить#все Реле 0 1
|
||||
button 2 13 Прихожая Реле 0 2
|
||||
button 3 14 Кухня Реле 0 3
|
||||
pwm 1 3 Яркость#коредор: Реле 1023 4
|
||||
pwm 2 4 Яркость#ванная: Реле 510 5
|
||||
analog adc 0 Аналоговый#вход Датчики fillgauge 1 1023 1 1023 6
|
||||
logging adc 1 100 Аналоговый#вход Датчики 7
|
||||
|
||||
//Это демо конфигурация. В ней показано как связать кнопки c помощью сценариев
|
||||
//Кнопка номер 1 связана с кнопкой 2, 3 и с pwm 2
|
||||
//Так же продемонстрированна система логгирования данных строкой logging
|
||||
//1 - это интервал между точками в минутах, 100 это количество точек
|
||||
@@ -1,13 +0,0 @@
|
||||
button1 = 1
|
||||
buttonSet 2 1
|
||||
buttonSet 3 1
|
||||
pwmSet 2 1024
|
||||
end
|
||||
button1 = 0
|
||||
buttonSet 2 0
|
||||
buttonSet 3 0
|
||||
pwmSet 2 0
|
||||
end
|
||||
adc > 50
|
||||
buttonSet 2 1
|
||||
end
|
||||
658
data/edit.htm
Normal file
658
data/edit.htm
Normal file
@@ -0,0 +1,658 @@
|
||||
<!--This is the plain html source of the hex encoded Editor-Page embedded in SPIFFSEditor.cpp -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>FS Editor</title>
|
||||
<style type="text/css" media="screen">
|
||||
.cm {
|
||||
z-index: 300;
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
border: 1px solid #444;
|
||||
background-color: #F5F5F5;
|
||||
display: none;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, .4);
|
||||
font-size: 12px;
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm ul {
|
||||
list-style: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.cm li {
|
||||
position: relative;
|
||||
min-width: 60px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cm span {
|
||||
color: #444;
|
||||
display: inline-block;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.cm li:hover {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.cm li:hover span {
|
||||
color: #EEE;
|
||||
}
|
||||
|
||||
.tvu ul,
|
||||
.tvu li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.tvu input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tvu {
|
||||
font: normal 12px Verdana, Arial, Sans-serif;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
color: #444;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.tvu span {
|
||||
margin-bottom: 5px;
|
||||
padding: 0 0 0 18px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADoSURBVBgZBcExblNBGAbA2ceegTRBuIKOgiihSZNTcC5LUHAihNJR0kGKCDcYJY6D3/77MdOinTvzAgCw8ysThIvn/VojIyMjIyPP+bS1sUQIV2s95pBDDvmbP/mdkft83tpYguZq5Jh/OeaYh+yzy8hTHvNlaxNNczm+la9OTlar1UdA/+C2A4trRCnD3jS8BB1obq2Gk6GU6QbQAS4BUaYSQAf4bhhKKTFdAzrAOwAxEUAH+KEM01SY3gM6wBsEAQB0gJ+maZoC3gI6iPYaAIBJsiRmHU0AALOeFC3aK2cWAACUXe7+AwO0lc9eTHYTAAAAAElFTkSuQmCC') no-repeat;
|
||||
background-position: 0px 0px;
|
||||
}
|
||||
|
||||
.tvu span:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
.tvu {
|
||||
-webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes webkit-adjacent-element-selector-bugfix {
|
||||
from {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#uploader {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 28px;
|
||||
line-height: 24px;
|
||||
padding-left: 10px;
|
||||
background-color: #444;
|
||||
color: #EEE;
|
||||
}
|
||||
|
||||
#tree {
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 160px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
#editor,
|
||||
#preview {
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 160px;
|
||||
border-left: 1px solid #EEE;
|
||||
}
|
||||
|
||||
#preview {
|
||||
background-color: #EEE;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#loader {
|
||||
position: absolute;
|
||||
top: 36%;
|
||||
right: 40%;
|
||||
}
|
||||
|
||||
.loader {
|
||||
z-index: 10000;
|
||||
border: 8px solid #b5b5b5;
|
||||
/* Grey */
|
||||
border-top: 8px solid #3498db;
|
||||
/* Blue */
|
||||
border-bottom: 8px solid #3498db;
|
||||
/* Blue */
|
||||
border-radius: 50%;
|
||||
width: 240px;
|
||||
height: 240px;
|
||||
animation: spin 2s linear infinite;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
if (typeof XMLHttpRequest === "undefined") {
|
||||
XMLHttpRequest = function () {
|
||||
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) { }
|
||||
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) { }
|
||||
try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { }
|
||||
throw new Error("This browser does not support XMLHttpRequest.");
|
||||
};
|
||||
}
|
||||
|
||||
function ge(a) {
|
||||
return document.getElementById(a);
|
||||
}
|
||||
function ce(a) {
|
||||
return document.createElement(a);
|
||||
}
|
||||
|
||||
function sortByKey(array, key) {
|
||||
return array.sort(function (a, b) {
|
||||
var x = a[key]; var y = b[key];
|
||||
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var QueuedRequester = function () {
|
||||
this.queue = [];
|
||||
this.running = false;
|
||||
this.xmlhttp = null;
|
||||
}
|
||||
QueuedRequester.prototype = {
|
||||
_request: function (req) {
|
||||
this.running = true;
|
||||
if (!req instanceof Object) return;
|
||||
var that = this;
|
||||
|
||||
function ajaxCb(x, d) {
|
||||
return function () {
|
||||
if (x.readyState == 4) {
|
||||
ge("loader").style.display = "none";
|
||||
d.callback(x.status, x.responseText);
|
||||
if (that.queue.length === 0) that.running = false;
|
||||
if (that.running) that._request(that.queue.shift());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ge("loader").style.display = "block";
|
||||
|
||||
var p = "";
|
||||
if (req.params instanceof FormData) {
|
||||
p = req.params;
|
||||
} else if (req.params instanceof Object) {
|
||||
for (var key in req.params) {
|
||||
if (p === "")
|
||||
p += (req.method === "GET") ? "?" : "";
|
||||
else
|
||||
p += "&";
|
||||
p += encodeURIComponent(key) + "=" + encodeURIComponent(req.params[key]);
|
||||
};
|
||||
}
|
||||
|
||||
this.xmlhttp = new XMLHttpRequest();
|
||||
this.xmlhttp.onreadystatechange = ajaxCb(this.xmlhttp, req);
|
||||
if (req.method === "GET") {
|
||||
this.xmlhttp.open(req.method, req.url + p, true);
|
||||
this.xmlhttp.send();
|
||||
} else {
|
||||
this.xmlhttp.open(req.method, req.url, true);
|
||||
if (p instanceof String)
|
||||
this.xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
this.xmlhttp.send(p);
|
||||
}
|
||||
},
|
||||
stop: function () {
|
||||
if (this.running) this.running = false;
|
||||
if (this.xmlhttp && this.xmlhttp.readyState < 4) {
|
||||
this.xmlhttp.abort();
|
||||
}
|
||||
},
|
||||
add: function (method, url, params, callback) {
|
||||
this.queue.push({ url: url, method: method, params: params, callback: callback });
|
||||
if (!this.running) {
|
||||
this._request(this.queue.shift());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var requests = new QueuedRequester();
|
||||
|
||||
function createFileUploader(element, tree, editor) {
|
||||
var xmlHttp;
|
||||
|
||||
var refresh = ce("button");
|
||||
refresh.innerHTML = 'Refresh List';
|
||||
ge(element).appendChild(refresh);
|
||||
|
||||
var input = ce("input");
|
||||
input.type = "file";
|
||||
input.multiple = false;
|
||||
input.name = "data";
|
||||
input.id = "upload-select";
|
||||
ge(element).appendChild(input);
|
||||
|
||||
var path = ce("input");
|
||||
path.id = "upload-path";
|
||||
path.type = "text";
|
||||
path.name = "path";
|
||||
path.defaultValue = "/";
|
||||
ge(element).appendChild(path);
|
||||
|
||||
var button = ce("button");
|
||||
button.innerHTML = 'Upload';
|
||||
ge(element).appendChild(button);
|
||||
|
||||
var mkfile = ce("button");
|
||||
mkfile.innerHTML = 'Create';
|
||||
ge(element).appendChild(mkfile);
|
||||
|
||||
var filename = ce("input");
|
||||
filename.id = "editor-filename";
|
||||
filename.type = "text";
|
||||
filename.disabled = true;
|
||||
filename.size = 20;
|
||||
ge(element).appendChild(filename);
|
||||
|
||||
var savefile = ce("button");
|
||||
savefile.innerHTML = ' Save ';
|
||||
ge(element).appendChild(savefile);
|
||||
|
||||
function httpPostProcessRequest(status, responseText) {
|
||||
if (status != 200)
|
||||
alert("ERROR[" + status + "]: " + responseText);
|
||||
else
|
||||
tree.refreshPath(path.value);
|
||||
}
|
||||
function createPath(p) {
|
||||
var formData = new FormData();
|
||||
formData.append("path", p);
|
||||
requests.add("PUT", "/edit", formData, httpPostProcessRequest);
|
||||
}
|
||||
|
||||
mkfile.onclick = function (e) {
|
||||
createPath(path.value);
|
||||
editor.loadUrl(path.value);
|
||||
path.value = "/";
|
||||
};
|
||||
|
||||
savefile.onclick = function (e) {
|
||||
editor.execCommand('saveCommand');
|
||||
};
|
||||
|
||||
refresh.onclick = function (e) {
|
||||
tree.refreshPath(path.value);
|
||||
};
|
||||
|
||||
button.onclick = function (e) {
|
||||
if (input.files.length === 0) {
|
||||
return;
|
||||
}
|
||||
var formData = new FormData();
|
||||
formData.append("data", input.files[0], path.value);
|
||||
requests.add("POST", "/edit", formData, httpPostProcessRequest);
|
||||
var uploadPath = ge("upload-path");
|
||||
uploadPath.value = "/";
|
||||
var uploadSelect = ge("upload-select");
|
||||
uploadSelect.value = "";
|
||||
};
|
||||
input.onchange = function (e) {
|
||||
if (input.files.length === 0) return;
|
||||
var filename = input.files[0].name;
|
||||
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
|
||||
var name = /(.*)\.[^.]+$/.exec(filename)[1];
|
||||
if (typeof name !== undefined) {
|
||||
filename = name;
|
||||
}
|
||||
path.value = "/" + filename + "." + ext;
|
||||
};
|
||||
}
|
||||
|
||||
function createTree(element, editor) {
|
||||
var preview = ge("preview");
|
||||
var treeRoot = ce("div");
|
||||
treeRoot.className = "tvu";
|
||||
ge(element).appendChild(treeRoot);
|
||||
|
||||
function loadDownload(path) {
|
||||
ge('download-frame').src = "/edit?download=" + path;
|
||||
}
|
||||
|
||||
function loadPreview(path) {
|
||||
var edfname = ge("editor-filename");
|
||||
edfname.value = path;
|
||||
ge("editor").style.display = "none";
|
||||
preview.style.display = "block";
|
||||
preview.innerHTML = '<img src="/edit?edit=' + path + '&_cb=' + Date.now() + '" style="max-width:100%; max-height:100%; margin:auto; display:block;" />';
|
||||
}
|
||||
|
||||
function fillFileMenu(el, path) {
|
||||
var list = ce("ul");
|
||||
el.appendChild(list);
|
||||
var action = ce("li");
|
||||
list.appendChild(action);
|
||||
if (isImageFile(path)) {
|
||||
action.innerHTML = "<span>Preview</span>";
|
||||
action.onclick = function (e) {
|
||||
loadPreview(path);
|
||||
if (document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
|
||||
};
|
||||
} else if (isTextFile(path)) {
|
||||
action.innerHTML = "<span>Edit</span>";
|
||||
action.onclick = function (e) {
|
||||
editor.loadUrl(path);
|
||||
if (document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
|
||||
};
|
||||
}
|
||||
var download = ce("li");
|
||||
list.appendChild(download);
|
||||
download.innerHTML = "<span>Download</span>";
|
||||
download.onclick = function (e) {
|
||||
loadDownload(path);
|
||||
if (document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
|
||||
};
|
||||
var delFile = ce("li");
|
||||
list.appendChild(delFile);
|
||||
delFile.innerHTML = "<span>Delete</span>";
|
||||
delFile.onclick = function (e) {
|
||||
httpDelete(path);
|
||||
if (document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
|
||||
};
|
||||
}
|
||||
|
||||
function showContextMenu(event, path, isfile) {
|
||||
var divContext = ce("div");
|
||||
var scrollTop = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
|
||||
var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft;
|
||||
var left = event.clientX + scrollLeft;
|
||||
var top = event.clientY + scrollTop;
|
||||
divContext.className = 'cm';
|
||||
divContext.style.display = 'block';
|
||||
divContext.style.left = left + 'px';
|
||||
divContext.style.top = top + 'px';
|
||||
fillFileMenu(divContext, path);
|
||||
document.body.appendChild(divContext);
|
||||
var width = divContext.offsetWidth;
|
||||
var height = divContext.offsetHeight;
|
||||
divContext.onmouseout = function (e) {
|
||||
if (e.clientX < left || e.clientX > (left + width) || e.clientY < top || e.clientY > (top + height)) {
|
||||
if (document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(divContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createTreeLeaf(path, name, size) {
|
||||
var leaf = ce("li");
|
||||
leaf.id = name;
|
||||
var label = ce("span");
|
||||
label.innerHTML = name;
|
||||
leaf.appendChild(label);
|
||||
leaf.onclick = function (e) {
|
||||
if (isTextFile(leaf.id.toLowerCase())) {
|
||||
editor.loadUrl(leaf.id);
|
||||
} else if (isImageFile(leaf.id.toLowerCase())) {
|
||||
loadPreview(leaf.id);
|
||||
}
|
||||
};
|
||||
leaf.oncontextmenu = function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
showContextMenu(e, leaf.id, true);
|
||||
};
|
||||
return leaf;
|
||||
}
|
||||
|
||||
function addList(parent, path, items) {
|
||||
sortByKey(items, 'name');
|
||||
var list = ce("ul");
|
||||
parent.appendChild(list);
|
||||
var ll = items.length;
|
||||
for (var i = 0; i < ll; i++) {
|
||||
if (items[i].type === "file")
|
||||
list.appendChild(createTreeLeaf(path, items[i].name, items[i].size));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function isTextFile(path) {
|
||||
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
|
||||
if (typeof ext !== undefined) {
|
||||
switch (ext) {
|
||||
case "cnv":
|
||||
case "txt":
|
||||
case "htm":
|
||||
case "html":
|
||||
case "js":
|
||||
case "css":
|
||||
case "xml":
|
||||
case "json":
|
||||
case "conf":
|
||||
case "ini":
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isImageFile(path) {
|
||||
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
|
||||
if (typeof ext !== undefined) {
|
||||
switch (ext) {
|
||||
case "png":
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "gif":
|
||||
case "bmp":
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
this.refreshPath = function (path) {
|
||||
treeRoot.removeChild(treeRoot.childNodes[0]);
|
||||
httpGet(treeRoot, "/");
|
||||
};
|
||||
|
||||
function delCb(path) {
|
||||
return function (status, responseText) {
|
||||
if (status != 200) {
|
||||
alert("ERROR[" + status + "]: " + responseText);
|
||||
} else {
|
||||
treeRoot.removeChild(treeRoot.childNodes[0]);
|
||||
httpGet(treeRoot, "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function httpDelete(filename) {
|
||||
var formData = new FormData();
|
||||
formData.append("path", filename);
|
||||
requests.add("DELETE", "/edit", formData, delCb(filename));
|
||||
}
|
||||
|
||||
function getCb(parent, path) {
|
||||
return function (status, responseText) {
|
||||
if (status == 200)
|
||||
addList(parent, path, JSON.parse(responseText));
|
||||
}
|
||||
}
|
||||
|
||||
function httpGet(parent, path) {
|
||||
requests.add("GET", "/edit", { list: path }, getCb(parent, path));
|
||||
}
|
||||
|
||||
httpGet(treeRoot, "/");
|
||||
return this;
|
||||
}
|
||||
|
||||
function createEditor(element, file, lang, theme, type) {
|
||||
function getLangFromFilename(filename) {
|
||||
var lang = "plain";
|
||||
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
|
||||
if (typeof ext !== undefined) {
|
||||
switch (ext) {
|
||||
case "cnv": lang = "plain"; break;
|
||||
case "txt": lang = "plain"; break;
|
||||
case "hex": lang = "plain"; break;
|
||||
case "conf": lang = "plain"; break;
|
||||
case "htm": lang = "html"; break;
|
||||
case "js": lang = "javascript"; break;
|
||||
case "css":
|
||||
case "scss":
|
||||
case "php":
|
||||
case "html":
|
||||
case "json":
|
||||
case "xml":
|
||||
case "ini": lang = ext;
|
||||
}
|
||||
}
|
||||
return lang;
|
||||
}
|
||||
|
||||
if (typeof file === "undefined") file = "/index.html";
|
||||
|
||||
if (typeof lang === "undefined") {
|
||||
lang = getLangFromFilename(file);
|
||||
}
|
||||
|
||||
if (typeof theme === "undefined") theme = "textmate";
|
||||
|
||||
if (typeof type === "undefined") {
|
||||
type = "text/" + lang;
|
||||
if (lang === "c_cpp") type = "text/plain";
|
||||
}
|
||||
|
||||
var editor = ace.edit(element);
|
||||
function httpPostProcessRequest(status, responseText) {
|
||||
if (status != 200) alert("ERROR[" + status + "]: " + responseText);
|
||||
}
|
||||
function httpPost(filename, data, type) {
|
||||
var formData = new FormData();
|
||||
formData.append("data", new Blob([data], { type: type }), filename);
|
||||
requests.add("POST", "/edit", formData, httpPostProcessRequest);
|
||||
}
|
||||
function httpGetProcessRequest(status, responseText) {
|
||||
ge("preview").style.display = "none";
|
||||
ge("editor").style.display = "block";
|
||||
if (status == 200)
|
||||
editor.setValue(responseText);
|
||||
else
|
||||
editor.setValue("");
|
||||
editor.clearSelection();
|
||||
}
|
||||
function httpGet(theUrl) {
|
||||
requests.add("GET", "/edit", { edit: theUrl }, httpGetProcessRequest);
|
||||
}
|
||||
|
||||
if (lang !== "plain") editor.getSession().setMode("ace/mode/" + lang);
|
||||
editor.setTheme("ace/theme/" + theme);
|
||||
editor.$blockScrolling = Infinity;
|
||||
editor.getSession().setUseSoftTabs(true);
|
||||
editor.getSession().setTabSize(2);
|
||||
editor.setHighlightActiveLine(true);
|
||||
editor.setShowPrintMargin(false);
|
||||
editor.commands.addCommand({
|
||||
name: 'saveCommand',
|
||||
bindKey: { win: 'Ctrl-S', mac: 'Command-S' },
|
||||
exec: function (editor) {
|
||||
httpPost(file, editor.getValue() + "", type);
|
||||
},
|
||||
readOnly: false
|
||||
});
|
||||
editor.commands.addCommand({
|
||||
name: 'undoCommand',
|
||||
bindKey: { win: 'Ctrl-Z', mac: 'Command-Z' },
|
||||
exec: function (editor) {
|
||||
editor.getSession().getUndoManager().undo(false);
|
||||
},
|
||||
readOnly: false
|
||||
});
|
||||
editor.commands.addCommand({
|
||||
name: 'redoCommand',
|
||||
bindKey: { win: 'Ctrl-Shift-Z', mac: 'Command-Shift-Z' },
|
||||
exec: function (editor) {
|
||||
editor.getSession().getUndoManager().redo(false);
|
||||
},
|
||||
readOnly: false
|
||||
});
|
||||
editor.loadUrl = function (filename) {
|
||||
var edfname = ge("editor-filename");
|
||||
edfname.value = filename;
|
||||
file = filename;
|
||||
lang = getLangFromFilename(file);
|
||||
type = "text/" + lang;
|
||||
if (lang !== "plain") editor.getSession().setMode("ace/mode/" + lang);
|
||||
httpGet(file);
|
||||
};
|
||||
return editor;
|
||||
}
|
||||
function onBodyLoad() {
|
||||
var vars = {};
|
||||
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) { vars[key] = value; });
|
||||
var editor = createEditor("editor", vars.file, vars.lang, vars.theme);
|
||||
var tree = createTree("tree", editor);
|
||||
createFileUploader("uploader", tree, editor);
|
||||
if (typeof vars.file === "undefined") vars.file = "/dev_conf.txt";
|
||||
editor.loadUrl(vars.file);
|
||||
};
|
||||
</script>
|
||||
<script id='ace' src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ace.js" type="text/javascript"
|
||||
charset="utf-8"></script>
|
||||
<script>
|
||||
if (typeof ace.edit == "undefined") {
|
||||
var script = document.createElement('script');
|
||||
script.src = "/ace.js";
|
||||
script.async = false;
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="onBodyLoad();">
|
||||
<div id="loader" class="loader"></div>
|
||||
<div id="uploader"></div>
|
||||
<div id="tree"></div>
|
||||
<div id="editor"></div>
|
||||
<div id="preview" style="display:none;"></div>
|
||||
<iframe id=download-frame style='display:none;'></iframe>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Binary file not shown.
@@ -23,12 +23,6 @@
|
||||
"action": "/?set.device",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "Список других устройств в сети",
|
||||
"action": "/?set.udp",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "Конфигурация WIFI",
|
||||
|
||||
1
data/items/analog-adc.txt
Normal file
1
data/items/analog-adc.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;gol;map[0,1024,0,100];c[1]
|
||||
1
data/items/bme280-hum.txt
Normal file
1
data/items/bme280-hum.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1]
|
||||
1
data/items/bme280-press.txt
Normal file
1
data/items/bme280-press.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1]
|
||||
1
data/items/bme280-temp.txt
Normal file
1
data/items/bme280-temp.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1]
|
||||
1
data/items/bmp280-press.txt
Normal file
1
data/items/bmp280-press.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;bmp280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1]
|
||||
1
data/items/bmp280-temp.txt
Normal file
1
data/items/bmp280-temp.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;bmp280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1]
|
||||
1
data/items/button-in.txt
Normal file
1
data/items/button-in.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;button-in;id;toggle;Кнопки;Освещение;order;pin;db[20]
|
||||
1
data/items/button-out.inv.txt
Normal file
1
data/items/button-out.inv.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;button-out;id;toggle;Кнопки;Освещение;order;pin;inv[1];st[1]
|
||||
1
data/items/button-out.npin.txt
Normal file
1
data/items/button-out.npin.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;button-out;id;toggleSunMoon;Кнопки;Освещение;order;st[0]
|
||||
1
data/items/button-out.pin.txt
Normal file
1
data/items/button-out.pin.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;button-out;id;toggle;Кнопки;Освещение;order;pin;st[0]
|
||||
1
data/items/dallas-temp.txt
Normal file
1
data/items/dallas-temp.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;sal;c[1]
|
||||
1
data/items/dht11-hum.txt
Normal file
1
data/items/dht11-hum.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht11];c[1]
|
||||
1
data/items/dht11-temp.txt
Normal file
1
data/items/dht11-temp.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht11];c[1]
|
||||
1
data/items/dht22-hum.txt
Normal file
1
data/items/dht22-hum.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht22];c[1]
|
||||
1
data/items/dht22-temp.txt
Normal file
1
data/items/dht22-temp.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht22];c[1]
|
||||
1
data/items/input-digit.txt
Normal file
1
data/items/input-digit.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;input-digit;id;inputDigit;Ввод;Введите#цифру;order;st[60]
|
||||
1
data/items/input-time.txt
Normal file
1
data/items/input-time.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;input-time;id;inputTime;Ввод;Введите#время;order;st[10-00-00]
|
||||
1
data/items/modbus.txt
Normal file
1
data/items/modbus.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;modbus;id;anydata;Modbus;Регистр;order;addr[1];reg[0];c[1]
|
||||
1
data/items/output-text.txt
Normal file
1
data/items/output-text.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;output-text;id;anydata;Вывод;Сигнализация;order;st[Обнаружено#движение]
|
||||
1
data/items/pwm-out.txt
Normal file
1
data/items/pwm-out.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;pwm-out;id;range;Ползунки;Яркость;order;pin;st[500]
|
||||
1
data/items/ultrasonic-cm.txt
Normal file
1
data/items/ultrasonic-cm.txt
Normal file
@@ -0,0 +1 @@
|
||||
0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;cin;map[0,500,0,100];c[1]
|
||||
Binary file not shown.
Binary file not shown.
1
data/s.conf.csv
Normal file
1
data/s.conf.csv
Normal file
@@ -0,0 +1 @@
|
||||
Тип элемента;Id;Виджет;Имя вкладки;Имя виджета;Позиция виджета
|
||||
|
1
data/s.scen.txt
Normal file
1
data/s.scen.txt
Normal file
@@ -0,0 +1 @@
|
||||
//
|
||||
@@ -1,142 +1,171 @@
|
||||
{
|
||||
"configs": [
|
||||
"/config.setup.json",
|
||||
"/config.option.json",
|
||||
"/config.live.json",
|
||||
"/lang/lang.ru.json"
|
||||
],
|
||||
"class": "col-sm-offset-1 col-sm-10",
|
||||
"content": [
|
||||
{
|
||||
"type": "h5",
|
||||
"title": "{{name}}",
|
||||
"class": "alert-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "{{ButMainPage}}",
|
||||
"action": "/",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "hr"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "Device ID: {{chipID}}"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "IP address: {{ip}}"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "Time: {{time}}"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "Uptime: {{uptime}}"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "Build version: {{firmware_version}}"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "LittleFS version: 2.3.5"
|
||||
},
|
||||
{
|
||||
"type": "hr"
|
||||
},
|
||||
{
|
||||
"type": "dropdown",
|
||||
"name": "help-url",
|
||||
"class": "btn btn-default",
|
||||
"style": "display:inline",
|
||||
"title": {
|
||||
"#": "{{SetDevPreset}}<span class=\"caret\"></span>",
|
||||
"/set?preset=001": "1.Вкл. выкл. локального реле",
|
||||
"/set?preset=002": "2.Вкл. выкл. локального реле в определенное время",
|
||||
"/set?preset=003": "3.Вкл. выкл. локального реле на определенный период времени",
|
||||
"/set?preset=004": "4.Вкл. выкл. нескольких локальных реле кнопкой в приложении",
|
||||
"/set?preset=005": "5.Вкл. выкл. локального реле физической кнопкой и кнопкой в приложении параллельно (для выключателя света)",
|
||||
"/set?preset=006": "6.Вкл. выкл. нескольких удаленных реле кнопкой в приложении (нужно указать Device ID)",
|
||||
"/set?preset=007": "7.Вкл. выкл. нескольких удаленных реле физической кнопкой (нужно указать Device ID)",
|
||||
"/set?preset=008": "8.Широтно импульсная модуляция",
|
||||
"/set?preset=009": "9.Сенсор DHT11 (темп, влажность) и логгирование",
|
||||
"/set?preset=010": "10.Сенсор DHT22, DHT33, DHT44, AM2302, RHT03 (темп, влажность) и логгирование",
|
||||
"/set?preset=011": "11.Аналоговый сенсор и логгирование",
|
||||
"/set?preset=012": "12.Cенсор bmp280 (темп, давление) и логгирование",
|
||||
"/set?preset=013": "13.Cенсор bme280 (темп, давление, влажность, высота) и логгирование",
|
||||
"/set?preset=014": "14.Сенсор DS18B20 (темп) и логгирование",
|
||||
"/set?preset=015": "15.Термостат на DS18B20 с переключением в ручной режим и логгированием",
|
||||
"/set?preset=016": "16.Котроль уровня в баке (датчик расстояния) на сенсорах: JSN-SR04T, HC-SR04, HY-SRF05 и логгирование",
|
||||
"/set?preset=017": "17.Датчик движения включающий свет",
|
||||
"/set?preset=018": "18.Охранный датчик движения",
|
||||
"/set?preset=019": "19.Система управления шаговыми двигателями на основе драйвера A4988 (открытие закрытие штор)",
|
||||
"/set?preset=020": "20.Система управления сервоприводами",
|
||||
"/set?preset=021": "21.Модуль uart (serial). Двухстороняя связь с устройством через uart. Получение данных и отправка команд",
|
||||
"/set?preset=100": "22.Настройки по умолчанию"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "h2",
|
||||
"title": "{{SetDevConf}}"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"state": "dev_conf.txt",
|
||||
"style": "width:100%;height:350px",
|
||||
"title": "Сохранить",
|
||||
"action": "/set?devinit",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "h2",
|
||||
"title": "Сценарии"
|
||||
},
|
||||
{
|
||||
"type": "checkbox",
|
||||
"name": "scen",
|
||||
"title": "Включить сценарии",
|
||||
"action": "/set?scen=[[scen]]",
|
||||
"state": "{{scen}}"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"state": "dev_scen.txt",
|
||||
"style": "width:100%;height:350px",
|
||||
"title": "Сохранить",
|
||||
"action": "/set?sceninit",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "Инструкция к системе автоматизации",
|
||||
"action": "https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/wiki/Instruction",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "Очистить логи сенсоров",
|
||||
"action": "/set?cleanlog",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "hr"
|
||||
},
|
||||
{
|
||||
"type": "h3",
|
||||
"name": "my-block",
|
||||
"style": "position:fixed;top:50%;left:50%;width:400px;margin-left:-200px;text-align:center;",
|
||||
"class": "hidden"
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"title": "Обновить прошивку устройства",
|
||||
"action": "/check",
|
||||
"response": "[[my-block]]",
|
||||
"class": "btn btn-block btn-default"
|
||||
}
|
||||
]
|
||||
"configs": [
|
||||
"/config.setup.json",
|
||||
"/config.option.json",
|
||||
"/config.live.json",
|
||||
"/lang/lang.ru.json"
|
||||
],
|
||||
"class": "col-sm-offset-1 col-sm-10",
|
||||
"content": [
|
||||
{
|
||||
"type": "h5",
|
||||
"title": "{{name}}",
|
||||
"class": "alert-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "{{ButMainPage}}",
|
||||
"action": "/",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "hr"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "Device ID: {{chipID}}"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "IP address: {{ip}}"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "Time: {{time}}"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "Uptime: {{uptime}}"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "Build version: {{firmware_version}}"
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "LittleFS version: 257"
|
||||
},
|
||||
{
|
||||
"type": "hr"
|
||||
},
|
||||
{
|
||||
"type": "dropdown",
|
||||
"name": "help-url",
|
||||
"class": "btn btn-default",
|
||||
"style": "display:inline",
|
||||
"title": {
|
||||
"#": "Выберите элемент из списка<span class=\"caret\"></span>",
|
||||
"/set?addItem=button-out.pin": "1.Кнопка управляющая пином",
|
||||
"/set?addItem=button-out.npin": "2.Кнопка виртуальная",
|
||||
"/set?addItem=button-in": "4.Кнопка физическая",
|
||||
"/set?addItem=pwm-out": "3.Широтно импульсная модуляция pwm",
|
||||
"/set?addItem=input-digit": "5.Окно ввода цифровых значений",
|
||||
"/set?addItem=input-time": "6.Окно ввода времени",
|
||||
"/set?addItem=output-text": "7.Окно вывода любого текста, предупреждения, цифры",
|
||||
"/set?addItem=analog-adc": "8.Датчик аналоговый, чтение аналогового входа",
|
||||
"/set?addItem=dallas-temp": "9.Датчик температуры ds18b20",
|
||||
"/set?addItem=ultrasonic-cm": "10.Датчик расстояния ультрозвуковой JSN-SR04T, HC-SR04, HY-SRF05",
|
||||
"/set?addItem=dht11-temp": "11.Датчик температуры DHT11",
|
||||
"/set?addItem=dht11-hum": "12.Датчик влажности DHT11",
|
||||
"/set?addItem=dht22-temp": "13.Датчик температуры DHT22, DHT33, DHT44, AM2302, RHT03",
|
||||
"/set?addItem=dht22-hum": "14.Датчик влажности DHT22, DHT33, DHT44, AM2302, RHT03",
|
||||
"/set?addItem=bme280-temp": "15.Датчик температуры bme280",
|
||||
"/set?addItem=bme280-hum": "16.Датчик влажности bme280",
|
||||
"/set?addItem=bme280-press": "17.Датчик давления bme280",
|
||||
"/set?addItem=bmp280-temp": "18.Датчик температуры bmp280",
|
||||
"/set?addItem=bmp280-press": "19.Датчик давления bmp280",
|
||||
"/set?addItem=modbus": "20.Прочитать регистр modbus устройства"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "hr"
|
||||
},
|
||||
{
|
||||
"type": "csv",
|
||||
"title": [
|
||||
"checkbox",
|
||||
"html",
|
||||
"text",
|
||||
"text",
|
||||
"text",
|
||||
"text",
|
||||
"text"
|
||||
],
|
||||
"state": "s.conf.csv",
|
||||
"style": "width:100%;",
|
||||
"action": "/set?saveItems",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "hr"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "Удалить выбранные элементы",
|
||||
"action": "javascript:{send_request(this,'/set?delChoosingItems');setTimeout(function(){location.href='/?set.device' ; }, 1000);}",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "Удалить все",
|
||||
"action": "/set?delAllItems",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "h2",
|
||||
"title": "Сценарии"
|
||||
},
|
||||
{
|
||||
"type": "checkbox",
|
||||
"name": "scen",
|
||||
"title": "Включить сценарии",
|
||||
"action": "/set?scen=[[scen]]",
|
||||
"state": "{{scen}}"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"state": "s.scen.txt",
|
||||
"style": "width:100%;height:350px",
|
||||
"title": "Сохранить",
|
||||
"action": "/set?sceninit",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "hr"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "Ручная настройка",
|
||||
"action": "/?set.manual",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "Инструкция к системе автоматизации",
|
||||
"action": "https://github.com/IoTManagerProject/IoTManager/wiki",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "Очистить логи сенсоров",
|
||||
"action": "/set?cleanlog",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "hr"
|
||||
},
|
||||
{
|
||||
"type": "h3",
|
||||
"name": "my-block",
|
||||
"style": "position:fixed;top:50%;left:50%;width:400px;margin-left:-200px;text-align:center;",
|
||||
"class": "hidden"
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"title": "Обновить прошивку устройства",
|
||||
"action": "/check",
|
||||
"response": "[[my-block]]",
|
||||
"class": "btn btn-block btn-default"
|
||||
}
|
||||
]
|
||||
}
|
||||
38
data/set.manual.json
Normal file
38
data/set.manual.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"configs": [
|
||||
"/config.setup.json",
|
||||
"/config.option.json",
|
||||
"/config.live.json",
|
||||
"/lang/lang.ru.json"
|
||||
],
|
||||
"class": "col-sm-offset-1 col-sm-10",
|
||||
"content": [
|
||||
{
|
||||
"type": "h5",
|
||||
"title": "{{name}}",
|
||||
"class": "alert-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"title": "{{ButMainPage}}",
|
||||
"action": "/",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"state": "s.conf.csv",
|
||||
"style": "width:100%;height:350px",
|
||||
"title": "Сохранить",
|
||||
"action": "/set?saveItems",
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"state": "s.scen.txt",
|
||||
"style": "width:100%;height:350px",
|
||||
"title": "Сохранить",
|
||||
"action": "/set?sceninit",
|
||||
"class": "btn btn-block btn-default"
|
||||
}
|
||||
]
|
||||
}
|
||||
6
data/widgets/alarm.json
Normal file
6
data/widgets/alarm.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"widget": "anydata",
|
||||
"icon": "body",
|
||||
"color": "red",
|
||||
"descrColor": "red"
|
||||
}
|
||||
5
data/widgets/anydataHum.json
Normal file
5
data/widgets/anydataHum.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"widget": "anydata",
|
||||
"after": "%",
|
||||
"icon": "water"
|
||||
}
|
||||
5
data/widgets/anydataPress.json
Normal file
5
data/widgets/anydataPress.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"widget": "anydata",
|
||||
"after": "mm",
|
||||
"icon": "speedometer"
|
||||
}
|
||||
5
data/widgets/anydataTemp.json
Normal file
5
data/widgets/anydataTemp.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"widget": "anydata",
|
||||
"after": "°С",
|
||||
"icon": "thermometer"
|
||||
}
|
||||
5
data/widgets/toggleSunMoon.json
Normal file
5
data/widgets/toggleSunMoon.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"widget": "toggle",
|
||||
"icon": "sunny",
|
||||
"iconOff": "moon"
|
||||
}
|
||||
566
doc/1.txt
Normal file
566
doc/1.txt
Normal file
@@ -0,0 +1,566 @@
|
||||
***
|
||||

|
||||
|
||||
## Возможности
|
||||
|
||||
- Объединение различных по типу и назначению устройств: управление, получение данных, и настройка параметров - всё в одном приложении
|
||||
|
||||
- Взаимодействие с устройствами осуществляется через "облачный" сервис с использованием протокола mqtt, позволит контролировать их из любой точки Мира (при наличии доступа в Интернет)
|
||||
|
||||
- Поддержка нескольких профилей и их переключение "на лету", дает возможность объединить устройства в группы
|
||||
|
||||
|
||||
Настройка (после "прошивки") производится через веб-интерфейс, чтобы получить к нему доступ необходимо соединиться с WiFi AP устройства и набрать в адресной строке браузера http://192.168.4.1.
|
||||
Далее выбрать типовой шаблон автоматизации, произвести настройку под свои требования и задачи.
|
||||
Основные разделы интерфейса: конфигурация и сценарии.
|
||||
В окне конфигурации задаются "объекты", "элементы управления" устройства (dashboard) - им устройство будет представлено в приложении компаньоне проекта. В окне сценариев задаются реакции на события и изменения в параметрах работы системы.
|
||||
|
||||
***
|
||||

|
||||
|
||||
## Команды, назначение и применение
|
||||
|
||||
Команды служат для настройки и управления устройством и его взаимодействия
|
||||
|
||||
**`buttonSet 1 1`** Изменит состояние "кнопки" №1, установит его в значение 1
|
||||
|
||||
**`pinSet 13 0`** Установит GPIO 13 состояние 0
|
||||
|
||||
**`pinChange 13`** Состояние GPIO 13 будет изменено на противоположное
|
||||
|
||||
**`pwmSet 1 500`** Настройка pwm №1 будет использовано значение 500
|
||||
|
||||
**`timeSet 1 08-00-00`** Установит для элемента ввода времени - inputTime значение 08:00:00
|
||||
|
||||
**`digitSet 1 56`** Элемент №1 (для цифровых параметров) отобразит число 56
|
||||
|
||||
**`stepperSet 1 100 1`** Шаговый двигатель №1 - вращение 100 "шагов" по часовой стрелке (для движения в обратную сторону используются отрицательные значения параметра)
|
||||
|
||||
**`servoSet 1 180`** Сервопривод №1 принять положение 180°
|
||||
|
||||
**`timerStart 1 60 sec`** Установить для таймера №1 обратный отсчёт в 60 секунд
|
||||
|
||||
**`timerStop 1`** Остановить таймер №1
|
||||
|
||||
**`textSet 1 Привет`** Установить для элемента текстовое поле №1 - "привет"
|
||||
|
||||
**`push Внимание Протечка`** Отправить push-уведомление с темой "внимание" и содержанием "протечка"
|
||||
|
||||
**`firmwareUpdate`** Обновить прошивку устройства "по воздуху"
|
||||
|
||||
**`firmwareVersion Версия прошивки Системные 1`** Узнать версию прошивки устройстве
|
||||
|
||||
## Сценарии
|
||||
|
||||
Элементарный блок в сценарии состоит из набора команд и триггера - условия для их выполнения
|
||||
|
||||
**temp > 60**
|
||||
digitSet 1 60
|
||||
stepperSet 1 100 1
|
||||
textSet 1 Перегрев
|
||||
**end**
|
||||
|
||||
Условие: когда температура превысит 60°
|
||||
Запустит: команда шаговому двигателю, в приложение отправить сообщение и цифровое значение температуры.
|
||||
|
||||
В сценарии может быть несколько блоков, при необходимости из приложения есть возможность "выключать" часть из них.
|
||||
Неактивные блоки сценария будут проигнорированы.
|
||||
|
||||
Для взаимодействия устройств между собой предусмотрены команды mqtt и http
|
||||
|
||||
**temp > 60**
|
||||
mqtt 154348-134 digitSet_1_56
|
||||
mqtt 154348-136 stepperSet _1_100_1
|
||||
http 192.168.1.10 textSet_1_Перегрев
|
||||
**end**
|
||||
|
||||
***
|
||||

|
||||
## 1.1 Объект "кнопка"
|
||||
|
||||
(эти строки мы пишем в "конфигурации устройства")
|
||||
|
||||
### a) кнопка управляющая выходом (пином). Пины нумеруются по системе нумирации gpio для esp контроллеров.
|
||||
|
||||
`button 1 13 кухня освещение 0 1`
|
||||
|
||||
**"button"** это объект создающий кнопку в приложении
|
||||
**"1"** это номер этой кнопки (необходимый для ее аутентификации)
|
||||
**"13"** это номер пина которым будет управлять данная кнопка
|
||||
**"кухня"** это название кнопки в приложении
|
||||
**"освещение"** это название вкладки в приложении на которой появится данная кнопка
|
||||
**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1)
|
||||
**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
|
||||
### б) виртуальная кнопка - кнопка реакцию на которую можно задать в сценариях:
|
||||
|
||||
`button 1 na запустить таймеры 0 1`
|
||||
|
||||
**"button"** это объект создающий кнопку в приложении
|
||||
**"1"** это номер этой кнопки (необходимый для ее аутентификации)
|
||||
**"na"** абривиатура not available означающая что эта кнопка виртуальная и что пин не установлен
|
||||
**"запустить"** это название кнопки в приложении
|
||||
**"таймеры"** это название вкладки в приложении на которой появится данная кнопка
|
||||
**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1)
|
||||
**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
### в) кнопка включающая и выключающая все сценарии:
|
||||
|
||||
`button 1 scenario запустить таймеры 0 1`
|
||||
|
||||
**"button"** это объект создающий кнопку в приложении
|
||||
**"1"** это номер этой кнопки (необходимый для ее аутентификации)
|
||||
**"scenario"** слово означающее что эта кнопка для управления сценариями
|
||||
**"запустить"** это название кнопки в приложении
|
||||
**"таймеры"** это название вкладки в приложении на которой появится данная кнопка
|
||||
**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1)
|
||||
**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
|
||||
### г) кнопка включающая выключающая определенные блоки сценариев:
|
||||
|
||||
`button 1 line1,line3, Включить#отправку#push Оповещение 0 1`
|
||||
|
||||
**"button"** это объект создающий кнопку в приложении
|
||||
**"1"** это номер этой кнопки (необходимый для ее аутентификации)
|
||||
**"line1,line3,"** это блоки сценариев нумирация сверху вниз. Блоком считается выражение от начала до слова end
|
||||
**"Включить#отправку#push"** это название кнопки в приложении
|
||||
**"Оповещение"** это название вкладки в приложении на которой появится данная кнопка
|
||||
**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1)
|
||||
**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
## 1.2 Команды управления объектом "кнопка"
|
||||
|
||||
(эти строки мы пишем в "сценариях")
|
||||
|
||||
### а) Команда включения выключения кнопки по ее номеру
|
||||
|
||||
`buttonSet 1 1`
|
||||
|
||||
**"buttonSet"** команда управления объектом button
|
||||
**"1"** номер кнопки которой будем управлять
|
||||
**"1"** состояние включено, 0 - выключено
|
||||
|
||||
### б) Команда изменения состояния кнопки на противоположное
|
||||
|
||||
`buttonChange 1`
|
||||
|
||||
**"buttonChange"** команда управления объектом button
|
||||
**"1"** номер кнопки которой будем управлять
|
||||
|
||||
## 1.3 Вызов событий объектом "кнопока"
|
||||
|
||||
(эти строки мы пишем в "сценариях")
|
||||
|
||||
объект button может быть равен либо 0 либо 1
|
||||
|
||||
`button1 = 1`
|
||||
`button2 = 0`
|
||||
|
||||
Пример использования:
|
||||
|
||||
`button1 = 1`
|
||||
`buttonSet 2 1`
|
||||
`buttonSet 3 0`
|
||||
`end`
|
||||
|
||||
|
||||
***
|
||||

|
||||
## 2.1 Объект "физическая кнопка"
|
||||
|
||||
`switch 1 0 10`
|
||||
|
||||
**switch** это объект создающий физическую кнопку
|
||||
**1** номер кнопки
|
||||
**0** пин кнопки (при подключении необходим подтягивающий резистор)
|
||||
**10** задержка для избавления от дребезга с мили секундах
|
||||
|
||||
## 2.2 Вызов событий объектом "физическая кнопка"
|
||||
|
||||
`switch1` может быть равна нулю или единицы, ноль - событие отбрасывания кнопки, единица - событие нажатия
|
||||
|
||||
`switch1 = 1`
|
||||
`buttonChange 1`
|
||||
`end`
|
||||
|
||||
|
||||
***
|
||||

|
||||
## 3.1 Объект "широтноимпульсная модуляция"
|
||||
|
||||
`pwm 1 12 яркость освещение 1023 1`
|
||||
|
||||
**"pwm"** это объект создающий управление шим в приложении в виде ползунка
|
||||
**"1"** это номер этого объекта
|
||||
**"12"** это номер пина на котором будет генерироваться шим заданной в приложении величены
|
||||
**"Яркость"** это название кнопки в приложении
|
||||
**"Оповещение"** это название вкладки в приложении на которой появится данная кнопка
|
||||
**"1023"** это начальное значение шим сигнала и ползунка (изменяется от 0 до 1023)
|
||||
**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
## 3.2 Команда управления объектом "широтноимпульсная модуляция"
|
||||
|
||||
`pwmSet 1 500`
|
||||
|
||||
**"pwmSet"** команда управления объектом
|
||||
**"1"** номер объекта, которым будем управлять
|
||||
**"500"** значение которое установится после выполнения команды (от 0 до 1023)
|
||||
|
||||
***
|
||||

|
||||
## 4.1 Объект "окно ввода времени"
|
||||
|
||||
`inputTime time1 Во#сколько#включить? Таймеры 20-30-00 1`
|
||||
|
||||
**inputTime** это объект создающий окно ввода в приложении
|
||||
**time1** переменная в которую будет записано время введенное в окно
|
||||
**Во#сколько#включить?** это название окна в приложении
|
||||
**Таймеры** это название вкладки в приложении
|
||||
**20-30-00** начальное значение времени после загрузки устройства
|
||||
**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
## 4.2 Управление объектом "окно ввода времени"
|
||||
|
||||
`timeSet 1 08-00-00`
|
||||
|
||||
**"timeSet"** команда управления объектом
|
||||
**"1"** номер объекта, которым будем управлять в данном случае окном ввода с `time1`
|
||||
**"08-00-00"** время которое хотим установить
|
||||
|
||||
В окно ввода можно вводить время в приложении но если необходимо изменить время автоматически
|
||||
по какому нибудь событию то можно использовать команду выше - **timeSet**.
|
||||
|
||||
## 4.3 Вызов событий объектом "окно ввода времени"
|
||||
|
||||
`timenow = time1`
|
||||
`buttonSet 1 1`
|
||||
`end`
|
||||
|
||||
`timenow` всегда хранит в себе текущее время, и поэтому исходя из данного сценария кнопка номер 1 включится в то время которое будет введено в окно ввода `time1`
|
||||
|
||||
***
|
||||

|
||||
## 5.1 Объект "окно ввода цифры"
|
||||
|
||||
`inputDigit digit1 Через#сколько#секунд#выключить? Таймеры 5 2`
|
||||
|
||||
**inputDigit** это объект создающий окно ввода в приложении
|
||||
**digit1** переменная в которую будет записана цифра, введенная в окно
|
||||
**Через#сколько#секунд#выключить?** это название окна в приложении
|
||||
**Таймеры** это название вкладки в приложении
|
||||
**5** цифра по умолчанию, после загрузки модуля
|
||||
**2** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
## 5.2 Управление объектом "окно ввода цифры":
|
||||
|
||||
`digitSet 1 56`
|
||||
|
||||
**"digitSet"** команда управления объектом
|
||||
**"1"** номер объекта, которым будем управлять в данном случае окном ввода с `digit1`
|
||||
**"56"** цифра которую хотим установить
|
||||
|
||||
В окно ввода можно вводить цифры в приложении, но если необходимо изменить цифру автоматически
|
||||
по какому нибудь событию, то можно использовать команду выше - **digitSet**.
|
||||
|
||||
## 5.3 Вызов событий объектом "окно ввода цифры"
|
||||
|
||||
`dallas > digit1`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
`button1 = 1`
|
||||
`timerStart 1 digit1 sec`
|
||||
`end`
|
||||
|
||||
***
|
||||

|
||||
## 6.1 Объект "dallas" (сенсор температуры ds18b20)
|
||||
|
||||
`dallas temp1 2 123456 Водонагреватель,#t°C Термостат any-data 1`
|
||||
|
||||
**dallas** это объект чтения датчика температуры
|
||||
**2** пин датчика температуры
|
||||
**Водонагреватель,#t°C** это название виджета в приложении
|
||||
**Датчики** название вкладки в приложении
|
||||
**any-data** или **progress-round** или **progress-line** три разных варианта виджета отображения
|
||||
**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
## 6.2 Вызов событий объектом "dallas"
|
||||
|
||||
В сценариях dallas можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=):
|
||||
|
||||
`dallas > digit1`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=):
|
||||
|
||||
`dallas > 60`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
|
||||
***
|
||||

|
||||
## 7.1 Объект "analog" (аналоговый вход контроллера)
|
||||
|
||||
`analog adc 0 Аналоговый#вход,#% Датчики progress-round 310 620 1 100 1`
|
||||
|
||||
**analog** это объект чтения аналогового входа
|
||||
**adc** это переменная
|
||||
**0** пин аналогового входа (для esp8266 всегда 0, для esp32 пока что не доделал читаться будет всегда пин 34)
|
||||
**Аналоговый#вход,#%** это название виджета в приложении
|
||||
**Датчики** название вкладки в приложении
|
||||
**any-data** или **progress-round** или **progress-line** три разных варианта виджета отображения
|
||||
**310** начальная величина читаемого диапазона
|
||||
**620** конечная величина читаемого диапазона
|
||||
**1** начальная величина выводимого диапазона
|
||||
**100** конечная величина выводимого диапазона
|
||||
**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
## 7.2 Вызов событий объектом "analog"
|
||||
|
||||
В сценариях analog можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=):
|
||||
|
||||
`analog > digit1`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=):
|
||||
|
||||
`analog > 50`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
|
||||
***
|
||||

|
||||
## 8.1 Объект "level" (ультразвуковой дальномер JSN-SR04T, HC-SR04, HY-SRF05)
|
||||
|
||||
`level Вода#в#баке,#% Датчики any-data 125 20 1`
|
||||
|
||||
**level** это объект чтения датчика расстояния
|
||||
**Вода#в#баке** это название виджета в приложении
|
||||
**Датчики** название вкладки в приложении
|
||||
**any-data** или **progress-round** или **progress-line** три разных варианта отображения виджета
|
||||
**125** расстояние от датчика до дна бака в сантиметрах
|
||||
**20** расстояние от датчика до поверхности воды, когда бак полный, в сантиметрах
|
||||
**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
Подключать дальномер нужно:
|
||||
|
||||
| | trig | echo |
|
||||
| :-: | :-: | :-: |
|
||||
| wemos | D5 | D6 |
|
||||
| esp | 14 | 12 |
|
||||
|
||||
## 8.2 Вызов событий объектом "level"
|
||||
|
||||
В сценариях level можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=):
|
||||
|
||||
`level > digit1`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=):
|
||||
|
||||
`level > 95`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
|
||||
|
||||
***
|
||||

|
||||
## 9.1 Объект "dht" (Сенсоры DHT11, DHT22, DHT33, DHT44, AM2302, RHT03)
|
||||
|
||||
dhtT DHT11 2 Температура#DHT,#t°C Датчики any-data 1
|
||||
dhtH DHT11 2 Влажность#DHT,#% Датчики any-data 2
|
||||
dhtComfort Степень#комфорта: Датчики 3
|
||||
dhtPerception Восприятие: Датчики 4
|
||||
dhtDewpoint Точка#росы: Датчики 5
|
||||
|
||||
**dhtT** или **dhtH** температура или влажность
|
||||
**DHT11** или **DHT22** чтение DHT11 или DHT22, DHT33, DHT44, AM2302, RHT03 соответственно
|
||||
**2** пин датчика
|
||||
**Температура#DHT,#t°C** это название виджета в приложении
|
||||
**Датчики** название вкладки в приложении
|
||||
**any-data** или **progress-round** или **progress-line** три разных варианта отображения виджета
|
||||
**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
## 9.2 Вызов событий объектом "dhtT" или "dhtH"
|
||||
|
||||
В сценариях "dhtT" или "dhtH" можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=):
|
||||
|
||||
`dhtT > digit1`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
`dhtH > digit1`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=):
|
||||
|
||||
`dhtT > 50`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
`dhtH < 40`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
|
||||
***
|
||||

|
||||
## 10.1 Объект "stepper" (Драйвер шагового двигателя A4988)
|
||||
|
||||
stepper 1 12 4
|
||||
stepper 2 13 5
|
||||
|
||||
**stepper** объект создающий шаговый двигатель
|
||||
**1** номер шаговика
|
||||
**12** номер пина количества шагов
|
||||
**4** номер пина направления
|
||||
|
||||
## 10.2 управление объектом "stepper"
|
||||
|
||||
`stepperSet 1 200 1`
|
||||
|
||||
**stepperSet** команда управления шаговым двигателем
|
||||
**1** номер шагового двигателя (их может быть не более двух)
|
||||
**200** количество шагов (обратное направление отрицательное значение параметра)
|
||||
**1** интервал между шагами (мс)
|
||||
|
||||
`button1 = 1`
|
||||
`stepperSet 1 200 1`
|
||||
`end`
|
||||
`button1 = 0`
|
||||
`stepperSet 1 -200 1`
|
||||
`end`
|
||||
|
||||
***
|
||||

|
||||
## 11.1 Объект "обратный таймер"
|
||||
|
||||
Прежде чем читать этот раздел запустите пресет №3 на устройстве.
|
||||
Нажав на кнопку "Выберите во что вы хотите превратить esp"
|
||||
|
||||
Можно использовать цифры из окон ввода:
|
||||
|
||||
`timerStart 1 digit1 sec`
|
||||
|
||||
Можно писать цифры прям в объект:
|
||||
|
||||
`timerStart 1 10 sec`
|
||||
|
||||
Можно установить часы минуты или секунды:
|
||||
|
||||
`timerStart 1 10 sec`
|
||||
`timerStart 1 10 min`
|
||||
`timerStart 1 10 hours`
|
||||
|
||||
Используем это объект в сценариях вот так:
|
||||
|
||||
`button1 = 1`
|
||||
`timerStart 1 digit1 sec`
|
||||
`end`
|
||||
|
||||
Смысл в том что при нажатии на кнопку один запуститься обратный отчет, на величину digit1 секунд. Если напишем например:
|
||||
|
||||
`dallas < 60`
|
||||
`timerStart 1 digit1 sec`
|
||||
`end`
|
||||
|
||||
то такой же отчет запустится когда значение температуры вырастит больше 60 градусов. Таким образом обратный отчет можно запустить реакцией на любое событие. Итак теперь обратный отчет запущен, обратный таймер уменьшается, и нам надо назначить действие на тот момент когда он обнулится. Для этого я придумал выражение: `timer1 = 0`
|
||||
|
||||
Используем его и в общем получаем вот такой сценарий:
|
||||
|
||||
`button1 = 1`
|
||||
`timerStart 1 digit1 sec`
|
||||
`end`
|
||||
`timer1 = 0`
|
||||
`buttonSet 1 0`
|
||||
`end`
|
||||
|
||||
Когда таймер закончит отсчёт, кнопка станет "неактивной". Используйте преет №3, как пример подобного сценария
|
||||
Например:
|
||||
|
||||
`dallas < 60`
|
||||
`buttonSet 1 0`
|
||||
`buttonSet 2 0`
|
||||
`pwmSet 1 1023`
|
||||
`mqtt 2653450020 buttonChange_1`
|
||||
`mqtt 2653450020 pinSet_13_1`
|
||||
`http 192.168.1.32 pinSet_14_1`
|
||||
`end`
|
||||
|
||||
Вот что может произойти на разных устройствах по одному событию повышения температуры...
|
||||
|
||||
|
||||
***
|
||||

|
||||
## 12 Журнал (лог) данных
|
||||
|
||||
|
||||
`logging analog 1 100 slow Аналоговый#вход Датчики 7`
|
||||
|
||||
**logging** объект для логирования
|
||||
**analog** или **dhtT** или **dhtH** какой сенсор будем логировать, можно указать любой
|
||||
**1** период между точками в минутах
|
||||
**100** количество точек (старые точки будут удаляться по мере добавления новых)
|
||||
**slow** или **fast** метод выгрузки графика в приложение, slow - выгружает график по одной точке (меньше расходуется оперативка, лучше использовать для esp8266), fast - выгрузка графика сразу (больше расход оперативки, подходит для esp32)
|
||||
**Аналоговый#вход** название графика в приложении
|
||||
**Датчики** название вкладки в приложении
|
||||
**7** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта
|
||||
|
||||
***
|
||||

|
||||
## 13 Взаимодействие устройств между собой
|
||||
|
||||
Устройства могут между собой обмениваться командами. Команды можно отправлять по http или по mqtt.
|
||||
По событию на одном устройстве можно вызвать действие на другом. Например на esp01 стоит датчик температуры, реле стоит на esp02.
|
||||
|
||||
Настройки esp01:
|
||||
|
||||
`dhtT temp 2 dht11 Температура#DHT,#t°C Датчики any-data 1`
|
||||
|
||||
`temp < 40`
|
||||
`http 192.168.10.25 buttonSet_1_1`
|
||||
`end`
|
||||
|
||||
Настройки esp02:
|
||||
|
||||
`button 1 13 Включить#реле Реле 0 1`
|
||||
|
||||
И теперь когда температура датчика на esp01 станет меньше 40 градусов то на esp02 будет отправлена команда на включение кнопки: buttonSet_1_1
|
||||
|
||||
Если вы хотите отправить команду через mqtt то сценарий будет выглядеть следующим образом:
|
||||
|
||||
`temp < 40`
|
||||
`mqtt 12343442-12413131 buttonSet_1_1`
|
||||
`end`
|
||||
|
||||
где `12343442-12413131` id esp02 той на которую отправляем команду. Id можно взять в веб интерфейсе на странице конфигурация устройства. Или в списке устройств в сети.
|
||||
|
||||
Теперь рассмотрим вариант внешнего управления esp с помощью get запросов.
|
||||
|
||||
`http://192.168.88.239/cmd?command=buttonSet%201%201`
|
||||
|
||||
Разберем эту строку. Сама команда в ней выглядит вот так: buttonSet%201%201. `%20` заменяют пробел.
|
||||
|
||||
То есть что бы составить get запрос на изменение например pwm нужно:
|
||||
|
||||
Взять команду `pwmSet 1 500`
|
||||
Заменить в ней пробелы на `%20` получится так: `pwmSet%201%20500`
|
||||
И добавить ее в конец строки `http://192.168.88.239/cmd?command=` где указывается ip адрес устройства
|
||||
|
||||
В итоге получится http://192.168.88.239/cmd?command=pwmSet%201%20500
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
85
doc/2.txt
Normal file
85
doc/2.txt
Normal file
@@ -0,0 +1,85 @@
|
||||
# В этой инструкции будет описано как с esp отправлять email и push
|
||||
|
||||
# Часть 1. Привязать email и pushbullet к сайту pushingbox
|
||||
|
||||
### 1. Необходимо перейти на сайт: [pushingbox](https://www.pushingbox.com/)
|
||||
### 2. Войти с помощью google
|
||||

|
||||
### 3. Перейти в мои сервисы и добавить новый сервис
|
||||

|
||||
### 4. Нас интересуют два сервиса email и pushbullet
|
||||

|
||||
### 5. Выбираем сначало сервис для отправки email. В окно `Name of your email configuration` - вводим слово "email". В окно `Email address` - вводим ваш email адрес. жмем submit
|
||||

|
||||
manager_modules_firmware/blob/master/push_instruction/Screenshot_6.png)
|
||||
### 6.1 Привязываем pushbullet. Переходим на сайт [pushbullet.com](https://www.pushbullet.com/)
|
||||
### 6.2 Входим с гуглом или фейсбуком
|
||||
### 6.3 Идем в настройки
|
||||

|
||||
### 6.4 Создаем токен
|
||||

|
||||
### 6.5 Идем опять в сервисы и теперь выбираем сервис pushbullet [pushingbox.com/services](https://www.pushingbox.com/services.php) нажимаем add service
|
||||
### Берем токен, и вставляем его в окно Access token.
|
||||
### Окно Device token (optional) оставляем пустым.
|
||||
### В окно Name of your Pushbullet configuration пишем слово "push".
|
||||

|
||||
|
||||
### 7. Теперь наш email и pushbullet привязаны к pushingbox. Далее можно скачать приложение pushbullet на телефон и войти с гуглом или фейсбуком сответственно с пунктом 6.3 этой инструкции
|
||||

|
||||
|
||||
# Часть 2. Создание сценариев отправки email
|
||||
|
||||
### 8.1. Сценарий для отправки email. Заходим в My Scenarios:
|
||||

|
||||
|
||||
### 8.2 Пишем слово email (это имя сценария отправки email) жмем add:
|
||||

|
||||
### 8.3 Нажимаем add an action
|
||||

|
||||
### 8.4 Выбираем наш email который мы зарегестрировали ранее и нажимаем Add an action with this service
|
||||

|
||||
### 8.5 Делаем все как на скриншоте и жмем submit
|
||||

|
||||
### 8.6 Возвращаемся на мои сценарии
|
||||

|
||||
### 8.7 Вставляем токен в веб интерфейс esp
|
||||

|
||||
|
||||
# Часть 3. Создание сценариев отправки push
|
||||
|
||||
### 9.1. Сценарий для отправки push. Заходим в My Scenarios:
|
||||

|
||||
|
||||
### 9.2 Пишем слово push (это имя сценария отправки email) жмем add:
|
||||

|
||||
### 9.3 Нажимаем add an action
|
||||

|
||||
### 9.4 Выбираем наш pushbullet который мы зарегестрировали ранее и нажимаем Add an action with this service
|
||||

|
||||
### 9.5 Делаем все как на скриншоте и жмем submit
|
||||

|
||||
### 9.6 Возвращаемся на мои сценарии
|
||||

|
||||
### 9.7 Вставляем токен в веб интерфейс esp
|
||||

|
||||
|
||||
# Часть 4. Итог
|
||||
|
||||
При создании такой конфигурации как на картинке:
|
||||
|
||||
`button 1 na Отправить#push Push 0 1`
|
||||
|
||||
|
||||
`button1 = 1`
|
||||
`push внимание кнопка#нажата`
|
||||
`end`
|
||||
|
||||

|
||||
|
||||
Если мы введем токен для email то будут приходить email
|
||||

|
||||
|
||||
Если для push то будут приходить push в pushbullet
|
||||

|
||||
|
||||
Способ описанный в данной инструкции более сложный в настройке но зато очень надежный.
|
||||
32
doc/3.txt
Normal file
32
doc/3.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
***
|
||||

|
||||
|
||||
### 1. Скачать архив из [релизов](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/releases) или из закрепленного сообщения группы телеграм с последней версией прошивки
|
||||
|
||||
***
|
||||

|
||||
|
||||
|
||||
### 2. Для ESP8266 c 4 и больше мб памяти (все сделать как на скриншотах)
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
***
|
||||

|
||||
|
||||
### 2. Для ESP8266 c 1 мб памяти (все сделать как на скриншотах)
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
***
|
||||

|
||||
|
||||
### 2. Для ESP32 (все сделать как на скриншотах)
|
||||
|
||||

|
||||
|
||||

|
||||
BIN
doc/orders.xlsm
Normal file
BIN
doc/orders.xlsm
Normal file
Binary file not shown.
BIN
doc/pictures/001 iot manager.jpeg
Normal file
BIN
doc/pictures/001 iot manager.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
doc/pictures/002 iot manager.jpeg
Normal file
BIN
doc/pictures/002 iot manager.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
BIN
doc/pictures/003 iot manager.jpeg
Normal file
BIN
doc/pictures/003 iot manager.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
doc/pictures/007 iot manager.jpg
Normal file
BIN
doc/pictures/007 iot manager.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 381 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 76 KiB |
8
include/BufferExecute.h
Normal file
8
include/BufferExecute.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
extern void loopCmdAdd(const String &cmdStr);
|
||||
extern void fileCmdExecute(const String &filename);
|
||||
extern void csvCmdExecute(String &cmdStr);
|
||||
extern void spaceCmdExecute(String &cmdStr);
|
||||
extern void loopCmdExecute();
|
||||
4
include/Bus.h
Normal file
4
include/Bus.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
void busInit();
|
||||
String i2c_scan();
|
||||
@@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class BusScanner {
|
||||
public:
|
||||
BusScanner(const char* tag, String& out, size_t tries) : _found{0},
|
||||
_tries{tries},
|
||||
_out{&out} {
|
||||
_tag = new char(strlen(tag) + 1);
|
||||
strcpy(_tag, tag);
|
||||
}
|
||||
|
||||
void scan() {
|
||||
init();
|
||||
bool res;
|
||||
do {
|
||||
res = syncScan();
|
||||
} while (!res && --_tries);
|
||||
|
||||
if (!_found) {
|
||||
addResult("не найдено");
|
||||
}
|
||||
}
|
||||
|
||||
const char* tag() {
|
||||
return _tag;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void init(){};
|
||||
|
||||
virtual boolean syncScan() = 0;
|
||||
|
||||
protected:
|
||||
void addResult(const String& str) {
|
||||
_out->concat(str);
|
||||
}
|
||||
|
||||
void addResult(uint8_t addr, boolean last = true) {
|
||||
_found++;
|
||||
String str = "0x";
|
||||
if (addr < 16) {
|
||||
str += "0";
|
||||
}
|
||||
str += String(addr, HEX);
|
||||
str += !last ? ", " : ", ";
|
||||
addResult(str);
|
||||
};
|
||||
|
||||
private:
|
||||
char* _tag;
|
||||
size_t _found;
|
||||
size_t _tries;
|
||||
String* _out;
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Bus/BusScanner.h"
|
||||
#include "Bus/I2CScanner.h"
|
||||
#include "Consts.h"
|
||||
#include "Utils/JsonUtils.h"
|
||||
|
||||
class BusScannerFactory {
|
||||
public:
|
||||
static BusScanner* get(String& config, BusScanner_t type, String& str) {
|
||||
switch (type) {
|
||||
case BS_I2C:
|
||||
return new I2CScanner(str);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Bus/BusScanner.h"
|
||||
|
||||
class I2CScanner : public BusScanner {
|
||||
public:
|
||||
I2CScanner(String& out);
|
||||
|
||||
protected:
|
||||
virtual void init() override;
|
||||
virtual boolean syncScan() override;
|
||||
};
|
||||
26
include/Class/CallBackTest.h
Normal file
26
include/Class/CallBackTest.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
#include <functional>
|
||||
|
||||
// Декларируем тип - сигнатуру метода , который мы готовы принять в данном случае это
|
||||
// должен быть метод без результата и без параметров.
|
||||
// Новый тип мы называем AsynсActionCb - хотя можешь назвать вообще как нравиться а что значит callBack
|
||||
|
||||
typedef std::function<void()> AsyncActionCb; //метод без результата и параметров
|
||||
typedef std::function<bool(const String)> AsyncParamActionCb; //метод без результата и параметров
|
||||
|
||||
class CallBackTest {
|
||||
private:
|
||||
long count;
|
||||
AsyncActionCb _cb;
|
||||
AsyncParamActionCb _pcb;
|
||||
|
||||
|
||||
public:
|
||||
CallBackTest();
|
||||
void loop();
|
||||
void setCallback(AsyncActionCb cb);
|
||||
void setCallback(AsyncParamActionCb pcb);
|
||||
};
|
||||
//extern CallBackTest* CB;
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "Global.h"
|
||||
#include "Utils/JsonUtils.h"
|
||||
|
||||
class LineParsing {
|
||||
protected:
|
||||
@@ -13,12 +14,14 @@ class LineParsing {
|
||||
String _order;
|
||||
|
||||
String _addr;
|
||||
String _reg;
|
||||
String _pin;
|
||||
String _map;
|
||||
String _c;
|
||||
String _inv;
|
||||
String _state;
|
||||
String _db;
|
||||
String _type;
|
||||
|
||||
public:
|
||||
LineParsing() :
|
||||
@@ -29,18 +32,20 @@ class LineParsing {
|
||||
_descr{""},
|
||||
_order{""},
|
||||
_addr{""},
|
||||
_reg{""},
|
||||
_pin{""},
|
||||
_map{""},
|
||||
_c{""},
|
||||
_inv{""},
|
||||
_state{""},
|
||||
_db{""}
|
||||
_db{""},
|
||||
_type{""}
|
||||
|
||||
{};
|
||||
|
||||
void update() {
|
||||
//String order = sCmd.order();
|
||||
//pm.info("create '" + order + "'");
|
||||
//SerialPrint("I","module","create '" + order + "'");
|
||||
for (int i = 1; i < 12; i++) {
|
||||
if (i == 1) _key = sCmd.next();
|
||||
if (i == 2) _file = sCmd.next();
|
||||
@@ -49,7 +54,7 @@ class LineParsing {
|
||||
if (i == 5) _order = sCmd.next();
|
||||
}
|
||||
|
||||
for (int i = 1; i < 6; i++) {
|
||||
for (int i = 1; i < 10; i++) {
|
||||
String arg = sCmd.next();
|
||||
if (arg != "") {
|
||||
if (arg.indexOf("pin[") != -1) {
|
||||
@@ -64,15 +69,33 @@ class LineParsing {
|
||||
if (arg.indexOf("db[") != -1) {
|
||||
_db = extractInner(arg);
|
||||
}
|
||||
if (arg.indexOf("map[") != -1) {
|
||||
_map = extractInner(arg);
|
||||
}
|
||||
if (arg.indexOf("c[") != -1) {
|
||||
_c = extractInner(arg);
|
||||
}
|
||||
if (arg.indexOf("type[") != -1) {
|
||||
_type = extractInner(arg);
|
||||
}
|
||||
if (arg.indexOf("addr[") != -1) {
|
||||
_addr = extractInner(arg);
|
||||
}
|
||||
if (arg.indexOf("reg[") != -1) {
|
||||
_reg = extractInner(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_page.replace("#", " ");
|
||||
|
||||
_descr.replace("#", " ");
|
||||
_page.replace(".", " ");
|
||||
_descr.replace(".", " ");
|
||||
|
||||
createWidgetClass(_descr, _page, _order, _file, _key);
|
||||
}
|
||||
|
||||
//jsonWriteStr(configOptionJson, _key + "_pin", _pin);
|
||||
|
||||
String gkey() {
|
||||
return _key;
|
||||
}
|
||||
@@ -89,14 +112,29 @@ class LineParsing {
|
||||
return _order;
|
||||
}
|
||||
String gpin() {
|
||||
return _pin;
|
||||
return _pin; //
|
||||
}
|
||||
String ginv() {
|
||||
return _inv;
|
||||
return _inv; //
|
||||
}
|
||||
String gstate() {
|
||||
return _state;
|
||||
}
|
||||
String gmap() {
|
||||
return _map;
|
||||
}
|
||||
String gc() {
|
||||
return _c;
|
||||
}
|
||||
String gtype() {
|
||||
return _type;
|
||||
}
|
||||
String gaddr() {
|
||||
return _addr;
|
||||
}
|
||||
String gregaddr() {
|
||||
return _reg;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_key = "";
|
||||
@@ -105,12 +143,14 @@ class LineParsing {
|
||||
_descr = "";
|
||||
_order = "";
|
||||
_addr = "";
|
||||
_reg = "";
|
||||
_pin = "";
|
||||
_map = "";
|
||||
_c = "";
|
||||
_inv = "";
|
||||
_state = "";
|
||||
_db = "";
|
||||
_type = "";
|
||||
}
|
||||
|
||||
String extractInnerDigit(String str) {
|
||||
@@ -120,30 +160,30 @@ class LineParsing {
|
||||
}
|
||||
|
||||
void createWidgetClass(String descr, String page, String order, String filename, String topic) {
|
||||
String buf = "{}";
|
||||
if (!loadWidgetClass(filename, buf)) {
|
||||
return;
|
||||
}
|
||||
descr.replace("#", " ");
|
||||
page.replace("#", " ");
|
||||
if (filename != "na") {
|
||||
String buf = "{}";
|
||||
if (!loadWidgetClass(filename, buf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
jsonWriteStr(buf, "page", page);
|
||||
jsonWriteStr(buf, "order", order);
|
||||
jsonWriteStr(buf, "descr", descr);
|
||||
jsonWriteStr(buf, "topic", prex + "/" + topic);
|
||||
jsonWriteStr(buf, "page", page);
|
||||
jsonWriteStr(buf, "order", order);
|
||||
jsonWriteStr(buf, "descr", descr);
|
||||
jsonWriteStr(buf, "topic", prex + "/" + topic);
|
||||
|
||||
#ifdef LAYOUT_IN_RAM
|
||||
all_widgets += widget + "\r\n";
|
||||
all_widgets += widget + "\r\n";
|
||||
#else
|
||||
addFile("layout.txt", buf);
|
||||
addFileLn("layout.txt", buf);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool loadWidgetClass(const String& filename, String& buf) {
|
||||
buf = readFile(getWidgetFileClass(filename), 2048);
|
||||
bool res = !(buf == "Failed" || buf == "Large");
|
||||
if (!res) {
|
||||
//pm.error("on load" + filename);
|
||||
//SerialPrint("[E]","module","on load" + filename);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -151,4 +191,13 @@ class LineParsing {
|
||||
const String getWidgetFileClass(const String& name) {
|
||||
return "/widgets/" + name + ".json";
|
||||
}
|
||||
|
||||
//String jsonWriteStr1(String& json, String name, String value) {
|
||||
// DynamicJsonBuffer jsonBuffer;
|
||||
// JsonObject& root = jsonBuffer.parseObject(json);
|
||||
// root[name] = value;
|
||||
// json = "";
|
||||
// root.printTo(json);
|
||||
// return json;
|
||||
//}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user