Различия
Показаны различия между двумя версиями страницы.
Предыдущая версия справа и слева Предыдущая версия Следующая версия | Предыдущая версия | ||
sw:openhab:examples:light [2023/05/23 10:57] – удалено - внешнее изменение (Дата неизвестна) 127.0.0.1 | sw:openhab:examples:light [2023/12/24 20:46] (текущий) – lazygatto | ||
---|---|---|---|
Строка 1: | Строка 1: | ||
+ | ====== Свет ====== | ||
+ | ===== Вариант #1 ===== | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | Поделюсь немного опытом использования байндинга в управлении светом. В развитие уже имеющейся инструкции автора на Гитхабе ([[https:// | ||
+ | |||
+ | Во-первых, | ||
+ | |||
+ | Вот правило для обычного включения/ | ||
+ | <sxh cpp> | ||
+ | /* Кнопка X */ | ||
+ | rule " | ||
+ | when Item Button_X changed to ON | ||
+ | then if (Light_X.state == OFF) { | ||
+ | sendCommand(Light_X, | ||
+ | postUpdate(Light_X, | ||
+ | } else { | ||
+ | sendCommand(Light_X, | ||
+ | postUpdate(Light_X, | ||
+ | } | ||
+ | postUpdate(Button_X, | ||
+ | end | ||
+ | </ | ||
+ | Во-вторых, | ||
+ | <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 " | ||
+ | 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:: | ||
+ | } | ||
+ | ] | ||
+ | end | ||
+ | |||
+ | rule " | ||
+ | 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 { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | end | ||
+ | </ | ||
+ | |||
+ | Если у вас несколько диммеров в одном файле, переменные внутри правил переименовывать в каждом не нужно, они локальные. Только Stored_Y и TimeON_Y должны быть у каждого диммера свои, и объявлены в начале файла. | ||
+ | |||
+ | Вторая серия. Использование датчиков присутствия/ | ||
+ | Это простое правило: | ||
+ | <code cpp> | ||
+ | val int PresenceDelay = 60 /* Задержка выключения света по датчикам присутствия в секундах */ | ||
+ | |||
+ | /* Свет в гардеробной */ | ||
+ | rule " | ||
+ | when | ||
+ | Item Presence_Closet changed to ON | ||
+ | then | ||
+ | if (Light_Closet.state == OFF) { | ||
+ | sendCommand(Light_Closet, | ||
+ | postUpdate(Light_Closet, | ||
+ | } | ||
+ | end | ||
+ | |||
+ | rule " | ||
+ | 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, | ||
+ | postUpdate(Light_Closet, | ||
+ | } | ||
+ | } | ||
+ | ] | ||
+ | end | ||
+ | </ | ||
+ | А это правило для света в санузле, | ||
+ | <code cpp> | ||
+ | val int PresenceDelay = 60 /* Задержка выключения света по датчикам присутствия в секундах */ | ||
+ | |||
+ | /* Свет в санузле */ | ||
+ | rule " | ||
+ | when | ||
+ | Item Presence_Corridor changed to ON or | ||
+ | Item Presence_WC changed to ON | ||
+ | then | ||
+ | if (Light_WC.state == OFF) { | ||
+ | sendCommand(Light_WC, | ||
+ | postUpdate(Light_WC, | ||
+ | } | ||
+ | end | ||
+ | |||
+ | rule " | ||
+ | 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, | ||
+ | postUpdate(Light_WC, | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | ] | ||
+ | end | ||
+ | </ | ||
+ | Файлов с правилами в Опенхабе может быть много, удобно делить правила на файлы по функционалу. | ||
+ | |||
+ | ===== Вариант #2 ===== | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | Хочу поделиться своим правилом для управления светом. Поскольку с java и Xtend я опыта раньше не имел, написано скорее всего не самым оптимальным образом. Кучу ошибок (в основном с типами) приходилось решать методом тыка, но сейчас по крайней мере редактор не ругается) Версия самая начальная, | ||
+ | |||
+ | Основная концепция такая: | ||
+ | Сделать код управления общим для всех, все различия вынести в настройки | ||
+ | Каждый выключатель или датчик движения отвечает за один или несколько выходов (свет, вытяжки и т.п.) | ||
+ | Если выходов несколько на одной кнопке, | ||
+ | Каждый выход настраивается отдельно и не зависит от того каким выключателем был включен | ||
+ | В зависимости от текущего режима (у меня это время суток), | ||
+ | При старте режима, | ||
+ | В разных режимах - можно сделать разное значение диммера | ||
+ | По длительному нажатию, | ||
+ | В данный момент, | ||
+ | |||
+ | Теперь к коду: megadevice.items | ||
+ | <code cpp> | ||
+ | // | ||
+ | Group: | ||
+ | // | ||
+ | Group: | ||
+ | // | ||
+ | //в sitemap прописан как | ||
+ | //Switch item=modeSelector mappings=[" | ||
+ | String modeSelector " | ||
+ | |||
+ | Switch F2BedroomBedButton " | ||
+ | Switch F2BedroomHallButtonL " | ||
+ | Switch F2BedroomHallButtonR " | ||
+ | Switch F2BedroomBra " | ||
+ | Switch F2BedroomBathMirror " | ||
+ | Switch F2BedroomBathLight " | ||
+ | Switch F2BedroomBathAir " | ||
+ | Switch F2BedroomLight1 " | ||
+ | Switch F2BedroomLight2 " | ||
+ | </ | ||
+ | |||
+ | Ниже в коде пример настройки для спальни с санузлом: | ||
+ | |||
+ | 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( | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ) | ||
+ | |||
+ | var HashMap< | ||
+ | newHashMap( | ||
+ | " | ||
+ | ), | ||
+ | " | ||
+ | ), | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ), | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ), | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ), | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ) | ||
+ | ) | ||
+ | |||
+ | // | ||
+ | var HashMap< | ||
+ | newLinkedHashMap( | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ) | ||
+ | |||
+ | // | ||
+ | //TODO: реализовать | ||
+ | var HashMap< | ||
+ | newLinkedHashMap( | ||
+ | " | ||
+ | " | ||
+ | ) | ||
+ | |||
+ | // Таймеры на отключение по timeout | ||
+ | var HashMap< | ||
+ | // Триггеры на сработавший механизм, | ||
+ | var HashMap< | ||
+ | // время нажатия now.getMillisOfDay | ||
+ | var HashMap< | ||
+ | | ||
+ | |||
+ | // | ||
+ | // | ||
+ | //mode берем из modeSelector | ||
+ | |||
+ | val Functions$Function3 getSettingsByCurrentMode = [ | ||
+ | String item, | ||
+ | HashMap< | ||
+ | HashMap< | ||
+ | var mode = modeSelector.state.toString | ||
+ | var HashMap< | ||
+ | |||
+ | if (gSettings.containsKey(item)) { | ||
+ | var settings = gSettings.get(item) | ||
+ | |||
+ | for (def_entry : def.entrySet()) { | ||
+ | var v = def_entry.getKey() | ||
+ | if (settings.containsKey(v+" | ||
+ | result.put(v, | ||
+ | } else { | ||
+ | if(settings.containsKey(v)) { | ||
+ | result.put(v, | ||
+ | } else { | ||
+ | result.put(v, | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } 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(), | ||
+ | _lastTriggers.put(t.getKey(), | ||
+ | } | ||
+ | end | ||
+ | |||
+ | rule " | ||
+ | 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, | ||
+ | } | ||
+ | |||
+ | // | ||
+ | val ArrayList< | ||
+ | |||
+ | // | ||
+ | if (swLights != null && swLights.size> | ||
+ | // | ||
+ | val firstLight = swLights.get(0) | ||
+ | //State первой лампы в группе | ||
+ | val firstLightState = gLights.members.findFirst[name.equals(firstLight)].state | ||
+ | // | ||
+ | //val firstLightSettings = getSettingsByCurrentMode.apply(firstLight, | ||
+ | |||
+ | |||
+ | /* | ||
+ | * Обработка длинного нажатия | ||
+ | */ | ||
+ | // Таймер на 2 секунды | ||
+ | var Timer longTimer = createTimer(now.plusSeconds(2), | ||
+ | // | ||
+ | if (sw.state == ON) { | ||
+ | swLights.forEach [ swl | | ||
+ | // | ||
+ | if (firstLightState==OFF) { | ||
+ | sendCommand(swl, | ||
+ | postUpdate(swl, | ||
+ | var Timer offTimer = _timeoutTimers.get(swl) | ||
+ | if (offTimer != null) { | ||
+ | offTimer.cancel | ||
+ | _timeoutTimers.put(swl, | ||
+ | logInfo(" | ||
+ | } | ||
+ | logInfo(" | ||
+ | } | ||
+ | // | ||
+ | else { | ||
+ | sendCommand(swl, | ||
+ | postUpdate(swl, | ||
+ | var Timer offTimer = _timeoutTimers.get(swl) | ||
+ | if (offTimer != null) { | ||
+ | offTimer.cancel | ||
+ | _timeoutTimers.put(swl, | ||
+ | logInfo(" | ||
+ | } | ||
+ | logInfo(" | ||
+ | } | ||
+ | ] | ||
+ | } | ||
+ | ]) | ||
+ | |||
+ | |||
+ | |||
+ | /* | ||
+ | * Обработка короткого нажатия | ||
+ | | ||
+ | swLights.forEach [ swl | | ||
+ | // | ||
+ | var lightSettings = getSettingsByCurrentMode.apply(swl, | ||
+ | //Item лампы | ||
+ | var lightItem = gLights.members.findFirst[name.equals(swl)] | ||
+ | | ||
+ | /* | ||
+ | * переключаем всю группу в одно состояние обратное от первого элемента | ||
+ | * TODO проверить на отключение таймеров | ||
+ | */ | ||
+ | // если лампа включена - выключаем | ||
+ | if (firstLightState==ON) { //TODO что делать если у первой лампы в группе стоит NEVER? | ||
+ | // если есть offTimeout, выключаем через некоторое время | ||
+ | if (lightSettings.get(" | ||
+ | val timerItem=lightItem | ||
+ | |||
+ | // | ||
+ | | ||
+ | | ||
+ | |||
+ | logInfo(" | ||
+ | var Timer offTimer = createTimer(now.plusSeconds(lightSettings.get(" | ||
+ | sendCommand(timerItem, | ||
+ | postUpdate(timerItem, | ||
+ | logInfo(" | ||
+ | ]) | ||
+ | _timeoutTimers.put(swl, | ||
+ | } | ||
+ | // просто выключаем | ||
+ | else { | ||
+ | sendCommand(lightItem, | ||
+ | postUpdate(lightItem, | ||
+ | |||
+ | var Timer offTimer = _timeoutTimers.get(swl) | ||
+ | if (offTimer != null) { | ||
+ | offTimer.cancel | ||
+ | _timeoutTimers.put(swl, | ||
+ | logInfo(" | ||
+ | } | ||
+ | |||
+ | logInfo(" | ||
+ | } | ||
+ | } | ||
+ | // если лампа выключена - включаем | ||
+ | else { | ||
+ | if (lightSettings.get(" | ||
+ | // | ||
+ | logInfo(" | ||
+ | } else if (lightSettings.get(" | ||
+ | // | ||
+ | sendCommand(lightItem, | ||
+ | postUpdate(lightItem, | ||
+ | logInfo(" | ||
+ | } else if (lightSettings.get(" | ||
+ | // | ||
+ | sendCommand(lightItem, | ||
+ | postUpdate(lightItem, | ||
+ | |||
+ | val timerItem=lightItem | ||
+ | var Timer offTimer = createTimer(now.plusSeconds(lightSettings.get(" | ||
+ | sendCommand(timerItem, | ||
+ | postUpdate(timerItem, | ||
+ | logInfo(" | ||
+ | ]) | ||
+ | _timeoutTimers.put(swl, | ||
+ | logInfo(" | ||
+ | } | ||
+ | |||
+ | } | ||
+ | ] | ||
+ | |||
+ | } | ||
+ | ] | ||
+ | |||
+ | // | ||
+ | for (sw : _switches_time.entrySet()) { | ||
+ | if (gSwitches.members.findFirst[name.equals(sw.getKey())].state==OFF){ | ||
+ | _switches_time.remove(sw.getKey()) | ||
+ | } | ||
+ | } | ||
+ | |||
+ | end | ||
+ | </ | ||
+ | |||
+ | |||
+ | {{tag> |