mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[resol] Add Resol Controller Binding - Initial Contribution (#9449)
Signed-off-by: Raphael Mack <ramack@raphael-mack.de>
This commit is contained in:
parent
16ffeecb90
commit
9bfb2f4313
@ -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
|
||||
|
@ -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>
|
||||
|
20
bundles/org.openhab.binding.resol/NOTICE
Normal file
20
bundles/org.openhab.binding.resol/NOTICE
Normal 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
|
251
bundles/org.openhab.binding.resol/README.md
Normal file
251
bundles/org.openhab.binding.resol/README.md
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
```
|
BIN
bundles/org.openhab.binding.resol/doc/RESOL_Logo_de.png
Normal file
BIN
bundles/org.openhab.binding.resol/doc/RESOL_Logo_de.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.0 KiB |
26
bundles/org.openhab.binding.resol/pom.xml
Normal file
26
bundles/org.openhab.binding.resol/pom.xml
Normal 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>
|
@ -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>
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user