====== Свет ======
===== Вариант #1 =====
[[https://www.ab-log.ru/forum/viewtopic.php?p=21446#p21446|Автор: d.v.ermakov » 17 мар 2016, 20:24]]
Поделюсь немного опытом использования байндинга в управлении светом. В развитие уже имеющейся инструкции автора на Гитхабе ([[https://github.com/Pshatsillo/openHABMegaDevice/blob/master/README.md|ссылка]]).
Во-первых, я не пользуюсь советом из инструкции переключать порт Меги (к которому подключена кнопка) в режим P&R. Для корректной работы правила по включению/выключению этого не нужно, а также это мешает нормальной работе автономного режима Меги (если сервер выключить). Поэтому обычные кнопки у меня в режиме P (и автономный режим настроен на всякий случай), только кнопки диммеров и датчики присутствия в режиме P&R.
Вот правило для обычного включения/выключения кнопкой:
/* Кнопка X */
rule "Button_X"
when Item Button_X changed to ON
then if (Light_X.state == OFF) {
sendCommand(Light_X, ON)
postUpdate(Light_X, ON)
} else {
sendCommand(Light_X, OFF)
postUpdate(Light_X, OFF)
}
postUpdate(Button_X, OFF) // Возвращаем кнопку в положение выключено
end
Во-вторых, вот правило для управления яркостью (диммирования) одной кнопкой. Эта кнопка должна быть в режиме P&R. Действует так: короткое нажатие выключает/включает на прежний уровень яркости, длинное нажатие плавно уменьшает яркость до 1%, затем увеличивает до 100%, затем снова уменьшает, и т. д.
val int DimmerDelay = 500 // Задержка нажатия кнопки для срабатывания диммера
val int DimmerSpeed = 100 // Задержка шага диммера
val int DimmerStep = 3 // Величина шага диммера
var Number Stored_Y = 1 // Яркость при первом включении 1, а потом запоминаем сюда яркость
var int TimeON_Y = 0 // Переменная для запоминания времени включения кнопки диммера
/* Диммер Y */
rule "Dimmer_Y ON"
when
Item Dimmer_Y changed to ON
then
TimeON_Y = now.getMillisOfDay
var Timer timer = null
timer = createTimer(now.plusMillis(DimmerDelay)) [|
var Number Percent = Light_Y.state as DecimalType
var Number Flag = 0
while (Dimmer_Y.state == ON) {
Percent = Percent - DimmerStep + Flag
if (Percent > 100) { Percent = 100 Flag = 0 }
if (Percent < 1) { Percent = 1 Flag = DimmerStep + DimmerStep }
sendCommand (Light_Y, Percent)
postUpdate (Light_Y, Percent)
Thread::sleep (DimmerSpeed)
}
]
end
rule "Dimmer_Y OFF"
when
Item Dimmer_Y changed to OFF
then
var int TimeOFF = now.getMillisOfDay
var int TimePressed = TimeOFF - TimeON_Y
if (TimePressed < DimmerDelay) {
if (Light_Y.state > 0) {
Stored_Y = Light_Y.state as DecimalType
sendCommand (Light_Y, 0)
postUpdate (Light_Y, 0)
} else {
sendCommand (Light_Y, Stored_Y)
postUpdate (Light_Y, Stored_Y)
}
}
end
Если у вас несколько диммеров в одном файле, переменные внутри правил переименовывать в каждом не нужно, они локальные. Только Stored_Y и TimeON_Y должны быть у каждого диммера свои, и объявлены в начале файла.
Вторая серия. Использование датчиков присутствия/движения для управления светом.
Это простое правило:
val int PresenceDelay = 60 /* Задержка выключения света по датчикам присутствия в секундах */
/* Свет в гардеробной */
rule "Light_Closet ON"
when
Item Presence_Closet changed to ON
then
if (Light_Closet.state == OFF) {
sendCommand(Light_Closet, ON)
postUpdate(Light_Closet, ON)
}
end
rule "Light_Closet OFF"
when
Item Presence_Closet changed to OFF
then
var Timer timer = null
timer = createTimer(now.plusSeconds(PresenceDelay)) [|
if (Presence_Closet.state == OFF) {
if (Light_Closet.state == ON) {
sendCommand(Light_Closet, OFF)
postUpdate(Light_Closet, OFF)
}
}
]
end
А это правило для света в санузле, если у вас есть два датчика движения, один в самом санузле, а второй в коридоре, непосредственно перед санузлом. Включается свет по любому из двух датчиков, а выключается по датчику коридора, при условии, что внутри никого нет. У меня двери в санузлы имеют большие вставки из матового стекла и я использую свет в санузле как ночное освещение коридора. Кроме того, у меня в санузлах по две группы света, маленькая ночная лампочка и основное освещение. Ну и можно изменить правило по вкусу.
val int PresenceDelay = 60 /* Задержка выключения света по датчикам присутствия в секундах */
/* Свет в санузле */
rule "Light_ WC"
when
Item Presence_Corridor changed to ON or
Item Presence_WC changed to ON
then
if (Light_WC.state == OFF) {
sendCommand(Light_WC, ON)
postUpdate(Light_WC, ON)
}
end
rule "Light_WC OFF"
when
Item Presence_Corridor changed to OFF
then
var Timer timer = null
timer = createTimer(now.plusSeconds(PresenceDelay)) [|
if (Presence_Corridor.state == OFF) {
if (Presence_WC.state == OFF) {
if (Light_WC.state == ON) {
sendCommand(Light_WC, OFF)
postUpdate(Light_WC, OFF)
}
}
}
]
end
Файлов с правилами в Опенхабе может быть много, удобно делить правила на файлы по функционалу.
===== Вариант #2 =====
[[https://www.ab-log.ru/forum/viewtopic.php?p=28452#p28452|Автор: bvasya » 06 июн 2017, 16:47]]
Хочу поделиться своим правилом для управления светом. Поскольку с java и Xtend я опыта раньше не имел, написано скорее всего не самым оптимальным образом. Кучу ошибок (в основном с типами) приходилось решать методом тыка, но сейчас по крайней мере редактор не ругается) Версия самая начальная, скорее всего буду переписывать, поэтому формат настроек может поменяться. У меня работает на OH2 + биндинг от OH1. На OH1 скорее всего работать не будет, там много поменялось в работе правил.
Основная концепция такая:
Сделать код управления общим для всех, все различия вынести в настройки
Каждый выключатель или датчик движения отвечает за один или несколько выходов (свет, вытяжки и т.п.)
Если выходов несколько на одной кнопке, за основу берем первый по порядку
Каждый выход настраивается отдельно и не зависит от того каким выключателем был включен
В зависимости от текущего режима (у меня это время суток), выходы могут по разному реагировать на нажатие кнопок
При старте режима, можно сменить состояние и т.п.
В разных режимах - можно сделать разное значение диммера
По длительному нажатию, выход включается или выключается без дополнительных настроек
В данный момент, работает только с кнопками. Датчики движения, диммеры и смену состояния при смене режима пока не делал. Бывают небольшие глюки, но пока не отлавливал причину. Код старался по максимуму комментировать и в логи сейчас практически все изменения пишутся.
Теперь к коду: megadevice.items
//група со всеми выключателями, на которые действует правило
Group:Switch:OR(ON,OFF) gSwitches "All switches" (All)
//група со всеми выходами, на которые действует правило
Group:Switch:OR(ON,OFF) gLights "All lights" (All)
//переключатель режимов
//в sitemap прописан как
//Switch item=modeSelector mappings=["morning"="Morning","day"="Day","evening"="Evening","night"="Night", "guard"="Guard"]
String modeSelector "M"
Switch F2BedroomBedButton "Спальня, выключатель у кровати [%s]" (F2, Bedroom, gSwitches) {megadevice="sec:192.168.0.22:3"}
Switch F2BedroomHallButtonL "Спальня, выключатель в корридоре левый [%s]" (F2, Bedroom, gSwitches) {megadevice="sec:192.168.0.22:5"}
Switch F2BedroomHallButtonR "Спальня, выключатель в корридоре правый [%s]" (F2, Bedroom, gSwitches) {megadevice="sec:192.168.0.22:6"}
Switch F2BedroomBra "Спальня, бра [%s]" (F2, Bedroom, gLights) {megadevice="sec:192.168.0.22:8"}
Switch F2BedroomBathMirror "Спальня санузел, зеркало [%s]" (F2, BedroomBath, gLights) {megadevice="sec:192.168.0.22:9"}
Switch F2BedroomBathLight "Спальня санузел, свет [%s]" (F2, BedroomBath, gLights) {megadevice="sec:192.168.0.22:10"}
Switch F2BedroomBathAir "Спальня санузел, вытяжка [%s]" (F2, BedroomBath, gLights) {megadevice="sec:192.168.0.31:22"}
Switch F2BedroomLight1 "Спальня, свет 1 [%s]" (F2, Bedroom, gLights) {megadevice="sec:192.168.0.22:12"}
Switch F2BedroomLight2 "Спальня, свет 2 [%s]" (F2, Bedroom, gLights) {megadevice="sec:192.168.0.22:13"}
Ниже в коде пример настройки для спальни с санузлом: бра обычно включается на 3 минуты, вечером и ночью без таймера. Основной свет в комнате вечером и ночью не включается (только по длительному нажатию). В санузле вчером и ночью вытяжка включается сразу и работает после выключения 10 минут, в остальное время вытяжка включается только после выключения света на 5 минут
megadevice.rules
import java.util.HashMap
import java.util.LinkedHashMap
import java.util.ArrayList
import java.util.Map
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import java.io.Serializable
/* Настройки ламп по умолчанию
*
* Для таймеров:
* Double.NEGATIVE_INFINITY - не включаем
* Double.POSITIVE_INFINITY - включаем без таймера
* null - ?
*
* Для старта:
* TODO: реализовать
* Double.NEGATIVE_INFINITY - выключаем
* Double.POSITIVE_INFINITY - включаем без таймера
* null - ничего не делаем
*
*/
val Double NEVER = Double.NEGATIVE_INFINITY
val Double FOREVER = Double.POSITIVE_INFINITY
var _default =
newHashMap(
"buttonTimeout" -> Double.POSITIVE_INFINITY, // таймаут при включении с кнопки
"sensorTimeout" -> 10, // таймаут при включении с сенсора
"started" -> null, // значение при старте режима
"offTimeout" -> 0, // таймаут на выключение после выключения с кнопки
"dimmer" -> 100 //значение диммера
)
var HashMap> _lights =
newHashMap(
"F2BedroomBathMirror" -> (newHashMap(
),
"F2BedroomBathLight" -> (newHashMap(
),
"F2BedroomBathAir" -> (newHashMap(
"buttonTimeout" -> Double.NEGATIVE_INFINITY,
"buttonTimeout_evening" -> Double.POSITIVE_INFINITY,
"buttonTimeout_night" -> Double.POSITIVE_INFINITY,
"offTimeout" -> 300,
"offTimeout_evening" -> 600,
"offTimeout_night" -> 600
),
"F2BedroomBra" -> (newHashMap(
"buttonTimeout" -> 180,
"buttonTimeout_evening" -> Double.POSITIVE_INFINITY,
"buttonTimeout_night" -> Double.POSITIVE_INFINITY
),
"F2BedroomLight1" -> (newHashMap(
"buttonTimeout_evening" -> Double.NEGATIVE_INFINITY,
"buttonTimeout_night" -> Double.NEGATIVE_INFINITY
),
"F2BedroomLight2" -> (newHashMap(
"buttonTimeout_evening" -> Double.NEGATIVE_INFINITY,
"buttonTimeout_night" -> Double.NEGATIVE_INFINITY
)
)
//кнопки
var HashMap> _switches =
newLinkedHashMap(
"F2BedroomBedButton" -> newArrayList("F2BedroomBra", "F2BedroomLight1", "F2BedroomLight2"),
"F2BedroomHallButtonL" -> newArrayList("F2BedroomBra", "F2BedroomLight1", "F2BedroomLight2"),
"F2BedroomHallButtonR" -> newArrayList("F2BedroomBathLight", "F2BedroomBathMirror", "F2BedroomBathAir")
)
//датчики
//TODO: реализовать
var HashMap> _sensors =
newLinkedHashMap(
"sensor1" -> newArrayList("light1"),
"sensor2" -> newArrayList("light2")
)
// Таймеры на отключение по timeout
var HashMap _timeoutTimers = newHashMap
// Триггеры на сработавший механизм, чтобы датчики движения не переназначали действия кнопок
var HashMap _lastTriggers = newHashMap
// время нажатия now.getMillisOfDay
var HashMap _switches_time = newHashMap
//лямбда для получения настроек в зависимости от режима
//приоритет настроек: mode настройка для лампы -> общая для лампы -> общая для всех
//mode берем из modeSelector
val Functions$Function3 getSettingsByCurrentMode = [
String item,
HashMap> gSettings,
HashMap def |
var mode = modeSelector.state.toString
var HashMap result = newHashMap
if (gSettings.containsKey(item)) {
var settings = gSettings.get(item)
for (def_entry : def.entrySet()) {
var v = def_entry.getKey()
if (settings.containsKey(v+"_"+mode)) {
result.put(v, settings.get(v+"_"+mode))
} else {
if(settings.containsKey(v)) {
result.put(v, settings.get(v))
} else {
result.put(v, def_entry.getValue())
}
}
}
} else {
result=def
}
result
]
rule "Set lights timers on system start"
when
System started
then
//создаем таймеры для лампочек
//TODO: добавлять только для тех, где есть timeout в настройках
for (t : _lights.entrySet()) {
_timeoutTimers.put(t.getKey(), null)
_lastTriggers.put(t.getKey(), "")
}
end
rule "switch _lights by settings"
when
Item gSwitches received update
then
(gSwitches).members.filter(x|x.state == ON).forEach [ sw |
//Сохраняем время нажатия кнопки
if (!_switches_time.containsKey(sw.name)) {
_switches_time.put(sw.name, now.getMillisOfDay)
}
//Лампы на данном выключателе
val ArrayList swLights = _switches.get(sw.name)
//Проверяем есть ли настройки для выключателя
if (swLights != null && swLights.size>=1) {
//первая лампа в группе
val firstLight = swLights.get(0)
//State первой лампы в группе
val firstLightState = gLights.members.findFirst[name.equals(firstLight)].state
//настройки первой лампы
//val firstLightSettings = getSettingsByCurrentMode.apply(firstLight, _lights, _default) as HashMap
/*
* Обработка длинного нажатия
*/
// Таймер на 2 секунды
var Timer longTimer = createTimer(now.plusSeconds(2), [ |
//если при срабатывании таймера кнопка по прежнему нажата
if (sw.state == ON) {
swLights.forEach [ swl |
//если было выключено - включаем и обнуляем таймеры
if (firstLightState==OFF) {
sendCommand(swl, "ON")
postUpdate(swl, "ON")
var Timer offTimer = _timeoutTimers.get(swl)
if (offTimer != null) {
offTimer.cancel
_timeoutTimers.put(swl, null)
logInfo("SLbyS", swl+": timeoutTimer : clear")
}
logInfo("SLbyS", swl+": long_press : ON")
}
//если было включено - выключаем и обнуляем таймеры
else {
sendCommand(swl, "OFF")
postUpdate(swl, "OFF")
var Timer offTimer = _timeoutTimers.get(swl)
if (offTimer != null) {
offTimer.cancel
_timeoutTimers.put(swl, null)
logInfo("SLbyS", swl+": timeoutTimer : clear")
}
logInfo("SLbyS", swl+": long_press : OFF")
}
]
}
])
/*
* Обработка короткого нажатия
*/
swLights.forEach [ swl |
//настройки лампы
var lightSettings = getSettingsByCurrentMode.apply(swl, _lights, _default) as HashMap
//Item лампы
var lightItem = gLights.members.findFirst[name.equals(swl)]
/*
* переключаем всю группу в одно состояние обратное от первого элемента
* TODO проверить на отключение таймеров
*/
// если лампа включена - выключаем
if (firstLightState==ON) { //TODO что делать если у первой лампы в группе стоит NEVER?
// если есть offTimeout, выключаем через некоторое время
if (lightSettings.get("offTimeout").intValue()>0) {
val timerItem=lightItem
//включаем, на случай, если было выключено
sendCommand(timerItem, "ON")
postUpdate(timerItem, "ON")
logInfo("SLbyS", swl+": offTimer : ON -> "+lightSettings.get("offTimeout").intValue()+" -> OFF")
var Timer offTimer = createTimer(now.plusSeconds(lightSettings.get("offTimeout").intValue()), [ |
sendCommand(timerItem, "OFF")
postUpdate(timerItem, "OFF")
logInfo("SLbyS", swl+": offTimer : OFF")
])
_timeoutTimers.put(swl, offTimer)
}
// просто выключаем
else {
sendCommand(lightItem, "OFF")
postUpdate(lightItem, "OFF")
var Timer offTimer = _timeoutTimers.get(swl)
if (offTimer != null) {
offTimer.cancel
_timeoutTimers.put(swl, null)
logInfo("SLbyS", swl+": timeoutTimer : clear")
}
logInfo("SLbyS", swl+": button : OFF")
}
}
// если лампа выключена - включаем
else {
if (lightSettings.get("buttonTimeout").doubleValue() == Double.NEGATIVE_INFINITY) {
//если NEGATIVE_INFINITY не включаем
logInfo("SLbyS", swl+": button : skip")
} else if (lightSettings.get("buttonTimeout").doubleValue() == Double.POSITIVE_INFINITY) {
//POSITIVE_INFINITY просто включаем
sendCommand(lightItem, "ON")
postUpdate(lightItem, "ON")
logInfo("SLbyS", swl+": button : ON")
} else if (lightSettings.get("buttonTimeout").intValue()>0) {
//включаем с таймером
sendCommand(lightItem, "ON")
postUpdate(lightItem, "ON")
val timerItem=lightItem
var Timer offTimer = createTimer(now.plusSeconds(lightSettings.get("buttonTimeout").intValue()), [ |
sendCommand(timerItem, "OFF")
postUpdate(timerItem, "OFF")
logInfo("SLbyS", swl+": startTimer : OFF")
])
_timeoutTimers.put(swl, offTimer)
logInfo("SLbyS", swl+": startTimer : ON -> "+lightSettings.get("buttonTimeout").intValue()+" -> OFF")
}
}
]
}
]
//обнуляем время нажатия кнопки после отпускания
for (sw : _switches_time.entrySet()) {
if (gSwitches.members.findFirst[name.equals(sw.getKey())].state==OFF){
_switches_time.remove(sw.getKey())
}
}
end
{{tag>openhab свет}}