[resol] Add Resol Controller Binding - Initial Contribution (#9449)

Signed-off-by: Raphael Mack <ramack@raphael-mack.de>
This commit is contained in:
Raphael Mack 2021-04-11 19:25:55 +02:00 committed by GitHub
parent 16ffeecb90
commit 9bfb2f4313
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2211 additions and 0 deletions

View File

@ -227,6 +227,7 @@
/bundles/org.openhab.binding.regoheatpump/ @crnjan
/bundles/org.openhab.binding.revogi/ @andibraeu
/bundles/org.openhab.binding.remoteopenhab/ @lolodomo
/bundles/org.openhab.binding.resol/ @ramack
/bundles/org.openhab.binding.rfxcom/ @martinvw @paulianttila
/bundles/org.openhab.binding.rme/ @kgoderis
/bundles/org.openhab.binding.robonect/ @reyem

View File

@ -1111,6 +1111,11 @@
<artifactId>org.openhab.binding.remoteopenhab</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.resol</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.rfxcom</artifactId>

View File

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

View File

@ -0,0 +1,251 @@
# Resol Binding
Resol Binding connects to Solar and System Controllers of RESOL - Elektronische Regelungen GmbH, also including branded versions from Viessmann, SOLEX, COSMO, SOLTEX, DeDietrich and many more.
![Resol](doc/RESOL_Logo_de.png)
This binding is based on and includes the [Resol-VBUS-Java library](https://github.com/danielwippermann/resol-vbus-java), developed by Daniel Wippermann.
## Supported Things
### Bridge
For the connection of the VBUS devices a network interface is required.
Supported interfaces are VBUS-LAN, KM1, KM2, DataLogger DL2 and DL3 as live data interface between openHAB and the Resol VBUS via network.
On the DL3 currently there is only the first VBUS channel supported and the sensors directly connected to the DL3 are not accessible via this binding.
Currently only network based bridges are supported and if a USB-VBUS interface shall be used a serial-network proxy has to be used.
In the binding might support USB-type bridges in future.
### Device
On top of the bridge things, which enable access to the VBUS, many - if not all - Resol Controllers and Modules like WMZ heat meters, HKM Heating circuit extensions etc. are supported including branded versions from different suppliers as thing type *device*.
The supported devices include
* Solar controller DeltaSol® A/AX/AX HE
* Solar controller DeltaSol® AL E HE
* Solar controller DeltaSol® CS (Plus)
* Solar controller DeltaSol® B
* Solar controller DeltaSol® BS series
* Solar controller DeltaSol® SLL
* Solar controller DeltaSol® SL
* Solar controller DeltaSol® BX series
* System controller DeltaSol® SLT
* System controller DeltaSol® MX
* System controller DeltaSol® E
* DeltaSol Fresh®
* DeltaSol® Pool
* DeltaSol® Minipool
* DeltaSol® ES
* Frista
* DeltaTherm® HC
* DeltaTherm® FK
* Deltatherm® HT
* DeltaTherm® DH
* Sonnenkraft SKSC3
* Sonnenkraft STRG BX PLUS
* Sonnenkraft SKSC3+
* Sonnenkraft SKSC3HE
* Sonnenkraft SKSR 1/2/3
* Vitosolic 200
* COSMO Multi
* Drainback DeDietrich
* Diemasol C
A more complete list can be found in the doc of the [resol-vbus-java library](http://danielwippermann.github.io/resol-vbus/vbus-packets.html).
### Emulated Extension Module EM
Some controllers like the Deltasol MX can be connected to an extension module, which can be emulated by the thing type *emulatedEM*.
The emulated EM is a virtual device, visible on the VBUS to a Resol controller and provides an interface between openHAB and the controller.
Relay channels are outputs from the controller point of view and therefore read-only in OH.
The sensor channels as inputs for the solar or system controller and intended to be written by OH.
## Discovery
Discovery is tested for VBus-LAN adapters DL2, DL3 and KM2 devices, it should also work for other devices providing a live data port.
After a bridge is detected in the local network the password needs to be given and the things on the VBUS will popup in the inbox.
## Bridge Configuration
The bridge is the device connecting the Resol VBUS to the network, usually a VBus-LAN adapter or integrated in some of the solar controllers like DL3.
For the connection from the Resol binding the bridge requires the configuration of the following parameters:
| Parameter | Type | Required | Description |
|----------------------|---------|----------|------------------------------------------------------------|
| ipAddress | String | yes | IP address or hostname of the VBUS adapter |
| password | String | yes | Password, defaults to 'vbus' for factory setting devices |
| port | Number | no | Port for the TCP connection, defaults to 7053 |
| adapterSerial | String | no | Serialnumber of the device (informative only) |
## Device Configuration
Depending on the solar/heating controller you have attached to your VBUS there will be a "controller" and several other things like heat quantity meters, heating circuit controls, etc.
These do not require any configuration parameters and will pop up in your inbox after the bridge has received data from them.
The name of the devices is usually the Resol name with spaced replaced by _ and a "-Controller" suffix like "DeltaSol_MX-Controller".
For configuration in files you can enable the logging with at least DEBUG level for the resol binding and search the logs for "ThingHandler for (.*) not registered." to identify the names of the things you can add for your VBUS devices.
## Emulated EM Configuration
*emulatedEM* devices cannot be auto-discovered and require beside the bridge the following configuration:
| Parameter | Type | Required | Description |
|-----------|------|----------|-----------------------------------------------------------------------------------------------------------------|
| moduleID | int | yes | The module ID on the VBUS in range 0-15, but further restrictions might apply depending on the resol controller. |
## Device Channels
The channels of a thing are determined automatically based on the received VBUS data and are highly dependent on the used device.
Here is a list of the channels of a DeltaSol MX with a heat quantity meter (HQM) and an extension module EM.
The channels supported for your device can be seen in the UI or in the logs if DEBUG logging is enabled for this binding after data is received from the physical device.
| Channel | Type | Description |
|-----------------------------------|--------------------------|----------------------------------------------------|
| pump_speed_relay_x | Number:Dimensionless | Percentage of the output state of relay 'x' |
| temperature_sensor_x | Number:Temperature | Temperature sensor 'x' of the controller |
| temperature_module_y_sensor_x | Number:Temperature | Temperature sensor 'x' of the extension module 'y' |
| pressure_sensor_x | Number:Pressure | Pressure sensor 'x' |
| humidity_sensor_x | Number:Dimensionless | Humidity sensor 'x' |
| irradiation_sensor_x | Number:Intensity | Sunlight intensity sensor |
| output_m | Number:Dimensionless | PWM/0-10V level value of the output 'm' |
| system_date | DateTime | Date and time of the controller clock |
| error_mask | Number | Bitmask for the different errors |
| error_sensor_line_broken | Number | Sensor line broken status (details for error_mask) |
| error_sensor_line_short-circuited | Number | Sensor short circuit status (details for error_mask) |
| flow_rate_sensor_x | Number:VolumetricFlowRate| Flow rate of sensor 'x' |
| flow_set_temperature | Number:Temperature | Heating circuit set temperature |
| operating_state | Number | Heating circuit operating state |
| heat_quantity | Number:Energy | Total heat quantity (of a HQM) |
| heat_quantity_today | Number:Energy | Todays heat quantity (of a HQM) |
| heat_quantity_week | Number:Energy | This weeks heat quantity (of a HQM) |
| heat_quantity_month | Number:Energy | This months heat quantity (of a HQM) |
| volume_in_total | Number:Volume | Total volume (of a HQM) |
| volume_today | Number:Volume | Todays volume (of a HQM) |
| volume_week | Number:Volume | This weeks volume (of a HQM) |
| volume_month | Number:Volume | This months volume (of a HQM) |
| power | Number:Power | Current power (of a HQM) |
Channels are dynamically created dependent on the devices connected to the VBus.
So far only reading is supported.
The classical channels are for temperature sensors and the like, but also relay outputs with the output level (0-100%) are visible as numerical values with the corresponding unit.
String values are localized as far as possible, but only French, German and English are supported by the underlaying library which is based on the vbus-specification file from Resol.
## EmulatedEM Channels
The channels of an emulated EM modules are as for physical EMs 5 relay channels and 6 input channels.
The relays are virtual outputs and read-only in OH.
The sensors support different types like temperature input which are simulated by a PT1000 resistance value, a switch and the raw resistance value.
Additionally the virtual input device for adjusting the heating circuits as a *BAS* is supported by two different channels for temperature and mode adjustment.
The type of the sensor inputs must be configured in the Resol Controller accordingly.
From all possible sensor channels (temperatureX, switchX, etc.) only one shall be linked to an item at a time, except for BAS which emulates a RCP12 room control unit where both, BasTempAdjustmentX and BasModeX shall be written from OH.
| Channel | Type | Description |
|----------------------|---------------------------|----------------------------------------------------|
| relayX | Number:Dimensionless | Read-only percentage of the virtual output state of relay 'x' as set by the Resol Controller. |
| temperatureX | Number:Temperature | Writable temperature value for the virtual input for sensor 'x'. |
| resistorX | Number:ElectricResistance | Writable resistance value for the virtual input for sensor 'x'. |
| switchX | Switch | Writable switch state for the virtual input for sensor 'x'. |
| BasTempAdjustmentX | Number:Temperature | Writable temperature adjustment for the virtual room control module BAS on the for the virtual input for sensor 'x'. Use together with BasModeX, not effective if BasModeX is OFF or Party. |
| BasModeX | Number | Writable heating circuit mode for the virtual room control module BAS on the for the virtual input for sensor 'x'. Use together with BasTempAdjustmentX.|
## Full Example
For a DeltaSol MX system controller with on extension module EM you can use this example:
resol.things
```
Bridge resol:vbuslan:VBUS "VBUSLAN" [ ipAddress="192.168.0.2", password="vbus", port=7053] {
Thing device DeltaSol_MX-Controller "DeltaSol MX [Controller]" []
Thing device DeltaSol_MX-Heating_circuit-1 "DeltaSol MX [Heating Circuit]" []
Thing device DeltaSol_MX-HQM-1 "DeltaSol MX [WMZ 1] Solar" []
Thing device DeltaSol_MX-Modules "DeltaSol MX [Modules]" []
Thing emulatedEM EM2 "Emulated EM2" [deviceId=2]
}
```
resol.items
```
/*************************************************/
/* Solar system */
/*************************************************/
Number:Temperature SolarTemperature "Solar Collector Temperature [%.1f %unit%]" <temperature> { channel="resol:device:VBUS:DeltaSol_MX-Controller:temperature_sensor_1" }
Number:Temperature TankTemperature "Solar Tank Temperature [%.1f %unit%]" <temperature> { channel="resol:device:VBUS:DeltaSol_MX-Controller:temperature_sensor_2" }
Number:Intensity Irradiation "Irradiation [%.1f %unit%]" <sun> {channel="resol:device:VBUS:DeltaSol_MX-Controller:irradiation_sensor_16"}
Number SolarPump "Solar pump [%.0f %%]" {channel="resol:device:VBUS:DeltaSol_MX-Controller:pump_speed_relay_1"}
/*************************************************/
/* Heating circuit */
/*************************************************/
Number:Temperature FlowSetTemperature "Flow Set Temperature [%.1f %unit%]" <temperature> {channel="resol:device:VBUS:DeltaSol_MX-Heating_circuit-1:flow_set_temperature"}
String HeatCircuit_OperatingState "HeatCircuit OperatingState [%s]" {channel="resol:device:VBUS:DeltaSol_MX-Heating_circuit-1:operating_state"}
/*************************************************/
/* Heat quantity meter */
/*************************************************/
Number:Energy SolarEnergy_today "Solar Energy (today) [%.1f %unit%]" {channel="resol:device:VBUS:DeltaSol_MX-HQM-1:heat_quantity_today"}
Number:Power SolarPower "Solar Power [%.0f %unit%]" {channel="resol:device:VBUS:DeltaSol_MX-HQM-1:power"}
/*************************************************/
/* Physical EM Module 1 */
/*************************************************/
Number:Temperature EM_Temperature_1 "Temperature EM sensor 1 [%.1f %unit%]" <temperature> {channel="resol:device:VBUS:DeltaSol_MX-Modules:temperature_module_1_sensor_1"}
/*************************************************/
/* Virtual EM Module 2, simulated by openHAB */
/*************************************************/
Number:Dimensionless Relay_1 "Virtual Relay 1 on EM2 [%d %%] {channel="resol:emulatedEM:VBUS:EM2:relay_1"}
Number:Dimensionless Emu_Temperature_1 "Virtual temperature input 1 on EM2 [%.1f %unit%] <temperature> {channel="resol:emulatedEM:VBUS:EM2:temperature_1"}
Switch Emu_Switch_2 "Virtual switch input 2 on EM2 " {channel="resol:emulatedEM:VBUS:EM2:switch_2"}
Number:Temperature EM_BAS_Set_Temperature_3 "Set Temperature of virtual room control unit on EM2 sensor 3 [%.1f %unit%]" <temperature> {channel="resol:emulatedEM:VBUS:EM2:bas_temp_adjust_3"}
Number EM_BAS_Mode "Mode of virtual room control unit on EM2 sensor 3 [%.1f %unit%]" <temperature> {channel="resol:emulatedEM:VBUS:EM2:bas_mode_3"}
/*************************************************/
/* Failure handling */
/*************************************************/
Number Errormask "Error mask [%.0f]" {channel="resol:device:VBUS:DeltaSol_MX-Controller:error_mask"}
Number Warningmask "Warning mask [%.0f]" {channel="resol:device:VBUS:DeltaSol_MX-Controller:warning_mask"}
String BrokenSensor "Broken Sensor [%s]" {channel="resol:device:VBUS:DeltaSol_MX-Controller:error_Sensor_line_broken"}
```
resol.sitemap
```
sitemap resol label="DeltaSol MX" {
Frame label="Solar" {
Text item=SolarTemperature valuecolor=[<0="white", <20="blue", <50="green", <80="orange", <120="red", >=120="black"]
Text item=TankTemperature valuecolor=[<30="blue", <50="yellow", <70="orange", >=70="red"]
Text item=Irradiation
Text item=SolarPump valuecolor=[==0="red",==100="green", >50="orange", >0="yellow"]
Text item=SolarPower
Text item=SolarEnergy_today
}
Frame label="Status" {
Text item=Errormask valuecolor=[==0="green", !=0="red"]
Text item=Warningmask valuecolor=[==0="green", !=0="red"]
Text item=BrokenSensor valuecolor=[=="Okay"="green", !="Okay"="red"]
}
Frame label="Emulated EM" {
Default item=Emu_Switch_2
Setpoint item=EM_BAS_Set_Temperature_3 label="Room Temperature Adjust [%.1f °C]" step=0.5 minValue=-15 maxValue=15
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.resol</artifactId>
<name>openHAB Add-ons :: Bundles :: Resol Binding</name>
<dependencies>
<dependency>
<groupId>de.resol</groupId>
<artifactId>vbus</artifactId>
<version>0.7.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -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>

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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 */
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -258,6 +258,7 @@
<module>org.openhab.binding.regoheatpump</module>
<module>org.openhab.binding.revogi</module>
<module>org.openhab.binding.remoteopenhab</module>
<module>org.openhab.binding.resol</module>
<module>org.openhab.binding.rfxcom</module>
<module>org.openhab.binding.rme</module>
<module>org.openhab.binding.robonect</module>