added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.elerotransmitterstick-${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-elerotransmitterstick" description="Elero TransmitterStick 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.elerotransmitterstick/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal;
|
||||
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link EleroTransmitterStickBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class EleroTransmitterStickBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "elerotransmitterstick";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_STICK = new ThingTypeUID(BINDING_ID, "elerostick");
|
||||
public static final ThingTypeUID THING_TYPE_ELERO_CHANNEL = new ThingTypeUID(BINDING_ID, "elerochannel");
|
||||
|
||||
public static final String CONTROL_CHANNEL = "control";
|
||||
public static final String STATUS_CHANNEL = "status";
|
||||
|
||||
// channel id property for elero channels.
|
||||
public static final String PROPERTY_CHANNEL_ID = "channelId";
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal;
|
||||
|
||||
import static org.openhab.binding.elerotransmitterstick.internal.EleroTransmitterStickBindingConstants.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.elerotransmitterstick.internal.discovery.EleroChannelDiscoveryService;
|
||||
import org.openhab.binding.elerotransmitterstick.internal.handler.EleroChannelHandler;
|
||||
import org.openhab.binding.elerotransmitterstick.internal.handler.EleroTransmitterStickHandler;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
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.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link EleroTransmitterStickHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.elerotransmitterstick")
|
||||
public class EleroTransmitterStickHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
|
||||
.unmodifiableSet(Stream.of(THING_TYPE_STICK, THING_TYPE_ELERO_CHANNEL).collect(Collectors.toSet()));
|
||||
|
||||
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
|
||||
private final SerialPortManager serialPortManager;
|
||||
|
||||
@Activate
|
||||
public EleroTransmitterStickHandlerFactory(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_STICK)) {
|
||||
EleroTransmitterStickHandler bridgeHandler = new EleroTransmitterStickHandler((Bridge) thing,
|
||||
serialPortManager);
|
||||
|
||||
EleroChannelDiscoveryService discoveryService = new EleroChannelDiscoveryService(bridgeHandler);
|
||||
discoveryServiceRegs.put(bridgeHandler.getThing().getUID(), bundleContext
|
||||
.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
|
||||
return bridgeHandler;
|
||||
} else if (thingTypeUID.equals(THING_TYPE_ELERO_CHANNEL)) {
|
||||
EleroChannelHandler h = new EleroChannelHandler(thing);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.config;
|
||||
|
||||
import org.openhab.binding.elerotransmitterstick.internal.handler.EleroChannelHandler;
|
||||
|
||||
/**
|
||||
* The {@link EleroChannelConfig} holds configuration data of a {@link EleroChannelHandler}
|
||||
*
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class EleroChannelConfig {
|
||||
public Integer channelId;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.config;
|
||||
|
||||
import org.openhab.binding.elerotransmitterstick.internal.handler.EleroTransmitterStickHandler;
|
||||
|
||||
/**
|
||||
* The {@link EleroTransmitterStickConfig} holds configuration data of a
|
||||
* {@link EleroTransmitterStickHandler}
|
||||
*
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class EleroTransmitterStickConfig {
|
||||
public String portName;
|
||||
public int updateInterval;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.elerotransmitterstick.internal.EleroTransmitterStickBindingConstants.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.openhab.binding.elerotransmitterstick.internal.handler.EleroTransmitterStickHandler;
|
||||
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.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link EleroChannelDiscoveryService} is responsible for discovery of elero channels from an Elero Transmitter
|
||||
* Stick.
|
||||
*
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class EleroChannelDiscoveryService extends AbstractDiscoveryService {
|
||||
private static final int DISCOVER_TIMEOUT_SECONDS = 30;
|
||||
private final Logger logger = LoggerFactory.getLogger(EleroChannelDiscoveryService.class);
|
||||
|
||||
private EleroTransmitterStickHandler bridge;
|
||||
private ScheduledFuture<?> sensorDiscoveryJob;
|
||||
|
||||
/**
|
||||
* Creates the discovery service for the given handler and converter.
|
||||
*/
|
||||
public EleroChannelDiscoveryService(EleroTransmitterStickHandler stickHandler) {
|
||||
super(Collections.singleton(THING_TYPE_ELERO_CHANNEL), DISCOVER_TIMEOUT_SECONDS, true);
|
||||
|
||||
bridge = stickHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
discoverSensors();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
super.stopScan();
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
logger.debug("Start Elero Channel background discovery");
|
||||
if (sensorDiscoveryJob == null || sensorDiscoveryJob.isCancelled()) {
|
||||
sensorDiscoveryJob = scheduler.scheduleWithFixedDelay(() -> {
|
||||
discoverSensors();
|
||||
}, 0, 2, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
logger.debug("Stop Elero Channel background discovery");
|
||||
if (sensorDiscoveryJob != null && !sensorDiscoveryJob.isCancelled()) {
|
||||
sensorDiscoveryJob.cancel(true);
|
||||
sensorDiscoveryJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverSensors() {
|
||||
if (bridge.getStick() == null) {
|
||||
logger.debug("Stick not opened, scanning skipped.");
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<Integer> channelIds = bridge.getStick().getKnownIds();
|
||||
if (channelIds.isEmpty()) {
|
||||
logger.debug("Could not obtain known channels from the stick, scanning skipped.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (Integer id : channelIds) {
|
||||
ThingUID sensorThing = new ThingUID(THING_TYPE_ELERO_CHANNEL, bridge.getThing().getUID(),
|
||||
String.valueOf(id));
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(sensorThing).withLabel("Channel " + id)
|
||||
.withRepresentationProperty("id").withBridge(bridge.getThing().getUID())
|
||||
.withProperty(PROPERTY_CHANNEL_ID, id).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.handler;
|
||||
|
||||
import static org.openhab.binding.elerotransmitterstick.internal.EleroTransmitterStickBindingConstants.*;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.openhab.binding.elerotransmitterstick.internal.config.EleroChannelConfig;
|
||||
import org.openhab.binding.elerotransmitterstick.internal.stick.CommandType;
|
||||
import org.openhab.binding.elerotransmitterstick.internal.stick.ResponseStatus;
|
||||
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.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link EleroChannelHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class EleroChannelHandler extends BaseThingHandler implements StatusListener {
|
||||
private final Logger logger = LoggerFactory.getLogger(EleroChannelHandler.class);
|
||||
|
||||
protected Integer channelId;
|
||||
protected EleroTransmitterStickHandler bridge;
|
||||
|
||||
public EleroChannelHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
bridge = (EleroTransmitterStickHandler) getBridge().getHandler();
|
||||
|
||||
channelId = getConfig().as(EleroChannelConfig.class).channelId;
|
||||
bridge.addStatusListener(channelId, this);
|
||||
|
||||
if (bridge.getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (bridge != null) {
|
||||
bridge.removeStatusListener(channelId, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
|
||||
logger.debug("Bridge for Elero channel handler for thing {} ({}) changed status to {}",
|
||||
getThing().getLabel(), getThing().getUID(), bridgeStatusInfo.getStatus().toString());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("Received command {} for channel {}", command, channelUID);
|
||||
|
||||
if (channelUID.getIdWithoutGroup().equals(CONTROL_CHANNEL)) {
|
||||
if (command == UpDownType.UP) {
|
||||
bridge.getStick().sendCommand(CommandType.UP, Collections.singletonList(channelId));
|
||||
} else if (command == UpDownType.DOWN) {
|
||||
bridge.getStick().sendCommand(CommandType.DOWN, Collections.singletonList(channelId));
|
||||
} else if (command == StopMoveType.STOP) {
|
||||
bridge.getStick().sendCommand(CommandType.STOP, Collections.singletonList(channelId));
|
||||
} else if (command instanceof PercentType) {
|
||||
CommandType cmd = CommandType.getForPercent(((PercentType) command).intValue());
|
||||
if (cmd != null) {
|
||||
bridge.getStick().sendCommand(cmd, Collections.singletonList(channelId));
|
||||
} else {
|
||||
logger.debug("Unhandled command {}.", command);
|
||||
}
|
||||
} else if (command == RefreshType.REFRESH) {
|
||||
bridge.getStick().requestUpdate(Collections.singletonList(channelId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void statusChanged(int channelId, ResponseStatus status) {
|
||||
logger.debug("Received updated state {} for thing {}", status, getThing().getUID().toString());
|
||||
|
||||
updateState(STATUS_CHANNEL, new StringType(status.toString()));
|
||||
|
||||
int percentage = ResponseStatus.getPercentageFor(status);
|
||||
if (percentage != -1) {
|
||||
updateState(CONTROL_CHANNEL, new PercentType(percentage));
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.handler;
|
||||
|
||||
import org.openhab.binding.elerotransmitterstick.internal.config.EleroTransmitterStickConfig;
|
||||
import org.openhab.binding.elerotransmitterstick.internal.stick.TransmitterStick;
|
||||
import org.openhab.binding.elerotransmitterstick.internal.stick.TransmitterStick.StickListener;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link EleroTransmitterStickHandler} is responsible for managing the connection to an elero transmitter stick.
|
||||
*
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class EleroTransmitterStickHandler extends BaseBridgeHandler implements BridgeHandler {
|
||||
private final SerialPortManager serialPortManager;
|
||||
private final TransmitterStick stick;
|
||||
|
||||
public EleroTransmitterStickHandler(Bridge bridge, SerialPortManager serialPortManager) {
|
||||
super(bridge);
|
||||
this.serialPortManager = serialPortManager;
|
||||
|
||||
stick = new TransmitterStick(new StickListener() {
|
||||
@Override
|
||||
public void connectionEstablished() {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionDropped(Exception e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUid, Command command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
stick.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
stick.initialize(getConfig().as(EleroTransmitterStickConfig.class), scheduler, serialPortManager);
|
||||
}
|
||||
|
||||
public TransmitterStick getStick() {
|
||||
return stick;
|
||||
}
|
||||
|
||||
public void addStatusListener(int channelId, StatusListener listener) {
|
||||
stick.addStatusListener(channelId, listener);
|
||||
}
|
||||
|
||||
public void removeStatusListener(int channelId, StatusListener listener) {
|
||||
stick.removeStatusListener(channelId, listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.handler;
|
||||
|
||||
import org.openhab.binding.elerotransmitterstick.internal.stick.ResponseStatus;
|
||||
|
||||
/**
|
||||
* The {@link StatusListener} is an interface for notifying interested listeners about
|
||||
* status changes of elero channels.
|
||||
*
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public interface StatusListener {
|
||||
void statusChanged(int channelId, ResponseStatus status);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class Command implements Delayed {
|
||||
public static final int TIMED_PRIORITY = 30;
|
||||
public static final int COMMAND_PRIORITY = 20;
|
||||
public static final int FAST_INFO_PRIORITY = 10;
|
||||
public static final int INFO_PRIORITY = 0;
|
||||
|
||||
private Integer[] channelId;
|
||||
private CommandType commandType;
|
||||
|
||||
protected int priority = COMMAND_PRIORITY;
|
||||
|
||||
public Command(final CommandType cmd, final Integer... channels) {
|
||||
channelId = channels;
|
||||
commandType = cmd;
|
||||
}
|
||||
|
||||
protected Command(final CommandType cmd, int priority, final Integer... channels) {
|
||||
this(cmd, channels);
|
||||
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Command " + commandType + " on channels " + Arrays.toString(channelId) + " with priority " + priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Delayed delayed) {
|
||||
if (delayed == this) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Long.compare(0, delayed.getDelay(TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDelay(TimeUnit unit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public Integer[] getChannelIds() {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
public CommandType getCommandType() {
|
||||
return commandType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
import org.openhab.core.util.HexUtils;
|
||||
|
||||
/**
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class CommandPacket {
|
||||
public static final byte EASY_CHECK = (byte) 0x4A;
|
||||
public static final byte EASY_SEND = (byte) 0x4C;
|
||||
public static final byte EASY_INFO = (byte) 0x4E;
|
||||
|
||||
byte[] data;
|
||||
|
||||
public CommandPacket(byte[] bytes) {
|
||||
data = new byte[bytes.length + 1];
|
||||
System.arraycopy(bytes, 0, data, 0, bytes.length);
|
||||
|
||||
data[bytes.length] = checksum(data);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public long getResponseTimeout() {
|
||||
if (data[2] == EASY_CHECK) {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
return 4000;
|
||||
}
|
||||
|
||||
private byte checksum(byte[] data) {
|
||||
long val = 0;
|
||||
|
||||
for (byte b : data) {
|
||||
val += b;
|
||||
}
|
||||
|
||||
val = val % 256;
|
||||
return (byte) (256 - val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return HexUtils.bytesToHex(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
/**
|
||||
* The {@link CommandType} is the type of the {@link Command}.
|
||||
*
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public enum CommandType {
|
||||
UP,
|
||||
INTERMEDIATE,
|
||||
VENTILATION,
|
||||
DOWN,
|
||||
STOP,
|
||||
INFO,
|
||||
CHECK,
|
||||
NONE;
|
||||
|
||||
public static CommandType getForPercent(int percentage) {
|
||||
if (percentage == 0) {
|
||||
return UP;
|
||||
}
|
||||
|
||||
if (percentage == 25) {
|
||||
return CommandType.INTERMEDIATE;
|
||||
}
|
||||
|
||||
if (percentage == 75) {
|
||||
return CommandType.VENTILATION;
|
||||
}
|
||||
|
||||
if (percentage == 100) {
|
||||
return CommandType.DOWN;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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.elerotransmitterstick.internal.stick;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class CommandUtil {
|
||||
/**
|
||||
* Create the two channel bytes for the given channel IDs
|
||||
*
|
||||
* @param channelIds channel ids (starting from 1)
|
||||
*/
|
||||
private static byte[] createChannelBits(Integer... channelIds) {
|
||||
long channels = 0;
|
||||
|
||||
for (int id : channelIds) {
|
||||
channels = channels + (1 << (id - 1));
|
||||
}
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(Short.BYTES);
|
||||
buffer.putShort((short) (channels % 32768));
|
||||
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
public static CommandPacket createPacket(Command cmd, Integer channelId) {
|
||||
return createPacket(cmd.getCommandType(), new Integer[] { channelId });
|
||||
}
|
||||
|
||||
public static CommandPacket createPacket(Command cmd) {
|
||||
return createPacket(cmd.getCommandType(), cmd.getChannelIds());
|
||||
}
|
||||
|
||||
public static CommandPacket createPacket(CommandType cmd, Integer... channelIds) {
|
||||
if (cmd == CommandType.INFO) {
|
||||
byte[] channelBits = createChannelBits(channelIds);
|
||||
|
||||
return new CommandPacket(
|
||||
new byte[] { (byte) 0xAA, 0x04, CommandPacket.EASY_INFO, channelBits[0], channelBits[1] });
|
||||
}
|
||||
|
||||
if (cmd == CommandType.CHECK) {
|
||||
return new CommandPacket(new byte[] { (byte) 0xaa, (byte) 0x02, CommandPacket.EASY_CHECK });
|
||||
}
|
||||
|
||||
byte[] channelBits = createChannelBits(channelIds);
|
||||
byte cmdByte = getCommandByte(cmd);
|
||||
|
||||
return new CommandPacket(
|
||||
new byte[] { (byte) 0xAA, 0x05, CommandPacket.EASY_SEND, channelBits[0], channelBits[1], cmdByte });
|
||||
}
|
||||
|
||||
private static byte getCommandByte(CommandType command) {
|
||||
switch (command) {
|
||||
case DOWN:
|
||||
return (byte) 0x40;
|
||||
case INTERMEDIATE:
|
||||
return (byte) 0x44;
|
||||
case STOP:
|
||||
return (byte) 0x10;
|
||||
case UP:
|
||||
return (byte) 0x20;
|
||||
case VENTILATION:
|
||||
return (byte) 0x24;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unhandled command type " + command);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
/**
|
||||
* The {@link ConnectException} is thrown on errors connecting to the elero transmitter stick.
|
||||
*
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class ConnectException extends Exception {
|
||||
private static final long serialVersionUID = 946529257121090885L;
|
||||
|
||||
public ConnectException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ConnectException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ConnectException(String message, Exception e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class DelayedCommand extends Command {
|
||||
private final long origin;
|
||||
private final long delay;
|
||||
|
||||
public DelayedCommand(CommandType cmd, long delayInMillis, int priority, Integer... channels) {
|
||||
super(cmd, priority, channels);
|
||||
|
||||
delay = delayInMillis;
|
||||
origin = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Delayed delayed) {
|
||||
if (delayed == this) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Long.compare(getDelay(TimeUnit.MILLISECONDS), delayed.getDelay(TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDelay(TimeUnit unit) {
|
||||
return unit.convert(delay - (System.currentTimeMillis() - origin), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " and delay " + getDelay(TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class Response {
|
||||
private ResponseStatus status;
|
||||
private int[] channels;
|
||||
|
||||
public Response(ResponseStatus status, int[] channels) {
|
||||
this.status = status;
|
||||
this.channels = channels;
|
||||
}
|
||||
|
||||
public Response(int[] channels) {
|
||||
this.channels = channels;
|
||||
}
|
||||
|
||||
public boolean isMoving() {
|
||||
return status == ResponseStatus.MOVING_DOWN || status == ResponseStatus.MOVING_UP;
|
||||
}
|
||||
|
||||
public int[] getChannelIds() {
|
||||
return channels;
|
||||
}
|
||||
|
||||
public boolean hasStatus() {
|
||||
return status != null;
|
||||
}
|
||||
|
||||
public ResponseStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return status + " for channels " + Arrays.toString(channels);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
/**
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public enum ResponseStatus {
|
||||
NO_INFORMATION((byte) 0x00),
|
||||
TOP((byte) 0x01),
|
||||
BOTTOM((byte) 0x02),
|
||||
INTERMEDIATE((byte) 0x03),
|
||||
VENTILATION((byte) 0x04),
|
||||
BLOCKING((byte) 0x05),
|
||||
OVERHEATED((byte) 0x06),
|
||||
TIMEOUT((byte) 0x07),
|
||||
START_MOVE_UP((byte) 0x08),
|
||||
START_MOVE_DOWN((byte) 0x09),
|
||||
MOVING_UP((byte) 0x0a),
|
||||
MOVING_DOWN((byte) 0x0b),
|
||||
STOPPED((byte) 0x0d),
|
||||
TOP_TILT((byte) 0x0e),
|
||||
BOTTOM_INTERMEDIATE((byte) 0x0f),
|
||||
SWITCHED_OFF((byte) 0x10),
|
||||
SWITCHED_ON((byte) 0x11);
|
||||
|
||||
public static final byte EASY_CONFIRM = (byte) 0x4B;
|
||||
public static final byte EASY_ACK = (byte) 0x4D;
|
||||
|
||||
private byte statusByte;
|
||||
|
||||
private ResponseStatus(byte statusByte) {
|
||||
this.statusByte = statusByte;
|
||||
}
|
||||
|
||||
public static ResponseStatus getFor(byte statusByte) {
|
||||
if (statusByte <= MOVING_DOWN.statusByte) {
|
||||
return ResponseStatus.values()[statusByte];
|
||||
}
|
||||
return ResponseStatus.values()[statusByte - 1];
|
||||
}
|
||||
|
||||
public static int getPercentageFor(ResponseStatus status) {
|
||||
switch (status) {
|
||||
case BOTTOM:
|
||||
return 100;
|
||||
case NO_INFORMATION:
|
||||
return -1;
|
||||
case TOP:
|
||||
return 0;
|
||||
case INTERMEDIATE:
|
||||
return 25;
|
||||
case VENTILATION:
|
||||
return 75;
|
||||
default:
|
||||
return 50;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class ResponseUtil {
|
||||
|
||||
public static Response createResponse(byte upperChannelByte, byte lowerChannelByte) {
|
||||
return new Response(getChannelIds(upperChannelByte, lowerChannelByte));
|
||||
}
|
||||
|
||||
public static Response createResponse(byte upperChannelByte, byte lowerChannelByte, byte responseType) {
|
||||
return new Response(ResponseStatus.getFor(responseType), getChannelIds(upperChannelByte, lowerChannelByte));
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the list of channels (starting with 1)
|
||||
*/
|
||||
private static int[] getChannelIds(byte upperChannelByte, byte lowerChannelByte) {
|
||||
int[] result = new int[16];
|
||||
int idx = 0;
|
||||
|
||||
byte b = lowerChannelByte;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ((b & 1) > 0) {
|
||||
result[idx++] = i + 1;
|
||||
}
|
||||
b = (byte) (b >> 1);
|
||||
}
|
||||
|
||||
b = upperChannelByte;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ((b & 1) > 0) {
|
||||
result[idx++] = i + 9;
|
||||
}
|
||||
b = (byte) (b >> 1);
|
||||
}
|
||||
|
||||
return Arrays.copyOfRange(result, 0, idx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.TooManyListenersException;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
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.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class SerialConnection {
|
||||
private final Logger logger = LoggerFactory.getLogger(SerialConnection.class);
|
||||
|
||||
private SerialPort serialPort;
|
||||
private boolean open;
|
||||
private String portName;
|
||||
private final List<Byte> bytes = new ArrayList<>();
|
||||
private Response response = null;
|
||||
private final SerialPortManager serialPortManager;
|
||||
|
||||
public SerialConnection(String portName, SerialPortManager serialPortManager) {
|
||||
this.portName = portName;
|
||||
this.serialPortManager = serialPortManager;
|
||||
}
|
||||
|
||||
public synchronized void open() throws ConnectException {
|
||||
try {
|
||||
if (!open) {
|
||||
logger.debug("Trying to open serial connection to port {}...", portName);
|
||||
|
||||
SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(portName);
|
||||
if (portIdentifier == null) {
|
||||
throw new ConnectException("No such port: " + portName);
|
||||
}
|
||||
|
||||
try {
|
||||
serialPort = portIdentifier.open("openhab", 3000);
|
||||
open = true;
|
||||
logger.debug("Serial connection to port {} opened.", portName);
|
||||
|
||||
serialPort.setSerialPortParams(38400, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
|
||||
SerialPort.PARITY_NONE);
|
||||
|
||||
serialPort.addEventListener(new SerialPortEventListener() {
|
||||
@Override
|
||||
public void serialEvent(SerialPortEvent event) {
|
||||
try {
|
||||
if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
|
||||
parseInput();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.warn("IOException reading from port {}!", portName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
serialPort.notifyOnDataAvailable(true);
|
||||
} catch (UnsupportedCommOperationException | TooManyListenersException ex) {
|
||||
close();
|
||||
throw new ConnectException(ex);
|
||||
}
|
||||
}
|
||||
} catch (PortInUseException ex) {
|
||||
throw new ConnectException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean isOpen() {
|
||||
return open;
|
||||
}
|
||||
|
||||
public synchronized void close() {
|
||||
if (open) {
|
||||
logger.debug("Closing serial connection to port {}...", portName);
|
||||
|
||||
serialPort.notifyOnDataAvailable(false);
|
||||
serialPort.removeEventListener();
|
||||
serialPort.close();
|
||||
open = false;
|
||||
}
|
||||
}
|
||||
|
||||
// send a packet to the stick and wait for the response
|
||||
public synchronized Response sendPacket(CommandPacket p) throws IOException {
|
||||
if (open) {
|
||||
Response r = response;
|
||||
|
||||
synchronized (bytes) {
|
||||
response = null;
|
||||
logger.debug("Writing packet to stick: {}", p);
|
||||
serialPort.getOutputStream().write(p.getBytes());
|
||||
|
||||
if (r != null) {
|
||||
return r;
|
||||
}
|
||||
|
||||
final long responseTimeout = p.getResponseTimeout();
|
||||
try {
|
||||
logger.trace("Waiting {} ms for answer from stick...", responseTimeout);
|
||||
bytes.wait(responseTimeout);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
r = response;
|
||||
response = null;
|
||||
}
|
||||
|
||||
logger.debug("Stick answered {} for packet {}.", r, p);
|
||||
return r;
|
||||
}
|
||||
|
||||
logger.warn("Stick skipped packet {}. Connection is not open.", p);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void parseInput() throws IOException {
|
||||
logger.trace("parsing input...");
|
||||
while (serialPort.getInputStream().available() > 0) {
|
||||
byte b = (byte) serialPort.getInputStream().read();
|
||||
bytes.add(b);
|
||||
}
|
||||
logger.trace("input parsed. buffer contains {} bytes.", bytes.size());
|
||||
analyzeBuffer();
|
||||
}
|
||||
|
||||
private void analyzeBuffer() {
|
||||
// drop everything before the beginning of the packet header 0xAA
|
||||
while (!bytes.isEmpty() && bytes.get(0) != (byte) 0xAA) {
|
||||
logger.trace("dropping byte {} from buffer", bytes.get(0));
|
||||
bytes.remove(0);
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("buffer contains {} bytes: {}", bytes.size(),
|
||||
ArrayUtils.toPrimitive(bytes.toArray(new Byte[bytes.size()])));
|
||||
}
|
||||
if (bytes.size() > 1) {
|
||||
// second byte should be length byte (has to be either 0x04 or 0x05)
|
||||
int len = bytes.get(1);
|
||||
logger.trace("packet length is {} bytes.", len);
|
||||
|
||||
if (len != 4 && len != 5) {
|
||||
// invalid length, drop packet
|
||||
bytes.remove(0);
|
||||
analyzeBuffer();
|
||||
} else if (bytes.size() > len + 1) {
|
||||
// we have a complete packet in the buffer, analyze it
|
||||
// third byte should be response type byte (has to be either EASY_CONFIRM or EASY_ACK)
|
||||
byte respType = bytes.get(2);
|
||||
|
||||
synchronized (bytes) {
|
||||
if (respType == ResponseStatus.EASY_CONFIRM) {
|
||||
logger.trace("response type is EASY_CONFIRM.");
|
||||
|
||||
long val = bytes.get(0) + bytes.get(1) + bytes.get(2) + bytes.get(3) + bytes.get(4)
|
||||
+ bytes.get(5);
|
||||
if (val % 256 == 0) {
|
||||
response = ResponseUtil.createResponse(bytes.get(3), bytes.get(4));
|
||||
} else {
|
||||
logger.warn("invalid response checksum. Skipping response.");
|
||||
}
|
||||
|
||||
bytes.notify();
|
||||
} else if (respType == ResponseStatus.EASY_ACK) {
|
||||
logger.trace("response type is EASY_ACK.");
|
||||
|
||||
long val = bytes.get(0) + bytes.get(1) + bytes.get(2) + bytes.get(3) + bytes.get(4)
|
||||
+ bytes.get(5) + bytes.get(6);
|
||||
if (val % 256 == 0) {
|
||||
response = ResponseUtil.createResponse(bytes.get(3), bytes.get(4), bytes.get(5));
|
||||
} else {
|
||||
logger.warn("invalid response checksum. Skipping response.");
|
||||
}
|
||||
|
||||
bytes.notify();
|
||||
} else {
|
||||
logger.warn("invalid response type {}. Skipping response.", respType);
|
||||
}
|
||||
}
|
||||
|
||||
bytes.remove(0);
|
||||
analyzeBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
/**
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class TimedCommand extends Command {
|
||||
private int duration;
|
||||
|
||||
public TimedCommand(CommandType cmd, int cmdDuration, Integer[] channels) {
|
||||
super(cmd, channels);
|
||||
|
||||
duration = cmdDuration;
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
/**
|
||||
* 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.elerotransmitterstick.internal.stick;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.DelayQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.openhab.binding.elerotransmitterstick.internal.config.EleroTransmitterStickConfig;
|
||||
import org.openhab.binding.elerotransmitterstick.internal.handler.StatusListener;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Volker Bier - Initial contribution
|
||||
*/
|
||||
public class TransmitterStick {
|
||||
private final Logger logger = LoggerFactory.getLogger(TransmitterStick.class);
|
||||
private final HashMap<Integer, ArrayList<StatusListener>> allListeners = new HashMap<>();
|
||||
private final StickListener listener;
|
||||
|
||||
private EleroTransmitterStickConfig config;
|
||||
private SerialPortManager serialPortManager;
|
||||
private CommandWorker worker;
|
||||
|
||||
public TransmitterStick(StickListener l) {
|
||||
listener = l;
|
||||
}
|
||||
|
||||
public synchronized void initialize(EleroTransmitterStickConfig stickConfig, ScheduledExecutorService scheduler,
|
||||
SerialPortManager serialPortManager) {
|
||||
logger.debug("Initializing Transmitter Stick...");
|
||||
config = stickConfig;
|
||||
this.serialPortManager = serialPortManager;
|
||||
worker = new CommandWorker();
|
||||
scheduler.schedule(worker, 0, TimeUnit.MILLISECONDS);
|
||||
logger.debug("Transmitter Stick initialized, worker running.");
|
||||
}
|
||||
|
||||
public synchronized void dispose() {
|
||||
logger.debug("Disposing Transmitter Stick...");
|
||||
worker.terminateUpdates();
|
||||
worker = null;
|
||||
config = null;
|
||||
logger.debug("Transmitter Stick disposed.");
|
||||
}
|
||||
|
||||
public synchronized ArrayList<Integer> getKnownIds() {
|
||||
if (worker != null) {
|
||||
return worker.validIds;
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public synchronized void sendCommand(CommandType cmd, List<Integer> channelIds) {
|
||||
if (worker != null) {
|
||||
worker.executeCommand(cmd, channelIds);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void requestUpdate(List<Integer> channelIds) {
|
||||
if (worker != null) {
|
||||
worker.requestUpdates(channelIds);
|
||||
}
|
||||
}
|
||||
|
||||
public void addStatusListener(int channelId, StatusListener listener) {
|
||||
synchronized (allListeners) {
|
||||
ArrayList<StatusListener> listeners = allListeners.get(channelId);
|
||||
if (listeners == null) {
|
||||
listeners = new ArrayList<>();
|
||||
allListeners.put(channelId, listeners);
|
||||
}
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeStatusListener(int channelId, StatusListener listener) {
|
||||
synchronized (allListeners) {
|
||||
ArrayList<StatusListener> listeners = allListeners.get(channelId);
|
||||
if (listeners != null) {
|
||||
listeners.remove(listener);
|
||||
|
||||
if (listeners.isEmpty()) {
|
||||
allListeners.remove(channelId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyListeners(int channelId, ResponseStatus status) {
|
||||
synchronized (allListeners) {
|
||||
ArrayList<StatusListener> listeners = allListeners.get(channelId);
|
||||
if (listeners != null) {
|
||||
for (StatusListener l : listeners) {
|
||||
l.statusChanged(channelId, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure we have
|
||||
* - only one INFO for the same channel ids
|
||||
* - only one other command for the same channel ids
|
||||
*/
|
||||
private static boolean prepareAddition(Command newCmd, Collection<Command> coll) {
|
||||
Iterator<Command> queuedCommands = coll.iterator();
|
||||
while (queuedCommands.hasNext()) {
|
||||
Command existingCmd = queuedCommands.next();
|
||||
|
||||
if (Arrays.equals(newCmd.getChannelIds(), existingCmd.getChannelIds())) {
|
||||
// remove pending INFOs for same channel ids
|
||||
if (newCmd.getCommandType() == CommandType.INFO && existingCmd.getCommandType() == CommandType.INFO) {
|
||||
if (existingCmd.getPriority() < newCmd.priority) {
|
||||
// we have an older INFO command with same or lower priority, remove
|
||||
queuedCommands.remove();
|
||||
} else {
|
||||
// existing has higher priority, skip addition
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (newCmd.getCommandType() != CommandType.INFO && existingCmd.getCommandType() != CommandType.INFO) {
|
||||
// we have an older command for the same channels, remove
|
||||
queuedCommands.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static class DueCommandSet extends TreeSet<Command> {
|
||||
private static final long serialVersionUID = -3216360253151368826L;
|
||||
|
||||
public DueCommandSet() {
|
||||
super(new Comparator<Command>() {
|
||||
/**
|
||||
* Due commands are sorted by priority first and then by delay.
|
||||
*/
|
||||
@Override
|
||||
public int compare(Command o1, Command o2) {
|
||||
if (o1.equals(o2)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int d = o2.getPriority() - o1.getPriority();
|
||||
if (d < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (d == 0 && o1.getDelay(TimeUnit.MILLISECONDS) < o2.getDelay(TimeUnit.MILLISECONDS)) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Command e) {
|
||||
if (TransmitterStick.prepareAddition(e, this)) {
|
||||
return super.add(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class CommandWorker implements Runnable {
|
||||
private ArrayList<Integer> validIds = new ArrayList<>();
|
||||
private final AtomicBoolean terminated = new AtomicBoolean();
|
||||
private final int updateInterval;
|
||||
private final SerialConnection connection;
|
||||
|
||||
private final BlockingQueue<Command> cmdQueue = new DelayQueue<Command>() {
|
||||
@Override
|
||||
public boolean add(Command e) {
|
||||
if (TransmitterStick.prepareAddition(e, this)) {
|
||||
return super.add(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
CommandWorker() {
|
||||
connection = new SerialConnection(config.portName, serialPortManager);
|
||||
updateInterval = config.updateInterval;
|
||||
}
|
||||
|
||||
void terminateUpdates() {
|
||||
terminated.set(true);
|
||||
|
||||
// add a NONE command to make the thread exit from the call to take()
|
||||
cmdQueue.add(new Command(CommandType.NONE));
|
||||
}
|
||||
|
||||
void requestUpdates(List<Integer> channelIds) {
|
||||
// this is a workaround for a bug in the stick firmware that does not
|
||||
// handle commands that are sent to multiple channels correctly
|
||||
if (channelIds.size() > 1) {
|
||||
for (int channelId : channelIds) {
|
||||
requestUpdates(Collections.singletonList(channelId));
|
||||
}
|
||||
} else if (!channelIds.isEmpty()) {
|
||||
final Integer[] ids = channelIds.toArray(new Integer[channelIds.size()]);
|
||||
|
||||
logger.debug("adding INFO command for channel id {} to queue...", Arrays.toString(ids));
|
||||
cmdQueue.add(new DelayedCommand(CommandType.INFO, 0, Command.FAST_INFO_PRIORITY, ids));
|
||||
}
|
||||
}
|
||||
|
||||
void executeCommand(CommandType command, List<Integer> channelIds) {
|
||||
// this is a workaround for a bug in the stick firmware that does not
|
||||
// handle commands that are sent to multiple channels correctly
|
||||
if (channelIds.size() > 1) {
|
||||
for (int channelId : channelIds) {
|
||||
executeCommand(command, Collections.singletonList(channelId));
|
||||
}
|
||||
} else if (!channelIds.isEmpty()) {
|
||||
final Integer[] ids = channelIds.toArray(new Integer[channelIds.size()]);
|
||||
|
||||
logger.debug("adding command {} for channel ids {} to queue...", command, Arrays.toString(ids));
|
||||
cmdQueue.add(new Command(command, ids));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
queryChannels();
|
||||
doWork();
|
||||
} catch (Throwable t) {
|
||||
logger.error("Worker stopped by unexpected exception", t);
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void doWork() {
|
||||
// list of due commands sorted by priority
|
||||
final DueCommandSet dueCommands = new DueCommandSet();
|
||||
|
||||
logger.debug("worker started.");
|
||||
while (!terminated.get()) {
|
||||
waitConnected();
|
||||
|
||||
try {
|
||||
// in case we have no commands that are currently due, wait for a new one
|
||||
if (dueCommands.isEmpty()) {
|
||||
logger.debug("No due commands, invoking take on queue...");
|
||||
dueCommands.add(cmdQueue.take());
|
||||
logger.trace("take returned {}", dueCommands.first());
|
||||
}
|
||||
|
||||
if (!terminated.get()) {
|
||||
// take all commands that are due from the queue
|
||||
logger.trace("Draining all available commands...");
|
||||
Command cmd;
|
||||
int drainCount = 0;
|
||||
while ((cmd = cmdQueue.poll()) != null) {
|
||||
drainCount++;
|
||||
dueCommands.remove(cmd);
|
||||
dueCommands.add(cmd);
|
||||
}
|
||||
logger.trace("Drained {} commands, active queue size is {}, queue size is {}", drainCount,
|
||||
dueCommands.size(), cmdQueue.size());
|
||||
|
||||
// process the command with the highest priority
|
||||
cmd = dueCommands.first();
|
||||
logger.debug("active command is {}", cmd);
|
||||
|
||||
if (cmd.getCommandType() != CommandType.NONE) {
|
||||
Response response = connection.sendPacket(CommandUtil.createPacket(cmd));
|
||||
// remove the command now we know it has been correctly processed
|
||||
dueCommands.pollFirst();
|
||||
|
||||
if (response != null && response.hasStatus()) {
|
||||
for (int id : response.getChannelIds()) {
|
||||
notifyListeners(id, response.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd instanceof TimedCommand) {
|
||||
long delay = 1000 * ((TimedCommand) cmd).getDuration();
|
||||
logger.debug("adding timed command STOP for channel ids {} to queue with delay {}...",
|
||||
cmd.getChannelIds(), delay);
|
||||
|
||||
cmdQueue.add(new DelayedCommand(CommandType.STOP, delay, Command.TIMED_PRIORITY,
|
||||
cmd.getChannelIds()));
|
||||
} else if (response != null && response.isMoving()) {
|
||||
logger.debug("adding timed command INFO for channel ids {} to queue with delay 2000...",
|
||||
cmd.getChannelIds());
|
||||
|
||||
cmdQueue.add(new DelayedCommand(CommandType.INFO, 2000, Command.FAST_INFO_PRIORITY,
|
||||
cmd.getChannelIds()));
|
||||
} else if (cmd.getCommandType() == CommandType.INFO) {
|
||||
logger.debug("adding timed command INFO for channel ids {} to queue with delay {}...",
|
||||
cmd.getChannelIds(), updateInterval * 1000);
|
||||
|
||||
cmdQueue.add(new DelayedCommand(CommandType.INFO, updateInterval * 1000,
|
||||
Command.INFO_PRIORITY, cmd.getChannelIds()));
|
||||
}
|
||||
} else {
|
||||
logger.trace("ignoring NONE command.");
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Got interrupt while waiting for next command time", e);
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (IOException e) {
|
||||
logger.error("Got IOException communicating with the stick", e);
|
||||
listener.connectionDropped(e);
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
logger.debug("worker finished.");
|
||||
}
|
||||
|
||||
private void queryChannels() {
|
||||
logger.debug("querying available channels...");
|
||||
while (!terminated.get()) {
|
||||
waitConnected();
|
||||
|
||||
try {
|
||||
Response r = null;
|
||||
while (r == null && !terminated.get() && connection.isOpen()) {
|
||||
logger.debug("sending CHECK packet...");
|
||||
r = connection.sendPacket(CommandUtil.createPacket(CommandType.CHECK));
|
||||
|
||||
if (r == null) {
|
||||
Thread.sleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
if (r != null) {
|
||||
int[] knownIds = r.getChannelIds();
|
||||
logger.debug("Worker found channels: {} ", Arrays.toString(knownIds));
|
||||
|
||||
for (int id : knownIds) {
|
||||
if (!validIds.contains(id)) {
|
||||
validIds.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
requestUpdates(validIds);
|
||||
break;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Got IOException communicating with the stick", e);
|
||||
listener.connectionDropped(e);
|
||||
connection.close();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Got interrupt while waiting for next command time", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void waitConnected() {
|
||||
if (!connection.isOpen()) {
|
||||
while (!connection.isOpen() && !terminated.get()) {
|
||||
try {
|
||||
connection.open();
|
||||
listener.connectionEstablished();
|
||||
} catch (ConnectException e1) {
|
||||
listener.connectionDropped(e1);
|
||||
}
|
||||
|
||||
if (!connection.isOpen() && !terminated.get()) {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Got interrupt while waiting for next command time", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.trace("finished waiting. connection open={}, terminated={}", connection.isOpen(), terminated.get());
|
||||
}
|
||||
}
|
||||
|
||||
public interface StickListener {
|
||||
void connectionEstablished();
|
||||
|
||||
void connectionDropped(Exception e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="elerotransmitterstick" 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>Elero Transmitter Stick Binding</name>
|
||||
<description>This is the binding for Elero Transmitter Sticks.</description>
|
||||
<author>Volker Bier</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="elerotransmitterstick"
|
||||
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="elerostick">
|
||||
<label>Elero Transmitter Stick</label>
|
||||
<description>Thing for an Elero Transmitter Stick</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="portName" type="text" required="true">
|
||||
<label>Port Name</label>
|
||||
<description>The name of the port to which the Elero Transmitter Stick is connected.</description>
|
||||
<context>serial-port</context>
|
||||
<limitToOptions>false</limitToOptions>
|
||||
</parameter>
|
||||
<parameter name="updateInterval" type="integer" min="5" max="3600" unit="s" step="5">
|
||||
<label>Update Interval</label>
|
||||
<description>The number of seconds to wait before requesting the status of the elero channels from the Elero
|
||||
Transmitter Stick.</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<thing-type id="elerochannel">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="elerostick"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Elero Channel</label>
|
||||
<description>One of the 15 elero channels available on an Elero Transmitter Stick.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="control" typeId="control"/>
|
||||
<channel id="status" typeId="status"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="channelId" type="integer" min="1" max="15" step="1" readOnly="true">
|
||||
<label>Channel ID</label>
|
||||
<description>The id of this channel.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<!-- Control Channel Type -->
|
||||
<channel-type id="control">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Control</label>
|
||||
<description>Allows to control the devices connected to this elero channel or group by sending UP, DOWN, STOP
|
||||
commands.</description>
|
||||
</channel-type>
|
||||
|
||||
<!-- Status Channel Type -->
|
||||
<channel-type id="status">
|
||||
<item-type>String</item-type>
|
||||
<label>Status</label>
|
||||
<description>Read only channel providing the current status of this elero channel or group.</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user