sw:openhab:examples:light

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
sw:openhab:examples:light [2023/05/23 10:57] – удалено - внешнее изменение (Дата неизвестна) 127.0.0.1sw:openhab:examples:light [2023/12/24 20:46] (текущий) lazygatto
Строка 1: Строка 1:
 +====== Свет ======
  
 +===== Вариант #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.
 +
 +Вот правило для обычного включения/выключения кнопкой:
 +<sxh cpp>
 +/* Кнопка 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
 +</sxh>
 +Во-вторых, вот правило для управления яркостью (диммирования) одной кнопкой. Эта кнопка должна быть в режиме P&R. Действует так: короткое нажатие выключает/включает на прежний уровень яркости, длинное нажатие плавно уменьшает яркость до 1%, затем увеличивает до 100%, затем снова уменьшает, и т. д.
 +<sxh cpp>
 +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
 +</sxh>
 +
 +Если у вас несколько диммеров в одном файле, переменные внутри правил переименовывать в каждом не нужно, они локальные. Только Stored_Y и TimeON_Y должны быть у каждого диммера свои, и объявлены в начале файла.
 +
 +Вторая серия. Использование датчиков присутствия/движения для управления светом.
 +Это простое правило:
 +<code cpp>
 +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
 +</code>
 +А это правило для света в санузле, если у вас есть два датчика движения, один в самом санузле, а второй в коридоре, непосредственно перед санузлом. Включается свет по любому из двух датчиков, а выключается по датчику коридора, при условии, что внутри никого нет. У меня двери в санузлы имеют большие вставки из матового стекла и я использую свет в санузле как ночное освещение коридора. Кроме того, у меня в санузлах по две группы света, маленькая ночная лампочка и основное освещение. Ну и можно изменить правило по вкусу.
 +<code cpp>
 +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
 +</code>
 +Файлов с правилами в Опенхабе может быть много, удобно делить правила на файлы по функционалу.
 +
 +===== Вариант #2 =====
 +
 +[[https://www.ab-log.ru/forum/viewtopic.php?p=28452#p28452|Автор: bvasya » 06 июн 2017, 16:47]]
 +
 +Хочу поделиться своим правилом для управления светом. Поскольку с java и Xtend я опыта раньше не имел, написано скорее всего не самым оптимальным образом. Кучу ошибок (в основном с типами) приходилось решать методом тыка, но сейчас по крайней мере редактор не ругается) Версия самая начальная, скорее всего буду переписывать, поэтому формат настроек может поменяться. У меня работает на OH2 + биндинг от OH1. На OH1 скорее всего работать не будет, там много поменялось в работе правил.
 +
 +Основная концепция такая:
 +Сделать код управления общим для всех, все различия вынести в настройки
 +Каждый выключатель или датчик движения отвечает за один или несколько выходов (свет, вытяжки и т.п.)
 +Если выходов несколько на одной кнопке, за основу берем первый по порядку
 +Каждый выход настраивается отдельно и не зависит от того каким выключателем был включен
 +В зависимости от текущего режима (у меня это время суток), выходы могут по разному реагировать на нажатие кнопок
 +При старте режима, можно сменить состояние и т.п.
 +В разных режимах - можно сделать разное значение диммера
 +По длительному нажатию, выход включается или выключается без дополнительных настроек
 +В данный момент, работает только с кнопками. Датчики движения, диммеры и смену состояния при смене режима пока не делал. Бывают небольшие глюки, но пока не отлавливал причину. Код старался по максимуму комментировать и в логи сейчас практически все изменения пишутся.
 +
 +Теперь к коду: megadevice.items
 +<code cpp>
 +//група со всеми выключателями, на которые действует правило
 +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"}
 +</code>
 +
 +Ниже в коде пример настройки для спальни с санузлом: бра обычно включается на 3 минуты, вечером и ночью без таймера. Основной свет в комнате вечером и ночью не включается (только по длительному нажатию). В санузле вчером и ночью вытяжка включается сразу и работает после выключения 10 минут, в остальное время вытяжка включается только после выключения света на 5 минут
 +
 +megadevice.rules
 +<code cpp>
 +
 +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<String, HashMap<String, ?>> _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<String, ArrayList<String>> _switches = 
 + newLinkedHashMap(
 + "F2BedroomBedButton" -> newArrayList("F2BedroomBra", "F2BedroomLight1", "F2BedroomLight2"),
 + "F2BedroomHallButtonL" -> newArrayList("F2BedroomBra", "F2BedroomLight1", "F2BedroomLight2"),
 + "F2BedroomHallButtonR" -> newArrayList("F2BedroomBathLight", "F2BedroomBathMirror", "F2BedroomBathAir")
 + )   
 +
 +//датчики
 +//TODO: реализовать
 +var HashMap<String, ArrayList<String>> _sensors = 
 + newLinkedHashMap(
 + "sensor1" -> newArrayList("light1"),
 + "sensor2" -> newArrayList("light2")
 + )   
 +
 +// Таймеры на отключение по timeout
 +var HashMap<String, Timer> _timeoutTimers = newHashMap
 +// Триггеры на сработавший механизм, чтобы датчики движения не переназначали действия кнопок
 +var HashMap<String, String> _lastTriggers = newHashMap    
 +// время нажатия now.getMillisOfDay
 +var HashMap<String, Number> _switches_time = newHashMap      
 +  
 +
 +//лямбда для получения настроек в зависимости от режима
 +//приоритет настроек: mode настройка для лампы -> общая для лампы -> общая для всех
 +//mode берем из modeSelector
 +
 +val Functions$Function3 getSettingsByCurrentMode = [ 
 + String item,
 + HashMap<String, HashMap<String, Number>> gSettings,
 + HashMap<String, Number> def |
 + var mode = modeSelector.state.toString
 + var HashMap<String, Number> 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<String> 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<String, Number>
 +
 +
 + /*
 +  * Обработка длинного нажатия
 +  */
 +  // Таймер на 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<String, Number>
 +                    //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
 +</code>
 +
 +
 +{{tag>openhab свет}}