Using native Modbus component for Helios KWL

Hi,

back in the days I hacked my own command_line script to retrieve data from my Helios KWL device.

This is the script:

#!/usr/bin/env python3.6

import logging
import argparse
import sys
import re

from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.payload import BinaryPayloadBuilder

SLAVE_ID = 180
FIRST_REGISTER_ADDR = 0x01

# read or write data from or to modbus via TCP, Helios KWL specific


def main(ip, variable, rtr):
    try:
        logging.basicConfig()
        log = logging.getLogger()
        log.setLevel(logging.ERROR)

        # stupid workaround to use the correct number of registers
        rtr = rtr + 3
        if rtr < 8:
            rtr = 8

        client = ModbusClient(ip, 502)
        client.connect()

        builder = BinaryPayloadBuilder(byteorder=Endian.Big,
                                       wordorder=Endian.Little)
        builder.add_string(variable + '\0')

        payload = builder.build()
        client.write_registers(FIRST_REGISTER_ADDR, payload,
                               skip_encode=True, unit=SLAVE_ID)

        result = client.read_holding_registers(
            FIRST_REGISTER_ADDR, rtr, unit=SLAVE_ID)

        output = BinaryPayloadDecoder.fromRegisters(result.registers,
                                                    byteorder=Endian.Little,
                                                    wordorder=Endian.Little).decode_string(rtr)

        # get rid of null bytes
        # http://chase-seibert.github.io/blog/2011/05/20/stripping-control-characters-in-python.html
        output = re.sub(u'([\u0000])', "", output.decode(
            sys.getdefaultencoding()))
    except Exception as e:
        print(e)
    else:
        print(output)


if __name__ == "__main__":
    #pid_file = '/tmp/helios.pid'
    #fp = open(pid_file, 'w')
    # try:
    #    fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
    # except IOError:
    #    # another instance is running
    #    sys.exit(1)
    # fp.write(str(os.getpid()))

    parser = argparse.ArgumentParser(description='Helios KWL Modbus interface')
    parser.add_argument('ip', help='IP address of Modbus slave')
    parser.add_argument(
        'registers', help='Number of registers to read. Can be obtained from the KWL documentation.')
    parser.add_argument(
        'variable', help='Variable to read. If value should be set, append =value, e.g. v00102=1 to set fan to level 1.')

    args = parser.parse_args()
    main(args.ip, args.variable, int(args.registers))
    # fp.close()
    # os.unlink(pid_file)

Not beautiful but it works :slight_smile:
I use it via command_line integration as follows:

# documentation modbus: https://www.heliosventilatoren.de/mbv/kwl-modbus_gateway_82269_0714_d_e_f.pdf
# error codes: http://www.heliosventilatoren.de/1alt/mbv/kwl_easycontrols_82200_0116_v_2.20_d_e_f.pdf
- platform: command_line
  name: Mode
  value_template: "{% if value.split ('=')[1] == '1' %}manual{% elif value.split ('=')[1] == '0' %}auto{% endif %}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 5 v00101'
- platform: command_line
  name: Fan level
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 5 v00102'
- platform: command_line
  name: Outdoor air temp
  value_template: "{{value.split ('=')[1]}}"
  unit_of_measurement: '°C'
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 8 v00104'
- platform: command_line
  name: Supply air temp
  value_template: "{{value.split ('=')[1]}}"
  unit_of_measurement: '°C'
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 8 v00105'
- platform: command_line
  name: Outgoing air temp
  value_template: "{{value.split ('=')[1]}}"
  unit_of_measurement: '°C'
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 8 v00106'
- platform: command_line
  name: Extract air temp
  value_template: "{{value.split ('=')[1]}}"
  unit_of_measurement: '°C'
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 8 v00107'
- platform: command_line
  name: Supply air fan speed
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 8 v00348'
  unit_of_measurement: 'rpm'
- platform: command_line
  name: Extract air fan speed
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 8 v00349'
  unit_of_measurement: 'rpm'
- platform: command_line
  name: Errors
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 5 v01300'
- platform: command_line
  name: Warnings
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 5 v01301'
- platform: command_line
  name: Infos
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 5 v01302'
- platform: command_line
  name: Error ID
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 9 v01123'
- platform: command_line
  name: Warning ID
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 6 v01124'
- platform: command_line
  name: Info ID
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 6 v01125'
- platform: command_line
  name: Filter change remaining time
  value_template: "{{float(value.split ('=')[1]) / 60 / 24 | round(2)}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 10 v01033'
- platform: command_line
  name: Party mode status
  value_template: "{% if value.split ('=')[1] == '1' %}on{% elif value.split ('=')[1] == '0' %}off{% endif %}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 5 v00094'
- platform: command_line
  name: Party mode duration
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 6 v00091'
- platform: command_line
  name: Party mode level
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 5 v00092'
- platform: command_line
  name: Party mode remaining time
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 7 v00093'
  unit_of_measurement: 'minutes'
- platform: command_line
  name: Bypass
  value_template: "{{value.split ('=')[1]}}"
  command: '/home/hass/homeassistant/helios/helios.py helios.fritz.box 6 v02119'

I now wanted to transform this to the native Modbus component but I don’t get it to work.

Current config from configuration.yaml:

modbus:
  type: tcp
  host: helios.fritz.box
  port: 502

The according yaml in the sensors directory - I tried different values but I always receive 2019-03-24 21:31:56 ERROR (SyncWorker_13) [homeassistant.components.modbus.sensor] No response from hub default, slave 180, register 1

- platform: modbus
  registers:
    - name: modbus test
      slave: 180
      register: 1
      register_type: holding
      count: 1

Any clues, hints? Thank you!

First…what version are you using?
This explaination applies from 0.88.0 onwards?

From the modbus sensor config example here

# Example configuration.yaml entry
sensor:
  platform: modbus
  registers:
    - name: Sensor1
      hub: hub1
      unit_of_measurement: °C
      slave: 1
      register: 100

“name” refers to YourSensorName

while you need to have an entry for the hub: YourModbusHub

Now for your modbus hub…That’s where it all gets a little confusing in the current modbus implementation I feel
From the Modbus config example for a single hub here

# Example configuration.yaml entry for a TCP connection
modbus:
  name: hub1
  type: tcp
  host: IP_ADDRESS
  port: 2020

So in this case “name” = YourModbusHub

Not sure why but that’s how you need to do it?

I gets more confusing.
In the config for multiple modbus hubs the below is shown.

# Example configuration.yaml entry for multiple TCP connections
modbus:
  - type: tcp
    host: IP_ADDRESS_1
    port: 2020
    hub: hub1

  - type: tcp
    host: IP_ADDRESS_2
    port: 501
    hub: hub2

I can confirm that will not work.

For multiple hubs you need to use:

modbus:
  - type: tcp
    host: IP_ADDRESS_1
    port: 2020
    name: hub1

  - type: tcp
    host: IP_ADDRESS_2
    port: 501
    name: hub2

That works for everything but modbus binary sersors atm.

Yeah so far I got it. The question is now how to configure the component in order to get the funky variables from the Helios KWL.

Thx :slight_smile:

Here is what I think you need to change in your config.

Hope that helps/works for you!
NOTE: The indentation has screwed up the way I have done this sorry.

This ERROR message is the one telling you it has a problem with the new “hub” setup.
“ERROR (SyncWorker_13) [homeassistant.components.modbus.sensor] No response from hub default”

By defining a “hub” name IE: name: YourHubName
And then defining a sensor which is part of that “hub” IE: hub: YourHubName

That should then work…I hope?

There is only one other thing that concerns me and that is “host: helios.fritz.box”
Is there any reason you cannot use an IP address instead of helios.fritz.box?

Does not work. Perhaps the Modbus component is incapable of doing it?
Funny aside: Just found my own RFE from 2017 https://github.com/home-assistant/home-assistant/issues/5185

Perhaps…but I have been running multiple hubs on 0.81.6 using the custom component workaround for quite a while and it just works.

The new implementation seems to have some problems which hopefully get sorted soon.

Can you continue to use you existing setup for a while?
Perhaps post an issue for your problem.

Hello,
Have you succecced to run the Modbus component for Helios KWL?
Personally, I go around in circles and can not :frowning:
thank you in advance
Mark

I want to integrate my Helios KWL with Home Assistant, too. Has there been any progress?

I’m working on a custom integration for Helios Easy Controls based units. The following sensors already working:

I’ve also started a climate device for it. After finishing it I will publish the integration on HACS.

1 Like

I’ve published custom integration to github. HACS support added so you can add a custom repository and install from Home Assistan ui.

Finally only a fan device was created and all other data added to attributes.

2 Likes

Cool!
How can I access the attributes in the UI?

You can define template sensor to use them. Example:

sensor:
  - platform: template
    sensors:
      helios_extract_air_temperature:
        unit_of_measurement: '°C'
        friendly_name: Extract air temperature
        icon_template: 'mdi:thermometer'
        value_template: "{{ state_attr('fan.helios', 'extract_air_temperature') }}"
1 Like

Works very well! Thank you!

@laszlojakab Thanks for the component first of all! I managed to get the fan you already included in a wenhook, but that’s about it.
Being a newbie in HA, where should I look to create an api target to pass a value of 1 to 4 for the fan speed? And similarly, any hint is appreciated in extracting the intake air temperature, again with an external get request.
thanks

keeps telling me that it is not a valid addon repo?

@laszlojakab Do you have an idea, how can I integrate Helios KWL 360 W (easyControls3) into HA?
@asev is it gonna work with your custom component?

Sorry but it won’t work with EasyControls v3 only with v2. See related issue here for details. @asev’s integration uses eazyctrl under the hood as mine so probably the homeassistant-helios integaration will also not support EasyControls v3.