iRobot 9xx on openHAB

Hi @jwiseman,

no. I’m not using PaperUI. Textual configuration only. Installed bundles on my system are:


It seems, that

val RoombaActions = getActions("mqtt", "mqtt:broker:roomba")

call returns nothing. Please, check the second parameter in a call: It should be name of the bridge.
On my system:

Kind regards,

Alexander.

Hi Alexander,

Posting your Things in Karaf was the clue to why mine is NOT working. My MQTT Topic Thing is NOT being created on the Thing definition area so I decided to create it manually after doing a deep dive how MQTT works using this video of @ David_Graeff.

Unfortunately, the MQTT Thing topic will NOT initialize for what ever reason so I’m going to have to get David_Graeff involved to see what is wrong.

bundle:list -s | grep mqtt

189 â Active   â  80 â 1.2.1.201809150405     â org.eclipse.paho.client.mqttv3
190 â Active   â  80 â 0.11.0.oh250M1         â org.eclipse.smarthome.io.mqttembeddedbroker
193 â Active   â  80 â 2.5.0.201903012159     â org.openhab.binding.mqtt
200 â Active   â  80 â 2.5.0.201903012159     â org.openhab.binding.mqtt.generic
204 â Active   â  80 â 0.11.0.oh250M1         â org.eclipse.smarthome.io.transport.mqtt

Karaf Things:

mqtt:systemBroker:embedded-mqtt-broker (Type=Bridge, Status=ONLINE, Label=MQTT Broker, Bridge=null)
mqtt:topic:roomba:state (Type=Thing, Status=UNINITIALIZED, Label=Roomba Status, Bridge=mqtt:broker:roomba)

OH Startup Errors:

2019-03-10 08:56:55.221 [ERROR] [org.openhab.binding.mqtt.generic    ] - FrameworkEvent ERROR - org.openhab.binding.mqtt.generic
org.osgi.framework.ServiceException: Exception in org.apache.felix.scr.impl.manager.SingleComponentManager.getService()
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.factoryGetService(ServiceFactoryUse.java:222) [?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.getService(ServiceFactoryUse.java:111) [?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceConsumer$2.getService(ServiceConsumer.java:45) [?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.getService(ServiceRegistrationImpl.java:508) [?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.getService(ServiceRegistry.java:461) [?:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.getService(BundleContextImpl.java:624) [?:?]
	at com.eclipsesource.jaxrs.publisher.internal.ResourceTracker.addingService(ResourceTracker.java:39) [15:com.eclipsesource.jaxrs.publisher:5.3.1.201602281253]
	at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:941) [?:?]
	at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:870) [?:?]
	at org.osgi.util.tracker.AbstractTracked.trackAdding(AbstractTracked.java:256) [?:?]
	at org.osgi.util.tracker.AbstractTracked.trackInitial(AbstractTracked.java:183) [?:?]
	at org.osgi.util.tracker.ServiceTracker.open(ServiceTracker.java:318) [?:?]
	at org.osgi.util.tracker.ServiceTracker.open(ServiceTracker.java:261) [?:?]
	at com.eclipsesource.jaxrs.publisher.internal.Activator.openAllServiceTracker(Activator.java:91) [15:com.eclipsesource.jaxrs.publisher:5.3.1.201602281253]
	at com.eclipsesource.jaxrs.publisher.internal.Activator.start(Activator.java:55) [15:com.eclipsesource.jaxrs.publisher:5.3.1.201602281253]
	at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:779) [?:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) [?:?]
	at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:772) [?:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:729) [?:?]
	at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:933) [?:?]
	at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) [?:?]
	at org.eclipse.osgi.container.Module.doStart(Module.java:581) [?:?]
	at org.eclipse.osgi.container.Module.start(Module.java:449) [?:?]
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1634) [?:?]
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1614) [?:?]
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1585) [?:?]
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1528) [?:?]
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1) [?:?]
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) [?:?]
	at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:340) [?:?]
Caused by: java.lang.NoClassDefFoundError: org/eclipse/smarthome/core/thing/type/ChannelGroupTypeProvider
	at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[?:?]
	at org.eclipse.osgi.internal.loader.ModuleClassLoader.defineClass(ModuleClassLoader.java:276) ~[?:?]
	at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.defineClass(ClasspathManager.java:655) ~[?:?]
	at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findClassImpl(ClasspathManager.java:578) ~[?:?]
	at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClassImpl(ClasspathManager.java:538) ~[?:?]
	at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:525) ~[?:?]
	at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:328) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:368) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:446) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:395) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:387) ~[?:?]
	at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:150) ~[?:?]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:?]
	at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) ~[?:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.initDependencyManagers(AbstractComponentManager.java:976) ~[?:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.collectDependencies(AbstractComponentManager.java:1003) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.getServiceInternal(SingleComponentManager.java:859) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.getService(SingleComponentManager.java:823) ~[?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse$1.run(ServiceFactoryUse.java:212) ~[?:?]
	at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.factoryGetService(ServiceFactoryUse.java:210) ~[?:?]
	... 30 more
Caused by: java.lang.ClassNotFoundException: org.eclipse.smarthome.core.thing.type.ChannelGroupTypeProvider cannot be found by org.openhab.binding.mqtt.generic_2.5.0.201903012159
	at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:433) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:395) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:387) ~[?:?]
	at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:150) ~[?:?]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:?]
	at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[?:?]
	at org.eclipse.osgi.internal.loader.ModuleClassLoader.defineClass(ModuleClassLoader.java:276) ~[?:?]
	at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.defineClass(ClasspathManager.java:655) ~[?:?]
	at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findClassImpl(ClasspathManager.java:578) ~[?:?]
	at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClassImpl(ClasspathManager.java:538) ~[?:?]
	at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:525) ~[?:?]
	at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:328) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:368) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:446) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:395) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:387) ~[?:?]
	at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:150) ~[?:?]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:?]
	at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) ~[?:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.initDependencyManagers(AbstractComponentManager.java:976) ~[?:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.collectDependencies(AbstractComponentManager.java:1003) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.getServiceInternal(SingleComponentManager.java:859) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.getService(SingleComponentManager.java:823) ~[?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse$1.run(ServiceFactoryUse.java:212) ~[?:?]
	at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.factoryGetService(ServiceFactoryUse.java:210) ~[?:?]
	... 30 more

Best, Jay

Hi @jwiseman,
There is no broker available. Please take a look here for textual thing configuration.

Kind regards,
Alexander

There is - just forgot to copy/paste it into the above post.

mqtt:broker:roomba (Type=Bridge, Status=ONLINE, Label=Roomba, Bridge=null)

Here’s Paper UI showing all the things and the Uninitialized Thing Topic which is causing the issues.

Capture

Items within the Uninitialized Thing Topic:

Best, Jay

This “ClassNotFoundException” sounds very fishy to me.

Which version of openHAB are you on? SNAPSHOT versions? If so, could you update to the very latest version first? If not SNAPSHOT, could you check that you are at least on 2.5.0M1?

You are correct; my OH version 2.3 is too old for this core function.

Per @David_Graeff

ChannelGroupTypeProvider was introduced with OH 2.4, so you are out of look here. OH 2.3 is too old to host the new MQTT binding’

I’ve posted this to see how to manually upgrade OH on a Synology NAS.

Best, Jay

Hi,

so what is the OH / MQTT binding minimum version requirement to get it working?

I’m running OH 2.4 docker image. MQTT bindings I installed through Paper UI. Things, rules and transformations I’ve created by hand per Alexanders examples. mqtt:broker:roomba is up and running but mqtt:topic:roomba:state gives me a communication error. No further info in the log.

I’m owning a 691, was able to extract the password using koalazak’s tool.

Do I have to upgrade to latest OH 2.5.0-snapshot docker image?

Thanks, Thomas

Hi @ollo,

i’m running openHAB 2.5.0.M1 on my production system.

Kind regards,

Alexander

I finally upgraded my Synology to v2.5m1 and the Roomba code from @falkena works now! Very happy . . .

Best, Jay

Hi,

I did the upgrade too and now the communication error is gone - great!
All items are online and RoombaWifistatJSON gets updated every 5sec.
Yet no update on RoombaShadowJSON, even after letting it run for a few hours.
Any idea what to check or how to debug?

Thanks!

Hi Alexander,

Would you be able to post more of your rules for specific actions/triggers that occur with the Roomba? I’m using your example of the DOCK and it works great! I’ve also created a START & STOP rule but that is the farthest I gotten.

Would like to implement more :wink:

Best, Jay

Should iRobot / Roomba be listed in https://www.openhab.org/addons/ somewhere?

I scanned the entire topic and from what I can see there are 4 takes on this:

  1. Dorita980 or Rest980 which is a REST api using Dorita980 in the background.
  2. Roomba980-Python
  3. An IFTTT solution
    • Very easy to setup, but offers limited integration
  4. A native MQTT2 binding

Right now I’m just wondering what I should use. I already got Rest980 working, but I’d like to get the amount of possibilities Roomba980-Python offers. Unfortunately it seems hard to setup and I can’t program in Python so I wouldn’t be able to create new functions easily. IFTTT is just to limited for my taste. But both Rest980 and Roomba980-Python aren’t as lightweight as a native MQTT binding.

What is the best option in terms of the factors below?

  • The amount of possibilties it offers (more is better)
  • Ease in setting up (easier is better)
  • Ease in using it (easier is better)
  • ‘Server load’ for lack of a better term (less is better)
1 Like

I am trying to get the Rest980 working, but when I try to start as described in the readme, I get an error, that port 3000 is already in use.

What can I do, to get the rest980 working, so that I can use it with openHAB?

Hi Jay,

may I ask for your START & STOP rule(s)? I got my setup finally fixed - stupid typo in the channel setup - and working, curious to setup rules for home automation.

Thanks, Thomas

Here is Home, Start and Stop.

Virtual Switches:

Switch	MainFloor_Roomba_Home_Switch	"Main Roomba Home [MAP(On_Off.map):%s]"								(Roomba)		[ "Switchable" ]
Switch	MainFloor_Roomba_Switch			"Main Roomba On/Off [MAP(On_Off.map):%s]"							(Roomba)		[ "Switchable" ]

Rules:

rule "Roomba MainFloor_Roomba_Home_Switch changed to ON"
	when
		Item MainFloor_Roomba_Home_Switch changed to ON
	then

		var mqttThing = getThingStatusInfo("mqtt:broker:roomba").getStatus()

		if (systemStarted.state != ON && mqttThing.toString() == 'ONLINE') {

			// val RuleRoombaActions = getActions("mqtt","mqtt:broker:roomba")
			RuleRoombaActions.publishMQTT("cmd", GetCommandJSON.apply("dock"))
			logInfo("Roomba", "MainFloor_Roomba_Home_Switch.state is " + MainFloor_Roomba_Home_Switch.state)
			logInfo("Roomba", "Requested Roomba to go HOME")
	
			MainFloor_Roomba_Switch.postUpdate(ON)

		} else if (systemStarted.state != ON && mqttThing.toString() != 'ONLINE') {

			logInfo("Roomba", "mqttThing is " + mqttThing)
			logInfo("Roomba", "MainFloor_Roomba_Home_Switch did NOT execute due to mqttThing being OFFLINE.")
			MainFloor_Roomba_Home_Switch.postUpdate(OFF)
		}
end


rule "Roomba MainFloor_Roomba_Switch changed to ON"
	when
		Item MainFloor_Roomba_Switch changed to ON
	then

		var mqttThing = getThingStatusInfo("mqtt:broker:roomba").getStatus()

		if (systemStarted.state != ON && mqttThing.toString() == 'ONLINE') {

			// val RuleRoombaActions = getActions("mqtt","mqtt:broker:roomba")
			RuleRoombaActions.publishMQTT("cmd", GetCommandJSON.apply("start"))
			logInfo("Roomba", "MainFloor_Roomba_Switch.state is " + MainFloor_Roomba_Switch.state)
			logInfo("Roomba", "Requested Roomba to START")

		} else if (systemStarted.state != ON && mqttThing.toString() != 'ONLINE') {

			logInfo("Roomba", "mqttThing is " + mqttThing)
			logInfo("Roomba", "MainFloor_Roomba_Switch did NOT execute due to mqttThing being OFFLINE.")
			MainFloor_Roomba_Switch.postUpdate(OFF)
		}
end


rule "Roomba MainFloor_Roomba_Switch changed to OFF"
	when
		Item MainFloor_Roomba_Switch changed to OFF
	then

		var mqttThing = getThingStatusInfo("mqtt:broker:roomba").getStatus()

		if (systemStarted.state != ON && mqttThing.toString() == 'ONLINE') {

			// val RuleRoombaActions = getActions("mqtt","mqtt:broker:roomba")
			RuleRoombaActions.publishMQTT("cmd", GetCommandJSON.apply("stop"))
			logInfo("Roomba", "MainFloor_Roomba_Switch.state is " + MainFloor_Roomba_Switch.state)
			logInfo("Roomba", "Requested Roomba to STOP")

			MainFloor_Roomba_Home_Switch.postUpdate(OFF)

		} else if (systemStarted.state != ON && mqttThing.toString() != 'ONLINE') {

			logInfo("Roomba", "mqttThing is " + mqttThing)
			logInfo("Roomba", "MainFloor_Roomba_Switch did NOT execute due to mqttThing being OFFLINE.")
			MainFloor_Roomba_Home_Switch.postUpdate(OFF)
		}
end

I’m still waiting for more Roomba examples myself from @ falkena.

Best, Jay

Hello,

interesting codes indeed. I tried Dorita980 and Rest980 and I had quite some fun with it. However, I was wondering: did someone succeed in accessing the camera and sensors data? Do you think it will ever be possible to command Roomba movements from Python?

1 Like

After some updates, seems that I can’t get my roomba back online.
When I check online, seems it has something to do with python3 and SSL?

Any suggestions what I can do?

./getpassword.py -R 192.168.111.26
waiting on port: 5678 for data
found 1 Roomba(s)
Make sure your robot (Anastacia) at IP 192.168.111.26 is on the Home Base and powered on (green lights on). Then press and hold the HOME button on your robot until it plays a series of tones (about 2 seconds). Release the button and your robot will flash WIFI light.
Press Enter to continue…
Received: {
“robotname”: “Anastacia”,
“sku”: “R960040”,
“nc”: 0,
“ver”: “3”,
“proto”: “mqtt”,
“ip”: “192.168.111.26”,
“hostname”: “Roomba-3164421080308590”,
“sw”: “v2.4.9-96”,
“mac”: “80:C5:F2:DF:69:CC”,
“cap”: {
“pp”: 1,
“multiPass”: 2,
“binFullDetect”: 1,
“ota”: 2,
“maps”: 1,
“pose”: 1,
“eco”: 1,
“langOta”: 1,
“edge”: 1,
“svcConf”: 1
}
}
Roomba (Anastacia) IP address is: 192.168.111.26
Connection Error TLS/SSL connection has been closed (EOF) (_ssl.c:727)
Traceback (most recent call last):
File “./getpassword.py”, line 39, in
main()
File “./getpassword.py”, line 36, in main
Password(arg.roombaIP,file=arg.configfile)
File “/etc/openhab2/scripts/roomba/password.py”, line 39, in init
self.get_password()
File “/etc/openhab2/scripts/roomba/password.py”, line 136, in get_password
wrappedSocket.send(packet)
File “/usr/lib/python2.7/ssl.py”, line 707, in send
v = self._sslobj.write(data)
ssl.SSLZeroReturnError: TLS/SSL connection has been closed (EOF) (_ssl.c:1829)

Hey guys,
My usb died and now I’m trying to set up my system again. I have latest 2.5 release and I’m trying to set up roomba trough MQTT (2). Brooker works fine, I’m receiving wifi status messages and also some state of robot. But I’m not able to start any command. I receiving this message in console:
[WARN ] [nhab.binding.mqtt.action.MQTTActions] - MQTT publish to cmd failed!

Things:

Bridge mqtt:broker:roomba “Roomba” [ clientID=“XXX”, host=“10.1.0.9”, port=8883, secure=true, username=“XXX”, password=“XXX”, certificatepin=true, publickeypin=true ]
{
Thing topic state “Roomba state” {
Channels:
Type string : wifistat “WiFi” [ stateTopic=“wifistat” ]
Type string : shadow “Status” [ stateTopic=“$aws/things/CLIENTID/shadow/#”]
}
}

Items:

String Roomba_command “Roomba command” (gRoomba)
String Roomba_Wifi “Roomba Wifi [%d dB]” (gRoomba)
String Roomba_Battery “Roomba Battery [%d %%]” (gRoomba)

Rules:


val RuleRoombaActions = getActions(“mqtt”,“mqtt:broker:roomba”)
val GetCommandJSON = [ String command | String::format(“{“command”:”%s",“time”:%d,“initiator”:“localApp”}“, command, now.millis / 1000) ]
val GetSettingJSON = [ String command | String::format(”{“state”:“%s”}") ]

rule “Roomba command”
when
Item Roomba_command received command
then
if (Roomba_command.state == “start”){
RuleRoombaActions.publishMQTT(“cmd”, GetCommandJSON.apply(“start”))
}else if(Roomba_command.state == “stop”){
RuleRoombaActions.publishMQTT(“cmd”, GetCommandJSON.apply(“stop”))
}

Can somebody please help me?
Thanks

I seem to be running into the same issue as Yogi is running into. I’ve setup the Roomba with MQTT & Mosquitto on a RPI Zero and I can easily read any MQTT message that comes around. I have the battery and the bin registering and working through BasicUI. That said, I can’t send any command to the Roomba. I get the following in my logs:

2020-01-11 23:17:19.533 [INFO ] [clipse.smarthome.model.script.ROOMBA] - {"command":"cmd","time":1578813439,"initiator":"localApp"}
2020-01-11 23:17:19.713 [WARN ] [nhab.binding.mqtt.action.MQTTActions] - MQTT publish to cmd failed!
2020-01-11 23:17:29.693 [INFO ] [.reconnect.PeriodicReconnectStrategy] - Try to restore connection to '192.168.1.142'. Next attempt in 60000ms
2020-01-11 23:17:29.907 [INFO ] [.transport.mqtt.MqttBrokerConnection] - Starting MQTT broker connection to '192.168.1.142' with clientid 3176C01070504730

I’m not sure why it tries to reconnect right after the failure, but it does that consistently. My setup is pretty much a copy/paste of everyone’s setup here. I’ve created a single update rule just to make it work

rule "RoombaStateUpdated"
when
        Item RoombaState received update
then
        val RoombaActions = getActions("mqtt", "mqtt:broker:roomba")

        // Commands: "start", "stop", "pause", "resume", "dock"
        val GetCommandJSON = [ String command |
          String::format("{\"command\":\"%s\",\"time\":%d,\"initiator\":\"localApp\"}", command, now.millis / 1000)
        ]

        logInfo("ROOMBA", GetCommandJSON.apply("cmd"))

        switch(RoombaState.state) {
                case 0: RoombaActions.publishMQTT("cmd", GetCommandJSON.apply("start"))
                case 1: RoombaActions.publishMQTT("cmd", GetCommandJSON.apply("stop"))
                case 2: RoombaActions.publishMQTT("cmd", GetCommandJSON.apply("pause"))
                case 3: RoombaActions.publishMQTT("cmd", GetCommandJSON.apply("resume"))
                case 4: RoombaActions.publishMQTT("cmd", GetCommandJSON.apply("dock"))
        }
end

I’d appreciate any direct help or hints about how I could go about debugging this issue.
Thanks