sw:openhab:examples:light

Свет

Автор: d.v.ermakov » 17 мар 2016, 20:24

Поделюсь немного опытом использования байндинга в управлении светом. В развитие уже имеющейся инструкции автора на Гитхабе (ссылка).

Во-первых, я не пользуюсь советом из инструкции переключать порт Меги (к которому подключена кнопка) в режим 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
Файлов с правилами в Опенхабе может быть много, удобно делить правила на файлы по функционалу.

Автор: 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<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

  • sw/openhab/examples/light.txt
  • Последнее изменение: 2023/12/24 20:46
  • lazygatto