A while ago I built a Modbus RTU to Modbus TCP Gateway based on a Beaglebone Green and a RS485 Cape (check my blog post from back then). It worked, but stoped working from time to time and I decided to fix that issue a few weeks ago. I updated the OS on the Beaglebone and realized that so many things changed since my initial implementation that I didn’t want to invest to much time.
So I decided to use a USB to RS485 converter and plug that directly into my server and control the ventilation unit directly from Home-Assistant.
Modbus configuration 🔗
configuration.yaml
1modbus:
2 name: ventilation_unit
3 type: serial
4 method: rtu
5 port: /dev/ttyUSB0
6 baudrate: 19200
7 stopbits: 1
8 bytesize: 8
9 parity: E
Nothing special here, the port must match the USB converter and the user that runs HA must have the rights to access the port (I had to add it to the uucp group).
ventilation.yaml
1- platform: modbus
2 registers:
3 - name: einlass_ventilator
4 hub: ventilation_unit
5 unit_of_measurement: '%'
6 slave: 1
7 register: 102
8 register_type: input
9 - name: Auslass Ventilator
10 hub: ventilation_unit
11 unit_of_measurement: '%'
12 slave: 1
13 register: 103
14 register_type: input
15 - name: Alarm
16 hub: ventilation_unit
17 slave: 1
18 register: 101
19 register_type: input
20 - name: CO2
21 hub: ventilation_unit
22 unit_of_measurement: 'PPM'
23 slave: 1
24 register: 10
25 register_type: input
26 - name: Temperatur Zuluft
27 hub: ventilation_unit
28 unit_of_measurement: '°C'
29 slave: 1
30 register: 0
31 register_type: input
32 scale: 0.1
33 offset: -30.0
34 precision: 1
35 - name: Temperatur Frischluft
36 hub: ventilation_unit
37 unit_of_measurement: '°C'
38 slave: 1
39 register: 2
40 register_type: input
41 scale: 0.1
42 offset: -30.0
43 precision: 1
44 - name: Temperatur Fortluft
45 hub: ventilation_unit
46 unit_of_measurement: '°C'
47 slave: 1
48 register: 3
49 register_type: input
50 scale: 0.1
51 offset: -30.0
52 precision: 1
53 - name: Temperatur Abluft
54 hub: ventilation_unit
55 unit_of_measurement: '°C'
56 slave: 1
57 register: 6
58 register_type: input
59 scale: 0.1
60 offset: -30.0
61 precision: 1
I set up the registers that I want to read as documented in the [Modbus PDF from Alpha Innotec]({static}/blog/2019/lg327TB-hass-knx/Modbus OPT250 - 20130813.pdf) (they sent it to me years ago when I asked them).
The only think that’s important is that the hub matches the name defined in configuration.yaml
.
The only kind of special thing here is the alram. It gives me a binary representation of all alarms. I used template binary sensors to decode them.
1- platform: template
2 sensors:
3 alarm_external_stop:
4 friendly_name: "Alarm Externer Stop"
5 value_template: >-
6 {{ states('sensor.alarm')|int|bitwise_and(1) }}
7 alarm_main_filter:
8 friendly_name: "Hauptfilter"
9 value_template: >-
10 {{ states('sensor.alarm')|int|bitwise_and(2) }}
11 alarm_high_preasure:
12 friendly_name: "Hochdruck"
13 value_template: >-
14 {{ states('sensor.alarm')|int|bitwise_and(4) }}
15 alarm_frost:
16 friendly_name: "Frost"
17 value_template: >-
18 {{ states('sensor.alarm')|int|bitwise_and(8) }}
19 alarm_communication_error:
20 friendly_name: "Kommunikationsfehler"
21 value_template: >-
22 {{ states('sensor.alarm')|int|bitwise_and(16) }}
23 alarm_external_filter:
24 friendly_name: "Externer Filter"
25 value_template: >-
26 {{ states('sensor.alarm')|int|bitwise_and(32) }}
27 alarm_fan_speed:
28 friendly_name: "Lüfter Geschwindigkeit"
29 value_template: >-
30 {{ states('sensor.alarm')|int|bitwise_and(64) }}
That enabled me to get the sensor data into HA:
Snippet from the Lovelace UI config
1- entities:
2 - entity: sensor.einlass_ventilator
3 icon: 'mdi:fan'
4 name: Einlass Ventilator
5 - entity: sensor.auslass_ventilator
6 icon: 'mdi:fan'
7 name: Auslass Ventilator
8 - entity: sensor.co2
9 icon: 'mdi:grain'
10 name: Kohlendioxyd
11 - entity: sensor.temperatur_frischluft
12 icon: 'mdi:thermometer'
13 name: Temperatur Frischluft
14 - entity: sensor.temperatur_zuluft
15 icon: 'mdi:thermometer'
16 name: Temperatur Zuluft
17 - entity: sensor.temperatur_abluft
18 icon: 'mdi:thermometer'
19 name: Temperatur Abluft
20 - entity: sensor.temperatur_fortluft
21 icon: 'mdi:thermometer'
22 name: Temperatur Fortluft
23 - entity: binary_sensor.alarm_external_stop
24 icon: 'mdi:alert-decagram-outline'
25 name: Alarm externer Stop
26 - entity: binary_sensor.alarm_main_filter
27 icon: 'mdi:alert-decagram-outline'
28 name: Alarm Hauptfilter
29 - entity: binary_sensor.alarm_high_preasure
30 icon: 'mdi:alert-decagram-outline'
31 name: Alarm Hochdruck
32 - entity: binary_sensor.alarm_frost
33 icon: 'mdi:alert-decagram-outline'
34 name: Alarm Frost
35 - entity: binary_sensor.alarm_communication_error
36 icon: 'mdi:alert-decagram-outline'
37 name: Alarm Kommunikation
38 - entity: binary_sensor.alarm_external_filter
39 icon: 'mdi:alert-decagram-outline'
40 name: Alarm Externer Filter
41 - entity: binary_sensor.alarm_fan_speed
42 icon: 'mdi:alert-decagram-outline'
43 name: Alarm Lüftergeschwindigkeit
44 show_header_toggle: false
45 title: Lüftungsanlage
46 type: entities
Writing ventilation levels 🔗
So I was able to read but not to write. I asked in the discord channel how to achieve that and was pointed towards the entity-button
card.
Unfortunately that card requires a entity_id
, so I decided to create kind of a dummy entity.
confguration.yaml
1input_boolean:
2 stufe:
3 name: "Lüftung Stufe"
With that I created a horizontal-stack
of entity-button
cards
Snippet from the Lovelace UI config
1- cards:
2 - entity: input_boolean.stufe
3 hold_action:
4 action: none
5 icon: 'mdi:fan-off'
6 name: Aus
7 show_icon: true
8 show_name: true
9 tap_action:
10 action: call-service
11 service: modbus.write_register
12 service_data:
13 address: 100
14 hub: ventilation_unit
15 unit: 1
16 value: 0
17 type: entity-button
18 - entity: input_boolean.stufe
19 hold_action:
20 action: none
21 icon: 'mdi:numeric-1-box'
22 name: Stufe 1
23 show_icon: true
24 show_name: true
25 tap_action:
26 action: call-service
27 service: modbus.write_register
28 service_data:
29 address: 100
30 hub: ventilation_unit
31 unit: 1
32 value: 1
33 type: entity-button
34 - entity: input_boolean.stufe
35 hold_action:
36 action: none
37 icon: 'mdi:numeric-2-box'
38 name: Stufe 2
39 show_icon: true
40 show_name: true
41 tap_action:
42 action: call-service
43 service: modbus.write_register
44 service_data:
45 address: 100
46 hub: ventilation_unit
47 unit: 1
48 value: 2
49 type: entity-button
50 - entity: input_boolean.stufe
51 hold_action:
52 action: none
53 icon: 'mdi:numeric-3-box'
54 name: Stufe 3
55 show_icon: true
56 show_name: true
57 tap_action:
58 action: call-service
59 service: modbus.write_register
60 service_data:
61 address: 100
62 hub: ventilation_unit
63 unit: 1
64 value: 3
65 type: entity-button
66 - entity: input_boolean.stufe
67 hold_action:
68 action: none
69 icon: 'mdi:numeric-4-box'
70 name: Stufe 4
71 show_icon: true
72 show_name: true
73 tap_action:
74 action: call-service
75 service: modbus.write_register
76 service_data:
77 address: 100
78 hub: ventilation_unit
79 unit: 1
80 value: 4
81 type: entity-button
82 type: horizontal-stack
That results in 5 buttons that when clicked call a service provided by the Modbus integration that writes a certain value into a Modbus register and therby sets the speed of the ventilators.
KNX integration 🔗
With my old solution I had two buttons on a KNX Glas button that allowd me to control the level without using the phone. The control was actually done by my Loxone Miniserver that has a KNX interface, but I wanted to reduce the amount of things that this device does because I need a Windows Software to make changes to it and that sucks :-)
Also I want HA to be able to react on KNX telegrams, so here is how I solved this last task.
First I need to configure that the KNX integration fires an event whenever a KNX telegram is received. For the moment I decided to not set any filters.
configuration.yaml
1knx:
2 fire_event: true
3 fire_event_filter: ["*"]
Then I created an automation that reacts on a certain KNX Group Address with a data value of 1
automation.yaml
1- alias: 'Set ventilation level to 4 via KNX Switch'
2 trigger:
3 platform: event
4 event_type: knx_event
5 event_data:
6 address: "6/1/162"
7 data: 1
8 action:
9 - service: modbus.write_register
10 data:
11 address: 100
12 hub: ventilation_unit
13 unit: 1
14 value: 4
15
16- alias: 'Set ventilation level to 2 via KNX Switch'
17 trigger:
18 platform: event
19 event_type: knx_event
20 event_data:
21 address: "6/1/161"
22 data: 1
23 action:
24 - service: modbus.write_register
25 data:
26 address: 100
27 hub: ventilation_unit
28 unit: 1
29 value: 2
The trigger
ist set to knx_event
and the event_data
is like a filter to only react on a given Group address and data. The action
calls the modbus.write_register
service and sends the right values to the Modbus registers of the ventilation unit.