Add a property to a item or type

Hey there,

I have pairs of two different items that belong together:

Temperature1 (temp) and automode1
Temperature2 (temp) and automode2
Temperature3 (temp) and automode3
and so on.

As I have this many times so I would like to be able to set the temperature forEach member of temp, but only if the corresponding automode is ON.

I thought about adding a new property to the Temperature and set it equal automode, so I could use something in a rule like:

	Temperature1.auto = automode1.state
	Temperature2.auto = automode2.state
	Temperature3.auto = automode3.state
		
	temp.forEach[t |
			sendCommand(if(t.auto != OFF) t, OFF)
	]

But this part doesn’t work:

Temperature1.auto = automode1.state
Temperature2.auto = automode2.state

Is it possible at all achieve do this? Or is there a better way?

Thanks in advance
Viktor

Do you mean something like this:
items managed by UI:

Switch OD_Switch_Lucht        "Vijver luchtpomp"				<light>
Switch OD_Switch_Water        "Vijver waterpomp"				<light>
Switch OD_Switch_Uv	      "Vijver UV licht"					<light>
your code goes here

And the definition of the actual switches are like this:

Switch	Klik21	"Vijver luchtpomp"			<light>	(gOD_Switches)					{ rfxcom=">18522370.5:LIGHTING2.AC:Command" }
Switch	Klik22	"Vijver waterpomp"			<light>	(gOD_Switches)					{ rfSwitch	Klik21	"Vijver luchtpomp"			<light>	(gOD_Switches)					{ rfxcom=">18522370.5:LIGHTING2.AC:Command" }
Switch	Klik23	"Vijver UV licht"			<light>	(gOD_Swithces)					{ rfxcom=">16979794.1:LIGHTING2.AC:Command" }

Then in one of my rules, I use this for setting the actual switches to the status specified by the UI:

    		Klik21.sendCommand(OD_Switch_Lucht.state)
    		Klik22.sendCommand(OD_Switch_Water.state)
    		Klik23.sendCommand(OD_Switch_Uv.state)

And this for turning them all off in one hit:

		gOD_Switches.sendCommand(OFF)

Hi,

thanks for your reply.

I’m not sure if I got you and the code right.
Right now I have something similar, I think:

Items:


Number	temperature1	"Set Temperatur WZ [%.1f °C]"	<heating>		(EG_Wohnzimmer, Temp_WZ, Temp)
Switch	automode1	"Automatik WZ"			<switch>		(EG_Wohnzimmer, Temp_WZ)

And then in the rule:

sendCommand(if(automode1.state == ON) temperature1, 24)

And this a couple of times… It works perfectly, but If I add a few thermostats I need to copy this all multiple times. That’s why I’m looking for a solution with a forEach, so I only need to add a few lines of code :slight_smile:

I just wrote up a Design Pattern for this sort of situation. I do this all the time.

In your case it would look something like

Temperature1 -> Temperature1_automode
Temperature2 -> Temperature2_automode

Assuming you put the automode Items into a gAutomode group and the Temperatures are in a group called gTemp

    gTemp.members.forEach[temp |
        val auto = gAutomode.members.filter[am|am.name == temp.name+"_automode"].head
        sendCommand(if(auto.state != OFF) temp.state, OFF)
    ]
1 Like

Rich,

cool, I understand the concept. Thanks for that, will try it! :slight_smile:

Hey Rich,

Not at home to try this but was wondering if my logic is correct below? Trying to reduce a lot of rules into a single rule below.

    gNameSonos.members.forEach[ temp |
        val staat   = gNameSonos.members.filter[a|a.name == temp.name+"_State"].head
	    val stop    = gNameSonos.members.filter[b|b.name == temp.name+"_Stop"].head
		val volume  = gNameSonos.members.filter[c|c.name == temp.name+"_Volume"].head
		val fav     = gNameSonos.members.filter[d|d.name == temp.name+"_Favorite"].head

		if (staat.state != 'PLAY' || staat.state != 'PLAYING' || staat.state != 'TRANSITIONING') {

			stop.sendCommand(ON)
				Thread::sleep(500)
			volume.sendCommand('10')
				Thread::sleep(500)
			fav.sendCommand(Sonos_Today_Favorite)
				Thread::sleep(500)
			stop.sendCommand(OFF)
				Thread::sleep(500)
		}
    ]

Best, Jay

Using Item registry methods to directly access Items by name are more efficient, a feature introduced in the five years since this thread was last updated (but now included in the Design Pattern referenced).

ScriptServiceUtil.getItemRegistry.getItem(someName)
in DSL

Searching through Groups still has application for those odd occasions where the target Item might not actually exist, and you want to handle that gracefully in the rule.

The overall logic looks sound but there are some concerns. I’m assuming your tagline is correct and you are still running OH 2.4. Some of what I’m about to say does not apply, or does not apply in the same way for OH 3.

  • Use findFirst instead of filter.*.head. It’s more efficient and more clear.
  • Long running rules are a potential big problem in OH versions prior to 3. Use timers instead of Thread::sleeps
  • You can add a filter to only return those where staat is in a state you care about instead of the if statement. I’m not sure it matters either way but it’s an option and would be slightly more efficient, though that sort of efficiency hardly matters.
gNameSonos.members.filter[ temp |
    val staat = gNameSonos.members.findFirst[a|a.name == temp.name+"_State"]
    (staat.state != 'Play' || staat.state != 'PLAYING' || staat.state != 'TRANSITIONING')
].forEach[ temp |
  val stop    = gNameSonos.members.findFirst[b|b.name == temp.name+"_Stop"]
  val volume  = gNameSonos.members.findFirst[c|c.name == temp.name+"_Volume"]
  val fav     = gNameSonos.members.findFirst[d|d.name == temp.name+"_Favorite"]
  stop.sendCommand(ON)
  createTimer(now.plusMillis(500),  [ | volume.sendCommand('10') ]
  createTimer(now.plusMillis(1000), [ | fav.sendCommand(Sonos_Today_Favorite) ]
  createTimer(now.plusMillis(1500), [ | stop.sendCommand(OFF) ]
]

Thank you Rich, looks like I’m still missing something on this rule.

Here’s the entire rule. The loginfo isn’t working so it must be having issues prior to that section.

				gNameSonos.members.filter[ temp |
				
				    val staat = gNameSonos.members.findFirst[a|a.name == temp.name+"_State"]
					
				    (staat.state != 'PLAY' || staat.state != 'PLAYING' || staat.state != 'TRANSITIONING')
					].forEach[ temp |
					
					  val stop    = gNameSonos.members.findFirst[b|b.name == temp.name+"_Stop"]
					  val volume  = gNameSonos.members.findFirst[c|c.name == temp.name+"_Volume"]
					  val fav     = gNameSonos.members.findFirst[d|d.name == temp.name+"_Favorite"]
					  val alone   = gNameSonos.members.findFirst[e|e.name == temp.name+"_StandAlone"]
					  val queue   = gNameSonos.members.findFirst[f|f.name == temp.name+"_ClearQueue"]
					  val remove  = gNameSonos.members.findFirst[g|g.name == temp.name+"_Remove"]
					  val url  	  = gNameSonos.members.findFirst[h|h.name == temp.name+"_CurrentAlbumCoverArtURL"]	
					  val proxy   = gNameSonos.members.findFirst[i|i.name == temp.name+"_CurrentAlbumCoverArtURL_Proxy"]
					  val track   = gNameSonos.members.findFirst[j|j.name == temp.name+"_CurrentTrack"]
					  
					  logInfo("STARTUP", staat + " state is " + staat.state)
					  logInfo("STARTUP", stop + " state is " + stop.state)
					  logInfo("STARTUP", volume + " state is " + volume.state)
					  logInfo("STARTUP", fav + " state is " + fav.state)
					  logInfo("STARTUP", alone + " state is " + alone.state)
					  logInfo("STARTUP", queue + " state is " + queue.state)
					  logInfo("STARTUP", remove + " state is " + remove.state)
					  logInfo("STARTUP", url + " state is " + url.state)
				      logInfo("STARTUP", proxy + " state is " + proxy.state)
					  logInfo("STARTUP", track + " state is " + track.state)
					  
					  stop.sendCommand(ON)
					  createTimer(now.plusMillis(500),   [ | volume.sendCommand('10') ])
					  createTimer(now.plusMillis(1000),  [ | fav.sendCommand(Sonos_Today_Favorite) ])
					  createTimer(now.plusMillis(1500),  [ | stop.sendCommand(OFF) ])
					  createTimer(now.plusMillis(2000),  [ | alone.postUpdate(OFF) ])
					  createTimer(now.plusMillis(2500),  [ | queue.postUpdate(OFF) ])
					  createTimer(now.plusMillis(3000),  [ | remove.postUpdate('OFF') ])
					  createTimer(now.plusMillis(3500),  [ | url.postUpdate(DummyImageURL) ])
					  createTimer(now.plusMillis(4000),  [ | proxy.postUpdate(DummyImageURL) ])
					  createTimer(now.plusMillis(4500),  [ | track.postUpdate(DummyTrack) ])	
				]

Here’s the error its causing:

2021-06-17 09:49:54.996 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.2021-06-17T09:49:54.871-05:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  logInfo(<XStringLiteralImpl>,<XBinaryOperationImplCustom>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  <XMemberFeatureCallImplCustom>.forEach(<XClosureImplCustom>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@10378f7f
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@6bfe49b3
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@67483c0f
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@52424e96
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@240c3c55
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@2094bf8b
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@7b8535a7
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@40f7eab9
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  <XFeatureCallImplCustom>.cancel()
  <XFeatureCallImplCustom>::sleep(<XNumberLiteralImpl>)
  <null>.SonosStartUp_tAlive = <XNullLiteralImplCustom>
} ] threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
	at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
Caused by: java.lang.NullPointerException: cannot invoke method public abstract org.eclipse.smarthome.core.types.State org.eclipse.smarthome.core.items.Item.getState() on null
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1071) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1061) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1047) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:772) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1116) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1046) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:902) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:226) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:872) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:226) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:872) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:226) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy225.apply(Unknown Source) ~[?:?]
	at org.eclipse.xtext.xbase.lib.internal.BooleanFunctionDelegate.apply(BooleanFunctionDelegate.java:41) ~[?:?]
	at com.google.common.collect.Iterators$7.computeNext(Iterators.java:652) ~[?:?]
	at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:143) ~[?:?]
	at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:138) ~[?:?]
	at java.lang.Iterable.forEach(Iterable.java:74) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1086) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1061) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1047) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:772) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy227.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
	... 1 more


Thank you for taking the time on this.

Best, Jay

The error is coming from inside one of your Timers.

Beyond that, about all I can say is if you reload the .rules file this rule is defined in and there are active timers running, you will see an error like this because the Timers will still exist and still be running. But they will fail with an error like this when they finally do go off.

So first make sure this isn’t just caused by your reloading the rule.

Then, if it’s not caused by a reload, add logging to each timer so you can try to figure out which timer is causing the problem. From there you should be able to figure out what the problem is.