Свет
Вариант #1
Автор: d.v.ermakov » 17 мар 2016, 20:24
Поделюсь немного опытом использования байндинга в управлении светом. В развитие уже имеющейся инструкции автора на Гитхабе (ссылка).
Во-первых, я не пользуюсь советом из инструкции переключать порт Меги (к которому подключена кнопка) в режим P&R. Для корректной работы правила по включению/выключению этого не нужно, а также это мешает нормальной работе автономного режима Меги (если сервер выключить). Поэтому обычные кнопки у меня в режиме P (и автономный режим настроен на всякий случай), только кнопки диммеров и датчики присутствия в режиме P&R.
Вот правило для обычного включения/выключения кнопкой:
1 2 3 4 5 6 7 8 9 10 11 12 |
/* Кнопка 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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
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 должны быть у каждого диммера свои, и объявлены в начале файла.
Вторая серия. Использование датчиков присутствия/движения для управления светом. Это простое правило:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
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
Автор: bvasya » 06 июн 2017, 16:47
Хочу поделиться своим правилом для управления светом. Поскольку с java и Xtend я опыта раньше не имел, написано скорее всего не самым оптимальным образом. Кучу ошибок (в основном с типами) приходилось решать методом тыка, но сейчас по крайней мере редактор не ругается) Версия самая начальная, скорее всего буду переписывать, поэтому формат настроек может поменяться. У меня работает на OH2 + биндинг от OH1. На OH1 скорее всего работать не будет, там много поменялось в работе правил.
Основная концепция такая: Сделать код управления общим для всех, все различия вынести в настройки Каждый выключатель или датчик движения отвечает за один или несколько выходов (свет, вытяжки и т.п.) Если выходов несколько на одной кнопке, за основу берем первый по порядку Каждый выход настраивается отдельно и не зависит от того каким выключателем был включен В зависимости от текущего режима (у меня это время суток), выходы могут по разному реагировать на нажатие кнопок При старте режима, можно сменить состояние и т.п. В разных режимах - можно сделать разное значение диммера По длительному нажатию, выход включается или выключается без дополнительных настроек В данный момент, работает только с кнопками. Датчики движения, диммеры и смену состояния при смене режима пока не делал. Бывают небольшие глюки, но пока не отлавливал причину. Код старался по максимуму комментировать и в логи сейчас практически все изменения пишутся.
Теперь к коду: megadevice.items
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//група со всеми выключателями, на которые действует правило 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
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 |