Отключение света через пару-тройку часов
За годы наблюдений я пришёл к умозаключению, что никакой осветительный прибор дома не должен работать более 2 (или 4 или 6 или 8 — в зависимости от назначения комнаты) часов. Поэтому везде стараюсь на как можно более низком уровне написать автоматическое выключение по таймеру. Для zigbee-устройств наиболее низким уровнем является скрипты SLS-хаба. Для этих устройств уже есть готовые примеры автовыключения света, но они используют однократный таймер, из-за чего один скрипт не может одновременно контролировать несколько выключателей. Для обхода этого ограничения использован таймер Cron.
Описание
Данный скрипт вызывается или правилом SB Rule у свойства state выключателя, или же по таймеру, который он же сам установил. Описание параметров и формата вызова см. в листинге. В принципе, достаточно одного параметра — таймаута в часах: LightTimeOut.lua,2 Повторный вызов осуществляется самим скриптом (т.е. рекурсивно) уже с тремя параметрами.
Для работы скрипта требуется SLS с прошивкой как минимум 2024.01.22d4. При разработке хотелось все делать одним скриптом: в т.ч. один скрипт на все выключатели в доме. Получилось).
Нюансы:
- управление любым количеством выключателей одним файлом,
- автоотключение обрабатывается этим же файлом через рекурсивный вызов,
- отключение по таймеру, для настройки которого не требуется править скрипт,
- при сработке выключателя во время работы таймера, последний не продлевается, а добавляется новый (поэтому не балуйтесь со светом),
- список /api/scripts со временем заполняется большим количеством деактивированных crontab-задач; для очистки списка, видимо, хорошо бы автоматически перезагружать хаб раз в неделю, например.
Листинг
-- LightTimeOut.lua --
-- Взято с https://slsys.io/docs/lua_doc/luaMainDoorLight , модифицировано под задачи mxid://@azata:gazizova.net
-- Пример правил Simple Bind: LightTimeOut.lua,2|sw|sportzal
-- или можно только с одним параметром: LightTimeOut.lua,2
-- разбираю аргументы
local arr = explode("|", Event.Param)
local delaytime = arr[1] -- время задержки выключения в часах или строка cron (для timer)
local caller = arr[2] -- кто вызвал: sw = switch (по умолчанию); timer = таймер
local dstDevice = arr[3] -- исполняющее устройство (реле/розетка/лампа), прописать ieeeAddr или nwkAddr или Friendly name
if (dstDevice == nil) then -- третий агрумент можно не указывать, если управляем тем же самым устройством, что вызывает скрипт
dstDevice = Event.FriendlyName
end
arr = nil -- cleanup
local scriptName = (explode(".", Event.Name))[1] -- имя данного скрипта
-- проверяю кто вызвал (а-ля switch-case)
if (caller == "sw") or (caller == nil) then -- сам выключатель - надо запустить таймер, если свет был включён
if (Event.State.Value == "ON") then
local gmt = 5 -- часовой пояс
local time = os.time() + delaytime * 3600 + gmt * 3600;
local t1 = math.modf(time/60);
-- local sec = time - t1*60;
local time = t1;
local t1 = math.modf(time/60);
local min = time - t1*60;
if (min == 0) then -- чтобы не удалять постоянно прописанные в crontab строчки, в которых я ставлю нулевые минуты, добавляю одну минутку
min = 1
end
local time = t1;
local t1 = math.modf(time/24);
local hour = time - t1*24;
local crontab = min .. " " .. hour .. " * * *"
local param = crontab .. "|timer|" .. dstDevice -- меняю целевое воздействие на OFF
scripts.addCron(scriptName, crontab, param)
end
elseif (caller == "timer") then -- вернулся по окончанию таймера
zigbee.set(dstDevice, "state", "OFF") -- выключить свет
-- zigbee.set(Event.FriendlyName, Event.State.Name, "OFF") -- выключить -- так не работает, при втором вызове теряется исходный Event
-- удаление задачи планировщика - на самом деле происходит не удаление, а деактивация этой задачи
scripts.removeCron(scriptName, delaytime)
end
-- LightTimeOut.lua --
-- Взято с https://slsys.io/docs/lua_doc/luaMainDoorLight , модифицировано под задачи mxid://@azata:gazizova.net
-- Пример правил Simple Bind: LightTimeOut.lua,2|sw|sportzal
-- или можно только с одним параметром: LightTimeOut.lua,2
-- разбираю аргументы
local arr = explode("|", Event.Param)
local delaytime = arr[1] -- время задержки выключения в часах или строка cron (для timer)
local caller = arr[2] -- кто вызвал: sw = switch (по умолчанию); timer = таймер
local dstDevice = arr[3] -- исполняющее устройство (реле/розетка/лампа), прописать ieeeAddr или nwkAddr или Friendly name
if (dstDevice == nil) then -- третий агрумент можно не указывать, если управляем тем же самым устройством, что вызывает скрипт
dstDevice = Event.FriendlyName
end
arr = nil -- cleanup
local scriptName = (explode(".", Event.Name))[1] -- имя данного скрипта
-- проверяю кто вызвал (а-ля switch-case)
if (caller == "sw") or (caller == nil) then -- сам выключатель - надо запустить таймер, если свет был включён
if (Event.State.Value == "ON") then
local gmt = 5 -- часовой пояс
local time = os.time() + delaytime * 3600 + gmt * 3600;
local t1 = math.modf(time/60);
-- local sec = time - t1*60;
local time = t1;
local t1 = math.modf(time/60);
local min = time - t1*60;
if (min == 0) then -- чтобы не удалять постоянно прописанные в crontab строчки, в которых я ставлю нулевые минуты, добавляю одну минутку
min = 1
end
local time = t1;
local t1 = math.modf(time/24);
local hour = time - t1*24;
local crontab = min .. " " .. hour .. " * * *"
local param = crontab .. "|timer|" .. dstDevice -- меняю целевое воздействие на OFF
scripts.addCron(scriptName, crontab, param)
end
elseif (caller == "timer") then -- вернулся по окончанию таймера
zigbee.set(dstDevice, "state", "OFF") -- выключить свет
-- zigbee.set(Event.FriendlyName, Event.State.Name, "OFF") -- выключить -- так не работает, при втором вызове теряется исходный Event
-- удаление задачи планировщика - на самом деле происходит не удаление, а деактивация этой задачи
scripts.removeCron(scriptName, delaytime)
end