Position estimator for shutters

  • Platform information:
    • Hardware: Raspberry 3b
    • OS: openhabian w
    • Java Runtime Environment: I think 11 32bit
    • openHAB version: 3
  • Issue of the topic:
    Hi,
    I would like to estimate my rollershutter positions according to the time they moved up / down.

I know that this is not 100% accurate, but I need this only for the wind sensor to see if they are already on top resp how far they roughly have to move up till they are on the upper most position.

I read a bit about design patterns and thought first best idea is to set for each shutter a timestamp if the UP-relay is switched on / off or DOWN-relay is switched on /off, and calculate the time between these events.

My switch items are called
Shutter1_UP,
Shutter2_UP,
Shutter3_UP,
Shutter1_DW,
Shutter2_DW,
Shutter3_DW.

So, i defined in my rules file the variables:

//Times shutters need to move completely up
     val Shutter1_UP_Zeit = 25.0 | s
     val Shutter2_UP_Zeit = 25.0 | s
     val Shutter3_UP_Zeit = 25.0 | s
...
//Times shutters need to move completely down
     val Shutter1_DW_Zeit = 25.0 | s
     val Shutter2_DW_Zeit = 25.0 | s
     val Shutter3_DW_Zeit = 25.0 | s
...
//start times when shutters were set to on
     var long Shutter1_UP_Startzeit
     var long Shutter2_UP_Startzeit
     var long Shutter3_UP_Startzeit

     var long Shutter1_DW_Startzeit
     var long Shutter2_DW_Startzeit
     var long Shutter3_DW_Startzeit
...
//End times when shutters were set to off
     var long Shutter1_UP_Endzeit
     var long Shutter2_UP_Endzeit
     var long Shutter3_UP_Endzeit

     var long Shutter1_DW_Endzeit
     var long Shutter2_DW_Endzeit
     var long Shutter3_DW_Endzeit

and wanted to use a rule something like

rule "start Timer when shutter begins to move"
   when
      Member of gShutters changed to ON
   then
   {
   postUpdate(triggeringItem.name+"_Startzeit" = now.toEpochSecond()) 
   }
end

rule "end Timer when shutter stops to move"
   when
      Member of gSonoffShutters changed to OFF
   then
   {  
      if(previousState == ON)
	  {
	postUpdate(triggeringItem.name+"_Endzeit",  now.toEpochSecond())
		 
		 
	  gRollershutterItem.members.forEach[ RollershutterItem rs |
		 //here goes the for each 
		  val EndzeitName = rs.name+"_Endzeit"
          val Endzeit = ScriptServiceUtil.getItemRegistry.getItem(EndzeitName)
		  val StartzeitName = rs.name+"_Startzeit"
          val Startzeit = ScriptServiceUtil.getItemRegistry.getItem(StartzeitName)
		 if (Endzeit.State <> Startzeit.state
		    { //Todo: update estimation
	            }
		 ]
	  }
   }
end

Unfortunately, I found out that postUpdate function does not work in this way for global variables, and use Number items for them did anyhow did not work either.

So I further searched around, and found out that there is already a rather convenient way to get the timestamp of a modify by simply adding the items

DateTime Shutter1_DW_TimeStamp "Timestamp" <clock> (gtestItems, gTimestamp) {channel="mqtt:topic:Shutter1:Power2" [profile="timestamp-update"]} 

So i wanted to map this via a rule of “Member of gTimestamp changed” to the items

DateTime Shutter1_Startzeit "Startzeit" (gtestItems, gTimestampStart)
DateTime Shutter1_Endzeit "Endzeit" (gtestItems, gTimestampStop)

, But there I stuck again in not knowing how to get the time in s resp. ms between these events.

So, to.make a long story short:

  1. is there a way to index via a string a global “var” like postUpdate for items?
  2. is there a more easy way to determine the time between 2 events?
  3. is my approach of the design patterns made correctly or is all I am doing here bullshit?

As I am struggling around now since a couple of days without real success, I would be really pleased if someone can point me to the correct direction how this task should be done in OpenHab, as I am unfortunately still a bloody beginner in OpenHab and it’s rules and methods, and… To be honest… In general, and would like to learn more from some more experienced users…

Thanks in advance,
Edizius

Providing you stick with file-based rules, yes. (The “globals” stuff gets complicated for UI entered rules)
Java provides a Map you can import which can be thought of as a kind of string-indexed array.
In your case you might have startmap and stopmap, each indexed by rollershutter name and containing epoch timestamps? EDIT - on second thoughts, you only want start; when it stops you’ll calculate new position using “now”.

Use of Map shown here

There are no native duration timers here. You do have to roll your own from timestamps.

It seems sound.

You will need to know the total travel time for each shutter. Maybe they vary even among “identical” actuators according to width.
They might take longer to open close;if it’s widely different you might need to make different calculations depending on direction of travel.
You might store these in Items so that get persisted and restored.

They’re probably non-linear, moving more slowly at one end of travel than the other,but you only want an estimate.

Assuming you’ve no end-stop detection either, you can come up with other ways to re-zero your position counter at one or other end of travel. Can you detect when the motor stops at an end-stop, or will you have to assume that when it has been driven longer than a particular time it must have come to the end?

If you did have stop detection, you could devise autocalibrate rules to populate the total travel times

1 Like

Thanks for your advices.
No, unfortunately I do not have a end-stop detection, so I just move a couple of seconds in the limit. 90% of the time the shutters are either open, or closed, so this should be enough for this rare cases.

I try to read your linked pages and try to work with it and will come back here in a couple of days.

Thanks for the tips :ok_hand::+1:

hi @rossko57

my first test run works like a charm.
My testrules file is actually:

import java.util.Map

val Map<String, Number> ShutterStartTimes = newHashMap

//Times shutters need to move completely up
     val Shutter1_UP_Zeit = 25.0 | s
     val Shutter2_UP_Zeit = 25.0 | s
     val Shutter3_UP_Zeit = 25.0 | s

//Times shutters need to move completely down
     val Shutter1_DW_Zeit = 25.0 | s
     val Shutter2_DW_Zeit = 25.0 | s
     val Shutter3_DW_Zeit = 25.0 | s
	 
// there is a way to initialize the Map statically but I always forget it so prefer a System started Rule
rule "initialize ShutterStartTimes"
when
    System started
then
    ShutterStartTimes.put("Shutter1_UP", 999999000)
    ShutterStartTimes.put("Shutter2_UP", 999999000)
    ShutterStartTimes.put("Shutter3_UP", 999999000)
	
    ShutterStartTimes.put("Shutter1_DW", 999999000)
    ShutterStartTimes.put("Shutter2_DW", 999999000)
    ShutterStartTimes.put("Shutter3_DW", 999999000)

end

rule "Shutter begins or stops to move"
   when
      Member of gtestItems changed
   then
   { 
      var long MyTimestamp = now.toInstant.toEpochMilli
      if (triggeringItem.state == ON) //start measurement
	  {
         logInfo("Test.rules", "Test run 3 start")
         ShutterStartTimes.put(triggeringItem.name, MyTimestamp)
      }
	  else  //end measurement
	  {
	    val StartTime = ShutterStartTimes.get(triggeringItem.name)
		val Duration = MyTimestamp - StartTime
		
          logInfo("Test.rules", "Duration of " + triggeringItem.name + " was " + Duration.toString + " (=" + MyTimestamp.toString + " - " + StartTime.toString + " ) ")
      }
	  
   }
end

and I received console output

2021-03-23 20:17:47.273 [INFO ] [openhab.core.model.script.Test.rules] - Test run 3 start

==> /var/log/openhab/events.log <==

2021-03-23 20:17:47.258 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'TestButton1' received command ON

2021-03-23 20:17:47.267 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'TestButton1' changed from OFF to ON

==> /var/log/openhab/openhab.log <==

2021-03-23 20:17:52.758 [INFO ] [openhab.core.model.script.Test.rules] - Duration of TestButton1 was 5479 (=1616527072749 - 1616527067270 ) 

==> /var/log/openhab/events.log <==

2021-03-23 20:17:52.739 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'TestButton1' received command OFF

2021-03-23 20:17:52.745 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'TestButton1' changed from ON to OFF

so, seems as it is so far working as desired. And that after coding with your support for 0,5h, compared to struggling around 4 nights without success :woozy_face:

I hope it will continue such fluently.

should the “total times for moving up / down” also be stored in a separate map, or is there a possibility to use mapping with more coloumns? →

val Map<String, Number, Number, Number> ShutterElements = newHashMap

where ShutterElements[0] is the shutter name, ShutterElements[1] is the timestamp introduced above, ShutterElements[2] is total duration to move from 0% to 100% and ShutterElements[3] is total duration to move from 100% to 0%?

I think its strictly key-value. Of course value could be a list or something, but for my own sanity I would choose to use separate simple Maps.

My instinct for the “constant” max runtimes is to put it/them in Item states, and let persistence manage save/restoreOnStartup. But I suppose that’s less useful without autocalibration.

Perhaps the next step is to work out “autozero” and “auto100%” rules for your position indicator.
Maybe start a timer when received UP/DOWN for max runtime + a margin.
If STOP received, cancel timer (and eventually do position calculation).
If timer expires, set position 0% or 100% as appropriate.

hi @rossko57 ,
yes i am using now

val Map<String, Number> ShutterStartTimestamps = newHashMap
val Map<String, Number> ShutterUpDurations = newHashMap
val Map<String, Number> ShutterDwDurations = newHashMap

even though they are constants. index for ShutterUpDurations and ShutterDwDurations is only Shutter1…Shutter3.

auto0 and auto100 is not needed for the moment, as I set my shutters in a way, that the relays open automatically after a certain time (30s i think), so it is always 100% / 0%, as this value is >25s which is the “real” duration.

The point i am struggling at the moment is more that the simple calculations

NewPosition = ( ActualPosition - ( Duration / ShutterDuration ) * 100 )

resp.

NewPosition = ( ActualPosition + ( Duration  / ShutterDuration ) * 100 )

fail with "

null in test

resp
Could not cast NULL to java.lang.Number; line 57, column 26, length 27 in test
as it can be seen in the log:

2021-03-23 21:39:27.758 [INFO ] [openhab.core.model.script.Test.rules] - Test run 4 start

==> /var/log/openhab/events.log <==

2021-03-23 21:39:27.746 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'Shutter1_UP' received command ON

2021-03-23 21:39:27.752 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Shutter1_UP' changed from OFF to ON

==> /var/log/openhab/openhab.log <==

2021-03-23 21:39:30.211 [INFO ] [openhab.core.model.script.Test.rules] - Duration of Shutter1_UP was 2.44800000 (=1616531970203 - 1616531967755 ) 

2021-03-23 21:39:30.225 [INFO ] [openhab.core.model.script.Test.rules] - 0 is actual position and direction UP

2021-03-23 21:39:30.230 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'test-2' failed: null in test

==> /var/log/openhab/events.log <==

2021-03-23 21:39:30.193 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'Shutter1_UP' received command OFF

2021-03-23 21:39:30.198 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Shutter1_UP' changed from ON to OFF

==> /var/log/openhab/openhab.log <==

2021-03-23 21:39:33.063 [INFO ] [openhab.core.model.script.Test.rules] - Test run 4 start

==> /var/log/openhab/events.log <==

2021-03-23 21:39:33.051 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'Shutter2_DW' received command ON

2021-03-23 21:39:33.056 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Shutter2_DW' changed from OFF to ON

==> /var/log/openhab/openhab.log <==

2021-03-23 21:39:35.255 [INFO ] [openhab.core.model.script.Test.rules] - Duration of Shutter2_DW was 2.18700000 (=1616531975247 - 1616531973060 ) 

2021-03-23 21:39:35.269 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'test-2' failed: Could not cast NULL to java.lang.Number; line 57, column 26, length 27 in test

==> /var/log/openhab/events.log <==

2021-03-23 21:39:35.238 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'Shutter2_DW' received command OFF

2021-03-23 21:39:35.245 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Shutter2_DW' changed from ON to OFF

and I do not understand really why…

This is the complete file

import java.util.Map

val Map<String, Number> ShutterStartTimestamps = newHashMap
val Map<String, Number> ShutterUpDurations = newHashMap
val Map<String, Number> ShutterDwDurations = newHashMap
	
// there is a way to initialize the Map statically but I always forget it so prefer a System started Rule
rule "initialize ShutterStartTimestamps"
when
    System started
then
    ShutterStartTimestamps.put("Shutter1_UP", 999999000)
    ShutterStartTimestamps.put("Shutter2_UP", 999999000)
    ShutterStartTimestamps.put("Shutter3_UP", 999999000)
	
    ShutterStartTimestamps.put("Shutter1_DW", 999999000)
    ShutterStartTimestamps.put("Shutter2_DW", 999999000)
    ShutterStartTimestamps.put("Shutter3_DW", 999999000)
	
	
    ShutterUpDurations.put("Shutter1", 25.0)
    ShutterUpDurations.put("Shutter2", 25.0)
    ShutterUpDurations.put("Shutter3", 25.0)
	
    ShutterDwDurations.put("Shutter1", 25.0)
    ShutterDwDurations.put("Shutter2", 25.0)
    ShutterDwDurations.put("Shutter3", 25.0)
	
	
end

rule "Shutter begins or stops to move"
   when
      Member of gtestItems changed
   then
   { 
      var long MyTimestamp = now.toInstant.toEpochMilli
      if (triggeringItem.state == ON) //start measurement
	  {
         logInfo("Test.rules", "Test run 4 start")
         ShutterStartTimestamps.put(triggeringItem.name, MyTimestamp)
      }
	  else  //end measurement
	  {
	    var Number NewPosition // = 0 // just to have an initial value
		var Number ShutterDuration
	    val StartTime = ShutterStartTimestamps.get(triggeringItem.name)
		val Duration = ( MyTimestamp - StartTime ) / 1000
		
          logInfo("Test.rules", "Duration of " + triggeringItem.name + " was " + Duration.toString + " (=" + MyTimestamp.toString + " - " + StartTime.toString + " ) ")
		  
		  
		  val ShutterName = triggeringItem.name.split('_').get(0)
		  val Direction = triggeringItem.name.split('_').get(1)
		  
		  val ShutterItem = gRollershutterItem.members.findFirst[ shutter | shutter.name == ShutterName ]
		  val ActualPosition = ShutterItem.state as Number
		  
		  if ( Direction == "UP" ) // 0 is upermost position
		  {
		     logInfo("Test.rules", ActualPosition.toString + " is actual position and direction UP" )
		     ShutterDuration = ShutterUpDurations.get(triggeringItem.name)
			 NewPosition = ( ActualPosition - ( Duration / ShutterDuration ) * 100 )
		  }
		  else //direction is "DW"
		  {
		     logInfo("Test.rules", ActualPosition.toString + " is actual position and direction  DW" )
		     ShutterDuration = ShutterDwDurations.get(triggeringItem.name)
			 NewPosition = ( ActualPosition + ( Duration  / ShutterDuration ) * 100 )
		  }
		  
		  logInfo("Test.rules", "New position = " + NewPosition.toString )
		  if (NewPosition < 0 )
	 	  {
		     postUpdate(ShutterName, 0 )
		  }
		  else
		  {
		     if (NewPosition > 100 )
			 {
			    postUpdate(ShutterName, 100 )
			 }
			 else
			 {
			    //postUpdate(ShutterName, NewPosition )
				ShutterItem.postUpdate(NewPosition )
			 }
		  }
		  
      }
   }
end

can you maybe tell me what the issue is with my calculation?!?

Uninitialized Items have state NULL.

What have you initialized that with? Hint; if you don’t know the previous position then you can’t work out a new position. You probably want to skip calculation if existing state is NULL.

When another rule detects limit and sets 0 or 100%, then this rule will have something to work with.

Wouldn’t you want to trigger on a command to your shutters? And then look at receivedCommand in the rule?

If you give your Rollershutter Items autoupdate=“false” then their state (position) will be unaffected by commands. Your rules will be supplying thatinstead, so that the Item behaves like a fully-featured real rollershutter.

Aaaah… Thx for the hint. Yes, I haven’t inialized the rollershutter items and assumed they will be 0 by default… Sometimes I just feel dump… facepalm

Regarding changed / received command I am not sure to be honest. What’s the advantage of command resp the disadvantage of “changed”. Item is a Switch in case this is important

It’s quite important to understand the distinction between commands and states in openHAB.
Commands are “do something” instructions.
States are “it’s like this” conditions.
So for example, we might command a Dimmer Item INCREASE and observe the state go from 50% to 60%.
Note in that example, that command and state are completely different.

Rollershutters are like that; the openHAB Item accepts commands UP,DOWN,STOP and has state 0-100 representing position.
You’re simulating the action with rules, but if you stick to the OH model then you can use standard widgets in UI etc.

So you’d want your simulator rules to listen out for commands and update the state.

The rollershutter Item can also accept number commands, for “go to position 40”. You’d be able to simulate that as well,having a rule that issues UP/DOWN and a timed STOP, but work on the “sensing” first.

The extra detail here is openHABs “autoupdate” feature, enabled on each Item by default. That also listens for commands, makes an educated guess at outcome, and pre-emptively updates the Item state. It’s to make a snappy UI display, but you won’t want it interfering here.

hi,
yes, I understand what is the difference between commands and states, but I do not understand why commands are better triggers in this case, because the state reflects more “what is” while command reflects “what should be”.

in my point of view i want to know what happened, and update the state. So my idea was to use the rollershutter only to display the percentages.

Anyhow, your point with “go to position 40” might be interessting for future, so i changed now to “command observation”.

I think with your help (on this and some other pages.) I’ve been now also able to finalize my proof of concept method and looks now as follows:

import java.util.Map

val Map<String, Number> ShutterStartTimestamps = newHashMap
val Map<String, Number> ShutterUpDurations = newHashMap
val Map<String, Number> ShutterDwDurations = newHashMap
	
// there is a way to initialize the Map statically but I always forget it so prefer a System started Rule
rule "initialize ShutterStartTimestamps"
when
    System started
then
    ShutterStartTimestamps.put("Shutter1_UP", 999999000)
    ShutterStartTimestamps.put("Shutter2_UP", 999999000)
    ShutterStartTimestamps.put("Shutter3_UP", 999999000)
	
    ShutterStartTimestamps.put("Shutter1_DW", 999999000)
    ShutterStartTimestamps.put("Shutter2_DW", 999999000)
    ShutterStartTimestamps.put("Shutter3_DW", 999999000)
	
	
    ShutterUpDurations.put("Shutter1", 25.0)
    ShutterUpDurations.put("Shutter2", 25.0)
    ShutterUpDurations.put("Shutter3", 25.0)
	
    ShutterDwDurations.put("Shutter1", 25.0)
    ShutterDwDurations.put("Shutter2", 25.0)
    ShutterDwDurations.put("Shutter3", 25.0)
	
	
	gRollershutterItem.members.forEach[ shutter |
			if (shutter === null)
			{
			   shutter.postUpdate(100)
			}
	]
end

rule "Shutter begins or stops to move"
   when
      Member of gtestItemsButtons received command
   then
   { 

      var long MyTimestamp = now.toInstant.toEpochMilli
      if (triggeringItem.state == ON) //start measurement
	  {
         logInfo("Test.rules", "Test run 2 start")
         ShutterStartTimestamps.put(triggeringItem.name, MyTimestamp)
      }
	  else  //end measurement
	  {
	    var Number NewPosition = 100 // just to have an initial value
		var Number ShutterDuration = 0
        var Number ActualPosition = 100
	    val StartTime = ShutterStartTimestamps.get(triggeringItem.name)
		val Duration = ( MyTimestamp - StartTime ) / 1000
         //DEBUG logInfo("Test.rules", "Duration of " + triggeringItem.name + " was " + Duration.toString + " (=" + MyTimestamp.toString + " - " + StartTime.toString + " ) ")

		  val ShutterName = triggeringItem.name.split('_').get(0)
		  val Direction = triggeringItem.name.split('_').get(1)
		  
		  val ShutterItem = gRollershutterItem.members.findFirst[ shutter | shutter.name == ShutterName ]
		  if (ShutterItem === null || ShutterItem.state == NULL) //item is unitialized
		  {
			ShutterItem.postUpdate(100)
		    logInfo("Test.rules", ShutterItem.name + " state was NULL, and is now forced to " + ActualPosition.toString )
		  }
		  ActualPosition = ShutterItem.state as Number
		  
		  if ( Direction == "UP" ) // 0 is upermost position
		  {
		     ShutterDuration = ShutterUpDurations.get(ShutterName)
			 if (ShutterDuration === null || ShutterDuration == NULL) 
			 {
			    ShutterDuration = 25.0
			    logInfo("Test.rules", "ShutterDuration was NULL, and is now forced to " + ShutterDuration.toString )
				
			 }
			 if (ActualPosition === null || ActualPosition == NULL) 
             {
                 NewPosition = ( 100  - ( Duration / ShutterDuration ) * 100 ) //if i do not know the real position i have to assume it started on the very bottom
             }
			 else
             {
		        logInfo("Test.rules", ActualPosition.toString + " is actual position, direction UP and ShutterDuration = " +  ShutterDuration.toString )
                NewPosition = ( ActualPosition - ( Duration / ShutterDuration ) * 100 )
		     }
          }
		  else //direction is "DW"
		  {
		     if ( Direction != "DW" )
			 {
			    logInfo("Test.rules", "WARNING!! Direction was expected to be UP or DW, so was handeled as DW although it was actually " + Direction.toString )
			 }
			 
		     ShutterDuration = ShutterDwDurations.get(ShutterName)
			 if (ShutterDuration === null || ShutterDuration == NULL) 
			 {
			    ShutterDuration = 25.0
			    logInfo("Test.rules", "ShutterDuration was NULL, and is now forced to " + ShutterDuration.toString )
				
			 }
			 if (ActualPosition === null || ActualPosition == NULL) 
             {
                 NewPosition = 100
             }
			 else
             {
		        //DEBUG logInfo("Test.rules", ActualPosition.toString + " is actual position, direction DW and ShutterDuration = " +  ShutterDuration.toString )
			    NewPosition = ( ActualPosition + ( Duration  / ShutterDuration ) * 100 )
			 }
		  }
		  
		  //DEBUG logInfo("Test.rules", "New position = " + NewPosition.toString )
		  
		  if (NewPosition <= 0.0 )
	 	  {
		     //postUpdate(ShutterName, 0 )
			 ShutterItem.postUpdate( 0.0 )
		  }
		  else
		  {
		     if (NewPosition >= 100.0 )
			 {
				ShutterItem.postUpdate(100.0 )
			 }
			 else
			 {
				ShutterItem.postUpdate(NewPosition )
			 }
		  }
      }
   }
end

A thousand times thanks for you and all the people pushing this community and making such a great job for this open source project. :ok_hand: :clap:

Hi @rossko57 ,
With this map method, the values are sometimes NULL during execution, I assume as only the rule file is changed without system restart.
Is there a possibility to call the “init” rule as soon as I see that a value is NULL

You can trigger a rule when an Item state changes to NULL

I usually write rules to deal with NULL states e.g. if you don’t (yet) know what XX is then you might not want to doing YY after all.

Is this only possible for items, or also for map variables? If so, how?

When
   ShutterStartTimestamps changes to NULL
Then
...

?

There is no way to trigger a rule from any kind of contents change of any variable, no.

Who cares if a variable is null, until you go to use it. Then you can test if it is null and decide what to do about that.
This is looping back around to -

and requiring you to have extra rules to detect “known” positions i.e. you can infer it must have got to one end or other.

It’s true that I do not care if it is null as long as I do not use it, but mistrust every variable blows up the code and if it is “a constant” it is annoying if I have to check it for every first use in the rule and think about an exit strategy.
The idea of the rule “initialize ShutterStartTimestamps” is to set default values, so they are not null anymore. If they are still null, I do not need a initialisation.

Therefore, I thought as a remedy I could at least check if one “constant” is null, and in that case reinitialise them. In the UI it is also possible to call a other rule, so I thought it is also possible via scripting

Maybe look more closely at what you do.

Pre-loading your timestamps Map with dummy “9999” values is avoidable, unnecessary.
At the time of use, you could instead test if an entry exists, and if not then act as though you found “9999” (or do whatever was really wanted to deal with first use).

The durations Map is indeed a more difficult proposal. Clearly you want some value at first use.
As I said previously, my gut feel for these would be to hold them in
Items as they are a kind of partner-property to each of your individual shutter Items. The usual Items persist/restore mechanisms could then be left to manage populating them.
But you certainly don’t have to do it that way.

You can call a “script” from a UI or file based rule. They’re not used very often, because you cannot pass stuff in or out. So far as I know, in neither case can the script share ‘globals’ (like a Map) with a calling script. As long as you want to do something like interact only with some Items, they are usable. Updating a bunch of Items with preconfigured constants, for example.

In your example, they’re all alike - but I guess you are planning ahead for them being individually tailored up/down each Item.
I think I’d cheat, once again testing to see if a value is present, and if not then assuming some default (25.0?) temporarily.

What problem are you trying to fix here, it goes wrong when you edit the rules file? How often is that going to happen in normal use? (This goes to choosing an easy bodge or a difficult technically-correct solution.)

Yes, all data is always lost if I save the rule file and during testing it is saved frequently. But, you are wright, once it is fine it will not be resaved frequently (unless there is the next rule I’d like to add). For now, I think, best approach is to define them during initialisation as well like it is done at e.g. Cannot assign a value in null context. and HashMap - what is wrong?

You can have many rules files. If you keep your shutters riles in one file and other stuff in a different file(s), then you you wouldn’t mess up shutters by editing other stuff.

Hi Anno_Nym! I am looking into the same challange as you, maybe you can help me a bit.

I have OH3, RPI4 and communicates via MQTT to a RFlink bridge.

My question: How can I get my Sunblinds down or up for example 20% of the total length or via a position which I fill in via a – for example -slider cell?

I would like to bring my Sunblinds down based on how much sun I would like to have in my living room. In the current set-up I have to watch how far the sunblind goes and if I would like to stop: I hit the stop button. Preferably I am able to use the slider cell, but don’t know how to link /configire this to the sunblinds commands UP/STOP/DOWN.

I tested with a script with a javascript SLEEP command (for example for 5 a 6 seconds), that works, but is not connected to a slider or Rollershutter cell. I am fully aware that my sunblind motor doesn’t give the state back, so own calculation of the position in % is precise enough.

Later on I would like to connect a wind meter and light meter, based on the moment of the day the sunblinds should go down for 20,30or 70% of the total lengts. First tings first, If iam able to put the % via a cell, that would be fine!

I saw you have made a script, since i am a new bee: How did you configure your systeem? Now I have a MQTT thing, linked into a Rollershutter item and published this item on the page in OpenhabPanel. What do I have to configure to get your script working? I have only one Sunblind… Really appriciate your help!

Just a word of caution;this is a technically ambitious project. You will learn a lot if you stick with it :smiley:

But you might first choose to get familiar with rules and timing with something a bit simpler, like an automated light. Find out how to use a rules based timer, instead of a simple delay, you’ll need that for blinds control.

1 Like