Parse parameters in URL to a string

I am trying to send OH the value of my alarm clock time (from Android tasker) so that my cron-triggered rules can happen relevant to the alarm time. For example if my alarm is set for 6am, I implemented that my thermostat turns on 10 mins beforehand and the lights turn on at 6.

I thought perhaps I can send the time value as part of the URL command processing capabilities of OH (i.e., http://[localhost]:[port]/CMD?[item]=[command] ). Then parse the value into a String in the rules.file

Is this approach feasible? Is there perhaps an easier way?

If you have a String Item then that Item will simply be set to [command], no parsing into a String necessary.

However, if I truly do understand what you are trying to do, what you want to do is then create a Timer which will go off at the described times. In that case yes, you can convert the String passed into a joda DateTime type and then do .plus/minusMinutes to set the time for the Timer.

Assuming you send your alarm in ISO format (e.g. 2016-04-09T20:56:00) all you need to do is

import org.joda.time.*

var Timer thermostatTimer = null
var Timer lightTimer = null
...

rule "Set alarm timers"
when
    Item AlarmTime received command
then
    val alarmDT = new DateTime(AlarmTime.state.toString)

    if(thermostatTimer != null) thermostatTimer.cancel
    thermostatTimer = createTimer(alarmDT.minusMinutes(10), [ |
        // turn on the thermostat
        thermostatTimer = null
    ]

    if(lightsTimer != null) lightTimer.cancel
    lightsTimer = createTimer(alarmDT.minusMinutes(6), [|
        // turn on the lights
        lightsTimer = null
    ]
end

NOTE: Just typed in the above, there could be syntax errors.

Thanks again for the suggestions.

So in the code I was writing I was leveraging the cron expressions to control start/stop times. And because I am new to this java/xbasic syntax I am trying to keep it simple for now haha.

Here is what I got so far. Initialized hour/min time to my preference, with optional calls to String item “Str_Alarm_Time” that parses hour/time from alarm setting in Android clock.

var String sHourDOn = "05"
var String sHourDOff = "05"
var String sMinDOn = "50"
var String sMinDOff = "30"

var String sCronRuleOn
var String sCronRuleOff
var String sRaw
var String sHourOn
var String sHourOff 
var String sMinOn
var String sMinOff



rule Startup
when
  System started
then
  sHourOff = sHourDOff //default start  hour
  sMinOff = sMinDOff //default start time
  sHourOn = sHourDOn //default end hour
  sMinOn = sMinDOn //default end time
  sCronRuleOn  = "0 " + sMinOn  + " " + sHourOn  + " ? * MON-FRI *"
  sCronRuleOff = "0 " + sMinOff + " " + sHourOff + " ? * MON-FRI *"
end

rule "Read Android Alarm Clock time"
when
  Item Str_Alarm_Time changed
then
  logInfo("Log.PrxClock", "Received alarm time from Android Clock.")
  sRaw = Str_Alarm_Time.state
  sHourOff = sRaw.substring(0,1)
  sMinOff = sRaw.substring(2,3)
  sHourOn = String.valueOf(new Integer(sHourDOff) - 20)
  sMinOn = sMinDOff
  //TODO handle properly the  date/time arithmetic
  sCronRuleOff = "0 " + sMinOff + " " + sHourOff + " ? * * *"
  sCronRuleOn  = "0 " + sMinOn  + " " + sHourOn  + " ? * * *"
end  

rule "TStat Heat @mornings"
when
  //Time cron "0 30 5 ? * MON-FRI *"
  Time cron sCronRuleOn
then
  logInfo("Log.TS1.Heat","Turning TStat Heat On. Warm-wake up :).")
  HVAC_HeatSetPoint.sendCommand("85")
  HVAC_HeatSetPoint.sendCommand("85") //send again in cases of error
  HVAC_HeatSetPoint.sendCommand("85") //send again in cases of error
  HVAC_Mode.sendCommand("1")
  HVAC_Mode.sendCommand("1") //send again in cases of error
  HVAC_Mode.sendCommand("1") //send again in cases of error
end

rule "TStat Off @mornings + 20min"
when
  //Time cron "0 50 5 ? * MON-FRI *"
  Time cron sCronRuleOff
then
  logInfo("Log.TS0","Turning TStat Off. Go take a shower :).")
  HVAC_HeatSetPoint.sendCommand("71")
  HVAC_HeatSetPoint.sendCommand("71") //send again in cases of error
  HVAC_HeatSetPoint.sendCommand("71") //send again in cases of error
  HVAC_Mode.sendCommand("0")
  HVAC_Mode.sendCommand("0") //send again in cases of error
  HVAC_Mode.sendCommand("0") //send again in cases of error
  logInfo("Log.PrxClock", Reset default sCronRule times...")

  sHourOff = sHourDOff //default start  hour
  sMinOff = sMinDOff //default start time
  sHourOn = sHourDOn //default end hour
  sMinOn = sMinDOn //default end time
  sCronRuleOn  = "0 " + sMinOn  + " " + sHourOn  + " ? * MON-FRI *"
  sCronRuleOff = "0 " + sMinOff + " " + sHourOff + " ? * MON-FRI *"
end

At first pass, it seems to be complaining about the substring() function…

Updated with latest changes:

  • it was really the [item].state , this wasn’t really a string. [item].state.toString did the trick.

  • substring was fine, but second argument was exclusive, so that was fixed.

    import org.openhab.core.library.types.*
    //import org.openhab.model.script.actions.*
    //import org.openhab.core.library.types.DecimalType
    //import java.lang.String

    val String sHourDOn = "05"
    val String sHourDOff = "05"
    val String sMinDOff = "50"
    val String sMinDOn = “30”

    var String sCronRuleOn
    var String sCronRuleOff
    var String sRaw
    var String sHourOn
    var String sHourOff
    var String sMinOn
    var String sMinOff

    rule Startup
    when
    System started
    then
    logInfo(“Log.PrxClock”, “Initializing Alarm Clock to preferred values…”)
    sHourOff = sHourDOff //default start hour
    sMinOff = sMinDOff //default start time
    sHourOn = sHourDOn //default end hour
    sMinOn = sMinDOn //default end time
    sCronRuleOn = "0 " + sMinOn + " " + sHourOn + " ? * MON-FRI *"
    sCronRuleOff = "0 " + sMinOff + " " + sHourOff + " ? * MON-FRI *"
    end

    rule "Read Android Alarm Clock time"
    when
    //Item Str_Alarm_Time received command
    //Item Str_Alarm_Time received update
    Item Str_Alarm_Time changed
    then
    logInfo(“Log.PrxClock”, “Received alarm time from Android Clock.”)
    sRaw = Str_Alarm_Time.state.toString
    sHourOff = sRaw.substring(0,2)
    sMinOff = sRaw.substring(2,4)

    var int intMinOn = new Integer(sMinOff) - 20
    sMinOn = ""+intMinOn
    sHourOn = sHourOff
    //TODO handle properly the date/time arithmetic
    //TODO make adaptive to time zones (UTC)
    //TODO rewrite last line to work on weekdays (MON-FRI)
    sCronRuleOff = "0 " + sMinOff + " " + sHourOff + " ? * * *"
    sCronRuleOn  = "0 " + sMinOn  + " " + sHourOn  + " ? * * *"
    logInfo("Log.PrxClock", sCronRuleOff)
    logInfo("Log.PrxClock", sCronRuleOn)
    

    end

    rule "TStat Heat @mornings"
    when
    //Time cron "0 30 5 ? * MON-FRI *"
    Time cron sCronRuleOn
    then
    logInfo(“Log.TS1.Heat”,“Turning TStat Heat On. Warm-wake up :).”)
    HVAC_HeatSetPoint.sendCommand(“85”)
    HVAC_HeatSetPoint.sendCommand(“85”) //send again in cases of error
    HVAC_HeatSetPoint.sendCommand(“85”) //send again in cases of error
    HVAC_Mode.sendCommand(“1”)
    HVAC_Mode.sendCommand(“1”) //send again in cases of error
    HVAC_Mode.sendCommand(“1”) //send again in cases of error
    end

    rule "TStat Off @mornings + 20min"
    when
    //Time cron "0 50 5 ? * MON-FRI *“
    Time cron sCronRuleOff
    then
    logInfo(“Log.TS0”,“Turning TStat Off. Go take a shower :).”)
    HVAC_HeatSetPoint.sendCommand(“71”)
    HVAC_HeatSetPoint.sendCommand(“71”) //send again in cases of error
    HVAC_HeatSetPoint.sendCommand(“71”) //send again in cases of error
    HVAC_Mode.sendCommand(“0”)
    HVAC_Mode.sendCommand(“0”) //send again in cases of error
    HVAC_Mode.sendCommand(“0”) //send again in cases of error
    logInfo(“Log.PrxClock”, Reseting default sCronRule times…”)

    sHourOff = sHourDOff //default start  hour
    sMinOff = sMinDOff //default start time
    sHourOn = sHourDOn //default end hour
    sMinOn = sMinDOn //default end time
    sCronRuleOn  = "0 " + sMinOn  + " " + sHourOn  + " ? * MON-FRI *"
    sCronRuleOff = "0 " + sMinOff + " " + sHourOff + " ? * MON-FRI *"
    

    end

EDIT: So cron can’t take a variable expression? That’s unfortunate…
EDIT2: So would the timer approach affect performance? For example, if I set an alarm on my phone on Friday for Monday morning, will the timer take CPU resources just to wait ~3 days till the actions can happen?

Had I know that was what you were planning I could have warned you. You cannot reference a var or a val in the when clause of a Rule.

I don’t know and it would take looking at the code to find out. I can’t say for sure whether it takes up zero CPU resources, it depends on how it implements the Timer scheduler. But I can say that what ever it does it is not a busy wait loop chewing through your CPU cycles doing nothing. I would not be surprised if it uses the same scheduling mechanism that the cron triggers use.

Similarly it will use up some RAM to hold on to the Timer until it goes off, but even there we are talking about hundreds of bytes, not kilobytes or megabytes…

So, if it does take CPU resources the amount used would be negligible if measurable at all. Similarly the extra RAM used would also be very small. In my opinion if setting a few dozen Timers does make a difference on your deployment you are running OH on under powered hardware and should consider hosting it on something else as setting Timers is not going to be your only problem. Many on this forum have been very successful running it on a Raspberry Pi 2 (first generation was under powered).

In general when running OH on appropriately powered hardware performance of Rules is rarely a problem. In all the time I’ve been using it and helping out on this forum I know of exactly one case where a user was not getting the kind of performance he needed and that was a case where he wanted to do a bunch of logic and cause a bunch of dimmers to change states within 250 msec which is an extreme case and something for which OH was not designed.

However, there is one area where you might run into trouble. Should OH go down for any reason all of your Timers will also go away. So you should make sure to set up persistence on your Alarm string with restoreOnStartup and a System started rule to recreate the Timers.

Understood, thanks for the detailed response.

So I originally wrote this thread because I thought the parsing would be the least trivial thing to do, and I was glancing at the many ways it could be done (HTTP GETs, POSTs, or the CMD servlet, or a mixture of these…).

I also asked for the easiest approach to do this because I thought variable cron expressions were doable, and easy enough. If it is not doable I would suppose the usual workaround of evaluating expressions at run-time is not feasible (or at least not recommended) in this language.

As to your point on the issues with timer, I can leverage parts of the code that handle startup/ending conditions. I guess I would prefer a default cron rule that is in a switch statement, so that it can serve as a backup if the timers ever happen to vanish.

I’m not sure what you mean by this.

Rules are triggered by events (that is why it makes no sense to have and “and” in the when clause of a Rule. One type of event a Rule can trigger on is Time using a cron syntax to define the time. You cannot define a cron string using a val or a var (i.e. change a cron trigger at runtime).

But that has nothing to do with being able to evaluate expressions at runtime. Just about anything you can think of can be evaluated at runtime in a Rule. In fact this is very common and an approved practice. You just can’t do it in a Rule trigger. Though you can have a Rule that sends a command to an Item based on some runtime determined expression which triggers another Rule.

I don’t think I understand this statement either. Are you saying you want a cron triggered rule that runs periodically (e.g. once a minute) with a switch statement to see what to do at that given time?

You can certainly do that but if you were concerned about wasted cpu with Timers …

Also, the only time a Timer should disappear is if your OH restarts so as long as you restore them in a System started triggered rule they will be there. So writing this “backup” would be solving a problem you don’t even have, not to mention being duplicative and pretty complex.

I think it makes sense to reply to your last paragraph, first. I think your point was to handle OH crashing using the persistence restoreOnStartup, so that when OH is restarted, the last commanded alarm time is restored. What I am trying to handle is when there is no commanded alarm time in the first place, I still want it to do stuff.

I can expand on this (as this was part of your question in the second textbox) I originally planned to have a default cron trigger to perform all the actions of the morning routine in the event I forget to set an alarm. So I wrote the code a such.

I couldn’t think of way to incorporate this plan with the timer approach, so I thought of making a simple if/else statement. If an alarm time passed to OH, then create timers to perform all actions relevant to this time. Else, use cron to setup the times to perform these actions relevant to a default time.

Yes it is a little cumbersome. But I guess this can be improved if I just stick with the timer approach and leverage it to perform actions relevant to a default time.

I was thinking along that direction before, but I guess I am not sure about the limitations of the syntax for the when clause and the items-file. Can I initialize the parsing string to some value (say “DEFAULT”). And does the clause “when Item changed” capture the event when the item is first intiialized? If so, the timer approach should be able to do it all.

Generally speaking, I agree it could be argued that this is more work than needed. Maybe I should just build a habit of setting an alarm and forget the whole cron thing haha.

And regarding the first quoted textbox: While I am not an expert on the subject behind this, in other languages, an expression can be evaluated at run-time such that the expression is not stored in some variable. Therefore in my case, the cron call would just see a non-variable string input and in theory should accept it and process it accordingly. I originally picked this up learning Matlab, but I sounded like a function applicable to many languages (including java if xbasic is built off that).

As you put it, I guess here my problem was I wasn’t aware that expression evaluation doesn’t work as a Rule trigger.

You original posting made it sound like you wanted a backup just in case the Timers failed. But what it really sounds like you want is a default behavior for when the alarm is not set. That changes my analysis significantly.

This statement is not as straight forward as you might thing because your Alarm Item, once set, will retain its value from now until OH stops. If you use restoreOnStartup your Alarm Item will have a value always. And I don’t think you can just send a command or post an update that will set an Item back to an Undefined state. So in short, you need some other flag or way to tell when the alarm has been set vise it not having been set for today. See below for how I would do it.

This isn’t too far off of what I’m about to suggest actually. It is just that I will take advantage of the event based nature of the Rules Domain Specific Language (DSL), and Timers to do it.

Yes but really only in a System started triggered Rule.

The answer is no if you are using restoreOnStartup but yes if you are initializing the Item from a System started triggered or any other rule.

Now that you have stated your full use case, no it isn’t really more work than is necessary.

Then what’s the good of having it automated? :slight_smile:

Those other languages are not event driven programming languages. The Rules DSL, like the programming language for other automation systems like Tasker, Asterisk, and FreeSwitch, is centered around event processing (verses Java which is centered around Objects or MatLab which is centered around making math easy to program). In this respect it bears a close resemblance to Actor based languages like Erlang.

In the Rules DSL, you set up a relationship between events (stuff in the when clause of a rule) and logic (stuff in the then clause of a rule). This relationship is parsed and established at .rules file load time and there is no way at this time to modify that mapping at runtime. Therefore the when clause of a rule must be statically defined at compile time and cannot be modified at runtime.

Furthermore, the when clause of a Rule is based on events. When X happens or Y happens trigger this rule. It isn’t state based. It makes no sense to say When X equals Foo and Y doesn’t equal bar. In order to evaluate a cron trigger at runtime you would have to be able to dynamically modify what events a rule can be triggered by. In the OH 1.x Rules DSL there is no mechanism to do that so you cannot dynamically create a cron trigger string.

I believe there might be some changes being made in a new Rules DSL being developed for OH 2, and I don’t know if the JSR233 rules (jython or JavaScript) have this same limitation.

But there is no way to get that String into the when clause of a rule at runtime. That is the crux of the problem.

Now for how I would set this up:

First of all I would set up persistence and use restoreOnStartup. Doing this makes life sooooo much easier in the long run. If you are worried about space use MapDB which only saves the most recent values. When you get to a point where you want to make some charts or look at historical values in your rules, set up rrd4j and only save those Items you care about to it but keep everything else in MapDB. You can set this up using Groups.

Items:

// Alarm gets populated with a REST call and an ISO formatted String YYYY-MM-DDTHH:MM, OH will automatically convert it to a DateTime
DateTime Alarm "Alarm [%1t$tm/%1$td/%1$tY %1$tT]"] <clock> (gRestoreOnStartup)

Rules:

val Timer thermometerTimer = null
val Timer lightsTimer = null

rule "Setup morning routine"
when
    Time cron "0 0 0 * * * ?" // midnight, set to the latest you would ever set the alarm but before the default alarm time
then

    var DateTime thermometerTime = null
    var DateTime lightTime = null
    // If it has been 24 hours since Alarm has been updated, use defaults
    if(Alarm.lastUpdate.isBefore(now.minusHours(24)) {
        thermometerTime = now.plusMinutes(<default offset from midnight>)
        lightsTime = now.plusMinutes(<default offset from midnight>)
    }
    else {
        thermometerTime = (Alarm.state as DateTimeType).minusMinutes(<default offset from alarm>)
        lightsTime = (Alarm.state as DateTimeType).minusMinutes(<default offset from alarm>)    
    }

    if(thermometerTimer != null) thermometerTimer.cancel
    thermometerTimer = createTimer(thermometerTime, [|
        // thermometer code
        thermometerTimer = null
    ])
    if(lightTimer != null) lightsTimer.cancel
    lightTimer = createTimer(lightsTime, [|
        // lights code
        lightsTimer = null
    ])
end

Theory of operation:
You can set the Alarm Item at any time using what ever means necessary. If you use something that DateTimeType can parse such as the standard ISO format it can convert a String to a DateTimeType for you.

When you set its new value it will save that to the database with a timestamp on when it was updated.

We have a rule that runs at midnight. Change this time to after you would ever set a new alarm but before the default alarm values. Also this clearly breaks down in the case where you set an alarm after the time has already passed.

This rule checks to see if Alarm has been update in the past 24 hours. If not the default times are used. If it has we calculate offsets from the Alarm time for the Timers to trigger.

NOTES:

  • DateTimeType and Timers require the date as well as time so what ever sets Alarm needs to pass the date of the alarm in addition to the time.
  • I just typed in the above. There are certainly going to be typos and/or other errors. This is particularly true when messing around with DateTime stuff.
  • There are a few edge cases not covered in the above. For example, if OH happens to restart after midnight but before the Timers go off you will lose your Timers. You can handle this but writing a System started rule to check if it is between midnight and your Alarm and recreate the Timers if it is. Exercise left to the student.

The above should at a minimum point you in a good direction.

Sorry for the late reply.

I ended having a few more issues with receiving the date/time message from Tasker. I realized I didn’t think about Tasker needing me to decide when I want to send my alarm time to OH. Since my OH server can only be accessed on LAN, this also means Wifi has to be on when my smarthphone sends the message. I don’t think I want to have my Wifi to turn-on automatically, send the message, then turn-off. I am very particular about adding on tasks and services to my phone since I really like its battery life, so the only automation task I am fine with is having it send messages at 3am and at every 6 hours periods This also means I will keep wifi on manually, but I am OK with that. (Until I decide to add vpn functionality that is.)

The next problem was the Tasker message is not really a standard UTC format. I am using the AutoAlarm plugin for Tasker. The plugin extracts the alarm time into variables %month , %day, %year, %hour, %min. Prior to using Tasker, the simple way I was parsing a generic message was by concatenating the datetime variables (%month%day%year%hour%min), then reading the first two values into a variable, then the next two, then the next four, next two, and the last two. However, the main problem was these variables don’t have a fixed number of digits (except for the year). For example, an alarm for 6:04 am would have %hour= 6, and %min =4. I attempted the workaround of making a DateTimeFormatter/DateTimeFormat class to look for this specific format, but ultimately this was unsuccessful.

The next approach was to pass the full message ( %month.%day.%year.%hour.%min ) as a string and parse it to different variables using the String.split() method. That was also bad, after recently learning of another limitation: OH has no primitive String array support. I even attempted using the ArrayList/List/Hash features that may be available, but again I was unsuccessful.

After wasting time and feeling frustrated about being constrained with the intricacies of an Xtend/Java language and the limited native OH toolsets available, I ran into a workaround that can solve both my parsing the received message problem, and the processing of it into the DateTime object. I don’t think it is a very good workaround since alot of code is repeated, but hey it works lol, and I am glad no more time is wasted on this.

Also, I wanted to update that I am no longer interested in having a default alarm time set. I realized that this would not be helpful for days that I have work-off, or for days I am away from home traveling. Perhaps I will have to reconsider extending the rules-file to disable timers for specific day intervals. Nevertheless here is my current fully-working solution.

import java.lang.Integer
import org.openhab.model.script.actions.*
import org.joda.time.*

var Timer thermostatTimer = null
var Timer lightTimerBR = null
var Timer lightTimerLR = null
var Timer MornTimerOff = null
 
var int iMonthOff
var int iDayOff
var int iYearOff
var int iHourOff
var int iMinOff

var DateTime dtAlarmBias



// Main

rule "Set M-F Morning Routine"
when
  Item Str_Alarm_Time changed
    
then

  var String sRaw = Str_Alarm_Time.state.toString

  //Hyphen (') stays on cmd1 and cmd5. Python script handles that and trims it.
  var String cmd1 = "python@@/home/pi/Documents/Code/OpenHAB/configurations/scripts/DateRaw_1Val.py@@'" + sRaw + "'@@1"
  var String cmd2 = "python@@/home/pi/Documents/Code/OpenHAB/configurations/scripts/DateRaw_1Val.py@@'" + sRaw + "'@@2"
  var String cmd3 = "python@@/home/pi/Documents/Code/OpenHAB/configurations/scripts/DateRaw_1Val.py@@'" + sRaw + "'@@3"
  var String cmd4 = "python@@/home/pi/Documents/Code/OpenHAB/configurations/scripts/DateRaw_1Val.py@@'" + sRaw + "'@@4"
  var String cmd5 = "python@@/home/pi/Documents/Code/OpenHAB/configurations/scripts/DateRaw_1Val.py@@'" + sRaw + "'@@5"

  logInfo("AlarmTime.Received","HTTP message received. sRaw="+sRaw)

  iMonthOff = new Integer(executeCommandLine(cmd1, 2000)) 
  iDayOff = new Integer(executeCommandLine(cmd2, 2000))
  iYearOff = new Integer(executeCommandLine(cmd3, 2000))
  iHourOff = new Integer(executeCommandLine(cmd4, 2000))
  iMinOff = new Integer(executeCommandLine(cmd5, 2000))

  var DateTime dtAlarm = new DateTime(iYearOff, iMonthOff, iDayOff, iHourOff, iMinOff)
  logInfo("AlarmTime.Received","New wake-up alarm: "+dtAlarm.toString() )

  while( dtAlarm.isBeforeNow() )
  {
    dtAlarm = dtAlarm.plusDays(1)
    logInfo("AlarmTime.InPast","Warning: This time is in the past, setting new alarm time: "+dtAlarm.toString() )
  }
  
  

  if(thermostatTimer != null) { thermostatTimer.cancel }
  dtAlarmBias = dtAlarm.minusMinutes(bias1)
  thermostatTimer = createTimer(dtAlarmBias)
  [|
    // turn on the thermostat
    logInfo("AlarmTime.TS1.Heat","Turning TStat Heat On. Warm-wake up :).")
    HVAC_HeatSetPoint.sendCommand("85")
    HVAC_HeatSetPoint.sendCommand("85") //send again in cases of error
    HVAC_HeatSetPoint.sendCommand("85") //send again in cases of error
    HVAC_HeatSetPoint.sendCommand("85") //send again in cases of error

    HVAC_Mode.sendCommand("1")
    HVAC_Mode.sendCommand("1") //send again in cases of error
    HVAC_Mode.sendCommand("1") //send again in cases of error

    // reset timer var
    thermostatTimer = null
  ]


  if(lightTimerBR != null) { lightTimerBR.cancel }
  lightTimerBR = createTimer(dtAlarm)
  [|
    // turn on the BR lights
    logInfo("AlarmTime.TS0BR1","Setting TStat Off + BR Lights On... Now go take a shower :).")
    BR.sendCommand("ON")
    BR.sendCommand("ON") //in case of low LOS
    BR.sendCommand("ON") //in case of low LOS
          
    // turn off TStat (reset set points first)
        
    HVAC_HeatSetPoint.sendCommand("71")
    HVAC_HeatSetPoint.sendCommand("71") //send again in cases of error
    HVAC_HeatSetPoint.sendCommand("71") //send again in cases of error

    TStatAll.sendCommand("OFF") 

    // reset timer var
    lightTimerBR = null
  ]



  if(lightTimerLR != null) { lightTimerLR.cancel }
  dtAlarmBias = dtAlarm.plusMinutes(bias2)
  lightTimerLR = createTimer(dtAlarmBias)
  [|
    // turn on the LR lights
    logInfo("AlarmTime.LR1","Setting LR Lights On... You are doing well :).")
    LR1.sendCommand("ON")
    LR1.sendCommand("ON") //in case of low LOS
    LR1.sendCommand("ON") //in case of low LOS
          
    // reset timer var
    lightTimerLR = null
  ]


  if(MornTimerOff != null) { MornTimerOff.cancel }
  dtAlarmBias = dtAlarm.plusMinutes(bias3)
  MornTimerOff = createTimer(dtAlarmBias)
  [|
    // turn all Things off
    logInfo("AlarmTime.AllOff", "Turning everything off...")

    MasterOff.sendCommand("OFF")
          
    // reset timer var
    MornTimerOff = null

    logInfo("AlarmTime.End", "Morning routine complete. Now go to work!")
  ]

end

So the workarounds included calling a python script to essentially perform the string-spltting, one at a time. Then populating each output to a set of integers. The DateTime object can also be created with an integer set of date/time parameters as its arguments, so I went in that direction. If someone has more experience with using the executeCommandlLine(), it seems the escape characters are being added to the month and min values. Currently I let python trim these out, but I would say proper usage is the better solution. I will say however I did borrow this line from another page where the person noted the line worked flawlessly with no escape character issues.

As for other details, I have made some rules that essentially act as master-off switches. These are called via the TStatAll and the MasterOff items. The actions in TStatAll are a subset of MasterOff (since this turn all Things off), but it also turns all lights off, and I didn’t mind the redundancy check.

Tasker was also giving me a problem that the message received was an alarm time that was set for 1 day before the current time. To solve this issue (and to provide some error handling when a message date/time is in the past), I push the the messaged date to the present + 1 day so that it can actually create the timers.

This rules-file, plus all supporting files written to meet my objectives, pretty much cover the fundamental stuff I wanted out of an open-source HA implementation for my 1br apartment. So now its matter of exploring what else people are using OH for, considering investing in more HA devices, and thinking of new ideas for automating stuff at home, which will make me that much more lazy lol, but at least more satisfied with my home, and more energy efficient for that matter.

Indeed it doesn’t but you can still do:

val splitStr = MyItem.state.toString.split

val first = splitStr.get(0)
val fourth = splitStr.get(3)
splitStr.forEach[s | //do something ]

I do this sort of string splitting routinely. The only real limitation is you have to use “get” instead of “”. I’m not sure what you tried or why it didn’t work.