added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.lghombot</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,111 @@
# LG HomBot Binding
The binding integrates a modified LG HomBot VR6260 based vacuum robots.
Details on how to modify your HomBot can be found at [roboter-forum.com](https://www.roboter-forum.com/index.php?thread/10009-lg-hombot-3-0-wlan-kamera-steuerung-per-weboberfläche/).
The binding uses the HTTP service on port 6260 right now.
Please take care when modifying your HomBot! This binding is a complement to a modified HomBot not an excuse to do the modification.
Remember, You are responsible if You brick Your HomBot.
## Supported Things
Hacked LG HomBot series 62XX are supported.
The service that the binding connects to is actually lg.srv found at [sourceforge.net](https://sourceforge.net/projects/lgsrv/) running on a HomBot.
## Discovery
The auto-discovery should hopefully find your HomBot, be aware that it will try to connect to port 6260 on all IP-addresses on your subnet.
If you already know the IP-address of your HomBot you might as well just configure it.
## Thing Configuration
The thing only requires an IP-address to function, this could also be found using the discovery method.
The parameter is called "ipAdress".
You can also configure the polling interval in seconds by setting "pollingPeriod", and the network port of the server by using "port".
## Channels
| Channel Type ID | Item Type | Description | Read/Write |
|-----------------|-----------|--------------------------------------------------------------------------|------------|
| state | String | Current state of the HomBot. | R |
| battery | Number | Current battery charge. | R |
| cpuLoad | Number | Current CPU load. | R |
| srvMem | Number | Current server memory load. | R |
| clean | Switch | Start cleaning / return home. | RW |
| start | Switch | Start cleaning. | RW |
| home | Switch | Send HomBot home. | RW |
| pause | Switch | Pause current activity. | RW |
| turbo | Switch | Turn turbo on/off. | RW |
| repeat | Switch | Turn repeat cleaning on/off. | RW |
| mode | String | Current cleaning mode. | RW |
| nickname | String | Nickname of the HomBot. | R |
| move | String | Manually control the HomBot. | RW |
| camera | Image | Image from the top camera. | R |
| lastClean | DateTime | Date of last clean. | R |
| map | Image | Image of clean area. | R |
| monday | DateTime | Time when cleaning is on Mondays. | R |
| tuesday | DateTime | Time when cleaning is on Tuesdays. | R |
| wednsday | DateTime | Time when cleaning is on Wednsdays. | R |
| thursday | DateTime | Time when cleaning is on Thursdays. | R |
| friday | DateTime | Time when cleaning is on Fridays. | R |
| saturday | DateTime | Time when cleaning is on Saturdays. | R |
| sunday | DateTime | Time when cleaning is on Sundays. | R |
## Full Example
Example of how to configure a thing.
demo.thing
```
Thing lghombot:LGHomBot:mycleanerbot "LGHomBot" @ "Living Room" [ ipAdress="192.168.0.2", pollingPeriod="3", port="6260" ]
```
Here are some examples on how to map the channels to items.
demo.items:
```
String HomBot_State "State [%s]" <CleaningRobot> { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:state" }
Number HomBot_Battery "Battery [%d%%]" { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:battery" }
Switch HomBot_Clean "Clean" { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:clean" }
Switch HomBot_Start "Start" { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:start" }
Switch HomBot_Home "Home" { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:home" }
Switch HomBot_Pause "Pause" { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:pause" }
Switch HomBot_Turbo "Turbo" { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:turbo" }
Switch HomBot_Repeat "Repeat" { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:repeat" }
String HomBot_CleanMode "Clean mode [%s]" { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:mode" }
String HomBot_Nickname { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:nickname" }
Image HomBot_Camera { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:camera" }
DateTime HomBot_LastClean { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:lastClean" }
Image HomBot_Map { channel="lghombot:LGHomBot:a4_24_56_8f_2c_5b:map" }
```
demo.sitemap:
```
sitemap demo label="Main Menu"
{
Frame label="HomBot" {
Text item=HomBot_State
Text item=HomBot_Battery
Switch item=HomBot_Clean
Switch item=HomBot_Start
Switch item=HomBot_Home
Switch item=HomBot_Pause
Switch item=HomBot_Turbo
Switch item=HomBot_Repeat
Selection item=HomBot_CleanMode mappings=[
"ZZ"="Zigzag mode",
"SB"="Cell by cell mode",
"SPOT"="Spiral spot mode",
"MACRO_SECTOR" = "My space mode" ]
Text item=HomBot_Nickname
Image item=HomBot_Camera
DateTime item=HomBot_LastClean
Image item=HomBot_Map
}
}
```

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.lghombot</artifactId>
<name>openHAB Add-ons :: Bundles :: LG HomBot Binding</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.lghombot-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-lghombot" description="LG HomBot Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.lghombot/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2010-2020 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.lghombot.internal;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.RawType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link CameraUtil} is responsible for parsing the raw yuv 422 image from a LG HomBot.
*
* @author Fredrik Ahlström - Initial contribution
*/
@NonNullByDefault
public class CameraUtil {
private static final Logger logger = LoggerFactory.getLogger(CameraUtil.class);
private CameraUtil() {
// No need to instance this class.
}
/**
* This converts a non-interleaved YUV-422 image to a JPEG image.
*
* @param yuvData The uncompressed YUV data
* @param width The width of image.
* @param height The height of the image.
* @return A JPEG image as a State
*/
static State parseImageFromBytes(byte[] yuvData, int width, int height) {
final int size = width * height;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < size; i++) {
double y = yuvData[i] & 0xFF;
double u = yuvData[size + i / 2] & 0xFF;
double v = yuvData[(int) (size * 1.5 + i / 2.0)] & 0xFF;
int r = Math.min(Math.max((int) (y + 1.371 * (v - 128)), 0), 255); // red
int g = Math.min(Math.max((int) (y - 0.336 * (u - 128) - 0.698 * (v - 128)), 0), 255); // green
int b = Math.min(Math.max((int) (y + 1.732 * (u - 128)), 0), 255); // blue
int p = (r << 16) | (g << 8) | b; // pixel
image.setRGB(i % width, i / width, p);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (!ImageIO.write(image, "jpg", baos)) {
logger.debug("Couldn't find JPEG writer.");
}
} catch (IOException e) {
logger.info("IOException creating JPEG image.", e);
}
byte[] byteArray = baos.toByteArray();
if (byteArray != null && byteArray.length > 0) {
return new RawType(byteArray, "image/jpeg");
} else {
return UnDefType.UNDEF;
}
}
}

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2010-2020 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.lghombot.internal;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link LGHomBotBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Fredrik Ahlström - Initial contribution
*/
@NonNullByDefault
public final class LGHomBotBindingConstants {
private static final String BINDING_ID = "lghombot";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_LGHOMBOT = new ThingTypeUID(BINDING_ID, "LGHomBot");
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_LGHOMBOT);
// List of all Channel ids
static final String CHANNEL_STATE = "state";
static final String CHANNEL_BATTERY = "battery";
static final String CHANNEL_CPU_LOAD = "cpuLoad";
static final String CHANNEL_SRV_MEM = "srvMem";
static final String CHANNEL_CLEAN = "clean";
static final String CHANNEL_START = "start";
static final String CHANNEL_HOME = "home";
static final String CHANNEL_PAUSE = "pause";
static final String CHANNEL_MODE = "mode";
static final String CHANNEL_TURBO = "turbo";
static final String CHANNEL_REPEAT = "repeat";
static final String CHANNEL_NICKNAME = "nickname";
static final String CHANNEL_MOVE = "move";
static final String CHANNEL_CAMERA = "camera";
static final String CHANNEL_LAST_CLEAN = "lastClean";
static final String CHANNEL_MAP = "map";
static final String CHANNEL_MONDAY = "monday";
static final String CHANNEL_TUESDAY = "tuesday";
static final String CHANNEL_WEDNESDAY = "wednesday";
static final String CHANNEL_THURSDAY = "thursday";
static final String CHANNEL_FRIDAY = "friday";
static final String CHANNEL_SATURDAY = "saturday";
static final String CHANNEL_SUNDAY = "sunday";
// List of all HomBot states
static final String HBSTATE_UNKNOWN = "UNKNOWN";
static final String HBSTATE_WORKING = "WORKING";
static final String HBSTATE_BACKMOVING = "BACKMOVING";
static final String HBSTATE_BACKMOVING_INIT = "BACKMOVING_INIT";
static final String HBSTATE_BACKMOVING_JOY = "BACKMOVING_JOY";
static final String HBSTATE_PAUSE = "PAUSE";
static final String HBSTATE_STANDBY = "STANDBY";
static final String HBSTATE_HOMING = "HOMING";
static final String HBSTATE_DOCKING = "DOCKING";
static final String HBSTATE_CHARGING = "CHARGING";
static final String HBSTATE_DIAGNOSIS = "DIAGNOSIS";
static final String HBSTATE_RESERVATION = "RESERVATION";
static final String HBSTATE_ERROR = "ERROR";
/**
* Default port number HomBot uses.
*/
public static final int DEFAULT_HOMBOT_PORT = 6260;
private LGHomBotBindingConstants() {
// No need to instance this class.
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2010-2020 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.lghombot.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.lghombot.internal.discovery.LGHomBotDiscovery;
/**
* The {@link LGHomBotConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Fredrik Ahlström - Initial contribution
*/
@NonNullByDefault
public class LGHomBotConfiguration {
/**
* Constant field used in {@link LGHomBotDiscovery} to set the configuration property during discovery. Value of
* this field needs to match {@link #ipAddress}
*/
public static final String IP_ADDRESS = "ipAddress";
/**
* IP Address (or host name) of HomBot
*/
public String ipAddress = "";
/**
* Port used by the HomBot
*/
public int port = LGHomBotBindingConstants.DEFAULT_HOMBOT_PORT;
/**
* Polling time (in seconds) to refresh state from the HomBot itself.
*/
public int pollingPeriod = 3;
}

View File

@@ -0,0 +1,677 @@
/**
* Copyright (c) 2010-2020 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.lghombot.internal;
import static org.openhab.binding.lghombot.internal.LGHomBotBindingConstants.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.DateTimeException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.util.UrlEncoded;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.RawType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link LGHomBotHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Fredrik Ahlström - Initial contribution
*/
@NonNullByDefault
public class LGHomBotHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(LGHomBotHandler.class);
// This is setup in initialize().
private LGHomBotConfiguration config = new LGHomBotConfiguration();
private @Nullable ScheduledFuture<?> refreshTimer;
// State of HomBot
private String currentState = "";
private String currentMode = "";
private String currentNickname = "";
private String currentSrvMem = "";
private DecimalType currentBattery = DecimalType.ZERO;
private DecimalType currentCPULoad = DecimalType.ZERO;
private OnOffType currentCleanState = OnOffType.OFF;
private OnOffType currentStartState = OnOffType.OFF;
private OnOffType currentHomeState = OnOffType.OFF;
private OnOffType currentTurbo = OnOffType.OFF;
private OnOffType currentRepeat = OnOffType.OFF;
private State currentImage = UnDefType.UNDEF;
private State currentMap = UnDefType.UNDEF;
private DateTimeType currentLastClean = new DateTimeType();
private String currentMonday = "";
private String currentTuesday = "";
private String currentWednesday = "";
private String currentThursday = "";
private String currentFriday = "";
private String currentSaturday = "";
private String currentSunday = "";
private final DateTimeFormatter formatterLG = DateTimeFormatter.ofPattern("yyyy/MM/dd/HH/mm/ss");
private boolean disposed = false;
private boolean refreshSchedule = false;
public LGHomBotHandler(Thing thing) {
super(thing);
}
@Override
public void dispose() {
super.dispose();
disposed = true;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command.equals(RefreshType.REFRESH)) {
refreshFromState(channelUID);
} else {
switch (channelUID.getId()) {
case CHANNEL_CLEAN:
if (command == OnOffType.ON) {
if (currentState.equals(HBSTATE_HOMING)) {
sendHomBotCommand("PAUSE");
}
sendHomBotCommand("CLEAN_START");
} else if (command == OnOffType.OFF) {
sendHomBotCommand("HOMING");
}
break;
case CHANNEL_START:
if (command == OnOffType.ON) {
sendHomBotCommand("CLEAN_START");
}
break;
case CHANNEL_HOME:
if (command == OnOffType.ON) {
sendHomBotCommand("HOMING");
}
break;
case CHANNEL_PAUSE:
if (command instanceof OnOffType) {
sendHomBotCommand("PAUSE");
}
break;
case CHANNEL_TURBO:
if (command == OnOffType.ON) {
sendHomBotCommand("TURBO", "true");
} else if (command == OnOffType.OFF) {
sendHomBotCommand("TURBO", "false");
}
break;
case CHANNEL_REPEAT:
if (command == OnOffType.ON) {
sendHomBotCommand("REPEAT", "true");
} else if (command == OnOffType.OFF) {
sendHomBotCommand("REPEAT", "false");
}
break;
case CHANNEL_MODE:
if (command instanceof StringType) {
switch (command.toString()) {
case "SB":
sendHomBotCommand("CLEAN_MODE", "CLEAN_SB");
break;
case "ZZ":
sendHomBotCommand("CLEAN_MODE", "CLEAN_ZZ");
break;
case "SPOT":
sendHomBotCommand("CLEAN_MODE", "CLEAN_SPOT");
break;
case "MACRO_SECTOR":
sendHomBotCommand("CLEAN_MODE", "CLEAN_MACRO_SECTOR");
break;
default:
break;
}
}
break;
case CHANNEL_MOVE:
if (command instanceof StringType) {
String commandString = command.toString();
switch (commandString) {
case "FORWARD":
case "FORWARD_LEFT":
case "FORWARD_RIGHT":
case "LEFT":
case "RIGHT":
case "BACKWARD":
case "BACKWARD_LEFT":
case "BACKWARD_RIGHT":
case "RELEASE":
sendHomBotJoystick(commandString);
break;
default:
break;
}
}
break;
default:
logger.debug("Command received for unknown channel {}: {}", channelUID.getId(), command);
break;
}
}
}
@Override
public void initialize() {
disposed = false;
logger.debug("Initializing handler for LG HomBot");
config = getConfigAs(LGHomBotConfiguration.class);
setupRefreshTimer(0);
}
@Override
public void handleRemoval() {
ScheduledFuture<?> localTimer = refreshTimer;
if (localTimer != null) {
localTimer.cancel(false);
refreshTimer = null;
}
updateStatus(ThingStatus.REMOVED);
}
/**
* Sets up a refresh timer (using the scheduler) with the given interval.
*
* @param initialWaitTime The delay before the first refresh. Maybe 0 to immediately
* initiate a refresh.
*/
private void setupRefreshTimer(int initialWaitTime) {
ScheduledFuture<?> localTimer = refreshTimer;
if (localTimer != null) {
localTimer.cancel(false);
}
refreshTimer = scheduler.scheduleWithFixedDelay(this::updateAllChannels, initialWaitTime, config.pollingPeriod,
TimeUnit.SECONDS);
}
private String buildHttpAddress(String path) {
return "http://" + config.ipAddress + ":" + config.port + path;
}
private void sendHomBotCommand(String command) {
String fullCmd = "/json.cgi?" + UrlEncoded.encodeString("{\"COMMAND\":\"" + command + "\"}");
sendCommand(fullCmd);
}
private void sendHomBotCommand(String command, String argument) {
String fullCmd = "/json.cgi?"
+ UrlEncoded.encodeString("{\"COMMAND\":{\"" + command + "\":\"" + argument + "\"}}");
sendCommand(fullCmd);
}
private void sendHomBotJoystick(String command) {
String fullCmd = "/json.cgi?" + UrlEncoded.encodeString("{\"JOY\":\"" + command + "\"}");
sendCommand(fullCmd);
}
private @Nullable String sendCommand(String path) {
String url = buildHttpAddress(path);
logger.trace("Executing: {}", url);
String status = null;
try {
status = HttpUtil.executeUrl("GET", url, 1000);
if (getThing().getStatus() != ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
}
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
logger.trace("Status received: {}", status);
return status;
}
private void refreshFromState(ChannelUID channelUID) {
switch (channelUID.getId()) {
case CHANNEL_STATE:
updateState(channelUID, StringType.valueOf(currentState));
break;
case CHANNEL_CLEAN:
updateState(channelUID, currentCleanState);
break;
case CHANNEL_START:
updateState(channelUID, currentStartState);
break;
case CHANNEL_HOME:
updateState(channelUID, currentHomeState);
break;
case CHANNEL_BATTERY:
updateState(channelUID, currentBattery);
break;
case CHANNEL_CPU_LOAD:
updateState(channelUID, currentCPULoad);
break;
case CHANNEL_SRV_MEM:
updateState(channelUID, StringType.valueOf(currentSrvMem));
break;
case CHANNEL_TURBO:
updateState(channelUID, currentTurbo);
break;
case CHANNEL_REPEAT:
updateState(channelUID, currentRepeat);
break;
case CHANNEL_MODE:
updateState(channelUID, StringType.valueOf(currentMode));
break;
case CHANNEL_NICKNAME:
updateState(channelUID, StringType.valueOf(currentNickname));
break;
case CHANNEL_CAMERA:
parseImage();
updateState(channelUID, currentImage);
break;
case CHANNEL_LAST_CLEAN:
updateState(channelUID, currentLastClean);
break;
case CHANNEL_MAP:
parseMap();
updateState(channelUID, currentMap);
break;
case CHANNEL_MONDAY:
updateState(channelUID, StringType.valueOf(currentMonday));
refreshSchedule = true;
break;
case CHANNEL_TUESDAY:
updateState(channelUID, StringType.valueOf(currentTuesday));
refreshSchedule = true;
break;
case CHANNEL_WEDNESDAY:
updateState(channelUID, StringType.valueOf(currentWednesday));
refreshSchedule = true;
break;
case CHANNEL_THURSDAY:
updateState(channelUID, StringType.valueOf(currentThursday));
refreshSchedule = true;
break;
case CHANNEL_FRIDAY:
updateState(channelUID, StringType.valueOf(currentFriday));
refreshSchedule = true;
break;
case CHANNEL_SATURDAY:
updateState(channelUID, StringType.valueOf(currentSaturday));
refreshSchedule = true;
break;
case CHANNEL_SUNDAY:
updateState(channelUID, StringType.valueOf(currentSunday));
refreshSchedule = true;
break;
default:
logger.warn("Channel refresh for {} not implemented!", channelUID.getId());
}
}
private void updateAllChannels() {
if (disposed) {
return;
}
if (refreshSchedule) {
refreshSchedule = false;
fetchSchedule();
return;
}
String status = sendCommand("/status.txt");
if (status != null && !status.isEmpty()) {
boolean parsingOk = true;
String[] rows = status.split("\\r?\\n");
for (String row : rows) {
int idx = row.indexOf('=');
if (idx == -1) {
continue;
}
final String key = row.substring(0, idx);
String value = row.substring(idx + 1).replace("\"", "");
switch (key) {
case "JSON_ROBOT_STATE":
if (value.isEmpty()) {
value = HBSTATE_UNKNOWN;
}
if (!value.equals(currentState)) {
currentState = value;
updateState(CHANNEL_STATE, StringType.valueOf(value));
switch (value) {
case HBSTATE_WORKING:
case HBSTATE_BACKMOVING:
case HBSTATE_BACKMOVING_INIT:
currentCleanState = OnOffType.ON;
currentStartState = OnOffType.ON;
currentHomeState = OnOffType.OFF;
break;
case HBSTATE_HOMING:
case HBSTATE_DOCKING:
currentCleanState = OnOffType.OFF;
currentStartState = OnOffType.OFF;
currentHomeState = OnOffType.ON;
break;
default:
currentCleanState = OnOffType.OFF;
currentStartState = OnOffType.OFF;
currentHomeState = OnOffType.OFF;
break;
}
updateState(CHANNEL_CLEAN, currentCleanState);
updateState(CHANNEL_START, currentStartState);
updateState(CHANNEL_HOME, currentHomeState);
}
break;
case "JSON_BATTPERC":
try {
DecimalType battery = DecimalType.valueOf(value);
if (!battery.equals(currentBattery)) {
currentBattery = battery;
updateState(CHANNEL_BATTERY, battery);
}
} catch (NumberFormatException e) {
logger.debug("Couldn't parse Battery Percent.");
parsingOk = false;
}
break;
case "CPU_IDLE":
if (isLinked(CHANNEL_CPU_LOAD)) {
try {
DecimalType cpuLoad = new DecimalType(100 - Double.valueOf(value).longValue());
if (!cpuLoad.equals(currentCPULoad)) {
currentCPULoad = cpuLoad;
updateState(CHANNEL_CPU_LOAD, cpuLoad);
}
} catch (NumberFormatException e) {
logger.debug("Couldn't parse CPU Idle.");
parsingOk = false;
}
}
break;
case "LGSRV_MEMUSAGE":
if (!value.equals(currentSrvMem)) {
currentSrvMem = value;
updateState(CHANNEL_SRV_MEM, StringType.valueOf(value));
}
break;
case "JSON_TURBO":
OnOffType turbo = OnOffType.from("true".equalsIgnoreCase(value));
if (!turbo.equals(currentTurbo)) {
currentTurbo = turbo;
updateState(CHANNEL_TURBO, turbo);
}
break;
case "JSON_REPEAT":
OnOffType repeat = OnOffType.from("true".equalsIgnoreCase(value));
if (!repeat.equals(currentRepeat)) {
currentRepeat = repeat;
updateState(CHANNEL_REPEAT, repeat);
}
break;
case "JSON_MODE":
if (!value.equals(currentMode)) {
currentMode = value;
updateState(CHANNEL_MODE, StringType.valueOf(value));
}
break;
case "JSON_NICKNAME":
if (!value.equals(currentNickname)) {
currentNickname = value;
updateState(CHANNEL_NICKNAME, StringType.valueOf(value));
}
break;
case "CLREC_LAST_CLEAN":
if (value.length() < 19) {
logger.debug("Couldn't parse Last Clean from: String length: {}", value.length());
parsingOk = false;
break;
}
final String stringDate = value.substring(0, 19);
try {
LocalDateTime localDateTime = LocalDateTime.parse(stringDate, formatterLG);
ZonedDateTime date = ZonedDateTime.of(localDateTime, ZoneId.systemDefault());
DateTimeType lastClean = new DateTimeType(date);
if (!lastClean.equals(currentLastClean)) {
currentLastClean = lastClean;
updateState(CHANNEL_LAST_CLEAN, lastClean);
}
} catch (DateTimeException e) {
logger.debug("Couldn't parse Last Clean from: {}", stringDate);
parsingOk = false;
}
break;
default:
break;
}
}
if (!parsingOk) {
logger.debug("Couldn't parse status response;\n {}", status);
}
}
}
private void fetchSchedule() {
String status = sendCommand("/.../usr/data/htdocs/timer.txt");
if (status != null && !status.isEmpty()) {
String monday = "";
String tuesday = "";
String wednesday = "";
String thursday = "";
String friday = "";
String saturday = "";
String sunday = "";
String[] rows = status.split("\\r?\\n");
for (String row : rows) {
int idx = row.indexOf('=');
String name = row.substring(0, idx);
String state = row.substring(idx + 1);
switch (name) {
case "MONDAY":
monday = state;
break;
case "TUESDAY":
tuesday = state;
break;
case "WEDNESDAY":
wednesday = state;
break;
case "THURSDAY":
thursday = state;
break;
case "FRIDAY":
friday = state;
break;
case "SATURDAY":
saturday = state;
break;
case "SUNDAY":
sunday = state;
break;
default:
break;
}
}
if (!currentMonday.equals(monday)) {
currentMonday = monday;
updateState(CHANNEL_MONDAY, StringType.valueOf(monday));
}
if (!currentTuesday.equals(tuesday)) {
currentTuesday = tuesday;
updateState(CHANNEL_TUESDAY, StringType.valueOf(tuesday));
}
if (!currentWednesday.equals(wednesday)) {
currentWednesday = wednesday;
updateState(CHANNEL_WEDNESDAY, StringType.valueOf(wednesday));
}
if (!currentThursday.equals(thursday)) {
currentThursday = thursday;
updateState(CHANNEL_THURSDAY, StringType.valueOf(thursday));
}
if (!currentFriday.equals(friday)) {
currentFriday = friday;
updateState(CHANNEL_FRIDAY, StringType.valueOf(friday));
}
if (!currentSaturday.equals(saturday)) {
currentSaturday = saturday;
updateState(CHANNEL_SATURDAY, StringType.valueOf(saturday));
}
if (!currentSunday.equals(sunday)) {
currentSunday = sunday;
updateState(CHANNEL_SUNDAY, StringType.valueOf(sunday));
}
}
}
private void parseImage() {
if (!isLinked(CHANNEL_CAMERA)) {
return;
}
final int width = 320;
final int height = 240;
final int size = width * height;
String url = buildHttpAddress("/images/snapshot.yuv");
RawType rawData = HttpUtil.downloadData(url, null, false, size * 2);
if (rawData != null) {
byte[] yuvData = rawData.getBytes();
currentImage = CameraUtil.parseImageFromBytes(yuvData, width, height);
} else {
logger.info("No camera image returned from HomBot.");
}
}
/** Parse the maps.html file to find the black-box filename. */
private String findBlackBoxFile() {
String url = buildHttpAddress("/sites/maps.html");
try {
String htmlString = HttpUtil.executeUrl("GET", url, 1000);
int idx = htmlString.indexOf("blkfiles");
return "/.../usr/data/blackbox/" + htmlString.substring(idx + 13, idx + 50);
} catch (IOException e1) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e1.getMessage());
}
return "";
}
private void parseMap() {
if (!isLinked(CHANNEL_MAP)) {
return;
}
final int tileSize = 10;
final int tileArea = tileSize * tileSize;
final int rowLength = 100;
final int scale = 1;
String blackBox = findBlackBoxFile();
String url = buildHttpAddress(blackBox);
RawType dlData = HttpUtil.downloadData(url, null, false, -1);
if (dlData == null) {
return;
}
byte[] mapData = dlData.getBytes();
final int tileCount = mapData[32];
int maxX = 0;
int maxY = 0;
int minX = 0x10000;
int minY = 0x10000;
int pixPos;
for (int i = 0; i < tileCount; i++) {
pixPos = (mapData[52 + i * 16] & 0xFF) + (mapData[52 + 1 + i * 16] << 8);
int xPos = (pixPos % rowLength) * tileSize;
int yPos = (pixPos / rowLength) * tileSize;
if (xPos < minX) {
minX = xPos;
}
if (xPos > maxX) {
maxX = xPos;
}
if (yPos > maxY) {
maxY = yPos;
}
if (yPos < minY) {
minY = yPos;
}
}
final int width = (tileSize + maxX - minX) * scale;
final int height = (tileSize + maxY - minY) * scale;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
image.setRGB(j, i, 0xFFFFFF);
}
}
for (int i = 0; i < tileCount; i++) {
pixPos = (mapData[52 + i * 16] & 0xFF) + (mapData[52 + 1 + i * 16] << 8);
int xPos = ((pixPos % rowLength) * tileSize - minX) * scale;
int yPos = (maxY - (pixPos / rowLength) * tileSize) * scale;
int indexTab = 16044 + i * tileArea;
for (int j = 0; j < tileSize; j++) {
for (int k = 0; k < tileSize; k++) {
int p = 0xFFFFFF;
if ((mapData[indexTab] & 0xF0) != 0) {
p = 0xFF0000;
} else if (mapData[indexTab] != 0) {
p = 0xBFBFBF;
}
image.setRGB(xPos + k * scale, yPos + (9 - j) * scale, p);
indexTab++;
}
}
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (!ImageIO.write(image, "png", baos)) {
logger.debug("Couldn't find PNG writer.");
}
} catch (IOException e) {
logger.info("IOException creating PNG image.", e);
}
byte[] byteArray = baos.toByteArray();
if (byteArray != null && byteArray.length > 0) {
currentMap = new RawType(byteArray, "image/png");
} else {
currentMap = UnDefType.UNDEF;
}
}
}

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2010-2020 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.lghombot.internal;
import static org.openhab.binding.lghombot.internal.LGHomBotBindingConstants.THING_TYPE_LGHOMBOT;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
/**
* The {@link LGHomBotHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Fredrik Ahlström - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.lghombot", service = ThingHandlerFactory.class)
public class LGHomBotHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_LGHOMBOT);
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_LGHOMBOT.equals(thingTypeUID)) {
return new LGHomBotHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,288 @@
/**
* Copyright (c) 2010-2020 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.lghombot.internal.discovery;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.lghombot.internal.LGHomBotBindingConstants;
import org.openhab.binding.lghombot.internal.LGHomBotConfiguration;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.net.CidrAddress;
import org.openhab.core.net.NetUtil;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Discovery class for the LG HomBot line. Right now we try to do http requests to all IPs on port 6260.
* If we get a connection and correct answer we set the IP as result.
*
* @author Fredrik Ahlström - Initial contribution
*/
@NonNullByDefault
@Component(service = { DiscoveryService.class, LGHomBotDiscovery.class }, configurationPid = "discovery.lghombot")
public class LGHomBotDiscovery extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(LGHomBotDiscovery.class);
/**
* HTTP read timeout (in milliseconds) - allows us to shutdown the listening every TIMEOUT
*/
private static final int TIMEOUT_MS = 500;
/**
* Timeout in seconds of the complete scan
*/
private static final int FULL_SCAN_TIMEOUT_SECONDS = 30;
/**
* Total number of concurrent threads during scanning.
*/
private static final int SCAN_THREADS = 10;
/**
* Whether we are currently scanning or not
*/
private boolean scanning;
private int octet;
private int ipMask;
private int addressCount;
private @Nullable CidrAddress baseIp;
/**
* The {@link ExecutorService} to run the listening threads on.
*/
private @Nullable ExecutorService executorService;
/**
* Constructs the discovery class using the thing IDs that we can discover.
*/
public LGHomBotDiscovery() {
super(LGHomBotBindingConstants.SUPPORTED_THING_TYPES_UIDS, FULL_SCAN_TIMEOUT_SECONDS, false);
}
private void setupBaseIp(CidrAddress adr) {
byte[] octets = adr.getAddress().getAddress();
addressCount = (1 << (32 - adr.getPrefix())) - 2;
ipMask = 0xFFFFFFFF << (32 - adr.getPrefix());
octets[0] &= ipMask >> 24;
octets[1] &= ipMask >> 16;
octets[2] &= ipMask >> 8;
octets[3] &= ipMask;
try {
InetAddress iAdr = InetAddress.getByAddress(octets);
baseIp = new CidrAddress(iAdr, (short) adr.getPrefix());
} catch (UnknownHostException e) {
logger.debug("Could not build net ip address.", e);
}
octet = 0;
}
private synchronized String getNextIPAddress(CidrAddress adr) {
octet++;
octet &= ~ipMask;
byte[] octets = adr.getAddress().getAddress();
octets[2] += (octet >> 8);
octets[3] += octet;
String address = "";
try {
InetAddress iAdr = null;
iAdr = InetAddress.getByAddress(octets);
address = iAdr.getHostAddress();
} catch (UnknownHostException e) {
logger.debug("Could not find next ip address.", e);
}
return address;
}
/**
* {@inheritDoc}
*
* Starts the scan. This discovery will:
* <ul>
* <li>Request this hosts first IPV4 address.</li>
* <li>Send a HTTP request on port 6260 to all IPs on the subnet.</li>
* <li>The response is then investigated to see if is an answer from a HomBot lg.srv</li>
* </ul>
* The process will continue until all addresses are checked, timeout or {@link #stopScan()} is called.
*/
@Override
protected void startScan() {
if (executorService != null) {
stopScan();
}
CidrAddress localAdr = getLocalIP4Address();
if (localAdr == null) {
stopScan();
return;
}
setupBaseIp(localAdr);
CidrAddress baseAdr = baseIp;
scanning = true;
ExecutorService localExecutorService = Executors.newFixedThreadPool(SCAN_THREADS);
executorService = localExecutorService;
for (int i = 0; i < addressCount; i++) {
localExecutorService.execute(() -> {
if (scanning && baseAdr != null) {
String ipAdd = getNextIPAddress(baseAdr);
String url = "http://" + ipAdd + ":" + LGHomBotBindingConstants.DEFAULT_HOMBOT_PORT + "/status.txt";
try {
String message = HttpUtil.executeUrl("GET", url, TIMEOUT_MS);
if (message != null && !message.isEmpty()) {
messageReceive(message, ipAdd);
}
} catch (IOException e) {
// Ignore, this is the expected behavior.
}
}
});
}
}
/**
* Tries to find valid IP4 address.
*
* @return An IP4 address or null if none is found.
*/
private @Nullable CidrAddress getLocalIP4Address() {
List<CidrAddress> adrList = NetUtil.getAllInterfaceAddresses().stream()
.filter(a -> a.getAddress() instanceof Inet4Address).collect(Collectors.toList());
for (CidrAddress adr : adrList) {
// Don't return a "fake" DHCP lease.
if (!adr.toString().startsWith("169.254.")) {
return adr;
}
}
return null;
}
/**
* lgsrv message has the following format
*
* <pre>
* JSON_ROBOT_STATE="CHARGING"
* JSON_BATTPERC="100"
* LGSRV_VERSION="lg.srv, V2.51 compiled 18.11.2016, by fx2"
* LGSRV_SUMCMD="0"
* LGSRV_SUMCMDSEC="0.000000"
* LGSRV_NUMHTTP="929"
* LGSRV_MEMUSAGE="0.387 MB"
* CPU_IDLE="67.92"
* CPU_USER="19.49"
* CPU_SYS="12.57"
* CPU_NICE="0.00"
* JSON_TURBO="false"
* JSON_REPEAT="false"
* JSON_MODE="ZZ"
* JSON_VERSION="16552"
* JSON_NICKNAME="HOMBOT"
* CLREC_CURRENTBUMPING="29441"
* CLREC_LAST_CLEAN="2018/08/30/11/00/00.826531"
* </pre>
*
* First parse the first string to see that it's a HomBot, then parse nickname, server version & firmware version.
* We then create our thing from it.
*
* @param message a response from a lgsrv to be parsed
* @param ipAddress current probed ip address
*/
private void messageReceive(String message, String ipAddress) {
if (!message.startsWith("JSON_ROBOT_STATE=")) {
return;
}
String model = "HomBot";
String nickName = "";
String srvVersion = "0";
String fwVersion = "0";
for (String msg : message.split("\\r?\\n")) {
int idx = msg.indexOf('=');
if (idx > 0) {
String name = msg.substring(0, idx);
if (name.equalsIgnoreCase("JSON_NICKNAME")) {
nickName = msg.substring(idx + 1).trim().replaceAll("\"", "");
} else if (name.equalsIgnoreCase("JSON_VERSION")) {
fwVersion = msg.substring(idx + 1).trim().replaceAll("\"", "");
} else if (name.equalsIgnoreCase("LGSRV_VERSION")) {
srvVersion = msg.substring(idx + 1).trim().replaceAll("\"", "");
}
}
}
if (!ipAddress.isEmpty()) {
if (nickName.isEmpty()) {
nickName = "HOMBOT1";
}
ThingTypeUID typeId = LGHomBotBindingConstants.THING_TYPE_LGHOMBOT;
ThingUID uid = new ThingUID(typeId, nickName);
Map<String, Object> properties = new HashMap<>(3);
properties.put(LGHomBotConfiguration.IP_ADDRESS, ipAddress);
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, fwVersion);
properties.put("server", srvVersion);
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withProperties(properties)
.withLabel(model + " (" + nickName + ")").build();
thingDiscovered(result);
}
}
/**
* {@inheritDoc}
*
* Stops the discovery scan. We set {@link #scanning} to false (allowing the listening threads to end naturally
* within {@link #TIMEOUT_MS) * {@link #SCAN_THREADS} time then shutdown the {@link #executorService}
*/
@Override
protected synchronized void stopScan() {
super.stopScan();
ExecutorService localExecutorService = executorService;
if (localExecutorService != null) {
scanning = false;
try {
localExecutorService.awaitTermination(TIMEOUT_MS * SCAN_THREADS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
logger.debug("Stop scan interrupted.", e);
}
localExecutorService.shutdown();
executorService = null;
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="lghombot" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>LG HomBot Binding</name>
<description>The LG HomBot binding allows control and supervision of your HomBot.</description>
<author>Fredrik Ahlström</author>
</binding:binding>

View File

@@ -0,0 +1,85 @@
# binding
binding.lghombot.name = LG HomBot Binding
binding.lghombot.description = The LG HomBot binding allows control and supervision of your HomBot.
# thing types
thing-type.lghombot.LGHomBot.label = LG HomBot
thing-type.lghombot.LGHomBot.description = HomBot vacuum robot
# thing type configuration
thing-type.config.lghombot.LGHomBot.ipAddress.label = Network Address
thing-type.config.lghombot.LGHomBot.ipAddress.description = The IP or host name of the HomBot.
thing-type.config.lghombot.LGHomBot.port.label = Port
thing-type.config.lghombot.LGHomBot.port.description = Port of the HomBot to control.
thing-type.config.lghombot.LGHomBot.pollingPeriod.label = Polling Period
thing-type.config.lghombot.LGHomBot.pollingPeriod.description = Time between polls in seconds.
# channel types
channel-type.lghombot.stateType.label = State
channel-type.lghombot.stateType.description = Current state.
channel-type.lghombot.stateType.state.option.UNKNOWN = Unknown
channel-type.lghombot.stateType.state.option.WORKING = Cleaning
channel-type.lghombot.stateType.state.option.BACKMOVING = Backmoving
channel-type.lghombot.stateType.state.option.BACKMOVING_INIT = Backmoving init
channel-type.lghombot.stateType.state.option.BACKMOVING_JOY = Backmoving joy
channel-type.lghombot.stateType.state.option.PAUSE = Paused
channel-type.lghombot.stateType.state.option.STANDBY = Standby
channel-type.lghombot.stateType.state.option.HOMING = Homing
channel-type.lghombot.stateType.state.option.DOCKING = Docking
channel-type.lghombot.stateType.state.option.CHARGING = Charging
channel-type.lghombot.stateType.state.option.DIAGNOSIS = Running diagnosis
channel-type.lghombot.stateType.state.option.RESERVATION = Changing settings
channel-type.lghombot.stateType.state.option.ERROR = Error
channel-type.lghombot.batteryType.label = Battery
channel-type.lghombot.batteryType.description = Current battery charge
channel-type.lghombot.cpuLoadType.label = CPU Load
channel-type.lghombot.cpuLoadType.description = Current CPU load
channel-type.lghombot.srvMemType.label = Used Memory
channel-type.lghombot.srvMemType.description = Memory used by webserver on HomBot.
channel-type.lghombot.cleanType.label = Clean
channel-type.lghombot.cleanType.description = Start cleaning / return home.
channel-type.lghombot.startType.label = Start
channel-type.lghombot.startType.description = Start cleaning.
channel-type.lghombot.homeType.label = Home
channel-type.lghombot.homeType.description = Return home.
channel-type.lghombot.stopType.label = Pause
channel-type.lghombot.stopType.description = Pause the HomBot.
channel-type.lghombot.turboType.label = Turbo
channel-type.lghombot.turboType.description = Turbo mode ON, OFF.
channel-type.lghombot.repeatType.label = Repeat
channel-type.lghombot.repeatType.description = Repeat cleaning ON, OFF.
channel-type.lghombot.modeType.label = Mode
channel-type.lghombot.modeType.description = Cleaning mode.
channel-type.lghombot.modeType.state.option.ZZ = Zigzag mode
channel-type.lghombot.modeType.state.option.SB = Cell by cell mode
channel-type.lghombot.modeType.state.option.SPOT = Spiral spot mode
channel-type.lghombot.modeType.state.option.MACRO_SECTOR = My space mode
channel-type.lghombot.nicknameType.label = Nickname
channel-type.lghombot.nicknameType.description = Nickname of the HomBot.
channel-type.lghombot.moveType.label = Move
channel-type.lghombot.moveType.description = Move direction.
channel-type.lghombot.cameraType.label = Camera
channel-type.lghombot.cameraType.description = Image feed from the top camera.
channel-type.lghombot.lastCleanType.label = Last Clean
channel-type.lghombot.lastCleanType.description = Last time the HomBot cleaned.
channel-type.lghombot.mapType.label = Map
channel-type.lghombot.mapType.description = Map of last cleaned area.
channel-type.lghombot.mondayType.label = Monday
channel-type.lghombot.mondayType.description = Scheduled start time on Monday.
channel-type.lghombot.tuesdayType.label = Tuesday
channel-type.lghombot.tuesdayType.description = Scheduled start time on Tuesday.
channel-type.lghombot.wednesdayType.label = Wednesday
channel-type.lghombot.wednesdayType.description = Scheduled start time on Wednesday.
channel-type.lghombot.thursdayType.label = Thursday
channel-type.lghombot.thursdayType.description = Scheduled start time on Thursday.
channel-type.lghombot.fridayType.label = Friday
channel-type.lghombot.fridayType.description = Scheduled start time on Friday.
channel-type.lghombot.saturdayType.label = Saturday
channel-type.lghombot.saturdayType.description = Scheduled start time on Saturday.
channel-type.lghombot.sundayType.label = Sunday
channel-type.lghombot.sundayType.description = Scheduled start time on Sunday.

View File

@@ -0,0 +1,86 @@
# binding
binding.lghombot.name = LG HomBot Binding
binding.lghombot.description = LG HomBot binding tillåter kontrol och övervakning av din HomBot.
# thing types
thing-type.lghombot.LGHomBot.label = LG HomBot
thing-type.lghombot.LGHomBot.description = HomBot Robotdamsugare
# thing type configuration
thing-type.config.lghombot.LGHomBot.ipAddress.label = Nätverksdress
thing-type.config.lghombot.LGHomBot.ipAddress.description = IP-adress eller namn på HomBot.
thing-type.config.lghombot.LGHomBot.port.label = Port
thing-type.config.lghombot.LGHomBot.port.description = Nätverksport på HomBot.
thing-type.config.lghombot.LGHomBot.pollingPeriod.label = Pollningsperiod
thing-type.config.lghombot.LGHomBot.pollingPeriod.description = Tid mellan pollningar i sekunder.
# channel types
channel-type.lghombot.stateType.label = Status
channel-type.lghombot.stateType.description = Nuvarande status.
channel-type.lghombot.stateType.state.option.UNKNOWN = Okänd
channel-type.lghombot.stateType.state.option.WORKING = Städar
channel-type.lghombot.stateType.state.option.BACKMOVING = Backar
channel-type.lghombot.stateType.state.option.BACKMOVING_INIT = Startar backning
channel-type.lghombot.stateType.state.option.BACKMOVING_JOY = Backar ur
channel-type.lghombot.stateType.state.option.PAUSE = Pausad
channel-type.lghombot.stateType.state.option.STANDBY = Väntar
channel-type.lghombot.stateType.state.option.HOMING = Åker hemåt
channel-type.lghombot.stateType.state.option.DOCKING = Dockar
channel-type.lghombot.stateType.state.option.CHARGING = Laddar
channel-type.lghombot.stateType.state.option.DIAGNOSIS = Kör diagnos
channel-type.lghombot.stateType.state.option.RESERVATION = Gör inställning
channel-type.lghombot.stateType.state.option.ERROR = Fel
channel-type.lghombot.batteryType.label = Batteri
channel-type.lghombot.batteryType.description = Nuvarande batteriladdning.
channel-type.lghombot.cpuLoadType.label = CPU belastning
channel-type.lghombot.cpuLoadType.description = Nuvarande CPU belastning.
channel-type.lghombot.srvMemType.label = Använt minne
channel-type.lghombot.srvMemType.description = Minne använt av webserver på HomBot.
channel-type.lghombot.cleanType.label = Städa
channel-type.lghombot.cleanType.description = Börja städa / återvänd hem.
channel-type.lghombot.startType.label = Starta
channel-type.lghombot.startType.description = Börja städa.
channel-type.lghombot.homeType.label = Hemåt
channel-type.lghombot.homeType.description = Återvänd hem.
channel-type.lghombot.stopType.label = Paus
channel-type.lghombot.stopType.description = Pausa HomBot.
channel-type.lghombot.turboType.label = Turbo
channel-type.lghombot.turboType.description = Turbo läge AV, PÅ.
channel-type.lghombot.repeatType.label = Återupprepa
channel-type.lghombot.repeatType.description = Återupprepande städning AV, PÅ.
channel-type.lghombot.modeType.label = Städläge
channel-type.lghombot.modeType.description = Städläge.
channel-type.lghombot.modeType.state.option.ZZ = Sicksack läge
channel-type.lghombot.modeType.state.option.SB = Cell vid cell läge
channel-type.lghombot.modeType.state.option.SPOT = Punktspiral läge
channel-type.lghombot.modeType.state.option.MACRO_SECTOR = Mitt utrymme läge
channel-type.lghombot.nicknameType.label = Smeknamn
channel-type.lghombot.nicknameType.description = Smeknamn på HomBot.
channel-type.lghombot.moveType.label = Styr
channel-type.lghombot.moveType.description = Styr riktning.
channel-type.lghombot.cameraType.label = Kamera
channel-type.lghombot.cameraType.description = Bild från toppkameran.
channel-type.lghombot.lastCleanType.label = Senaste städning
channel-type.lghombot.lastCleanType.description = Senaste tillfälle som HomBot städade.
channel-type.lghombot.mapType.label = Karta
channel-type.lghombot.mapType.description = Karta över senaste städade arean.
channel-type.lghombot.mondayType.label = Måndag
channel-type.lghombot.mondayType.description = Schemalagd tid på Måndag.
channel-type.lghombot.tuesdayType.label = Tisdag
channel-type.lghombot.tuesdayType.description = Schemalagd tid på Tisdag.
channel-type.lghombot.wednesdayType.label = Onsdag
channel-type.lghombot.wednesdayType.description = Schemalagd tid på Onsdag.
channel-type.lghombot.thursdayType.label = Torsdag
channel-type.lghombot.thursdayType.description = Schemalagd tid på Torsdag.
channel-type.lghombot.fridayType.label = Fredag
channel-type.lghombot.fridayType.description = Schemalagd tid på Fredag.
channel-type.lghombot.saturdayType.label = Lördag
channel-type.lghombot.saturdayType.description = Schemalagd tid på Lördag.
channel-type.lghombot.sundayType.label = Söndag
channel-type.lghombot.sundayType.description = Schemalagd tid på Söndag.

View File

@@ -0,0 +1,239 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="lghombot"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="LGHomBot">
<label>LG HomBot</label>
<description>HomBot vacuum robot.</description>
<category>CleaningRobot</category>
<channels>
<channel id="state" typeId="stateType"/>
<channel id="battery" typeId="batteryType"/>
<channel id="cpuLoad" typeId="cpuLoadType"/>
<channel id="srvMem" typeId="srvMemType"/>
<channel id="clean" typeId="cleanType"/>
<channel id="start" typeId="startType"/>
<channel id="home" typeId="homeType"/>
<channel id="pause" typeId="pauseType"/>
<channel id="turbo" typeId="turboType"/>
<channel id="repeat" typeId="repeatType"/>
<channel id="mode" typeId="modeType"/>
<channel id="nickname" typeId="nicknameType"/>
<channel id="move" typeId="moveType"/>
<channel id="camera" typeId="cameraType"/>
<channel id="lastClean" typeId="lastCleanType"/>
<channel id="map" typeId="mapType"/>
<channel id="monday" typeId="mondayType"/>
<channel id="tuesday" typeId="tuesdayType"/>
<channel id="wednesday" typeId="wednesdayType"/>
<channel id="thursday" typeId="thursdayType"/>
<channel id="friday" typeId="fridayType"/>
<channel id="saturday" typeId="saturdayType"/>
<channel id="sunday" typeId="sundayType"/>
</channels>
<properties>
<property name="vendor">LG</property>
<property name="modelId">HomBot</property>
</properties>
<representation-property>deviceId</representation-property>
<config-description>
<parameter name="ipAddress" type="text" required="true">
<label>Network Address</label>
<description>The IP or host name of the HomBot.</description>
<context>network-address</context>
</parameter>
<parameter name="port" type="integer" min="1000" max="65535">
<label>Port</label>
<description>Port of the HomBot to control.</description>
<default>6260</default>
<advanced>true</advanced>
</parameter>
<parameter name="pollingPeriod" type="integer" min="1" max="60" unit="s">
<label>Polling Period</label>
<description>Time between polls in seconds.</description>
<default>3</default>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
<channel-type id="stateType">
<item-type>String</item-type>
<label>State</label>
<description>Current state.</description>
<state pattern="%s" readOnly="true">
<options>
<option value="UNKNOWN">Unknown</option>
<option value="WORKING">Cleaning</option>
<option value="BACKMOVING">Backmoving</option>
<option value="BACKMOVING_INIT">Backmoving init</option>
<option value="BACKMOVING_JOY">Backmoving joy</option>
<option value="PAUSE">Pause</option>
<option value="STANDBY">Standby</option>
<option value="HOMING">Homing</option>
<option value="DOCKING">Docking</option>
<option value="CHARGING">Charging</option>
<option value="DIAGNOSIS">Running diagnosis</option>
<option value="RESERVATION">Changing settings</option>
<option value="ERROR">Error</option>
</options>
</state>
</channel-type>
<channel-type id="batteryType">
<item-type>Number</item-type>
<label>Battery</label>
<description>Current battery charge.</description>
<category>BatteryLevel</category>
<state pattern="%d%%" readOnly="true"/>
</channel-type>
<channel-type id="cpuLoadType" advanced="true">
<item-type>Number</item-type>
<label>CPU Load</label>
<description>Current CPU load.</description>
<state pattern="%d%%" readOnly="true"/>
</channel-type>
<channel-type id="srvMemType" advanced="true">
<item-type>String</item-type>
<label>Used Memory</label>
<description>Memory used by webserver on HomBot.</description>
<state pattern="%s" readOnly="true"/>
</channel-type>
<channel-type id="cleanType">
<item-type>Switch</item-type>
<label>Clean</label>
<description>Start cleaning / return home.</description>
<tags>
<tag>Switchable</tag>
</tags>
</channel-type>
<channel-type id="startType">
<item-type>Switch</item-type>
<label>Start</label>
<description>Start cleaning.</description>
</channel-type>
<channel-type id="homeType">
<item-type>Switch</item-type>
<label>Home</label>
<description>Return home.</description>
</channel-type>
<channel-type id="pauseType" advanced="true">
<item-type>Switch</item-type>
<label>Pause</label>
<description>Pause the HomBot.</description>
</channel-type>
<channel-type id="turboType">
<item-type>Switch</item-type>
<label>Turbo</label>
<description>Turbo mode ON, OFF.</description>
</channel-type>
<channel-type id="repeatType">
<item-type>Switch</item-type>
<label>Repeat</label>
<description>Repeat cleaning ON, OFF.</description>
</channel-type>
<channel-type id="modeType">
<item-type>String</item-type>
<label>Mode</label>
<description>Cleaning mode.</description>
<state>
<options>
<option value="ZZ">Zigzag mode</option>
<option value="SB">Cell by cell mode</option>
<option value="SPOT">Spiral spot mode</option>
<option value="MACRO_SECTOR">My space mode</option>
</options>
</state>
</channel-type>
<channel-type id="nicknameType">
<item-type>String</item-type>
<label>Nickname</label>
<description>Nickname of the HomBot.</description>
<state pattern="%s" readOnly="true"/>
</channel-type>
<channel-type id="moveType" advanced="true">
<item-type>String</item-type>
<label>Move</label>
<description>Move direction.</description>
<category>MoveControl</category>
<state>
<options>
<option value="FORWARD">Forward</option>
<option value="FORWARD_LEFT">Forward left</option>
<option value="FORWARD_RIGHT">Forward right</option>
<option value="LEFT">Left</option>
<option value="RIGHT">Right</option>
<option value="BACKWARD">Backward</option>
<option value="BACKWARD_LEFT">Backward left</option>
<option value="BACKWARD_RIGHT">Backward right</option>
<option value="RELEASE">Release</option>
</options>
</state>
</channel-type>
<channel-type id="cameraType">
<item-type>Image</item-type>
<label>Camera</label>
<description>Image feed from the top camera.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="lastCleanType">
<item-type>DateTime</item-type>
<label>Last Clean</label>
<description>Last time the HomBot cleaned.</description>
<state pattern="%1$tF %1$tR" readOnly="true"/>
</channel-type>
<channel-type id="mapType" advanced="true">
<item-type>Image</item-type>
<label>Cleaning map</label>
<description>Map of last cleaned area.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="mondayType" advanced="true">
<item-type>String</item-type>
<label>Monday</label>
<description>Scheduled start time on Monday.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="tuesdayType" advanced="true">
<item-type>String</item-type>
<label>Tuesday</label>
<description>Scheduled start time on Tuesday.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="wednesdayType" advanced="true">
<item-type>String</item-type>
<label>Wednesday</label>
<description>Scheduled start time on Wednesday.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="thursdayType" advanced="true">
<item-type>String</item-type>
<label>Thursday</label>
<description>Scheduled start time on Thursday.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="fridayType" advanced="true">
<item-type>String</item-type>
<label>Friday</label>
<description>Scheduled start time on Friday.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="saturdayType" advanced="true">
<item-type>String</item-type>
<label>Saturday</label>
<description>Scheduled start time on Saturday.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="sundayType" advanced="true">
<item-type>String</item-type>
<label>Sunday</label>
<description>Scheduled start time on Sunday.</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>