mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[modbus.lambda] Initial contribution (#19378)
* New Crowdin updates (#19375) Signed-off-by: Christian Koch <78686276+chilobo@users.noreply.github.com> Signed-off-by: Johannes Koch <github@lockhead.net>
This commit is contained in:
@@ -246,6 +246,7 @@
|
||||
/bundles/org.openhab.binding.modbus.e3dc/ @weymann
|
||||
/bundles/org.openhab.binding.modbus.foxinverter/ @holgerfriedrich
|
||||
/bundles/org.openhab.binding.modbus.helioseasycontrols/ @bern77
|
||||
/bundles/org.openhab.binding.modbus.lambda/ @chilobo
|
||||
/bundles/org.openhab.binding.modbus.kermi/ @KaaNee
|
||||
/bundles/org.openhab.binding.modbus.sbc/ @fwolter
|
||||
/bundles/org.openhab.binding.modbus.stiebeleltron/ @pail23
|
||||
|
||||
@@ -1221,6 +1221,11 @@
|
||||
<artifactId>org.openhab.binding.modbus.kermi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.modbus.lambda</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.modbus.sbc</artifactId>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
@@ -0,0 +1,314 @@
|
||||
# Lambda Heat Pump
|
||||
|
||||
This extension adds support for the Lambda Heat Pump modbus protocol as provided by
|
||||
<https://lambda-wp.at/wp-content/uploads/2025/02/Modbus-Beschreibung-und-Protokoll.pdf>
|
||||
|
||||
A Lambda Heat Pump has to be reachable within your network.
|
||||
If you plan to use the E-Manager part to hand over your PV excess to the heat pump ask Lambda support to configure it to
|
||||
E-Meter Kommunikationsart: ModBus Client
|
||||
E-Meter Messpunkt: E-Eintrag
|
||||
|
||||
Other configurations of the E-Manager are not supported (yet).
|
||||
|
||||
## Supported Things
|
||||
|
||||
This bundle adds the following thing types to the Modbus binding.
|
||||
Note, that the things will show up under the Modbus binding.
|
||||
|
||||
| Thing | ThingTypeID | Description |
|
||||
| --------------- | --------------------| --------------------------------------- |
|
||||
| General | general | General sections Ambient and E-Manager |
|
||||
| Heat Pump | heat-pump | Heat Pump section |
|
||||
| Boiler | boiler | Boiler section |
|
||||
| Buffer | buffer | Buffer section |
|
||||
| Solar | solar | Solar section |
|
||||
| Heating Circuit | heating-circuit | Heating Circuit section |
|
||||
|
||||
A Modbus Bridge has to be installed before installing the above mentioned things.
|
||||
The binding supports installations with more than one Heat Pump, Boiler, Buffer, Heating Circuit.
|
||||
For each of these parts you have to provide the Subindex of your thing in the configurations section, usually using the User Interface.
|
||||
So if you have two Heating Circuits use 0 for the first and 1 for the second Heating Circuit.
|
||||
Handling of General System Settings (Base Adress 200) is not supported (yet). Solar functions (Base Address 4000) are now supported.
|
||||
Some of the registers noted RW in the manual are read only in the binding.
|
||||
|
||||
## Discovery
|
||||
|
||||
This extension does not support autodiscovery. The things need to be added manually.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
You first need to set up a TCP Modbus bridge according to the Modbus documentation.
|
||||
A typical modbus bridge configuration would look like this:
|
||||
|
||||
```java
|
||||
Bridge modbus:tcp:bridge [ host="10.0.0.2", port=502, id=1 ]
|
||||
```
|
||||
|
||||
You then add the things of the Lambda Heat Pump system as part of the modbus binding.
|
||||
Things in this extension will use the selected bridge to connect to the device.
|
||||
|
||||
The following parameters are valid for all things:
|
||||
|
||||
| Parameter | Type | Required | Default if omitted | Description |
|
||||
| --------- | ------- | -------- | ------------------ | -------------------------------------------------------------------------- |
|
||||
| refresh | integer | no | 30 | Poll interval in seconds. Increase this if you encounter connection errors |
|
||||
| maxTries | integer | no | 3 | Number of retries when before giving up reading from this thing. |
|
||||
|
||||
Heat Pump, Boiler, Buffer and Heating Circuit things use another parameter Subindex to add one or more things of this type:
|
||||
|
||||
| Parameter | Type | Required | Default if omitted | Description |
|
||||
| --------- | ------- | -------- | ------------------ | -------------------------------------------------------------------------- |
|
||||
| Subindex | integer | yes | 0 | Subindex for things of the same thing type, starting with 0 |
|
||||
|
||||
| Thing Type | Range |
|
||||
| ---------------- | ------- |
|
||||
| Heatpump | 0..2 |
|
||||
| Boiler | 0..4 |
|
||||
| Buffer | 0..4 |
|
||||
| Solar | 0..2 |
|
||||
| Heating Circuit | 0..11 |
|
||||
|
||||
## Channels
|
||||
|
||||
Channels within the things are grouped into channel groups.
|
||||
|
||||
### General:
|
||||
|
||||
### Ambient Group
|
||||
|
||||
| Channel ID | Item Type | Read only | Description |
|
||||
| -------------------------------- | ------------------ | --------- | ------------------------------------------------------------------------ |
|
||||
| ambient-error-number | Number | true | Ambient Error Number (0 = No error) |
|
||||
| ambient-operating-state | Number | true | Ambient Operating State (0 = OFF, 1 = AUTOMATIC, 2 = MANUAL, 3 = ERROR) |
|
||||
| actual-ambient-temperature | Number:Temperature | false | Actual Ambient Temperature |
|
||||
| average-ambient-temperature | Number:Temperature | true | Arithmetic average temperature of the last 60 minutes |
|
||||
| calculated-ambient-temperature | Number:Temperature | true | Temperature for calculations in heat distribution modules |
|
||||
|
||||
### E-Manager Group
|
||||
|
||||
This group contains parameters signaling the PV excess to the heat pump.
|
||||
|
||||
| Channel ID | Item Type | Read only | Description |
|
||||
| -------------------------------- | ------------------ | --------- | -------------------------------------------------------------------------------------- |
|
||||
| emanager-error-number | Number | true | E-Manager Error Number (0 = No error) |
|
||||
| emanager-operating-state | Number | true | E-Manager Operating State (0 = OFF, 1 = AUTOMATIC, 2 = MANUAL, 3 = ERROR 4 = OFFLINE |
|
||||
| actual-power | Number:Power | false | Actual excess power 0 W .. 65535 W |
|
||||
| actual-power-signed | Number:Power | false | Actual excess power -32768 W .. 32767 W |
|
||||
| actual-power-consumption | Number:Power | true | Power consumption of heat-pump (only valid when Betriebsart: Automatik, 0 W otherwise) |
|
||||
| power-consumption-setpoint | Number:Power | false | Power consumption setpoint for heat pump 1 |
|
||||
|
||||
### Heat Pump Group
|
||||
|
||||
This group contains general operational information about the heat pump itself.
|
||||
|
||||
| Channel ID | Item Type | Read only | Description |
|
||||
| -------------------------------| --------------------------| --------- | ----------------------------------------------------------------------- |
|
||||
| heat-pump-error-state | Number | true | Error state (0 = NONE, 1 = MESSAGE, 2 = WARNING, 3 = ALARM, 4 = FAULT) |
|
||||
| heat-pump-error-number | Number | true | Error number: scrolling through all active error numbers (1..99) |
|
||||
| heat-pump-state | Number | true | State: See Modbus description manual, link above |
|
||||
| heat-pump-operating-state | Number | true | Operating State: See Modbus description manual, link above |
|
||||
| heat-pump-t-flow | Number:Temperature | true | Flow line termperature |
|
||||
| heat-pump-t-return | Number:Temperature | true | Return line temperature |
|
||||
| heat-pump-vol-sink | Number:VolumetricFlowRate | true | Volume flow heat sink |
|
||||
| heat-pump-t-eqin | Number:Temperature | true | Energy source inlet temperature |
|
||||
| heat-pump-t-eqout | Number:Temperature | true | Energy source outlet temperature |
|
||||
| heat-pump-vol-source | Number:VolumetricFlowRate | true | Volume flow energy source |
|
||||
| heat-pump-compressor-rating | Number | true | Compressor unit rating |
|
||||
| heat-pump-qp-heating | Number:Power | true | Actual heating capacity |
|
||||
| heat-pump-fi-power-consumption | Number:Power | true | Frequency inverter actual power consumption |
|
||||
| heat-pump-cop | Number | true | Coefficient of performance |
|
||||
| heat-pump-request-password | Number | false | Password register to release modbus request registers |
|
||||
| heat-pump-request-type | Number: | false | Request Type |
|
||||
| heat-pump-request-t-flow | Number:Temperature | false | Requested flow line termperature |
|
||||
| heat-pump-request-t-return | Number:Temperature | false | Requested return line temperature |
|
||||
| heat-pump-request-heat-sink | Number:Temperature | false | Requested temperature difference between flow and return line |
|
||||
| heat-pump-relais-state | Number:Temperature | true | Heatpump Relais State for 2nd heating stage |
|
||||
| heat-pump-vdae | Number:Energy | true | Accumulated electrical energy consumption of compressor unit |
|
||||
| heat-pump-vdaq | Number:Energy | true | Accumulated thermal energy output of compressor unit |
|
||||
|
||||
### Boiler Group
|
||||
|
||||
This group contains information about the boiler for the water for domestic use / tap water / washwater.
|
||||
|
||||
| Channel ID | Item Type | Read only | Description |
|
||||
| ------------------------------------- | ------------------ | --------- | ------------------------------------------------------------------- |
|
||||
| boiler-error-number | Number | true | Boiler Error Number (0 = No error) |
|
||||
| boiler-operating-state | Number | true | Boiler Operating State: See Modbus description manual, link above |
|
||||
| boiler-actual-high-temperature | Number:Temperature | true | Actual temperature boiler high sensor |
|
||||
| boiler-actual-low-temperature | Number:Temperature | true | Actual temperature boiler low sensor |
|
||||
| maximum-boiler-temperature | Number:Temperature | false | Setting for maximum boiler temperature (min = 25.0°C; max = 65.0°C) |
|
||||
|
||||
### Buffer Group
|
||||
|
||||
This group contains information about the buffer for the heating circuit.
|
||||
|
||||
| Channel ID | Item Type | Read only | Description |
|
||||
| ------------------------------------------------| ------------------ | --------- | ---------------------------------------------------------------------|
|
||||
| buffer-error-number | Number | true | Buffer Error Number (0 = No error) |
|
||||
| buffer-operating-state | Number | true | Buffer Operating State: See Modbus description manual, link above |
|
||||
| buffer-actual-high-temperature | Number:Temperature | true | Actual temperature buffer high sensor |
|
||||
| buffer-actual-low-temperature | Number:Temperature | true | Actual temperature buffer low sensor |
|
||||
| buffer-actual-modbus-temperature | Number:Temperature | false | Actual temperature set via modbus |
|
||||
| buffer-request-type | Number | false | Request Type: See Modbus description manual, link above |
|
||||
| buffer-request-flow-line-temperature | Number:Temperature | false | Requested flow line temperature |
|
||||
| buffer-request-return-line-temperature | Number:Temperature | false | Requested return line temperature |
|
||||
| buffer-request-heat-sink-temperature-difference | Number:Temperature | false | Requested temperature difference between flow line and return line |
|
||||
| buffer-request-heating-capacity | Number:Power | false | Requested capacity |
|
||||
| maximum-buffer-temperature | Number:Temperature | false | Setting for maximum buffer temperature ) |
|
||||
### Solar Group
|
||||
|
||||
This group contains information about the solar thermic component.
|
||||
|
||||
| Channel ID | Item Type | Read only | Description |
|
||||
| ------------------------------ | ------------------ | --------- | ------------------------------------------------------------------- |
|
||||
| solar-error-number | Number | true | Solar Error Number (0 = No error) |
|
||||
| solar-operating-state | Number | true | Solar Operating State: See Modbus description manual, link above |
|
||||
| solar-collector-temperature | Number:Temperature | true | Temperature of the solar collector |
|
||||
| solar-storage-temperature | Number:Temperature | true | Temperature of the solar storage |
|
||||
| solar-pump-speed | Number | true | Speed of the solar pump |
|
||||
| solar-heat-quantity | Number:Energy | true | Heat quantity produced by solar |
|
||||
| solar-power-output | Number:Power | true | Current power output of solar |
|
||||
| solar-operating-hours | Number | true | Operating hours of solar component |
|
||||
|
||||
### Heating Circuit Group
|
||||
|
||||
This group contains general operational information about the heating circuit.
|
||||
|
||||
| Channel ID | Item Type | Read only | Description |
|
||||
| ---------------------------------------------- | -------------------| --------- | ----------------------------------------------------------- |
|
||||
| heating-circuit-error-number | Number | true | Error Number (0 = No error) |
|
||||
| heating-circuit-operating-state | Number | true | Operating State: See Modbus description manual, link above |
|
||||
| heating-circuit-flow-line-temperature | Number:Temperature | true | Actual temperature flow line sensor |
|
||||
| heating-circuit-return-line-temperature | Number:Temperature | true | Actual temperature return line sensor |
|
||||
| heating-circuit-room-device-temperature | Number:Temperature | false | Actual temperature room device sensor |
|
||||
| heating-circuit-setpoint-flow-line-temperature | Number:Temperature | false | Setpoint temperature flow line |
|
||||
| heating-circuit-operating-mode | Number | false | Operating Mode: See Modbus description manual, link above |
|
||||
| heating-circuit-target-temperature-flow-line | Number:Temperature | false | Setpoin temperature flow line (min = 15.0°C; max = 65.0°C) |
|
||||
| heating-circuit-offset-flow-line-temperature | Number:Temperature | false | Setting for flow line temperature setpoint offset |
|
||||
| heating-circuit-room-heating-temperature | Number:Temperature | false | Setting for heating mode room setpoint temperature |
|
||||
| heating-circuit-room-cooling-temperature | Number:Temperature | false | Setting for cooling mode room setpoint temperature |
|
||||
|
||||
## Full Example
|
||||
|
||||
### `demo.things` Example
|
||||
|
||||
```java
|
||||
Bridge modbus:tcp:Bridge "Lambda Modbus TCP Bridge" [ host="192.168.223.83", port=502, id=1, enableDiscovery=false ] {
|
||||
Thing general lambdageneral "Lambda General" (modbus:tcp:Bridge) [ refresh=60, subindex=0 ]
|
||||
Thing heat-pump lambdaheat-pump "Lambda Heatpump" (modbus:tcp:Bridge) [ refresh=60, subindex=0 ]
|
||||
Thing boiler lambdaboiler "Lambda Boiler" (modbus:tcp:Bridge) [ refresh=60, subindex=0 ]
|
||||
Thing buffer lambdabuffer "Lambda Buffer" (modbus:tcp:Bridge) [ refresh=60, subindex=0 ]
|
||||
Thing solar lambdasolar "Lambda Solar" (modbus:tcp:Bridge) [ refresh=60, subindex=0 ]
|
||||
}
|
||||
```
|
||||
|
||||
### Items Lambda Boiler
|
||||
|
||||
```java
|
||||
Number lambdaboiler_errornumber "Boiler Error Number" (lambdaboiler) { channel="modbus:boiler:Lambda_Bridge:lambdaboiler:boiler-group#boiler-error-number" }
|
||||
Number lambdaboiler_operatingstate "Boiler Operating State" (lambdaboiler) { channel="modbus:boiler:Lambda_Bridge:lambdaboiler:boiler-group#boiler-operating-state" }
|
||||
Number:Temperature lambdaboiler_actualhightemperature "Boiler Actual High Temperature" (lambdaboiler) { channel="modbus:boiler:Lambda_Bridge:lambdaboiler:boiler-group#boiler-actual-high-temperature" }
|
||||
Number:Temperature lambdaboiler_actuallowtemperature "Boiler Actual Low Temperature" (lambdaboiler) { channel="modbus:boiler:Lambda_Bridge:lambdaboiler:boiler-group#boiler-actual-low-temperature" }
|
||||
Number:Temperature lambdaboiler_maximumboilertemperature "Maximum Boiler Temperature" (lambdaboiler) { channel="modbus:boiler:Lambda_Bridge:lambdaboiler:boiler-group#maximum-boiler-temperature" }
|
||||
```
|
||||
|
||||
### Items Lambda Buffer
|
||||
|
||||
```java
|
||||
Number lambdabuffer_errornumber "Buffer Error Number" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#buffer-error-number" }
|
||||
Number lambdabuffer_operatingstate "Buffer Operating State" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#buffer-operating-state" }
|
||||
Number:Temperature lambdabuffer_actualhightemperature "Buffer Actual High Temperature" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#buffer-actual-high-temperature" }
|
||||
Number:Temperature lambdabuffer_actuallowtemperature "Buffer Actual Low Temperature" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#buffer-actual-low-temperature" }
|
||||
Number:Temperature lambdabuffer_actualmodbustemperature "Actual Modbus Temperature" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#buffer-actual-modbus-temperature" }
|
||||
Number lambdabuffer_requesttype "Request Type" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#buffer-request-type" }
|
||||
Number:Temperature lambdabuffer_requestflowlinetemperature "Request Flow Line Temperature" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#buffer-request-flow-line-temperature" }
|
||||
Number:Temperature lambdabuffer_requestreturnlinetemperature "Request Return Line Temperature" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#buffer-request-return-line-temperature" }
|
||||
Number:Temperature lambdabuffer_requestheatsinktemperature "Requested Heat Sink Temperature Difference" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#buffer-request-heat-sink-temperature" }
|
||||
Number:Power lambdabuffer_requestheatingcapacity "Requested Heating Capacity" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#buffer-request-heating-capacity" }
|
||||
Number:Temperature lambdabuffer_maximumbuffertemperature "Maximum Buffer Temperature" (lambdabuffer) { channel="modbus:buffer:Lambda_Bridge:lambdabuffer:buffer-group#maximum-buffer-temperature" }
|
||||
```
|
||||
|
||||
### Items Lambda General
|
||||
|
||||
```java
|
||||
Number lambdaambient_operatingstate "Ambient Operating State" (lambdageneral) { channel="modbus:general:Lambda_Bridge:lambdageneral:ambient-group#ambient-operating-state" }
|
||||
Number lambdaambient_errornumber "Ambient Error Number" (lambdageneral) { channel="modbus:general:Lambda_Bridge:lambdageneral:ambient-group#ambient-error-number" }
|
||||
Number:Temperature lambdaambient_actualambienttemperature "Ambient Actual Temperature" (lambdageneral) { channel="modbus:general:Lambda_Bridge:lambdageneral:ambient-group#actual-ambient-temperature" }
|
||||
Number:Temperature lambdaambient_averageambienttemperature "Ambient Average Temperature" (lambdageneral) { channel="modbus:general:Lambda_Bridge:lambdageneral:ambient-group#average-ambient-temperature" }
|
||||
Number:Temperature lambdaambient_calculatedambienttemperature "Ambient Calculated Temperature" (lambdageneral) { channel="modbus:general:Lambda_Bridge:lambdageneral:ambient-group#calculated-ambient-temperature" }
|
||||
Number lambdaemanager_operatingstate "EManager Operating State" (lambdageneral) { channel="modbus:general:Lambda_Bridge:lambdageneral:emanager-group#emanager-operating-state" }
|
||||
Number lambdaemanager_errornumber "EManager Error Number" (lambdageneral) { channel="modbus:general:Lambda_Bridge:lambdageneral:emanager-group#emanager-error-number" }
|
||||
Number:Power lambdaemanager_actualpower "EManager Actual Power" (lambdageneral) { channel="modbus:general:Lambda_Bridge:lambdageneral:emanager-group#actual-power" }
|
||||
Number:Power lambdaemanager_actualpowerconsumption "EManager Actual Power Consumption" (lambdageneral) { channel="modbus:general:Lambda_Bridge:lambdageneral:emanager-group#actual-power-consumption" }
|
||||
Number:Power lambdaemanager_powerconsumptionsetpoint "EManager Power Consumption Setpoint" (lambdageneral) { channel="modbus:general:Lambda_Bridge:lambdageneral:emanager-group#power-consumption-setpoint" }
|
||||
|
||||
```
|
||||
|
||||
### Items Heatingcircuit
|
||||
|
||||
```java
|
||||
Number lambdaheatingcircuit_errornumber "Heating Circuit Error Number" (lambdaheatingcircuit) { channel="modbus:heating-circuit:Lambda_Bridge:lambdaheatingcircuit:heating-circuit-group#heating-circuit-error-number" }
|
||||
Number lambdaheatingcircuit_operatingstate "Heating Circuit Operating State" (lambdaheatingcircuit) { channel="modbus:heating-circuit:Lambda_Bridge:lambdaheatingcircuit:heating-circuit-group#heating-circuit-operating-state" }
|
||||
Number:Temperature lambdaheatingcircuit_flowlinetemperature "Heating Circuit Flow Line Temperature" (lambdaheatingcircuit) { channel="modbus:heating-circuit:Lambda_Bridge:lambdaheatingcircuit:heating-circuit-group#heating-circuit-flow-line-temperature" }
|
||||
Number:Temperature lambdaheatingcircuit_returnlinetemperature "Heating Circuit Return Line Temperature" (lambdaheatingcircuit) { channel="modbus:heating-circuit:Lambda_Bridge:lambdaheatingcircuit:heating-circuit-group#heating-circuit-return-line-temperature" }
|
||||
Number:Temperature lambdaheatingcircuit_roomdevicetemperature "Heating Circuit Room Device Temperature" (lambdaheatingcircuit) { channel="modbus:heating-circuit:Lambda_Bridge:lambdaheatingcircuit:heating-circuit-group#heating-circuit-room-device-temperature" }
|
||||
Number:Temperature lambdaheatingcircuit_setpointflowlinetemperature "Heating Circuit Setpoint Flow Line Temperature" (lambdaheatingcircuit) { channel="modbus:heating-circuit:Lambda_Bridge:lambdaheatingcircuit:heating-circuit-group#heating-circuit-setpoint-flow-line-temperature" }
|
||||
Number lambdaheatingcircuit_operatingmode "Heating Circuit Operating Mode" (lambdaheatingcircuit) { channel="modbus:heating-circuit:Lambda_Bridge:lambdaheatingcircuit:heating-circuit-group#heating-circuit-operating-mode" }
|
||||
Number:Temperature lambdaheatingcircuit_offsetflowlinetemperature "Heating Circuit Offset Flow Line Temperature"(lambdaheatingcircuit) { channel="modbus:heating-circuit:Lambda_Bridge:lambdaheatingcircuit:heating-circuit-group#heating-circuit-offset-flow-line-temperature" }
|
||||
Number:Temperature lambdaheatingcircuit_roomheatingtemperature "Heating Circuit Room Heating Temperature" (lambdaheatingcircuit) { channel="modbus:heating-circuit:Lambda_Bridge:lambdaheatingcircuit:heating-circuit-group#heating-circuit-room-heating-temperature" }
|
||||
Number:Temperature lambdaheatingcircuit_roomcoolingtemperature "Heating Circuit Room Cooling Temperature" (lambdaheatingcircuit) { channel="modbus:heating-circuit:Lambda_Bridge:lambdaheatingcircuit:heating-circuit-group#heating-circuit-room-cooling-temperature" }
|
||||
```
|
||||
|
||||
### Items Lambda Solar
|
||||
|
||||
```java
|
||||
Number lambdasolar_errornumber "Solar Error Number" (lambdasolar) { channel="modbus:solar:Lambda_Bridge:lambdasolar:solar-group#solar-error-number" }
|
||||
Number lambdasolar_operatingstate "Solar Operating State" (lambdasolar) { channel="modbus:solar:Lambda_Bridge:lambdasolar:solar-group#solar-operating-state" }
|
||||
Number:Temperature lambdasolar_collectortemperature "Solar Collector Temperature" (lambdasolar) { channel="modbus:solar:Lambda_Bridge:lambdasolar:solar-group#solar-collector-temperature" }
|
||||
Number:Temperature lambdasolar_storagetemperature "Solar Storage Temperature" (lambdasolar) { channel="modbus:solar:Lambda_Bridge:lambdasolar:solar-group#solar-storage-temperature" }
|
||||
Number lambdasolar_pumpspeed "Solar Pump Speed" (lambdasolar) { channel="modbus:solar:Lambda_Bridge:lambdasolar:solar-group#solar-pump-speed" }
|
||||
Number:Energy lambdasolar_heatquantity "Solar Heat Quantity" (lambdasolar) { channel="modbus:solar:Lambda_Bridge:lambdasolar:solar-group#solar-heat-quantity" }
|
||||
Number:Power lambdasolar_poweroutput "Solar Power Output" (lambdasolar) { channel="modbus:solar:Lambda_Bridge:lambdasolar:solar-group#solar-power-output" }
|
||||
Number lambdasolar_operatinghours "Solar Operating Hours" (lambdasolar) { channel="modbus:solar:Lambda_Bridge:lambdasolar:solar-group#solar-operating-hours" }
|
||||
```
|
||||
|
||||
### Items Lambda Heatpump
|
||||
|
||||
```java
|
||||
Number lambdaheatpump_errorstate "Heatpump Error State" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-error-state" }
|
||||
Number lambdaheatpump_errornumber "Heatpump Error Number" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-error-number" }
|
||||
Number lambdaheatpump_state "Heatpump State" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-state" }
|
||||
Number lambdaheatpump_operatingstate "Heatpump Operating State" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-operating-state" }
|
||||
Number:Temperature lambdaheatpump_tflow "Heatpump Flow Line Temperature" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-t-flow" }
|
||||
Number:Temperature lambdaheatpump_treturn "Heatpump Return Line Temperature" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-t-return" }
|
||||
Number:VolumetricFlowRate lambdaheatpump_volsink "Heatpump Volume Flow Heat Sink" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-vol-sink" }
|
||||
Number:Temperature lambdaheatpump_teqin "Heatpump Energy Source Inlet Temperature" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-t-eqin" }
|
||||
Number:Temperature lambdaheatpump_teqout "Heatpump Energy Source Outlet Temperature" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-t-eqout" }
|
||||
Number:VolumetricFlowRate lambdaheatpump_volsource "Heatpump Volume Flow Energy Source" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-vol-source" }
|
||||
Number lambdaheatpump_compressorrating "Heatpump Compressor Rating" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-compressor-rating" }
|
||||
Number:Power lambdaheatpump_qpheating "Heatpump Actual Heating Capacity" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-qp-heating" }
|
||||
Number:Power lambdaheatpump_fipowerconsumption "Heatpump Frequency inverter Actual Power Consumption" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-fi-power-consumption" }
|
||||
Number lambdaheatpump_cop "Heatpump COP" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-cop" }
|
||||
Number lambdaheatpump_requestpassword "Heatpump Request Password" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-request-password" }
|
||||
Number lambdaheatpump_requesttype "Heatpump Request Type" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-request-type" }
|
||||
Number lambdaheatpump_requesttflow "Heatpump Requested Flow Line Temperature" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-request-t-flow" }
|
||||
Number lambdaheatpump_requesttreturn "Heatpump Requested Return Line Temperature" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-request-t-return" }
|
||||
Number lambdaheatpump_requestheatsink "Heatpump Requested Heat Sink Temperature" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-request-heat-sink" }
|
||||
Number lambdaheatpump_relaisstate "Heatpump Relais State" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-relais-state" }
|
||||
Number:Energy lambdaheatpump_vdae "Heatpump Accumulated Electrical Energy consumption" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-vdae" }
|
||||
Number:Energy lambdaheatpump_vdaq "Heatpump Accumulated Thermical Energy consumption" (lambdaheatpump) { channel="modbus:heat-pump:Lambda_Bridge:lambdaheatpumpheat-pump-group#heat-pump-vdaq" }
|
||||
```
|
||||
|
||||
### Example: (DSL) Send Power value the E-Manager of the Lambda Heat Pump
|
||||
|
||||
'''
|
||||
// Sending Value to Heatpump
|
||||
// Script has to send a value about every 30 seconds, for example with cron settings.
|
||||
// Calculate power_to_heat-pump using your data provided by the PV system.
|
||||
// var int power_to_heat-pump = ((lambdaemanager_actualpowerconsumption.state as Number) - (PW_Battery.state as Number) - (PW_Grid.state as Number)).intValue
|
||||
|
||||
var int power_to_heat-pump = 1000
|
||||
|
||||
lambdaemanager_actualpower.sendCommand(power_to_heat-pump)
|
||||
'''
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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>5.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.modbus.lambda</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Lambda Binding</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.modbus</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link BoilerConfiguration} class contains fields mapping
|
||||
* thing configuration parameters.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BoilerConfiguration extends GeneralConfiguration {
|
||||
/**
|
||||
* Subindex to calculate the base adress of the modbus registers
|
||||
*/
|
||||
private int subindex = 0;
|
||||
|
||||
public int getSubindex() {
|
||||
return subindex;
|
||||
}
|
||||
|
||||
public void setSubindex(int subindex) {
|
||||
this.subindex = subindex;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link BufferConfiguration} class contains fields mapping
|
||||
* thing configuration parameters.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BufferConfiguration extends GeneralConfiguration {
|
||||
/**
|
||||
* Subindex to calculate the base adress of the modbus registers
|
||||
*/
|
||||
private int subindex = 0;
|
||||
|
||||
public int getSubindex() {
|
||||
return subindex;
|
||||
}
|
||||
|
||||
public void setSubindex(int subindex) {
|
||||
this.subindex = subindex;
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link GeneralConfiguration} class contains fields mapping
|
||||
* thing configuration parameters.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GeneralConfiguration {
|
||||
/**
|
||||
* Refresh interval in seconds
|
||||
*/
|
||||
private int refresh = 30;
|
||||
|
||||
private int maxTries = 3;// backwards compatibility and tests
|
||||
|
||||
/**
|
||||
* Gets refresh period in milliseconds
|
||||
*/
|
||||
public long getRefreshMillis() {
|
||||
return refresh * 1000;
|
||||
}
|
||||
|
||||
public int getMaxTries() {
|
||||
return maxTries;
|
||||
}
|
||||
|
||||
public void setMaxTries(int maxTries) {
|
||||
this.maxTries = maxTries;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link HeatingCircuitConfiguration} class contains fields mapping
|
||||
* thing configuration parameters.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HeatingCircuitConfiguration extends GeneralConfiguration {
|
||||
/**
|
||||
* Subindex to calculate the base adress of the modbus registers
|
||||
*/
|
||||
private int subindex = 0;
|
||||
|
||||
public int getSubindex() {
|
||||
return subindex;
|
||||
}
|
||||
|
||||
public void setSubindex(int subindex) {
|
||||
this.subindex = subindex;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link HeatpumpConfiguration} class contains fields mapping
|
||||
* thing configuration parameters.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HeatpumpConfiguration extends GeneralConfiguration {
|
||||
/**
|
||||
* Subindex to calculate the base adress of the modbus registers
|
||||
*/
|
||||
private int subindex = 0;
|
||||
|
||||
public int getSubindex() {
|
||||
return subindex;
|
||||
}
|
||||
|
||||
public void setSubindex(int subindex) {
|
||||
this.subindex = subindex;
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.ModbusBindingConstants;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link LambdaBindingConstants} class defines common
|
||||
* constants, which are used across the whole binding.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LambdaBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = ModbusBindingConstants.BINDING_ID;
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_GENERAL = new ThingTypeUID(BINDING_ID, "general");
|
||||
public static final ThingTypeUID THING_TYPE_BOILER = new ThingTypeUID(BINDING_ID, "boiler");
|
||||
public static final ThingTypeUID THING_TYPE_BUFFER = new ThingTypeUID(BINDING_ID, "buffer");
|
||||
public static final ThingTypeUID THING_TYPE_HEAT_PUMP = new ThingTypeUID(BINDING_ID, "heat-pump");
|
||||
public static final ThingTypeUID THING_TYPE_SOLAR = new ThingTypeUID(BINDING_ID, "solar");
|
||||
public static final ThingTypeUID THING_TYPE_HEATING_CIRCUIT = new ThingTypeUID(BINDING_ID, "heating-circuit");
|
||||
|
||||
// Channel group ids
|
||||
public static final String GROUP_GENERAL_AMBIENT = "ambient-group";
|
||||
public static final String GROUP_GENERAL_EMANAGER = "emanager-group";
|
||||
public static final String GROUP_HEAT_PUMP = "heat-pump-group";
|
||||
public static final String GROUP_BOILER = "boiler-group";
|
||||
public static final String GROUP_BOILER_REG50 = "boiler-reg50-group";
|
||||
public static final String GROUP_BUFFER = "buffer-group";
|
||||
public static final String GROUP_BUFFER_REG50 = "buffer-reg50-group";
|
||||
public static final String GROUP_SOLAR = "solar-group";
|
||||
public static final String GROUP_SOLAR_REG50 = "solar-reg50-group";
|
||||
public static final String GROUP_HEATING_CIRCUIT = "heating-circuit-group";
|
||||
public static final String GROUP_HEATING_CIRCUIT_REG50 = "heating-circuit-reg50-group";
|
||||
|
||||
// List of all Channel ids in device information group
|
||||
// General Ambient
|
||||
public static final String CHANNEL_AMBIENT_ERROR_NUMBER = "ambient-error-number";
|
||||
public static final String CHANNEL_AMBIENT_OPERATING_STATE = "ambient-operating-state";
|
||||
public static final String CHANNEL_ACTUAL_AMBIENT_TEMPERATURE = "actual-ambient-temperature";
|
||||
public static final String CHANNEL_AVERAGE_AMBIENT_TEMPERATURE = "average-ambient-temperature";
|
||||
public static final String CHANNEL_CALCULATED_AMBIENT_TEMPERATURE = "calculated-ambient-temperature";
|
||||
|
||||
// General E-manager
|
||||
public static final String CHANNEL_EMANAGER_ERROR_NUMBER = "emanager-error-number";
|
||||
public static final String CHANNEL_EMANAGER_OPERATING_STATE = "emanager-operating-state";
|
||||
public static final String CHANNEL_EMANAGER_ACTUAL_POWER = "emanager-actual-power";
|
||||
public static final String CHANNEL_EMANAGER_ACTUAL_POWER_SIGNED = "emanager-actual-power-signed";
|
||||
public static final String CHANNEL_EMANAGER_ACTUAL_POWER_CONSUMPTION = "emanager-actual-power-consumption";
|
||||
public static final String CHANNEL_POWER_CONSUMPTION_SETPOINT = "emanager-power-consumption-setpoint";
|
||||
|
||||
// Heatpump
|
||||
public static final String CHANNEL_HEAT_PUMP_ERROR_STATE = "heat-pump-error-state";
|
||||
public static final String CHANNEL_HEAT_PUMP_ERROR_NUMBER = "heat-pump-error-number";
|
||||
public static final String CHANNEL_HEAT_PUMP_STATE = "heat-pump-state";
|
||||
public static final String CHANNEL_HEAT_PUMP_OPERATING_STATE = "heat-pump-operating-state";
|
||||
public static final String CHANNEL_HEAT_PUMP_T_FLOW = "heat-pump-t-flow";
|
||||
public static final String CHANNEL_HEAT_PUMP_T_RETURN = "heat-pump-t-return";
|
||||
public static final String CHANNEL_HEAT_PUMP_VOL_SINK = "heat-pump-vol-sink";
|
||||
public static final String CHANNEL_HEAT_PUMP_T_EQIN = "heat-pump-t-eqin";
|
||||
public static final String CHANNEL_HEAT_PUMP_T_EQOUT = "heat-pump-t-eqout";
|
||||
public static final String CHANNEL_HEAT_PUMP_VOL_SOURCE = "heat-pump-vol-source";
|
||||
public static final String CHANNEL_HEAT_PUMP_COMPRESSOR_RATING = "heat-pump-compressor-rating";
|
||||
public static final String CHANNEL_HEAT_PUMP_QP_HEATING = "heat-pump-qp-heating";
|
||||
public static final String CHANNEL_HEAT_PUMP_FI_POWER_CONSUMPTION = "heat-pump-fi-power-consumption";
|
||||
public static final String CHANNEL_HEAT_PUMP_COP = "heat-pump-cop";
|
||||
public static final String CHANNEL_HEAT_PUMP_REQUEST_PASSWORD = "heat-pump-request-password";
|
||||
public static final String CHANNEL_HEAT_PUMP_REQUEST_TYPE = "heat-pump-request-type";
|
||||
public static final String CHANNEL_HEAT_PUMP_REQUEST_T_FLOW = "heat-pump-request-t-flow";
|
||||
public static final String CHANNEL_HEAT_PUMP_REQUEST_T_RETURN = "heat-pump-request-t-return";
|
||||
public static final String CHANNEL_HEAT_PUMP_REQUEST_HEAT_SINK = "heat-pump-request-heat-sink";
|
||||
public static final String CHANNEL_HEAT_PUMP_RELAIS_STATE = "heat-pump-relais-state";
|
||||
public static final String CHANNEL_HEAT_PUMP_VDAE = "heat-pump-vdae";
|
||||
public static final String CHANNEL_HEAT_PUMP_VDAQ = "heat-pump-vdaq";
|
||||
|
||||
// Boiler
|
||||
public static final String CHANNEL_BOILER_ERROR_NUMBER = "boiler-error-number";
|
||||
public static final String CHANNEL_BOILER_OPERATING_STATE = "boiler-operating-state";
|
||||
public static final String CHANNEL_BOILER_ACTUAL_HIGH_TEMPERATURE = "boiler-actual-high-temperature";
|
||||
public static final String CHANNEL_BOILER_ACTUAL_LOW_TEMPERATURE = "boiler-actual-low-temperature";
|
||||
public static final String CHANNEL_BOILER_MAXIMUM_BOILER_TEMPERATURE = "maximum-boiler-temperature";
|
||||
|
||||
// Buffer
|
||||
public static final String CHANNEL_BUFFER_ERROR_NUMBER = "buffer-error-number";
|
||||
public static final String CHANNEL_BUFFER_OPERATING_STATE = "buffer-operating-state";
|
||||
public static final String CHANNEL_BUFFER_ACTUAL_HIGH_TEMPERATURE = "buffer-actual-high-temperature";
|
||||
public static final String CHANNEL_BUFFER_ACTUAL_LOW_TEMPERATURE = "buffer-actual-low-temperature";
|
||||
public static final String CHANNEL_BUFFER_ACTUAL_MODBUS_TEMPERATURE = "buffer-actual-modbus-temperature";
|
||||
public static final String CHANNEL_BUFFER_REQUEST_TYPE = "buffer-request-type";
|
||||
public static final String CHANNEL_BUFFER_REQUEST_FLOW_LINE_TEMPERATURE = "buffer-request-flow-line-temperature";
|
||||
public static final String CHANNEL_BUFFER_REQUEST_RETURN_LINE_TEMPERATURE = "buffer-request-return-line-temperature";
|
||||
public static final String CHANNEL_BUFFER_REQUEST_HEAT_SINK_TEMPERATURE = "buffer-request-heat-sink-temperature-difference";
|
||||
public static final String CHANNEL_BUFFER_REQUEST_HEATING_CAPACITY = "buffer-request-heating-capacity";
|
||||
public static final String CHANNEL_BUFFER_MAXIMUM_BUFFER_TEMPERATURE = "maximum-buffer-temperature";
|
||||
|
||||
// Solar
|
||||
public static final String CHANNEL_SOLAR_ERROR_NUMBER = "solar-error-number";
|
||||
public static final String CHANNEL_SOLAR_OPERATING_STATE = "solar-operating-state";
|
||||
public static final String CHANNEL_SOLAR_COLLECTOR_TEMPERATURE = "solar-collector-temperature";
|
||||
public static final String CHANNEL_SOLAR_BUFFER1_TEMPERATURE = "solar-buffer1-temperature";
|
||||
public static final String CHANNEL_SOLAR_BUFFER2_TEMPERATURE = "solar-buffer2-temperature";
|
||||
public static final String CHANNEL_SOLAR_MAXIMUM_BUFFER_TEMPERATURE = "solar-maximum-buffer-temperature";
|
||||
public static final String CHANNEL_SOLAR_BUFFER_CHANGEOVER_TEMPERATURE = "solar-buffer-changeover-temperature";
|
||||
|
||||
// Heating Circuit
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_ERROR_NUMBER = "heating-circuit-error-number";
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_OPERATING_STATE = "heating-circuit-operating-state";
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_FLOW_LINE_TEMPERATURE = "heating-circuit-flow-line-temperature";
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_RETURN_LINE_TEMPERATURE = "heating-circuit-return-line-temperature";
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_ROOM_DEVICE_TEMPERATURE = "heating-circuit-room-device-temperature";
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_SETPOINT_FLOW_LINE_TEMPERATURE = "heating-circuit-setpoint-flow-line-temperature";
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_OPERATING_MODE = "heating-circuit-operating-mode";
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_TARGET_TEMPERATURE_FLOW_LINE = "heating-circuit-target-temperature-flow-line";
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_OFFSET_FLOW_LINE_TEMPERATURE = "heating-circuit-offset-flow-line-temperature";
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_ROOM_HEATING_TEMPERATURE = "heating-circuit-room-heating-temperature";
|
||||
public static final String CHANNEL_HEATING_CIRCUIT_ROOM_COOLING_TEMPERATURE = "heating-circuit-room-cooling-temperature";
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal;
|
||||
|
||||
import static org.openhab.binding.modbus.lambda.internal.LambdaBindingConstants.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.modbus.lambda.internal.handler.BoilerHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.handler.BufferHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.handler.GeneralHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.handler.HeatingCircuitHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.handler.HeatpumpHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.handler.SolarHandler;
|
||||
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.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link LambdaHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.lambda", service = ThingHandlerFactory.class)
|
||||
public class LambdaHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(LambdaHandlerFactory.class);
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_GENERAL, THING_TYPE_HEAT_PUMP,
|
||||
THING_TYPE_BOILER, THING_TYPE_BUFFER, THING_TYPE_HEATING_CIRCUIT, THING_TYPE_SOLAR);
|
||||
|
||||
private static final Map<ThingTypeUID, Function<Thing, ThingHandler>> HANDLER_FACTORY_MAP = Map.of(
|
||||
THING_TYPE_HEAT_PUMP, HeatpumpHandler::new, THING_TYPE_GENERAL, GeneralHandler::new, THING_TYPE_BUFFER,
|
||||
BufferHandler::new, THING_TYPE_BOILER, BoilerHandler::new, THING_TYPE_HEATING_CIRCUIT,
|
||||
HeatingCircuitHandler::new, THING_TYPE_SOLAR, SolarHandler::new);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
logger.debug("LambdaHandlerFactory thingTypeUID: {}", thingTypeUID);
|
||||
|
||||
Function<Thing, ThingHandler> factory = HANDLER_FACTORY_MAP.get(thingTypeUID);
|
||||
return factory != null ? factory.apply(thing) : null;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SolarConfiguration} class contains fields mapping
|
||||
* thing configuration parameters for the solar handler.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class SolarConfiguration extends GeneralConfiguration {
|
||||
/**
|
||||
* Subindex for multiple solar components
|
||||
*/
|
||||
private int subindex = 0;
|
||||
|
||||
public int getSubindex() {
|
||||
return subindex;
|
||||
}
|
||||
|
||||
public void setSubindex(int subindex) {
|
||||
this.subindex = subindex;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Dto class for the Ambient Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
public class AmbientBlock {
|
||||
public int ambientErrorNumber;
|
||||
public int ambientOperatingState;
|
||||
public int actualAmbientTemperature;
|
||||
public int averageAmbientTemperature;
|
||||
public int calculatedAmbientTemperature;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Dto class for the Boiler Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
public class BoilerBlock {
|
||||
public int boilerErrorNumber;
|
||||
public int boilerOperatingState;
|
||||
public int boilerActualHighTemperature;
|
||||
public int boilerActualLowTemperature;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Dto class for the BoilerReg50 Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
public class BoilerReg50Block {
|
||||
|
||||
public int boilerMaximumBoilerTemperature;
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Dto class for the Buffer Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
public class BufferBlock {
|
||||
public int bufferErrorNumber;
|
||||
public int bufferOperatingState;
|
||||
public int bufferActualHighTemperature;
|
||||
public int bufferActualLowTemperature;
|
||||
public int bufferActualModbusTemperature;
|
||||
public int bufferRequestType;
|
||||
public int bufferrequestFlowLineTemperature;
|
||||
public int bufferrequestReturnLineTemperature;
|
||||
public int bufferrequestHeatSinkTemperature;
|
||||
public int bufferrequestHeatingCapacity;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Dto class for the BufferReg50 Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
public class BufferReg50Block {
|
||||
|
||||
public int bufferMaximumBufferTemperature;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Dto class for the EManager Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
public class EManagerBlock {
|
||||
public int emanagerErrorNumber;
|
||||
public int emanagerOperatingState;
|
||||
public int emanagerActualPower;
|
||||
public int emanagerActualPowerSigned;
|
||||
public int emanagerActualPowerConsumption;
|
||||
public int emanagerPowerConsumptionSetpoint;
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Dto class for the HeatingCircuit Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
public class HeatingCircuitBlock {
|
||||
public int heatingcircuitErrorNumber;
|
||||
public int heatingcircuitOperatingState;
|
||||
public int heatingcircuitFlowLineTemperature;
|
||||
public int heatingcircuitReturnLineTemperature;
|
||||
public int heatingcircuitRoomDeviceTemperature;
|
||||
public int heatingcircuitSetpointFlowLineTemperature;
|
||||
public int heatingcircuitOperatingMode;
|
||||
public int heatingcircuitTargetTemperatureFlowLine;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Dto class for the HeatingCircuitReg50 Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
|
||||
public class HeatingCircuitReg50Block {
|
||||
public int heatingcircuitOffsetFlowLineTemperature;
|
||||
public int heatingcircuitRoomHeatingTemperature;
|
||||
public int heatingcircuitRoomCoolingTemperature;
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Dto class for the Heatpump Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
public class HeatpumpBlock {
|
||||
public int heatpumpErrorState;
|
||||
public int heatpumpErrorNumber;
|
||||
public int heatpumpState;
|
||||
public int heatpumpOperatingState;
|
||||
public int heatpumpTFlow;
|
||||
public int heatpumpTReturn;
|
||||
public int heatpumpVolSink;
|
||||
public int heatpumpTEQin;
|
||||
public int heatpumpTEQout;
|
||||
public int heatpumpVolSource;
|
||||
public int heatpumpCompressorRating;
|
||||
public int heatpumpQpHeating;
|
||||
public int heatpumpFIPowerConsumption;
|
||||
public int heatpumpCOP;
|
||||
public int heatpumpRequestPassword;
|
||||
public int heatpumpRequestType;
|
||||
public int heatpumpRequestTFlow;
|
||||
public int heatpumpRequestTReturn;
|
||||
public int heatpumpRequestHeatSink;
|
||||
public int heatpumpRelaisState;
|
||||
public long heatpumpVdAE;
|
||||
public long heatpumpVdAQ;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Data transfer object for solar thermic component data
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus to add handler
|
||||
*/
|
||||
public class SolarBlock {
|
||||
public int solarErrorNumber;
|
||||
public int solarOperatingState;
|
||||
public int solarCollectorTemperature;
|
||||
public int solarBuffer1Temperature;
|
||||
public int solarBuffer2Temperature;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.dto;
|
||||
|
||||
/**
|
||||
* Dto class for the SolarReg50 Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
|
||||
public class SolarReg50Block {
|
||||
public int solarMaximumBufferTemperature;
|
||||
public int solarBufferChangeoverTemperature;
|
||||
}
|
||||
+531
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.handler;
|
||||
|
||||
import static org.openhab.binding.modbus.lambda.internal.LambdaBindingConstants.*;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
import static org.openhab.core.library.unit.Units.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
|
||||
import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.BoilerConfiguration;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.BoilerBlock;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.BoilerReg50Block;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.BoilerBlockParser;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.BoilerReg50BlockParser;
|
||||
import org.openhab.core.io.transport.modbus.AsyncModbusFailure;
|
||||
import org.openhab.core.io.transport.modbus.ModbusCommunicationInterface;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadFunctionCode;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.PollTask;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
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.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link BoilerHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels and for polling the modbus.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BoilerHandler extends BaseThingHandler {
|
||||
|
||||
public abstract class AbstractBasePoller {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(BoilerHandler.class);
|
||||
|
||||
private volatile @Nullable PollTask pollTask;
|
||||
|
||||
public synchronized void unregisterPollTask() {
|
||||
PollTask task = pollTask;
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusCommunicationInterface mycomms = BoilerHandler.this.comms;
|
||||
if (mycomms != null) {
|
||||
mycomms.unregisterRegularPoll(task);
|
||||
}
|
||||
pollTask = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register poll task This is where we set up our regular poller
|
||||
*/
|
||||
public synchronized void registerPollTask(int address, int length, ModbusReadFunctionCode readFunctionCode) {
|
||||
logger.debug("Setting up regular polling Address: {}", address);
|
||||
|
||||
ModbusCommunicationInterface mycomms = BoilerHandler.this.comms;
|
||||
BoilerConfiguration myconfig = Objects.requireNonNull(BoilerHandler.this.config);
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("Boiler: registerPollTask called without proper configuration");
|
||||
}
|
||||
|
||||
ModbusReadRequestBlueprint request = new ModbusReadRequestBlueprint(getSlaveId(), readFunctionCode, address,
|
||||
length, myconfig.getMaxTries());
|
||||
|
||||
long refreshMillis = myconfig.getRefreshMillis();
|
||||
|
||||
pollTask = mycomms.registerRegularPoll(request, refreshMillis, 1000, result -> {
|
||||
result.getRegisters().ifPresent(this::handlePolledData);
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}, BoilerHandler.this::handleReadError);
|
||||
}
|
||||
|
||||
public synchronized void poll() {
|
||||
PollTask task = pollTask;
|
||||
ModbusCommunicationInterface mycomms = BoilerHandler.this.comms;
|
||||
if (task != null && mycomms != null) {
|
||||
mycomms.submitOneTimePoll(task.getRequest(), task.getResultCallback(), task.getFailureCallback());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void handlePolledData(ModbusRegisterArray registers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logger instance
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(BoilerHandler.class);
|
||||
|
||||
/**
|
||||
* Configuration instance
|
||||
*/
|
||||
protected @Nullable BoilerConfiguration config = null;
|
||||
|
||||
/**
|
||||
* Parsers used to convert incoming raw messages into state blocks
|
||||
*/
|
||||
|
||||
private final BoilerBlockParser boilerBlockParser = new BoilerBlockParser();
|
||||
private final BoilerReg50BlockParser boilerReg50BlockParser = new BoilerReg50BlockParser();
|
||||
|
||||
/**
|
||||
* These are the tasks used to poll the device
|
||||
*/
|
||||
|
||||
private volatile @Nullable AbstractBasePoller boilerPoller = null;
|
||||
private volatile @Nullable AbstractBasePoller boilerReg50Poller = null;
|
||||
|
||||
/**
|
||||
* Communication interface to the slave endpoint we're connecting to
|
||||
*/
|
||||
protected volatile @Nullable ModbusCommunicationInterface comms = null;
|
||||
/**
|
||||
* This is the slave id, we store this once initialization is complete
|
||||
*/
|
||||
private volatile int slaveId;
|
||||
|
||||
/**
|
||||
* Instances of this handler should get a reference to the modbus manager
|
||||
*
|
||||
* @param thing the thing to handle
|
||||
*/
|
||||
public BoilerHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address address of the value to be written on the modbus
|
||||
*
|
||||
* @param shortValue value to be written on the modbus
|
||||
*/
|
||||
protected void writeInt16(int address, short shortValue) {
|
||||
BoilerConfiguration myconfig = Objects.requireNonNull(BoilerHandler.this.config);
|
||||
ModbusCommunicationInterface mycomms = BoilerHandler.this.comms;
|
||||
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("registerPollTask called without proper configuration");
|
||||
}
|
||||
// big endian byte ordering
|
||||
byte hi = (byte) (shortValue >> 8);
|
||||
byte lo = (byte) shortValue;
|
||||
ModbusRegisterArray data = new ModbusRegisterArray(hi, lo);
|
||||
|
||||
ModbusWriteRegisterRequestBlueprint request = new ModbusWriteRegisterRequestBlueprint(slaveId, address, data,
|
||||
true, myconfig.getMaxTries());
|
||||
|
||||
mycomms.submitOneTimeWrite(request, result -> {
|
||||
if (hasConfigurationError()) {
|
||||
return;
|
||||
}
|
||||
BoilerHandler.this.updateStatus(ThingStatus.ONLINE);
|
||||
}, failure -> {
|
||||
BoilerHandler.this.handleWriteError(failure);
|
||||
});
|
||||
}
|
||||
|
||||
private short getScaledInt16Value(Command command) throws LambdaException {
|
||||
if (command instanceof QuantityType quantityCommand) {
|
||||
QuantityType<?> c = quantityCommand.toUnit(CELSIUS);
|
||||
if (c != null) {
|
||||
return (short) (c.doubleValue() * 10);
|
||||
} else {
|
||||
throw new LambdaException("Unsupported unit");
|
||||
}
|
||||
}
|
||||
if (command instanceof DecimalType c) {
|
||||
return (short) (c.doubleValue() * 10);
|
||||
}
|
||||
throw new LambdaException("Unsupported command type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle incoming commands.
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (RefreshType.REFRESH == command) {
|
||||
String groupId = channelUID.getGroupId();
|
||||
if (groupId != null) {
|
||||
AbstractBasePoller poller;
|
||||
switch (groupId) {
|
||||
case GROUP_BOILER:
|
||||
poller = boilerPoller;
|
||||
break;
|
||||
case GROUP_BOILER_REG50:
|
||||
poller = boilerReg50Poller;
|
||||
break;
|
||||
default:
|
||||
poller = null;
|
||||
break;
|
||||
}
|
||||
if (poller != null) {
|
||||
logger.trace("Boiler: Polling initiated");
|
||||
poller.poll();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (GROUP_BOILER_REG50.equals(channelUID.getGroupId())) {
|
||||
switch (channelUID.getIdWithoutGroup()) {
|
||||
case CHANNEL_BOILER_MAXIMUM_BOILER_TEMPERATURE:
|
||||
writeInt16(reg50baseadress, getScaledInt16Value(command));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (LambdaException error) {
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String cls = error.getClass().getName();
|
||||
String msg = error.getMessage();
|
||||
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with: %s: %s", cls, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization: Load the config object of the block Connect to the slave
|
||||
* bridge Start the periodic polling
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(BoilerConfiguration.class);
|
||||
startUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adresses for the polling registers, used for reading and writing
|
||||
*/
|
||||
private int baseadress;
|
||||
private int reg50baseadress;
|
||||
|
||||
/**
|
||||
* This method starts the operation of this handler Connect to the slave bridge
|
||||
* Start the periodic polling
|
||||
*/
|
||||
private void startUp() {
|
||||
if (comms != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusEndpointThingHandler slaveEndpointThingHandler = getEndpointThingHandler();
|
||||
if (slaveEndpointThingHandler == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
slaveId = slaveEndpointThingHandler.getSlaveId();
|
||||
comms = slaveEndpointThingHandler.getCommunicationInterface();
|
||||
} catch (EndpointNotInitializedException e) {
|
||||
logger.debug("Boiler: Error setting up SlaveId");
|
||||
}
|
||||
|
||||
if (comms == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
logger.debug("CommunicationInterface of boiler is null, Thing & Bridge are offline");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
logger.debug("CommunicationInterface config of boiler is null, Thing & Bridge are offline");
|
||||
return;
|
||||
}
|
||||
|
||||
BoilerConfiguration myconfig = Objects.requireNonNull(BoilerHandler.this.config);
|
||||
|
||||
baseadress = 2000 + 100 * myconfig.getSubindex();
|
||||
reg50baseadress = baseadress + 50;
|
||||
|
||||
if (boilerPoller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledBoilerData(registers);
|
||||
}
|
||||
};
|
||||
poller.registerPollTask(baseadress, 4, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
boilerPoller = poller;
|
||||
}
|
||||
|
||||
if (boilerReg50Poller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledBoilerReg50Data(registers);
|
||||
}
|
||||
};
|
||||
|
||||
poller.registerPollTask(reg50baseadress, 1, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
boilerReg50Poller = poller;
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose the binding correctly
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the poll tasks and release the endpoint reference
|
||||
*/
|
||||
private void tearDown() {
|
||||
AbstractBasePoller poller = boilerPoller;
|
||||
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
boilerPoller = null;
|
||||
}
|
||||
poller = boilerReg50Poller;
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
boilerReg50Poller = null;
|
||||
}
|
||||
|
||||
comms = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slave id from the bridge
|
||||
*/
|
||||
public int getSlaveId() {
|
||||
return slaveId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the endpoint handler from the bridge this handler is connected to Checks
|
||||
* that we're connected to the right type of bridge
|
||||
*
|
||||
* @return the endpoint handler or null if the bridge does not exist
|
||||
*/
|
||||
private @Nullable ModbusEndpointThingHandler getEndpointThingHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
logger.debug("Bridge is null");
|
||||
return null;
|
||||
}
|
||||
if (bridge.getStatus() != ThingStatus.ONLINE) {
|
||||
logger.debug("Bridge is not online");
|
||||
return null;
|
||||
}
|
||||
|
||||
ThingHandler handler = bridge.getHandler();
|
||||
if (handler == null) {
|
||||
logger.debug("Bridge handler is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (handler instanceof ModbusEndpointThingHandler thingHandler) {
|
||||
return thingHandler;
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected bridge handler: " + handler.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected State getScaled(Number value, Unit<?> unit, Double pow) {
|
||||
double factor = Math.pow(10, pow);
|
||||
return QuantityType.valueOf(value.doubleValue() * factor, unit);
|
||||
}
|
||||
|
||||
protected State getUnscaled(Number value, Unit<?> unit) {
|
||||
return QuantityType.valueOf(value.doubleValue(), unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns high value * 1000 + low value
|
||||
*
|
||||
* @param high the high value
|
||||
*
|
||||
* @param low the low value
|
||||
*
|
||||
* @return the scaled value as a DecimalType
|
||||
*/
|
||||
protected State getEnergyQuantity(int high, int low) {
|
||||
double value = high * 1000 + low;
|
||||
return QuantityType.valueOf(value, KILOWATT_HOUR);
|
||||
}
|
||||
|
||||
/**
|
||||
* These methods are called each time new data has been polled from the modbus
|
||||
* slave The register array is first parsed, then each of the channels are
|
||||
* updated to the new values
|
||||
*
|
||||
* @param registers byte array read from the modbus slave
|
||||
*/
|
||||
|
||||
protected void handlePolledBoilerData(ModbusRegisterArray registers) {
|
||||
BoilerBlock block = boilerBlockParser.parse(registers);
|
||||
|
||||
// Boiler group
|
||||
updateState(channelUID(GROUP_BOILER, CHANNEL_BOILER_ERROR_NUMBER), new DecimalType(block.boilerErrorNumber));
|
||||
updateState(channelUID(GROUP_BOILER, CHANNEL_BOILER_OPERATING_STATE),
|
||||
new DecimalType(block.boilerOperatingState));
|
||||
updateState(channelUID(GROUP_BOILER, CHANNEL_BOILER_ACTUAL_HIGH_TEMPERATURE),
|
||||
getScaled(block.boilerActualHighTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_BOILER, CHANNEL_BOILER_ACTUAL_LOW_TEMPERATURE),
|
||||
getScaled(block.boilerActualLowTemperature, CELSIUS, -1.0));
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
protected void handlePolledBoilerReg50Data(ModbusRegisterArray registers) {
|
||||
BoilerReg50Block block = boilerReg50BlockParser.parse(registers);
|
||||
|
||||
// BoilerReg50 groupaximumBoilerTemperature: {}",
|
||||
updateState(channelUID(GROUP_BOILER_REG50, CHANNEL_BOILER_MAXIMUM_BOILER_TEMPERATURE),
|
||||
getScaled(block.boilerMaximumBoilerTemperature, CELSIUS, -1.0));
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bridgeStatusInfo
|
||||
*/
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
super.bridgeStatusChanged(bridgeStatusInfo);
|
||||
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
startUp();
|
||||
} else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
|
||||
tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleReadError(AsyncModbusFailure<ModbusReadRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with read: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleWriteError(AsyncModbusFailure<ModbusWriteRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with write: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if we're in a CONFIGURATION_ERROR state
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean hasConfigurationError() {
|
||||
ThingStatusInfo statusInfo = getThing().getStatusInfo();
|
||||
return statusInfo.getStatus() == ThingStatus.OFFLINE
|
||||
&& statusInfo.getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset communication status to ONLINE if we're in an OFFLINE state
|
||||
*/
|
||||
protected void resetCommunicationError() {
|
||||
ThingStatusInfo statusInfo = thing.getStatusInfo();
|
||||
if (ThingStatus.OFFLINE.equals(statusInfo.getStatus())
|
||||
&& ThingStatusDetail.COMMUNICATION_ERROR.equals(statusInfo.getStatusDetail())) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel UID for the specified group and channel id
|
||||
*
|
||||
* @param string the channel group
|
||||
*
|
||||
* @param string the channel id in that group
|
||||
*
|
||||
* @return the globally unique channel uid
|
||||
*/
|
||||
ChannelUID channelUID(String group, String id) {
|
||||
return new ChannelUID(getThing().getUID(), group, id);
|
||||
}
|
||||
}
|
||||
+582
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.handler;
|
||||
|
||||
import static org.openhab.binding.modbus.lambda.internal.LambdaBindingConstants.*;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
import static org.openhab.core.library.unit.Units.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
|
||||
import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.BufferConfiguration;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.BufferBlock;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.BufferReg50Block;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.BufferBlockParser;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.BufferReg50BlockParser;
|
||||
import org.openhab.core.io.transport.modbus.AsyncModbusFailure;
|
||||
import org.openhab.core.io.transport.modbus.ModbusCommunicationInterface;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadFunctionCode;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.PollTask;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
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.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link BufferHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels and for polling the modbus.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BufferHandler extends BaseThingHandler {
|
||||
|
||||
public abstract class AbstractBasePoller {
|
||||
|
||||
private volatile @Nullable PollTask pollTask;
|
||||
|
||||
public synchronized void unregisterPollTask() {
|
||||
PollTask task = pollTask;
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusCommunicationInterface mycomms = BufferHandler.this.comms;
|
||||
if (mycomms != null) {
|
||||
mycomms.unregisterRegularPoll(task);
|
||||
}
|
||||
pollTask = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register poll task This is where we set up our regular poller
|
||||
*/
|
||||
public synchronized void registerPollTask(int address, int length, ModbusReadFunctionCode readFunctionCode) {
|
||||
ModbusCommunicationInterface mycomms = BufferHandler.this.comms;
|
||||
BufferConfiguration myconfig = Objects.requireNonNull(BufferHandler.this.config);
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("registerPollTask called without proper configuration");
|
||||
}
|
||||
|
||||
ModbusReadRequestBlueprint request = new ModbusReadRequestBlueprint(getSlaveId(), readFunctionCode, address,
|
||||
length, myconfig.getMaxTries());
|
||||
|
||||
long refreshMillis = myconfig.getRefreshMillis();
|
||||
|
||||
pollTask = mycomms.registerRegularPoll(request, refreshMillis, 1000, result -> {
|
||||
result.getRegisters().ifPresent(this::handlePolledData);
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}, BufferHandler.this::handleReadError);
|
||||
}
|
||||
|
||||
public synchronized void poll() {
|
||||
PollTask task = pollTask;
|
||||
ModbusCommunicationInterface mycomms = BufferHandler.this.comms;
|
||||
if (task != null && mycomms != null) {
|
||||
mycomms.submitOneTimePoll(task.getRequest(), task.getResultCallback(), task.getFailureCallback());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void handlePolledData(ModbusRegisterArray registers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logger instance
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(BufferHandler.class);
|
||||
|
||||
/**
|
||||
* Configuration instance
|
||||
*/
|
||||
protected @Nullable BufferConfiguration config = null;
|
||||
|
||||
/**
|
||||
* Parsers used to convert incoming raw messages into state blocks
|
||||
*/
|
||||
|
||||
private final BufferBlockParser bufferBlockParser = new BufferBlockParser();
|
||||
private final BufferReg50BlockParser bufferReg50BlockParser = new BufferReg50BlockParser();
|
||||
|
||||
/**
|
||||
* These are the tasks used to poll the device
|
||||
*/
|
||||
private volatile @Nullable AbstractBasePoller bufferPoller = null;
|
||||
private volatile @Nullable AbstractBasePoller bufferReg50Poller = null;
|
||||
/**
|
||||
* Communication interface to the slave endpoint we're connecting to
|
||||
*/
|
||||
protected volatile @Nullable ModbusCommunicationInterface comms = null;
|
||||
/**
|
||||
* This is the slave id, we store this once initialization is complete
|
||||
*/
|
||||
private volatile int slaveId;
|
||||
|
||||
/**
|
||||
* Instances of this handler should get a reference to the modbus manager
|
||||
*
|
||||
* @param thing the thing to handle
|
||||
*/
|
||||
public BufferHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address address of the value to be written on the modbus
|
||||
*
|
||||
* @param shortValue value to be written on the modbus
|
||||
*/
|
||||
protected void writeInt16(int address, short shortValue) {
|
||||
BufferConfiguration myconfig = Objects.requireNonNull(BufferHandler.this.config);
|
||||
ModbusCommunicationInterface mycomms = BufferHandler.this.comms;
|
||||
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("registerPollTask called without proper configuration");
|
||||
}
|
||||
// big endian byte ordering
|
||||
byte hi = (byte) (shortValue >> 8);
|
||||
byte lo = (byte) shortValue;
|
||||
ModbusRegisterArray data = new ModbusRegisterArray(hi, lo);
|
||||
|
||||
ModbusWriteRegisterRequestBlueprint request = new ModbusWriteRegisterRequestBlueprint(slaveId, address, data,
|
||||
true, myconfig.getMaxTries());
|
||||
|
||||
mycomms.submitOneTimeWrite(request, result -> {
|
||||
if (hasConfigurationError()) {
|
||||
return;
|
||||
}
|
||||
BufferHandler.this.updateStatus(ThingStatus.ONLINE);
|
||||
}, failure -> {
|
||||
BufferHandler.this.handleWriteError(failure);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command get the value of this command.
|
||||
*
|
||||
* @return short the value of the command as short
|
||||
*/
|
||||
private short getInt16Value(Command command) throws LambdaException {
|
||||
if (command instanceof QuantityType quantityCommand) {
|
||||
QuantityType<?> c = quantityCommand.toUnit(WATT);
|
||||
if (c != null) {
|
||||
return c.shortValue();
|
||||
} else {
|
||||
throw new LambdaException("Unsupported unit");
|
||||
}
|
||||
}
|
||||
if (command instanceof DecimalType c) {
|
||||
return c.shortValue();
|
||||
}
|
||||
throw new LambdaException("Unsupported command type");
|
||||
}
|
||||
|
||||
private short getScaledInt16Value(Command command) throws LambdaException {
|
||||
if (command instanceof QuantityType quantityCommand) {
|
||||
QuantityType<?> c = quantityCommand.toUnit(CELSIUS);
|
||||
if (c != null) {
|
||||
return (short) (c.doubleValue() * 10);
|
||||
} else {
|
||||
throw new LambdaException("Unsupported unit");
|
||||
}
|
||||
}
|
||||
if (command instanceof DecimalType c) {
|
||||
return (short) (c.doubleValue() * 10);
|
||||
}
|
||||
throw new LambdaException("Unsupported command type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle incoming commands.
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (RefreshType.REFRESH == command) {
|
||||
String groupId = channelUID.getGroupId();
|
||||
if (groupId != null) {
|
||||
AbstractBasePoller poller;
|
||||
switch (groupId) {
|
||||
case GROUP_BUFFER:
|
||||
poller = bufferPoller;
|
||||
break;
|
||||
case GROUP_BUFFER_REG50:
|
||||
poller = bufferReg50Poller;
|
||||
break;
|
||||
default:
|
||||
poller = null;
|
||||
break;
|
||||
}
|
||||
if (poller != null) {
|
||||
logger.trace("Buffer: Polling initiated");
|
||||
poller.poll();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (GROUP_BUFFER.equals(channelUID.getGroupId())) {
|
||||
switch (channelUID.getIdWithoutGroup()) {
|
||||
case CHANNEL_BUFFER_ACTUAL_MODBUS_TEMPERATURE:
|
||||
writeInt16(baseadress + 4, getScaledInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_BUFFER_REQUEST_TYPE:
|
||||
writeInt16(baseadress + 5, getInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_BUFFER_REQUEST_FLOW_LINE_TEMPERATURE:
|
||||
writeInt16(baseadress + 6, getScaledInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_BUFFER_REQUEST_RETURN_LINE_TEMPERATURE:
|
||||
writeInt16(baseadress + 7, getScaledInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_BUFFER_REQUEST_HEAT_SINK_TEMPERATURE:
|
||||
writeInt16(baseadress + 8, getScaledInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_BUFFER_REQUEST_HEATING_CAPACITY:
|
||||
writeInt16(baseadress + 9, getScaledInt16Value(command));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GROUP_BUFFER_REG50.equals(channelUID.getGroupId())) {
|
||||
switch (channelUID.getIdWithoutGroup()) {
|
||||
case CHANNEL_BUFFER_MAXIMUM_BUFFER_TEMPERATURE:
|
||||
writeInt16(reg50baseadress, getScaledInt16Value(command));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (LambdaException error) {
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String cls = error.getClass().getName();
|
||||
String msg = error.getMessage();
|
||||
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with: %s: %s", cls, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization: Load the config object of the block Connect to the slave
|
||||
* bridge Start the periodic polling
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(BufferConfiguration.class);
|
||||
|
||||
startUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adresses for the polling registers, used for reading and writing
|
||||
*/
|
||||
private int baseadress;
|
||||
private int reg50baseadress;
|
||||
|
||||
/**
|
||||
* This method starts the operation of this handler Connect to the slave bridge
|
||||
* Start the periodic polling1
|
||||
*/
|
||||
private void startUp() {
|
||||
if (comms != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusEndpointThingHandler slaveEndpointThingHandler = getEndpointThingHandler();
|
||||
if (slaveEndpointThingHandler == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
slaveId = slaveEndpointThingHandler.getSlaveId();
|
||||
comms = slaveEndpointThingHandler.getCommunicationInterface();
|
||||
} catch (EndpointNotInitializedException e) {
|
||||
logger.debug("Buffer: Error setting up SlaveId");
|
||||
}
|
||||
|
||||
if (comms == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
logger.debug("CommunicationInterface of buffer is null, Thing & Bridge are offline");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
logger.debug("Invalid comms/config/manager ref for Buffer handler");
|
||||
return;
|
||||
}
|
||||
|
||||
BufferConfiguration myconfig = Objects.requireNonNull(BufferHandler.this.config);
|
||||
|
||||
baseadress = 3000 + 100 * myconfig.getSubindex();
|
||||
reg50baseadress = baseadress + 50;
|
||||
|
||||
if (bufferPoller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledBufferData(registers);
|
||||
}
|
||||
};
|
||||
|
||||
poller.registerPollTask(baseadress, 10, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
bufferPoller = poller;
|
||||
}
|
||||
if (bufferReg50Poller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledBufferReg50Data(registers);
|
||||
}
|
||||
};
|
||||
|
||||
poller.registerPollTask(reg50baseadress, 1, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
bufferReg50Poller = poller;
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose the binding correctly
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the poll tasks and release the endpoint reference
|
||||
*/
|
||||
private void tearDown() {
|
||||
AbstractBasePoller poller = bufferPoller;
|
||||
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
bufferPoller = null;
|
||||
}
|
||||
poller = bufferReg50Poller;
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
bufferReg50Poller = null;
|
||||
}
|
||||
|
||||
comms = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slave id from the bridge
|
||||
*/
|
||||
public int getSlaveId() {
|
||||
return slaveId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the endpoint handler from the bridge this handler is connected to Checks
|
||||
* that we're connected to the right type of bridge
|
||||
*
|
||||
* @return the endpoint handler or null if the bridge does not exist
|
||||
*/
|
||||
private @Nullable ModbusEndpointThingHandler getEndpointThingHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
logger.debug("Bridge is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (bridge.getStatus() != ThingStatus.ONLINE) {
|
||||
logger.debug("Bridge is not online");
|
||||
return null;
|
||||
}
|
||||
|
||||
ThingHandler handler = bridge.getHandler();
|
||||
if (handler == null) {
|
||||
logger.debug("Bridge handler is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (handler instanceof ModbusEndpointThingHandler thingHandler) {
|
||||
return thingHandler;
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected bridge handler: " + handler.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected State getScaled(Number value, Unit<?> unit, Double pow) {
|
||||
double factor = Math.pow(10, pow);
|
||||
return QuantityType.valueOf(value.doubleValue() * factor, unit);
|
||||
}
|
||||
|
||||
protected State getUnscaled(Number value, Unit<?> unit) {
|
||||
return QuantityType.valueOf(value.doubleValue(), unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns high value * 1000 + low value
|
||||
*
|
||||
* @param high the high value
|
||||
*
|
||||
* @param low the low value
|
||||
*
|
||||
* @return the scaled value as a DecimalType
|
||||
*/
|
||||
protected State getEnergyQuantity(int high, int low) {
|
||||
double value = high * 1000 + low;
|
||||
return QuantityType.valueOf(value, KILOWATT_HOUR);
|
||||
}
|
||||
|
||||
/**
|
||||
* These methods are called each time new data has been polled from the modbus
|
||||
* slave The register array is first parsed, then each of the channels are
|
||||
* updated to the new values
|
||||
*
|
||||
* @param registers byte array read from the modbus slave
|
||||
*/
|
||||
|
||||
protected void handlePolledBufferData(ModbusRegisterArray registers) {
|
||||
BufferBlock block = bufferBlockParser.parse(registers);
|
||||
|
||||
// Buffer group
|
||||
updateState(channelUID(GROUP_BUFFER, CHANNEL_BUFFER_ERROR_NUMBER), new DecimalType(block.bufferErrorNumber));
|
||||
updateState(channelUID(GROUP_BUFFER, CHANNEL_BUFFER_OPERATING_STATE),
|
||||
new DecimalType(block.bufferOperatingState));
|
||||
updateState(channelUID(GROUP_BUFFER, CHANNEL_BUFFER_ACTUAL_HIGH_TEMPERATURE),
|
||||
getScaled(block.bufferActualHighTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_BUFFER, CHANNEL_BUFFER_ACTUAL_LOW_TEMPERATURE),
|
||||
getScaled(block.bufferActualLowTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_BUFFER, CHANNEL_BUFFER_ACTUAL_MODBUS_TEMPERATURE),
|
||||
getScaled(block.bufferActualModbusTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_BUFFER, CHANNEL_BUFFER_REQUEST_TYPE), new DecimalType(block.bufferRequestType));
|
||||
updateState(channelUID(GROUP_BUFFER, CHANNEL_BUFFER_REQUEST_FLOW_LINE_TEMPERATURE),
|
||||
getScaled(block.bufferrequestFlowLineTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_BUFFER, CHANNEL_BUFFER_REQUEST_RETURN_LINE_TEMPERATURE),
|
||||
getScaled(block.bufferrequestReturnLineTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_BUFFER, CHANNEL_BUFFER_REQUEST_HEAT_SINK_TEMPERATURE),
|
||||
getScaled(block.bufferrequestHeatSinkTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_BUFFER, CHANNEL_BUFFER_REQUEST_HEATING_CAPACITY),
|
||||
getScaled(block.bufferrequestHeatingCapacity, WATT, 2.0));
|
||||
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
protected void handlePolledBufferReg50Data(ModbusRegisterArray registers) {
|
||||
BufferReg50Block block = bufferReg50BlockParser.parse(registers);
|
||||
|
||||
// BufferReg50 group
|
||||
updateState(channelUID(GROUP_BUFFER_REG50, CHANNEL_BUFFER_MAXIMUM_BUFFER_TEMPERATURE),
|
||||
getScaled(block.bufferMaximumBufferTemperature, CELSIUS, -1.0));
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bridgeStatusInfo
|
||||
*/
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
super.bridgeStatusChanged(bridgeStatusInfo);
|
||||
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
startUp();
|
||||
} else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
|
||||
tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleReadError(AsyncModbusFailure<ModbusReadRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with read: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleWriteError(AsyncModbusFailure<ModbusWriteRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with write: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if we're in a CONFIGURATION_ERROR state
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean hasConfigurationError() {
|
||||
ThingStatusInfo statusInfo = getThing().getStatusInfo();
|
||||
return statusInfo.getStatus() == ThingStatus.OFFLINE
|
||||
&& statusInfo.getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset communication status to ONLINE if we're in an OFFLINE state
|
||||
*/
|
||||
protected void resetCommunicationError() {
|
||||
ThingStatusInfo statusInfo = thing.getStatusInfo();
|
||||
if (ThingStatus.OFFLINE.equals(statusInfo.getStatus())
|
||||
&& ThingStatusDetail.COMMUNICATION_ERROR.equals(statusInfo.getStatusDetail())) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel UID for the specified group and channel id
|
||||
*
|
||||
* @param string the channel group
|
||||
*
|
||||
* @param string the channel id in that group
|
||||
*
|
||||
* @return the globally unique channel uid
|
||||
*/
|
||||
ChannelUID channelUID(String group, String id) {
|
||||
return new ChannelUID(getThing().getUID(), group, id);
|
||||
}
|
||||
}
|
||||
+542
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.handler;
|
||||
|
||||
import static org.openhab.binding.modbus.lambda.internal.LambdaBindingConstants.*;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
import static org.openhab.core.library.unit.Units.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
|
||||
import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.GeneralConfiguration;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.AmbientBlock;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.EManagerBlock;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.AmbientBlockParser;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.EManagerBlockParser;
|
||||
import org.openhab.core.io.transport.modbus.AsyncModbusFailure;
|
||||
import org.openhab.core.io.transport.modbus.ModbusCommunicationInterface;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadFunctionCode;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.PollTask;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
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.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link GeneralHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels and for polling the modbus.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GeneralHandler extends BaseThingHandler {
|
||||
|
||||
public abstract class AbstractBasePoller {
|
||||
|
||||
private volatile @Nullable PollTask pollTask;
|
||||
|
||||
public synchronized void unregisterPollTask() {
|
||||
PollTask task = pollTask;
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusCommunicationInterface mycomms = GeneralHandler.this.comms;
|
||||
if (mycomms != null) {
|
||||
mycomms.unregisterRegularPoll(task);
|
||||
}
|
||||
pollTask = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register poll task This is where we set up our regular poller
|
||||
*/
|
||||
public synchronized void registerPollTask(int address, int length, ModbusReadFunctionCode readFunctionCode) {
|
||||
ModbusCommunicationInterface mycomms = GeneralHandler.this.comms;
|
||||
GeneralConfiguration myconfig = Objects.requireNonNull(GeneralHandler.this.config);
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("registerPollTask called without proper configuration");
|
||||
}
|
||||
|
||||
ModbusReadRequestBlueprint request = new ModbusReadRequestBlueprint(getSlaveId(), readFunctionCode, address,
|
||||
length, myconfig.getMaxTries());
|
||||
|
||||
long refreshMillis = myconfig.getRefreshMillis();
|
||||
|
||||
pollTask = mycomms.registerRegularPoll(request, refreshMillis, 1000, result -> {
|
||||
result.getRegisters().ifPresent(this::handlePolledData);
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}, GeneralHandler.this::handleReadError);
|
||||
}
|
||||
|
||||
public synchronized void poll() {
|
||||
PollTask task = pollTask;
|
||||
ModbusCommunicationInterface mycomms = GeneralHandler.this.comms;
|
||||
if (task != null && mycomms != null) {
|
||||
mycomms.submitOneTimePoll(task.getRequest(), task.getResultCallback(), task.getFailureCallback());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void handlePolledData(ModbusRegisterArray registers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logger instance
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(GeneralHandler.class);
|
||||
|
||||
/**
|
||||
* Configuration instance
|
||||
*/
|
||||
protected @Nullable GeneralConfiguration config = null;
|
||||
/**
|
||||
* Parsers used to convert incoming raw messages into state blocks
|
||||
*/
|
||||
private final AmbientBlockParser ambientBlockParser = new AmbientBlockParser();
|
||||
private final EManagerBlockParser emanagerBlockParser = new EManagerBlockParser();
|
||||
|
||||
/**
|
||||
* These are the tasks used to poll the device
|
||||
*/
|
||||
private volatile @Nullable AbstractBasePoller ambientPoller = null;
|
||||
private volatile @Nullable AbstractBasePoller emanagerPoller = null;
|
||||
|
||||
/**
|
||||
* Communication interface to the slave endpoint we're connecting to
|
||||
*/
|
||||
protected volatile @Nullable ModbusCommunicationInterface comms = null;
|
||||
/**
|
||||
* This is the slave id, we store this once initialization is complete
|
||||
*/
|
||||
private volatile int slaveId;
|
||||
|
||||
/**
|
||||
* Instances of this handler should get a reference to the modbus manager
|
||||
*
|
||||
* @param thing the thing to handle
|
||||
*/
|
||||
public GeneralHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address address of the value to be written on the modbus
|
||||
*
|
||||
* @param shortValue value to be written on the modbus
|
||||
*/
|
||||
protected void writeInt16(int address, short shortValue) {
|
||||
GeneralConfiguration myconfig = Objects.requireNonNull(GeneralHandler.this.config);
|
||||
ModbusCommunicationInterface mycomms = GeneralHandler.this.comms;
|
||||
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("registerPollTask called without proper configuration");
|
||||
}
|
||||
// big endian byte ordering
|
||||
byte hi = (byte) (shortValue >> 8);
|
||||
byte lo = (byte) shortValue;
|
||||
ModbusRegisterArray data = new ModbusRegisterArray(hi, lo);
|
||||
|
||||
ModbusWriteRegisterRequestBlueprint request = new ModbusWriteRegisterRequestBlueprint(slaveId, address, data,
|
||||
true, myconfig.getMaxTries());
|
||||
|
||||
mycomms.submitOneTimeWrite(request, result -> {
|
||||
if (hasConfigurationError()) {
|
||||
return;
|
||||
}
|
||||
GeneralHandler.this.updateStatus(ThingStatus.ONLINE);
|
||||
}, failure -> {
|
||||
GeneralHandler.this.handleWriteError(failure);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command get the value of this command.
|
||||
*
|
||||
* @return short the value of the command as short
|
||||
*/
|
||||
private short getInt16Value(Command command) throws LambdaException {
|
||||
if (command instanceof QuantityType quantityCommand) {
|
||||
QuantityType<?> c = quantityCommand.toUnit(WATT);
|
||||
if (c != null) {
|
||||
return c.shortValue();
|
||||
} else {
|
||||
throw new LambdaException("Unsupported unit");
|
||||
}
|
||||
}
|
||||
if (command instanceof DecimalType c) {
|
||||
return c.shortValue();
|
||||
}
|
||||
throw new LambdaException("Unsupported command type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle incoming commands.
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (RefreshType.REFRESH == command) {
|
||||
String groupId = channelUID.getGroupId();
|
||||
if (groupId != null) {
|
||||
AbstractBasePoller poller;
|
||||
switch (groupId) {
|
||||
case GROUP_GENERAL_AMBIENT:
|
||||
poller = ambientPoller;
|
||||
break;
|
||||
case GROUP_GENERAL_EMANAGER:
|
||||
poller = emanagerPoller;
|
||||
break;
|
||||
default:
|
||||
poller = null;
|
||||
break;
|
||||
}
|
||||
if (poller != null) {
|
||||
logger.trace("General: Polling initiated");
|
||||
poller.poll();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (GROUP_GENERAL_AMBIENT.equals(channelUID.getGroupId())) {
|
||||
switch (channelUID.getIdWithoutGroup()) {
|
||||
case CHANNEL_ACTUAL_AMBIENT_TEMPERATURE:
|
||||
writeInt16(2, getInt16Value(command));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GROUP_GENERAL_EMANAGER.equals(channelUID.getGroupId())) {
|
||||
switch (channelUID.getIdWithoutGroup()) {
|
||||
case CHANNEL_EMANAGER_ACTUAL_POWER:
|
||||
writeInt16(102, getInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_EMANAGER_ACTUAL_POWER_SIGNED:
|
||||
writeInt16(102, getInt16Value(command));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (LambdaException error) {
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String cls = error.getClass().getName();
|
||||
String msg = error.getMessage();
|
||||
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with: %s: %s", cls, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization: Load the config object of the block Connect to the slave
|
||||
* bridge Start the periodic polling
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(GeneralConfiguration.class);
|
||||
|
||||
logger.debug("Initializing thing with properties: {}", thing.getProperties());
|
||||
|
||||
startUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method starts the operation of this handler Connect to the slave bridge
|
||||
* Start the periodic polling1
|
||||
*/
|
||||
private void startUp() {
|
||||
if (comms != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusEndpointThingHandler slaveEndpointThingHandler = getEndpointThingHandler();
|
||||
if (slaveEndpointThingHandler == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
slaveId = slaveEndpointThingHandler.getSlaveId();
|
||||
|
||||
comms = slaveEndpointThingHandler.getCommunicationInterface();
|
||||
} catch (EndpointNotInitializedException e) {
|
||||
logger.debug("General: Error setting up SlaveId");
|
||||
}
|
||||
|
||||
if (comms == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
logger.debug("CommunicationInterface of general section is null, Thing & Bridge are offline");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
logger.debug("Invalid comms/config/manager ref for lambda general handler");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ambientPoller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledAmbientData(registers);
|
||||
}
|
||||
};
|
||||
poller.registerPollTask(0, 5, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
ambientPoller = poller;
|
||||
}
|
||||
|
||||
if (emanagerPoller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledEManagerData(registers);
|
||||
}
|
||||
};
|
||||
poller.registerPollTask(100, 5, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
emanagerPoller = poller;
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose the binding correctly
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the poll tasks and release the endpoint reference
|
||||
*/
|
||||
private void tearDown() {
|
||||
AbstractBasePoller poller = ambientPoller;
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
ambientPoller = null;
|
||||
}
|
||||
|
||||
poller = emanagerPoller;
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
emanagerPoller = null;
|
||||
}
|
||||
|
||||
comms = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slave id from the bridge
|
||||
*/
|
||||
public int getSlaveId() {
|
||||
return slaveId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the endpoint handler from the bridge this handler is connected to Checks
|
||||
* that we're connected to the right type of bridge
|
||||
*
|
||||
* @return the endpoint handler or null if the bridge does not exist
|
||||
*/
|
||||
private @Nullable ModbusEndpointThingHandler getEndpointThingHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
logger.debug("Bridge is null");
|
||||
return null;
|
||||
}
|
||||
if (bridge.getStatus() != ThingStatus.ONLINE) {
|
||||
logger.debug("Bridge is not online");
|
||||
return null;
|
||||
}
|
||||
|
||||
ThingHandler handler = bridge.getHandler();
|
||||
if (handler == null) {
|
||||
logger.debug("Bridge handler is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (handler instanceof ModbusEndpointThingHandler thingHandler) {
|
||||
return thingHandler;
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected bridge handler: " + handler.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected State getScaled(Number value, Unit<?> unit, Double pow) {
|
||||
double factor = Math.pow(10, pow);
|
||||
return QuantityType.valueOf(value.doubleValue() * factor, unit);
|
||||
}
|
||||
|
||||
protected State getUnscaled(Number value, Unit<?> unit) {
|
||||
return QuantityType.valueOf(value.doubleValue(), unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns high value * 1000 + low value
|
||||
*
|
||||
* @param high the high value
|
||||
*
|
||||
* @param low the low value
|
||||
*
|
||||
* @return the scaled value as a DecimalType
|
||||
*/
|
||||
protected State getEnergyQuantity(int high, int low) {
|
||||
double value = high * 1000 + low;
|
||||
return QuantityType.valueOf(value, KILOWATT_HOUR);
|
||||
}
|
||||
|
||||
/**
|
||||
* These methods are called each time new data has been polled from the modbus
|
||||
* slave The register array is first parsed, then each of the channels are
|
||||
* updated to the new values
|
||||
*
|
||||
* @param registers byte array read from the modbus slave
|
||||
*/
|
||||
protected void handlePolledAmbientData(ModbusRegisterArray registers) {
|
||||
// Ambient group
|
||||
AmbientBlock block = ambientBlockParser.parse(registers);
|
||||
|
||||
updateState(channelUID(GROUP_GENERAL_AMBIENT, CHANNEL_AMBIENT_ERROR_NUMBER),
|
||||
new DecimalType(block.ambientErrorNumber));
|
||||
updateState(channelUID(GROUP_GENERAL_AMBIENT, CHANNEL_AMBIENT_OPERATING_STATE),
|
||||
new DecimalType(block.ambientOperatingState));
|
||||
updateState(channelUID(GROUP_GENERAL_AMBIENT, CHANNEL_ACTUAL_AMBIENT_TEMPERATURE),
|
||||
getScaled(block.actualAmbientTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_GENERAL_AMBIENT, CHANNEL_AVERAGE_AMBIENT_TEMPERATURE),
|
||||
getScaled(block.averageAmbientTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_GENERAL_AMBIENT, CHANNEL_CALCULATED_AMBIENT_TEMPERATURE),
|
||||
getScaled(block.calculatedAmbientTemperature, CELSIUS, -1.0));
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
protected void handlePolledEManagerData(ModbusRegisterArray registers) {
|
||||
// EManager group
|
||||
EManagerBlock block = emanagerBlockParser.parse(registers);
|
||||
updateState(channelUID(GROUP_GENERAL_EMANAGER, CHANNEL_EMANAGER_ERROR_NUMBER),
|
||||
new DecimalType(block.emanagerErrorNumber));
|
||||
updateState(channelUID(GROUP_GENERAL_EMANAGER, CHANNEL_EMANAGER_OPERATING_STATE),
|
||||
new DecimalType(block.emanagerOperatingState));
|
||||
updateState(channelUID(GROUP_GENERAL_EMANAGER, CHANNEL_EMANAGER_ACTUAL_POWER_CONSUMPTION),
|
||||
getUnscaled(block.emanagerActualPowerConsumption, WATT));
|
||||
updateState(channelUID(GROUP_GENERAL_EMANAGER, CHANNEL_EMANAGER_ACTUAL_POWER),
|
||||
getUnscaled(block.emanagerActualPower, WATT));
|
||||
updateState(channelUID(GROUP_GENERAL_EMANAGER, CHANNEL_EMANAGER_ACTUAL_POWER_SIGNED),
|
||||
getUnscaled(block.emanagerActualPowerSigned, WATT));
|
||||
updateState(channelUID(GROUP_GENERAL_EMANAGER, CHANNEL_POWER_CONSUMPTION_SETPOINT),
|
||||
getUnscaled(block.emanagerPowerConsumptionSetpoint, WATT));
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bridgeStatusInfo
|
||||
*/
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
super.bridgeStatusChanged(bridgeStatusInfo);
|
||||
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
startUp();
|
||||
} else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
|
||||
tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleReadError(AsyncModbusFailure<ModbusReadRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with read: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleWriteError(AsyncModbusFailure<ModbusWriteRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with write: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if we're in a CONFIGURATION_ERROR state
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean hasConfigurationError() {
|
||||
ThingStatusInfo statusInfo = getThing().getStatusInfo();
|
||||
return statusInfo.getStatus() == ThingStatus.OFFLINE
|
||||
&& statusInfo.getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset communication status to ONLINE if we're in an OFFLINE state
|
||||
*/
|
||||
protected void resetCommunicationError() {
|
||||
ThingStatusInfo statusInfo = thing.getStatusInfo();
|
||||
if (ThingStatus.OFFLINE.equals(statusInfo.getStatus())
|
||||
&& ThingStatusDetail.COMMUNICATION_ERROR.equals(statusInfo.getStatusDetail())) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel UID for the specified group and channel id
|
||||
*
|
||||
* @param string the channel group
|
||||
*
|
||||
* @param string the channel id in that group
|
||||
*
|
||||
* @return the globally unique channel uid
|
||||
*/
|
||||
ChannelUID channelUID(String group, String id) {
|
||||
return new ChannelUID(getThing().getUID(), group, id);
|
||||
}
|
||||
}
|
||||
+590
@@ -0,0 +1,590 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.handler;
|
||||
|
||||
import static org.openhab.binding.modbus.lambda.internal.LambdaBindingConstants.*;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
import static org.openhab.core.library.unit.Units.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
|
||||
import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.HeatingCircuitConfiguration;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.HeatingCircuitBlock;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.HeatingCircuitReg50Block;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.HeatingCircuitBlockParser;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.HeatingCircuitReg50BlockParser;
|
||||
import org.openhab.core.io.transport.modbus.AsyncModbusFailure;
|
||||
import org.openhab.core.io.transport.modbus.ModbusCommunicationInterface;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadFunctionCode;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.PollTask;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
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.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link HeatingCircuitHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels and for polling the modbus.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HeatingCircuitHandler extends BaseThingHandler {
|
||||
|
||||
public abstract class AbstractBasePoller {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(HeatingCircuitHandler.class);
|
||||
|
||||
private volatile @Nullable PollTask pollTask;
|
||||
|
||||
public synchronized void unregisterPollTask() {
|
||||
PollTask task = pollTask;
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusCommunicationInterface mycomms = HeatingCircuitHandler.this.comms;
|
||||
if (mycomms != null) {
|
||||
mycomms.unregisterRegularPoll(task);
|
||||
}
|
||||
pollTask = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register poll task This is where we set up our regular poller
|
||||
*/
|
||||
public synchronized void registerPollTask(int address, int length, ModbusReadFunctionCode readFunctionCode) {
|
||||
logger.debug("Setting up regular polling Address: {}", address);
|
||||
|
||||
ModbusCommunicationInterface mycomms = HeatingCircuitHandler.this.comms;
|
||||
HeatingCircuitConfiguration myconfig = Objects.requireNonNull(HeatingCircuitHandler.this.config);
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("HeatingCircuit: registerPollTask called without proper configuration");
|
||||
}
|
||||
|
||||
ModbusReadRequestBlueprint request = new ModbusReadRequestBlueprint(getSlaveId(), readFunctionCode, address,
|
||||
length, myconfig.getMaxTries());
|
||||
|
||||
long refreshMillis = myconfig.getRefreshMillis();
|
||||
|
||||
pollTask = mycomms.registerRegularPoll(request, refreshMillis, 1000, result -> {
|
||||
result.getRegisters().ifPresent(this::handlePolledData);
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}, HeatingCircuitHandler.this::handleReadError);
|
||||
}
|
||||
|
||||
public synchronized void poll() {
|
||||
PollTask task = pollTask;
|
||||
ModbusCommunicationInterface mycomms = HeatingCircuitHandler.this.comms;
|
||||
if (task != null && mycomms != null) {
|
||||
mycomms.submitOneTimePoll(task.getRequest(), task.getResultCallback(), task.getFailureCallback());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void handlePolledData(ModbusRegisterArray registers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logger instance
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(HeatingCircuitHandler.class);
|
||||
|
||||
/**
|
||||
* Configuration instance
|
||||
*/
|
||||
protected @Nullable HeatingCircuitConfiguration config = null;
|
||||
|
||||
/**
|
||||
* Parsers used to convert incoming raw messages into state blocks
|
||||
*/
|
||||
|
||||
private final HeatingCircuitBlockParser heatingcircuitBlockParser = new HeatingCircuitBlockParser();
|
||||
private final HeatingCircuitReg50BlockParser heatingcircuitReg50BlockParser = new HeatingCircuitReg50BlockParser();
|
||||
|
||||
/**
|
||||
* These are the tasks used to poll the device
|
||||
*/
|
||||
|
||||
private volatile @Nullable AbstractBasePoller heatingcircuitPoller = null;
|
||||
private volatile @Nullable AbstractBasePoller heatingcircuitReg50Poller = null;
|
||||
/**
|
||||
* Communication interface to the slave endpoint we're connecting to
|
||||
*/
|
||||
protected volatile @Nullable ModbusCommunicationInterface comms = null;
|
||||
/**
|
||||
* This is the slave id, we store this once initialization is complete
|
||||
*/
|
||||
private volatile int slaveId;
|
||||
|
||||
/**
|
||||
* Instances of this handler should get a reference to the modbus manager
|
||||
*
|
||||
* @param thing the thing to handle
|
||||
*/
|
||||
public HeatingCircuitHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address address of the value to be written on the modbus
|
||||
*
|
||||
* @param shortValue value to be written on the modbus
|
||||
*/
|
||||
protected void writeInt16(int address, short shortValue) {
|
||||
HeatingCircuitConfiguration myconfig = Objects.requireNonNull(HeatingCircuitHandler.this.config);
|
||||
ModbusCommunicationInterface mycomms = HeatingCircuitHandler.this.comms;
|
||||
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("registerPollTask called without proper configuration");
|
||||
}
|
||||
// big endian byte ordering
|
||||
byte hi = (byte) (shortValue >> 8);
|
||||
byte lo = (byte) shortValue;
|
||||
ModbusRegisterArray data = new ModbusRegisterArray(hi, lo);
|
||||
|
||||
ModbusWriteRegisterRequestBlueprint request = new ModbusWriteRegisterRequestBlueprint(slaveId, address, data,
|
||||
true, myconfig.getMaxTries());
|
||||
|
||||
mycomms.submitOneTimeWrite(request, result -> {
|
||||
if (hasConfigurationError()) {
|
||||
return;
|
||||
}
|
||||
HeatingCircuitHandler.this.updateStatus(ThingStatus.ONLINE);
|
||||
}, failure -> {
|
||||
HeatingCircuitHandler.this.handleWriteError(failure);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command get the value of this command.
|
||||
*
|
||||
* @return short the value of the command as short
|
||||
*/
|
||||
private short getInt16Value(Command command) throws LambdaException {
|
||||
if (command instanceof QuantityType<?> quantityCommand) {
|
||||
QuantityType<?> c = quantityCommand.toUnit(WATT);
|
||||
if (c != null) {
|
||||
return c.shortValue();
|
||||
} else {
|
||||
throw new LambdaException("Unsupported unit");
|
||||
}
|
||||
}
|
||||
if (command instanceof DecimalType c) {
|
||||
return c.shortValue();
|
||||
}
|
||||
throw new LambdaException("Unsupported command type");
|
||||
}
|
||||
|
||||
private short getScaledInt16Value(Command command) throws LambdaException {
|
||||
if (command instanceof QuantityType<?> quantityCommand) {
|
||||
QuantityType<?> c = quantityCommand.toUnit(CELSIUS);
|
||||
if (c != null) {
|
||||
return (short) (c.doubleValue() * 10);
|
||||
} else {
|
||||
throw new LambdaException("Unsupported unit");
|
||||
}
|
||||
}
|
||||
if (command instanceof DecimalType c) {
|
||||
return (short) (c.doubleValue() * 10);
|
||||
}
|
||||
throw new LambdaException("Unsupported command type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle incoming commands.
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (RefreshType.REFRESH == command) {
|
||||
String groupId = channelUID.getGroupId();
|
||||
if (groupId != null) {
|
||||
AbstractBasePoller poller;
|
||||
switch (groupId) {
|
||||
case GROUP_HEATING_CIRCUIT:
|
||||
poller = heatingcircuitPoller;
|
||||
break;
|
||||
case GROUP_HEATING_CIRCUIT_REG50:
|
||||
poller = heatingcircuitReg50Poller;
|
||||
break;
|
||||
default:
|
||||
poller = null;
|
||||
break;
|
||||
}
|
||||
if (poller != null) {
|
||||
logger.trace("HeatingCircuit: Polling initiated");
|
||||
poller.poll();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (GROUP_HEATING_CIRCUIT.equals(channelUID.getGroupId())) {
|
||||
switch (channelUID.getIdWithoutGroup()) {
|
||||
case CHANNEL_HEATING_CIRCUIT_ROOM_DEVICE_TEMPERATURE:
|
||||
writeInt16(baseadress + 4, getScaledInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_HEATING_CIRCUIT_SETPOINT_FLOW_LINE_TEMPERATURE:
|
||||
writeInt16(baseadress + 5, getScaledInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_HEATING_CIRCUIT_OPERATING_MODE:
|
||||
writeInt16(baseadress + 6, getInt16Value(command));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (GROUP_HEATING_CIRCUIT_REG50.equals(channelUID.getGroupId())) {
|
||||
switch (channelUID.getIdWithoutGroup()) {
|
||||
case CHANNEL_HEATING_CIRCUIT_OFFSET_FLOW_LINE_TEMPERATURE:
|
||||
writeInt16(reg50baseadress, getScaledInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_HEATING_CIRCUIT_ROOM_HEATING_TEMPERATURE:
|
||||
writeInt16(reg50baseadress + 1, getScaledInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_HEATING_CIRCUIT_ROOM_COOLING_TEMPERATURE:
|
||||
writeInt16(reg50baseadress + 2, getScaledInt16Value(command));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (LambdaException error) {
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String cls = error.getClass().getName();
|
||||
String msg = error.getMessage();
|
||||
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with: %s: %s", cls, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization: Load the config object of the block Connect to the slave
|
||||
* bridge Start the periodic polling
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(HeatingCircuitConfiguration.class);
|
||||
|
||||
logger.debug("Initializing thing with properties: {}", thing.getProperties());
|
||||
|
||||
startUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adresses for the polling registers, used for reading and writing
|
||||
*/
|
||||
private int baseadress;
|
||||
private int reg50baseadress;
|
||||
|
||||
/**
|
||||
* This method starts the operation of this handler Connect to the slave bridge
|
||||
* Start the periodic polling1
|
||||
*/
|
||||
private void startUp() {
|
||||
if (comms != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusEndpointThingHandler slaveEndpointThingHandler = getEndpointThingHandler();
|
||||
if (slaveEndpointThingHandler == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
slaveId = slaveEndpointThingHandler.getSlaveId();
|
||||
|
||||
comms = slaveEndpointThingHandler.getCommunicationInterface();
|
||||
} catch (EndpointNotInitializedException e) {
|
||||
logger.debug("HeatingCircuit: Error setting up SlaveId");
|
||||
|
||||
}
|
||||
|
||||
if (comms == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
logger.debug("CommunicationInterface of Heating Circuit is null, Thing & Bridge are offline");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
logger.debug("Invalid comms/config/manager ref for lambda heatingcircuit handler");
|
||||
return;
|
||||
}
|
||||
|
||||
HeatingCircuitConfiguration myconfig = Objects.requireNonNull(HeatingCircuitHandler.this.config);
|
||||
|
||||
baseadress = 5000 + 100 * myconfig.getSubindex();
|
||||
reg50baseadress = baseadress + 50;
|
||||
|
||||
if (heatingcircuitPoller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledHeatingData(registers);
|
||||
}
|
||||
};
|
||||
// neu: poller.registerPollTask(baseadress, 8, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
poller.registerPollTask(baseadress, 7, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
heatingcircuitPoller = poller;
|
||||
}
|
||||
if (heatingcircuitReg50Poller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledHeatingReg50Data(registers);
|
||||
}
|
||||
};
|
||||
|
||||
poller.registerPollTask(reg50baseadress, 3, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
heatingcircuitReg50Poller = poller;
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose the binding correctly
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the poll tasks and release the endpoint reference
|
||||
*/
|
||||
private void tearDown() {
|
||||
AbstractBasePoller poller = heatingcircuitPoller;
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
heatingcircuitPoller = null;
|
||||
}
|
||||
|
||||
poller = heatingcircuitReg50Poller;
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
heatingcircuitReg50Poller = null;
|
||||
}
|
||||
comms = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slave id from the bridge
|
||||
*/
|
||||
public int getSlaveId() {
|
||||
return slaveId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the endpoint handler from the bridge this handler is connected to Checks
|
||||
* that we're connected to the right type of bridge
|
||||
*
|
||||
* @return the endpoint handler or null if the bridge does not exist
|
||||
*/
|
||||
private @Nullable ModbusEndpointThingHandler getEndpointThingHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
logger.debug("Bridge is null");
|
||||
return null;
|
||||
}
|
||||
if (bridge.getStatus() != ThingStatus.ONLINE) {
|
||||
logger.debug("Bridge is not online");
|
||||
return null;
|
||||
}
|
||||
|
||||
ThingHandler handler = bridge.getHandler();
|
||||
if (handler == null) {
|
||||
logger.debug("Bridge handler is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (handler instanceof ModbusEndpointThingHandler thingHandler) {
|
||||
return thingHandler;
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected bridge handler: " + handler.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected State getScaled(Number value, Unit<?> unit, Double pow) {
|
||||
double factor = Math.pow(10, pow);
|
||||
return QuantityType.valueOf(value.doubleValue() * factor, unit);
|
||||
}
|
||||
|
||||
protected State getUnscaled(Number value, Unit<?> unit) {
|
||||
return QuantityType.valueOf(value.doubleValue(), unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns high value * 1000 + low value
|
||||
*
|
||||
* @param high the high value
|
||||
*
|
||||
* @param low the low value
|
||||
*
|
||||
* @return the scaled value as a DecimalType
|
||||
*/
|
||||
protected State getEnergyQuantity(int high, int low) {
|
||||
double value = high * 1000 + low;
|
||||
return QuantityType.valueOf(value, KILOWATT_HOUR);
|
||||
}
|
||||
|
||||
/**
|
||||
* These methods are called each time new data has been polled from the modbus
|
||||
* slave The register array is first parsed, then each of the channels are
|
||||
* updated to the new values
|
||||
*
|
||||
* @param registers byte array read from the modbus slave
|
||||
*/
|
||||
|
||||
protected void handlePolledHeatingData(ModbusRegisterArray registers) {
|
||||
HeatingCircuitBlock block = heatingcircuitBlockParser.parse(registers);
|
||||
|
||||
updateState(channelUID(GROUP_HEATING_CIRCUIT, CHANNEL_HEATING_CIRCUIT_ERROR_NUMBER),
|
||||
new DecimalType(block.heatingcircuitErrorNumber));
|
||||
updateState(channelUID(GROUP_HEATING_CIRCUIT, CHANNEL_HEATING_CIRCUIT_OPERATING_STATE),
|
||||
new DecimalType(block.heatingcircuitOperatingState));
|
||||
updateState(channelUID(GROUP_HEATING_CIRCUIT, CHANNEL_HEATING_CIRCUIT_FLOW_LINE_TEMPERATURE),
|
||||
getScaled(block.heatingcircuitFlowLineTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_HEATING_CIRCUIT, CHANNEL_HEATING_CIRCUIT_RETURN_LINE_TEMPERATURE),
|
||||
getScaled(block.heatingcircuitReturnLineTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_HEATING_CIRCUIT, CHANNEL_HEATING_CIRCUIT_ROOM_DEVICE_TEMPERATURE),
|
||||
getScaled(block.heatingcircuitRoomDeviceTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_HEATING_CIRCUIT, CHANNEL_HEATING_CIRCUIT_SETPOINT_FLOW_LINE_TEMPERATURE),
|
||||
getScaled(block.heatingcircuitSetpointFlowLineTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_HEATING_CIRCUIT, CHANNEL_HEATING_CIRCUIT_OPERATING_MODE),
|
||||
new DecimalType(block.heatingcircuitOperatingMode));
|
||||
// neu: updateState(channelUID(GROUP_HEATING_CIRCUIT, CHANNEL_HEATING_CIRCUIT_TARGET_TEMPERATURE_FLOW_LINE),
|
||||
// neu: getScaled(block.heatingcircuitTargetTemperatureFlowLine, CELSIUS, -1.0));
|
||||
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
protected void handlePolledHeatingReg50Data(ModbusRegisterArray registers) {
|
||||
HeatingCircuitReg50Block block = heatingcircuitReg50BlockParser.parse(registers);
|
||||
|
||||
// HeatingCircuit1Settting group
|
||||
|
||||
updateState(channelUID(GROUP_HEATING_CIRCUIT_REG50, CHANNEL_HEATING_CIRCUIT_OFFSET_FLOW_LINE_TEMPERATURE),
|
||||
getScaled(block.heatingcircuitOffsetFlowLineTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_HEATING_CIRCUIT_REG50, CHANNEL_HEATING_CIRCUIT_ROOM_HEATING_TEMPERATURE),
|
||||
getScaled(block.heatingcircuitRoomHeatingTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_HEATING_CIRCUIT_REG50, CHANNEL_HEATING_CIRCUIT_ROOM_COOLING_TEMPERATURE),
|
||||
getScaled(block.heatingcircuitRoomCoolingTemperature, CELSIUS, -1.0));
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bridgeStatusInfo
|
||||
*/
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
super.bridgeStatusChanged(bridgeStatusInfo);
|
||||
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
startUp();
|
||||
} else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
|
||||
tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleReadError(AsyncModbusFailure<ModbusReadRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with read: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleWriteError(AsyncModbusFailure<ModbusWriteRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with write: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if we're in a CONFIGURATION_ERROR state
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean hasConfigurationError() {
|
||||
ThingStatusInfo statusInfo = getThing().getStatusInfo();
|
||||
return statusInfo.getStatus() == ThingStatus.OFFLINE
|
||||
&& statusInfo.getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset communication status to ONLINE if we're in an OFFLINE state
|
||||
*/
|
||||
protected void resetCommunicationError() {
|
||||
ThingStatusInfo statusInfo = thing.getStatusInfo();
|
||||
if (ThingStatus.OFFLINE.equals(statusInfo.getStatus())
|
||||
&& ThingStatusDetail.COMMUNICATION_ERROR.equals(statusInfo.getStatusDetail())) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel UID for the specified group and channel id
|
||||
*
|
||||
* @param string the channel group
|
||||
*
|
||||
* @param string the channel id in that group
|
||||
*
|
||||
* @return the globally unique channel uid
|
||||
*/
|
||||
ChannelUID channelUID(String group, String id) {
|
||||
return new ChannelUID(getThing().getUID(), group, id);
|
||||
}
|
||||
}
|
||||
+534
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.handler;
|
||||
|
||||
import static org.openhab.binding.modbus.lambda.internal.LambdaBindingConstants.*;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
import static org.openhab.core.library.unit.Units.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
|
||||
import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.HeatpumpConfiguration;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.HeatpumpBlock;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.HeatpumpBlockParser;
|
||||
import org.openhab.core.io.transport.modbus.AsyncModbusFailure;
|
||||
import org.openhab.core.io.transport.modbus.ModbusCommunicationInterface;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadFunctionCode;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.PollTask;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
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.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link HeatpumpHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels and for polling the modbus.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HeatpumpHandler extends BaseThingHandler {
|
||||
|
||||
public abstract class AbstractBasePoller {
|
||||
|
||||
private volatile @Nullable PollTask pollTask;
|
||||
|
||||
public synchronized void unregisterPollTask() {
|
||||
PollTask task = pollTask;
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusCommunicationInterface mycomms = HeatpumpHandler.this.comms;
|
||||
if (mycomms != null) {
|
||||
mycomms.unregisterRegularPoll(task);
|
||||
}
|
||||
pollTask = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register poll task This is where we set up our regular poller
|
||||
*/
|
||||
public synchronized void registerPollTask(int address, int length, ModbusReadFunctionCode readFunctionCode) {
|
||||
ModbusCommunicationInterface mycomms = HeatpumpHandler.this.comms;
|
||||
HeatpumpConfiguration myconfig = Objects.requireNonNull(HeatpumpHandler.this.config);
|
||||
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("Heatpump: registerPollTask called without proper configuration");
|
||||
}
|
||||
|
||||
ModbusReadRequestBlueprint request = new ModbusReadRequestBlueprint(getSlaveId(), readFunctionCode, address,
|
||||
length, myconfig.getMaxTries());
|
||||
|
||||
long refreshMillis = myconfig.getRefreshMillis();
|
||||
|
||||
pollTask = mycomms.registerRegularPoll(request, refreshMillis, 1000, result -> {
|
||||
result.getRegisters().ifPresent(this::handlePolledData);
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}, HeatpumpHandler.this::handleReadError);
|
||||
}
|
||||
|
||||
public synchronized void poll() {
|
||||
PollTask task = pollTask;
|
||||
ModbusCommunicationInterface mycomms = HeatpumpHandler.this.comms;
|
||||
if (task != null && mycomms != null) {
|
||||
mycomms.submitOneTimePoll(task.getRequest(), task.getResultCallback(), task.getFailureCallback());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void handlePolledData(ModbusRegisterArray registers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logger instance
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(HeatpumpHandler.class);
|
||||
|
||||
/**
|
||||
* Configuration instance
|
||||
*/
|
||||
protected @Nullable HeatpumpConfiguration config = null;
|
||||
/**
|
||||
* Parsers used to convert incoming raw messages into state blocks
|
||||
*/
|
||||
|
||||
private final HeatpumpBlockParser heatpumpBlockParser = new HeatpumpBlockParser();
|
||||
/**
|
||||
* These are the tasks used to poll the device
|
||||
*/
|
||||
private volatile @Nullable AbstractBasePoller heatpumpPoller = null;
|
||||
/**
|
||||
* Communication interface to the slave endpoint we're connecting to
|
||||
*/
|
||||
protected volatile @Nullable ModbusCommunicationInterface comms = null;
|
||||
/**
|
||||
* This is the slave id, we store this once initialization is complete
|
||||
*/
|
||||
private volatile int slaveId;
|
||||
|
||||
/**
|
||||
* Instances of this handler should get a reference to the modbus manager
|
||||
*
|
||||
* @param thing the thing to handle
|
||||
*/
|
||||
public HeatpumpHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address address of the value to be written on the modbus
|
||||
*
|
||||
* @param shortValue value to be written on the modbus
|
||||
*/
|
||||
protected void writeInt16(int address, short shortValue) {
|
||||
HeatpumpConfiguration myconfig = Objects.requireNonNull(HeatpumpHandler.this.config);
|
||||
ModbusCommunicationInterface mycomms = HeatpumpHandler.this.comms;
|
||||
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("registerPollTask called without proper configuration");
|
||||
}
|
||||
// big endian byte ordering
|
||||
byte hi = (byte) (shortValue >> 8);
|
||||
byte lo = (byte) shortValue;
|
||||
ModbusRegisterArray data = new ModbusRegisterArray(hi, lo);
|
||||
|
||||
ModbusWriteRegisterRequestBlueprint request = new ModbusWriteRegisterRequestBlueprint(slaveId, address, data,
|
||||
true, myconfig.getMaxTries());
|
||||
|
||||
mycomms.submitOneTimeWrite(request, result -> {
|
||||
if (hasConfigurationError()) {
|
||||
return;
|
||||
}
|
||||
HeatpumpHandler.this.updateStatus(ThingStatus.ONLINE);
|
||||
}, failure -> {
|
||||
HeatpumpHandler.this.handleWriteError(failure);
|
||||
});
|
||||
}
|
||||
|
||||
private short getScaledInt16Value(Command command) throws LambdaException {
|
||||
if (command instanceof QuantityType<?> quantityCommand) {
|
||||
QuantityType<?> c = quantityCommand.toUnit(CELSIUS);
|
||||
if (c != null) {
|
||||
return (short) (c.doubleValue() * 10);
|
||||
} else {
|
||||
throw new LambdaException("Unsupported unit");
|
||||
}
|
||||
}
|
||||
if (command instanceof DecimalType c) {
|
||||
return (short) (c.doubleValue() * 10);
|
||||
}
|
||||
throw new LambdaException("Unsupported command type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle incoming commands.
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (RefreshType.REFRESH == command) {
|
||||
String groupId = channelUID.getGroupId();
|
||||
if (groupId != null) {
|
||||
AbstractBasePoller poller;
|
||||
switch (groupId) {
|
||||
case GROUP_HEAT_PUMP:
|
||||
poller = heatpumpPoller;
|
||||
break;
|
||||
default:
|
||||
poller = null;
|
||||
break;
|
||||
}
|
||||
if (poller != null) {
|
||||
logger.trace("Heatpump: Polling initiated");
|
||||
poller.poll();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (GROUP_HEAT_PUMP.equals(channelUID.getGroupId())) {
|
||||
switch (channelUID.getIdWithoutGroup()) {
|
||||
case CHANNEL_HEAT_PUMP_REQUEST_T_FLOW:
|
||||
writeInt16(baseadress + 16, getScaledInt16Value(command));
|
||||
break;
|
||||
|
||||
case CHANNEL_HEAT_PUMP_REQUEST_T_RETURN:
|
||||
writeInt16(baseadress + 17, getScaledInt16Value(command));
|
||||
break;
|
||||
|
||||
case CHANNEL_HEAT_PUMP_REQUEST_HEAT_SINK:
|
||||
writeInt16(baseadress + 18, getScaledInt16Value(command));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (LambdaException error) {
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String cls = error.getClass().getName();
|
||||
String msg = error.getMessage();
|
||||
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with: %s: %s", cls, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization: Load the config object of the block Connect to the slave
|
||||
* bridge Start the periodic polling
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(HeatpumpConfiguration.class);
|
||||
|
||||
logger.debug("Initializing thing with properties: {}", thing.getProperties());
|
||||
|
||||
startUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adresses for the polling registers, used for reading and writing
|
||||
*/
|
||||
private int baseadress;
|
||||
|
||||
/**
|
||||
* This method starts the operation of this handler Connect to the slave bridge
|
||||
* Start the periodic polling1
|
||||
*/
|
||||
private void startUp() {
|
||||
if (comms != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusEndpointThingHandler slaveEndpointThingHandler = getEndpointThingHandler();
|
||||
if (slaveEndpointThingHandler == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
slaveId = slaveEndpointThingHandler.getSlaveId();
|
||||
|
||||
comms = slaveEndpointThingHandler.getCommunicationInterface();
|
||||
} catch (EndpointNotInitializedException e) {
|
||||
// this will be handled below as endpoint remains null
|
||||
}
|
||||
|
||||
if (comms == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
logger.debug("CommunicationInterface of Heat Pump is null, Thing & Bridge are offline");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
logger.debug("Invalid comms/config/manager ref for lambda heat-pump handler");
|
||||
return;
|
||||
}
|
||||
|
||||
HeatpumpConfiguration myconfig = Objects.requireNonNull(HeatpumpHandler.this.config);
|
||||
|
||||
baseadress = 1000 + 100 * myconfig.getSubindex();
|
||||
|
||||
if (heatpumpPoller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledHeatpumpData(registers);
|
||||
}
|
||||
};
|
||||
|
||||
poller.registerPollTask(baseadress, 24, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
heatpumpPoller = poller;
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose the binding correctly
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the poll tasks and release the endpoint reference
|
||||
*/
|
||||
private void tearDown() {
|
||||
AbstractBasePoller poller = heatpumpPoller;
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
heatpumpPoller = null;
|
||||
}
|
||||
|
||||
comms = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slave id from the bridge
|
||||
*/
|
||||
public int getSlaveId() {
|
||||
return slaveId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the endpoint handler from the bridge this handler is connected to Checks
|
||||
* that we're connected to the right type of bridge
|
||||
*
|
||||
* @return the endpoint handler or null if the bridge does not exist
|
||||
*/
|
||||
private @Nullable ModbusEndpointThingHandler getEndpointThingHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
logger.debug("Bridge is null");
|
||||
return null;
|
||||
}
|
||||
if (bridge.getStatus() != ThingStatus.ONLINE) {
|
||||
logger.debug("Bridge is not online");
|
||||
return null;
|
||||
}
|
||||
|
||||
ThingHandler handler = bridge.getHandler();
|
||||
if (handler == null) {
|
||||
logger.debug("Bridge handler is null");
|
||||
return null;
|
||||
}
|
||||
if (handler instanceof ModbusEndpointThingHandler thingHandler) {
|
||||
return thingHandler;
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected bridge handler: " + handler.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected State getScaled(Number value, Unit<?> unit, Double pow) {
|
||||
double factor = Math.pow(10, pow);
|
||||
return QuantityType.valueOf(value.doubleValue() * factor, unit);
|
||||
}
|
||||
|
||||
protected State getUnscaled(Number value, Unit<?> unit) {
|
||||
return QuantityType.valueOf(value.doubleValue(), unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns high value * 1000 + low value
|
||||
*
|
||||
* @param high the high value
|
||||
*
|
||||
* @param low the low value
|
||||
*
|
||||
* @return the scaled value as a DecimalType
|
||||
*/
|
||||
protected State getEnergyQuantity(int high, int low) {
|
||||
double value = high * 1000 + low;
|
||||
return QuantityType.valueOf(value, KILOWATT_HOUR);
|
||||
}
|
||||
|
||||
/**
|
||||
* These methods are called each time new data has been polled from the modbus
|
||||
* slave The register array is first parsed, then each of the channels are
|
||||
* updated to the new values
|
||||
*
|
||||
* @param registers byte array read from the modbus slave
|
||||
*/
|
||||
|
||||
protected void handlePolledHeatpumpData(ModbusRegisterArray registers) {
|
||||
HeatpumpBlock block = heatpumpBlockParser.parse(registers);
|
||||
|
||||
// Heatpump group
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_ERROR_STATE),
|
||||
new DecimalType(block.heatpumpErrorState));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_ERROR_NUMBER),
|
||||
new DecimalType(block.heatpumpErrorNumber));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_OPERATING_STATE),
|
||||
new DecimalType(block.heatpumpOperatingState));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_STATE), new DecimalType(block.heatpumpState));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_T_FLOW),
|
||||
getScaled(block.heatpumpTFlow, CELSIUS, -2.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_T_RETURN),
|
||||
getScaled(block.heatpumpTReturn, CELSIUS, -2.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_VOL_SINK),
|
||||
getScaled(block.heatpumpVolSink, LITRE_PER_MINUTE, -2.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_T_EQIN),
|
||||
getScaled(block.heatpumpTEQin, CELSIUS, -2.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_T_EQOUT),
|
||||
getScaled(block.heatpumpTEQout, CELSIUS, -2.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_VOL_SOURCE),
|
||||
getScaled(block.heatpumpVolSource, LITRE_PER_MINUTE, -2.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_COMPRESSOR_RATING),
|
||||
getScaled(block.heatpumpCompressorRating, PERCENT, -2.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_QP_HEATING),
|
||||
getScaled(block.heatpumpQpHeating, WATT, 2.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_FI_POWER_CONSUMPTION),
|
||||
getUnscaled(block.heatpumpFIPowerConsumption, WATT));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_COP), getScaled(block.heatpumpCOP, PERCENT, -2.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_REQUEST_PASSWORD),
|
||||
new DecimalType(block.heatpumpRequestPassword));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_REQUEST_TYPE),
|
||||
new DecimalType(block.heatpumpRequestType));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_REQUEST_T_FLOW),
|
||||
getScaled(block.heatpumpRequestTFlow, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_REQUEST_T_RETURN),
|
||||
getScaled(block.heatpumpRequestTReturn, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_REQUEST_HEAT_SINK),
|
||||
getScaled(block.heatpumpRequestHeatSink, KELVIN, -1.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_RELAIS_STATE),
|
||||
new DecimalType(block.heatpumpRelaisState));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_VDAE),
|
||||
getScaled(block.heatpumpVdAE, KILOWATT_HOUR, -3.0));
|
||||
updateState(channelUID(GROUP_HEAT_PUMP, CHANNEL_HEAT_PUMP_VDAQ),
|
||||
getScaled(block.heatpumpVdAQ, KILOWATT_HOUR, -3.0));
|
||||
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bridgeStatusInfo
|
||||
*/
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
super.bridgeStatusChanged(bridgeStatusInfo);
|
||||
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
startUp();
|
||||
} else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
|
||||
tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleReadError(AsyncModbusFailure<ModbusReadRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with read: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleWriteError(AsyncModbusFailure<ModbusWriteRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with write: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if we're in a CONFIGURATION_ERROR state
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean hasConfigurationError() {
|
||||
ThingStatusInfo statusInfo = getThing().getStatusInfo();
|
||||
return statusInfo.getStatus() == ThingStatus.OFFLINE
|
||||
&& statusInfo.getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset communication status to ONLINE if we're in an OFFLINE state
|
||||
*/
|
||||
protected void resetCommunicationError() {
|
||||
ThingStatusInfo statusInfo = thing.getStatusInfo();
|
||||
if (ThingStatus.OFFLINE.equals(statusInfo.getStatus())
|
||||
&& ThingStatusDetail.COMMUNICATION_ERROR.equals(statusInfo.getStatusDetail())) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel UID for the specified group and channel id
|
||||
*
|
||||
* @param string the channel group
|
||||
*
|
||||
* @param string the channel id in that group
|
||||
*
|
||||
* @return the globally unique channel uid
|
||||
*/
|
||||
ChannelUID channelUID(String group, String id) {
|
||||
return new ChannelUID(getThing().getUID(), group, id);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Thrown when the lambda handler sees an error.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class LambdaException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public LambdaException() {
|
||||
}
|
||||
|
||||
public LambdaException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
+437
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.handler;
|
||||
|
||||
import static org.openhab.binding.modbus.lambda.internal.LambdaBindingConstants.*;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
|
||||
import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler;
|
||||
import org.openhab.binding.modbus.lambda.internal.SolarConfiguration;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.SolarBlock;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.SolarReg50Block;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.SolarBlockParser;
|
||||
import org.openhab.binding.modbus.lambda.internal.parser.SolarReg50BlockParser;
|
||||
import org.openhab.core.io.transport.modbus.AsyncModbusFailure;
|
||||
import org.openhab.core.io.transport.modbus.ModbusCommunicationInterface;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadFunctionCode;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint;
|
||||
import org.openhab.core.io.transport.modbus.PollTask;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
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.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link SolarHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels and for polling the modbus
|
||||
* for solar thermic component data.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SolarHandler extends BaseThingHandler {
|
||||
|
||||
public abstract class AbstractBasePoller {
|
||||
|
||||
private volatile @Nullable PollTask pollTask;
|
||||
|
||||
public synchronized void unregisterPollTask() {
|
||||
PollTask task = pollTask;
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusCommunicationInterface mycomms = SolarHandler.this.comms;
|
||||
if (mycomms != null) {
|
||||
mycomms.unregisterRegularPoll(task);
|
||||
}
|
||||
pollTask = null;
|
||||
}
|
||||
|
||||
public synchronized void registerPollTask(int address, int length, ModbusReadFunctionCode readFunctionCode) {
|
||||
ModbusCommunicationInterface mycomms = SolarHandler.this.comms;
|
||||
SolarConfiguration myconfig = Objects.requireNonNull(SolarHandler.this.config);
|
||||
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("Solar: registerPollTask called without proper configuration");
|
||||
}
|
||||
|
||||
ModbusReadRequestBlueprint request = new ModbusReadRequestBlueprint(getSlaveId(), readFunctionCode, address,
|
||||
length, myconfig.getMaxTries());
|
||||
|
||||
long refreshMillis = myconfig.getRefreshMillis();
|
||||
|
||||
pollTask = mycomms.registerRegularPoll(request, refreshMillis, 1000, result -> {
|
||||
result.getRegisters().ifPresent(this::handlePolledData);
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}, SolarHandler.this::handleReadError);
|
||||
}
|
||||
|
||||
public synchronized void poll() {
|
||||
PollTask task = pollTask;
|
||||
ModbusCommunicationInterface mycomms = SolarHandler.this.comms;
|
||||
if (task != null && mycomms != null) {
|
||||
mycomms.submitOneTimePoll(task.getRequest(), task.getResultCallback(), task.getFailureCallback());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void handlePolledData(ModbusRegisterArray registers);
|
||||
}
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SolarHandler.class);
|
||||
|
||||
protected @Nullable SolarConfiguration config = null;
|
||||
|
||||
private final SolarBlockParser solarBlockParser = new SolarBlockParser();
|
||||
private final SolarReg50BlockParser solarReg50BlockParser = new SolarReg50BlockParser();
|
||||
|
||||
private volatile @Nullable AbstractBasePoller solarPoller = null;
|
||||
private volatile @Nullable AbstractBasePoller solarReg50Poller = null;
|
||||
|
||||
protected volatile @Nullable ModbusCommunicationInterface comms = null;
|
||||
|
||||
private volatile int slaveId;
|
||||
|
||||
public SolarHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command get the value of this command.
|
||||
*
|
||||
* @return short the value of the command as short
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param address address of the value to be written on the modbus
|
||||
*
|
||||
* @param shortValue value to be written on the modbus
|
||||
*/
|
||||
protected void writeInt16(int address, short shortValue) {
|
||||
SolarConfiguration myconfig = Objects.requireNonNull(SolarHandler.this.config);
|
||||
ModbusCommunicationInterface mycomms = SolarHandler.this.comms;
|
||||
|
||||
if (myconfig == null || mycomms == null) {
|
||||
throw new IllegalStateException("registerPollTask called without proper configuration");
|
||||
}
|
||||
// big endian byte ordering
|
||||
byte hi = (byte) (shortValue >> 8);
|
||||
byte lo = (byte) shortValue;
|
||||
ModbusRegisterArray data = new ModbusRegisterArray(hi, lo);
|
||||
|
||||
ModbusWriteRegisterRequestBlueprint request = new ModbusWriteRegisterRequestBlueprint(slaveId, address, data,
|
||||
true, myconfig.getMaxTries());
|
||||
|
||||
mycomms.submitOneTimeWrite(request, result -> {
|
||||
if (hasConfigurationError()) {
|
||||
return;
|
||||
}
|
||||
SolarHandler.this.updateStatus(ThingStatus.ONLINE);
|
||||
}, failure -> {
|
||||
SolarHandler.this.handleWriteError(failure);
|
||||
});
|
||||
}
|
||||
|
||||
private short getScaledInt16Value(Command command) throws LambdaException {
|
||||
if (command instanceof QuantityType<?> quantityCommand) {
|
||||
QuantityType<?> c = quantityCommand.toUnit(CELSIUS);
|
||||
if (c != null) {
|
||||
return (short) (c.doubleValue() * 10);
|
||||
} else {
|
||||
throw new LambdaException("Unsupported unit");
|
||||
}
|
||||
}
|
||||
if (command instanceof DecimalType c) {
|
||||
return (short) (c.doubleValue() * 10);
|
||||
}
|
||||
throw new LambdaException("Unsupported command type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (RefreshType.REFRESH == command) {
|
||||
String groupId = channelUID.getGroupId();
|
||||
if (groupId != null) {
|
||||
AbstractBasePoller poller;
|
||||
switch (groupId) {
|
||||
case GROUP_SOLAR:
|
||||
poller = solarPoller;
|
||||
break;
|
||||
case GROUP_SOLAR_REG50:
|
||||
poller = solarReg50Poller;
|
||||
break;
|
||||
default:
|
||||
poller = null;
|
||||
break;
|
||||
}
|
||||
if (poller != null) {
|
||||
logger.trace("Solar: Polling initiated");
|
||||
poller.poll();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (GROUP_SOLAR_REG50.equals(channelUID.getGroupId())) {
|
||||
switch (channelUID.getIdWithoutGroup()) {
|
||||
case CHANNEL_SOLAR_MAXIMUM_BUFFER_TEMPERATURE:
|
||||
writeInt16(reg50baseadress, getScaledInt16Value(command));
|
||||
break;
|
||||
case CHANNEL_SOLAR_BUFFER_CHANGEOVER_TEMPERATURE:
|
||||
writeInt16(reg50baseadress + 1, getScaledInt16Value(command));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (LambdaException error) {
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String cls = error.getClass().getName();
|
||||
String msg = error.getMessage();
|
||||
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(SolarConfiguration.class);
|
||||
|
||||
logger.debug("Initializing solar thing with properties: {}", thing.getProperties());
|
||||
|
||||
startUp();
|
||||
}
|
||||
|
||||
private int baseadress;
|
||||
private int reg50baseadress;
|
||||
|
||||
private void startUp() {
|
||||
if (comms != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusEndpointThingHandler slaveEndpointThingHandler = getEndpointThingHandler();
|
||||
if (slaveEndpointThingHandler == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
slaveId = slaveEndpointThingHandler.getSlaveId();
|
||||
comms = slaveEndpointThingHandler.getCommunicationInterface();
|
||||
} catch (EndpointNotInitializedException e) {
|
||||
// this will be handled below as endpoint remains null
|
||||
}
|
||||
|
||||
if (comms == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
logger.debug("CommunicationInterface of Solar is null, Thing & Bridge are offline");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
logger.debug("Invalid comms/config/manager ref for lambda solar handler");
|
||||
return;
|
||||
}
|
||||
|
||||
SolarConfiguration myconfig = Objects.requireNonNull(SolarHandler.this.config);
|
||||
|
||||
// Base address for solar is 4000 as mentioned in README.md
|
||||
baseadress = 4000 + 100 * myconfig.getSubindex();
|
||||
reg50baseadress = baseadress + 50;
|
||||
|
||||
if (solarPoller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledSolarData(registers);
|
||||
}
|
||||
};
|
||||
|
||||
poller.registerPollTask(baseadress, 5, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
solarPoller = poller;
|
||||
}
|
||||
if (solarReg50Poller == null) {
|
||||
AbstractBasePoller poller = new AbstractBasePoller() {
|
||||
@Override
|
||||
protected void handlePolledData(ModbusRegisterArray registers) {
|
||||
handlePolledSolarReg50Data(registers);
|
||||
}
|
||||
};
|
||||
|
||||
poller.registerPollTask(reg50baseadress, 2, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
|
||||
solarReg50Poller = poller;
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
tearDown();
|
||||
}
|
||||
|
||||
private void tearDown() {
|
||||
AbstractBasePoller poller = solarPoller;
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
solarPoller = null;
|
||||
}
|
||||
|
||||
poller = solarReg50Poller;
|
||||
if (poller != null) {
|
||||
poller.unregisterPollTask();
|
||||
solarReg50Poller = null;
|
||||
}
|
||||
|
||||
comms = null;
|
||||
}
|
||||
|
||||
public int getSlaveId() {
|
||||
return slaveId;
|
||||
}
|
||||
|
||||
private @Nullable ModbusEndpointThingHandler getEndpointThingHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
logger.debug("Bridge is null");
|
||||
return null;
|
||||
}
|
||||
if (bridge.getStatus() != ThingStatus.ONLINE) {
|
||||
logger.debug("Bridge is not online");
|
||||
return null;
|
||||
}
|
||||
|
||||
ThingHandler handler = bridge.getHandler();
|
||||
if (handler == null) {
|
||||
logger.debug("Bridge handler is null");
|
||||
return null;
|
||||
}
|
||||
if (handler instanceof ModbusEndpointThingHandler thingHandler) {
|
||||
return thingHandler;
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected bridge handler: " + handler.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected State getScaled(Number value, Unit<?> unit, Double pow) {
|
||||
double factor = Math.pow(10, pow);
|
||||
return QuantityType.valueOf(value.doubleValue() * factor, unit);
|
||||
}
|
||||
|
||||
protected void handlePolledSolarData(ModbusRegisterArray registers) {
|
||||
SolarBlock block = solarBlockParser.parse(registers);
|
||||
|
||||
// Update solar channels
|
||||
updateState(channelUID(GROUP_SOLAR, CHANNEL_SOLAR_ERROR_NUMBER), new DecimalType(block.solarErrorNumber));
|
||||
updateState(channelUID(GROUP_SOLAR, CHANNEL_SOLAR_OPERATING_STATE), new DecimalType(block.solarOperatingState));
|
||||
updateState(channelUID(GROUP_SOLAR, CHANNEL_SOLAR_COLLECTOR_TEMPERATURE),
|
||||
getScaled(block.solarCollectorTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_SOLAR, CHANNEL_SOLAR_BUFFER1_TEMPERATURE),
|
||||
getScaled(block.solarBuffer1Temperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_SOLAR, CHANNEL_SOLAR_BUFFER2_TEMPERATURE),
|
||||
getScaled(block.solarBuffer2Temperature, CELSIUS, -1.0));
|
||||
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
protected void handlePolledSolarReg50Data(ModbusRegisterArray registers) {
|
||||
SolarReg50Block block = solarReg50BlockParser.parse(registers);
|
||||
|
||||
updateState(channelUID(GROUP_SOLAR_REG50, CHANNEL_SOLAR_MAXIMUM_BUFFER_TEMPERATURE),
|
||||
getScaled(block.solarMaximumBufferTemperature, CELSIUS, -1.0));
|
||||
updateState(channelUID(GROUP_SOLAR_REG50, CHANNEL_SOLAR_BUFFER_CHANGEOVER_TEMPERATURE),
|
||||
getScaled(block.solarBufferChangeoverTemperature, CELSIUS, -1.0));
|
||||
|
||||
resetCommunicationError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
super.bridgeStatusChanged(bridgeStatusInfo);
|
||||
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
startUp();
|
||||
} else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
|
||||
tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleReadError(AsyncModbusFailure<ModbusReadRequestBlueprint> failure) {
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with read: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle errors received during communication
|
||||
*/
|
||||
protected void handleWriteError(AsyncModbusFailure<ModbusWriteRequestBlueprint> failure) {
|
||||
// Ignore all incoming data and errors if configuration is not correct
|
||||
if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
return;
|
||||
}
|
||||
String msg = failure.getCause().getMessage();
|
||||
String cls = failure.getCause().getClass().getName();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
String.format("Error with write: %s: %s", cls, msg));
|
||||
}
|
||||
|
||||
protected boolean hasConfigurationError() {
|
||||
ThingStatusInfo statusInfo = getThing().getStatusInfo();
|
||||
return statusInfo.getStatus() == ThingStatus.OFFLINE
|
||||
&& statusInfo.getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR;
|
||||
}
|
||||
|
||||
protected void resetCommunicationError() {
|
||||
ThingStatusInfo statusInfo = thing.getStatusInfo();
|
||||
if (ThingStatus.OFFLINE.equals(statusInfo.getStatus())
|
||||
&& ThingStatusDetail.COMMUNICATION_ERROR.equals(statusInfo.getStatusDetail())) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
ChannelUID channelUID(String group, String id) {
|
||||
return new ChannelUID(getThing().getUID(), group, id);
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.io.transport.modbus.ModbusBitUtilities;
|
||||
import org.openhab.core.io.transport.modbus.ModbusConstants.ValueType;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
|
||||
/**
|
||||
* Base class for parsers with some helper methods
|
||||
*
|
||||
* @author Nagy Attila Gabor - Initial contribution
|
||||
* @author Paul Frank - Added more methods
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AbstractBaseParser {
|
||||
|
||||
/**
|
||||
* Extract an optional double value
|
||||
*
|
||||
* @param raw the register array to extract from
|
||||
*
|
||||
* @param index the address of the field
|
||||
*
|
||||
* @return the parsed value or empty if the field is not implemented
|
||||
*/
|
||||
protected Optional<Double> extractOptionalDouble(ModbusRegisterArray raw, int index) {
|
||||
return ModbusBitUtilities.extractStateFromRegisters(raw, index, ValueType.INT16)
|
||||
.map(value -> ((double) value.intValue()) / 10.0).filter(value -> value != (short) 0x8000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a mandatory double value
|
||||
*
|
||||
* @param raw the register array to extract from
|
||||
*
|
||||
* @param index the address of the field
|
||||
*
|
||||
* @param def the default value
|
||||
*
|
||||
* @return the parsed value or the default if the field is not implemented
|
||||
*/
|
||||
protected Double extractDouble(ModbusRegisterArray raw, int index, double def) {
|
||||
return Objects.requireNonNull(extractOptionalDouble(raw, index).orElse(def));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract an optional int16 value
|
||||
*
|
||||
* @param raw the register array to extract from
|
||||
*
|
||||
* @param index the address of the field
|
||||
*
|
||||
* @return the parsed value or empty if the field is not implemented
|
||||
*/
|
||||
protected Optional<Short> extractOptionalInt16(ModbusRegisterArray raw, int index) {
|
||||
return ModbusBitUtilities.extractStateFromRegisters(raw, index, ValueType.INT16).map(DecimalType::shortValue)
|
||||
.filter(value -> value != (short) 0x8000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a mandatory int16 value
|
||||
*
|
||||
* @param raw the register array to extract from
|
||||
*
|
||||
* @param index the address of the field
|
||||
*
|
||||
* @param def the default value
|
||||
*
|
||||
* @return the parsed value or the default if the field is not implemented
|
||||
*/
|
||||
protected Short extractInt16(ModbusRegisterArray raw, int index, short def) {
|
||||
return Objects.requireNonNull(extractOptionalInt16(raw, index).orElse(def));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract an optional uint16 value
|
||||
*
|
||||
* @param raw the register array to extract from
|
||||
*
|
||||
* @param index the address of the field
|
||||
*
|
||||
* @return the parsed value or empty if the field is not implemented
|
||||
*/
|
||||
protected Optional<Integer> extractOptionalUInt16(ModbusRegisterArray raw, int index) {
|
||||
return ModbusBitUtilities.extractStateFromRegisters(raw, index, ValueType.UINT16).map(DecimalType::intValue)
|
||||
.filter(value -> value != 0xffff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a mandatory uint16 value
|
||||
*
|
||||
* @param raw the register array to extract from
|
||||
*
|
||||
* @param index the address of the field
|
||||
*
|
||||
* @param def the default value
|
||||
*
|
||||
* @return the parsed value or the default if the field is not implemented
|
||||
*/
|
||||
protected Integer extractUInt16(ModbusRegisterArray raw, int index, int def) {
|
||||
return Objects.requireNonNull(extractOptionalUInt16(raw, index).orElse(def));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract an optional acc32 value
|
||||
*
|
||||
* @param raw the register array to extract from
|
||||
*
|
||||
* @param index the address of the field
|
||||
*
|
||||
* @return the parsed value or empty if the field is not implemented
|
||||
*/
|
||||
protected Optional<Long> extractOptionalUInt32(ModbusRegisterArray raw, int index) {
|
||||
return ModbusBitUtilities.extractStateFromRegisters(raw, index, ValueType.UINT32).map(DecimalType::longValue)
|
||||
.filter(value -> value != 0);
|
||||
}
|
||||
|
||||
protected Optional<Long> extractOptionalInt32(ModbusRegisterArray raw, int index) {
|
||||
return ModbusBitUtilities.extractStateFromRegisters(raw, index, ValueType.INT32).map(DecimalType::longValue)
|
||||
.filter(value -> value != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a mandatory acc32 value
|
||||
*
|
||||
* @param raw the register array to extract from
|
||||
*
|
||||
* @param index the address of the field
|
||||
*
|
||||
* @param def the default value
|
||||
*
|
||||
* @return the parsed value or default if the field is not implemented
|
||||
*/
|
||||
protected Long extractUInt32(ModbusRegisterArray raw, int index, long def) {
|
||||
return Objects.requireNonNull(extractOptionalUInt32(raw, index).orElse(def));
|
||||
}
|
||||
|
||||
protected Long extractInt32(ModbusRegisterArray raw, int index, long def) {
|
||||
return Objects.requireNonNull(extractOptionalInt32(raw, index).orElse(def));
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.AmbientBlock;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parses lambda modbus data into an Ambient Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AmbientBlockParser extends AbstractBaseParser {
|
||||
|
||||
public AmbientBlock parse(ModbusRegisterArray raw) {
|
||||
AmbientBlock block = new AmbientBlock();
|
||||
block.ambientErrorNumber = extractInt16(raw, 0, (short) 0);
|
||||
block.ambientOperatingState = extractUInt16(raw, 1, (short) 0);
|
||||
block.actualAmbientTemperature = extractInt16(raw, 2, (short) 0);
|
||||
block.averageAmbientTemperature = extractInt16(raw, 3, (short) 0);
|
||||
block.calculatedAmbientTemperature = extractInt16(raw, 4, (short) 0);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.BoilerBlock;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parses lambda modbus data into a Boiler Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BoilerBlockParser extends AbstractBaseParser {
|
||||
|
||||
public BoilerBlock parse(ModbusRegisterArray raw) {
|
||||
BoilerBlock block = new BoilerBlock();
|
||||
block.boilerErrorNumber = extractUInt16(raw, 0, (short) 0);
|
||||
block.boilerOperatingState = extractInt16(raw, 1, (short) 0);
|
||||
block.boilerActualHighTemperature = extractInt16(raw, 2, (short) 0);
|
||||
block.boilerActualLowTemperature = extractInt16(raw, 3, (short) 0);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.BoilerReg50Block;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parses modbus data into an BoilerReg50 Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BoilerReg50BlockParser extends AbstractBaseParser {
|
||||
public BoilerReg50Block parse(ModbusRegisterArray raw) {
|
||||
BoilerReg50Block block = new BoilerReg50Block();
|
||||
block.boilerMaximumBoilerTemperature = extractInt16(raw, 0, (short) 0);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.BufferBlock;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parses lambda modbus data into a Buffer Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BufferBlockParser extends AbstractBaseParser {
|
||||
|
||||
public BufferBlock parse(ModbusRegisterArray raw) {
|
||||
BufferBlock block = new BufferBlock();
|
||||
block.bufferErrorNumber = extractInt16(raw, 0, (short) 0);
|
||||
block.bufferOperatingState = extractUInt16(raw, 1, (short) 0);
|
||||
block.bufferActualHighTemperature = extractInt16(raw, 2, (short) 0);
|
||||
block.bufferActualLowTemperature = extractInt16(raw, 3, (short) 0);
|
||||
block.bufferActualModbusTemperature = extractInt16(raw, 4, (short) 0);
|
||||
block.bufferRequestType = extractInt16(raw, 5, (short) 0);
|
||||
block.bufferrequestFlowLineTemperature = extractInt16(raw, 6, (short) 0);
|
||||
block.bufferrequestReturnLineTemperature = extractInt16(raw, 7, (short) 0);
|
||||
block.bufferrequestHeatSinkTemperature = extractInt16(raw, 8, (short) 0);
|
||||
block.bufferrequestHeatingCapacity = extractInt16(raw, 9, (short) 0);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.BufferReg50Block;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parses lambda modbus data into an BufferReg50 Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BufferReg50BlockParser extends AbstractBaseParser {
|
||||
|
||||
public BufferReg50Block parse(ModbusRegisterArray raw) {
|
||||
BufferReg50Block block = new BufferReg50Block();
|
||||
block.bufferMaximumBufferTemperature = extractUInt16(raw, 0, (short) 0);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.EManagerBlock;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parses inverter modbus data into an EManager Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EManagerBlockParser extends AbstractBaseParser {
|
||||
|
||||
public EManagerBlock parse(ModbusRegisterArray raw) {
|
||||
EManagerBlock block = new EManagerBlock();
|
||||
block.emanagerErrorNumber = extractInt16(raw, 0, (short) 0);
|
||||
block.emanagerOperatingState = extractUInt16(raw, 1, (short) 0);
|
||||
block.emanagerActualPower = extractUInt16(raw, 2, (short) 0);
|
||||
block.emanagerActualPowerSigned = extractInt16(raw, 2, (short) 0);
|
||||
block.emanagerActualPowerConsumption = extractInt16(raw, 3, (short) 0);
|
||||
block.emanagerPowerConsumptionSetpoint = extractInt16(raw, 4, (short) 0);
|
||||
|
||||
return block;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.HeatingCircuitBlock;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parses lambda
|
||||
* modbus data into a Heating Circuit Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HeatingCircuitBlockParser extends AbstractBaseParser {
|
||||
|
||||
public HeatingCircuitBlock parse(ModbusRegisterArray raw) {
|
||||
HeatingCircuitBlock block = new HeatingCircuitBlock();
|
||||
block.heatingcircuitErrorNumber = extractInt16(raw, 0, (short) 0);
|
||||
block.heatingcircuitOperatingState = extractUInt16(raw, 1, (short) 0);
|
||||
block.heatingcircuitFlowLineTemperature = extractInt16(raw, 2, (short) 0);
|
||||
block.heatingcircuitReturnLineTemperature = extractInt16(raw, 3, (short) 0);
|
||||
block.heatingcircuitRoomDeviceTemperature = extractInt16(raw, 4, (short) 0);
|
||||
block.heatingcircuitSetpointFlowLineTemperature = extractInt16(raw, 5, (short) 0);
|
||||
block.heatingcircuitOperatingMode = extractInt16(raw, 6, (short) 0);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.HeatingCircuitReg50Block;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parses lambda modbus data into a HeatingCircuitReg50 Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HeatingCircuitReg50BlockParser extends AbstractBaseParser {
|
||||
|
||||
public HeatingCircuitReg50Block parse(ModbusRegisterArray raw) {
|
||||
HeatingCircuitReg50Block block = new HeatingCircuitReg50Block();
|
||||
block.heatingcircuitOffsetFlowLineTemperature = extractInt16(raw, 0, (short) 0);
|
||||
block.heatingcircuitRoomHeatingTemperature = extractInt16(raw, 1, (short) 0);
|
||||
block.heatingcircuitRoomCoolingTemperature = extractInt16(raw, 2, (short) 0);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.HeatpumpBlock;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parses lambda modbus data into a Heatpump Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HeatpumpBlockParser extends AbstractBaseParser {
|
||||
|
||||
public HeatpumpBlock parse(ModbusRegisterArray raw) {
|
||||
HeatpumpBlock block = new HeatpumpBlock();
|
||||
block.heatpumpErrorState = extractUInt16(raw, 0, (short) 0);
|
||||
block.heatpumpErrorNumber = extractInt16(raw, 1, (short) 0);
|
||||
block.heatpumpState = extractUInt16(raw, 2, (short) 0);
|
||||
block.heatpumpOperatingState = extractUInt16(raw, 3, (short) 0);
|
||||
block.heatpumpTFlow = extractInt16(raw, 4, (short) 0);
|
||||
block.heatpumpTReturn = extractInt16(raw, 5, (short) 0);
|
||||
block.heatpumpVolSink = extractInt16(raw, 6, (short) 0);
|
||||
block.heatpumpTEQin = extractInt16(raw, 7, (short) 0);
|
||||
block.heatpumpTEQout = extractInt16(raw, 8, (short) 0);
|
||||
block.heatpumpVolSource = extractInt16(raw, 9, (short) 0);
|
||||
block.heatpumpCompressorRating = extractUInt16(raw, 10, (short) 0);
|
||||
block.heatpumpQpHeating = extractInt16(raw, 11, (short) 0);
|
||||
block.heatpumpFIPowerConsumption = extractInt16(raw, 12, (short) 0);
|
||||
block.heatpumpCOP = extractInt16(raw, 13, (short) 0);
|
||||
block.heatpumpRequestPassword = extractUInt16(raw, 14, (short) 0);
|
||||
block.heatpumpRequestType = extractInt16(raw, 15, (short) 0);
|
||||
block.heatpumpRequestTFlow = extractInt16(raw, 16, (short) 0);
|
||||
block.heatpumpRequestTReturn = extractInt16(raw, 17, (short) 0);
|
||||
block.heatpumpRequestHeatSink = extractInt16(raw, 18, (short) 0);
|
||||
block.heatpumpRelaisState = extractInt16(raw, 19, (short) 0);
|
||||
block.heatpumpVdAE = extractInt32(raw, 20, (long) 0);
|
||||
block.heatpumpVdAQ = extractInt32(raw, 22, (long) 0);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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
|
||||
*/
|
||||
/**
|
||||
* Parses lambda modbus data into a Solar Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
|
||||
package org.openhab.binding.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.SolarBlock;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parser for solar thermic component data from modbus registers
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SolarBlockParser extends AbstractBaseParser {
|
||||
|
||||
public SolarBlock parse(ModbusRegisterArray registers) {
|
||||
SolarBlock block = new SolarBlock();
|
||||
block.solarErrorNumber = extractUInt16(registers, 0, 0);
|
||||
block.solarOperatingState = extractUInt16(registers, 1, 0);
|
||||
block.solarCollectorTemperature = extractInt16(registers, 2, (short) 0);
|
||||
// Two different versions for usage in different configurations of the manufacturer
|
||||
block.solarBuffer1Temperature = extractInt16(registers, 3, (short) 0);
|
||||
block.solarBuffer2Temperature = extractUInt16(registers, 4, 0);
|
||||
|
||||
return block;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.modbus.lambda.internal.parser;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.lambda.internal.dto.SolarReg50Block;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
|
||||
/**
|
||||
* Parses lambda modbus data into a SolarReg50 Block
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christian Koch - modified for lambda heat pump based on stiebeleltron binding for modbus
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SolarReg50BlockParser extends AbstractBaseParser {
|
||||
|
||||
public SolarReg50Block parse(ModbusRegisterArray raw) {
|
||||
SolarReg50Block block = new SolarReg50Block();
|
||||
block.solarMaximumBufferTemperature = extractInt16(raw, 0, (short) 0);
|
||||
block.solarBufferChangeoverTemperature = extractInt16(raw, 1, (short) 0);
|
||||
|
||||
return block;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="modbus" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
<type>binding</type>
|
||||
<name>Lambda Heat Pump Binding</name>
|
||||
<description>Lambda heat pump binding via Modbus TCP/IP.</description>
|
||||
<connection>local</connection>
|
||||
|
||||
</addon:addon>
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
# add-on
|
||||
|
||||
addon.modbus.lambda.name = Lambda Heat Pump Binding
|
||||
addon.modbus.lambda.description = The Lambda Heat Pump binding supports the different parts of a Lambda Heat Pump heating.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.modbus.general.label = Lambda General Sections
|
||||
thing-type.modbus.general.description = Provides Channels of the General Sections
|
||||
thing-type.modbus.heat-pump.label = Lambda Heat Pump
|
||||
thing-type.modbus.heat-pump.description = Provides Channels of a Heat Pump
|
||||
thing-type.modbus.boiler.label = Lambda Boiler
|
||||
thing-type.modbus.boiler.description = Provides Channels of a Boiler
|
||||
thing-type.modbus.buffer.label = Lambda Buffer
|
||||
thing-type.modbus.buffer.description = Provides Channels of a Buffer
|
||||
thing-type.modbus.heating-circuit.label = Lambda Heating Circuit
|
||||
thing-type.modbus.heating-circuit.description = Provides Channels of a Heating Circuit
|
||||
thing-type.modbus.solar.label = Lambda Solar
|
||||
thing-type.modbus.solar.description = Provides Channels of Solar
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.general.modbusconfig.maxTries.label = Maximum Tries When Reading
|
||||
thing-type.config.general.modbusconfig.maxTries.description = Number of tries when reading data, if some of the reading fail. For single try, enter 1.
|
||||
thing-type.config.general.modbusconfig.refresh.label = Refresh Interval
|
||||
thing-type.config.general.modbusconfig.refresh.description = Refresh interval in seconds. Use zero to disable automatic polling.
|
||||
thing-type.config.general.baseadress.label = BaseAdress of general section
|
||||
|
||||
thing-type.config.boiler.modbusconfig.maxTries.label = Maximum Tries When Reading
|
||||
thing-type.config.boiler.modbusconfig.maxTries.description = Number of tries when reading data, if some of the reading fail. For single try, enter 1.
|
||||
thing-type.config.boiler.modbusconfig.refresh.label = Refresh Interval
|
||||
thing-type.config.boiler.modbusconfig.refresh.description = Refresh interval in seconds. Use zero to disable automatic polling.
|
||||
thing-type.config.boiler.baseadress.label = BaseAdress of boiler
|
||||
|
||||
thing-type.config.buffer.modbusconfig.maxTries.label = Maximum Tries When Reading
|
||||
thing-type.config.buffer.modbusconfig.maxTries.description = Number of tries when reading data, if some of the reading fail. For single try, enter 1.
|
||||
thing-type.config.buffer.modbusconfig.refresh.label = Refresh Interval
|
||||
thing-type.config.buffer.modbusconfig.refresh.description = Refresh interval in seconds. Use zero to disable automatic polling.
|
||||
thing-type.config.buffer.baseadress.label = BaseAdress of buffer
|
||||
|
||||
thing-type.config.heat-pump.modbusconfig.maxTries.label = Maximum Tries When Reading
|
||||
thing-type.config.heat-pump.modbusconfig.maxTries.description = Number of tries when reading data, if some of the reading fail. For single try, enter 1.
|
||||
thing-type.config.heat-pump.modbusconfig.refresh.label = Refresh Interval
|
||||
thing-type.config.heat-pump.modbusconfig.refresh.description = Refresh interval in seconds. Use zero to disable automatic polling.
|
||||
thing-type.config.heat-pump.baseadress.label = BaseAdress of Heatpump
|
||||
|
||||
thing-type.config.heating-circuit.modbusconfig.maxTries.label = Maximum Tries When Reading
|
||||
thing-type.config.heating-circuit.modbusconfig.maxTries.description = Number of tries when reading data, if some of the reading fail. For single try, enter 1.
|
||||
thing-type.config.heating-circuit.modbusconfig.refresh.label = Refresh Interval
|
||||
thing-type.config.heating-circuit.modbusconfig.refresh.description = Refresh interval in seconds. Use zero to disable automatic polling.
|
||||
thing-type.config.heating-circuit.baseadress.label = BaseAdress of Heating Circuit
|
||||
|
||||
thing-type.config.solar.modbusconfig.maxTries.label = Maximum Tries When Reading
|
||||
thing-type.config.solar.modbusconfig.maxTries.description = Number of tries when reading data, if some of the reading fail. For single try, enter 1.
|
||||
thing-type.config.solar.modbusconfig.refresh.label = Refresh Interval
|
||||
thing-type.config.solar.modbusconfig.refresh.description = Refresh interval in seconds. Use zero to disable automatic polling.
|
||||
thing-type.config.solar.baseadress.label = Base Adress of Heating Circuit
|
||||
|
||||
# channel group types
|
||||
|
||||
channel-group-type.modbus.ambient.label = General Ambient Group
|
||||
channel-group-type.modbus.emanager.label = General E-Manager Group
|
||||
channel-group-type.modbus.heat-pump.label = Heatpump Group
|
||||
channel-group-type.modbus.boiler.label = Boiler Group
|
||||
channel-group-type.modbus.boilerreg50.label = Boiler Reg50 Group
|
||||
channel-group-type.modbus.buffer.label = Buffer Group
|
||||
channel-group-type.modbus.bufferreg50.label = Buffer Reg50 Group
|
||||
channel-group-type.modbus.heating-circuit.label = Heating-Circuit Group
|
||||
channel-group-type.modbus.heating-circuitreg50.label = Heating-Circuit Reg50 Group
|
||||
channel-group-type.modbus.solar.label = Solar Group
|
||||
channel-group-type.modbus.solarreg50.label = Solar Reg50 Group
|
||||
|
||||
# channel types
|
||||
|
||||
# General Ambient
|
||||
channel-type.modbus.ambient-error-number-channel.label = Ambient Error Number
|
||||
channel-type.modbus.ambient-operating-state-channel.label = Ambient Operating State
|
||||
channel-type.modbus.actual-ambient-temperature-channel.label = Actual Ambient Temperature
|
||||
channel-type.modbus.average-ambient-temperature-channel.label = Average Ambient Temperature 1h
|
||||
channel-type.modbus.calculated-ambient-temperature-channel.label = Calculated Ambient Temperature
|
||||
|
||||
# General E-Manager
|
||||
channel-type.modbus.emanager-error-number-channel.label = E-Manager Error Number
|
||||
channel-type.modbus.emanager-operating-state-channel.label = E-Manager Operating State
|
||||
channel-type.modbus.emanager-actual-power-channel.label = Actual Power (input or excess)
|
||||
channel-type.modbus.emanager-actual-power-signed-channel.label = Actual Power Signed (input or excess)
|
||||
channel-type.modbus.emanager-actual-power-consumption-channel.label = Actual Power Consumption
|
||||
channel-type.modbus.emanager-power-consumption-setpoint-channel.label = Power Consumption setpoint as sum of all heat pumps
|
||||
|
||||
# Heat Pump
|
||||
|
||||
channel-type.modbus.heat-pump-error-state-channel.label = Heatpump Error State
|
||||
channel-type.modbus.heat-pump-error-number-channel.label = Heatpump Error Number
|
||||
channel-type.modbus.heat-pump-state-channel.label = Heatpump State
|
||||
channel-type.modbus.heat-pump-operating-state-channel.label = Heatpump Operating State
|
||||
channel-type.modbus.heat-pump-t-flow-channel.label = Heatpump T-Flow
|
||||
channel-type.modbus.heat-pump-t-return-channel.label = Heatpump T-Return
|
||||
channel-type.modbus.heat-pump-vol-sink-channel.label = Heatpump Volume flow heat sink
|
||||
channel-type.modbus.heat-pump-t-eqin-channel.label = Heatpump Energy source inlet temperature
|
||||
channel-type.modbus.heat-pump-t-eqout-channel.label = Heatpump Energy source outlet temperature
|
||||
channel-type.modbus.heat-pump-vol-source-channel.label = Heatpump Volume flow energy source
|
||||
channel-type.modbus.heat-pump-compressor-rating-channel.label = Heatpump Compressor unit rating
|
||||
channel-type.modbus.heat-pump-qp-heating-channel.label = Heatpump Actual heating capacity
|
||||
channel-type.modbus.heat-pump-fi-power-consumption-channel.label = Heatpump Frequency inverter actual power consumption
|
||||
channel-type.modbus.heat-pump-cop-channel.label = Heatpump COP
|
||||
channel-type.modbus.heat-pump-request-password-channel.label = Heatpump Request Password
|
||||
channel-type.modbus.heat-pump-request-type-channel.label = Heatpump Request Type
|
||||
channel-type.modbus.heat-pump-request-t-flow-channel.label = Heatpump requested T-Flow
|
||||
channel-type.modbus.heat-pump-request-t-return-channel.label = Heatpump requested T-Return
|
||||
channel-type.modbus.heat-pump-request-heat-sink-channel.label = Heatpump requested temperature difference between T-Flow and T-Return
|
||||
channel-type.modbus.heat-pump-relais-state-channel.label = Heatpump Relais State
|
||||
channel-type.modbus.heat-pump-vdae-channel.label = Heatpump VdA E
|
||||
channel-type.modbus.heat-pump-vdaq-channel.label = Heatpump VdA Q
|
||||
|
||||
# Boiler
|
||||
channel-type.modbus.boiler-error-number-channel.label = Boiler Error Number
|
||||
channel-type.modbus.boiler-operating-state-channel.label = Boiler Operating State
|
||||
channel-type.modbus.boiler-actual-high-temperature-channel.label = Boiler Actual High Temperature
|
||||
channel-type.modbus.boiler-actual-low-temperature-channel.label = Boiler Actual Low Temperature
|
||||
channel-type.modbus.maximum-boiler-temperature-channel.label = Maximum Boiler Temperature
|
||||
|
||||
# Buffer
|
||||
channel-type.modbus.buffer-error-number-channel.label = Buffer Error Number
|
||||
channel-type.modbus.buffer-operating-state-channel.label = Buffer Operating State
|
||||
channel-type.modbus.buffer-actual-high-temperature-channel.label = Buffer Actual High Temperature
|
||||
channel-type.modbus.buffer-actual-low-temperature-channel.label = Buffer Actual Low Temperature
|
||||
channel-type.modbus.buffer-actual-modbus-temperature-channel.label = Buffer Actual Modbus Temperature
|
||||
channel-type.modbus.buffer-request-type-channel.label = Buffer Request Type
|
||||
channel-type.modbus.buffer-request-flow-line-temperature-channel.label = Buffer Request Flow Line Temperature
|
||||
channel-type.modbus.buffer-request-return-line-temperature-channel.label = Buffer Request Return Line Temperature
|
||||
channel-type.modbus.buffer-request-heat-sink-temperature-difference-channel.label = Buffer Request Heat Sink Temperatur Difference
|
||||
channel-type.modbus.buffer-request-heating-capacity-channel.label = Buffer Request Heating Capacity
|
||||
channel-type.modbus.maximum-buffer-temperature-channel.label = Maximum Buffer Temperature
|
||||
|
||||
# Heating Circuit
|
||||
channel-type.modbus.heating-circuit-error-number-channel.label = Heating circuit Error Number
|
||||
channel-type.modbus.heating-circuit-operating-state-channel.label = Heating circuit Operating State
|
||||
channel-type.modbus.heating-circuit-flow-line-temperature-channel.label = Heating circuit Flow Line Temperature
|
||||
channel-type.modbus.heating-circuit-return-line-temperature-channel.label = Heating circuit Return Line Temperature
|
||||
channel-type.modbus.heating-circuit-room-device-temperature-channel.label = Heating circuit Room Device Temperature
|
||||
channel-type.modbus.heating-circuit-setpoint-flow-line-temperature-channel.label = Heating circuit Setpoint Flow Line Temperature
|
||||
channel-type.modbus.heating-circuit-operating-mode-channel.label = Heating circuit Operating Mode
|
||||
channel-type.modbus.heating-circuit-target-temperature-flow-line-channel.label = Heating circuit Target Temperature Flow Line
|
||||
channel-type.modbus.heating-circuit-offset-flow-line-temperature-channel.label = Heating circuit Offset Flow Line Temperature
|
||||
channel-type.modbus.heating-circuit-room-heating-temperature-channel.label = Heating circuit Room Heating Temperature
|
||||
channel-type.modbus.heating-circuit-room-cooling-temperature-channel.label = Heating circuit Room Cooling Temperature
|
||||
|
||||
# Solar
|
||||
|
||||
channel-type.modbus.solar-error-number-channel.label = Solar Error Number
|
||||
channel-type.modbus.solar-operating-state-channel.label = Operating State
|
||||
channel-type.modbus.solar-collector-temperature-channel.label = Solar Collector Temperature
|
||||
channel-type.modbus.solar-buffer1-temperature-channel.label = Solar Buffer 1 Temperature
|
||||
channel-type.modbus.solar-buffer2-temperature-channel.label = Solar Buffer 2 Temperature
|
||||
channel-type.modbus.solar-maximum-buffer-temperature-channel.label = Solar Maximum Buffer Temperature
|
||||
channel-type.modbus.solar-buffer-changeover-temperature-channel.label = Solar Buffer Changeover Temperature
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="modbus"
|
||||
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="boiler">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="tcp"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Boiler</label>
|
||||
<description>Lambda Boiler connected through modbus</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="boiler-group" typeId="boiler"/>
|
||||
<channel-group id="boiler-reg50-group" typeId="boiler-reg50"/>
|
||||
</channel-groups>
|
||||
|
||||
<config-description>
|
||||
<parameter name="refresh" type="integer" min="1" max="3600">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Poll interval in seconds. Increase this if you encounter connection errors.</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="maxTries" type="integer" min="1" max="10">
|
||||
<label>Maximum Tries</label>
|
||||
<description>Number of retries before giving up reading from this thing.</description>
|
||||
<default>3</default>
|
||||
</parameter>
|
||||
<parameter name="subindex" type="integer" min="0" max="4" required="true">
|
||||
<label>Subindex</label>
|
||||
<description>Subindex for multiple boiler components</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="boiler">
|
||||
<label>Boiler</label>
|
||||
<channels>
|
||||
<channel id="boiler-error-number" typeId="boiler-error-number-channel"/>
|
||||
<channel id="boiler-operating-state" typeId="boiler-operating-state-channel"/>
|
||||
<channel id="boiler-actual-high-temperature" typeId="boiler-actual-high-temperature-channel"/>
|
||||
<channel id="boiler-actual-low-temperature" typeId="boiler-actual-low-temperature-channel"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="boiler-reg50">
|
||||
<label>Boiler Reg50 Group</label>
|
||||
<channels>
|
||||
<channel id="maximum-boiler-temperature" typeId="maximum-boiler-temperature-channel"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="boiler-error-number-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Error Number</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">No Error</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="boiler-operating-state-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Operating State</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">STBY</option>
|
||||
<option value="1">DHW</option>
|
||||
<option value="2">LEGIO</option>
|
||||
<option value="3">SUMMER</option>
|
||||
<option value="4">FROST</option>
|
||||
<option value="5">HOLIDAY</option>
|
||||
<option value="6">PRIO-STOP</option>
|
||||
<option value="7">ERROR</option>
|
||||
<option value="8">OFF</option>
|
||||
<option value="9">PROMPT-DHW</option>
|
||||
<option value="10">TRAILING-STOP</option>
|
||||
<option value="11">TEMP-LOCK</option>
|
||||
<option value="12">STBY-FROST</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="boiler-actual-high-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Boiler Actual High Temperature</label>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="boiler-actual-low-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Boiler Actual Low Temperature</label>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="maximum-boiler-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Maximum Boiler Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="modbus"
|
||||
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="buffer">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="tcp"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Buffer</label>
|
||||
<description>Lambda Buffer connected through modbus</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="buffer-group" typeId="buffer"/>
|
||||
<channel-group id="buffer-reg50-group" typeId="buffer-reg50"/>
|
||||
</channel-groups>
|
||||
|
||||
<config-description>
|
||||
<parameter name="refresh" type="integer" min="1" max="3600">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Poll interval in seconds. Increase this if you encounter connection errors.</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="maxTries" type="integer" min="1" max="10">
|
||||
<label>Maximum Tries</label>
|
||||
<description>Number of retries before giving up reading from this thing.</description>
|
||||
<default>3</default>
|
||||
</parameter>
|
||||
<parameter name="subindex" type="integer" min="0" max="4" required="true">
|
||||
<label>Subindex</label>
|
||||
<description>Subindex for multiple buffer components</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="buffer">
|
||||
<label>Buffer</label>
|
||||
<channels>
|
||||
<channel id="buffer-error-number" typeId="buffer-error-number-channel"/>
|
||||
<channel id="buffer-operating-state" typeId="buffer-operating-state-channel"/>
|
||||
<channel id="buffer-actual-high-temperature" typeId="buffer-actual-high-temperature-channel"/>
|
||||
<channel id="buffer-actual-low-temperature" typeId="buffer-actual-low-temperature-channel"/>
|
||||
<channel id="buffer-actual-modbus-temperature" typeId="buffer-actual-modbus-temperature-channel"/>
|
||||
<channel id="buffer-request-type" typeId="buffer-request-type-channel"/>
|
||||
<channel id="buffer-request-flow-line-temperature" typeId="buffer-request-flow-line-temperature-channel"/>
|
||||
<channel id="buffer-request-return-line-temperature" typeId="buffer-request-return-line-temperature-channel"/>
|
||||
<channel id="buffer-request-heat-sink-temperature" typeId="buffer-request-heat-sink-temperature-channel"/>
|
||||
<channel id="buffer-request-heating-capacity" typeId="buffer-request-heating-capacity-channel"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="buffer-reg50">
|
||||
<label>Buffer Reg50 Group</label>
|
||||
<channels>
|
||||
<channel id="maximum-buffer-temperature" typeId="maximum-buffer-temperature-channel"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="buffer-error-number-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Error Number</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">No Error</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="buffer-operating-state-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Operating State</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">STBY</option>
|
||||
<option value="1">HEATING</option>
|
||||
<option value="2">COOLING</option>
|
||||
<option value="3">SUMMER</option>
|
||||
<option value="4">FROST</option>
|
||||
<option value="5">HOLIDAY</option>
|
||||
<option value="6">PRIO-STOP</option>
|
||||
<option value="7">ERROR</option>
|
||||
<option value="8">OFF</option>
|
||||
<option value="9">STBY-FROST</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="buffer-actual-high-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Actual High Temperature</label>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="buffer-actual-low-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Buffer Actual Low Temperature</label>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="buffer-actual-modbus-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Actual Modbus Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="buffer-request-type-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Request Type</label>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="-1">INVALID REQUEST</option>
|
||||
<option value="0">NO REQUEST</option>
|
||||
<option value="1">FLOW PUMP CIRCULATION</option>
|
||||
<option value="2">CENTRAL HEATING</option>
|
||||
<option value="3">CENTRAL COOLING</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="buffer-request-flow-line-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Request Flow Line Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="buffer-request-return-line-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Request Return Line Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="buffer-request-heat-sink-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Request Heat Sink Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="buffer-request-heating-capacity-channel">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Request Heating Capacity</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="maximum-buffer-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Max. Buffer Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="modbus"
|
||||
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="general">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="tcp"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Lambda General</label>
|
||||
<description> Ambient and E-Manager sections connected through modbus</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="ambient-group" typeId="ambient"/>
|
||||
<channel-group id="emanager-group" typeId="emanager"/>
|
||||
</channel-groups>
|
||||
|
||||
<config-description>
|
||||
<parameter name="refresh" type="integer" min="1" max="3600">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Poll interval in seconds. Increase this if you encounter connection errors.</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="maxTries" type="integer" min="1" max="10">
|
||||
<label>Maximum Tries</label>
|
||||
<description>Number of retries before giving up reading from this thing.</description>
|
||||
<default>3</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="ambient">
|
||||
<label>General Ambient</label>
|
||||
<channels>
|
||||
<channel id="ambient-error-number" typeId="ambient-error-number-channel"/>
|
||||
<channel id="ambient-operating-state" typeId="ambient-operating-state-channel"/>
|
||||
<channel id="actual-ambient-temperature" typeId="actual-ambient-temperature-channel"/>
|
||||
<channel id="average-ambient-temperature" typeId="average-ambient-temperature-channel"/>
|
||||
<channel id="calculated-ambient-temperature" typeId="calculated-ambient-temperature-channel"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="emanager">
|
||||
<label>General E-Manager</label>
|
||||
<channels>
|
||||
<channel id="emanager-error-number" typeId="emanager-error-number-channel"/>
|
||||
<channel id="emanager-operating-state" typeId="emanager-operating-state-channel"/>
|
||||
<channel id="emanager-actual-power" typeId="emanager-actual-power-channel"/>
|
||||
<channel id="emanager-actual-power-signed" typeId="emanager-actual-power-signed-channel"/>
|
||||
<channel id="emanager-actual-power-consumption" typeId="emanager-actual-power-consumption-channel"/>
|
||||
<channel id="power-consumption-setpoint" typeId="emanager-power-consumption-setpoint-channel"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
|
||||
<channel-type id="ambient-error-number-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Ambient Error Number</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">No Error</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ambient-operating-state-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Ambient Operating State</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">OFF</option>
|
||||
<option value="1">AUTOMATIC</option>
|
||||
<option value="2">MANUAL</option>
|
||||
<option value="3">ERROR</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="actual-ambient-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Actual Ambient Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="average-ambient-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Average Ambient Temperature 1h</label>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="calculated-ambient-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Calculated Ambient Temperature</label>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="emanager-error-number-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>E-Manager Error Number</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">No Error</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="emanager-operating-state-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>E-Manager Operating State</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">OFF</option>
|
||||
<option value="1">AUTOMATIC</option>
|
||||
<option value="2">MANUAL</option>
|
||||
<option value="3">ERROR</option>
|
||||
<option value="4">OFFLINE</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="emanager-actual-power-channel">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Actual Power</label>
|
||||
<state readOnly="false" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="emanager-actual-power-signed-channel">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Actual Power Signed (input or excess)</label>
|
||||
<state readOnly="false" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="emanager-actual-power-consumption-channel">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Actual Power Consumption</label>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="emanager-power-consumption-setpoint-channel">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Total Power Consumption</label>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="modbus"
|
||||
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="heat-pump">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="tcp"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Heat Pump</label>
|
||||
<description>Lambda Heat Pump connected through modbus</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="heat-pump-group" typeId="heat-pump"/>
|
||||
</channel-groups>
|
||||
|
||||
<config-description>
|
||||
<parameter name="refresh" type="integer" min="0" unit="s">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Refresh interval in seconds. Use zero to disable automatic polling.</description>
|
||||
<default>30</default>
|
||||
<unitLabel>s</unitLabel>
|
||||
</parameter>
|
||||
|
||||
<parameter name="maxTries" type="integer" min="1">
|
||||
<label>Maximum Tries</label>
|
||||
<default>3</default>
|
||||
<description>Number of tries when reading data, if some of the reading fail. For single try, enter 1.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="subindex" type="integer" required="true" min="0" max="2">
|
||||
<label>Subindex of Heatpump Thing (0..2)</label>
|
||||
<description>See Lambda Modbus description</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="heat-pump">
|
||||
<label>Heat Pump</label>
|
||||
|
||||
<channels>
|
||||
<channel id="heat-pump-error-state" typeId="heat-pump-error-state"/>
|
||||
<channel id="heat-pump-error-number" typeId="heat-pump-error-number"/>
|
||||
<channel id="heat-pump-state" typeId="heat-pump-state"/>
|
||||
<channel id="heat-pump-operating-state" typeId="heat-pump-operating-state"/>
|
||||
<channel id="heat-pump-t-flow" typeId="heat-pump-t-flow"/>
|
||||
<channel id="heat-pump-t-return" typeId="heat-pump-t-return"/>
|
||||
<channel id="heat-pump-vol-sink" typeId="heat-pump-vol-sink"/>
|
||||
<channel id="heat-pump-t-eqin" typeId="heat-pump-t-eqin"/>
|
||||
<channel id="heat-pump-t-eqout" typeId="heat-pump-t-eqout"/>
|
||||
<channel id="heat-pump-vol-source" typeId="heat-pump-vol-source"/>
|
||||
<channel id="heat-pump-compressor-rating" typeId="heat-pump-compressor-rating"/>
|
||||
<channel id="heat-pump-qp-heating" typeId="heat-pump-qp-heating"/>
|
||||
<channel id="heat-pump-fi-power-consumption" typeId="heat-pump-fi-power-consumption"/>
|
||||
<channel id="heat-pump-cop" typeId="heat-pump-cop"/>
|
||||
<channel id="heatpump-request-password" typeId="heatpump-request-password-channel"/>
|
||||
<channel id="heatpump-request-type" typeId="heatpump-request-type-channel"/>
|
||||
<channel id="heat-pump-request-t-flow" typeId="heat-pump-request-t-flow"/>
|
||||
<channel id="heat-pump-request-t-return" typeId="heat-pump-request-t-return"/>
|
||||
<channel id="heat-pump-request-heat-sink" typeId="heat-pump-request-heat-sink"/>
|
||||
<channel id="heatpump-relais-state" typeId="heatpump-relais-state-channel"/>
|
||||
<channel id="heat-pump-vdae" typeId="heat-pump-vdae"/>
|
||||
<channel id="heat-pump-vdaq" typeId="heat-pump-vdaq"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="heat-pump-error-state">
|
||||
<item-type>Number</item-type>
|
||||
<label>Heatpump Error State</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">NONE</option>
|
||||
<option value="1">MESSAGE</option>
|
||||
<option value="2">WARNING</option>
|
||||
<option value="3">ALARM</option>
|
||||
<option value="4">FAULT</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-error-number">
|
||||
<item-type>Number</item-type>
|
||||
<label>Heatpump Error Number</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">No Error</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-state">
|
||||
<item-type>Number</item-type>
|
||||
<label>Heatpump State</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">INIT</option>
|
||||
<option value="1">REFERENCE</option>
|
||||
<option value="2">RESTART-BLOCK</option>
|
||||
<option value="3">READY</option>
|
||||
<option value="4">START PUMPS</option>
|
||||
<option value="5">START COMPRESSOR</option>
|
||||
<option value="6">PRE-REGULATION</option>
|
||||
<option value="7">REGULATION</option>
|
||||
<option value="8">Not used</option>
|
||||
<option value="9">COOLING</option>
|
||||
<option value="10">DEFROSTING</option>
|
||||
<option value="20">STOPPING</option>
|
||||
<option value="30">FAULT-LOCK</option>
|
||||
<option value="31">ALARM-BLOCK</option>
|
||||
<option value="40">ERROR-RESET</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-operating-state">
|
||||
<item-type>Number</item-type>
|
||||
<label>Heatpump Operating State</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">STBY</option>
|
||||
<option value="1">CH</option>
|
||||
<option value="2">DHW</option>
|
||||
<option value="3">CC</option>
|
||||
<option value="4">CIRCULATE</option>
|
||||
<option value="5">DEFROST</option>
|
||||
<option value="6">OFF</option>
|
||||
<option value="7">FROST</option>
|
||||
<option value="8">STBY-FROST</option>
|
||||
<option value="9">Not used</option>
|
||||
<option value="10">SUMMER</option>
|
||||
<option value="11">HOLIDAY</option>
|
||||
<option value="12">ERROR</option>
|
||||
<option value="13">WARNING</option>
|
||||
<option value="14">INFO-MESSAGE</option>
|
||||
<option value="15">TIME-BLOCK</option>
|
||||
<option value="16">RELEASE-BLOCK</option>
|
||||
<option value="17">MINTEMP-BLOCK</option>
|
||||
<option value="18">FIRMWARE-DOWNLOAD</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-t-flow">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Heatpump T-Flow</label>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-t-return">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Heatpump T-Return</label>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-vol-sink">
|
||||
<item-type>Number:VolumetricFlowRate</item-type>
|
||||
<label>Heatpump Volume flow heat sink</label>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-t-eqin">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Heatpump Energy source inlet temperature</label>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-t-eqout">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Heatpump Energy source outlet temperature</label>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-vol-source">
|
||||
<item-type>Number:VolumetricFlowRate</item-type>
|
||||
<label>Heatpump Volume flow energy source</label>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-compressor-rating">
|
||||
<item-type>Number</item-type>
|
||||
<label>Heatpump Compressor unit rating</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-qp-heating">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Heatpump Actual heating capacity</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-fi-power-consumption">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Heatpump Frequency inverter actual power consumption</label>
|
||||
<state readOnly="true" pattern="%.1f kW"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-cop">
|
||||
<item-type>Number</item-type>
|
||||
<label>Heatpump COP</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heatpump-request-password-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Requested Password</label>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heatpump-request-type-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Requested Type</label>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="0">NO REQUEST</option>
|
||||
<option value="1">FLOW PUMP CIRCULATION</option>
|
||||
<option value="2">CENTRAL HEATING</option>
|
||||
<option value="3">CENTRAL COOLING</option>
|
||||
<option value="4">DOMESTIC HOT WATER</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-request-t-flow">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Heatpump requested T-Flow</label>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-request-t-return">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Heatpump requested T-Return</label>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-request-heat-sink">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Heatpump requested difference between T-Flow and T-Return</label>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heatpump-relais-state-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Relais State</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="1">NO Relais Error for 2nd heating stage activated</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-vdae">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Heatpump VdA E since last reset</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat-pump-vdaq">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Heatpump VdA Q since last reset</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="modbus"
|
||||
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="heating-circuit">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="tcp"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Heating Circuit</label>
|
||||
|
||||
<description>Lambda Heating Circuit connected through modbus</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="heating-circuit-group" typeId="heating-circuit"/>
|
||||
<channel-group id="heating-circuit-reg50-group" typeId="heating-circuit-reg50"/>
|
||||
</channel-groups>
|
||||
|
||||
<config-description>
|
||||
|
||||
<parameter name="refresh" type="integer" min="0" unit="s">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Refresh interval in seconds. Use zero to disable automatic polling.</description>
|
||||
<default>30</default>
|
||||
<unitLabel>s</unitLabel>
|
||||
</parameter>
|
||||
|
||||
<parameter name="maxTries" type="integer" min="1">
|
||||
<label>Maximum Tries</label>
|
||||
<default>3</default>
|
||||
<description>Number of tries when reading data, if some of the reading fail. For single try, enter 1.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="subindex" type="integer" required="true" min="0" max="11">
|
||||
<label>Subindex of Heating Circuit (0..11)</label>
|
||||
<description>See Lambda Modbus description</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="heating-circuit">
|
||||
<label>Heating-Circuit Group</label>
|
||||
<channels>
|
||||
<channel id="heating-circuit-error-number" typeId="heating-circuit-error-number-channel"/>
|
||||
<channel id="heating-circuit-operating-state" typeId="heating-circuit-operating-state-channel"/>
|
||||
<channel id="heating-circuit-flow-line-temperature" typeId="heating-circuit-flow-line-temperature-channel"/>
|
||||
<channel id="heating-circuit-return-line-temperature"
|
||||
typeId="heating-circuit-return-line-temperature-channel"/>
|
||||
<channel id="heating-circuit-room-device-temperature"
|
||||
typeId="heating-circuit-room-device-temperature-channel"/>
|
||||
<channel id="heating-circuit-setpoint-flow-line-temperature"
|
||||
typeId="heating-circuit-setpoint-flow-line-temperature-channel"/>
|
||||
<channel id="heating-circuit-operating-mode" typeId="heating-circuit-operating-mode-channel"/>
|
||||
<channel id="heating-circuit-target-temperature-flow-line"
|
||||
typeId="heating-circuit-target-temperature-flow-line-channel"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="heating-circuit-reg50">
|
||||
<label>Heating-Circuit Reg50 Group</label>
|
||||
<channels>
|
||||
<channel id="heating-circuit-offset-flow-line-temperature"
|
||||
typeId="heating-circuit-offset-flow-line-temperature-channel"/>
|
||||
<channel id="heating-circuit-room-heating-temperature"
|
||||
typeId="heating-circuit-room-heating-temperature-channel"/>
|
||||
<channel id="heating-circuit-room-cooling-temperature"
|
||||
typeId="heating-circuit-room-cooling-temperature-channel"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="heating-circuit-error-number-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Error Number</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">No Error</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heating-circuit-operating-state-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Operating State</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">HEATING</option>
|
||||
<option value="1">ECO</option>
|
||||
<option value="2">COOLING</option>
|
||||
<option value="3">FLOORDRY</option>
|
||||
<option value="4">FROST</option>
|
||||
<option value="5">MAX-TEMP</option>
|
||||
<option value="6">ERROR</option>
|
||||
<option value="7">SERVICE</option>
|
||||
<option value="8">HOLIDAY</option>
|
||||
<option value="9">CH-SUMMER</option>
|
||||
<option value="10">CC-WINTER</option>
|
||||
<option value="11">PRIO-STOP</option>
|
||||
<option value="12">OFF</option>
|
||||
<option value="13">RELEASE-OFF</option>
|
||||
<option value="14">TIME-OFF</option>
|
||||
<option value="15">STBY</option>
|
||||
<option value="16">STBY-HEATING</option>
|
||||
<option value="17">STBY-ECHO</option>
|
||||
<option value="18">STBY-COOLING</option>
|
||||
<option value="19">STBY-FROST</option>
|
||||
<option value="20">STBY-FLOORDRY</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heating-circuit-flow-line-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Flow Line Temperature</label>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heating-circuit-return-line-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Return Line Temperature</label>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heating-circuit-room-device-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Room Device Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heating-circuit-setpoint-flow-line-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Setpoint Flow Line Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heating-circuit-operating-mode-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Operating Mode</label>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="0">OFF(RW)</option>
|
||||
<option value="1">MANUAL(R)</option>
|
||||
<option value="2">AUTOMATIK(RW)</option>
|
||||
<option value="3">AUTO-HEATING(RW)</option>
|
||||
<option value="4">AUTO-COOLING(RW)</option>
|
||||
<option value="5">FROST(RW)</option>
|
||||
<option value="6">SUMMER(RW)</option>
|
||||
<option value="7">FLOOR-DRY(R)</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heating-circuit-offset-flow-line-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Set Offset Flow Line Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heating-circuit-room-heating-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Set Room Heating Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heating-circuit-room-cooling-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Set Room Cooling Temperature</label>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="modbus"
|
||||
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="solar">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="tcp"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Solar</label>
|
||||
<description>Solar thermic component data</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="solar-group" typeId="solar"/>
|
||||
<channel-group id="solar-reg50-group" typeId="solar-reg50"/>
|
||||
</channel-groups>
|
||||
|
||||
<config-description>
|
||||
<parameter name="refresh" type="integer" min="1" max="3600">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Poll interval in seconds. Increase this if you encounter connection errors.</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="maxTries" type="integer" min="1" max="10">
|
||||
<label>Maximum Tries</label>
|
||||
<description>Number of retries before giving up reading from this thing.</description>
|
||||
<default>3</default>
|
||||
</parameter>
|
||||
<parameter name="subindex" type="integer" min="0" max="1" required="true">
|
||||
<label>Subindex</label>
|
||||
<description>Subindex for multiple solar components</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="solar">
|
||||
<label>Solar</label>
|
||||
<channels>
|
||||
<channel id="solar-error-number" typeId="solar-error-number-channel"/>
|
||||
<channel id="solar-operating-state" typeId="solar-operating-state-channel"/>
|
||||
<channel id="solar-collector-temperature" typeId="solar-collector-temperature-channel"/>
|
||||
<channel id="solar-buffer1-temperature" typeId="solar-buffer1-temperature-channel"/>
|
||||
<channel id="solar-buffer2-temperature" typeId="solar-buffer2-temperature-channel"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="solar-reg50">
|
||||
<label>Solar Reg50 Group</label>
|
||||
<channels>
|
||||
<channel id="solar-maximum-buffer-temperature" typeId="solar-maximum-buffer-temperature-channel"/>
|
||||
<channel id="solar-buffer-changeover-temperature" typeId="solar-buffer-changeover-temperature-channel"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="solar-error-number-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Solar Error Number</label>
|
||||
<description>Error number (0 = No error)</description>
|
||||
<state readOnly="true" pattern="%d">
|
||||
<options>
|
||||
<option value="0">NO ERROR</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="solar-operating-state-channel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Solar Operating State</label>
|
||||
<description>Operating state of the solar component</description>
|
||||
<state readOnly="true" pattern="%d">
|
||||
<options>
|
||||
<option value="0">STBY</option>
|
||||
<option value="1">HEATING</option>
|
||||
<option value="2">ERROR</option>
|
||||
<option value="3">OFF</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="solar-collector-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Solar Collector Temperature</label>
|
||||
<description>Temperature of the solar collector</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="solar-buffer1-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Solar Buffer1 Temperature</label>
|
||||
<description>Temperature of the solar Buffer 1</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="solar-buffer2-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Solar Buffer2 Temperature</label>
|
||||
<description>Temperature of the solar Buffer 2</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="solar-maximum-buffer-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Solar Maximum Buffer Temperature</label>
|
||||
<description>Maximum Buffer Temperature</description>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="solar-buffer-changeover-temperature-channel">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Solar Buffer Changeover Temperature</label>
|
||||
<description>Solar Buffer Changeover Temperature</description>
|
||||
<state readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -279,6 +279,7 @@
|
||||
<module>org.openhab.binding.modbus.e3dc</module>
|
||||
<module>org.openhab.binding.modbus.foxinverter</module>
|
||||
<module>org.openhab.binding.modbus.kermi</module>
|
||||
<module>org.openhab.binding.modbus.lambda</module>
|
||||
<module>org.openhab.binding.modbus.sbc</module>
|
||||
<module>org.openhab.binding.modbus.studer</module>
|
||||
<module>org.openhab.binding.modbus.sungrow</module>
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.e3dc/${project.version}</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.helioseasycontrols/${project.version}</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.kermi/${project.version}</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.lambda/${project.version}</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.sbc/${project.version}</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.stiebeleltron/${project.version}</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.studer/${project.version}</bundle>
|
||||
|
||||
Reference in New Issue
Block a user