Приведенные примеры служат исключительно для демонстации построения тех или иных алгоритмов и не отмменяет необходимость в изучении языка программирования LUA.
-- init.lua --
-- Таймер. Ежеминутный запуск скрипта для отслеживания юзеров в сети
scripts.setTimer("personesTracker", 60)
-- Таймер. Запуск скрипта для каких-либо ежеминутных проверок/действий
scripts.setTimer("flush", 60, "bowl")
-- Уведомление в Telegram о старте шлюза --
telegram.settoken("51778***5:AAG0bvK***")
telegram.setchat("-3348***")
telegram.send("SLS загружен!!!")
--[[
Уведомление в Telegram о времени восхода и заката солнца.
Для правильной работы необходимо в системных настройках
заполнить данные о координатах шлюза в меню Settings -> Time & Location
--]]
local sunset_hour, sunset_min = os.sunset()
local sunrise_hour, sunrise_min = os.sunrise()
telegram.send("sunrise " .. sunset_hour .. ":" .. sunset_min)
-- Инициализация объектов для обмена между скриптами --
obj.setType("there.is.no.spoon", "BOOL")
obj.set("there.is.no.spoon", true, true)
-- Привязка скрипта btn_sw1.lua к событию нажатия аппаратной кнопки шлюза --
obj.onChange("io.input0.value", "btn_sw1.lua")
При разработке сценариев, часто требуется получить все параметры, передаваемые в него через события. Но, не все свойства бывают описаны в документации. Особенно при добавлении в прошивку новых функций или доработке существующих. Для получения и разбора всех свойств, может помочь такой пример:
for key, val in pairs(Event) do
if type(val) == "table" then
for key2, val2 in pairs(val) do
print("Event." .. key .. "." .. key2, val2)
end
else
print("Event." .. key, val)
end
end
Пример вывода:
[22:28:49.552] [Scripts] Event.State.OldValue true
[22:28:49.558] [Scripts] Event.State.Name contact
[22:28:49.563] [Scripts] Event.State.Value false
[22:28:49.568] [Scripts] Event.Name tables.lua
[22:28:49.667] [Scripts] Event.Type 1
[22:28:49.672] [Scripts] Event.Param
[22:28:49.678] [Scripts] Event.nwkAddr 42108
[22:28:49.687] [Scripts] Event.Time.min 28
[22:28:49.694] [Scripts] Event.Time.month 12
[22:28:49.702] [Scripts] Event.Time.year 2022
[22:28:49.709] [Scripts] Event.Time.sec 49
[22:28:49.713] [Scripts] Event.Time.hour 22
[22:28:49.721] [Scripts] Event.Time.wday 5
[22:28:49.726] [Scripts] Event.Time.day 9
[22:28:49.733] [Scripts] Event.ModelId lumi.sensor_magnet
[22:28:49.738] [Scripts] Event.FriendlyName
[22:28:49.745] [Scripts] Event.ieeeAddr 0x0123456789ABCDEF
По нажатию на аппаратную кнопку будет включаться режим сопряжения (Join) Zigbee устройств.
- в
init.lua
добавить код:
obj.onChange("io.input0.value", "btn_sw1.lua")
- создать
btn_sw1.lua
с кодом:
zigbee.join(255, "0x0000")
-- однократное нажатие, включить свет (яркость max)
if Event.State.Value == "single" then
value = 255
-- двойное нажатие, выключить свет (яркость 0)
elseif Event.State.Value == "double" then
value = 0
-- другие нажатия - ничего не делать
else
return
end
-- непосредственно, отправка полученного значения в целевое устройство
zigbee.set("lamp_1", "brightness", value)
if Event.State.Value == "single" then
value = "ON"
elseif Event.State.Value == "double" then
value = "OFF"
else
return
end
zigbee.set("lamp_1", "state", value)
Статья пользователя Сергей Кушеев
Управление свойствами осветительных приборов: получить состояние, включить, выключить, изменить яркость/цвет/цветовую температуру, на примере лампы TS0505B
- статус вкл/выкл - state = ON/OFF
- яркость - brightness = 0..255
- текущий режим управления цветом - color_mode = xy (цвет) | color_temp (температура)
- цветовая температура - color_temp = 153..500 по шкале Mired
- цвет - color = значения XYHS в объекте JSON
- скорость нарастания значения при изменении - transition = 1..255 сек
local device = "0xA4C138FD68EAA226"
brightness = zigbee.value(device, "brightness")
color_mode = zigbee.value(device, "color_mode")
color = zigbee.value(device, "color")
color_temp = zigbee.value(device, "color_temp")
state = zigbee.value(device, "state")
transition = zigbee.value(device, "transition")
print("brightness\t", brightness)
print("color_mode\t", color_mode)
print("color\t\t", color)
print("color_temp\t", color_temp)
print("state\t\t", state)
print("transition\t", transition)
Пример вывода
brightness 1
color_mode color_temp
color {"x":0.696955,"y":0.299588,"hue":328.8189,"saturation":0.716535}
color_temp 295
state ON
transition 0
Для управления статусом отправить в состояние state
ON
- включитьOFF
- выключитьTOGGLE
- переключить
При этом устройство включится со значениями яркости, цвета и температуры, установленными ранее.
Также устройство включится, если отправить любое значение яркости > 0 и выключится если отправить яркость = 0.
local device = "0xA4C138FD68EAA226"
zigbee.set(device, "state", "ON") -- включить
zigbee.set(device, "state", "OFF") -- выключить
zigbee.set(device, "state", "TOGGLE") -- переключить
local device = "0xA4C138FD68EAA226"
zigbee.set(device, "brightness", 25) -- установить яркость на 10%. Если лампа выключена - включится
zigbee.set(device, "brightness", 0) -- выключить
Управление цветовой температурой осуществляется по шкале Mired. Для конвертации можно воспользоваться формулой:
color_temp_mired = 1000000 / color_temp_kelvin
local device = "0xA4C138FD68EAA226"
local color_temp = 1000000/4700
zigbee.set(device, "color_temp", color_temp) -- установить цветовую температуру 4700 Кельвин
Для изменения цвета можно отправлять значения в следующих форматах:
color
основные цвета, например "red"xy
, например '{"x":0.697,"y":0.3}'hue, saturation
, например '{"hue":328.82,"saturation":0.717}'hue
, например '{"hue":328.82}'saturation
, например '{"saturation":0.717}'hex
, например '{"hex": "#RRGGBB"}'rgb
, например '{"r":200,"g":0,"b":0}'
local device = "0xA4C138FD68EAA226"
local color_xy = '{"x":0.697,"y":0.3}'
zigbee.set(device, "color", color_xy)
local device = "0xA4C138FD68EAA226"
local color_rgb = '{"r":200,"g":0,"b":0}'
zigbee.set(device, "color", color_rgb)
Плавность изменения режимов, например яркости, управляется состоянием transition
- время в секундах, в течение которого будет происходить изменение установленного значения
local device = "0xA4C138FD68EAA226"
zigbee.set(device, "transition", 3) -- при изменении значения, например яркости, последняя будет нарастать от текущего значения до установленного в течение 3 секунд
-- включить JOIN через роутер "plug1", на 255 секунд
zigbee.join(255, "plug1")
-- Получаем значение температуры и округляем до целого
local device = "FriendlyName" -- также можно использовать ieeeAddr и nwkAddr
temp = zigbee.value(device, "temperature")
temp = math.floor(temp)
print("Текущая температура: " .. temp .. " C°")
-- Вызов команды GET в конвертере для состояния brightness, устройства с FriendlyName lamPochka
zigbee.get("lamPochka", "brightness")
- при нажатии кнопки выключателя переключает лампу lamp_1 через контроль яркости. Может выполняться как самостоятельно, например по таймеру, так и из правила Simple Bind
-- получаем значение состояния 'click' кнопки 'lumi.sensor_switch'
-- если значение = single
if zigbee.value("lumi.sensor_switch", "click") == "single" then
-- переключаем (toggle) лампу
-- получим текущее значение яркости лампы
current_brightness = zigbee.value("lamp_1", "brightness")
-- если лампа выключена (яркость = 0)
if current_brightness == 0 then
-- включим её
zigbee.set("lamp_1", "brightness", 255)
else -- если включена (значение яркости отлично от 0)
-- включим
zigbee.set("lamp_1", "brightness", 0)
end
end
- при нажатии кнопки переключает лампу 0x00124B0009FE36FC, через установку состояния "State" в "TOGGLE". Должен выполняться из SB Rule
if Event.State.Value == "single" then
zigbee.set("0x00124B0009FE36FC", "state", "toggle")
end
Пример инициализации с сохранением данных
local res= zigbee.setState("0x00124B001F7CA144", "prop_float", floatVal, "FLOAT")
local res= zigbee.setState("0x00124B001F7CA144", "prop_bool", boolVal, "BOOL")
local res= zigbee.setState("0x00124B001F7CA144", "prop_int", intVal, "INT")
local res= zigbee.setState("0x00124B001F7CA144", "prop_int", "strVal", "STR")
os.save()
Необходимо создать lua скрипт и назначить его вызов при изменении pressure в правило SB:
-- kPa2mmhg.lua
local pressure = zigbee.value(tostring(Event.ieeeAddr), "pressure")
zigbee.setState(Event.ieeeAddr, "pressure_mm", pressure * 0.75, "FLOAT")
Второй вариант. Сразу записать правило в такм виде:
#zigbee.setState(Event.ieeeAddr, "pressure_mm", zigbee.value(tostring(Event.ieeeAddr), "pressure") * 0.75, "FLOAT")
Управляет устройством Yeelight. Описание протокола
result = yeelight.send("192.168.0.100", "get_prop", '["power","not_exist","bright"]')
Результат
{"id":18,"result":{"on","","100"}}
result = yeelight.send("192.168.0.100", "toggle", '[]')
Результат
{ "method": "props", "params": { "power": "off" } }
result = yeelight.send("192.168.0.100", "set_bright", '[100,"smooth",500]')
Результат
{ "method": "props", "params": { "active_bright": 100, "bright": 100 } }
local gmt = 3 -- часовой пояс
local time = os.time() + 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;
local time = t1;
local t1 = math.modf(time/24);
local hour = time - t1*24;
print(hour .. ":" .. min .. ":" .. sec)
Такой сценарий легко решается без использования lua скриптов. Разберем пример из датчика движения Aqara lumi.sensor_motion и исполнительного реле на 8 каналов с modkam.
Вариант 1 с использованием команд SimpleBind
На вкладке датчика движения настраиваем при включении occupation=true будет включать освещение, при occupation=false соответственно выключать.
Пример команды SimpleBind для включения реле по датчику движения:
true,0x00124B001F7CA144,state_l1,ON;false,0x00124B001F7CA144,state_l1,OFF;
С помощью occupancy_timeout можно задать интервал, в течении которого будет сбрасываться значение датчика.
Вариант 2 с использованием значений датчика освещения и сценариев lua
В данном примере мы будем использовать датчик движения Aqara lumi.sensor_motion.aq2 со встроенным датчиком освещенности и исполнительное реле на 8 каналов с modkam.
Создаем сценарий occupancy.lua
local state = zigbee.value(tostring(Event.ieeeAddr), "occupancy")
local lightlevel = zigbee.value(tostring(Event.ieeeAddr), "illuminance")
local minlightlevel = 300 -- зададим минимальный уровень освещенности, когда необходимо включать освещение
if (state) then telegram.send("Датчик движения ".. Event.FriendlyName .." обнаружил активность")
if (lightlevel < minlightlevel) then
zigbee.set("0x00124B001F7CA144", "state_l2", "ON")
telegram.send("Свет в комнате ".. Event.FriendlyName .." включили")
else
telegram.send("Значение датчика движения ".. Event.FriendlyName .." нормализовалось")
if (lightlevel < minlightlevel) then
zigbee.set("0x00124B001F7CA144", "state_l2", "OFF")
telegram.send("Свет в комнате ".. Event.FriendlyName .." выключили")
end
end
На вкладке датчика движения привязываем сценарий
Вариант 3 с использованием астротаймера и сценариев lua
В данном примере мы будем использовать любой датчик движения, а в качестве определения необходимости включения освещения будем использовать астро-таймер, встроенный в SLS шлюз. Управлять будем реле на 8 каналов с modkam. Перед использованием астротаймера, не забудьте в настройках системы указать ваши координаты:
Создаем сценарий occupancy_astro.lua
local state = zigbee.value(tostring(Event.ieeeAddr), "occupancy")
local sunset_hour, sunset_min = os.sunset() --закат
local sunrise_hour, sunrise_min = os.sunrise() --рассвет
local str = Event.Param
local p = {}
for x in string.gmatch(str,'([^:]+)') do
table.insert(p, x)
end
local ieee=p[1]
local par=p[2]
zigbee.setState(ieee, par .. "_activate_on", "STR") --добавляем переменную для хранения информации, что управляемый сенсор включен датчиком движения
if (state) then -- следует учитывать, что если тип переменной STR, то проверяется ее наличие, а не булево значение
if Event.Time.hour >= sunset_hour or Event.Time.hour <= sunrise_hour then
--если выключен, мы его включаем и записываем, кто включил
if (zigbee.value(ieee, par) == "OFF") then
zigbee.set(ieee, par, "ON")
zigbee.set(ieee, par .. "_activate_on", "PIR")
end
end
else
if Event.Time.hour >= sunset_hour or Event.Time.hour <= sunrise_hour then
--проверяем, если включен не датчиком движений PIR, то не трогаем
if (zigbee.value(ieee, par .. "_activate_on")) == "PIR" then
zigbee.set(ieee, par, "OFF")
zigbee.set(ieee, par .. "_activate_on", "")
end
end
end
Для корректной работы сценария, необходимо передать два параметра через двоеточие, например: occupancy_astro.lua,0x00124B001F7CA144:state_l1. Сценарий запоминает, что свет включен по датчику движения, и выключает только в том случае, если был включен по датчику движения.
Вариант 4. Привязка нескольких устройств с использованием астротаймера и сценариев lua
При вызове lua скрипта необходимо в виде параметров передать список привязываемых устройств. Пример параметров:
occupancy_astro2.lua,0x00124B001EC83D62:state_l4/0x00124B001EC83D62:state_l2
Пример сценария occupancy_astro2.lua
local state = zigbee.value(tostring(Event.ieeeAddr), "occupancy")
local sunset_hour, sunset_min = os.sunset() --закат
local sunrise_hour, sunrise_min = os.sunrise() --рассвет
local str=Event.Param
for x1 in string.gmatch(str,'([^/]+)') do
local p = {}
for x2 in string.gmatch(x1,'([^:]+)') do
table.insert(p, x2)
end
local ieee=p[1]
local par=p[2]
zigbee.setState(ieee, par .. "_activate_on", "STR") --добавляем переменную для хранения информации, что управляемый сенсор включен датчиком движения
if (state) then
if Event.Time.hour >= sunset_hour or Event.Time.hour <= sunrise_hour then
--если выключен, мы его включаем и записываем, кто включил
if (zigbee.value(ieee, par)=="OFF") then
zigbee.set(ieee, par, "ON")
zigbee.set(ieee, par.."_activate_on", "PIR")
--telegram.send("Свет в комнате ".. Event.FriendlyName .." включили ("..par..")")
end
end
else
if Event.Time.hour >= sunset_hour or Event.Time.hour <= sunrise_hour then
--проверяем, если включен не датчиком движений PIR, то не трогаем
if (zigbee.value(ieee, par.."_activate_on") )=="PIR" then
zigbee.set(ieee, par, "OFF")
zigbee.set(ieee, par.."_activate_on", "")
--telegram.send("Свет в комнате ".. Event.FriendlyName .." выключили")
end
end
end
Периодичность теста устанавливается периодичностью запуска скрипта по таймеру. Например Cron в 19:00 каждый день
scripts.setTimer("uptime_check", "0 19 * * *")
-- uptime_check.lua
-- ieeeAddr, nwAddr, friendly_name устройств для контроля и допустимый период отвала
local devices = {
{name = "cnt_kitchen", uptime = 180}, -- 0x00124B0027AEBF18
{name = "cnt_toilet", uptime = 180} -- 0x00124B0027AEBF1C
}
for key, device in pairs(devices) do
--print(key, device)
if math.ceil((os.time() - zigbee.value(device['name'], 'last_seen'))/60) > device['uptime'] then
msg = 'Error: ' .. device['name'] .. ' is down!'
telegram.send(msg)
end
end
if (Event.State.Value ~= Event.State.OldValue) then
if (Event.State.Value) then
telegram.send("Дверь закрыта")
else
telegram.send("Дверь открыта")
end
end
local state = zigbee.value(tostring(Event.ieeeAddr), "occupancy")
if (state) then
telegram.send("Датчик движения ".. Event.ieeeAddr .." обнаружил активность")
else
telegram.send("Значение датчика ".. Event.FriendlyName .." движения нормализовалось")
end
local temp = round2(zigbee.value(tostring(Event.ieeeAddr), "temperature"),1)
local hum = round2(zigbee.value(tostring(Event.ieeeAddr), "humidity"),1)
telegram.send("Значение ДТВ ".. Event.FriendlyName .. " ".. temp.."° / " .. hum .. "%")
Вариант через GPIO
local gmt = 3
local time = os.time()
local hour = (math.modf(time / 3600) + gmt) % 24
if hour >= 22 or hour < 6 then
if Event.State.Value then
gpio.pwm(0, 255)
gpio.pwm(1, 255)
gpio.pwm(2, 255)
else
gpio.pwm(0, 0)
gpio.pwm(1, 0)
gpio.pwm(2, 0)
end
end
Вариант через MQTT:
local gmt = 3
local time = os.time()
local hour = (math.modf(time / 3600) + gmt) % 24
if hour >= 22 or hour <6 then
if Event.State.Value then
mqtt.pub('ZigBeeSls/led', '{"mode":"manual","hex":"#FF0000"}')
else
mqtt.pub('ZigBeeSls/led', '{"mode": "off"}')
end
end
Постановка
obj.set("security_status", "armed")
Проверка
if obj.get("security_status") == "armed" then
print("Объект на охране.")
else
print("Объект не на охране.")
end
Астротаймером называется обычный таймер, имеющий привязку к циклам захода\восхода солнца.
Так как на разной широте время захода и восхода отличается, то в таких таймерах присутствует установка долготы/широты. Параметры долготы/широты задаются через Web на вкладке Settings->Location
Астротаймер вызывается скриптом OneMinTimer.lua
каждую минуту:
local sunrise_add_min <const> = 15
local sunrise_hour, sunrise_min = os.sunrise()
sunrise_min = sunrise_min + sunrise_add_min
if sunrise_min > 59 then
sunrise_hour = sunrise_hour + 1
sunrise_min = sunrise_min - 60
end
if Event.Time.hour == sunrise_hour and Event.Time.min == sunrise_min then
print(sunrise_hour .. ":" .. sunrise_min)
end
Данный скрипт напечатает время рассвета, через 15 минут после наступления, т.е. если 8:55 наступает рассвет, добавляем 15 минут, то скрипт сработает в 9:10. Можно задавать не более 60 минут.
local sunset_add_min <const> = 20
local sunset_hour, sunset_min = os.sunset()
sunset_min = sunset_min + sunset_add_min
if sunset_min > 59 then
sunset_hour = sunset_hour + 1
sunset_min = sunset_min - 60
end
if Event.Time.hour == sunset_hour and Event.Time.min == sunset_min then
-- Включает уличный свет через 20 минут после заката
telegram.send("Включено уличное освещение")
http.request("http://192.168.2.200:8888/objects/?object=MegaD3-8&op=m&m=extended")
end
local sunrise_add_min <const> = 1
local sunrise_hour, sunrise_min = os.sunrise()
sunrise_min = sunrise_min + sunrise_add_min
if sunrise_min > 59 then
sunrise_hour = sunrise_hour + 1
sunrise_min = sunrise_min - 60
end
if Event.Time.hour == sunrise_hour and Event.Time.min == sunrise_min then
-- Открывает шторы после рассвета
telegram.send("Шторы в гостиной открыты")
zigbee.set("0x5C0272FFFEC8A21B", "position", 0)
end
sunrise_h, sunrise_m = os.sunrise()
sunset_h, sunset_m = os.sunset()
sunshine = (Event.Time.hour*60+Event.Time.min)>(sunrise_h*60+sunrise_m) and (Event.Time.hour*60+Event.Time.min)<(sunset_h*60+sunset_m)
-- sunshine - булева переменная, показывает время суток, дневное или вечернее.
192.168.1.5 - адрес другого шлюза. Нельзя запускать на самом себе таким образом, используйте объект audio.
http.request("http://192.168.1.5/audio?action=setvolume&value=100")
http.request("http://192.168.1.5/audio?action=play&url=http://funny-dog.surge.sh/door_bell.mp3")
--zvonok.lua скрипт ставим на датчик открытия двери если кнопка ON меняем на Single (одиночное нажатие)
if Event.State.Value == "ON" then
audio.setvolume(100)
audio.playurl("http://funny-dog.surge.sh/door_bell.mp3")
end
Создаем сценарий curtian.lua:
local str = Event.Param
local p = {}
for x in string.gmatch(str,'([^:]+)') do
table.insert(p, x)
end
local remoteieee = p[1] -- ieee адрес привода для штор
local minlevel = p[2] --зададим минимальный уровень
local maxlevel = p[3] --зададим максимальный уровень
local btn=zigbee.value(tostring(Event.ieeeAddr), "action")
if (btn == "single") then --при однократном нажатии переключим шторы в противоположный режим
zigbee.get(remoteieee, "running")
local running = zigbee.value(remoteieee, "running")
if (running == true) then
zigbee.set(remoteieee, "state", "STOP")
else
local position = zigbee.value(remoteieee, "position")
if (tonumber(position)<=tonumber(maxlevel)-10) then
zigbee.set(remoteieee, "position", maxlevel)
else
zigbee.set(remoteieee, "position", minlevel)
end
end
end
if (btn=="double") then --при двукратном нажатии откроем шторы
zigbee.set(remoteieee, "position", maxlevel)
end
if (btn=="triple") then --при трехкратном нажатии закроем шторы
zigbee.set(remoteieee, "position", minlevel)
end
if (btn=="long_release") then --при длительном нажатии остановим привод
zigbee.set(remoteieee, "state", "STOP")
end
В разделе SB выключателя WXKG01LM в метрике action прописываем вызов сценария curtain.lua, ieee адрес привода Tuya, значение position при открытии и значение при закрытии.
curtain.lua,0x5C0272FFFECAAC69:0:75
Теперь при однократном нажатии кнопки осуществляется переключение в противоположный режим, при двукратном - открытие, трёхкратном - закрытие.
onemintimer.lua:
--включаем циркуляцию ГВС каждые 10 минут
if (Event.Time.min == 0) or (Event.Time.min == 20) or (Event.Time.min == 40) then
zigbee.set("0x00124B001EC823EC", "state_l1", "OFF")
telegram.send("Циркуляция ГВС выключена")
end
if (Event.Time.min == 10) or (Event.Time.min == 30) or (Event.Time.min == 50) then
zigbee.set("0x00124B001EC823EC", "state_l1", "ON")
telegram.send("Циркуляция ГВС включена") end
Управление адресными светодиодами, подключенными к контроллеру WLED
WLED поддерживает множество вариантов управления, в том числе mqtt. Ниже собраны примеры кода на lua для управления через mqtt.
Управление и переключение режимов можно легко "повесить" на zigbee-кнопку, поддерживающие многократные нажатия.
mqtt.pub('wled/f6dafd', 'toggle')
mqtt.pub('wled/f6dafd', 'on')
mqtt.pub('wled/f6dafd', 'off')
mqtt.pub('wled/f6dafd/col', '#36A615DD')
mqtt.pub('wled/f6dafd/col', '#'..math.random(0,16777215))
mqtt.pub('wled/f6dafd/api', 'win&A=128&FX='..math.random(0, 50)) -- установка произвольного эффекта
mqtt.pub('wled/f6dafd/api', 'win&A=128&FX=0') -- отключение эффектов
local state = zigbee.value(tostring(Event.ieeeAddr), "water_leak")
if (state) then
telegram.send("Внимание!!! Сработал датчик протечки " .. Event.FriendlyName)
zigbee.set("0x000D6F001314C316", "warning", 2)
else
telegram.send("Протечка на датчике " .. Event.FriendlyName .. " устранена")
end
Данный сценарий тестировался для выключателей, которые подключены к 8-ми канальному реле modkam и привода для штор от Aqara. При первом нажатии создается таймер на 3 секунды, который инициирует действия.
callback.lua (привязывается в интерфейсе к каналу, который будем считать)
function search_value (tbl, val)
for i = 1, #tbl do
if tbl[i][1] == val then
return i
end
end
return nil
end
if (Event.State.Value ~= Event.State.OldValue) then
local ar = {
{"0x00124B001EC84F3C.state_l8","0x00158D0002EE1285", 'cover'}, --спальня
{"0x00124B001F7CA144.state_l2","0x50325FFFFEA65978", 'cover'}, --Анюта
{"0x00124B001F7CA144.state_l8","0x50325FFFFEA457C4", 'cover'}, --Софа
{"0x00124B001EC80F20.state_l7","0x5C0272FFFECAAC69", 'cover'}, --Гостиная
{"0x00124B001EC84F3C.state_l7","hello",'telega'}}
myevent = Event.ieeeAddr .. "." .. Event.State.Name
remotedev = ar[search_value(ar, myevent)][2]
remotetype =ar[search_value(ar, myevent)][3]
obj.setType(remotedev .. "_cnt", "INT")
obj.setType(remotedev .. "_timer", "BOOL")
obj.setShare(remotedev .. "_cnt", true)
obj.setShare(remotedev .. "_timer", true)
timer=obj.get(remotedev .. "_timer")
obj.set(remotedev .."_cnt", obj.get(remotedev .. "_cnt") + 1)
--проверяем, если скрипт был запущен давно и не сбросился
curr, prev = obj.getTime(remotedev.. "_timer")
if (timer == true and os.time() - curr > 60) then
obj.set(remotedev .. "_timer", false)
obj.set(remotedev .. "_cnt" , 0)
end
--запускам таймер сброса на 3 сек
if (timer == false) then
scripts.setTimer("clear_cnt", os.time() + 3,remotedev)
obj.set(remotedev .. "_timer",true)
end
end
Сценарий, который выполняет сами действия clear_cnt.lua
remotedev = Event.Param
val = obj.get(remotedev .. "_cnt")
if (val > 1) then
obj.set(remotedev .. "_cnt" , 0)
obj.set(remotedev .. "_timer", false)
--telegram.send(val..", счетчик сброшен,"..remotedev)
scripts.setTimer("clear_cnt", 0)
end
if (val == 4) then
-- telegram.send("val:"..val)
position=zigbee.value(remotedev, "position")
-- telegram.send("position:"..position)
if (position > 6) then
-- telegram.send("закрываю шторы")
zigbee.set(remotedev, "position", 0)
else
-- telegram.send("открываю шторы")
zigbee.set(remotedev, "position", 100)
end
end
Данный сценарий тестировался для выключателей, которые подключены к 8-ми канальному реле modkam. Сценарий удобно применять в помещениях, где пребывание обычно недолгое, например гардеробная комната, прихожая и т.д., где дети всегда часто забывают выключать свет. На всякий случай предусмотрим передачу параметров (если тригером будет не сам объект), для этого привяжем к управляемой линии вызов сценария toggle_timer
Сценарий toggle_timer.lua:
local p = {}
local waittime = 900
for x in string.gmatch(Event.Param ,'([^:]+)') do
table.insert(p, x)
end
local ieee = p[1]
local parname = p[2]
local state = zigbee.value(ieee, parname)
local timer = obj.get(ieee .. "_" .. parname .. "_timer")
local curr, prev = obj.getTime(ieee .. "_" .. parname .. "_timer")
obj.setType(ieee .. "_" .. parname .. "_timer", "BOOL")
obj.setShare(ieee .. "_" .. parname .. "_timer", true)
--сброс таймера, если по какойто причине таймер просрочен
if (state == "ON" and timer == true and os.time() - curr > waittime + 10) then
obj.set(ieee .. "_" .. parname .. "_timer", false)
end
--запускам таймер выключения
if (state == "ON" and timer == false) then
scripts.setTimer("toggle_timer2", os.time() + waittime, ieee .. ":" .. parname)
obj.set(ieee .. "_" .. parname .. "_timer", true)
end
сценарий, который будет вызываться по таймеру (выключение света) toggle_timer2.lua:
local p = explode(":", Event.Param)
local ieee = p[1]
local parname = p[2]
zigbee.set(ieee, parname, "OFF")
obj.set(ieee.. "_" .. parname .. "_timer", false)
--telegram.send(ieee.."."..parname.." выключен по таймеру")
scripts.setTimer("toggle_timer2", 0)
Здесь описан способ отправки показаний в УК, не имеющей API для этого
Статья пользователя Алексей Цуркан
Статья пользователя Alexander Posazhennikov
Автоматизации в умном доме 2. SLS, MQTT, Mikrotik
Решение пользователя Nekto Ivanov
. Ищите его в основной группе Telegram. Контакт у него засекречен)
Решение позволяет контролировать регистрацию устройств на DHCP сервере роутера Mikrotik. RouterOS позволяет вызывать скрипт при регистрации и при удалении регистрации вызывать скрипт, который передаст данный об отключенном или подключенном устройстве в SLS посредством HTTP API. Скрипт:
:if ($leaseBound = 1) do={
/tool fetch url="http://192.168.0.2/api/scripts?action=evalFile&path=presense.lua¶m=Online+$leaseActIP+$leaseActMAC" keep-result=no
} else={
/tool fetch url="http://192.168.0.2/api/scripts?action=evalFile&path=presense.lua¶m=Offline+$leaseActIP+$leaseActMAC" keep-result=no
}
необходимо вставить в окно Lease Script в свойствах DHCP сервера Mikrotik.
Таким образом, при каждой регистрации и её удалении в SLS будет вызываться скрипт presense.lua
, в который будет передан параметр вида
Online+IP+MAC
при подключении устройстваOffline+IP+MAC
при отключении устройства
Внутри скрипта LUA этот параметр можно принять посредством переменной события Event.Param
и обработать по своему усмотрению. Например отправить в Telegram:
telegram.send(Event.Param)
Для того чтобы в SLS прилетали регистрации только нужных устройство можно добавить их выборку посредством регулярного выражения. Например, так мы выделим IP адреса из диапазона 10-19:
^192\\.168\\.0\\.[1].\$
:if ($leaseActIP~"^192\\.168\\.0\\.[1].\$") do={
:if ($leaseBound = 1) do={
/tool fetch url="http://192.168.0.2/api/scripts?action=evalFile&path=presense.lua¶m=Online+$leaseActIP+$leaseActMAC" keep-result=no
} else={
/tool fetch url="http://192.168.0.2/api/scripts?action=evalFile&path=presense.lua¶m=Offline+$leaseActIP+$leaseActMAC" keep-result=no
}
}
Следует отметить, что событие регистрации возникает сразу при подключении, а событие удаления регистрации в пределах времени TTL записи DHCP (10 мин). Для "важных" устройств это время можно уменьшить. Для этого нужно открыть запись аренды (Lease), сделать её статической (Make static), закрыть/открыть и тогда появится возможность для этой записи установить TTL отличный от заданного на уровне сервера:
При этом, устройство с данным MAC адресом, будет всегда получать зафиксированный IP адрес.
Также, замечено, что некоторые устройства могут некорректно продлевать аренду адреса. При этом могут возникать ложные срабатывания скрипта. В таком случае воспользуйтесь решением выше.
Статья пользователя Сергей Кушеев
SLS шлюз. Автоматизация. Пишем управление устройствами.
Пример сценария для записи значений в локальное хранилище (int или sd), сценарий сохраняет файл с именем файла, равным дате. В виду того, что для преобразования записанного файла в json используется память SLS, не рекомендуется запускать часто. В случае переполнения памяти, запись готового json не будет выполнена. Пример отображения графиков через ui.html.
local new_ust, prev_ust = obj.get('thermo.boiler.target_temperature') --получение текущего значения уставки
local ul_ot, prev_ul_ot = obj.get('thermo.boiler.temperature_outside')--получение уличной темпетуры с котла по ОТ
local new_ds18, prev_ds18 = obj.get('1w.28-96A907D6013C/temperature') --получение значения датчика температуры
local ul_bt, prev_bt = obj.get('thermo.boiler.temperature')--получение температуры теплоносителя (отопления)
local new_dhwt, prev_dhwt = obj.get('thermo.dhw.temperature') --получение температуры бойлера
local dhwst, prev_dhwst = obj.get('thermo.dhw.status') --нагрев воды
local flame, prev_flame = obj.get('thermo.boiler.flame') --нагрев отопления
local avr_temp, avr_temp = obj.get('average_temp') --средняя температура в доме
if (flame=="true") then flame_status=100 else flame_status=0 end --переназнчаем, так как почему-то значения хранятся в STRING
if (dhwst=="true") then dhwst_status=100 else dhwst_status=0 end --переназнчаем, так как почему-то значения хранятся в STRING
local cdate=string.format("%02d", Event.Time.day).."/"..string.format("%02d", Event.Time.month).."/"..Event.Time.year --приводим дату в нужный формат
local ctime=string.format("%02d", Event.Time.hour)..":"..string.format("%02d", Event.Time.min)..":"..string.format("%02d", Event.Time.sec) --приводим время в нужный формат
local ctimesh=string.format("%02d", Event.Time.hour)..":"..string.format("%02d", Event.Time.min) --приводим время в краткий формат
--local storage='int' --для записи во внутренню память
local storage='sd' --для записи на карту памяти
local fn="/"..storage.."/!log_"..string.gsub(cdate, '/', '_')..".txt"
local fnjs="/"..storage.."/!log_"..string.gsub(cdate, '/', '_')..".json"
os.fileWrite(fn,'{"new_ust":'..new_ust..',"ul_ot":'.. ul_ot.. ',"new_ds18":'..new_ds18..',"ul_bt":'..ul_bt..',"new_dhwt":'.. new_dhwt .. ',"unixtime":'..os.time()..
',"datetime":"'..cdate.." "..ctime..'","dhwt_status":'.. dhwst_status..',"flame_status":'.. flame_status..',"average_temp":'.. avr_temp..',"ctimesh":"'.. ctimesh.. '"},\n',true)
value = os.fileRead(fn)
local js='{"temp":['..value..']}'
js=string.gsub(js, ',\n]}', '\n]}')
os.fileWrite(fnjs,js)
print("Список файлов доступен тут http://"..net.localIP().."/api/storage?path=/"..storage.."/")
local free=http.request2("http://"..net.localIP().."/api/storage/info")
print("http://"..net.localIP().."/api/storage?path="..fnjs)
PS. Документ в разработке