mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[irobot] Add command "cleanRegions" to clean specific regions only (#9775)
Signed-off-by: Florian Binder <fb@java4.info>
This commit is contained in:
parent
c9dbc46fd1
commit
e4b959382f
@ -32,7 +32,7 @@ known, however, whether the password is eternal or can change during factory res
|
||||
|
||||
| channel | type | description | Read-only |
|
||||
|---------------|--------|----------------------------------------------------|-----------|
|
||||
| command | String | Command to execute: clean, spot, dock, pause, stop | N |
|
||||
| command | String | Command to execute: clean, cleanRegions, spot, dock, pause, stop | N |
|
||||
| cycle | String | Current mission: none, clean, spot | Y |
|
||||
| phase | String | Current phase of the mission; see below. | Y |
|
||||
| battery | Number | Battery charge in percents | Y |
|
||||
@ -52,6 +52,7 @@ known, however, whether the password is eternal or can change during factory res
|
||||
| always_finish | Switch | Whether to keep cleaning if the bin becomes full | N |
|
||||
| power_boost | String | Power boost mode: "auto", "performance", "eco" | N |
|
||||
| clean_passes | String | Number of cleaning passes: "auto", "1", "2" | N |
|
||||
| last_command | String | Json string containing the parameters of the last executed command | N |
|
||||
|
||||
Known phase strings and their meanings:
|
||||
|
||||
@ -137,6 +138,14 @@ Error codes. Data type is string in order to be able to utilize mapping to human
|
||||
| 75 | Navigation problem |
|
||||
| 76 | Hardware problem detected |
|
||||
|
||||
## Cleaning specific regions
|
||||
You can clean one or many specific regions of a given map by sending the following String to the command channel:
|
||||
|
||||
```
|
||||
cleanRegions:<pmapId>;<region_id1>,<region_id2>,..
|
||||
```
|
||||
The easiest way to determine the pmapId and region_ids is to monitor the last_command channel while starting a new mission for the specific region with the iRobot-App.
|
||||
|
||||
## Known Problems / Caveats
|
||||
|
||||
1. Sending "pause" command during missions other than "clean" is equivalent to sending "stop"
|
||||
|
@ -48,8 +48,10 @@ public class IRobotBindingConstants {
|
||||
public static final String CHANNEL_ALWAYS_FINISH = "always_finish";
|
||||
public static final String CHANNEL_POWER_BOOST = "power_boost";
|
||||
public static final String CHANNEL_CLEAN_PASSES = "clean_passes";
|
||||
public static final String CHANNEL_LAST_COMMAND = "last_command";
|
||||
|
||||
public static final String CMD_CLEAN = "clean";
|
||||
public static final String CMD_CLEAN_REGIONS = "cleanRegions";
|
||||
public static final String CMD_SPOT = "spot";
|
||||
public static final String CMD_DOCK = "dock";
|
||||
public static final String CMD_PAUSE = "pause";
|
||||
|
@ -12,10 +12,17 @@
|
||||
*/
|
||||
package org.openhab.binding.irobot.internal.dto;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* iRobot MQTT protocol messages
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
* @author Florian Binder - Added CleanRoomsRequest
|
||||
*
|
||||
*/
|
||||
public class MQTTProtocol {
|
||||
@ -23,6 +30,29 @@ public class MQTTProtocol {
|
||||
public String getTopic();
|
||||
}
|
||||
|
||||
public static class CleanRoomsRequest extends CommandRequest {
|
||||
public int ordered;
|
||||
public String pmap_id;
|
||||
public List<Region> regions;
|
||||
|
||||
public CleanRoomsRequest(String cmd, String mapId, String[] regions) {
|
||||
super(cmd);
|
||||
ordered = 1;
|
||||
pmap_id = mapId;
|
||||
this.regions = Arrays.stream(regions).map(i -> new Region(i)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static class Region {
|
||||
public String region_id;
|
||||
public String type;
|
||||
|
||||
public Region(String id) {
|
||||
this.region_id = id;
|
||||
this.type = "rid";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandRequest implements Request {
|
||||
public String command;
|
||||
public long time;
|
||||
@ -31,7 +61,7 @@ public class MQTTProtocol {
|
||||
public CommandRequest(String cmd) {
|
||||
command = cmd;
|
||||
time = System.currentTimeMillis() / 1000;
|
||||
initiator = "localApp";
|
||||
initiator = "openhab";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -56,6 +86,7 @@ public class MQTTProtocol {
|
||||
public static class CleanMissionStatus {
|
||||
public String cycle;
|
||||
public String phase;
|
||||
public String initiator;
|
||||
public int error;
|
||||
}
|
||||
|
||||
@ -183,6 +214,9 @@ public class MQTTProtocol {
|
||||
public String bootloaderVer;
|
||||
// "umiVer":"6",
|
||||
public String umiVer;
|
||||
// "lastCommand":
|
||||
// {"command":"start","initiator":"localApp","time":1610283995,"ordered":1,"pmap_id":"AAABBBCCCSDDDEEEFFF","regions":[{"region_id":"6","type":"rid"}]}
|
||||
public JsonElement lastCommand;
|
||||
}
|
||||
|
||||
// Data comes as JSON string: {"state":{"reported":<Actual content here>}}
|
||||
|
@ -25,6 +25,7 @@ import java.util.Hashtable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@ -65,6 +66,7 @@ import com.google.gson.stream.JsonReader;
|
||||
*
|
||||
* @author hkuhn42 - Initial contribution
|
||||
* @author Pavel Fedin - Rewrite for 900 series
|
||||
* @author Florian Binder - added cleanRegions command and lastCommand channel
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RoombaHandler extends BaseThingHandler implements MqttConnectionObserver, MqttMessageSubscriber {
|
||||
@ -128,8 +130,25 @@ public class RoombaHandler extends BaseThingHandler implements MqttConnectionObs
|
||||
cmd = isPaused ? "resume" : "start";
|
||||
}
|
||||
|
||||
if (cmd.startsWith(CMD_CLEAN_REGIONS)) {
|
||||
// format: cleanRegions:<pmid>;<region_id1>,<region_id2>,...
|
||||
if (Pattern.matches("cleanRegions:[^:;,]+;.+(,[^:;,]+)*", cmd)) {
|
||||
String[] cmds = cmd.split(":");
|
||||
String[] params = cmds[1].split(";");
|
||||
|
||||
String mapId = params[0];
|
||||
String[] regionIds = params[1].split(",");
|
||||
|
||||
sendRequest(new MQTTProtocol.CleanRoomsRequest("start", mapId, regionIds));
|
||||
} else {
|
||||
logger.warn("Invalid request: {}", cmd);
|
||||
logger.warn("Correct format: cleanRegions:<pmid>;<region_id1>,<region_id2>,...>");
|
||||
}
|
||||
} else {
|
||||
sendRequest(new MQTTProtocol.CommandRequest(cmd));
|
||||
}
|
||||
|
||||
}
|
||||
} else if (ch.startsWith(CHANNEL_SCHED_SWITCH_PREFIX)) {
|
||||
MQTTProtocol.Schedule schedule = lastSchedule;
|
||||
|
||||
@ -489,6 +508,10 @@ public class RoombaHandler extends BaseThingHandler implements MqttConnectionObs
|
||||
}
|
||||
}
|
||||
|
||||
if (reported.lastCommand != null) {
|
||||
reportString(CHANNEL_LAST_COMMAND, reported.lastCommand.toString());
|
||||
}
|
||||
|
||||
reportProperty(Thing.PROPERTY_FIRMWARE_VERSION, reported.softwareVer);
|
||||
reportProperty("navSwVer", reported.navSwVer);
|
||||
reportProperty("wifiSwVer", reported.wifiSwVer);
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
<channels>
|
||||
<channel id="command" typeId="command"/>
|
||||
<channel id="last_command" typeId="lastCommand"/>
|
||||
<channel id="cycle" typeId="cycle"/>
|
||||
<channel id="phase" typeId="phase"/>
|
||||
<channel id="battery" typeId="battery"/>
|
||||
@ -255,4 +256,12 @@
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="lastCommand" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Last Command</label>
|
||||
<description>The last command which has been received by the iRobot</description>
|
||||
<state readOnly="true">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.irobot.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.spi.LocationAwareLogger;
|
||||
|
||||
/**
|
||||
* Test the MQTT protocol with local iRobot (without openhab running).
|
||||
* This class is used to test the binding against a local iRobot instance.
|
||||
*
|
||||
* @author Florian Binder - Initial contribution
|
||||
*/
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
class RoombaHandlerTest {
|
||||
|
||||
private static final String IP_ADDRESS = "<iRobotIP>";
|
||||
private static final String PASSWORD = "<PasswordForIRobot>";
|
||||
|
||||
private RoombaHandler handler;
|
||||
private @Mock Thing myThing;
|
||||
private ThingHandlerCallback callback;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
Logger l = LoggerFactory.getLogger(RoombaHandler.class);
|
||||
Field f = l.getClass().getDeclaredField("currentLogLevel");
|
||||
f.setAccessible(true);
|
||||
f.set(l, LocationAwareLogger.TRACE_INT);
|
||||
|
||||
Configuration config = new Configuration();
|
||||
config.put("ipaddress", RoombaHandlerTest.IP_ADDRESS);
|
||||
config.put("password", RoombaHandlerTest.PASSWORD);
|
||||
|
||||
Mockito.when(myThing.getConfiguration()).thenReturn(config);
|
||||
Mockito.when(myThing.getUID()).thenReturn(new ThingUID("mocked", "irobot", "uid"));
|
||||
|
||||
callback = Mockito.mock(ThingHandlerCallback.class);
|
||||
|
||||
handler = new RoombaHandler(myThing);
|
||||
handler.setCallback(callback);
|
||||
}
|
||||
|
||||
// @Test
|
||||
void testInit() throws InterruptedException, IOException {
|
||||
handler.initialize();
|
||||
Mockito.verify(myThing, Mockito.times(1)).getConfiguration();
|
||||
|
||||
System.in.read();
|
||||
handler.dispose();
|
||||
}
|
||||
|
||||
// @Test
|
||||
void testCleanRegion() throws IOException, InterruptedException {
|
||||
handler.initialize();
|
||||
|
||||
System.in.read();
|
||||
|
||||
ChannelUID cmd = new ChannelUID("my:thi:blabla:command");
|
||||
handler.handleCommand(cmd, new StringType("cleanRegions:AABBCCDDEEFFGGHH;2,3"));
|
||||
|
||||
System.in.read();
|
||||
handler.dispose();
|
||||
}
|
||||
|
||||
// @Test
|
||||
void testDock() throws IOException, InterruptedException {
|
||||
handler.initialize();
|
||||
|
||||
System.in.read();
|
||||
|
||||
ChannelUID cmd = new ChannelUID("my:thi:blabla:command");
|
||||
handler.handleCommand(cmd, new StringType("dock"));
|
||||
|
||||
System.in.read();
|
||||
handler.dispose();
|
||||
}
|
||||
|
||||
// @Test
|
||||
void testStop() throws IOException, InterruptedException {
|
||||
handler.initialize();
|
||||
|
||||
System.in.read();
|
||||
|
||||
ChannelUID cmd = new ChannelUID("my:thi:blabla:command");
|
||||
handler.handleCommand(cmd, new StringType("stop"));
|
||||
|
||||
System.in.read();
|
||||
handler.dispose();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user