[SOLVED] Getting error when using timestamp for managing lights on and off with motion sensor

  • Platform information:
    • Hardware: Raspberry Pi 3 Model B+
    • OS: LinuxMint 19.1
    • openHAB version: 2.4.0
  • Issue of the topic: Getting error when using Timestamp method to manage lights with motion sensors
  • Please post configurations (if applicable):
    • Rules code related to the issue:
      /* Variable Declarations */
      var motionStamp

/* Rules */
rule “Dimmer when Motion”
when
Item Motion changed from OFF to ON
then
postUpdate(motionStamp, new DateTimeType())
Dimmer.sendCommand(ON)
logInfo(“Dimmer on motion”)
end

rule “Dimmer off after 30 minutes without Motion”
when
Item ntp_ntp_local_dateTime changed // Every minute
then
if(now.minusMinutes(30).isAfter((motionStamp as DateTimeType).zonedDateTime.toInstant.toEpochMilli)) {
Dimmer.sendCommand(OFF)
logInfo(“Dimmer off after 30 minutes without motion”)
}
end

  • If logs where generated please post these here using code fences:
    [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule ‘Dimmer off after 30 minutes without Motion’: cannot invoke method public java.time.ZonedDateTime org.eclipse.smarthome.core.library.types.DateTimeType.getZonedDateTime() on null

I do not know German but my guess is that you want the lights to turn off some time after no motion is detected. I use a timer with the v1 expire binding. It is set or reset every time motion is detected. The light towns off when the timer expires.

I hope to move my rules to Jython and avoid the need for the v1 binding.

2 Likes

Here is what I use for a basement (cellar) stairway light with sensors at the top & bottom.
Item:

Switch BasementTimer { expire="2m,command=OFF" }

Rules

rule "Basement Light On"
when
    Item basement_motion_1 changed from OFF to ON or Item basement_motion_2 chan
ged from OFF to ON
then
    basement_light.sendCommand(ON)
    // start or reset Timer
    BasementTimer.sendCommand(ON)
end

rule "Basement Light Off"
when
    Item BasementTimer changed from ON to OFF
then
    basement_light.sendCommand(OFF)
end

2 Likes

To get rid of the error-message, try:`

if(now.minusMinutes(30).isAfter((motionStamp.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli))

(motionStamp.state)

and welcome in the Community.

Cheers,
Peter

2 Likes

Thank you fibu-freak!
I changed it and now I am getting:
[ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule ‘Dimmer off after 30 minutes without Motion’: ‘state’ is not a member of ‘null’; line 20, column 38, length 28

Hmm ?! Maybe you have a look on this post. There are some examples.

I made a test with a similar rule. Here’s the snippet:

	   if(now.isBefore((Sunset_Time_Test.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)) { // https://community.openhab.org/t/solved-datetimetype-is-deprecated/37296/31
           logInfo("Datum testen", "Line 70 - now {} is before {} ", now, Sunset_Time_Test.state as DateTimeType)

and the result in the logger:

2019-10-13 17:48:50.446 [INFO ] [.smarthome.model.script.Datum testen] - Line 70 - now 2019-10-13T17:48:50.445+02:00 is before 2019-10-13T21:10:00.000+0200 

Corresponding item:

DateTime              Sunset_Time_Test                "Sonnenuntergang Test Start[%1$ta, %1$td.%1$tm.%1$ty,  %1$tH:%1$tM]"  <sunset>                  (gAstrotest)      {channel="astro:sun:stowing1:set#start"}

So how does your item looks like ?

Here is the whole rule were I test such stuff. Maybe this can help you too:

var boolean log = true
rule "Datum testen"
  when
    Item Dummy4 changed to ON
  then
       var day_1 = new DateTime(localLastMeasurement_00.state.toString).toString("yyMMddHHmm")
       var day_2 = new DateTime(localLastMeasurement_00.state.toString).plusDays(1).plusHours(6).toString("yyMMddHHmm")
       logInfo ("Datum testen", "day0 ist {} und day1 ist {} ", day_1,day_2)
       var day0 = new DateTime(localLastMeasurement_00.state.toString).toString("yyMMdd")
       var day1 = new DateTime(localLastMeasurement_00.state.toString).plusDays(1).toString("yyMMdd")
       var day2 = new DateTime(localLastMeasurement_00.state.toString).plusDays(2).toString("yyMMdd")
        logInfo ("Datum testen", "day0 ist {} day1 ist {} day2 ist {} day3 ist {} day4 ist {} day5 ist {}", day0, day1, day2)

       val day_act = new DateTime(now.toLocalDateTime.toString).toString("yyMMddHHmm")
       logInfo ("Datum testen", "aktuell now1 ist {} ", day_act)


       val morning_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(18)
       logInfo ("Datum testen", "morningstart ist {} ", morning_start)
       val morning_start1 = now.withTimeAtStartOfDay.plusDays(0).minusHours(0)
       logInfo ("Datum testen", "morningstart1 ist {} ", morning_start1)
       val morning_start2 = new DateTime(now.withTimeAtStartOfDay.plusDays(1).plusHours(7))
       logInfo ("Datum testen", "morningstart2 ist {} ", morning_start2)
       val morning_start3 = new DateTime(now.withTimeAtStartOfDay).toString("HHmm")
       logInfo ("Datum testen", "morningstart3 ist {} ", morning_start3)
        var Number morning_start4 = new DateTime(now.withTimeAtStartOfDay).toString("HHmm")
       logInfo ("Datum testen", "morningstart4 ist {} ", morning_start4)
        var Number morning_start5 = new DateTime(now.withTimeAtStartOfDay.plusHours(7)).toString("HHmm")
       logInfo("Datum testen", "morningstart5 ist {} ", morning_start5)
        var morning_start6 = now().toString("HHmm")
       logInfo("Datum testen", "morningstart6 ist {} ", morning_start6)
         var morning_start7 = (now.withTimeAtStartOfDay.plusHours(7)).getHourOfDay
       logInfo("Datum testen", "morningstart7 ist {} ", morning_start7)
         var morning_start8 = now.withTimeAtStartOfDay.getHourOfDay
       logInfo("Datum testen", "morningstart8 ist {} ", morning_start8)
   
       val morning_now = now.toLocalDateTime
       logInfo ("Datum testen", "morningnow ist {} ", morning_now)

       val day_now = now.toLocalDateTime.toString("yyMMddHHmm")
       logInfo ("Datum testen", "aktuell start ist {} ", day_now)

       val day_test = new DateTime(now).toString("yyMMddHHmm")
       logInfo ("Datum testen", "aktuell test ist {} ", day_test)

       val day_test1 = new DateTime(now)
       logInfo ("Datum testen", "aktuell test1 ist {} ", day_test1)


       val day_test_now = now
       logInfo ("Datum testen", "aktuell test_now ist {} ", day_test_now)
       
     if ((now.withTimeAtStartOfDay.plusHours(22)).toString("HHmm") <= now().toString("HHmm")  && (now.withTimeAtStartOfDay.plusHours(23)).toString("HHmm") >= now().toString("HHmm")) {
         logInfo("Datum testen","Na geht doch")
     }
     if (now.withTimeAtStartOfDay.plusHours(22).getHourOfDay >= 22  && now.withTimeAtStartOfDay.plusHours(23).getHourOfDay <= 23) {
         logInfo("Datum testen","Na geht doch oder auch nicht")
     }
     var Number min = 13
     var Number max = 15

     if (now.withTimeAtStartOfDay.plusHours(min).getHourOfDay >= min  && now.withTimeAtStartOfDay.plusHours(max).getHourOfDay <= max) {
         logInfo("Datum testen","und es geht doch: " + now().getHourOfDay )
     }
     if (now.withTimeAtStartOfDay.plusHours(min).getHourOfDay <= now().getHourOfDay  && now.withTimeAtStartOfDay.plusHours(max).getHourOfDay > now.getHourOfDay()) {
         logInfo("Datum testen","wie wär's damit")
     }
	   
	   if(now.isBefore((Sunset_Time_Test.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)) { // https://community.openhab.org/t/solved-datetimetype-is-deprecated/37296/31
          logInfo("Datum testen", "Line 70 - now {} is before {} ", now, Sunset_Time_Test.state as DateTimeType)
          return;
      } 
end

This is somewhat an XY Problem. Your over all approach is awkward and suboptimal. A more appropriate approach would be to use Timers. See Design Pattern: Motion Sensor Timer which has an example using both expire and another with Timers.

For lots of examples showing time comparisons, see Design Pattern: Time Of Day.

The root of your problem is “Dimmer off after 30 minutes without Motion” runs every minute. When you just start up OH, motionStamp is NULL because there has been no moton. You can’t cast NULL to DateTimeType.

The reason why I want timestamp is to be able to see the last time a motion was detected and similarly use the above to set the delay.
Ok, so for the DateTimeType to work properly I need to have the Astro binding installed? or what do I use in the Variable declaration?
var motionStamp = ???

That is fine. But you are not forced to use the same Item to manage a delay.
Using a timer instead will not cost you anything.

Not at all. When your Item has just been reset, at system start or after your edits, it will have state NULL, until a motion event happens.
All your rule needs to do is look to see if state is NULL before doing anything else with it.

What the timers are doing is just starting a timer to avoid the usually needless complication of working with a timestamp.

Ok, thanks Bruce_Osborne, rossko57, rlkoshak, fibu-freak !
I am changing to timer items, looks easier.

1 Like

So, I need to install the Add-on binding called Expire Binding for your example rule to work properly?

That is correct. When I have time I will try to make a jython rule that does not need the binding.