mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[herzborg] Herzborg binding (#9327)
Supports Herzborg curtain motors over RS-485 network Signed-off-by: Pavel Fedin <pavel_fedin@mail.ru> Co-authored-by: Matthew Skinner <matt@pcmus.com> Co-authored-by: Fabian Wolter <github@fabian-wolter.de> Co-authored-by: Matthew Skinner <matt@pcmus.com> Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
This commit is contained in:
parent
8ebd4e9047
commit
7a407523dc
@ -120,6 +120,7 @@
|
||||
/bundles/org.openhab.binding.helios/ @kgoderis
|
||||
/bundles/org.openhab.binding.heliosventilation/ @ramack
|
||||
/bundles/org.openhab.binding.heos/ @Wire82
|
||||
/bundles/org.openhab.binding.herzborg/ @Sonic-Amiga
|
||||
/bundles/org.openhab.binding.homeconnect/ @bruestel
|
||||
/bundles/org.openhab.binding.homematic/ @FStolte @gerrieg @mdicke2s
|
||||
/bundles/org.openhab.binding.homewizard/ @Daniel-42
|
||||
|
@ -591,6 +591,11 @@
|
||||
<artifactId>org.openhab.binding.heos</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.herzborg</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.homeconnect</artifactId>
|
||||
|
13
bundles/org.openhab.binding.herzborg/NOTICE
Normal file
13
bundles/org.openhab.binding.herzborg/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
|
83
bundles/org.openhab.binding.herzborg/README.md
Normal file
83
bundles/org.openhab.binding.herzborg/README.md
Normal file
@ -0,0 +1,83 @@
|
||||
# Herzborg Binding
|
||||
|
||||
This binding supports smart curtain motors by Herzborg (http://www.herzborg.com/pro_list.aspx?TypeID=1)
|
||||
|
||||
## Supported Things
|
||||
|
||||
- `herzborg` A bridge thing that connects to a RS485 serial bus.
|
||||
- `curtain` A curtain motor thing that can be controlled via the `herzborg` bridge .
|
||||
|
||||
The binding was developed and tested using DT300TV-1.2/14 type motor; others are expected to be compatible
|
||||
|
||||
## Discovery
|
||||
|
||||
Due to nature of serial bus being used, no automatic discovery is possible.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
### Serial Bus Bridge (id "serial_bus")
|
||||
|
||||
| Parameter | Meaning |
|
||||
|-----------|---------------------------------------------------------|
|
||||
| port | Serial port name to use |
|
||||
|
||||
Herzborg devices appear to use fixed 9600 8n1 communication parameters, so no other parameters are needed
|
||||
|
||||
### Curtain Motor Thing (id "curtain")
|
||||
|
||||
| Parameter | Meaning |
|
||||
|---------------|---------------------------------------------------------|
|
||||
| address | Address of the motor on the serial bus. |
|
||||
| poll_interval | Polling interval in seconds |
|
||||
|
||||
## Channels
|
||||
|
||||
| channel | type | description | Read-only |
|
||||
|------------|---------------|-----------------------------------------------|-----------|
|
||||
| position | RollerShutter | Controls position of the curtain. Position reported back is in percents; 0 - fully closed; 100 - fully open | N |
|
||||
| mode | String | Reports current motor mode: | Y |
|
||||
| | | 0 - Stop | |
|
||||
| | | 1 - Open | |
|
||||
| | | 2 - Close | |
|
||||
| | | 3 - Setting | |
|
||||
| reverse | Switch | Reverses direction when switched on | N |
|
||||
| handStart | Switch | Enable / disable hand start function | N |
|
||||
| extSwitch | String | External (low-voltage) switch mode: | N |
|
||||
| | | 1 - dual channel biased switch | |
|
||||
| | | 2 - dual channel rocker switch | |
|
||||
| | | 3 - DC246 electronic switch | |
|
||||
| | | 4 - single button cyclic switch | |
|
||||
| hvSwitch | String | Main (high-voltage) switch mode: | N |
|
||||
| | | 0 - dual channel biased switch | |
|
||||
| | | 1 - hotel mode(power on while card in) | |
|
||||
| | | 2 - dual channel rocker switch | |
|
||||
|
||||
All the channels are read-write
|
||||
|
||||
## Example
|
||||
|
||||
herzborg.things:
|
||||
|
||||
```
|
||||
Bridge herzborg:serial_bus:my_herzborg_bus [ port="/dev/ttyAMA1" ]
|
||||
{
|
||||
Thing herzborg:curtain:livingroom [ address=1234, poll_interval=1 ]
|
||||
}
|
||||
```
|
||||
|
||||
herzborg.items:
|
||||
|
||||
```
|
||||
Rollershutter LivingRoom_Window {channel="herzborg:curtain:livingroom:position"}
|
||||
```
|
||||
|
||||
herzborg.sitemap:
|
||||
|
||||
```
|
||||
Frame label="Living room curtain"
|
||||
{
|
||||
Switch item=LivingRoom_Window label="Control" mappings=["DOWN"="Close", "STOP"="Stop", "UP"="Open"]
|
||||
Slider item=LivingRoom_Window label="Position [%d %%]" minValue=0 maxValue=100
|
||||
}
|
||||
|
||||
```
|
17
bundles/org.openhab.binding.herzborg/pom.xml
Normal file
17
bundles/org.openhab.binding.herzborg/pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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.3.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.herzborg</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Herzborg Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.herzborg-${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-herzborg" description="Herzborg 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.herzborg/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.herzborg.internal;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.herzborg.internal.dto.HerzborgProtocol.Function;
|
||||
import org.openhab.binding.herzborg.internal.dto.HerzborgProtocol.Packet;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link Bus} is a handy base class, implementing data communication with Herzborg devices.
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Bus {
|
||||
private final Logger logger = LoggerFactory.getLogger(Bus.class);
|
||||
|
||||
protected @Nullable InputStream dataIn;
|
||||
protected @Nullable OutputStream dataOut;
|
||||
|
||||
public static class Result {
|
||||
ThingStatusDetail code;
|
||||
@Nullable
|
||||
String message;
|
||||
|
||||
Result(ThingStatusDetail code, String msg) {
|
||||
this.code = code;
|
||||
this.message = msg;
|
||||
}
|
||||
|
||||
Result(ThingStatusDetail code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
public Bus() {
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
private void safeClose(@Nullable Closeable stream) {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error closing I/O stream: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
safeClose(dataOut);
|
||||
safeClose(dataIn);
|
||||
|
||||
dataOut = null;
|
||||
dataIn = null;
|
||||
}
|
||||
|
||||
public synchronized @Nullable Packet doPacket(Packet pkt) throws IOException {
|
||||
OutputStream dataOut = this.dataOut;
|
||||
InputStream dataIn = this.dataIn;
|
||||
|
||||
if (dataOut == null || dataIn == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int readLength = Packet.MIN_LENGTH;
|
||||
|
||||
switch (pkt.getFunction()) {
|
||||
case Function.READ:
|
||||
// The reply will include data itself
|
||||
readLength += pkt.getDataLength();
|
||||
break;
|
||||
case Function.WRITE:
|
||||
// The reply is number of bytes written
|
||||
readLength += 1;
|
||||
break;
|
||||
case Function.CONTROL:
|
||||
// The whole packet will be echoed back
|
||||
readLength = pkt.getBuffer().length;
|
||||
break;
|
||||
default:
|
||||
// We must not have anything else here
|
||||
throw new IllegalStateException("Unknown function code");
|
||||
}
|
||||
|
||||
dataOut.write(pkt.getBuffer());
|
||||
|
||||
int readOffset = 0;
|
||||
byte[] replyBuffer = new byte[readLength];
|
||||
|
||||
while (readLength > 0) {
|
||||
int n = dataIn.read(replyBuffer, readOffset, readLength);
|
||||
|
||||
if (n < 0) {
|
||||
throw new IOException("EOF from serial port");
|
||||
} else if (n == 0) {
|
||||
throw new IOException("Serial read timeout");
|
||||
}
|
||||
|
||||
readOffset += n;
|
||||
readLength -= n;
|
||||
}
|
||||
|
||||
return new Packet(replyBuffer);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
InputStream dataIn = this.dataIn;
|
||||
|
||||
if (dataIn != null) {
|
||||
// Unfortunately Java streams can't be flushed. Just read and drop all the characters
|
||||
while (dataIn.available() > 0) {
|
||||
dataIn.read();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.herzborg.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link BusHandler} is a handy base class, implementing data communication with Herzborg devices.
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class BusHandler extends BaseBridgeHandler {
|
||||
protected Bus bus;
|
||||
|
||||
public BusHandler(Bridge bridge, Bus bus) {
|
||||
super(bridge);
|
||||
this.bus = bus;
|
||||
}
|
||||
|
||||
public Bus getBus() {
|
||||
return bus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// Nothing to do here, but we have to implement it
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.herzborg.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link CurtainConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CurtainConfiguration {
|
||||
public int address;
|
||||
public int pollInterval;
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.herzborg.internal;
|
||||
|
||||
import static org.openhab.binding.herzborg.internal.HerzborgBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.herzborg.internal.dto.HerzborgProtocol.ControlAddress;
|
||||
import org.openhab.binding.herzborg.internal.dto.HerzborgProtocol.DataAddress;
|
||||
import org.openhab.binding.herzborg.internal.dto.HerzborgProtocol.Function;
|
||||
import org.openhab.binding.herzborg.internal.dto.HerzborgProtocol.Packet;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.StopMoveType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.types.UpDownType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
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.thing.binding.BridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link CurtainHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CurtainHandler extends BaseThingHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(CurtainHandler.class);
|
||||
|
||||
private CurtainConfiguration config = new CurtainConfiguration();
|
||||
private @Nullable ScheduledFuture<?> pollFuture;
|
||||
private @Nullable Bus bus;
|
||||
|
||||
public CurtainHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
String ch = channelUID.getId();
|
||||
Packet pkt = null;
|
||||
|
||||
switch (ch) {
|
||||
case CHANNEL_POSITION:
|
||||
if (command instanceof UpDownType) {
|
||||
pkt = buildPacket(Function.CONTROL,
|
||||
(command == UpDownType.UP) ? ControlAddress.OPEN : ControlAddress.CLOSE);
|
||||
} else if (command instanceof StopMoveType) {
|
||||
pkt = buildPacket(Function.CONTROL, ControlAddress.STOP);
|
||||
} else if (command instanceof DecimalType) {
|
||||
pkt = buildPacket(Function.CONTROL, ControlAddress.PERCENT, ((DecimalType) command).byteValue());
|
||||
}
|
||||
break;
|
||||
case CHANNEL_REVERSE:
|
||||
if (command instanceof OnOffType) {
|
||||
pkt = buildPacket(Function.WRITE, DataAddress.DEFAULT_DIR, command.equals(OnOffType.ON) ? 1 : 0);
|
||||
}
|
||||
break;
|
||||
case CHANNEL_HAND_START:
|
||||
if (command instanceof OnOffType) {
|
||||
pkt = buildPacket(Function.WRITE, DataAddress.HAND_START, command.equals(OnOffType.ON) ? 0 : 1);
|
||||
}
|
||||
break;
|
||||
case CHANNEL_EXT_SWITCH:
|
||||
if (command instanceof StringType) {
|
||||
pkt = buildPacket(Function.WRITE, DataAddress.EXT_SWITCH, Byte.valueOf(command.toString()));
|
||||
}
|
||||
break;
|
||||
case CHANNEL_HV_SWITCH:
|
||||
if (command instanceof StringType) {
|
||||
pkt = buildPacket(Function.WRITE, DataAddress.EXT_HV_SWITCH, Byte.valueOf(command.toString()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (pkt != null) {
|
||||
final Packet p = pkt;
|
||||
scheduler.schedule(() -> {
|
||||
Packet reply = doPacket(p);
|
||||
|
||||
if (reply != null) {
|
||||
logger.trace("Function {} addr {} reply {}", p.getFunction(), p.getDataAddress(),
|
||||
DatatypeConverter.printHexBinary(reply.getBuffer()));
|
||||
}
|
||||
}, 0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private Packet buildPacket(byte function, byte data_addr) {
|
||||
return new Packet((short) config.address, function, data_addr);
|
||||
}
|
||||
|
||||
private Packet buildPacket(byte function, byte data_addr, byte value) {
|
||||
return new Packet((short) config.address, function, data_addr, value);
|
||||
}
|
||||
|
||||
private Packet buildPacket(byte function, byte data_addr, int value) {
|
||||
return buildPacket(function, data_addr, (byte) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
Bridge bridge = getBridge();
|
||||
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, "Bridge not present");
|
||||
return;
|
||||
}
|
||||
|
||||
BridgeHandler handler = bridge.getHandler();
|
||||
|
||||
if (handler == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, "Bridge has no handler");
|
||||
return;
|
||||
}
|
||||
|
||||
bus = ((BusHandler) handler).getBus();
|
||||
config = getConfigAs(CurtainConfiguration.class);
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
logger.trace("Successfully initialized, starting poll");
|
||||
pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 1, config.pollInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
stopPoll();
|
||||
}
|
||||
|
||||
private void stopPoll() {
|
||||
ScheduledFuture<?> poll = pollFuture;
|
||||
pollFuture = null;
|
||||
|
||||
if (poll != null) {
|
||||
poll.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable synchronized Packet doPacket(Packet pkt) {
|
||||
Bus bus = this.bus;
|
||||
|
||||
if (bus == null) {
|
||||
// This is an impossible situation but Eclipse forces us to handle it
|
||||
logger.warn("No Bridge sending commands");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Packet reply = bus.doPacket(pkt);
|
||||
|
||||
if (reply == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (reply.isValid()) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
return reply;
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Invalid response received: " + DatatypeConverter.printHexBinary(reply.getBuffer()));
|
||||
bus.flush();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void poll() {
|
||||
Packet reply = doPacket(buildPacket(Function.READ, DataAddress.POSITION, 4));
|
||||
|
||||
if (reply != null) {
|
||||
byte position = reply.getData(0);
|
||||
byte reverse = reply.getData(1);
|
||||
byte handStart = reply.getData(2);
|
||||
byte mode = reply.getData(3);
|
||||
|
||||
// If calibration has been lost, position is reported as -1.
|
||||
updateState(CHANNEL_POSITION,
|
||||
(position > 100 || position < 0) ? UnDefType.UNDEF : new PercentType(position));
|
||||
updateState(CHANNEL_REVERSE, reverse != 0 ? OnOffType.ON : OnOffType.OFF);
|
||||
updateState(CHANNEL_HAND_START, handStart == 0 ? OnOffType.ON : OnOffType.OFF);
|
||||
updateState(CHANNEL_MODE, new StringType(String.valueOf(mode)));
|
||||
}
|
||||
|
||||
Packet extReply = doPacket(buildPacket(Function.READ, DataAddress.EXT_SWITCH, 2));
|
||||
|
||||
if (extReply != null) {
|
||||
byte extSwitch = extReply.getData(0);
|
||||
byte hvSwitch = extReply.getData(1);
|
||||
|
||||
updateState(CHANNEL_EXT_SWITCH, new StringType(String.valueOf(extSwitch)));
|
||||
updateState(CHANNEL_HV_SWITCH, new StringType(String.valueOf(hvSwitch)));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.herzborg.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link HerzborgBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HerzborgBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "herzborg";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_SERIAL_BUS = new ThingTypeUID(BINDING_ID, "serialBus");
|
||||
public static final ThingTypeUID THING_TYPE_CURTAIN = new ThingTypeUID(BINDING_ID, "curtain");
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_POSITION = "position";
|
||||
public static final String CHANNEL_MODE = "mode";
|
||||
public static final String CHANNEL_REVERSE = "reverse";
|
||||
public static final String CHANNEL_HAND_START = "handStart";
|
||||
public static final String CHANNEL_EXT_SWITCH = "extSwitch";
|
||||
public static final String CHANNEL_HV_SWITCH = "hvSwitch";
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.herzborg.internal;
|
||||
|
||||
import static org.openhab.binding.herzborg.internal.HerzborgBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
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 HerzborgHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.herzborg", service = ThingHandlerFactory.class)
|
||||
public class HerzborgHandlerFactory extends BaseThingHandlerFactory {
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_SERIAL_BUS,
|
||||
THING_TYPE_CURTAIN);
|
||||
|
||||
private final SerialPortManager serialPortManager;
|
||||
|
||||
@Activate
|
||||
public HerzborgHandlerFactory(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 (THING_TYPE_CURTAIN.equals(thingTypeUID)) {
|
||||
return new CurtainHandler(thing);
|
||||
} else if (THING_TYPE_SERIAL_BUS.equals(thingTypeUID)) {
|
||||
return new SerialBusHandler((Bridge) thing, serialPortManager);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.herzborg.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.io.transport.serial.PortInUseException;
|
||||
import org.openhab.core.io.transport.serial.SerialPort;
|
||||
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.ThingStatusDetail;
|
||||
|
||||
/**
|
||||
* The {@link SerialBus} implements specific handling for Herzborg serial bus,
|
||||
* connected directly via a serial port.
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SerialBus extends Bus {
|
||||
private SerialPortManager serialPortManager;
|
||||
private @Nullable SerialPort serialPort;
|
||||
|
||||
public SerialBus(SerialPortManager manager) {
|
||||
serialPortManager = manager;
|
||||
}
|
||||
|
||||
public Result initialize(@Nullable String port) {
|
||||
if (port == null) {
|
||||
return new Result(ThingStatusDetail.CONFIGURATION_ERROR, "Port is not specified");
|
||||
}
|
||||
SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(port);
|
||||
if (portIdentifier == null) {
|
||||
return new Result(ThingStatusDetail.CONFIGURATION_ERROR, "No such port: " + port);
|
||||
}
|
||||
|
||||
SerialPort commPort;
|
||||
try {
|
||||
commPort = portIdentifier.open(this.getClass().getName(), 2000);
|
||||
} catch (PortInUseException e1) {
|
||||
return new Result(ThingStatusDetail.CONFIGURATION_ERROR, "Port " + port + " is in use");
|
||||
}
|
||||
|
||||
try {
|
||||
// Herzborg serial bus operates with fixed parameters
|
||||
commPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
|
||||
commPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
|
||||
} catch (UnsupportedCommOperationException e) {
|
||||
return new Result(ThingStatusDetail.CONFIGURATION_ERROR, "Invalid port configuration");
|
||||
}
|
||||
|
||||
try {
|
||||
commPort.enableReceiveThreshold(8);
|
||||
commPort.enableReceiveTimeout(1000);
|
||||
} catch (UnsupportedCommOperationException e) {
|
||||
// OpenHAB's serial-over-IP doesn't support these, so let's ignore the exception
|
||||
}
|
||||
|
||||
InputStream dataIn = null;
|
||||
OutputStream dataOut = null;
|
||||
String error = null;
|
||||
|
||||
try {
|
||||
dataIn = commPort.getInputStream();
|
||||
dataOut = commPort.getOutputStream();
|
||||
|
||||
if (dataIn == null) {
|
||||
error = "No input stream available on the serial port";
|
||||
} else if (dataOut == null) {
|
||||
error = "No output stream available on the serial port";
|
||||
} else {
|
||||
dataOut.flush();
|
||||
if (dataIn.markSupported()) {
|
||||
dataIn.reset();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
error = e.getMessage();
|
||||
}
|
||||
|
||||
if (error != null) {
|
||||
return new Result(ThingStatusDetail.HANDLER_INITIALIZING_ERROR, error);
|
||||
}
|
||||
|
||||
this.serialPort = commPort;
|
||||
this.dataIn = dataIn;
|
||||
this.dataOut = dataOut;
|
||||
|
||||
return new Result(ThingStatusDetail.NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
SerialPort port = serialPort;
|
||||
|
||||
if (port == null) {
|
||||
return; // Nothing to do in this case
|
||||
}
|
||||
|
||||
port.removeEventListener();
|
||||
super.dispose();
|
||||
port.close();
|
||||
serialPort = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.herzborg.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link SerialBusConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SerialBusConfiguration {
|
||||
public @Nullable String port;
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.herzborg.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
|
||||
/**
|
||||
* The {@link SerialBusHandler} implements specific handling for Herzborg serial bus,
|
||||
* connected directly via a serial port.
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SerialBusHandler extends BusHandler {
|
||||
private SerialBusConfiguration config = new SerialBusConfiguration();
|
||||
|
||||
public SerialBusHandler(Bridge bridge, SerialPortManager portManager) {
|
||||
super(bridge, new SerialBus(portManager));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(SerialBusConfiguration.class);
|
||||
|
||||
Bus.Result result = ((SerialBus) bus).initialize(config.port);
|
||||
|
||||
if (result.code == ThingStatusDetail.NONE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, result.code, result.message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
bus.dispose();
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.herzborg.internal.dto;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* Herzborg binary protocol
|
||||
*
|
||||
* @author Pavel Fedin - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class HerzborgProtocol {
|
||||
public static class Function {
|
||||
public static final byte READ = 0x01;
|
||||
public static final byte WRITE = 0x02;
|
||||
public static final byte CONTROL = 0x03;
|
||||
public static final byte REQUEST = 0x04;
|
||||
}
|
||||
|
||||
public static class ControlAddress {
|
||||
public static final byte OPEN = 0x01;
|
||||
public static final byte CLOSE = 0x02;
|
||||
public static final byte STOP = 0x03;
|
||||
public static final byte PERCENT = 0x04;
|
||||
public static final byte DELETE_LIMIT = 0x07;
|
||||
public static final byte DEFAULT = 0x08;
|
||||
public static final byte SET_CONTEXT = 0x09;
|
||||
public static final byte RUN_CONTEXT = 0x0A;
|
||||
public static final byte DEL_CONTEXT = 0x0B;
|
||||
}
|
||||
|
||||
public static class DataAddress {
|
||||
public static final byte ID_L = 0x00;
|
||||
public static final byte ID_H = 0x01;
|
||||
public static final byte POSITION = 0x02;
|
||||
public static final byte DEFAULT_DIR = 0x03;
|
||||
public static final byte HAND_START = 0x04;
|
||||
public static final byte MODE = 0x05;
|
||||
public static final byte EXT_SWITCH = 0x27;
|
||||
public static final byte EXT_HV_SWITCH = 0x28;
|
||||
}
|
||||
|
||||
public static class Packet {
|
||||
private static final int HEADER_LENGTH = 5;
|
||||
private static final int CRC16_LENGTH = 2;
|
||||
public static final int MIN_LENGTH = HEADER_LENGTH + CRC16_LENGTH;
|
||||
|
||||
private static final byte START = 0x55;
|
||||
|
||||
private ByteBuffer dataBuffer;
|
||||
private int dataLength; // Packet length without CRC16
|
||||
|
||||
public Packet(byte[] data) {
|
||||
dataBuffer = ByteBuffer.wrap(data);
|
||||
dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
dataLength = data.length - CRC16_LENGTH;
|
||||
}
|
||||
|
||||
private void setHeader(short device_addr, byte function, byte data_addr, int data_length) {
|
||||
dataLength = HEADER_LENGTH + data_length;
|
||||
|
||||
dataBuffer = ByteBuffer.allocate(dataLength + CRC16_LENGTH);
|
||||
dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
dataBuffer.put(START);
|
||||
dataBuffer.putShort(device_addr);
|
||||
dataBuffer.put(function);
|
||||
dataBuffer.put(data_addr);
|
||||
}
|
||||
|
||||
private void setCrc16() {
|
||||
dataBuffer.putShort(crc16(dataLength));
|
||||
}
|
||||
|
||||
public Packet(short device_addr, byte function, byte data_addr) {
|
||||
setHeader(device_addr, function, data_addr, 0);
|
||||
setCrc16();
|
||||
}
|
||||
|
||||
public Packet(short device_addr, byte function, byte data_addr, byte value) {
|
||||
int dataLength = (function == Function.WRITE) ? 2 : 1;
|
||||
|
||||
setHeader(device_addr, function, data_addr, dataLength);
|
||||
if (function == Function.WRITE) {
|
||||
// WRITE command also requires length of data to be written
|
||||
dataBuffer.put((byte) 1);
|
||||
}
|
||||
dataBuffer.put(value);
|
||||
setCrc16();
|
||||
}
|
||||
|
||||
public byte[] getBuffer() {
|
||||
return dataBuffer.array();
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return dataBuffer.get(0) == START && crc16(dataLength) == dataBuffer.getShort(dataLength);
|
||||
}
|
||||
|
||||
public byte getFunction() {
|
||||
return dataBuffer.get(3);
|
||||
}
|
||||
|
||||
public byte getDataAddress() {
|
||||
return dataBuffer.get(4);
|
||||
}
|
||||
|
||||
public byte getDataLength() {
|
||||
return dataBuffer.get(HEADER_LENGTH);
|
||||
}
|
||||
|
||||
public byte getData(int offset) {
|
||||
return dataBuffer.get(HEADER_LENGTH + offset);
|
||||
}
|
||||
|
||||
// Herzborg uses modbus variant of CRC16
|
||||
// Code adapted from https://habr.com/ru/post/418209/
|
||||
private short crc16(int length) {
|
||||
int crc = 0xFFFF;
|
||||
for (int i = 0; i < length; i++) {
|
||||
crc = crc ^ Byte.toUnsignedInt(dataBuffer.get(i));
|
||||
for (int j = 0; j < 8; j++) {
|
||||
int mask = ((crc & 0x1) != 0) ? 0xA001 : 0x0000;
|
||||
crc = ((crc >> 1) & 0x7FFF) ^ mask;
|
||||
}
|
||||
}
|
||||
return (short) crc;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="herzborg" 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>Herzborg Binding</name>
|
||||
<description>This is the binding for Herzborg smart curtain motors.</description>
|
||||
|
||||
</binding:binding>
|
@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="herzborg"
|
||||
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">
|
||||
<bridge-type id="serialBus">
|
||||
<label>Herzborg Serial Bus</label>
|
||||
<description>RS485 bus</description>
|
||||
<config-description>
|
||||
<parameter name="port" type="text" required="true">
|
||||
<label>Serial Port</label>
|
||||
<context>serial-port</context>
|
||||
<description>Serial port to use, for example /dev/ttyS0 or COM1</description>
|
||||
<default>/dev/ttyS0</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<thing-type id="curtain">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="serialBus"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Herzborg Curtain Motor</label>
|
||||
<description>Curtain motor</description>
|
||||
<channels>
|
||||
<channel id="position" typeId="position"/>
|
||||
<channel id="mode" typeId="mode"/>
|
||||
<channel id="reverse" typeId="reverse"/>
|
||||
<channel id="handStart" typeId="handStart"/>
|
||||
<channel id="extSwitch" typeId="extSwitch"/>
|
||||
<channel id="hwSwitch" typeId="hwSwitch"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="address" type="integer">
|
||||
<label>Address</label>
|
||||
<description>Device address on the bus.</description>
|
||||
<default>65278</default>
|
||||
</parameter>
|
||||
<parameter name="pollInterval" type="integer" unit="s">
|
||||
<label>Poll Interval</label>
|
||||
<description>Poll interval in seconds</description>
|
||||
<default>1</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="position">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Position</label>
|
||||
<description>Curtain position control</description>
|
||||
<category>Blinds</category>
|
||||
</channel-type>
|
||||
<channel-type id="mode">
|
||||
<item-type>String</item-type>
|
||||
<label>Mode</label>
|
||||
<description>Motor mode</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">Stop</option>
|
||||
<option value="1">Open</option>
|
||||
<option value="2">Close</option>
|
||||
<option value="3">Setting</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="reverse" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Reverse Motor</label>
|
||||
<description>Reverse default motor direction</description>
|
||||
</channel-type>
|
||||
<channel-type id="handStart" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Start By Hand</label>
|
||||
<description>Enable or disable start by hand</description>
|
||||
</channel-type>
|
||||
<channel-type id="extSwitch" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>External Switch</label>
|
||||
<description>External switch type</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="1">2-channel biased</option>
|
||||
<option value="2">2-channel rocker</option>
|
||||
<option value="3">DC246</option>
|
||||
<option value="4">single button</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="hwSwitch" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>HV Switch</label>
|
||||
<description>High-voltage switch type (only for EV motor)</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="0">2-channel biased</option>
|
||||
<option value="1">hotel mode</option>
|
||||
<option value="2">2-channel rocker</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
@ -152,6 +152,7 @@
|
||||
<module>org.openhab.binding.helios</module>
|
||||
<module>org.openhab.binding.heliosventilation</module>
|
||||
<module>org.openhab.binding.heos</module>
|
||||
<module>org.openhab.binding.herzborg</module>
|
||||
<module>org.openhab.binding.homeconnect</module>
|
||||
<module>org.openhab.binding.homematic</module>
|
||||
<module>org.openhab.binding.homewizard</module>
|
||||
|
Loading…
Reference in New Issue
Block a user