NickWaterton / Roomba980-Python

Python program and library to control iRobot Roomba 980 Vacuum Cleaner
MIT License
384 stars 108 forks source link

Gettimestamp is not working #51

Open fpellicciotti opened 5 years ago

fpellicciotti commented 5 years ago

Hi,

I am not sure if this is an issue. But I re-installed everything and everything seems to work. except, during the logs and sitemap, I get: Rule 'Roomba Last Update Timestamp': The name 'getTimestamp' cannot be resolved to an item or type; line 48, column 5, length 12

NickWaterton commented 5 years ago

getTimestamp is my own OpenHab lambda for returning a DateTime or string timestamp. You can replace it with anything that returns a date/time (eg new DateTimeType() or now()).

My actual lambda is this

val Functions$Function2<GenericItem, String, String> getTimestamp = [  //function (lambda) to get a timestamp. Returns formatted string and optionally updates an item
    item,
    date_format |

    var date_time_format = date_format
    if(date_format == "" || date_format === null) date_time_format = "%1$ta %1$tT" //default format Day Hour:Minute:Seconds
    var String Timestamp = String::format( date_time_format, new Date() )
    if(item != NULL && item !== null) {
        var t = new DateTimeType()
        if(item instanceof DateTimeItem) {
            item.postUpdate(t) ////postUpdate(item, t)
            logInfo("Last Update", item.name + " DateTimeItem updated at: " + Timestamp )
            }
        else if(item instanceof StringItem) {
            item.postUpdate(Timestamp) //postUpdate(item, Timestamp)
            logInfo("Last Update", item.name + " StringItem updated at: " + Timestamp )
            }
        else
            logWarn("Last Update", item.name + " is not DateTime or String - not updating")
    }
    Timestamp
    ]
fpellicciotti commented 5 years ago

I saw that, however, where would I put this function?

Sent from my iPhone. On Jan 11, 2019, 1:01 PM -0500, Nick Waterton notifications@github.com, wrote:

getTimestamp is my own OpenHab lambda for returning a DateTime or string timestamp. You can replace it with anything that returns a date/time (eg new DateTimeType() or now()). My actual lambda is this val Functions$Function2<GenericItem, String, String> getTimestamp = [ //function (lambda) to get a timestamp. Returns formatted string and optionally updates an item item, date_format |

var date_time_format = date_format if(date_format == "" || date_format === null) date_time_format = "%1$ta %1$tT" //default format Day Hour:Minute:Seconds var String Timestamp = String::format( date_time_format, new Date() ) if(item != NULL && item !== null) { var t = new DateTimeType() if(item instanceof DateTimeItem) { item.postUpdate(t) ////postUpdate(item, t) logInfo("Last Update", item.name + " DateTimeItem updated at: " + Timestamp ) } else if(item instanceof StringItem) { item.postUpdate(Timestamp) //postUpdate(item, Timestamp) logInfo("Last Update", item.name + " StringItem updated at: " + Timestamp ) } else logWarn("Last Update", item.name + " is not DateTime or String - not updating") } Timestamp ] — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

NickWaterton commented 5 years ago

It's an Openhab Rule, it goes in the same .rules file as the other rules goes. With the latest versions of OH, you have to include this at the top of the rules files for lambdas to work

import org.eclipse.xtext.xbase.lib.Functions    //apparently needed for OH2 lambda's
import org.eclipse.xtext.xbase.lib.Procedures   //apparently needed for OH2 lambda's
fpellicciotti commented 5 years ago

I've copied the two lines on top of Roomba.rules and I copied the lambda in rooma.rules as well, now I get this in the logs:

Configuration model 'roomnba.rules' has errors, therefore ignoring it: [155,1]: missing EOF at 'val'

NickWaterton commented 5 years ago

Can you post your complete Roomba.rules file? you have some errors in it somewhere. What version of OpenHab are you running?

fpellicciotti commented 5 years ago

I am using OH 2.4.0

Here is my complete Roomba.rules:

/ Roomba Rules / import org.eclipse.xtext.xbase.lib.Functions //apparently needed for OH2 lambda's import org.eclipse.xtext.xbase.lib.Procedures //apparently needed for OH2 lambda's

rule "Roomba start and stop" when Item roomba_control received command then logInfo("Roomba", "Roomba ON/OFF received command: " + receivedCommand) if (receivedCommand == ON) sendCommand(roomba_command, "start") if (receivedCommand == OFF) { sendCommand(roomba_command, "stop") Thread::sleep(1000) sendCommand(roomba_command, "dock") } end

rule "Roomba Auto Boost Control" when Item roomba_carpetBoost changed then logInfo("Roomba", "Roomba Boost changed to: Auto " + roomba_carpetBoost.state + " Manual: " + roomba_vacHigh.state) if (roomba_carpetBoost.state == ON && roomba_vacHigh.state == ON) sendCommand(roomba_vacHigh, OFF) end

rule "Roomba Manual Boost Control" when Item roomba_vacHigh changed then logInfo("Roomba", "Roomba Boost changed to: Auto " + roomba_carpetBoost.state + " Manual: " + roomba_vacHigh.state) if (roomba_carpetBoost.state == ON && roomba_vacHigh.state == ON) sendCommand(roomba_carpetBoost, OFF) end

rule "Roomba Auto Passes Control" when Item roomba_noAutoPasses changed or Item roomba_twoPass changed then logInfo("Roomba", "Roomba Passes changed to: Auto " + roomba_noAutoPasses.state + " Manual: " + roomba_twoPass.state) if (roomba_noAutoPasses.state == ON && roomba_twoPass.state == ON) sendCommand(roomba_twoPass, OFF) end

rule "Roomba Last Update Timestamp" when Item roomba_rssi received update then getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR") end

rule "Roomba Bin Full" when Item roomba_full changed from OFF to ON then val Timestamp = getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR") pushNotification("Roomba", "BIN FULL reported by Roomba at: " + Timestamp) end

rule "Roomba Error" when Item roomba_error changed from OFF to ON then val Timestamp = getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR") pushNotification("Roomba", "ERROR reported by Roomba at: " + Timestamp) sendMail(mailTo, "Roomba", "ERROR reported by Roomba at: " + Timestamp + "See attachment for details", "http://your_OH_ip:port/static/map.png") end

rule "Roomba percent completed" when Item roomba_sqft received update then var sqft_completed = roomba_sqft.state as Number

var max_sqft = 470  //insert max square footage here
var min_sqft = 0

var Number completed_percent = 0

if (sqft_completed < min_sqft) {completed_percent = 0)
else if (sqft_completed > max_sqft) {completed_percent = 100}
else {
    completed_percent = (((sqft_completed - min_sqft) * 100) / (max_sqft-min_sqft)).intValue
    }
logInfo("Roomba", "Roomba percent complete "+roomba_sqft.state+" of "+max_sqft.toString+" calculated as " + completed_percent.toString + "%")
postUpdate(roomba_percent_complete,completed_percent)

end

rule "Roomba update command" when Item roomba_phase received update then logInfo("Roomba", "Roomba phase received update: " + roomba_phase.state} switch(roomba_phase.state) { case "run" : postUpdate(roomba_command,"start") case "hmUsrDock" : postUpdate(roomba_command,"pause") case "hmMidMsn" : postUpdate(roomba_command,"pause") case "hmPostMsn" : { postUpdate(roomba_command,"dock") getTimestamp.apply(roomba_lastmissioncompleted, "%1$ta %1$tR") } case "charge" : postUpdate(roomba_command,"dock") case "stop" : postUpdate(roomba_command,"stop") case "pause" : postUpdate(roomba_command,"pause") case "stuck" : postUpdate(roomba_command,"stop") } end

rule "Roomba Notifications" when Item roomba_status changed then logInfo("Roomba", "Roomba status is: " + roomba_status.state} val Timestamp = getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR") switch(roomba_status.state) { case "Running" : pushNotification("Roomba", "Roomba is RUNNING at: " + Timestamp) case "Docking - End Mission" : { createTimer(now.plusSeconds(2)) [| pushNotification("Roomba", "Roomba has FINISHED cleaning at: " + Timestamp) sendMail(mailTo, "Roomba", "Roomba has FINISHED cleaning at: " + Timestamp + "See attachment for details", "http://your_OH_ip:port/static/map.png") ] } case "Stuck" : { pushNotification("Roomba", "HELP! Roomba is STUCK at: " + Timestamp) sendMail(mailTo, "Roomba", "HELP! Roomba is STUCK at: " + Timestamp + "See attachment for location", "http://your_OH_ip:port/static/map.png") } } end

rule "Roomba Schedule Display" when Item roomba_cycle changed or Item roomba_cleanSchedule_h changed or Item roomba_cleanSchedule_m changed then logInfo("Roomba", "Roomba Schedule: Day " + roomba_cycle.state + " Hour: " + roomba_cleanSchedule_h.state + " Minute: " + roomba_cleanSchedule_m.state) var String schedule = "" var String days = (roomba_cycle.state as StringType).toString var String hours = (roomba_cleanSchedule_h.state as StringType).toString var String minutes = (roomba_cleanSchedule_m.state as StringType).toString val ArrayList daysOfWeek = newArrayList("Sun","Mon","Tues","Wed","Thur","Fri","Sat") val ArrayList daysList = new ArrayList(days.replace("[","").replace("]","").replace("'","").split(",")) val ArrayList hoursList = new ArrayList(hours.replace("[","").replace("]","").split(",")) val ArrayList minutesList = new ArrayList(minutes.replace("[","").replace("]","").split(",")) daysList.forEach[ item, i | if(item.trim() == "start") { schedule += daysOfWeek.get(i) + ": " + hoursList.get(i) + ":" + minutesList.get(i) + ", " } ] postUpdate(roomba_cleanSchedule, schedule.trim()) end

val Functions$Function2<GenericItem, String, String> getTimestamp = [ //function (lambda) to get a timestamp. Returns formatted string and optionally updates an item item, date_format |

var date_time_format = date_format
if(date_format == "" || date_format === null) date_time_format = "%1$ta %1$tT" //default format Day Hour:Minute:Seconds
var String Timestamp = String::format( date_time_format, new Date() )
if(item != NULL && item !== null) {
    var t = new DateTimeType()
    if(item instanceof DateTimeItem) {
        item.postUpdate(t) ////postUpdate(item, t)
        logInfo("Last Update", item.name + " DateTimeItem updated at: " + Timestamp )
        }
    else if(item instanceof StringItem) {
        item.postUpdate(Timestamp) //postUpdate(item, Timestamp)
        logInfo("Last Update", item.name + " StringItem updated at: " + Timestamp )
        }
    else
        logWarn("Last Update", item.name + " is not DateTime or String - not updating")
}
Timestamp
]
fpellicciotti commented 5 years ago

I've simply used your Roomba.rules file and added the function at the end of the file and added the 2 import lines on top.

NickWaterton commented 5 years ago

I think lambdas are meant to go at the beginning of the rules file, just after the import statement. They are like global variables.

fpellicciotti commented 5 years ago

I did that too, now I get even more errors:

2019-01-11 15:49:07.259 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'roomba2.rules' has errors, therefore ignoring it: [147,15]: missing ')' at 'Stuck' [148,84]: no viable alternative at input 'is' [148,95]: mismatched input ':' expecting ']' [149,67]: no viable alternative at input '", "' [149,84]: no viable alternative at input 'is' [149,95]: mismatched input ':' expecting 'end' [158,1]: no viable alternative at input 'end' [160,1]: mismatched input 'rule' expecting 'then'

NickWaterton commented 5 years ago

You haven't filled in any of the variables. For instance mailTo on line 146 is a global variable containing an e-mail addresss.. like this:

val String mailTo = "your-e-mail@gmail.com"

or just replace mailTo with an actual e-mail address (in quotes as shown)

You do realize that things like "http://your_OH_ip:port/static/map.png" is not an actual web address right? you have to replace your_OH_ip:port with your actual ip address and port of your OH server. These things are just placeholders for you to fill in with your actual values. I don't know what your e-mail address and ip address are.

fpellicciotti commented 5 years ago

I might have figured it out. I removed the Roomba.rule file, and copied a new file line by line.

There were some lines that had { instead of (… I corrected them and now I got furhtur… I just get the following error now:

2019-01-11 16:56:50.139 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Roomba Last Update Timestamp': null 2019-01-11 16:56:52.945 [WARN ] [ui.internal.items.ItemUIRegistryImpl] - Exception while formatting value 'OFF' of item roomba_error with format '%': Conversion = '%'

NickWaterton commented 5 years ago

Can you repost you actual rule file? and use code flags (<> above) it's easier to read. A single error (lie a %) will kill OH's rule engine.

fpellicciotti commented 5 years ago

roomba.rules.txt

I've attached the rules file (renamed to txt file)

fpellicciotti commented 5 years ago

Please note that this is a straight copy and paste from your code on the main page....

NickWaterton commented 5 years ago

I need to see your actual rules file, the one you sent won't work as you haven't filled in any of the placeholders, like mailTo or your_OH_ip:portwith actual values, so it won't work as is (or delete the rules I mention below).

It also assumes that you have e-mail and pushNotification set up, if you don't the sendMail and pushNotification won't work either. You would have to delete these rules (or just comment out the pushNotification and sendMail until you have them set up):

rule "Roomba Error"
when
    Item roomba_error changed from OFF to ON
then
    val Timestamp = getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR")
    pushNotification("Roomba", "ERROR reported by Roomba at: " + Timestamp)
    sendMail(mailTo, "Roomba", "ERROR reported by Roomba at: " + Timestamp + "See attachment for details", "http://your_OH_ip:port/static/map.png")
end

rule "Roomba Notifications"
when
    Item roomba_status changed
then
    logInfo("Roomba", "Roomba status is: " + roomba_status.state}
    val Timestamp = getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR")
    switch(roomba_status.state) {
        case "Running"                  : pushNotification("Roomba", "Roomba is RUNNING at: " + Timestamp)
        case "Docking - End Mission"    : {
                                          createTimer(now.plusSeconds(2)) [|
                                              pushNotification("Roomba", "Roomba has FINISHED cleaning at: " + Timestamp)
                                              sendMail(mailTo, "Roomba", "Roomba has FINISHED cleaning at: " + Timestamp + "See attachment for details", "http://your_OH_ip:port/static/map.png")
                                              ]
                                          }
        case "Stuck"                    : {
                                          pushNotification("Roomba", "HELP! Roomba is STUCK at: " + Timestamp)
                                          sendMail(mailTo, "Roomba", "HELP! Roomba is STUCK at: " + Timestamp + "See attachment for location", "http://your_OH_ip:port/static/map.png")
                                          }
    }
end

as they would not work without email and pushNotification set up.

Also I don't know where

import time
import datetime
import date

comes from, but that can't work.

fpellicciotti commented 5 years ago

roomba.rules.2.txt

Attached with info mailTo or your_OH_ip:port filled in. However, I am more concerned about the error:

2019-01-11 16:56:50.139 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Roomba Last Update Timestamp': null 2019-01-11 16:56:52.945 [WARN ] [ui.internal.items.ItemUIRegistryImpl] - Exception while formatting value 'OFF' of item roomba_error with format '%': Conversion = '%'

Where the timestamp is null. It does not seem to get the timestamp and the format error of the value OFF.

fpellicciotti commented 5 years ago

I gave up on the lambda function. Instead I went with: roomba_lastheardfrom.postUpdate( new DateTimeType() )

Which works :).

However in the logs, I still get the formatting error

NickWaterton commented 5 years ago

Line 144 is still incorrect, but that shouldn't cause your error. Are all your items defined correctly?

This rule is incorrect syntax:

rule "Roomba Last Update Timestamp"
when
    Item roomba_rssi received update
then
   Timestamp = getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR")
end

It should be:

rule "Roomba Last Update Timestamp"
when
    Item roomba_rssi received update
then
    getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR")
end

This is probably the cause of the errors.

What about all the other occurrences of getTimestamp, what did you replace those with? some rules use the returned Timestamp String as part of the notification message.

All I can say is that these rules work perfectly in my OH 2.4 set up. (actually I think I'm on 2.5 now, but I have been using the rules for nearly 2 years).

You have some basic syntax errors in these rules, you need to go through them carefully to check what you have done, or use the Visual Studio OH plugin to check for you.

swamiller commented 5 years ago

Nick, i have mine mostly working but I'm also having an issue with the Date(). I'm getting error message in VSCode:

"Date cannot be resolved.(org.eclipse.xtext.diagnostics.Diagnostic.Linking) [10,66]"

My OH log shows the the following error:

2019-02-09 14:01:14.710 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Roomba Last Update Timestamp': null

All the associated items that use the "Timestamp" function are null as well.

I have the lambda at the top of my rules file with the recommended imports.

Here is my rules file:

import org.eclipse.xtext.xbase.lib.Functions    //apparently needed for OH2 lambda's
import org.eclipse.xtext.xbase.lib.Procedures   //apparently needed for OH2 lambda's

val Functions$Function2<GenericItem, String, String> getTimestamp = [  //function (lambda) to get a timestamp. Returns formatted string and optionally updates an item
    item,
    date_format |

    var date_time_format = date_format
    if(date_format == "" || date_format === null) date_time_format = "%1$ta %1$tT" //default format Day Hour:Minute:Seconds
    var String Timestamp = String::format( date_time_format, new Date() )
    if(item != NULL && item !== null) {
        var t = new DateTimeType()
        if(item instanceof DateTimeItem) {
            item.postUpdate(t) ////postUpdate(item, t)
            logInfo("Last Update", item.name + " DateTimeItem updated at: " + Timestamp )
            }
        else if(item instanceof StringItem) {
            item.postUpdate(Timestamp) //postUpdate(item, Timestamp)
            logInfo("Last Update", item.name + " StringItem updated at: " + Timestamp )
            }
        else
            logWarn("Last Update", item.name + " is not DateTime or String - not updating")
    }
    Timestamp
    ]
/* Roomba Rules */
rule "Roomba start and stop"
when
    Item roomba_control received command
then
    logInfo("Roomba", "Roomba ON/OFF received command: " + receivedCommand)
    if (receivedCommand == ON)
        sendCommand(roomba_command, "start")
    if (receivedCommand == OFF) {
        sendCommand(roomba_command, "stop")
        Thread::sleep(1000)
        sendCommand(roomba_command, "dock")
    }
end

rule "Roomba Auto Boost Control"
when
    Item roomba_carpetBoost changed
then
    logInfo("Roomba", "Roomba Boost changed to: Auto " + roomba_carpetBoost.state + " Manual: " + roomba_vacHigh.state)
    if (roomba_carpetBoost.state == ON && roomba_vacHigh.state == ON)
        sendCommand(roomba_vacHigh, OFF)
end

rule "Roomba Manual Boost Control"
when
    Item roomba_vacHigh changed
then
    logInfo("Roomba", "Roomba Boost changed to: Auto " + roomba_carpetBoost.state + " Manual: " + roomba_vacHigh.state)
    if (roomba_carpetBoost.state == ON && roomba_vacHigh.state == ON)
        sendCommand(roomba_carpetBoost, OFF)
end

rule "Roomba Auto Passes Control"
when
    Item roomba_noAutoPasses changed or
    Item roomba_twoPass changed
then
    logInfo("Roomba", "Roomba Passes changed to: Auto " + roomba_noAutoPasses.state + " Manual: " + roomba_twoPass.state)
    if (roomba_noAutoPasses.state == ON && roomba_twoPass.state == ON)
        sendCommand(roomba_twoPass, OFF)
end

rule "Roomba Last Update Timestamp"
when
    Item roomba_rssi received update
then
    getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR")
end

rule "Roomba Bin Full"
when
    Item roomba_full changed from OFF to ON
then
    val Timestamp = getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR")
    sendPushoverMessage(pushoverBuilder("BIN FULL reported by Roomba at: " + Timestamp))
end

rule "Roomba Error"
when
    Item roomba_error changed from OFF to ON
then
    val Timestamp = getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR")
    sendPushoverMessage(pushoverBuilder("ERROR reported by Roomba at: " + Timestamp))
    sendMail("myemailaddress", "Roomba", "ERROR reported by Roomba at: " + Timestamp + "See attachment for details", "http:XXX.XXX.X.XX:8080/static/map.png")
end

rule "Roomba percent completed"
when
    Item roomba_sqft received update
then
    var sqft_completed = roomba_sqft.state as Number

    var max_sqft = 470  //insert max square footage here
    var min_sqft = 0

    var Number completed_percent = 0

    if (sqft_completed < min_sqft) {completed_percent = 0}
    else if (sqft_completed > max_sqft) {completed_percent = 100}
    else {
        completed_percent = (((sqft_completed - min_sqft) * 100) / (max_sqft-min_sqft)).intValue
        }
    logInfo("Roomba", "Roomba percent complete "+roomba_sqft.state+" of "+max_sqft.toString+" calculated as " + completed_percent.toString + "%")
    postUpdate(roomba_percent_complete,completed_percent)
end

rule "Roomba update command"
when
    Item roomba_phase received update
then
    logInfo("Roomba", "Roomba phase received update: " + roomba_phase.state)
    switch(roomba_phase.state) {
        case "run"          : postUpdate(roomba_command,"start")
        case "hmUsrDock"    : postUpdate(roomba_command,"pause")
        case "hmMidMsn"     : postUpdate(roomba_command,"pause")
        case "hmPostMsn"    : {
                              postUpdate(roomba_command,"dock")
                              getTimestamp.apply(roomba_lastmissioncompleted, "%1$ta %1$tR")
                              }
        case "charge"       : postUpdate(roomba_command,"dock")
        case "stop"         : postUpdate(roomba_command,"stop")
        case "pause"        : postUpdate(roomba_command,"pause")
        case "stuck"        : postUpdate(roomba_command,"stop")
    }
end

rule "Roomba Notifications"
when
    Item roomba_status changed
then
    logInfo("Roomba", "Roomba status is: " + roomba_status.state)
    val Timestamp = getTimestamp.apply(roomba_lastheardfrom, "%1$ta %1$tR")
    switch(roomba_status.state) {
        case "Running"                  : sendPushoverMessage(pushoverBuilder("Roomba is RUNNING at: " + Timestamp))
        case "Docking - End Mission"    : {
                                          createTimer(now.plusSeconds(2)) [|
                                              sendPushoverMessage(pushoverBuilder("Roomba has FINISHED cleaning at: " + Timestamp))
                                              sendMail("myemailaddress", "Roomba", "Roomba has FINISHED cleaning at: " + Timestamp + "See attachment for details", "http://XXX.XXX.X.XX:8080/static/map.png")
                                              ]
                                          }
        case "Stuck"                    : {
                                          sendPushoverMessage(pushoverBuilder("HELP! Roomba is STUCK at: " + Timestamp))
                                          sendMail("myemailaddress", "Roomba", "HELP! Roomba is STUCK at: " + Timestamp + "See attachment for location", "http://XXX.XXX.X.XX:8080/static/map.png")
                                          }
    }
end

rule "Roomba Schedule Display"
when
    Item roomba_cycle changed or
    Item roomba_cleanSchedule_h changed or
    Item roomba_cleanSchedule_m changed
then
    logInfo("Roomba", "Roomba Schedule: Day " + roomba_cycle.state + " Hour: " + roomba_cleanSchedule_h.state + " Minute: " + roomba_cleanSchedule_m.state)
    var String schedule = ""
    var String days = (roomba_cycle.state as StringType).toString
    var String hours = (roomba_cleanSchedule_h.state as StringType).toString
    var String minutes = (roomba_cleanSchedule_m.state as StringType).toString
    val ArrayList daysOfWeek = newArrayList("Sun","Mon","Tues","Wed","Thur","Fri","Sat")
    val ArrayList<String> daysList = new ArrayList(days.replace("[","").replace("]","").replace("'","").split(","))
    val ArrayList<String> hoursList = new ArrayList(hours.replace("[","").replace("]","").split(","))
    val ArrayList<String> minutesList = new ArrayList(minutes.replace("[","").replace("]","").split(","))
    daysList.forEach[ item, i |
        if(item.trim() == "start") {
            schedule += daysOfWeek.get(i) + ": " + hoursList.get(i) + ":" + minutesList.get(i) + ", "
        }
    ]
    postUpdate(roomba_cleanSchedule, schedule.trim())
end

Everything else seems to be funtioning normal with exception of another VSCode error regarding "ArrayList cannot be resolved" which I don't think is related.

Any ideas?

RESOLVED: I figured it out. Although everything you read about OH 2 is you don't need any imports, however the Date() issue and the other "ArrayList cannot be resolved" issue are both resolved by adding the following imports:

import java.util.ArrayList
import java.util.Date