[resol] Add Resol Controller Binding - Initial Contribution (#9449)
Signed-off-by: Raphael Mack <ramack@raphael-mack.de>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.resol-${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-resol" description="Resol Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.resol/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
|
||||
import de.resol.vbus.Packet;
|
||||
import de.resol.vbus.Specification;
|
||||
import de.resol.vbus.SpecificationFile.Language;
|
||||
|
||||
/**
|
||||
* The {@link ResolBaseThingHandler} class is a common ancestor for Resol thing handlers, capabale of handling vbus
|
||||
* packets
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class ResolBaseThingHandler extends BaseThingHandler {
|
||||
|
||||
public ResolBaseThingHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
protected abstract void packetReceived(Specification spec, Language lang, Packet packet);
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.resol.internal.ResolBindingConstants;
|
||||
import org.openhab.binding.resol.internal.ResolBridgeConfiguration;
|
||||
import org.openhab.binding.resol.internal.discovery.ResolDeviceDiscoveryService;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
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.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.resol.vbus.Connection;
|
||||
import de.resol.vbus.Connection.ConnectionState;
|
||||
import de.resol.vbus.ConnectionAdapter;
|
||||
import de.resol.vbus.Packet;
|
||||
import de.resol.vbus.Specification;
|
||||
import de.resol.vbus.SpecificationFile;
|
||||
import de.resol.vbus.SpecificationFile.Language;
|
||||
import de.resol.vbus.TcpDataSource;
|
||||
import de.resol.vbus.TcpDataSourceProvider;
|
||||
|
||||
/**
|
||||
* The {@link ResolBridgeHandler} class handles the connection to the VBUS/LAN adapter.
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ResolBridgeHandler extends BaseBridgeHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ResolBridgeHandler.class);
|
||||
|
||||
private String ipAddress = "";
|
||||
private String password = "";
|
||||
private int refreshInterval;
|
||||
private boolean isConnected = false;
|
||||
private String unconnectedReason = "";
|
||||
|
||||
// Background Runnable
|
||||
private @Nullable ScheduledFuture<?> pollingJob;
|
||||
|
||||
private @Nullable Connection tcpConnection;
|
||||
private final Specification spec;
|
||||
|
||||
// Managing Thing Discovery Service
|
||||
private @Nullable ResolDeviceDiscoveryService discoveryService = null;
|
||||
|
||||
private boolean scanning;
|
||||
|
||||
private final @Nullable LocaleProvider localeProvider;
|
||||
|
||||
public ResolBridgeHandler(Bridge bridge, @Nullable LocaleProvider localeProvider) {
|
||||
super(bridge);
|
||||
spec = Specification.getDefaultSpecification();
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
public void updateStatus() {
|
||||
if (isConnected) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, unconnectedReason);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerDiscoveryService(ResolDeviceDiscoveryService discoveryService) {
|
||||
this.discoveryService = discoveryService;
|
||||
}
|
||||
|
||||
public void unregisterDiscoveryService() {
|
||||
discoveryService = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(ResolDeviceDiscoveryService.class);
|
||||
}
|
||||
|
||||
public void registerResolThingListener(ResolEmuEMThingHandler resolEmuEMThingHandler) {
|
||||
synchronized (this) {
|
||||
Connection con = tcpConnection;
|
||||
if (con != null) {
|
||||
resolEmuEMThingHandler.useConnection(con);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pollingRunnable() {
|
||||
if (!isConnected) {
|
||||
synchronized (ResolBridgeHandler.this) {
|
||||
Connection connection = tcpConnection;
|
||||
/* first cleanup in case there is an old but failed TCP connection around */
|
||||
try {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
|
||||
getThing().getThings().stream().forEach(thing -> {
|
||||
ThingHandler th = thing.getHandler();
|
||||
if (th instanceof ResolEmuEMThingHandler) {
|
||||
((ResolEmuEMThingHandler) th).stop();
|
||||
}
|
||||
});
|
||||
|
||||
connection = null;
|
||||
tcpConnection = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("TCP disconnect failed: {}", e.getMessage());
|
||||
}
|
||||
TcpDataSource source = null;
|
||||
/* now try to establish a new TCP connection */
|
||||
try {
|
||||
source = TcpDataSourceProvider.fetchInformation(InetAddress.getByName(ipAddress), 500);
|
||||
if (source != null) {
|
||||
source.setLivePassword(password);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
isConnected = false;
|
||||
unconnectedReason = Objects.requireNonNullElse(e.getMessage(), "");
|
||||
}
|
||||
if (source != null) {
|
||||
try {
|
||||
logger.debug("Opening a new connection to {} {} @{}", source.getProduct(),
|
||||
source.getDeviceName(), source.getAddress());
|
||||
connection = source.connectLive(0, 0x0020);
|
||||
tcpConnection = connection;
|
||||
} catch (Exception e) {
|
||||
// this generic Exception catch is required, as TcpDataSource.connectLive throws this
|
||||
// generic type
|
||||
isConnected = false;
|
||||
unconnectedReason = Objects.requireNonNullElse(e.getMessage(), "");
|
||||
}
|
||||
|
||||
if (connection != null) {
|
||||
// Add a listener to the Connection to monitor state changes and
|
||||
// read incoming frames
|
||||
connection.addListener(new ResolConnectorAdapter());
|
||||
}
|
||||
}
|
||||
// Establish the connection
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.connect();
|
||||
final Connection c = connection;
|
||||
// now set the connection the thing handlers for the emulated EMs
|
||||
|
||||
getThing().getThings().stream().forEach(thing -> {
|
||||
ThingHandler th = thing.getHandler();
|
||||
if (th instanceof ResolEmuEMThingHandler) {
|
||||
((ResolEmuEMThingHandler) th).useConnection(c);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
unconnectedReason = Objects.requireNonNullElse(e.getMessage(), "");
|
||||
isConnected = false;
|
||||
}
|
||||
} else {
|
||||
isConnected = false;
|
||||
}
|
||||
if (!isConnected) {
|
||||
logger.debug("Cannot establish connection to {} ({})", ipAddress, unconnectedReason);
|
||||
} else {
|
||||
unconnectedReason = "";
|
||||
}
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void startAutomaticRefresh() {
|
||||
ScheduledFuture<?> job = pollingJob;
|
||||
if (job == null || job.isCancelled()) {
|
||||
pollingJob = scheduler.scheduleWithFixedDelay(this::pollingRunnable, 0, refreshInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public ThingStatus getStatus() {
|
||||
return getThing().getStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// No commands supported - nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
updateStatus();
|
||||
ResolBridgeConfiguration configuration = getConfigAs(ResolBridgeConfiguration.class);
|
||||
ipAddress = configuration.ipAddress;
|
||||
refreshInterval = configuration.refreshInterval;
|
||||
password = configuration.password;
|
||||
startAutomaticRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ScheduledFuture<?> job = pollingJob;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
pollingJob = null;
|
||||
}
|
||||
try {
|
||||
Connection connection = tcpConnection;
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
getThing().getThings().stream().forEach(thing -> {
|
||||
ThingHandler th = thing.getHandler();
|
||||
if (th instanceof ResolEmuEMThingHandler) {
|
||||
((ResolEmuEMThingHandler) th).stop();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// we don't care about exceptions on disconnect in dispose
|
||||
}
|
||||
}
|
||||
|
||||
Locale getLocale() {
|
||||
if (localeProvider != null) {
|
||||
return localeProvider.getLocale();
|
||||
} else {
|
||||
return Locale.getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/* adapter to react on connection state changes and handle received packets */
|
||||
private class ResolConnectorAdapter extends ConnectionAdapter {
|
||||
@Override
|
||||
public void connectionStateChanged(@Nullable Connection connection) {
|
||||
synchronized (ResolBridgeHandler.this) {
|
||||
if (connection == null) {
|
||||
isConnected = false;
|
||||
} else {
|
||||
ConnectionState connState = connection.getConnectionState();
|
||||
if (ConnectionState.CONNECTED.equals(connState)) {
|
||||
isConnected = true;
|
||||
} else if (ConnectionState.DISCONNECTED.equals(connState)
|
||||
|| ConnectionState.INTERRUPTED.equals(connState)) {
|
||||
isConnected = false;
|
||||
}
|
||||
logger.debug("Connection state changed to: {}", connState.toString());
|
||||
|
||||
if (isConnected) {
|
||||
unconnectedReason = "";
|
||||
} else {
|
||||
unconnectedReason = "TCP connection failed: " + connState.toString();
|
||||
}
|
||||
}
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packetReceived(@Nullable Connection connection, @Nullable Packet packet) {
|
||||
if (connection == null || packet == null) {
|
||||
return;
|
||||
}
|
||||
Language lang = SpecificationFile.getLanguageForLocale(getLocale());
|
||||
boolean packetHandled = false;
|
||||
String thingType = spec.getSourceDeviceSpec(packet).getName(); // use En here
|
||||
|
||||
thingType = thingType.replace(" [", "-");
|
||||
thingType = thingType.replace("]", "");
|
||||
thingType = thingType.replace(" #", "-");
|
||||
thingType = thingType.replace(" ", "_");
|
||||
thingType = thingType.replace("/", "_");
|
||||
thingType = thingType.replaceAll("[^A-Za-z0-9_-]+", "_");
|
||||
|
||||
/*
|
||||
* It would be nice for the combination of MX and EM devices to filter only those with a peerAddress of
|
||||
* 0x10, because the MX redelivers the data from the EM to the DFA.
|
||||
* But the MX is the exception in this case and many other controllers do not redeliver data, so we keep it.
|
||||
*/
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Received Data from {} (0x{}/0x{}) naming it {}",
|
||||
spec.getSourceDeviceSpec(packet).getName(lang),
|
||||
Integer.toHexString(spec.getSourceDeviceSpec(packet).getSelfAddress()),
|
||||
Integer.toHexString(spec.getSourceDeviceSpec(packet).getPeerAddress()), thingType);
|
||||
}
|
||||
|
||||
for (Thing t : getThing().getThings()) {
|
||||
ResolBaseThingHandler th = (ResolBaseThingHandler) t.getHandler();
|
||||
boolean isEM = t instanceof ResolEmuEMThingHandler;
|
||||
|
||||
if (t.getUID().getId().contentEquals(thingType)
|
||||
|| (isEM && th != null && spec.getSourceDeviceSpec(packet)
|
||||
.getPeerAddress() == ((ResolEmuEMThingHandler) th).getVbusAddress())) {
|
||||
if (th != null) {
|
||||
th.packetReceived(spec, lang, packet);
|
||||
packetHandled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ResolDeviceDiscoveryService discovery = discoveryService;
|
||||
if (!packetHandled && scanning && discovery != null) {
|
||||
// register the seen device
|
||||
discovery.addThing(getThing().getUID(), ResolBindingConstants.THING_ID_DEVICE, thingType,
|
||||
spec.getSourceDeviceSpec(packet).getName(lang));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startScan() {
|
||||
scanning = true;
|
||||
}
|
||||
|
||||
public void stopScan() {
|
||||
scanning = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.handler;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.resol.internal.ResolEmuEMConfiguration;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
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.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.resol.vbus.Connection;
|
||||
import de.resol.vbus.Connection.ConnectionState;
|
||||
import de.resol.vbus.Packet;
|
||||
import de.resol.vbus.Specification;
|
||||
import de.resol.vbus.SpecificationFile.Language;
|
||||
import de.resol.vbus.deviceemulators.EmDeviceEmulator;
|
||||
|
||||
/**
|
||||
* The {@link ResolEmuEMThingHandler} is responsible for emulating a EM device
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ResolEmuEMThingHandler extends ResolBaseThingHandler implements PropertyChangeListener {
|
||||
public static final String CHANNEL_RELAY = "relay_";
|
||||
public static final String CHANNEL_TEMP = "temperature_";
|
||||
public static final String CHANNEL_RESIST = "resistor_";
|
||||
public static final String CHANNEL_SWITCH = "switch_";
|
||||
public static final String CHANNEL_TEMP_ADJUST = "bas_temp_adjust_";
|
||||
public static final String CHANNEL_MODE = "bas_mode_";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ResolEmuEMThingHandler.class);
|
||||
|
||||
private int vbusAddress = 0x6650;
|
||||
private int deviceId = 1;
|
||||
private @Nullable EmDeviceEmulator device;
|
||||
|
||||
private @Nullable ResolBridgeHandler bridgeHandler;
|
||||
|
||||
private static class BasSetting {
|
||||
float temperatureOffset = 0.0f;
|
||||
int mode = 4;
|
||||
}
|
||||
|
||||
private BasSetting[] basValues = { new BasSetting(), new BasSetting(), new BasSetting(), new BasSetting(),
|
||||
new BasSetting(), new BasSetting() };
|
||||
private long lastTime = System.currentTimeMillis();
|
||||
|
||||
// Background Runnable
|
||||
private @Nullable ScheduledFuture<?> updateJob;
|
||||
|
||||
public ResolEmuEMThingHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
ResolEmuEMConfiguration configuration = getConfigAs(ResolEmuEMConfiguration.class);
|
||||
deviceId = configuration.deviceId;
|
||||
vbusAddress = 0x6650 + deviceId;
|
||||
|
||||
bridgeHandler = getBridgeHandler();
|
||||
registerResolThingListener(bridgeHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
EmDeviceEmulator dev = device;
|
||||
ScheduledFuture<?> job = updateJob;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
}
|
||||
if (dev != null) {
|
||||
dev.stop();
|
||||
dev.removePropertyChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRunnable() {
|
||||
EmDeviceEmulator d = device;
|
||||
if (d != null) {
|
||||
long now = System.currentTimeMillis();
|
||||
int diff = (int) (now - lastTime);
|
||||
lastTime = now;
|
||||
|
||||
d.update(diff);
|
||||
}
|
||||
}
|
||||
|
||||
private void startAutomaticUpdate() {
|
||||
ScheduledFuture<?> job = updateJob;
|
||||
if (job == null || job.isCancelled()) {
|
||||
updateJob = scheduler.scheduleWithFixedDelay(this::updateRunnable, 0, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized @Nullable ResolBridgeHandler getBridgeHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
logger.debug("Required bridge not defined for thing {}.", thing.getThingTypeUID());
|
||||
return null;
|
||||
} else {
|
||||
return getBridgeHandler(bridge);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized @Nullable ResolBridgeHandler getBridgeHandler(Bridge bridge) {
|
||||
ResolBridgeHandler bridgeHandler = null;
|
||||
|
||||
ThingHandler handler = bridge.getHandler();
|
||||
if (handler instanceof ResolBridgeHandler) {
|
||||
bridgeHandler = (ResolBridgeHandler) handler;
|
||||
} else {
|
||||
logger.debug("No available bridge handler found yet. Bridge: {} .", bridge.getUID());
|
||||
}
|
||||
return bridgeHandler;
|
||||
}
|
||||
|
||||
private void registerResolThingListener(@Nullable ResolBridgeHandler bridgeHandler) {
|
||||
if (bridgeHandler != null) {
|
||||
bridgeHandler.registerResolThingListener(this);
|
||||
} else {
|
||||
logger.debug("Can't register {} at bridge as bridgeHandler is null.", this.getThing().getUID());
|
||||
}
|
||||
}
|
||||
|
||||
public int getVbusAddress() {
|
||||
return vbusAddress;
|
||||
}
|
||||
|
||||
public void useConnection(Connection connection) {
|
||||
EmDeviceEmulator device = this.device;
|
||||
if (device != null) {
|
||||
device.stop();
|
||||
device.removePropertyChangeListener(this);
|
||||
}
|
||||
device = new EmDeviceEmulator(connection, deviceId);
|
||||
this.device = device;
|
||||
device.addPropertyChangeListener(this);
|
||||
device.start();
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
setRelayChannelValue(i, device.getRelayValueByNr(i));
|
||||
}
|
||||
startAutomaticUpdate();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
EmDeviceEmulator device = this.device;
|
||||
if (device != null) {
|
||||
device.stop();
|
||||
}
|
||||
ScheduledFuture<?> updateJob = this.updateJob;
|
||||
if (updateJob != null) {
|
||||
updateJob.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
String chID = channelUID.getId();
|
||||
int channel = chID.charAt(chID.length() - 1) - '0';
|
||||
float value = 0;
|
||||
int intValue = 0;
|
||||
|
||||
if (command instanceof QuantityType<?>) {
|
||||
value = Objects.requireNonNullElse(((QuantityType<?>) command).toUnit(SIUnits.CELSIUS),
|
||||
new QuantityType<>(888.8, SIUnits.CELSIUS)).floatValue();
|
||||
} else if (command instanceof OnOffType) {
|
||||
intValue = ((OnOffType) command).equals(OnOffType.ON) ? 1 : 0;
|
||||
} else if (command instanceof DecimalType) {
|
||||
intValue = ((DecimalType) command).intValue();
|
||||
value = intValue;
|
||||
} else {
|
||||
/* nothing to do */
|
||||
return;
|
||||
}
|
||||
|
||||
EmDeviceEmulator dev = device;
|
||||
if (dev != null) {
|
||||
if (chID.startsWith(CHANNEL_TEMP)) {
|
||||
dev.setResistorValueByNrAndPt1000Temperatur(channel, value);
|
||||
updateState(channelUID, new QuantityType<>(value, SIUnits.CELSIUS));
|
||||
} else if (chID.startsWith(CHANNEL_SWITCH)) {
|
||||
if (intValue == 0) {
|
||||
/* switch is open => 1 megaohm */
|
||||
dev.setResistorValueByNr(channel, 1000000000);
|
||||
updateState(channelUID, OnOffType.OFF);
|
||||
} else {
|
||||
/* switch is closed */
|
||||
dev.setResistorValueByNr(channel, 0);
|
||||
updateState(channelUID, OnOffType.ON);
|
||||
}
|
||||
} else if (chID.startsWith(CHANNEL_RESIST)) {
|
||||
dev.setResistorValueByNr(channel, (int) (value * 1000.0));
|
||||
updateState(channelUID, new QuantityType<>(intValue, Units.OHM));
|
||||
} else if (chID.startsWith(CHANNEL_TEMP_ADJUST)) {
|
||||
basValues[channel - 1].temperatureOffset = value;
|
||||
updateBas(channel);
|
||||
updateState(channelUID, new QuantityType<>(value, SIUnits.CELSIUS));
|
||||
} else if (chID.startsWith(CHANNEL_MODE)) {
|
||||
basValues[channel - 1].mode = intValue;
|
||||
updateBas(channel);
|
||||
updateState(channelUID, new QuantityType<>(intValue, Units.ONE));
|
||||
} else {
|
||||
/* set resistor value for Open Connection, 1 megaohm */
|
||||
dev.setResistorValueByNr(channel, 1000000000);
|
||||
updateState(channelUID, new QuantityType<>(1000000, Units.OHM));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBas(int channel) {
|
||||
int resistor = 0; /* in milliohm */
|
||||
int delta = (int) ((basValues[channel - 1].temperatureOffset * 210.0f / 15.0f) * 1000.0f);
|
||||
switch (basValues[channel - 1].mode) {
|
||||
case 4: /* Automatic range 76 - 496 ohm */
|
||||
resistor = 286 * 1000 + delta;
|
||||
break;
|
||||
case 0: /* OFF range 1840 - 2260 ohm */
|
||||
resistor = 2050 * 1000 + delta;
|
||||
break;
|
||||
case 2: /* Night range 660 - 1080 ohm */
|
||||
resistor = 870 * 1000 + delta;
|
||||
break;
|
||||
case 3: /* Party is automatic mode with +15K */
|
||||
resistor = 286 * 1000 + 210 * 1000;
|
||||
break;
|
||||
case 1: /* Summer range 1240 - 1660 ohm */
|
||||
resistor = 1450 * 1000 + delta;
|
||||
break;
|
||||
default:
|
||||
/* signal a shortcut as error */
|
||||
resistor = 0;
|
||||
break;
|
||||
}
|
||||
EmDeviceEmulator device = this.device;
|
||||
if (device != null) {
|
||||
device.setResistorValueByNr(channel, resistor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(@Nullable PropertyChangeEvent evt) {
|
||||
if (evt != null) {
|
||||
String s = evt.getPropertyName();
|
||||
if (s.startsWith("relay") && s.endsWith("Value")) {
|
||||
int v = (Integer) evt.getNewValue();
|
||||
int i = Integer.parseInt(s.substring(5, 6));
|
||||
setRelayChannelValue(i, v);
|
||||
} else if (s.contentEquals("connectionState")) {
|
||||
ConnectionState ste = (ConnectionState) evt.getNewValue();
|
||||
if (ste.equals(ConnectionState.CONNECTED)) {
|
||||
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ste.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setRelayChannelValue(int relay, double value) {
|
||||
String channelId = CHANNEL_RELAY + relay;
|
||||
updateState(channelId, new DecimalType(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packetReceived(Specification spec, Language lang, Packet packet) {
|
||||
/* nothing to do here */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.handler;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.resol.internal.ResolBindingConstants;
|
||||
import org.openhab.binding.resol.internal.ResolStateDescriptionOptionProvider;
|
||||
import org.openhab.binding.resol.internal.providers.ResolChannelTypeProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.StateOption;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.resol.vbus.Packet;
|
||||
import de.resol.vbus.Specification;
|
||||
import de.resol.vbus.Specification.PacketFieldSpec;
|
||||
import de.resol.vbus.Specification.PacketFieldValue;
|
||||
import de.resol.vbus.SpecificationFile;
|
||||
import de.resol.vbus.SpecificationFile.Enum;
|
||||
import de.resol.vbus.SpecificationFile.EnumVariant;
|
||||
import de.resol.vbus.SpecificationFile.Language;
|
||||
|
||||
/**
|
||||
* The {@link ResolThingHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ResolThingHandler extends ResolBaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ResolThingHandler.class);
|
||||
|
||||
private ResolStateDescriptionOptionProvider stateDescriptionProvider;
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
|
||||
DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS_GENERAL);
|
||||
|
||||
static {
|
||||
synchronized (DATE_FORMAT) {
|
||||
DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
}
|
||||
|
||||
public ResolThingHandler(Thing thing, ResolStateDescriptionOptionProvider stateDescriptionProvider) {
|
||||
super(thing);
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
/* we ignore the commands for now on purpose */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
ResolBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
updateStatus(bridgeHandler.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized @Nullable ResolBridgeHandler getBridgeHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
logger.debug("Required bridge not defined for device.");
|
||||
return null;
|
||||
} else {
|
||||
return getBridgeHandler(bridge);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized @Nullable ResolBridgeHandler getBridgeHandler(Bridge bridge) {
|
||||
ResolBridgeHandler bridgeHandler = null;
|
||||
|
||||
ThingHandler handler = bridge.getHandler();
|
||||
if (handler instanceof ResolBridgeHandler) {
|
||||
bridgeHandler = (ResolBridgeHandler) handler;
|
||||
} else {
|
||||
logger.debug("No available bridge handler found yet. Bridge: {} .", bridge.getUID());
|
||||
}
|
||||
return bridgeHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void packetReceived(Specification spec, Language lang, Packet packet) {
|
||||
PacketFieldValue[] pfvs = spec.getPacketFieldValuesForHeaders(new Packet[] { packet });
|
||||
for (PacketFieldValue pfv : pfvs) {
|
||||
logger.trace("Id: {}, Name: {}, Raw: {}, Text: {}", pfv.getPacketFieldId(), pfv.getName(lang),
|
||||
pfv.getRawValueDouble(), pfv.formatTextValue(null, Locale.getDefault()));
|
||||
|
||||
String channelId = pfv.getName(); // use English name as channel
|
||||
channelId = channelId.replace(" [", "-");
|
||||
channelId = channelId.replace("]", "");
|
||||
channelId = channelId.replace("(", "-");
|
||||
channelId = channelId.replace(")", "");
|
||||
channelId = channelId.replace(" #", "-");
|
||||
channelId = channelId.replaceAll("[^A-Za-z0-9_-]+", "_");
|
||||
|
||||
channelId = channelId.toLowerCase(Locale.ENGLISH);
|
||||
|
||||
ChannelTypeUID channelTypeUID;
|
||||
|
||||
if (pfv.getPacketFieldSpec().getUnit().getUnitId() >= 0) {
|
||||
channelTypeUID = new ChannelTypeUID(ResolBindingConstants.BINDING_ID,
|
||||
pfv.getPacketFieldSpec().getUnit().getUnitCodeText());
|
||||
} else if (pfv.getPacketFieldSpec().getType() == SpecificationFile.Type.DateTime) {
|
||||
channelTypeUID = new ChannelTypeUID(ResolBindingConstants.BINDING_ID, "DateTime");
|
||||
} else {
|
||||
/* used for enums and the numeric types without unit */
|
||||
channelTypeUID = new ChannelTypeUID(ResolBindingConstants.BINDING_ID, "None");
|
||||
}
|
||||
|
||||
String acceptedItemType;
|
||||
|
||||
Thing thing = getThing();
|
||||
switch (pfv.getPacketFieldSpec().getType()) {
|
||||
case DateTime:
|
||||
acceptedItemType = "DateTime";
|
||||
break;
|
||||
case WeekTime:
|
||||
case Number:
|
||||
acceptedItemType = ResolChannelTypeProvider.itemTypeForUnit(pfv.getPacketFieldSpec().getUnit());
|
||||
break;
|
||||
case Time:
|
||||
default:
|
||||
acceptedItemType = "String";
|
||||
break;
|
||||
}
|
||||
Channel a = thing.getChannel(channelId);
|
||||
|
||||
if (a == null) {
|
||||
/* channel doesn't exit, let's create it */
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId);
|
||||
|
||||
if (pfv.getEnumVariant() != null) {
|
||||
/* create a state option channel */
|
||||
List<StateOption> options = new ArrayList<>();
|
||||
PacketFieldSpec ff = pfv.getPacketFieldSpec();
|
||||
Enum e = ff.getEnum();
|
||||
for (long l : e.getValues()) {
|
||||
EnumVariant v = e.getEnumVariantForValue(l);
|
||||
options.add(new StateOption(Long.toString(l), v.getText(lang)));
|
||||
}
|
||||
|
||||
stateDescriptionProvider.setStateOptions(channelUID, options);
|
||||
|
||||
Channel channel = ChannelBuilder.create(channelUID, "Number").withType(channelTypeUID)
|
||||
.withLabel(pfv.getName(lang)).build();
|
||||
|
||||
thingBuilder.withChannel(channel).withLabel(thing.getLabel());
|
||||
updateThing(thingBuilder.build());
|
||||
} else if (pfv.getRawValueDouble() != null) {
|
||||
/* a number channel */
|
||||
Channel channel = ChannelBuilder.create(channelUID, acceptedItemType).withType(channelTypeUID)
|
||||
.withLabel(pfv.getName(lang)).build();
|
||||
|
||||
thingBuilder.withChannel(channel).withLabel(thing.getLabel());
|
||||
updateThing(thingBuilder.build());
|
||||
}
|
||||
logger.debug("Creating channel: {}", channelUID);
|
||||
}
|
||||
|
||||
if (pfv.getEnumVariant() != null) {
|
||||
/* update the enum / State channel */
|
||||
this.updateState(channelId, new StringType(Long.toString(pfv.getRawValueLong())));
|
||||
|
||||
} else {
|
||||
switch (pfv.getPacketFieldSpec().getType()) {
|
||||
case Number:
|
||||
Double dd = pfv.getRawValueDouble();
|
||||
if (dd != null) {
|
||||
if (!isSpecialValue(dd)) {
|
||||
/* only set the value if no error occurred */
|
||||
|
||||
String str = pfv.formatText();
|
||||
if (str.endsWith("RH")) {
|
||||
/* unit %RH for relative humidity is not known in openHAB UoM, so we remove it */
|
||||
str = str.substring(0, str.length() - 2);
|
||||
}
|
||||
if (str.endsWith("Ω")) {
|
||||
QuantityType<?> q = new QuantityType<>(dd, Units.OHM);
|
||||
this.updateState(channelId, q);
|
||||
} else {
|
||||
try {
|
||||
QuantityType<?> q = new QuantityType<>(str);
|
||||
this.updateState(channelId, q);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("unit of '{}' unknown in openHAB", str);
|
||||
QuantityType<?> q = new QuantityType<>(dd.toString());
|
||||
this.updateState(channelId, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* else {
|
||||
* field not available in this packet, e. g. old firmware version not (yet) transmitting it
|
||||
* }
|
||||
*/
|
||||
break;
|
||||
case DateTime:
|
||||
synchronized (DATE_FORMAT) {
|
||||
DateTimeType d = new DateTimeType(DATE_FORMAT.format(pfv.getRawValueDate()));
|
||||
this.updateState(channelId, d);
|
||||
}
|
||||
break;
|
||||
case WeekTime:
|
||||
case Time:
|
||||
default:
|
||||
Bridge b = getBridge();
|
||||
if (b != null) {
|
||||
String value = pfv.formatTextValue(pfv.getPacketFieldSpec().getUnit(),
|
||||
((ResolBridgeHandler) b).getLocale());
|
||||
try {
|
||||
QuantityType<?> q = new QuantityType<>(value);
|
||||
this.updateState(channelId, q);
|
||||
} catch (IllegalArgumentException e) {
|
||||
this.updateState(channelId, new StringType(value));
|
||||
logger.debug("unit of '{}' unknown in openHAB, using string", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check if the given value is a special one like 888.8 or 999.9 for shortcut or open load on a sensor wire */
|
||||
private boolean isSpecialValue(Double dd) {
|
||||
if ((Math.abs(dd - 888.8) < 0.1) || (Math.abs(dd - (-888.8)) < 0.1)) {
|
||||
/* value out of range */
|
||||
return true;
|
||||
}
|
||||
if (Math.abs(dd - 999.9) < 0.1) {
|
||||
/* sensor not reachable */
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.internal;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link ResolBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ResolBindingConstants {
|
||||
|
||||
private static final String BRIDGE_VBUSLAN = "vbuslan";
|
||||
|
||||
public static final String BINDING_ID = "resol";
|
||||
|
||||
// List of all ChannelTypeUIDs is empty, as we got totally rid of static channel types.
|
||||
// ChannelTypeUIDs are constructed from the BINDING_ID and the UnitCodeTextIndex from the VSF
|
||||
|
||||
// List of all Thing Type
|
||||
public static final String THING_ID_DEVICE = "device";
|
||||
public static final String THING_ID_EMU_EM = "emulatedEM";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_UID_BRIDGE = new ThingTypeUID(BINDING_ID, BRIDGE_VBUSLAN);
|
||||
|
||||
public static final ThingTypeUID THING_TYPE_UID_DEVICE = new ThingTypeUID(BINDING_ID, THING_ID_DEVICE);
|
||||
|
||||
public static final ThingTypeUID THING_TYPE_UID_EMU_EM = new ThingTypeUID(BINDING_ID, THING_ID_EMU_EM);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_UID_BRIDGE,
|
||||
THING_TYPE_UID_DEVICE, THING_TYPE_UID_EMU_EM);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_BRIDGE_THING_TYPES_UIDS = Set.of(THING_TYPE_UID_BRIDGE);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ResolBridgeConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ResolBridgeConfiguration {
|
||||
|
||||
public String ipAddress = "";
|
||||
public String password = "vbus";
|
||||
public Integer port = 7053;
|
||||
public String adapterSerial = "";
|
||||
public Integer refreshInterval = 300;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ResolEmuEMConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ResolEmuEMConfiguration {
|
||||
public Integer deviceId = 1;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.internal;
|
||||
|
||||
import static org.openhab.binding.resol.internal.ResolBindingConstants.SUPPORTED_THING_TYPES_UIDS;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.resol.handler.ResolBridgeHandler;
|
||||
import org.openhab.binding.resol.handler.ResolEmuEMThingHandler;
|
||||
import org.openhab.binding.resol.handler.ResolThingHandler;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
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 ResolHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.resol", service = ThingHandlerFactory.class)
|
||||
public class ResolHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final LocaleProvider localeProvider;
|
||||
|
||||
private final ResolStateDescriptionOptionProvider stateDescriptionProvider;
|
||||
|
||||
@Activate
|
||||
public ResolHandlerFactory(final @Reference ResolStateDescriptionOptionProvider stateDescriptionProvider,
|
||||
final @Reference LocaleProvider localeProvider) {
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
@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(ResolBindingConstants.THING_TYPE_UID_DEVICE)) {
|
||||
return new ResolThingHandler(thing, stateDescriptionProvider);
|
||||
}
|
||||
|
||||
if (thingTypeUID.equals(ResolBindingConstants.THING_TYPE_UID_EMU_EM)) {
|
||||
return new ResolEmuEMThingHandler(thing);
|
||||
}
|
||||
|
||||
if (thingTypeUID.equals(ResolBindingConstants.THING_TYPE_UID_BRIDGE)) {
|
||||
return new ResolBridgeHandler((Bridge) thing, localeProvider);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
|
||||
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
|
||||
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* Dynamic provider of state options for the Resol binding.
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@Component(service = { DynamicStateDescriptionProvider.class, ResolStateDescriptionOptionProvider.class })
|
||||
@NonNullByDefault
|
||||
public class ResolStateDescriptionOptionProvider extends BaseDynamicStateDescriptionProvider {
|
||||
@Reference
|
||||
protected void setChannelTypeI18nLocalizationService(
|
||||
final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
||||
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
|
||||
}
|
||||
|
||||
protected void unsetChannelTypeI18nLocalizationService(
|
||||
final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
||||
this.channelTypeI18nLocalizationService = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.internal.discovery;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.resol.handler.ResolBridgeHandler;
|
||||
import org.openhab.binding.resol.internal.ResolBindingConstants;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ResolDeviceDiscoveryService} class handles the discovery of things.
|
||||
*
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ResolDeviceDiscoveryService extends AbstractDiscoveryService
|
||||
implements DiscoveryService, ThingHandlerService {
|
||||
|
||||
private static final String THING_PROPERTY_TYPE = "type";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ResolDeviceDiscoveryService.class);
|
||||
|
||||
private @Nullable ResolBridgeHandler resolBridgeHandler;
|
||||
|
||||
public ResolDeviceDiscoveryService() throws IllegalArgumentException {
|
||||
super(Set.of(ResolBindingConstants.THING_TYPE_UID_DEVICE), 15, false);
|
||||
}
|
||||
|
||||
public void addThing(ThingUID bridgeUID, String thingType, String type, String name) {
|
||||
logger.trace("Adding new Resol thing: {}", type);
|
||||
ThingUID thingUID = null;
|
||||
switch (thingType) {
|
||||
case ResolBindingConstants.THING_ID_DEVICE:
|
||||
thingUID = new ThingUID(ResolBindingConstants.THING_TYPE_UID_DEVICE, bridgeUID, type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (thingUID != null) {
|
||||
logger.trace("Adding new Discovery thingType: {} bridgeType: {}", thingUID.getAsString(),
|
||||
bridgeUID.getAsString());
|
||||
|
||||
Map<String, Object> properties = new HashMap<>(1);
|
||||
properties.put(THING_PROPERTY_TYPE, type);
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
|
||||
.withRepresentationProperty(THING_PROPERTY_TYPE).withProperties(properties).withLabel(name).build();
|
||||
logger.trace("call register: {} label: {}", discoveryResult.getBindingId(), discoveryResult.getLabel());
|
||||
thingDiscovered(discoveryResult);
|
||||
} else {
|
||||
logger.debug("Discovered Thing is unsupported: type '{}'", type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
ResolBridgeHandler resolBridgeHandler = this.resolBridgeHandler;
|
||||
if (resolBridgeHandler != null) {
|
||||
resolBridgeHandler.registerDiscoveryService(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
ResolBridgeHandler resolBridgeHandler = this.resolBridgeHandler;
|
||||
if (resolBridgeHandler != null) {
|
||||
resolBridgeHandler.unregisterDiscoveryService();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
ResolBridgeHandler resolBridgeHandler = this.resolBridgeHandler;
|
||||
if (resolBridgeHandler != null) {
|
||||
resolBridgeHandler.startScan();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopScan() {
|
||||
ResolBridgeHandler resolBridgeHandler = this.resolBridgeHandler;
|
||||
if (resolBridgeHandler != null) {
|
||||
resolBridgeHandler.stopScan();
|
||||
}
|
||||
super.stopScan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThingHandler(ThingHandler handler) {
|
||||
if (handler instanceof ResolBridgeHandler) {
|
||||
resolBridgeHandler = (ResolBridgeHandler) handler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return resolBridgeHandler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.internal.discovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.resol.internal.ResolBindingConstants;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.resol.vbus.TcpDataSource;
|
||||
import de.resol.vbus.TcpDataSourceProvider;
|
||||
|
||||
/**
|
||||
* The {@link ResolVBusBridgeDiscovery} class provides the DiscoverySerivce to
|
||||
* discover Resol VBus-LAN adapters
|
||||
*
|
||||
* @author Raphael Mack - Initial contribution
|
||||
*/
|
||||
@Component(service = DiscoveryService.class)
|
||||
@NonNullByDefault
|
||||
public class ResolVBusBridgeDiscovery extends AbstractDiscoveryService {
|
||||
public static final String THING_PROPERTY_IPADDRESS = "ipAddress";
|
||||
public static final String THING_PROPERTY_PORT = "port";
|
||||
public static final String THING_PROPERTY_ADAPTER_SERIAL = "adapterSerial";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ResolVBusBridgeDiscovery.class);
|
||||
|
||||
private volatile boolean discoveryRunning = false;
|
||||
private @Nullable Future<?> searchFuture;
|
||||
|
||||
public ResolVBusBridgeDiscovery() throws IllegalArgumentException {
|
||||
super(ResolBindingConstants.SUPPORTED_BRIDGE_THING_TYPES_UIDS, 35, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
discoveryRunning = true;
|
||||
searchFuture = scheduler.submit(this::searchRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopScan() {
|
||||
discoveryRunning = false;
|
||||
if (searchFuture != null) {
|
||||
searchFuture.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The runnable for the search routine.
|
||||
*/
|
||||
public void searchRunnable() {
|
||||
try {
|
||||
InetAddress broadcastAddress = InetAddress
|
||||
.getByAddress(new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255 });
|
||||
|
||||
TcpDataSource[] dataSources = TcpDataSourceProvider.discoverDataSources(broadcastAddress, 3, 500, false);
|
||||
|
||||
Map<String, TcpDataSource> currentDataSourceById = new HashMap<String, TcpDataSource>();
|
||||
for (TcpDataSource ds : dataSources) {
|
||||
if (!discoveryRunning) {
|
||||
break;
|
||||
}
|
||||
InetAddress address = ds.getAddress();
|
||||
String addressId = address.getHostAddress();
|
||||
TcpDataSource dsWithInfo;
|
||||
try {
|
||||
dsWithInfo = TcpDataSourceProvider.fetchInformation(address, 1500);
|
||||
logger.trace("Discovered Resol VBus-LAN interface @{} {} ({})", addressId,
|
||||
dsWithInfo.getDeviceName(), dsWithInfo.getSerial());
|
||||
|
||||
currentDataSourceById.put(addressId, dsWithInfo);
|
||||
addAdapter(addressId, dsWithInfo);
|
||||
// here we can add the detection of Multi-Channel interfaces like DL3
|
||||
} catch (InterruptedIOException ex) {
|
||||
/* openHAB interrupted the io thread and wants to shutdown */
|
||||
break;
|
||||
} catch (IOException ex) {
|
||||
/* address is no valid adapter */
|
||||
}
|
||||
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
logger.debug("Could not resolve IPv4 broadcast address");
|
||||
}
|
||||
}
|
||||
|
||||
private void addAdapter(String remoteIP, TcpDataSource dsWithInfo) {
|
||||
String adapterSerial = dsWithInfo.getSerial();
|
||||
Map<String, Object> properties = new HashMap<>(3);
|
||||
properties.put(THING_PROPERTY_IPADDRESS, remoteIP);
|
||||
properties.put(THING_PROPERTY_PORT, dsWithInfo.getLivePort());
|
||||
properties.put(THING_PROPERTY_ADAPTER_SERIAL, adapterSerial);
|
||||
|
||||
ThingUID uid = new ThingUID(ResolBindingConstants.THING_TYPE_UID_BRIDGE, adapterSerial);
|
||||
thingDiscovered(DiscoveryResultBuilder.create(uid).withRepresentationProperty(THING_PROPERTY_IPADDRESS)
|
||||
.withProperties(properties).withLabel(dsWithInfo.getName()).build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.resol.internal.providers;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.resol.internal.ResolBindingConstants;
|
||||
import org.openhab.core.thing.type.ChannelType;
|
||||
import org.openhab.core.thing.type.ChannelTypeBuilder;
|
||||
import org.openhab.core.thing.type.ChannelTypeProvider;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.StateDescriptionFragmentBuilder;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
import de.resol.vbus.Specification;
|
||||
import de.resol.vbus.SpecificationFile.Unit;
|
||||
|
||||
/**
|
||||
* @author Raphael Mack - Initial Contribution
|
||||
*
|
||||
*/
|
||||
@Component(service = { ChannelTypeProvider.class, ResolChannelTypeProvider.class })
|
||||
@NonNullByDefault
|
||||
public class ResolChannelTypeProvider implements ChannelTypeProvider {
|
||||
private Map<ChannelTypeUID, ChannelType> channelTypes = new ConcurrentHashMap<ChannelTypeUID, ChannelType>();
|
||||
|
||||
public ResolChannelTypeProvider() {
|
||||
// let's add all channel types from known by the resol-vbus java library
|
||||
|
||||
Specification spec = Specification.getDefaultSpecification();
|
||||
|
||||
Unit[] units = spec.getUnits();
|
||||
for (Unit u : units) {
|
||||
ChannelTypeUID channelTypeUID = new ChannelTypeUID(ResolBindingConstants.BINDING_ID, u.getUnitCodeText());
|
||||
|
||||
// maybe we could use pfv.getPacketFieldSpec().getPrecision() here
|
||||
int precision = 1;
|
||||
if (u.getUnitId() >= 0) {
|
||||
ChannelType ctype = ChannelTypeBuilder
|
||||
.state(channelTypeUID, u.getUnitFamily().toString(), itemTypeForUnit(u))
|
||||
.withStateDescriptionFragment(StateDescriptionFragmentBuilder.create()
|
||||
.withPattern("%." + precision + "f " + u.getUnitTextText().replace("%", "%%"))
|
||||
.withReadOnly(true).build())
|
||||
.build();
|
||||
|
||||
channelTypes.put(channelTypeUID, ctype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ChannelType> getChannelTypes(@Nullable Locale locale) {
|
||||
return channelTypes.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) {
|
||||
if (channelTypes.containsKey(channelTypeUID)) {
|
||||
return channelTypes.get(channelTypeUID);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String itemTypeForUnit(Unit u) {
|
||||
String itemType = "Number";
|
||||
switch (u.getUnitFamily()) {
|
||||
case Temperature:
|
||||
itemType += ":Temperature";
|
||||
break;
|
||||
case Energy:
|
||||
itemType += ":Energy";
|
||||
break;
|
||||
case VolumeFlow:
|
||||
itemType += ":VolumetricFlowRate";
|
||||
break;
|
||||
case Pressure:
|
||||
itemType += ":Pressure";
|
||||
break;
|
||||
case Volume:
|
||||
itemType += ":Volume";
|
||||
break;
|
||||
case Time:
|
||||
itemType += ":Time";
|
||||
break;
|
||||
case Power:
|
||||
itemType += ":Power";
|
||||
break;
|
||||
case None:
|
||||
switch (u.getUnitCodeText()) {
|
||||
case "Hertz":
|
||||
itemType += ":Frequency";
|
||||
break;
|
||||
case "Hectopascals":
|
||||
itemType += ":Pressure";
|
||||
break;
|
||||
case "MetersPerSecond":
|
||||
itemType += ":Speed";
|
||||
break;
|
||||
case "Milliamperes":
|
||||
itemType += ":ElectricCurrent";
|
||||
break;
|
||||
case "Milliseconds":
|
||||
itemType += ":Time";
|
||||
break;
|
||||
case "Ohms":
|
||||
itemType += ":ElectricResistance";
|
||||
break;
|
||||
case "Percent":
|
||||
itemType += ":Dimensionless";
|
||||
break;
|
||||
case "PercentRelativeHumidity":
|
||||
itemType += ":Dimensionless";
|
||||
break;
|
||||
case "Volts":
|
||||
itemType += ":ElectricPotential";
|
||||
break;
|
||||
case "WattsPerSquareMeter":
|
||||
itemType += ":Intensity";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return itemType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="resol" 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>Resol Binding</name>
|
||||
<description>This is the binding for Resol solar and system controllers (including branded versions).</description>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,80 @@
|
||||
# binding
|
||||
binding.resol.name = Resol Binding
|
||||
binding.resol.description = Verbindet Solar- und Systemregler des Herstellers Resol und weitere, die für andere Marken von Resol produziert werden.
|
||||
|
||||
# thing types
|
||||
thing-type.resol.device.label = Resol Gerät
|
||||
thing-type.resol.device.description = Solar- oder Systemregler oder ein anderes Gerät welches mit dem Resol VBus verbunden ist.
|
||||
thing-type.resol.emulatedEM.label = Emuliertes EM Modul
|
||||
thing-type.resol.emulatedEM.description = Emulation eines Erweiterungs-Modules (EM) welches über den VBus an dafür ausgelegten Resol Reglern angebunden wird. Es ersetzt ein physikalisch vorhandenes EM durch openHAB.
|
||||
thing-type.resol.emulatedEM.channel.relay_1.label = Relais Status 1
|
||||
thing-type.resol.emulatedEM.channel.relay_2.label = Relais Status 2
|
||||
thing-type.resol.emulatedEM.channel.relay_3.label = Relais Status 3
|
||||
thing-type.resol.emulatedEM.channel.relay_4.label = Relais Status 4
|
||||
thing-type.resol.emulatedEM.channel.relay_5.label = Relais Status 5
|
||||
thing-type.resol.emulatedEM.channel.switch_1.label = Schalter 1
|
||||
thing-type.resol.emulatedEM.channel.switch_2.label = Schalter 2
|
||||
thing-type.resol.emulatedEM.channel.switch_3.label = Schalter 3
|
||||
thing-type.resol.emulatedEM.channel.switch_4.label = Schalter 4
|
||||
thing-type.resol.emulatedEM.channel.switch_5.label = Schalter 5
|
||||
thing-type.resol.emulatedEM.channel.switch_6.label = Schalter 6
|
||||
thing-type.resol.emulatedEM.channel.temperature_1.label = Temperatur 1
|
||||
thing-type.resol.emulatedEM.channel.temperature_2.label = Temperatur 2
|
||||
thing-type.resol.emulatedEM.channel.temperature_3.label = Temperatur 3
|
||||
thing-type.resol.emulatedEM.channel.temperature_4.label = Temperatur 4
|
||||
thing-type.resol.emulatedEM.channel.temperature_5.label = Temperatur 5
|
||||
thing-type.resol.emulatedEM.channel.temperature_6.label = Temperatur 6
|
||||
thing-type.resol.emulatedEM.channel.resistor_1.label = Widerstand 1
|
||||
thing-type.resol.emulatedEM.channel.resistor_2.label = Widerstand 2
|
||||
thing-type.resol.emulatedEM.channel.resistor_3.label = Widerstand 3
|
||||
thing-type.resol.emulatedEM.channel.resistor_4.label = Widerstand 4
|
||||
thing-type.resol.emulatedEM.channel.resistor_5.label = Widerstand 5
|
||||
thing-type.resol.emulatedEM.channel.resistor_6.label = Widerstand 6
|
||||
thing-type.resol.emulatedEM.channel.bas_temp_adjust_1.label = Temperatur Anpassung 1
|
||||
thing-type.resol.emulatedEM.channel.bas_temp_adjust_2.label = Temperatur Anpassung 2
|
||||
thing-type.resol.emulatedEM.channel.bas_temp_adjust_3.label = Temperatur Anpassung 3
|
||||
thing-type.resol.emulatedEM.channel.bas_temp_adjust_4.label = Temperatur Anpassung 4
|
||||
thing-type.resol.emulatedEM.channel.bas_temp_adjust_5.label = Temperatur Anpassung 5
|
||||
thing-type.resol.emulatedEM.channel.bas_temp_adjust_6.label = Temperatur Anpassung 6
|
||||
thing-type.resol.emulatedEM.channel.bas_mode_1.label = Betriebsart 1
|
||||
thing-type.resol.emulatedEM.channel.bas_mode_2.label = Betriebsart 2
|
||||
thing-type.resol.emulatedEM.channel.bas_mode_3.label = Betriebsart 3
|
||||
thing-type.resol.emulatedEM.channel.bas_mode_4.label = Betriebsart 4
|
||||
thing-type.resol.emulatedEM.channel.bas_mode_5.label = Betriebsart 5
|
||||
thing-type.resol.emulatedEM.channel.bas_mode_6.label = Betriebsart 6
|
||||
thing-type.resol.vbuslan.label = VBusLAN Adapter
|
||||
thing-type.resol.vbuslan.description = Diese bridge verwendet ein Gerät mit TCP/IP live port als Schnittstelle zum Resol VBus. Dies kann als eigenständiger VBus-LAN Adapter aber auch ein andere Gerät mit integriertem VBus live port wie einem KM2, DL2/3 oder sonstiges sein.
|
||||
.
|
||||
# thing type config description
|
||||
# thing-type.config.resol.device does not have configuration parameters
|
||||
thing-type.config.resol.emulatedEM.deviceId.label = Modul ID
|
||||
thing-type.config.resol.emulatedEM.deviceId.description = Subaddress des emulierten EM Moduls. Der verwendbare Bereich hängt vom verwendeten Regler ab.
|
||||
thing-type.config.resol.vbuslan.ipAddress.label = IP-Adresse
|
||||
thing-type.config.resol.vbuslan.ipAddress.description = IP-Adresse der VBus-LAN Schnittstelle.
|
||||
thing-type.config.resol.vbuslan.port.label = Live Data Port
|
||||
thing-type.config.resol.vbuslan.port.description = TCP-Port der Live-Data-Schnittstelle des VBus Gateways.
|
||||
thing-type.config.resol.vbuslan.adapterSerial.label = Adapter Seriennummer
|
||||
thing-type.config.resol.vbuslan.adapterSerial.description = Seriennummer des VBus-LAN-Schnittstellengerätes (nur zur Information).
|
||||
thing-type.config.resol.vbuslan.password.label = Passwort
|
||||
thing-type.config.resol.vbuslan.password.description = Passwort für die VBus-LAN Schnittstelle.
|
||||
thing-type.config.resol.vbuslan.refreshInterval.label = Aktualisierungsintervall
|
||||
thing-type.config.resol.vbuslan.refreshInterval.description = Zeitintervall in Sekunden um die Verbindung zur VBus-LAN-Schnittstelle zu prüfen. Die Aktualisierung der Daten geschieht unabhängig hiervon sobald sie auf dem VBus empfangen werden.
|
||||
|
||||
# channel types
|
||||
channel-type.resol.relay.label = Relais Zustand
|
||||
channel-type.resol.relay.description = Virtueller Relay Ausgang welcher vom Regler gesetzt wird und verwendet werden kann um den Zustand vom Resol Regler zu openHAB zu übertragen.
|
||||
channel-type.resol.temperature.label = Temperatur
|
||||
channel-type.resol.temperature.description = Virtueller Temperatur Sensor Eingang.
|
||||
channel-type.resol.resistance.label = Widerstand
|
||||
channel-type.resol.resistance.description = Virtueller Widerstandseingang.
|
||||
channel-type.resol.switch.label = Schalter
|
||||
channel-type.resol.switch.description = Virtueller Schaltereingang.
|
||||
channel-type.resol.temperatureAdjust.label = Temperatur Anpassung
|
||||
channel-type.resol.temperatureAdjust.description = Virtueller Eingang zur Parallelverschiebung der Temperatur eines Heizkreises an einem emulierten BAS (Raumbediengerät RCP12).
|
||||
channel-type.resol.operationmode.label = Betriebsart
|
||||
channel-type.resol.operationmode.description = Virtueller Eingang zur Betriebsartumschaltung eines Heizkreises an einem emulierten BAS (Raumbediengerät RCP12).
|
||||
channel-type.resol.operationmode.state.option.0 = Aus
|
||||
channel-type.resol.operationmode.state.option.1 = Sommer
|
||||
channel-type.resol.operationmode.state.option.2 = Nacht
|
||||
channel-type.resol.operationmode.state.option.3 = Party
|
||||
channel-type.resol.operationmode.state.option.4 = Automatik
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="resol"
|
||||
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="vbuslan">
|
||||
<label>Bridge VBusLAN Adapter</label>
|
||||
<description>This bridge represents the Resol VBus-LAN adapter which can be any device with a TCP/IP live port, either
|
||||
the standalone device VBus-LAN Adapter, KM2, DL2/3 or similar.
|
||||
</description>
|
||||
<representation-property>ipAddress</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="ipAddress" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>IP Address</label>
|
||||
<description>The IP address of the of the VBus-LAN gateway.</description>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" required="false" min="1024" max="65535">
|
||||
<label>Live Data Port</label>
|
||||
<description>Port for live data on the VBUS-LAN gateway.</description>
|
||||
<default>7053</default>
|
||||
</parameter>
|
||||
<parameter name="adapterSerial" type="text" required="false">
|
||||
<label>Adapter Serial Number</label>
|
||||
<description>The serial number of the adapter (informative).</description>
|
||||
</parameter>
|
||||
<parameter name="password" type="text" required="true">
|
||||
<label>Password</label>
|
||||
<description>The password for the VBusLAN connection.</description>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer" required="false" min="5" max="1800" unit="s">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Refresh time in seconds to check the connection to the VBus gateway. Data updates are propagated to
|
||||
openHAB independently from this setting as soon as they are received on the VBus.</description>
|
||||
<default>300</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="resol"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="device">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="vbuslan"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Resol Device</label>
|
||||
<description>Solar or system controller (or any other real device on the VBus) from Resol.</description>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="emulatedEM">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="vbuslan"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Emulated EM Device</label>
|
||||
<description>Emulation of an Extension Module (EM) device which can be connected through the VBUS to Resol controllers
|
||||
which support the EM devices. Replaces a physically available EM by openHAB.</description>
|
||||
<channels>
|
||||
<channel id="relay_1" typeId="relay">
|
||||
<label>Relay 1</label>
|
||||
</channel>
|
||||
<channel id="relay_2" typeId="relay">
|
||||
<label>Relay 2</label>
|
||||
</channel>
|
||||
<channel id="relay_3" typeId="relay">
|
||||
<label>Relay 3</label>
|
||||
</channel>
|
||||
<channel id="relay_4" typeId="relay">
|
||||
<label>Relay 4</label>
|
||||
</channel>
|
||||
<channel id="relay_5" typeId="relay">
|
||||
<label>Relay 5</label>
|
||||
</channel>
|
||||
|
||||
<channel id="switch_1" typeId="switch">
|
||||
<label>Switch 1</label>
|
||||
</channel>
|
||||
<channel id="switch_2" typeId="switch">
|
||||
<label>Switch 2</label>
|
||||
</channel>
|
||||
<channel id="switch_3" typeId="switch">
|
||||
<label>Switch 3</label>
|
||||
</channel>
|
||||
<channel id="switch_4" typeId="switch">
|
||||
<label>Switch 4</label>
|
||||
</channel>
|
||||
<channel id="switch_5" typeId="switch">
|
||||
<label>Switch 5</label>
|
||||
</channel>
|
||||
<channel id="switch_6" typeId="switch">
|
||||
<label>Switch 6</label>
|
||||
</channel>
|
||||
|
||||
<channel id="temperature_1" typeId="temperature">
|
||||
<label>Temperature 1</label>
|
||||
</channel>
|
||||
<channel id="temperature_2" typeId="temperature">
|
||||
<label>Temperature 2</label>
|
||||
</channel>
|
||||
<channel id="temperature_3" typeId="temperature">
|
||||
<label>Temperature 3</label>
|
||||
</channel>
|
||||
<channel id="temperature_4" typeId="temperature">
|
||||
<label>Temperature 4</label>
|
||||
</channel>
|
||||
<channel id="temperature_5" typeId="temperature">
|
||||
<label>Temperature 5</label>
|
||||
</channel>
|
||||
<channel id="temperature_6" typeId="temperature">
|
||||
<label>Temperature 6</label>
|
||||
</channel>
|
||||
|
||||
<channel id="resistor_1" typeId="resistance">
|
||||
<label>Resistor 1</label>
|
||||
</channel>
|
||||
<channel id="resistor_2" typeId="resistance">
|
||||
<label>Resistor 2</label>
|
||||
</channel>
|
||||
<channel id="resistor_3" typeId="resistance">
|
||||
<label>Resistor 3</label>
|
||||
</channel>
|
||||
<channel id="resistor_4" typeId="resistance">
|
||||
<label>Resistor 4</label>
|
||||
</channel>
|
||||
<channel id="resistor_5" typeId="resistance">
|
||||
<label>Resistor 5</label>
|
||||
</channel>
|
||||
<channel id="resistor_6" typeId="resistance">
|
||||
<label>Resistor 6</label>
|
||||
</channel>
|
||||
|
||||
<channel id="bas_temp_adjust_1" typeId="temperatureAdjust">
|
||||
<label>Temperature Adjustment 1</label>
|
||||
</channel>
|
||||
<channel id="bas_temp_adjust_2" typeId="temperatureAdjust">
|
||||
<label>Temperature Adjustment 2</label>
|
||||
</channel>
|
||||
<channel id="bas_temp_adjust_3" typeId="temperatureAdjust">
|
||||
<label>Temperature Adjustment 3</label>
|
||||
</channel>
|
||||
<channel id="bas_temp_adjust_4" typeId="temperatureAdjust">
|
||||
<label>Temperature Adjustment 4</label>
|
||||
</channel>
|
||||
<channel id="bas_temp_adjust_5" typeId="temperatureAdjust">
|
||||
<label>Temperature Adjustment 5</label>
|
||||
</channel>
|
||||
<channel id="bas_temp_adjust_6" typeId="temperatureAdjust">
|
||||
<label>Temperature Adjustment 6</label>
|
||||
</channel>
|
||||
|
||||
<channel id="bas_mode_1" typeId="operationmode">
|
||||
<label>Operating Mode 1</label>
|
||||
</channel>
|
||||
<channel id="bas_mode_2" typeId="operationmode">
|
||||
<label>Operating Mode 2</label>
|
||||
</channel>
|
||||
<channel id="bas_mode_3" typeId="operationmode">
|
||||
<label>Operating Mode 3</label>
|
||||
</channel>
|
||||
<channel id="bas_mode_4" typeId="operationmode">
|
||||
<label>Operating Mode 4</label>
|
||||
</channel>
|
||||
<channel id="bas_mode_5" typeId="operationmode">
|
||||
<label>Operating Mode 5</label>
|
||||
</channel>
|
||||
<channel id="bas_mode_6" typeId="operationmode">
|
||||
<label>Operating Mode 6</label>
|
||||
</channel>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="integer" required="true" min="1" max="15">
|
||||
<label>Module ID</label>
|
||||
<description>The (sub-)address of the emulated EM device, usable range depends on the used controller.</description>
|
||||
<default>1</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="None">
|
||||
<item-type>Number</item-type>
|
||||
<label>Any</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="NoneHidden" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Any</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="relay" advanced="false">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Relay State</label>
|
||||
<description>Virtual relay output, will be set by the controller and can be used to communicate data from the Resol
|
||||
controller to openHAB.</description>
|
||||
<category>Switch</category>
|
||||
<state pattern="%d %%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperature" advanced="false">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Virtual temperature sensor input.</description>
|
||||
<category>Temperature</category>
|
||||
<state pattern="%.1f %unit%" readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="resistance" advanced="false">
|
||||
<item-type>Number:ElectricResistance</item-type>
|
||||
<label>Resistance</label>
|
||||
<description>Virtual resistance input.</description>
|
||||
<state pattern="%.1f %unit%" readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switch" advanced="false">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Switch</label>
|
||||
<description>Virtual switch input.</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperatureAdjust" advanced="false">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature Adjustment</label>
|
||||
<description>Virtual temperature offset on heating circuit of an emulated BAS (RCP12 room control unit).</description>
|
||||
<category>Temperature</category>
|
||||
<state pattern="%.1f %unit%" readOnly="false" min="-15" max="15"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="operationmode" advanced="false">
|
||||
<item-type>Number</item-type>
|
||||
<label>Operating Mode</label>
|
||||
<description>Virtual operating mode of the heating circuit controlled by the emulated BAS (RCP12 room control unit).</description>
|
||||
<state pattern="%d" readOnly="false" min="0" max="4" step="1">
|
||||
<options>
|
||||
<option value="0">OFF</option>
|
||||
<option value="1">Summer</option>
|
||||
<option value="2">Night</option>
|
||||
<option value="3">Party</option>
|
||||
<option value="4">Automatic</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user