Создание собственной визуализации
В SLS gateway можно создавать собственные визуализации - dashboards (панель управления), ui. Далее UI (user unterface, интерфейс пользователя).
UI - это веб-страничка, которую пользователь SLS может написать сам используя технологии веб-дизайна. Начиная от простого html + javascript и заканчивая различными популярными движками типа React, Vue и т.д. За UI в SLS отвечает файлик /ui.html
и соответвующий пункт меню веб-интерфейса SLS (UI).
Для разработки удобно подсмотреть как работает родной интерфейс SLS или UI, написанный энтузиастами, с помощью инструментов разработчика в современных браузерах.
Информация об устройствах
Для вывода статической информации об устройствах достаточно считать файлик devices.json
и распарсив его сформировать html страничку. В данный файлик сохраняется информация об устройствах каждые 30 минут и при ручном сохранении через меню Actions
-> Save
Также, файлик с устройствами удобно использовать для первичной инициализации UI.
Пример devices.json
{
"13827": {
"cid": 118,
"DateCode": "20191205",
"ep": {
"1": {
"devId": 770,
"In": {
"0": true,
"3": true,
"1026": true,
"1027": true,
"1029": true,
"65535": true
},
"Out": {
"0": true,
"4": true,
"65535": true
},
"profId": 260
}
},
"flags": 32,
"friendly_name": "dht_bedroom-out",
"ieeeAddr": "00158D00089762DB",
"Interview": {
"State": 4,
"TS": 1671972416
},
"ManufCode": 0,
"ManufName": "LUMI",
"ModelId": "lumi.weather",
"PS": 3,
"Rtg": [],
"SB": {
"humidity": "espEasy.lua",
"pressure": "#zigbee.setState(Event.ieeeAddr, \"pressure_mm\", zigbee.value(tostring(Event.ieeeAddr), \"pressure\") * 0.75, \"INT\")",
"temperature": "espEasy.lua"
},
"st": {
"battery": 92,
"humidity": 69.7,
"last_seen": 1706154914,
"linkquality": 109,
"pressure": 992.8,
"pressure_mm": 744,
"temperature": -12.98,
"trSeqNum": 226,
"voltage": 2.98
},
"supported": 1,
"type": "EndDevice"
}
}
{
"13827": {
"cid": 118,
"DateCode": "20191205",
"ep": {
"1": {
"devId": 770,
"In": {
"0": true,
"3": true,
"1026": true,
"1027": true,
"1029": true,
"65535": true
},
"Out": {
"0": true,
"4": true,
"65535": true
},
"profId": 260
}
},
"flags": 32,
"friendly_name": "dht_bedroom-out",
"ieeeAddr": "00158D00089762DB",
"Interview": {
"State": 4,
"TS": 1671972416
},
"ManufCode": 0,
"ManufName": "LUMI",
"ModelId": "lumi.weather",
"PS": 3,
"Rtg": [],
"SB": {
"humidity": "espEasy.lua",
"pressure": "#zigbee.setState(Event.ieeeAddr, \"pressure_mm\", zigbee.value(tostring(Event.ieeeAddr), \"pressure\") * 0.75, \"INT\")",
"temperature": "espEasy.lua"
},
"st": {
"battery": 92,
"humidity": 69.7,
"last_seen": 1706154914,
"linkquality": 109,
"pressure": 992.8,
"pressure_mm": 744,
"temperature": -12.98,
"trSeqNum": 226,
"voltage": 2.98
},
"supported": 1,
"type": "EndDevice"
}
}
Здесь, в удобном виде, хранится вся информация об устройствах: состояния, адреса, кластеры. настроенные SimpleBind.
Пример получения устройств в переменную allDevicesInfo
:
const allDevicesInfo = await httpGet('/api/zigbee/devices');
async function httpGet(url) {
console.log('starting http get');
const response = await fetch(url);
if (response.status !== 200) {
throw new Error('Network response was not 200', response);
}
return response.json();
}
const allDevicesInfo = await httpGet('/api/zigbee/devices');
async function httpGet(url) {
console.log('starting http get');
const response = await fetch(url);
if (response.status !== 200) {
throw new Error('Network response was not 200', response);
}
return response.json();
}
Обновление информации
Для динамического обновления информации в UI удобно использовать механизм веб-сокетов. SLS имеет свой WS сервер, который доступен по порту HTTP + 1. Например, если порт HTTP = 80, то порт WS = 81. Это следует учитывать при настройке маппинга портов для доступа к UI снаружи. Кстати, через веб-сокет обновляется и информация в родном веб-интефейсе SLS и работает страничка с логами.
Пример функций для работы с WS:
let startedWebSockets = false;
let wsSocket;
let wsTimer = 0;
function startWebSockets() {
// инициализация
let serverUrl = `ws://${window.location.hostname}:81/log`;
try {
console.log(`Connecting to ${serverUrl}`);
wsSocket = new WebSocket(serverUrl)
} catch (err) {
console.error(`Failed connecting to ${serverUrl}`, err);
return false;
}
wsSocket.binaryType = 'blob';
// при успешной инициализации, подписаться на обновление zigbee устройств и/или объектов
wsSocket.onopen = function (j) {
console.log('WS connected');
startedWebSockets = true;
clearTimeout(wsTimer);
subscribeToDevices();
subscribeToObjects();
};
// обработчик, вызываемый при поступлении сообщения от WS
wsSocket.onmessage = function (message) {
console.log(`WS data`);
const data = JSON.parse(message.data);
console.log(`Action: ${data.category} Data: `, data);
// ваша функция, котороя непосредственно обновляет ваш контент
deviceUpdate(data);
}
// обработчик перезапуска WS при его закрытии
wsSocket.onclose = function (j) {
startedWebSockets = false;
wsTimer = setTimeout('startWebSockets();', 5 * 1000);
console.log('WS disconnected');
}
}
// подписка на обновление устройств
function subscribeToDevices() {
console.log('Sending menu subscription request...');
wsSocket.send(JSON.stringify({
action: 'subscribe',
category: 'zigbee',
}));
return false;
}
// подписка на обновление объектов
function subscribeToObjects() {
console.log('Sending menu subscription request...');
wsSocket.send(JSON.stringify({
action: 'subscribe',
category: 'objects',
}));
return false;
}
let startedWebSockets = false;
let wsSocket;
let wsTimer = 0;
function startWebSockets() {
// инициализация
let serverUrl = `ws://${window.location.hostname}:81/log`;
try {
console.log(`Connecting to ${serverUrl}`);
wsSocket = new WebSocket(serverUrl)
} catch (err) {
console.error(`Failed connecting to ${serverUrl}`, err);
return false;
}
wsSocket.binaryType = 'blob';
// при успешной инициализации, подписаться на обновление zigbee устройств и/или объектов
wsSocket.onopen = function (j) {
console.log('WS connected');
startedWebSockets = true;
clearTimeout(wsTimer);
subscribeToDevices();
subscribeToObjects();
};
// обработчик, вызываемый при поступлении сообщения от WS
wsSocket.onmessage = function (message) {
console.log(`WS data`);
const data = JSON.parse(message.data);
console.log(`Action: ${data.category} Data: `, data);
// ваша функция, котороя непосредственно обновляет ваш контент
deviceUpdate(data);
}
// обработчик перезапуска WS при его закрытии
wsSocket.onclose = function (j) {
startedWebSockets = false;
wsTimer = setTimeout('startWebSockets();', 5 * 1000);
console.log('WS disconnected');
}
}
// подписка на обновление устройств
function subscribeToDevices() {
console.log('Sending menu subscription request...');
wsSocket.send(JSON.stringify({
action: 'subscribe',
category: 'zigbee',
}));
return false;
}
// подписка на обновление объектов
function subscribeToObjects() {
console.log('Sending menu subscription request...');
wsSocket.send(JSON.stringify({
action: 'subscribe',
category: 'objects',
}));
return false;
}
Также, обновлять данные можно и статически с помощью http_api. Следующий пример выводит значение температуры с датчика
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
<div id="lbl_temp"></div>
<script>
fetch('/api/zigbee?action=getStateValue&dev=0x000D6F00106A67FD&name=temperature')
.then((response) => {
return response.json();
})
.then((data) => {
document.getElementById("lbl_temp").innerHTML = '<i class="fa fa-thermometer-full" aria-hidden="true"></i> ' + data;
});
</script>
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
<div id="lbl_temp"></div>
<script>
fetch('/api/zigbee?action=getStateValue&dev=0x000D6F00106A67FD&name=temperature')
.then((response) => {
return response.json();
})
.then((data) => {
document.getElementById("lbl_temp").innerHTML = '<i class="fa fa-thermometer-full" aria-hidden="true"></i> ' + data;
});
</script>
Для управления устройствами также можно использовать встроенные http_api.
Пример функции для управления устройствами
function setState(device, name, value) {
return httpGet(`/api/zigbee?dev=${device}&action=setState&name=${name}&value=${value}`);
}
function setState(device, name, value) {
return httpGet(`/api/zigbee?dev=${device}&action=setState&name=${name}&value=${value}`);
}
Примеры
Энтузиасты SLS разработали свои варианты UI и поделились с нами своими наработками. Можно использовать как в готовом виде, так и для разработки собственного варианта.
Пример dashboard от Nick7zmail
Скачать
Пример dashboard от BalagurovAV
Пример dashboard от solominsn
solominsn: Кстати, а кто-нибудь пользуется UI? Мне понравился Gateway/dashboard/nick7zmail/ui.html, хотел не много код подправить для себя, а там код обфускатором прошли.
solominsn: я переписал не много этот код. Последняя версия bootstrap, свежий JS (думаю что все используют версию браузера не старше 2 лет). Не много упростил конфигурирование.