added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.rme/.classpath
Normal file
32
bundles/org.openhab.binding.rme/.classpath
Normal 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>
|
||||
23
bundles/org.openhab.binding.rme/.project
Normal file
23
bundles/org.openhab.binding.rme/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.rme</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>
|
||||
13
bundles/org.openhab.binding.rme/NOTICE
Normal file
13
bundles/org.openhab.binding.rme/NOTICE
Normal 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
|
||||
46
bundles/org.openhab.binding.rme/README.md
Normal file
46
bundles/org.openhab.binding.rme/README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# RME Binding
|
||||
|
||||
This binding is for the RME RainManager rain water pump and management system
|
||||
|
||||
## Supported Things
|
||||
|
||||
Manager
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The Thing configuration requires the name of the serial port that is used to connect the host with the RME unit
|
||||
|
||||
## Channels
|
||||
|
||||
All devices support some of the following channels:
|
||||
|
||||
| Channel Type ID | Item Type | Description | | |
|
||||
|-----------------|-----------|---------------------------------------------------------------------------|---|---|
|
||||
| waterlevel | Number | Indicates the % the cistern is filled with water | | |
|
||||
| mode | String | Indicates the operation mode of the RME Rain Manager, Automatic or Manual | | |
|
||||
| source | String | Indicates water source used to supply water, e.g cistern or city | | |
|
||||
| exitpump | Switch | Indicates whether the exit pump is switched on | | |
|
||||
| entrypump | Switch | Indicates whether the entry pump is switched on | | |
|
||||
|
||||
## Example
|
||||
|
||||
.things
|
||||
|
||||
```
|
||||
Thing rme:manager:m1 [ port="/dev/tty.usbserial-FTGVQA6D" ]
|
||||
```
|
||||
|
||||
.items
|
||||
|
||||
```
|
||||
Number RMECisternLevel "Water level [%.1f %%]"(rme) {channel="rme:manager:m1:waterlevel"}
|
||||
String RMEMode (rme) {channel="rme:manager:m1:mode"}
|
||||
String RMESource (rme) {channel="rme:manager:m1:source"}
|
||||
Switch RMEExitPump (rme) {channel="rme:manager:m1:exitpump"}
|
||||
Switch RMEEntryPump (rme) {channel="rme:manager:m1:entrypump"}
|
||||
Switch RMEWaterExchange (rme) {channel="rme:manager:m1:waterexchange"}
|
||||
Switch RMECisternSupply (rme) {channel="rme:manager:m1:cisternsupplyalarm"}
|
||||
Switch RMEOverflowAlarm (rme) {channel="rme:manager:m1:overflowalarm"}
|
||||
Switch RMECisternBlockedAlarm (rme) {channel="rme:manager:m1:cisternblockedalarm"}
|
||||
Switch RMEFilterCleaning(rme) {channel="rme:manager:m1:filtercleaning"}
|
||||
```
|
||||
17
bundles/org.openhab.binding.rme/pom.xml
Normal file
17
bundles/org.openhab.binding.rme/pom.xml
Normal 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/xsd/maven-4.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.rme</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: RME Binding</name>
|
||||
|
||||
</project>
|
||||
10
bundles/org.openhab.binding.rme/src/main/feature/feature.xml
Normal file
10
bundles/org.openhab.binding.rme/src/main/feature/feature.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.rme-${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-rme" description="RME Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-transport-serial</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.rme/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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.rme.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link RMEBinding} class defines common constants, which are used across
|
||||
* the whole binding.
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RMEBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "rme";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_MANAGER = new ThingTypeUID(BINDING_ID, "manager");
|
||||
|
||||
// List of all Channel ids
|
||||
public enum DataField {
|
||||
|
||||
LEVEL("waterlevel", 1),
|
||||
MODE("mode", 2),
|
||||
SOURCE("source", 3),
|
||||
EXITPUMP("exitpump", 4),
|
||||
ENTRYPUMP("entrypump", 5),
|
||||
WATEREXCHANGE("waterexchange", 6),
|
||||
CISTERNSUPPLYALARM("cisternsupplyalarm", 7),
|
||||
OVERFLOWALARM("overflowalarm", 8),
|
||||
CISTERNBLOCKEDALARM("cisternblockedalarm", 9),
|
||||
FILTERCLEANING("filtercleaning", 10);
|
||||
|
||||
private final String id;
|
||||
private final int number;
|
||||
|
||||
private DataField(final String id, final int number) {
|
||||
this.number = number;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(number);
|
||||
}
|
||||
|
||||
public int toNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public static DataField get(int valueSelectorNumber) throws IllegalArgumentException {
|
||||
for (DataField c : DataField.values()) {
|
||||
if (c.number == valueSelectorNumber) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a valid value selector");
|
||||
}
|
||||
|
||||
public static DataField get(String valueSelectorText) throws IllegalArgumentException {
|
||||
for (DataField c : DataField.values()) {
|
||||
if (c.id.equals(valueSelectorText)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a valid value selector");
|
||||
}
|
||||
|
||||
public String channelID() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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.rme.internal;
|
||||
|
||||
import static org.openhab.binding.rme.internal.RMEBindingConstants.THING_TYPE_MANAGER;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.rme.internal.handler.RMEThingHandler;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
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.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link RMEHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.rme")
|
||||
public class RMEHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_MANAGER);
|
||||
|
||||
private final SerialPortManager serialPortManager;
|
||||
|
||||
@Activate
|
||||
public RMEHandlerFactory(final @Reference SerialPortManager serialPortManager) {
|
||||
this.serialPortManager = serialPortManager;
|
||||
}
|
||||
|
||||
@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 (thingTypeUID.equals(THING_TYPE_MANAGER)) {
|
||||
return new RMEThingHandler(thing, serialPortManager);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* 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.rme.internal.handler;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.binding.rme.internal.RMEBindingConstants.DataField;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link RMEThingHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
public class RMEThingHandler extends SerialThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RMEThingHandler.class);
|
||||
|
||||
private static final StringType AUTOMATIC = new StringType("Automatic");
|
||||
private static final StringType CITY = new StringType("City");
|
||||
private static final StringType MANUAL = new StringType("Manual");
|
||||
private static final StringType RAIN = new StringType("Rain");
|
||||
|
||||
public RMEThingHandler(Thing thing, SerialPortManager serialPortManager) {
|
||||
super(thing, serialPortManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing RME handler.");
|
||||
|
||||
if (getConfig().get(BAUD_RATE) == null) {
|
||||
baud = 2400;
|
||||
} else {
|
||||
baud = (int) getConfig().get(BAUD_RATE);
|
||||
}
|
||||
|
||||
if (getConfig().get(BUFFER_SIZE) == null) {
|
||||
bufferSize = 1024;
|
||||
} else {
|
||||
bufferSize = (int) getConfig().get(BUFFER_SIZE);
|
||||
}
|
||||
|
||||
port = (String) getConfig().get(PORT);
|
||||
|
||||
sleep = 250;
|
||||
|
||||
interval = 5000;
|
||||
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.warn("The RME Rain Manager is a read-only device and can not handle commands");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataReceived(String receivedLine) {
|
||||
String line = StringUtils.chomp(receivedLine);
|
||||
|
||||
// little hack to overcome Locale limits of the RME Rain Manager
|
||||
// note to the attentive reader : should we add support for system
|
||||
// locale's in the Type classes? ;-)
|
||||
line = line.replace(",", ".");
|
||||
line = line.trim();
|
||||
|
||||
Pattern responsePattern = Pattern.compile("(.*);(0|1);(0|1);(0|1);(0|1);(0|1);(0|1);(0|1);(0|1);(0|1)");
|
||||
|
||||
try {
|
||||
logger.trace("Processing '{}'", line);
|
||||
|
||||
Matcher matcher = responsePattern.matcher(line);
|
||||
if (matcher.matches()) {
|
||||
for (int i = 1; i <= matcher.groupCount(); i++) {
|
||||
switch (DataField.get(i)) {
|
||||
case LEVEL: {
|
||||
DecimalType decimalType = new DecimalType(matcher.group(i));
|
||||
updateState(new ChannelUID(getThing().getUID(), DataField.get(i).channelID()), decimalType);
|
||||
break;
|
||||
}
|
||||
case MODE: {
|
||||
StringType stringType = null;
|
||||
if (matcher.group(i).equals("0")) {
|
||||
stringType = MANUAL;
|
||||
} else if (matcher.group(i).equals("1")) {
|
||||
stringType = AUTOMATIC;
|
||||
}
|
||||
if (stringType != null) {
|
||||
updateState(new ChannelUID(getThing().getUID(), DataField.get(i).channelID()),
|
||||
stringType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOURCE: {
|
||||
StringType stringType = null;
|
||||
if (matcher.group(i).equals("0")) {
|
||||
stringType = RAIN;
|
||||
} else if (matcher.group(i).equals("1")) {
|
||||
stringType = CITY;
|
||||
}
|
||||
if (stringType != null) {
|
||||
updateState(new ChannelUID(getThing().getUID(), DataField.get(i).channelID()),
|
||||
stringType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (matcher.group(i).equals("0")) {
|
||||
updateState(new ChannelUID(getThing().getUID(), DataField.get(i).channelID()),
|
||||
OnOffType.OFF);
|
||||
} else if (matcher.group(i).equals("1")) {
|
||||
updateState(new ChannelUID(getThing().getUID(), DataField.get(i).channelID()),
|
||||
OnOffType.ON);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while receiving data : '{}'", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
/**
|
||||
* 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.rme.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.TooManyListenersException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.openhab.core.io.transport.serial.PortInUseException;
|
||||
import org.openhab.core.io.transport.serial.SerialPort;
|
||||
import org.openhab.core.io.transport.serial.SerialPortEvent;
|
||||
import org.openhab.core.io.transport.serial.SerialPortEventListener;
|
||||
import org.openhab.core.io.transport.serial.SerialPortIdentifier;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
|
||||
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.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link SerialThingHandler} is responsible for handling commands, which
|
||||
* are sent to one of the channels. Thing Handler classes that use serial
|
||||
* communications can extend/implement this class, but must make sure they
|
||||
* supplement the configuration parameters into the {@link SerialConfiguration}
|
||||
* Configuration of the underlying Thing, if not already specified in the
|
||||
* thing.xml definition
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
public abstract class SerialThingHandler extends BaseThingHandler implements SerialPortEventListener {
|
||||
|
||||
// List of all Configuration parameters
|
||||
public static final String PORT = "port";
|
||||
public static final String BAUD_RATE = "baud";
|
||||
public static final String BUFFER_SIZE = "buffer";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SerialThingHandler.class);
|
||||
|
||||
private SerialPort serialPort;
|
||||
private SerialPortIdentifier portId;
|
||||
private final SerialPortManager serialPortManager;
|
||||
private InputStream inputStream;
|
||||
private OutputStream outputStream;
|
||||
protected int baud;
|
||||
protected String port;
|
||||
protected int bufferSize;
|
||||
protected long sleep = 100;
|
||||
protected long interval = 0;
|
||||
private Thread readerThread = null;
|
||||
|
||||
public SerialThingHandler(Thing thing, SerialPortManager serialPortManager) {
|
||||
super(thing);
|
||||
this.serialPortManager = serialPortManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when data is received on the serial port
|
||||
*
|
||||
* @param line the received data as a String
|
||||
*
|
||||
**/
|
||||
public abstract void onDataReceived(String line);
|
||||
|
||||
/**
|
||||
* Write data to the serial port
|
||||
*
|
||||
* @param msg the received data as a String
|
||||
*
|
||||
**/
|
||||
public void writeString(String msg) {
|
||||
String port = (String) this.getConfig().get(PORT);
|
||||
|
||||
try {
|
||||
// write string to serial port
|
||||
outputStream.write(msg.getBytes());
|
||||
outputStream.flush();
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Error writing '" + msg + "' to serial port " + port + " : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialEvent(SerialPortEvent arg0) {
|
||||
try {
|
||||
/*
|
||||
* The short select() timeout in the native code of the nrjavaserial lib does cause a high CPU load, despite
|
||||
* the fix published (see https://github.com/NeuronRobotics/nrjavaserial/issues/22). A workaround for this
|
||||
* problem is to (1) put the Thread initiated by the nrjavaserial library to sleep forever, so that the
|
||||
* number of calls to the select() function gets minimized, and (2) implement a Threaded streamreader
|
||||
* directly in java
|
||||
*/
|
||||
logger.trace("RXTX library CPU load workaround, sleep forever");
|
||||
Thread.sleep(Long.MAX_VALUE);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing serial thing handler.");
|
||||
if (serialPort != null) {
|
||||
serialPort.removeEventListener();
|
||||
}
|
||||
if (readerThread != null) {
|
||||
try {
|
||||
readerThread.interrupt();
|
||||
readerThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error while closing the input stream: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error while closing the output stream: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
if (serialPort != null) {
|
||||
serialPort.close();
|
||||
}
|
||||
readerThread = null;
|
||||
inputStream = null;
|
||||
outputStream = null;
|
||||
serialPort = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing serial thing handler.");
|
||||
|
||||
if (baud == 0) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Baud rate is not configured");
|
||||
return;
|
||||
} else if (port == null || port.isEmpty()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Serial port is not configured");
|
||||
return;
|
||||
}
|
||||
|
||||
portId = serialPortManager.getIdentifier(port);
|
||||
|
||||
if (portId == null) {
|
||||
String availablePorts = serialPortManager.getIdentifiers().map(id -> id.getName())
|
||||
.collect(Collectors.joining(System.lineSeparator()));
|
||||
String description = String.format("Serial port '%s' could not be found. Available ports are:%n%s", port,
|
||||
availablePorts);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, description);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// initialize serial port
|
||||
try {
|
||||
serialPort = portId.open("openHAB", 2000);
|
||||
} catch (PortInUseException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Could not open serial port " + port + ": " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
inputStream = serialPort.getInputStream();
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Could not open serial port " + port + ": " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
serialPort.addEventListener(this);
|
||||
} catch (TooManyListenersException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Could not open serial port " + port + ": " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// activate the DATA_AVAILABLE notifier
|
||||
serialPort.notifyOnDataAvailable(true);
|
||||
|
||||
try {
|
||||
// set port parameters
|
||||
serialPort.setSerialPortParams(baud, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
|
||||
} catch (UnsupportedCommOperationException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Could not configure serial port " + port + ": " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// get the output stream
|
||||
outputStream = serialPort.getOutputStream();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Could not communicate with the serial port " + port + ": " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
readerThread = new SerialPortReader(inputStream);
|
||||
readerThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// by default, we write anything we received as a string to the serial port
|
||||
writeString(command.toString());
|
||||
}
|
||||
|
||||
public class SerialPortReader extends Thread {
|
||||
|
||||
private static final byte LINE_FEED = (byte) '\n';
|
||||
private static final byte CARRIAGE_RETURN = (byte) '\r';
|
||||
|
||||
private boolean interrupted = false;
|
||||
private InputStream inputStream;
|
||||
private boolean hasInterval = interval == 0 ? false : true;
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss,SSS");
|
||||
|
||||
public SerialPortReader(InputStream in) {
|
||||
this.inputStream = in;
|
||||
this.setName("SerialPortReader-" + getThing().getUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt() {
|
||||
interrupted = true;
|
||||
super.interrupt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
byte[] dataBuffer = new byte[bufferSize];
|
||||
byte[] tmpData = new byte[bufferSize];
|
||||
int index = 0;
|
||||
int len = -1;
|
||||
boolean foundStart = false;
|
||||
|
||||
logger.debug("Serial port listener for serial port '{}' has started", port);
|
||||
|
||||
try {
|
||||
while (!interrupted) {
|
||||
long startOfRead = System.currentTimeMillis();
|
||||
|
||||
if ((len = inputStream.read(tmpData)) > 0) {
|
||||
foundStart = false;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (hasInterval && i > 0) {
|
||||
if (tmpData[i] != LINE_FEED && tmpData[i] != CARRIAGE_RETURN) {
|
||||
if (tmpData[i - 1] == LINE_FEED || tmpData[i - 1] == CARRIAGE_RETURN) {
|
||||
index = 0;
|
||||
foundStart = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tmpData[i] != LINE_FEED && tmpData[i] != CARRIAGE_RETURN) {
|
||||
dataBuffer[index++] = tmpData[i];
|
||||
}
|
||||
|
||||
if (tmpData[i] == LINE_FEED || tmpData[i] == CARRIAGE_RETURN) {
|
||||
if (index > 1) {
|
||||
if (hasInterval) {
|
||||
if (foundStart) {
|
||||
onDataReceived(new String(Arrays.copyOf(dataBuffer, index)));
|
||||
break;
|
||||
} else {
|
||||
index = 0;
|
||||
foundStart = true;
|
||||
}
|
||||
} else {
|
||||
onDataReceived(new String(Arrays.copyOf(dataBuffer, index)));
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (index == bufferSize) {
|
||||
if (!hasInterval) {
|
||||
onDataReceived(new String(Arrays.copyOf(dataBuffer, index)));
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasInterval) {
|
||||
try {
|
||||
Thread.sleep(Math.max(interval - (System.currentTimeMillis() - startOfRead), 0));
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(sleep);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
}
|
||||
} catch (InterruptedIOException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (IOException e) {
|
||||
logger.error("An exception occurred while reading serial port '{}' : {}", port, e.getMessage(), e);
|
||||
}
|
||||
|
||||
logger.debug("Serial port listener for serial port '{}' has stopped", port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="rme" 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>openHAB RME Binding</name>
|
||||
<description>This is the binding for RME Rain Manager.</description>
|
||||
<author>Karel Goderis</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="rme" 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="manager">
|
||||
<label>RME Rain Manager</label>
|
||||
<description>The RME Rain Manager is a controllable water supply unit that can monitor a gauge in a cistern, and
|
||||
switch between rain water and city provided water</description>
|
||||
|
||||
<channels>
|
||||
<channel id="waterlevel" typeId="waterlevel"/>
|
||||
<channel id="mode" typeId="mode"/>
|
||||
<channel id="source" typeId="source"/>
|
||||
<channel id="exitpump" typeId="statusflag">
|
||||
<label>Exit Pump Active</label>
|
||||
</channel>
|
||||
<channel id="entrypump" typeId="statusflag">
|
||||
<label>Entry Pump Active</label>
|
||||
</channel>
|
||||
<channel id="waterexchange" typeId="statusflag">
|
||||
<label>Water Exchange Active</label>
|
||||
</channel>
|
||||
<channel id="cisternsupplyalarm" typeId="statusflag">
|
||||
<label>Cistern Supply Alarm</label>
|
||||
</channel>
|
||||
<channel id="overflowalarm" typeId="statusflag">
|
||||
<label>Overflow Alarm</label>
|
||||
</channel>
|
||||
<channel id="cisternblockedalarm" typeId="statusflag">
|
||||
<label>Cistern Blocked Alarm</label>
|
||||
</channel>
|
||||
<channel id="filtercleaning" typeId="statusflag">
|
||||
<label>Filter Cleaning Required</label>
|
||||
</channel>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="port" type="text">
|
||||
<label>Serial Port</label>
|
||||
<context>serial-port</context>
|
||||
<limitToOptions>false</limitToOptions>
|
||||
<description>Serial Port the RME Rain Manager is connected to</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<!-- Water Level Channel Type -->
|
||||
<channel-type id="waterlevel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Water Level</label>
|
||||
<description>Indicates the % the cistern is filled with water</description>
|
||||
<state readOnly="true" pattern="%.1f %%"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- Operation Mode Channel Type -->
|
||||
<channel-type id="mode">
|
||||
<item-type>String</item-type>
|
||||
<label>Operation Mode</label>
|
||||
<description>Indicates the operation mode of the RME Rain Manager</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="Automatic">Automatic</option>
|
||||
<option value="Manual">Manual</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<!-- Operation Mode Channel Type -->
|
||||
<channel-type id="source">
|
||||
<item-type>String</item-type>
|
||||
<label>Water Source</label>
|
||||
<description>Indicates water source used to supply water, e.g cistern or city</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="Rain">Rain</option>
|
||||
<option value="City">City</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<!-- Status Flag Channel Type -->
|
||||
<channel-type id="statusflag" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Status Flag</label>
|
||||
<description>Status Flag of an RME operational parameter, e.g ON if set, OFF if unset</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="ON">ON</option>
|
||||
<option value="OFF">OFF</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user