Hello comunity,
I was looking for any option how to connect my air ventilation unit with Home Assistant. I couldnt find nothing about connection so I decided create my own. Belive it can help somenone. I have SPCM modul for communication but it uses connectair app and own cloud.
What we need:
- S&P Sabik 350 ventilation unit (maybe it works on 210 or 500)
- ESP32-WROOM-32D with WiFi and BT
- Communication unit MAX485 RS485 to TTL
- some cables
- working ESP home
- working Home Assistant
Setup
- first connect ESP32 with MAX485 and Sabik
ESP32 pinout
MAX485 pinout
| ESP32 | MAX485 | SABIK 350 modbus RTU |
|---|---|---|
| 5V | VCC | |
| GND | GND | |
| GPIO16 | DI | |
| GPIO17 | RO | |
| GPIO4 | DE + RE | |
| A | connector 32 | |
| B | connector 32 |
- install yaml into ESP
Code
esphome:
name: "your esp name here"
friendly_name: "your friendly name here"
esp32:
board: esp32dev
framework:
type: arduino
logger:
level: DEBUG
wifi:
ssid: "your SSID here"
password: "your password here"
manual_ip:
static_ip: esp ip here
gateway: gw ip here
subnet: subnet here
ota:
platform: esphome
password: "your password here"
api:
encryption:
key:"your key here"
uart:
id: modbus_uart
tx_pin: GPIO16
rx_pin: GPIO17
baud_rate: 19200
parity: EVEN
stop_bits: 1
modbus:
- id: hub1
uart_id: modbus_uart
flow_control_pin: GPIO4
modbus_controller:
- id: sabik
address: 1
modbus_id: hub1
command_throttle: 200ms
setup_priority: -10
sensor:
- platform: modbus_controller
name: "Communication error"
id: communication_error_raw
address: 4
register_type: read
value_type: U_WORD
- platform: modbus_controller
name: "Defrost status"
id: defrost_status_raw
address: 5
register_type: read
value_type: U_WORD
- platform: modbus_controller
modbus_controller_id: sabik
name: "Extract Air Temperature"
address: 25
unit_of_measurement: "°C"
register_type: read
value_type: S_WORD
accuracy_decimals: 1
filters:
- multiply: 0.1
- platform: modbus_controller
name: "Exhaust Air Temperature"
address: 26
unit_of_measurement: "°C"
register_type: read
value_type: S_WORD
accuracy_decimals: 1
filters:
- multiply: 0.1
- platform: modbus_controller
name: "Outdoor Air Temperature"
address: 27
unit_of_measurement: "°C"
register_type: read
value_type: S_WORD
accuracy_decimals: 1
filters:
- multiply: 0.1
- platform: modbus_controller
name: "Supply Air Temperature"
address: 28
unit_of_measurement: "°C"
register_type: read
value_type: S_WORD
accuracy_decimals: 1
filters:
- multiply: 0.1
- platform: modbus_controller
name: "Relative Humidity Extract Air"
address: 29
unit_of_measurement: "%"
register_type: read
value_type: U_WORD
- platform: modbus_controller
name: "Relative Humidity Exhaust Air"
address: 30
unit_of_measurement: "%"
register_type: read
value_type: U_WORD
- platform: modbus_controller
name: "Relative Humidity Outdoor Air"
address: 31
unit_of_measurement: "%"
register_type: read
value_type: U_WORD
- platform: modbus_controller
name: "Relative Humidity Supply Air"
address: 32
unit_of_measurement: "%"
register_type: read
value_type: U_WORD
- platform: modbus_controller
name: "RPM Extract Motor"
address: 61
unit_of_measurement: "rpm"
register_type: read
value_type: U_WORD
- platform: modbus_controller
name: "RPM Supply Motor"
address: 62
unit_of_measurement: "rpm"
register_type: read
value_type: U_WORD
- platform: modbus_controller
name: "Bypass Damper Position Raw"
id: bypass_damper_raw
address: 63
register_type: read
value_type: U_WORD
- platform: modbus_controller
name: "Actual Working Mode Raw"
id: actual_working_mode_raw
address: 90
register_type: read
value_type: U_WORD
- platform: modbus_controller
name: "Selected Airflow Raw"
id: selected_airflow_raw
address: 132
register_type: holding
value_type: U_WORD
text_sensor:
- platform: template
name: "Communication Error"
lambda: |-
switch (int(id(communication_error_raw).state)) {
case 0: return {"No error"};
case 1: return {"Remote controller error"};
case 2: return {"Modbus RTU error"};
default: return {"Unknown"};
}
update_interval: 10s
- platform: template
name: "Defrost Status"
lambda: |-
switch (int(id(defrost_status_raw).state)) {
case 0: return {"Not active"};
case 1: return {"Active (fireplace defrost)"};
case 2: return {"Active (with pre-heater)"};
case 3: return {"Active (unbalanced airflows)"};
default: return {"Unknown"};
}
update_interval: 10s
- platform: template
name: "Bypass Damper Position"
lambda: |-
switch (int(id(bypass_damper_raw).state)) {
case 0: return {"Closed"};
case 1: return {"Open"};
case 2: return {"Error"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Actual Working Mode"
lambda: |-
switch (int(id(actual_working_mode_raw).state)) {
case 0: return {"Snooze"};
case 1: return {"Low speed"};
case 2: return {"Medium speed"};
case 3: return {"High speed"};
case 4: return {"Boost"};
case 5: return {"Auto (humidity)"};
case 6: return {"Auto (VOC)"};
case 7: return {"Auto (0-10V)"};
case 8: return {"Boost in auto"};
case 9: return {"Weekly 1"};
case 10: return {"Weekly 2"};
case 11: return {"Weekly 3"};
case 12: return {"Weekly 4"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Selected Airflow"
lambda: |-
switch (int(id(selected_airflow_raw).state)) {
case 0: return {"Manual (low)"};
case 1: return {"Manual (medium)"};
case 2: return {"Manual (nominal)"};
case 3: return {"Auto"};
case 4: return {"Snooze"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Active Alarm Status"
lambda: |-
if (id(active_alarms).state) {
return {"Alarm Active"};
} else {
return {"No Alarm"};
}
update_interval: 5s
- platform: template
name: "Filter Alarm Status"
lambda: |-
if (id(filter_alarm).state) {
return {"Alarm On"};
} else {
return {"Alarm Off"};
}
update_interval: 5s
- platform: template
name: "Extract Temp Sensor Text"
lambda: |-
switch (int(id(extract_temp_status).state)) {
case 0: return {"Correct"};
case 1: return {"Error"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Exhaust Temp Sensor Text"
lambda: |-
switch (int(id(exhaust_temp_status).state)) {
case 0: return {"Correct"};
case 1: return {"Error"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Outdoor Temp Sensor Text"
lambda: |-
switch (int(id(outdoor_temp_status).state)) {
case 0: return {"Correct"};
case 1: return {"Error"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Supply Temp Sensor Text"
lambda: |-
switch (int(id(supply_temp_status).state)) {
case 0: return {"Correct"};
case 1: return {"Error"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Extract Fan Status Text"
lambda: |-
switch (int(id(extract_fan_status).state)) {
case 0: return {"Correct"};
case 1: return {"Error"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Supply Fan Status Text"
lambda: |-
switch (int(id(supply_fan_status).state)) {
case 0: return {"Correct"};
case 1: return {"Error"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Bypass Active Text"
lambda: |-
switch (int(id(bypass_active).state)) {
case 0: return {"Not active"};
case 1: return {"Active"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Boost Contact Status Text"
lambda: |-
switch (int(id(boost_contact_status).state)) {
case 0: return {"Not active"};
case 1: return {"Active"};
default: return {"Unknown"};
}
update_interval: 5s
- platform: template
name: "Boost Status Text"
lambda: |-
switch (int(id(boost_status).state)) {
case 1: return {"Boost active"};
case 0: return {"Boost not active"};
default: return {"Unknown"};
}
update_interval: 5s
switch:
- platform: modbus_controller
name: "Manual By-pass"
register_type: coil
address: 8
- platform: modbus_controller
name: "Allow Automatic By-pass"
register_type: coil
address: 9
- platform: modbus_controller
name: "Summer Mode"
register_type: coil
address: 10
- platform: modbus_controller
name: "Manual Boost"
register_type: coil
address: 16
- platform: modbus_controller
name: "Snooze Mode"
register_type: coil
address: 17
- platform: modbus_controller
name: "Working Mode"
register_type: coil
address: 26
- platform: modbus_controller
name: "Reset Filter Alarm"
register_type: coil
address: 1
id: reset_filter_alarm_switch
binary_sensor:
- platform: modbus_controller
name: "Active Alarms"
address: 0
id: active_alarms
register_type: discrete_input
- platform: modbus_controller
name: "Filter Alarm"
address: 1
id: filter_alarm
register_type: discrete_input
- platform: modbus_controller
name: "Extract Temp Sensor Status"
id: extract_temp_status
address: 6
register_type: discrete_input
- platform: modbus_controller
name: "Exhaust Temp Sensor Status"
id: exhaust_temp_status
address: 7
register_type: discrete_input
- platform: modbus_controller
name: "Outdoor Temp Sensor Status"
id: outdoor_temp_status
address: 8
register_type: discrete_input
- platform: modbus_controller
name: "Supply Temp Sensor Status"
id: supply_temp_status
address: 9
register_type: discrete_input
- platform: modbus_controller
name: "Extract Fan Status"
id: extract_fan_status
address: 10
register_type: discrete_input
- platform: modbus_controller
name: "Supply Fan Status"
id: supply_fan_status
address: 11
register_type: discrete_input
- platform: modbus_controller
name: "Bypass Active"
id: bypass_active
address: 15
register_type: discrete_input
- platform: modbus_controller
name: "Boost Contact Status"
id: boost_contact_status
address: 28
register_type: discrete_input
- platform: modbus_controller
name: "Boost Status"
id: boost_status
address: 29
register_type: discrete_input
interval:
- interval: 1s
then:
- if:
condition:
switch.is_on: reset_filter_alarm_switch
then:
- switch.turn_off: reset_filter_alarm_switch
select:
- platform: modbus_controller
id: selected_airflow_select
name: "Selected Airflow"
address: 132
value_type: U_WORD
optionsmap:
"Manual (low airflow)": 0
"Manual (medium airflow)": 1
"Manual (nominal airflow)": 2
"Auto": 3
"Snooze": 4
- if is connection correct you will see at logs
[21:52:47][D][text_sensor:069]: 'Boost Status Text': Sending state 'Boost active'
[21:52:47][D][text_sensor:069]: 'Supply Temp Sensor Text': Sending state 'Correct'
[21:52:47][D][text_sensor:069]: 'Bypass Active Text': Sending state 'Not active'
[21:52:47][D][text_sensor:069]: 'Bypass Damper Position': Sending state 'Closed'
[21:52:47][D][text_sensor:069]: 'Extract Fan Status Text': Sending state 'Correct'
[21:52:48][D][text_sensor:069]: 'Actual Working Mode': Sending state 'Boost in auto'
[21:52:48][D][text_sensor:069]: 'Supply Fan Status Text': Sending state 'Correct'
[21:52:48][D][text_sensor:069]: 'Extract Temp Sensor Text': Sending state 'Correct'
[21:52:48][D][text_sensor:069]: 'Boost Contact Status Text': Sending state 'Not active'
- now you can create custom card at homeassistant dashboard. this is only example
code
type: vertical-stack
cards:
- type: entities
title: Rekuperácia – režimy a prepínače
show_header_toggle: false
entities:
- entity: sensor.esphome_web_0ba9a0_rpm_supply_motor
- entity: sensor.esphome_web_0ba9a0_rpm_extract_motor
- entity: select.esphome_web_0ba9a0_selected_airflow
name: Režim prúdenia
- entity: sensor.esphome_web_0ba9a0_actual_working_mode
name: Pracovný režim
- entity: switch.esphome_web_0ba9a0_manual_by_pass
name: Manuálny bypass
- entity: switch.esphome_web_0ba9a0_allow_automatic_by_pass
name: Automatický bypass
- entity: switch.esphome_web_0ba9a0_manual_boost
name: Boost režim
- entity: switch.esphome_web_0ba9a0_snooze_mode
name: Snooze režim
- entity: switch.esphome_web_0ba9a0_summer_mode
name: Letný režim
- entity: switch.esphome_web_0ba9a0_reset_filter_alarm
name: Reset filtračného alarmu
- entity: switch.esphome_web_0ba9a0_summer_mode
- type: entities
title: Teploty a vlhkosti
show_header_toggle: false
entities:
- entity: sensor.esphome_web_0ba9a0_supply_air_temperature
- entity: sensor.esphome_web_0ba9a0_extract_air_temperature
- entity: sensor.esphome_web_0ba9a0_outdoor_air_temperature
- entity: sensor.esphome_web_0ba9a0_exhaust_air_temperature
- entity: sensor.esphome_web_0ba9a0_relative_humidity_supply_air
- entity: sensor.esphome_web_0ba9a0_relative_humidity_extract_air
- entity: sensor.esphome_web_0ba9a0_relative_humidity_outdoor_air
- entity: sensor.esphome_web_0ba9a0_relative_humidity_exhaust_air
- type: entities
title: Stav a diagnostika
show_header_toggle: false
entities:
- entity: sensor.esphome_web_0ba9a0_actual_working_mode
- entity: sensor.esphome_web_0ba9a0_selected_airflow
- entity: sensor.esphome_web_0ba9a0_active_alarm_status
- entity: sensor.esphome_web_0ba9a0_filter_alarm_status
- entity: sensor.esphome_web_0ba9a0_bypass_damper_position
- entity: sensor.esphome_web_0ba9a0_defrost_status
- entity: sensor.esphome_web_0ba9a0_boost_status_text
- enjoy
