Files
IoTManagerWeb/examples/Dashboard.svelte

1677 lines
58 KiB
Svelte
Raw Normal View History

2021-09-16 02:00:52 +08:00
<script>
import { onMount } from "svelte";
import Cookies, { get } from "js-cookie";
//import { Tabs, Tab, TabList, TabPanel } from "svelte-tabs";
import Toggle from "svelte-toggle";
import Chart from "svelte-frappe-charts";
import Logo from "./Logo.svelte";
import mqtt from "mqtt/dist/mqtt.min";
// -------------------Swipe-------------------------------------
import { Swipe, SwipeItem } from "svelte-swipe";
let swipe_holder_height = 0;
let active_item;
let SwipeComp;
function heightChanged({ detail }) {
if (detail.height < 700) {
swipe_holder_height = 700;
} else {
swipe_holder_height = detail.height;
}
}
let newPage;
function changeSlide(i) {
newPage = 0;
newPage = 1;
SwipeComp.goTo(i);
}
const swipeConfig = {
autoplay: false,
delay: 2000,
showIndicators: false,
transitionDuration: 1000,
defaultIndex: 0,
};
let items = [];
let MQTTconnections = [];
// темная тема
onMount(async () => {
if (Cookies.get("darktheme") == "true") {
window.document.body.classList.value = "dark-mode";
//window.document.body.classList.toggle("dark-mode");
}
});
function toggleTheme() {
// window.document.body.classList.toggle("dark-mode");
if (window.document.body.classList.value == "dark-mode") {
window.document.body.classList.value = "light-mode";
Cookies.set("darktheme", "False", { expires: 365 });
} else {
window.document.body.classList.toggle("dark-mode");
Cookies.set("darktheme", "true", { expires: 365 });
}
}
//секция переменных============================================
let myip = document.location.hostname;
let configSetupJson = "{}";
let espName = "IotManager";
let chipID = "";
let colors;
let lineOptions;
let axisOptions;
let graf_1 = [];
let graf_2 = [];
let NewWiget = "";
let NewPage = "";
let NewPrefics = "";
let date = "";
let hours = "";
let minutes = "";
let temp;
let selectedprefics;
// декларируем массив для списка всех ESP в сети
let devices = [];
// декларируем массив для списка всех websocket
let socket = [];
// декларируем массив для списка всех виджетов
let wigets = [];
// декларируем массив для списка всех страниц
let pages = [];
// декларируем массив для списка всех префиксов
let prefics = [{ prefics: "" }];
// выбраный префикс
let selected;
// массивы для построения графиков
let graf_time = [];
let graf_val = [];
let dataLine = [];
// ==на время разработки==
//myip = "192.168.36.127";
let connectionType = "MQTT";
let client;
let topic;
let connected = false;
let clientId = "IotManager_";
// MQTT====================================================================
function MQTTChange() {
// console.log("The selected option is " + JSON.stringify(selected));
Cookies.set("selectedMQTT", selected, { expires: 365 });
wigets = [];
prefics = [];
pages = [];
newPage = 0;
clientId += "_" + Math.floor(Math.random() * 10000);
connected = false;
const mqtt_options = {
clientId: clientId,
servers: [
{
host: selected.mqtt_host,
port: Number(selected.mqtt_port),
protocol: selected.connection_protocol,
},
],
path: "" + selected.mqtt_path,
protocolId: "MQTT",
protocolVersion: 4,
keepalive: 30,
clean: true,
reconnectPeriod: 100000,
connectTimeout: 30 * 1000,
rejectUnauthorized: false,
};
mqtt_options.username = selected.mqtt_username;
mqtt_options.password = selected.mqtt_password;
topic = selected.mqtt_prefix;
const url = `${mqtt_options.servers[0].protocol}://${mqtt_options.servers[0].host}:${mqtt_options.servers[0].port}${mqtt_options.path}`;
const onConnect = () => {
console.log(`MQTT connected ${url}`);
connected = client.connected;
const topic = selected.mqtt_prefix;
client.subscribe(topic + "/#", function (err) {
if (err) {
console.error(err);
} else {
console.log(`MQTT subscribed on topic '${topic}'`);
client.publish(topic, "HELLO");
}
});
};
const onMessage = (topic, message) => {
const msg = message.toString();
const time = new Date().getTime();
addMessage(msg, topic);
};
//try{
client = mqtt.connect(mqtt_options);
client.on("connect", onConnect);
client.on("message", onMessage);
client.on("error", (err) => {
console.log("MQTT", err);
client.end();
connected = client.connected;
});
client.on("close", () => {
console.log("MQTT " + clientId + " disconnected");
connected = client.connected;
});
}
if (connectionType == "MQTT") {
// ==на время разработки==
// MQTTconnections = JSON.parse(MQTTconnections);
MQTTconnections = [
{
user_id: "1",
connection_name: "demo",
connection_protocol: "wss",
mqtt_host: "meef.ru",
mqtt_port: "18883",
mqtt_prefix: "/demo",
mqtt_username: "IotManager:guest",
mqtt_password: "guest",
mqtt_path: "/ws",
mqtt_id: "mqtt_id",
},
{
user_id: "1",
connection_name: "IotManager",
connection_protocol: "wss",
mqtt_host: "meef.ru",
mqtt_port: "18883",
mqtt_prefix: "/IotManager",
mqtt_username: "IotManager:guest",
mqtt_password: "guest",
mqtt_path: "/ws",
mqtt_id: "mqtt_id",
},
];
if (Cookies.get("selectedMQTT")) {
selected = Cookies.get("selectedMQTT");
} else {
selected = MQTTconnections[0];
}
if (selected) {
selected = JSON.parse(selected);
}
MQTTChange();
if (Cookies.get("darktheme") == "") {
window.document.body.classList.value = "dark-mode";
}
if (!Cookies.get("darktheme")) {
window.document.body.classList.value = "dark-mode";
Cookies.set("darktheme", "true", { expires: 365 });
}
}
// MQTT =============================================================================================
// подключаемся к локальной машине, получаем карту сети
function getNetworkMap() {
devices = JSON.parse(
'[{"deviceID":"","deviceIP":"' + myip + '","deviceName":""}]'
);
devices[0].id = 0;
devices[0].status = false;
console.log("Подключаемся к WS на localhost ", devices);
if (connectionType != "MQTT") {
socket[0] = new WebSocket("ws://" + myip + "/ws");
socket[0].addEventListener("open", function (event) {
devices[0].id = 0;
devices[0].status = true;
connected = true;
devices = devices;
console.log("WS CONNECTED! " + myip);
// получаем "карту сети" по websocket
socket[0].send("getNetworkMap");
// получаем конфигурацию ESP по websocket
socket[0].send("HELLO");
});
socket[0].addEventListener("message", function (event) {
if (Cookies.get("consolelog") == "true") {
console.log("NEW data packet " + myip, event.data);
}
// запускаем обработку пришедшего сообщения
addMessage(event.data, 0);
});
// Обработка ошибок websocket
socket[0].addEventListener("close", (event) => {
if (item) {
console.log("ws close " + item.deviceIP);
} else {
console.log("ws close " + myip);
}
devices[0].status = false;
connected = false;
devices = devices;
});
socket[0].addEventListener("error", function (event) {
if (item) {
console.log(item.deviceIP + " WebSocket error: ", event);
} else {
console.log(myip + " WebSocket error: ", event);
}
devices[0].status = false;
connected = false;
devices = devices;
});
}
}
if (connectionType != "MQTT") {
getNetworkMap();
}
function addConnection(devices) {
if (devices) {
devices.forEach(function (item, i, arr) {
console.log("trying connect", item);
// Create WebSocket connection.
socket[i] = new WebSocket("ws://" + item.deviceIP + "/ws");
// Connection opened
socket[i].addEventListener("open", function (event) {
devices[i].id = i;
devices[i].status = true;
connected = true;
devices = devices;
console.log("WS CONNECTED! " + item.deviceIP);
socket[i].send("HELLO");
});
// Listen for messages
socket[i].addEventListener("message", function (event) {
// вывод отладочных сообщений в консоль
if (Cookies.get("consolelog") == "true") {
console.log("NEW data packet " + item.deviceIP, event.data);
}
// запускаем обработку пришедшего сообщения
let time = new Date().getTime();
addMessage(event.data, i);
});
// Обработка ошибок websocket
socket[i].addEventListener("close", (event) => {
console.log("ws close " + item.deviceIP);
devices[i].status = false;
connected = false;
devices = devices;
Shuttervisibil();
});
socket[i].addEventListener("error", function (event) {
console.log(item.deviceIP + " WebSocket error: ", event);
devices[i].status = false;
connected = false;
devices = devices;
Shuttervisibil();
});
});
}
}
//console.log(devices);
//обрабатываем пришедшее сообщение =====================================================================
function addMessage(data, socket) {
if (connectionType == "MQTT") {
// console.log("NEW data packet " + socket, data);
topic = socket;
}
let json;
let tmp;
try {
let net = JSON.parse(data);
// Если пришла карта сети
if (net[0].deviceID) {
console.log("пришла карта сети :", net);
devices = net;
//Conf=[];
addConnection(devices);
NetworkMap = data;
}
} catch (e) {
// console.log('ERROR parse JSON', tmp);
}
tmp = "[" + data + "]";
//tmp = [data];
try {
tmp = JSON.parse(tmp);
tmp.forEach(function (json, i, array) {
// собираем виджеты
// если пришедшее сообщение виджет
if (json.widget) {
// ищем его в массиве виджетов
NewWiget = "";
wigets.forEach(function (item, i, array) {
if (json.topic == item.topic) {
NewWiget = json.topic;
// console.log("Нашли совпадение виджета");
}
});
// ищем новые страницы
NewPage = "";
pages.forEach(function (item, i, array) {
if (json.page == item.page) {
NewPage = json.page;
// console.log("Нашли совпадение страницы");
}
});
// ищем новые префиксы
NewPrefics = "";
prefics.forEach(function (item, i, array) {
let topic = getprefics(json.topic);
if (topic == item.prefics) {
NewPrefics = item.prefics;
// console.log("Нашли совпадение префикса");
}
});
// Новый виджет дописываем в массив виджетов
if (!NewWiget) {
// дописываем виджету из какого сокета он получен
json.socket = socket;
// дописываем виджету префикс
json.prefics = getprefics(json.topic);
json.closed = true;
wigets = [...wigets, json];
// сортируем
wigets.sort((a, b) => a.order - b.order);
}
// Дописываем новую страницу в массив страниц
if (!NewPage) {
pages = [
...pages,
JSON.parse(
JSON.stringify({ page: json.page, topic: json.topic })
),
];
pages.sort(function (a, b) {
if (a.page < b.page) {
return -1;
}
if (a.page > b.page) {
return 1;
}
return 0;
});
}
// Дописываем новый префикс в массив префиксов
if (!NewPrefics) {
prefics = [
...prefics,
JSON.parse(JSON.stringify({ prefics: getprefics(json.topic) })),
];
}
}
// закончили сбор виджетов
// если новое сообщение статус
if (json.status) {
// ищем виджет к которому относится этот статус
wigets.forEach(function (element) {
//отличие MQTT и WS========================================================!!!!!!!!!!!!!!
let messegetopic;
if (connectionType === "MQTT") {
messegetopic = topic.replace("/status", "");
} else {
messegetopic = json.topic.replace("/status", "");
}
// ========================================================================
if (element.topic == messegetopic) {
// если получен статус график
if (element.widget == "chart") {
graf_time = [];
graf_val = [];
try {
temp = json.status;
temp.forEach(function (item, i, arr) {
// получаем время
date = new Date(item.x * 1000);
hours = date.getHours();
// minutes =date.getMinutes();
minutes = checkTime(date.getMinutes());
function checkTime(i) {
return i < 10 ? "0" + i : i;
}
let days = ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"];
let dayW = days[date.getDay()];
//let dw = days[dayW];
let day = date.getDate();
let month = checkTime(date.getMonth());
//graf_time = [... graf_time, hours+':'+minutes+' '+day+'.'+month, ];
if ((element.dateFormat = "HH:mm")) {
graf_time = [
...graf_time,
hours + ":" + minutes + " " + dayW,
];
} else if ((element.dateFormat = "DD.MM.YYYY")) {
graf_time = [
...graf_time,
day + "." + month + " " + dayW,
];
} else {
graf_time = [...graf_time, hours + ":" + minutes];
}
graf_val = [...graf_val, item.y1];
});
axisOptions = { xAxisMode: "tick", xIsSeries: true };
if (element.pointRadius == "0") {
lineOptions = { hideDots: 1, regionFill: 1, spline: 1 };
} else {
lineOptions = { regionFill: 1, dotSize: 3, spline: 1 };
}
dataLine = {
labels: graf_time,
datasets: [
{
name: element.descr,
values: graf_val,
},
],
};
if (!element.status) {
// архив графика отсутсутствует, то создаем статус и заполняем
element.status = dataLine;
} else {
//console.log(" дописываем новые значения к имеющимся");
element.status.labels = [
...element.status.labels,
dataLine.labels[0],
];
element.status.datasets[0].values = [
...element.status.datasets[0].values,
dataLine.datasets[0].values[0],
];
}
} catch (e) {
console.log(
"полученные данные для графика содержат ошибки",
element.status
);
}
//два графика в одном / пока работает криво только для WS , надо делать
if (connectionType === "MQTT") {
if (topic.includes("_1/status")) {
graf_1 = element.status.datasets[0];
}
if (topic.includes("_2/status")) {
graf_2 = element.status.datasets[0];
element.status.datasets[1] = graf_1;
}
} else {
}
}
// данные для прочих витжетов (заменяем статус на новый)
else {
element.status = json.status;
element.send = false;
}
}
wigets = wigets;
devices = devices;
pages = pages;
});
}
// для mysens
if (json.info) {
// ищем виджет к которому относится INFO
wigets.forEach(function (element) {
//отличие MQTT и WS========================================================!!!!!!!!!!!!!!
let messegetopic;
if (connectionType === "MQTT") {
messegetopic = topic.replace("/status", "");
} else {
messegetopic = json.topic.replace("/status", "");
}
// ========================================================================
if (element.topic == messegetopic) {
element.nodeInfo = json.info;
}
});
}
if (json.color) {
// ищем виджет к которому относится цвет INFO
wigets.forEach(function (element) {
//отличие MQTT и WS========================================================!!!!!!!!!!!!!!
let messegetopic;
if (connectionType === "MQTT") {
messegetopic = topic.replace("/status", "");
} else {
messegetopic = json.topic.replace("/status", "");
}
// ========================================================================
if (element.topic == messegetopic) {
element.nodeInfoColor = json.color;
}
});
}
});
} catch (e) {
console.log("ERROR parse JSON", tmp);
}
tmp = null;
temp = null;
json = null;
data = null;
NewWiget = null;
NewPage = null;
NewPrefics = null;
date = null;
hours = null;
minutes = null;
}
// функция получаем префикс
function getprefics(topic) {
if (topic[0] == "/") {
topic = topic.slice(0, topic.indexOf("/", 2));
return topic;
} else {
topic = topic.slice(0, topic.indexOf("/", 0));
return topic;
}
}
// функция если произошла смена префикса в интерфейсе
function handleSubmit() {
console.log("The selected option is " + JSON.stringify(selected.prefics));
selectedprefics = selected.prefics;
}
// шлем данные в websocket
function WSpush(ws, uri, val) {
// console.log(ws, uri, val);
if (connectionType == "MQTT") {
client.publish(uri + "/control", val.toString());
} else {
socket[ws].send(
'{"path":"' + uri + '/control", "status":"' + val.toString() + '"}'
);
if (socket[ws].readyState != 1) {
addConnection(devices);
// добавить GET запрос
}
}
}
if (connectionType != "MQTT") {
// addConnection(devices);
}
// обратный отсчет для Shutter
let frame;
let elapsed = 0;
let duration = 5000;
let last_time = window.performance.now();
(function update() {
frame = requestAnimationFrame(update);
const time = window.performance.now();
elapsed += Math.min(time - last_time, duration - elapsed);
last_time = time;
if (elapsed == duration) {
Shutterhiddn();
}
})();
let Shuttervisibl = "visibility: hidden;";
function Shuttervisibil() {
Shuttervisibl = "visibility: visible;";
// duration = sec;
elapsed = 0;
if (elapsed === duration) {
Shuttervisibl = "visibility: hidden;";
}
}
function Shutterhiddn() {
Shuttervisibl = "visibility: hidden;";
}
Shuttervisibil();
</script>
<!---->
{#if connectionType == "MQTT"}
{#if MQTTconnections[1]}
<form on:submit|preventDefault={MQTTChange}>
<select bind:value={selected} on:change={MQTTChange}>
{#each MQTTconnections as MQTTconnection}
{#if MQTTconnection.connection_name == selected.connection_name}
<option selected value={MQTTconnection}>
{MQTTconnection.connection_name}
</option>
{:else}
<option value={MQTTconnection}>
{MQTTconnection.connection_name}
</option>
{/if}
{/each}
</select>
</form>
{/if}
{:else}
<div
class="Shutter"
style="{Shuttervisibl} position: absolute; z-index: 1; right: 1%; top: 0px"
id="layer1"
>
<span>
<progress value={elapsed / duration} />
<br /><br />
<!--Перечисляем девайсы в сети-->
{#each devices as NetworkDevice, i}
{#if !NetworkDevice.status && NetworkDevice.status != false}
<p align="left" style=" margin-top: -5px; margin-bottom: -5px">
{NetworkDevice.deviceIP}
{NetworkDevice.deviceName} waiting
</p>
{/if}
{#if NetworkDevice.status == true}
<p
align="left"
style="color: green; margin-top: -5px; margin-bottom: -5px"
>
{NetworkDevice.deviceIP}
{NetworkDevice.deviceName} connected
</p>
{/if}
{#if NetworkDevice.status == false}
<p
align="left"
style="color: red; margin-top: -5px; margin-bottom: -5px"
on:click={addConnection(devices)}
>
{NetworkDevice.deviceIP}
{NetworkDevice.deviceName} disconnected
</p>
{/if}
{/each}
</span>
</div>
{/if}
<div
style=" position: absolute; z-index: 2; right: 7%; top: 0%; color:red"
on:click={toggleTheme}
id="toggleTheme"
>
🔆
</div>
{#if connectionType == "MQTT"}
{#if connected == false}
<div
style=" position: absolute; z-index: 2; right: 2%; top: 0%; color:red"
on:click={toggleTheme}
id="layerMQTT"
>
MQTT
</div>
{#if MQTTconnections[0].mqtt_port == ""}
<div align="center">
Для подключения MQTT обязателььно должен быть указан порт "MQTT wss port
(TLS)"
</div>
{/if}
{/if}
{#if connected == true}
<div
style=" position: absolute; z-index: 2; right: 2%; top: 0%; color:green"
on:click={toggleTheme}
id="layerMQTT"
>
MQTT
</div>
{/if}
{:else if connected == true}
<div
on:mouseenter={Shuttervisibil}
on:click={Shutterhiddn}
style="color:green; position: absolute; z-index: 2; right: 1.5%; top: 0px"
id="layerWS; "
>
localNET
</div>
{:else}
<div
on:mouseenter={Shuttervisibil}
on:click={Shutterhiddn}
style="color:red; position: absolute; z-index: 2; right: 1.5%; top: 0px"
id="layerWS; "
>
localNET
</div>
{/if}
<!-- селектор префиксов -->
{#if prefics[2]}
<form on:submit|preventDefault={handleSubmit}>
<select style="float: left" bind:value={selected} on:change={handleSubmit}>
{#each prefics as curentprefics}
<option value={curentprefics}>
{curentprefics.prefics}
</option>
{/each}
</select>
</form>
{/if}
<!--закончили селектор префиксов -->
{#if !pages[0]}
<p align="center"><Logo /></p>
{/if}
<!-------------------------------------------------------->
<p align="center">
{#each pages as page, i}
<button type="button" on:click={() => changeSlide(i)}>{page.page}</button>
{/each}
</p>
{#if newPage == 1}
<div class="swipe-holder" style="height:{swipe_holder_height}px">
<Swipe bind:active_item bind:this={SwipeComp}>
{#each pages as pagesName, i}
<SwipeItem
active={active_item == i}
allow_dynamic_height={true}
on:swipe_item_height_change={heightChanged}
>
<table border="0" style="margin-left: 0%" width="99%">
{#each wigets as widget, i}
<!--Отображаем виджеты только для выбранного префикса-->
{#if !selectedprefics || selectedprefics === widget.prefics}
{#if widget.page === pagesName.page}
<tr>
<!-- Toggle -->
{#if widget.widget === "toggle"}
<td style="width: 100%;">
<span style="float: left"> {widget.descr}</span>
</td>
<td />
<td>
{#if widget.status == "1"}
<span style="float: right">
<Toggle
style="float: right"
on:toggle={WSpush(widget.socket, widget.topic, 0)}
label=""
toggledColor="#6495ED"
untoggledColor="#FF6347"
switchColor="#eee"
/>
</span>
{:else}
<span style="float: right">
<Toggle
on:toggle={WSpush(widget.socket, widget.topic, 1)}
label=""
toggledColor="#FF6347"
untoggledColor="gray"
switchColor="#eee"
toggled=""
/>
</span>
{/if}
</td>{/if}
<!-- anydata -->
{#if widget.widget === "anydata"}
<td>
{#if widget.descrColor}
<span style="float: left">
<lable
align="left"
style="color: {widget.descrColor}; font-family: '{widget.descrfont}'"
>{widget.descr}</lable
>
</span>
{:else}
<span
style="float: left; font-family: '{widget.descrfont}'"
>
{widget.descr}
<!--Инфо от ноды mysens отображается под названием виджета -->
<div
class="letter"
align="left"
style="color: {!widget.nodeInfoColor
? 'gray'
: widget.nodeInfoColor}"
>
{!widget.nodeInfo ? "" : widget.nodeInfo}
</div>
</span>
{/if}
</td>
<td>
<!--Инфо от ноды mysens отображается по середине
<lable align='left' style='color: {!widget.nodeInfoColor ? 'gray' : widget.nodeInfoColor}'>
</lable>-->
</td>
<td align="right" style="white-space: nowrap;">
<!--Делаем anidata разноцветными если есть кастомизация цвета-->
{#if Array.isArray(widget.color) && widget.status}
{#each widget.color as anydataColor, i}
<!--// убираем знаки после запятой -->
{#if anydataColor.level && widget.status < anydataColor.level && widget.status > widget.color[i - 1].level && i > 0}
<lable
align="left"
style="color: {anydataColor.value}; font-family: '{widget.font}' "
>{Math.round(widget.status * 10) / 10
? Math.round(widget.status * 10) / 10
: widget.status}{!widget.after
? ""
: widget.after}</lable
>
{/if}
{/each}
<!--если цвет задан значением а не массивом-->
{:else if widget.color && widget.status}
<lable
align="left"
style="color: {widget.color}; font-family: '{widget.font}'"
>{Math.round(widget.status * 10) / 10
? Math.round(widget.status * 10) / 10
: widget.status}{widget.after}</lable
>
<!--если цвет не задан и статус пустой-->
{:else if !widget.status}
<lable align="left">...</lable>
<!--если цвет не задан-->
{:else if widget.status}
<lable
align="left"
style="font-family: '{widget.font}'"
>{Math.round(widget.status * 10) / 10
? Math.round(widget.status * 10) / 10
: widget.status}{!widget.after
? ""
: widget.after}</lable
>
{:else}
<lable
align="left"
style="font-family: '{widget.font}'"
>
{!widget.status ? "" : widget.status}{!widget.after
? ""
: widget.after}
</lable>
{/if}
</td>
{/if}
<!-- input -->
{#if widget.widget === "input"}
<td><span style="float: left"> {widget.descr}</span></td>
<td />
{#if widget.type === "number"}
<td align="right">
<div
style="float: right; display:inline; width: 120px "
>
<input
type="button"
value="- "
style=" border: 1px solid lightblue; width: 25px"
on:click={WSpush(
widget.socket,
widget.topic,
widget.status - 1
)}
/>
<input
class:red={widget["send"] == true}
style="width: 45px "
type="tel"
neme={widget.topic}
bind:value={widget.status}
size="20"
on:change={((widget["send"] = true),
WSpush(
widget.socket,
widget.topic,
widget.status
))}
min="-1000"
max="1000000"
/>
<input
type="button"
value="+ "
style="border: 1px solid lightblue; width: 25px"
on:click={WSpush(
widget.socket,
widget.topic,
widget.status - 1 + 2
)}
/>
</div>
</td>
{:else if widget.type === "time"}
<td align="right">
<div style="float: right; display:inline-block">
<input
class:red={widget["send"] == true}
type="time"
bind:value={widget.status}
size="20"
on:change={((widget["send"] = true),
WSpush(
widget.socket,
widget.topic,
widget.status
))}
min="00:00"
max="23:59"
required
/>
</div>
</td>
{:else}
<td align="right"
><div style="display:inline-block">
<input
class:red={widget["send"] == true}
bind:value={widget.status}
size="20"
on:change={((widget["send"] = true),
WSpush(
widget.socket,
widget.topic,
widget.status
))}
/>
</div></td
>
{/if}
{/if}
<!-- btn -->
{#if widget.widget == "btn"}
<td><span style="float: left">{widget.descr}</span></td>
<td />
<td align="right">
{#if widget.status != 0 && widget.status != 1}
<button
class="btn"
on:click={((widget["send"] = true),
WSpush(widget.socket, widget.topic, 1))}
><span
>&nbsp;&nbsp;{!widget.status
? ""
: widget.status}&nbsp;&nbsp;</span
></button
><br />
{:else}
<button
class="btn"
on:click={((widget["send"] = true),
WSpush(widget.socket, widget.topic, 1))}
><span
>&nbsp;&nbsp; {!widget.text
? ""
: widget.text}&nbsp;&nbsp;</span
></button
><br />
{/if}
</td>
{/if}
<!-- select -->
{#if widget.widget === "select"}
<td><span style="float: left">{widget.descr}</span></td>
<td />
<td align="right">
{#if widget.status == 0}
<button
class="btnoff"
on:click={((widget["send"] = true),
(this.style.border = "1px solid red"),
WSpush(widget.socket, widget.topic, 1))}
><span
>{!widget.options[0]
? "OFF"
: widget.options[0]}</span
></button
><br />
{:else}
<button
class="btn"
on:click={((widget["send"] = true),
(this.style.border = "1px solid red"),
WSpush(widget.socket, widget.topic, 0))}
><span
>{!widget.options[1]
? "ON"
: widget.options[1]}</span
></button
><br />
{/if}
</td>
{/if}
<!-- chart -->
{#if widget.widget === "chart"}
<td colspan="3">
{#if widget.status}
{#if widget.topic.includes("_2")}
<Chart
data={widget.status}
{lineOptions}
{axisOptions}
colors={[!widget.color ? "red" : widget.color]}
type={!widget.type ? "line" : widget.type}
height="300"
/>
{:else if !widget.topic.includes("_1")}
{widget.descr}
<Chart
data={widget.status}
{lineOptions}
{axisOptions}
colors={[
!widget.color ? "light-blue" : widget.color,
]}
type={!widget.type ? "line" : widget.type}
height="300"
/>
{/if}
{/if}
</td>
{/if}
<!-- range -->
{#if widget.widget === "range"}
<td colspan="3">
<div>
{widget.descr}
{widget.status / 10}
{widget.after}
</div>
<label>
<input
type="range"
bind:value={widget.status}
min={widget.min}
max={widget.max * 10}
on:change={WSpush(
widget.socket,
widget.topic,
widget.status
)}
/>
</label>
</td>
{/if}
</tr>
{/if}
{/if}
{/each}
</table>
</SwipeItem>
{/each}
</Swipe>
</div>
{/if}
<!------------------------------------------------------->
<!--
<Tabs>
<table border="0" style="margin-left: 0%" width="99%">
<tr
><td>
<TabList>
{#each pages as pagesName, i}
<Tab>{pagesName.page}</Tab>
{/each}
</TabList>
</td></tr
>
</table>
{#each pages as pagesName, i}
<TabPanel>
<table border="0" style="margin-left: 0%" width="99%">
{#each wigets as widget, i}
!--Отображаем виджеты только для выбранного префикса--
{#if !selectedprefics || selectedprefics === widget.prefics}
{#if widget.page === pagesName.page}
<tr>
-- Toggle --
{#if widget.widget === "toggle"}
<td style="width: 100%;">
<span style="float: left"> {widget.descr}</span>
</td>
<td />
<td>
{#if widget.status == "1"}
<span style="float: right">
<Toggle
style="float: right"
on:toggle={WSpush(widget.socket, widget.topic, 0)}
label=""
toggledColor="#6495ED"
untoggledColor="#FF6347"
switchColor="#eee"
/>
</span>
{:else}
<span style="float: right">
<Toggle
on:toggle={WSpush(widget.socket, widget.topic, 1)}
label=""
toggledColor="#FF6347"
untoggledColor="gray"
switchColor="#eee"
toggled=""
/>
</span>
{/if}
</td>{/if}
-- anydata --
{#if widget.widget === "anydata"}
<td>
{#if widget.descrColor}
<span style="float: left">
<lable
align="left"
style="color: {widget.descrColor}; font-family: '{widget.descrfont}'"
>{widget.descr}</lable
>
</span>
{:else}
<span
style="float: left; font-family: '{widget.descrfont}'"
>
{widget.descr}
--Инфо от ноды mysens отображается под названием виджета --
<div
class="letter"
align="left"
style="color: {!widget.nodeInfoColor
? 'gray'
: widget.nodeInfoColor}"
>
{!widget.nodeInfo ? "" : widget.nodeInfo}
</div>
</span>
{/if}
</td>
<td>
--Инфо от ноды mysens отображается по середине
<lable align='left' style='color: {!widget.nodeInfoColor ? 'gray' : widget.nodeInfoColor}'></lable>
--
</td>
<td align="right" style="white-space: nowrap;">
--Делаем anidata разноцветными если есть кастомизация цвета--
{#if Array.isArray(widget.color) && widget.status}
{#each widget.color as anydataColor, i}
--// убираем знаки после запятой --
{#if anydataColor.level && widget.status < anydataColor.level && widget.status > widget.color[i - 1].level && i > 0}
<lable
align="left"
style="color: {anydataColor.value}; font-family: '{widget.font}' "
>{Math.round(widget.status * 10) / 10
? Math.round(widget.status * 10) / 10
: widget.status}{!widget.after
? ""
: widget.after}</lable
>
{/if}
{/each}
--если цвет задан значением а не массивом--
{:else if widget.color && widget.status}
<lable
align="left"
style="color: {widget.color}; font-family: '{widget.font}'"
>{Math.round(widget.status * 10) / 10
? Math.round(widget.status * 10) / 10
: widget.status}{widget.after}</lable
>
--если цвет не задан и статус пустой--
{:else if !widget.status}
<lable align="left">...</lable>
--если цвет не задан--
{:else if widget.status}
<lable align="left" style="font-family: '{widget.font}'"
>{Math.round(widget.status * 10) / 10
? Math.round(widget.status * 10) / 10
: widget.status}{!widget.after
? ""
: widget.after}</lable
>
{:else}
<lable align="left" style="font-family: '{widget.font}'">
{!widget.status ? "" : widget.status}{!widget.after
? ""
: widget.after}
</lable>
{/if}
</td>
{/if}
-- input --
{#if widget.widget === "input"}
<td><span style="float: left"> {widget.descr}</span></td>
<td />
{#if widget.type === "number"}
<td align="right">
<div
style="float: right; display:inline; width: 120px "
>
<input
type="button"
value="- "
style=" border: 1px solid lightblue; width: 25px"
on:click={WSpush(
widget.socket,
widget.topic,
widget.status - 1
)}
/>
<input
class:red={widget["send"] == true}
style="width: 45px "
type="tel"
neme={widget.topic}
bind:value={widget.status}
size="20"
on:change={((widget["send"] = true),
WSpush(widget.socket, widget.topic, widget.status))}
min="-1000"
max="1000000"
/>
<input
type="button"
value="+ "
style="border: 1px solid lightblue; width: 25px"
on:click={WSpush(
widget.socket,
widget.topic,
widget.status - 1 + 2
)}
/>
</div>
</td>
{:else if widget.type === "time"}
<td align="right">
<div style="float: right; display:inline-block">
<input
class:red={widget["send"] == true}
type="time"
bind:value={widget.status}
size="20"
on:change={((widget["send"] = true),
WSpush(widget.socket, widget.topic, widget.status))}
min="00:00"
max="23:59"
required
/>
</div>
</td>
{:else}
<td align="right"
><div style="display:inline-block">
<input
class:red={widget["send"] == true}
bind:value={widget.status}
size="20"
on:change={((widget["send"] = true),
WSpush(widget.socket, widget.topic, widget.status))}
/>
</div></td
>
{/if}
{/if}
-- btn --
{#if widget.widget == "btn"}
<td><span style="float: left">{widget.descr}</span></td>
<td />
<td align="right">
{#if widget.status != 0 && widget.status != 1}
<button
class="btn"
on:click={((widget["send"] = true),
WSpush(widget.socket, widget.topic, 1))}
><span
>&nbsp;&nbsp;{!widget.status
? ""
: widget.status}&nbsp;&nbsp;</span
></button
><br />
{:else}
<button
class="btn"
on:click={((widget["send"] = true),
WSpush(widget.socket, widget.topic, 1))}
><span
>&nbsp;&nbsp; {!widget.text
? ""
: widget.text}&nbsp;&nbsp;</span
></button
><br />
{/if}
</td>
{/if}
-- select --
{#if widget.widget === "select"}
<td><span style="float: left">{widget.descr}</span></td>
<td />
<td align="right">
{#if widget.status == 0}
<button
class="btnoff"
on:click={((widget["send"] = true),
(this.style.border = "1px solid red"),
WSpush(widget.socket, widget.topic, 1))}
><span
>{!widget.options[0]
? "OFF"
: widget.options[0]}</span
></button
><br />
{:else}
<button
class="btn"
on:click={((widget["send"] = true),
(this.style.border = "1px solid red"),
WSpush(widget.socket, widget.topic, 0))}
><span
>{!widget.options[1] ? "ON" : widget.options[1]}</span
></button
><br />
{/if}
</td>
{/if}
-- chart --
{#if widget.widget === "chart"}
<td colspan="3">
{#if widget.status}
{#if widget.topic.includes("_2")}
<Chart
data={widget.status}
{lineOptions}
{axisOptions}
colors={[!widget.color ? "red" : widget.color]}
type={!widget.type ? "line" : widget.type}
height="300"
/>
{:else if !widget.topic.includes("_1")}
{widget.descr}
<Chart
data={widget.status}
{lineOptions}
{axisOptions}
colors={[!widget.color ? "light-blue" : widget.color]}
type={!widget.type ? "line" : widget.type}
height="300"
/>
{/if}
{/if}
</td>
{/if}
-- range --
{#if widget.widget === "range"}
<td colspan="3">
<div>
{widget.descr}
{widget.status / 10}
{widget.after}
</div>
<label>
<input
type="range"
bind:value={widget.status}
min={widget.min}
max={widget.max * 10}
on:change={WSpush(
widget.socket,
widget.topic,
widget.status
)}
/>
</label>
</td>
{/if}
</tr>
{/if}
{/if}
{/each}
</table>
</TabPanel>
{/each}
</Tabs>
-->
<br /><br />
<div class="letter" align="center">developed by: avaks@meef.ru</div>
<style>
:global(.svelte-tabs__tab-list) {
display: flex;
justify-content: space-evenly;
flex-wrap: wrap;
}
:global(.svelte-tabs li.svelte-tabs__tab) {
color: gray;
}
:global(.svelte-tabs li.svelte-tabs__selected) {
color: green;
}
.red {
color: crimson;
}
.green {
color: rgb(20, 220, 37);
}
.letter {
color: grey;
font-size: 60%;
padding-left: 15px;
opacity: 0.8;
}
progress {
height: 4px;
}
input {
text-align: right;
border: 1px solid #6699ff;
width: 100%;
}
label {
display: flex;
justify-content: space-evenly;
flex-wrap: wrap;
}
:global(body.light-mode) {
background-color: white;
}
:global(body.dark-mode) {
background-color: #1d3040;
color: #bfc2c7;
}
:global(body.dark-mode) span {
background-color: #1d3040;
color: #bfc2c7;
}
:global(body.dark-mode) div {
background-color: #1d3040;
color: #bfc2c7;
}
:global(body.dark-mode) input {
background-color: #1d3040;
color: #bfc2c7;
}
:global(body.dark-mode) select {
background-color: #1d3040;
color: #bfc2c7;
padding: 10px;
border-radius: 10px;
padding-left: 20px;
}
:global(body.dark-mode) lable {
background-color: #1d3040;
color: #bfc2c7;
}
select {
padding: 10px;
border-radius: 10px;
padding-left: 20px;
}
.Shutter {
background-color: hsl(200, 16%, 96%);
color: blak;
padding: 10px;
border-radius: 5px;
}
:global(body.dark-mode) .Shutter {
background-color: #1d3040;
color: #bfc2c7;
padding: 10px;
border-radius: 5px;
}
.btn {
display: inline-block;
box-sizing: border-box;
padding: 1px;
margin: 0 0px 5px 0;
outline: none;
border: 1px solid #63b8ff;
border-radius: 10px;
height: 36px;
line-height: 0;
font-size: 14px;
font-weight: 500;
text-decoration: none;
color: #fff;
background-color: #fff;
position: relative;
overflow: hidden;
vertical-align: top;
cursor: pointer;
user-select: none;
appearance: none;
touch-action: manipulation;
}
.btn span {
box-shadow: 7px 7px 5px rgba(0, 0, 0, 0.5);
display: block;
box-sizing: border-box;
padding: 0 8px;
height: 32px;
line-height: 33px;
border: 1px solid #63b8ff;
border-radius: 8px;
font-size: 14px;
color: black;
text-align: center;
font-weight: 600;
}
.btnoff {
display: inline-block;
box-sizing: border-box;
padding: 1px;
margin: 0 0px 5px 0;
outline: none;
border: 1px solid rgb(85, 84, 84);
border-radius: 10px;
height: 36px;
line-height: 0;
font-size: 14px;
font-weight: 500;
text-decoration: none;
color: #fff;
background-color: #fff;
position: relative;
overflow: hidden;
vertical-align: top;
cursor: pointer;
user-select: none;
appearance: none;
touch-action: manipulation;
}
.btnoff span {
box-shadow: 7px 7px 5px rgba(0, 0, 0, 0.5);
display: block;
box-sizing: border-box;
padding: 0 8px;
height: 32px;
line-height: 33px;
border: 1px solid rgb(85, 84, 84);
border-radius: 8px;
font-size: 14px;
color: black;
text-align: center;
font-weight: 600;
}
.swipe-holder {
height: 30vh;
width: 100%;
}
button {
color: gray;
background-color: #fff;
border-bottom-style: solid;
border-bottom-width: 1px;
border-top-width: 0px;
border-left-width: 0px;
border-right-width: 0px;
padding-left: 4px;
padding-right: 4px;
padding-top: 1px;
padding-bottom: 1px;
}
:global(body.dark-mode) button {
background-color: #1d3040;
color: #bfc2c7;
}
</style>