Systemair SAVECare Ventilation unit

Can anyone give me the examples for config of vsr300/500 i cant for the life of me figure out
Should everything be in configuration.yaml or splitted up in sensor.yaml switches.yaml etc.

I try to use this instead of node red i always get a error in my config, has the configyration.yaml changed the last year on how it should be set up?

@Ontrackx
If i understand you correct you are using the modbus integration in Home assistant and not Node red.
I haven’t used the modbus integration in HA after switching to Node red (2021), so i don’t have any sensor examples in yaml for you, but i see others have some examples in earlier posts. If you follow the examples in the HA modbus section, it should be quite straight forward. There have been a few breaking changes regarding the modus intergration in HA, if you haven’t adapted the config accordingly, maybe that’s the cause in your case?

You must not split up your config, my opinion is that splitting up the config gives better structure if you have lots of sensors, switches etc.
There is lots of threads in the forum of how to split up the config if uncertain of the approach.

Not much of help but hopefully some.

Hi!
I got it working after some tries, and I’ve been uploading my backup to Github as a personal backup/sharing spot. You can take a look at my files at https://github.com/Ztaeyn/HomeAssistant-VTR-Modbus. It’s still work in progress but fully functional, but I hope it will get you to the next step.

I’ve split it up into separate files to keep configuration.yaml tidy.
I used the incomplete files from @Tempo_Trevis from the start of the thread and I’m trying to add in the missing parts myself to get know modbus better.

I hope it’s ok I borrowed your files @Tempo_Trevis .takker så mye for innsatsen du har gjort.

Bare fint andre fĂĽr glede av info :slight_smile:
Let me know if there is any sensors you are missing, and i can share the modbus addresses being used in my Node red config in a list or text file.

I just found this thread today after spending the day putting together my own config. Here’s what I have for a VSR 500.

Everything except the REG_OUTPUT_Y1_ANALOG (Heater A0 state) seems to work fine. That one just never shows anything other than 0, even when the heater is on.

-David

Modbus config for a SystemAir SAVE VSR 500 with sensors, switches, and binary_sensors:

- type: tcp
  name: "FTX Modbus"
  host: 192.168.1.13
  port: 8899
  delay: 4
  timeout: 4
  sensors:
    - name: "FTX Outdoor Temperature"
      unique_id: ftx_outdoor_temperature
      address: 12101
      slave: 1
      data_type: int16
      scan_interval: 10
      unit_of_measurement: °C
      device_class: temperature
      state_class: measurement
      scale: 0.1
      precision: 1
    - name: "FTX Supply Temperature"
      unique_id: ftx_supply_temperature
      address: 12102
      slave: 1
      data_type: int16
      scan_interval: 10
      unit_of_measurement: °C
      device_class: temperature
      state_class: measurement
      scale: 0.1
      precision: 1
    - name: "FTX Extract Temperature"
      unique_id: ftx_extract_temperature
      address: 12543 #This is PDM Input 1 not 12104
      slave: 1
      data_type: int16
      scan_interval: 10
      unit_of_measurement: °C
      device_class: temperature
      state_class: measurement
      scale: 0.1
      precision: 1
    - name: "FTX Over Heat Temperature"
      unique_id: ftx_over_heat_temperature
      address: 12107
      slave: 1
      data_type: int16
      scan_interval: 100
      unit_of_measurement: °C
      device_class: temperature
      state_class: measurement
      scale: 0.1
      precision: 1    
    - name: "FTX Humidity"
      unique_id: ftx_humidity
      address: 12135 #This is PDM Input 2 not 12108
      slave: 1
      data_type: uint16
      scan_interval: 10
      unit_of_measurement: '%'
      device_class: humidity
      state_class: measurement
    - name: "FTX Extract Fan Speed RPM"
      unique_id: ftx_extract_fan_speed_rpm
      address: 12401
      slave: 1
      data_type: uint16
      scan_interval: 10
      unit_of_measurement: RPM
      state_class: measurement
    - name: "FTX Supply Fan Speed RPM"
      unique_id: ftx_supply_fan_speed_rpm
      address: 12400
      slave: 1
      data_type: uint16
      scan_interval: 10
      unit_of_measurement: RPM
      state_class: measurement
    - name: "FTX Supply Fan Speed Percent"
      unique_id: ftx_supply_fan_speed_percent
      address: 14000
      slave: 1
      data_type: uint16
      scan_interval: 10
      unit_of_measurement: '%'
      state_class: measurement
    - name: "FTX Extract Fan Speed Percent"
      unique_id: ftx_extract_fan_speed_percent
      address: 14001
      slave: 1
      data_type: uint16
      scan_interval: 10
      unit_of_measurement: '%'
      state_class: measurement
    - name: "FTX Supply Setpoint Temperature"
      unique_id: ftx_supply_setpoint_temperature
      address: 2000
      slave: 1
      data_type: int16
      scan_interval: 20
      unit_of_measurement: °C
      device_class: temperature
      state_class: measurement
      scale: 0.1
      precision: 1
    - name: "FTX Supply Setpoint Temperature SATC"
      unique_id: ftx_supply_setpoint_temperature_satc
      address: 2053
      slave: 1
      data_type: int16
      scan_interval: 100
      unit_of_measurement: °C
      device_class: temperature
      state_class: measurement
      scale: 0.1
      precision: 1
    - name: "FTX Heat Exchanger Active"
      unique_id: ftx_heat_exchanger_active
      address: 14102
      data_type: uint16
      slave: 1
      scan_interval: 5
      unit_of_measurement: '%'
      state_class: measurement
    - name: "FTX Heater Active"
      unique_id: ftx_heater_active
      address: 14100
      data_type: uint16
      slave: 1
      scan_interval: 5
      unit_of_measurement: '%'
      state_class: measurement
    - name: "FTX Active User Mode"
      unique_id: ftx_active_user_mode
      address: 1160
      data_type: uint16
      slave: 1
      scan_interval: 10
    - name: "FTX Fan Control Mode"
      unique_id: ftx_fan_control_mode
      address: 1273
      data_type: uint16
      slave: 1
      scan_interval: 100    
    - name: "FTX Temperature Control Mode"
      unique_id: ftx_temperature_control_mode
      address: 2030
      data_type: uint16
      slave: 1
      scan_interval: 100   
    - name: "FTX Supply Fan Manual Override Percent"
      unique_id: ftx_supply_fan_manual_override_percent
      unit_of_measurement: '%'
      address: 13800
      slave: 1
      data_type: uint16
      scan_interval: 100
    - name: "FTX Extract Fan Manual Override Percent"
      unique_id: ftx_extract_fan_manual_override_percent
      unit_of_measurement: '%'
      address: 13801
      slave: 1
      data_type: uint16
      scan_interval: 100
    - name: "FTX Eco Mode Temperature Offset"
      unique_id: ftx_eco_mode_temperature_offset
      address: 2503
      slave: 1
      data_type: int16
      input_type: holding
      scale: 0.1
      precision: 1
      scan_interval: 100
      unit_of_measurement: °C
      device_class: temperature
      state_class: measurement
    - name: "FTX Schedule Active Temperature Offset"
      unique_id: ftx_schedule_active_temperature_offset
      address: 5000
      slave: 1
      data_type: int16
      input_type: holding
      scale: 0.1
      precision: 1
      scan_interval: 100
      unit_of_measurement: °C
      device_class: temperature
      state_class: measurement
    - name: "FTX Schedule Active Fan Level"
      unique_id: ftx_schedule_active_fan_level
      address: 5059
      data_type: uint16
      slave: 1
      scan_interval: 100   
    - name: "FTX Schedule Inactive Temperature Offset"
      unique_id: ftx_schedule_inactive_temperature_offset
      address: 5001
      slave: 1
      data_type: int16
      input_type: holding
      scale: 0.1
      precision: 1
      scan_interval: 100
      unit_of_measurement: °C
      device_class: temperature
      state_class: measurement
    - name: "FTX Schedule Inactive Fan Level"
      unique_id: ftx_schedule_inactive_fan_level
      address: 5060
      data_type: uint16
      slave: 1
      scan_interval: 100   
    - name: "FTX Filter Months Remaining"
      unique_id: ftx_filter_months_remaining
      address: 7004
      data_type: uint32
      swap: word
      count: 2
      slave: 1
      scale: 0.000000379477838 # From seconds to months
      precision: 1
      state_class: measurement
      device_class: duration
      unit_of_measurement: months
      scan_interval: 100   
    - name: "FTX Filter Alarm"
      unique_id: ftx_filter_alarm
      address: 15141
      data_type: uint16
      slave: 1
      scan_interval: 100   
  switches:
    - name: "FTX Supply Fan Manual Override Enable"
      unique_id: ftx_supply_fan_manual_override_enable
      address: 13600
      slave: 1
      scan_interval: 100
      verify:
    - name: "FTX Extract Fan Manual Override Enable"
      unique_id: ftx_extract_fan_manual_override_enable
      address: 13601
      slave: 1
      scan_interval: 100
      verify:
    - name: "FTX Eco Mode Enabled"
      unique_id: ftx_eco_mode_enabled
      address: 2504
      slave: 1
      scan_interval: 100
      verify:
    - name: "FTX Free Cooling Mode Enabled"
      unique_id: ftx_free_cooling_mode_enabled
      address: 4100
      slave: 1
      scan_interval: 100
      verify:
    - name: "FTX Heater Enabled"
      unique_id: ftx_heater_enabled
      address: 13200
      slave: 1
      scan_interval: 100
      verify:
    - name: "FTX Allow Manual Fan Stop"
      unique_id: ftx_allow_manual_fan_stop
      address: 1352
      slave: 1
      scan_interval: 100
      verify:
  binary_sensors:
    - name: "FTX Fans Running"
      unique_id: ftx_fans_running
      address: 1350
      slave: 1
      input_type: input
      scan_interval: 10
    - name: "FTX Heater Control"
      unique_id: ftx_heater_control
      address: 14380
      slave: 1
      input_type: input
      scan_interval: 5
    - name: "FTX Eco Mode Active"
      unique_id: ftx_eco_mode_active
      address: 2505
      slave: 1
      input_type: input
      scan_interval: 10
    - name: "FTX Free Cooling Mode Active"
      unique_id: ftx_free_cooling_mode_active
      address: 4110
      slave: 1
      input_type: input
      scan_interval: 100
    - name: "FTX Refresh Mode Input"
      unique_id: ftx_refresh_mode_input
      address: 12021
      slave: 1
      input_type: input
      scan_interval: 5
    - name: "FTX Cooking Mode Input"
      unique_id: ftx_cooking_mode_input
      address: 12022
      slave: 1
      input_type: input
      scan_interval: 5
    - name: "FTX Alarm"
      unique_id: ftx_alarm
      address: 14002
      slave: 1
      input_type: input
      scan_interval: 10
      

Here are some automations to allow you to use human-readable input_numbers and input_selects for setting the modes. You’ll have to recreate them yourself as I added them with the GUI and I don’t have code for them. But they are pretty obvious from the scripts below.

- alias: FTX set Temperature Setpoint from UI
  id: ftx_set_temperature_setpoint_from_ui
  trigger:
    - platform: state
      entity_id: input_number.ftx_temperature_setpoint
  action:
    - service: modbus.write_register
      data:
        address: 2000
        slave: 1
        value: >
          {{ states('input_number.ftx_temperature_setpoint')|int(1)*10 }}
        hub: "FTX Modbus"
    - delay: "00:00:02"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_supply_setpoint_temperature

- alias: FTX update Temperature Setpoint UI
  id: ftx_update_temperature_setpoint_ui
  trigger:
    - platform: state
      entity_id: sensor.ftx_supply_setpoint_temperature
  condition:
    - condition: template
      value_template: >
        {{ states('input_number.ftx_temperature_setpoint')|int(1) != states('sensor.ftx_supply_setpoint_temperature')|int(1) }}
  action:
    - service: input_number.set_value
      entity_id: input_number.ftx_temperature_setpoint
      data:
        value: >
          {{ states('sensor.ftx_supply_setpoint_temperature')|int(1) }}



- alias: FTX set Manual Fan Speed Level from UI and Manual Mode
  id: ftx_set_manual_fan_speed_level_from_ui_and_manual_mode
  description: 'When switched out of Auto, set the speed and switch to manual mode'
  trigger:
    platform: state
    entity_id: input_select.ftx_manual_fan_speed
    not_to: "Auto"
  action:
    - service: input_select.select_option
      entity_id: input_select.ftx_active_user_mode
      data:
        option: 'Manual'
    - delay: "00:00:02"
    - service: modbus.write_register
      data:
        address: 1130
        slave: 1
        value: >
            {% set mapper = 
              {
                'Off':'0',
                'Low':'2',
                'Normal':'3',
                'High':'4'
                }
            %}
            {{ mapper[states('input_select.ftx_manual_fan_speed')] }}
        hub: "FTX Modbus"


- alias: FTX unset Manual Fan Speed Level from UI and set Auto
  id: ftx_unset_active_user_mode_from_ui_and_set_auto
  description: 'When switched to Auto, set the mode to auto'
  trigger:
    platform: state
    entity_id: input_select.ftx_manual_fan_speed
    to: "Auto"
  action:
    - service: input_select.select_option
      entity_id: input_select.ftx_active_user_mode
      data:
        option: 'Auto'




# Note: Stove is tricky. You can't choose it, but it can be activated!
- alias: FTX set Active User Mode from UI
  id: ftx_set_active_user_mode_from_ui
  trigger:
    platform: state
    entity_id: input_select.ftx_active_user_mode
  action:
    - service: modbus.write_register
      data:
        address: 1161
        slave: 1
        value: >
            {% set mapper = 
              {
                'Auto':'1',
                'Manual':'2',
                'Crowded':'3',
                'Refresh':'4',
                'Fireplace':'5',
                'Away':'6',
                'Holiday':'7',
                'Stove':'0'
                }
            %}
            {{ mapper[states('input_select.ftx_active_user_mode')] }}
        hub: "FTX Modbus"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_active_user_mode

# - alias: FTX turn on Stove Mode via external input
#   id: ftx_turn_on_stove_mode_via_external_input
#   trigger:
#     platform: state
#     entity_id: input_select.ftx_active_user_mode
#   condition:
#     - condition: state
#       entity_id: input_select.ftx_active_user_mode
#       state: Stove
  

- alias: FTX set UI from Active User Mode
  id: ftx_set_ui_from_active_user_mode
  description: 'If the FTX has changed modes, then check if the UI is in-sync, and, if not, fix it.'
  trigger: 
    platform: state
    entity_id: sensor.ftx_active_user_mode
  condition:
    - condition: template
      value_template: >
            {% set mapper = 
              {
                'Auto':'0',
                'Manual':'1',
                'Crowded':'2',
                'Refresh':'3',
                'Fireplace':'4',
                'Away':'5',
                'Holiday':'6',
                'Stove':'7'
                }
            %}
            {{ mapper[states('input_select.ftx_active_user_mode')] != states('sensor.ftx_active_user_mode')|int(1) }}
  action:
    - service: input_select.select_option
      entity_id: input_select.ftx_active_user_mode
      data:
        option: >
            {% set mapper = 
              {
                '0':'Auto',
                '1':'Manual',
                '2':'Crowded',
                '3':'Refresh',
                '4':'Fireplace',
                '5':'Away',
                '6':'Holiday',
                '7':'Stove'
                }
            %}
            {{ mapper[states('sensor.ftx_active_user_mode')] }}



- alias: FTX set Temperature Control from UI
  id: ftx_set_temperature_control_from_ui
  trigger:
    platform: state
    entity_id: input_select.ftx_temperature_control_mode
  action:
    - if:
        - condition: state
          entity_id: input_select.ftx_temperature_control_mode
          state: Supply
      then:
        - service: modbus.write_register
          data: 
            address: 2030
            slave: 1
            value: 0
            hub: "FTX Modbus"
      else:
        - service: modbus.write_register
          data: 
            address: 2030
            slave: 1
            value: 2
            hub: "FTX Modbus"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_temperature_control_mode




- alias: FTX set Supply Fan Manual Override from UI
  id: ftx_set_supply_fan_manual_override_from_ui
  description: 'Update the FTX Supply Fan Manual Override from the UI controller'
  trigger:
    - platform: state
      entity_id: input_number.ftx_supply_fan_manual_override_percent
  action:
    - service: modbus.write_register
      data:
        address: 13800
        slave: 1
        value: >
          {{ states('input_number.ftx_supply_fan_manual_override_percent')|int(1) }}
        hub: "FTX Modbus"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_supply_fan_manual_override_percent




- alias: FTX set Extract Fan Manual Override from UI
  id: ftx_set_extract_fan_manual_override_from_ui
  description: 'Update the FTX Extract Fan Manual Override from the UI controller'
  trigger:
    - platform: state
      entity_id: input_number.ftx_extract_fan_manual_override_percent
  action:
    - service: modbus.write_register
      data:
        address: 13801
        slave: 1
        value: >
          {{ states('input_number.ftx_extract_fan_manual_override_percent')|int(1) }}
        hub: "FTX Modbus"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_extract_fan_manual_override_percent


- alias: FTX set ECO Mode Offset from UI
  id: ftx_set_eco_mode_offset_from_ui
  description: 'Update the FTX ECO Mode Offset from the UI controller.'
  trigger:
    - platform: state
      entity_id: input_number.ftx_eco_mode_temperature_offset
  action:
    - service: modbus.write_register
      data:
        address: 2503
        slave: 1
        value: >
          {{ states('input_number.ftx_eco_mode_temperature_offset')|int(1)*10 }}
        hub: "FTX Modbus"
    - delay: "00:00:02"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_eco_mode_temperature_offset

- alias: FTX update ECO Mode Offset UI
  id: ftx_update_eco_mode_offset_ui
  description: 'Update the UI controller for the ECO mode if the value on the FTX changes and it is not the same.'
  trigger:
    - platform: state
      entity_id: sensor.ftx_eco_mode_temperature_offset
  condition:
    - condition: template
      value_template: >
        {{ states('input_number.ftx_eco_mode_temperature_offset')|int(1) != states('sensor.ftx_eco_mode_temperature_offset')|int(1) }}
  action:
    - service: input_number.set_value
      entity_id: input_number.ftx_eco_mode_temperature_offset
      data:
        value: >
          {{ states('sensor.ftx_eco_mode_temperature_offset')|int(1) }}

Enjoy!

3 Likes

@Tempo_Trevis I just took a look at your NodeRed repository and was curious about your energy calculations. I don’t use NodeRed myself, so I was wondering if you could let me know how you calculated the saved energy. I can see doing the air volume times the temperature differences between the intake and output (I don’t have an output sensor, but I can add one easily enough). Is that how you approach it? Or do you have an estimate of the heater as well? I have a power meter on the whole system, so I can get that part.

Thanks!
-David

Hi,
Im not sure but i believe the “Heater A0 state” is for an external heater regulator output, which are more commonly used on bigger industrial ventilation units. If the modbus addresses on your unit are the same as mine you can read the el. heater output command at modbus adr. 2148 (0-100% output).

1 Like

Hi,
I’m using a formula estimating the sensible heat in kW produced by the ventilation unit, it looks as shown below in Node red. This is only an estimate and will not be 100% accurate as there are parameters like air density that changes with the temperature, moisture (only using a fixed value for this as best guess), the air volume are only being estimated as i don’t have any sensor for that as well. But i believe it will give an good indication of what the unit produce in kW further accumulated to energy.

From there i accumulate the sensible heat each 10s to get estimated produced energy in kWt (could use the “Riemann sum integral” but i wasn’t aware of that option when i integrated the ventilation unit).

To get the “true produced energy” i simply subtracted the “estimated produced(delivered) power - power consumption” and did as mentioned above (accumulated the result each 10s to get estimated true produced energy). In that way the el.heater consumption is also being subtracted as it is being measured in the consumption.

In this example you only need the following sensors which you probably already have in your system:

  • Measured supply temp.
  • Measured inlet temp.
  • Supply fan output ( to estimate airflow [m3/s]
  • your factor for air density

I hope this answered your questions, there might be better ways of approaching this.

True estimated energy so far (since 2020/21):

Consumption so far (since 2020/21):
image

// hs := cp*ρ*q*dt   
// Eks.= (1.006 kJ/kg°C) (1.202 kg/m3) (1 m3/s) ((20°C) - (0°C)) 

//hs := sensible heat (kW)
//cp := specific heat of air (1.006 kJ/kg°C)
//p  := density of air (1.202 kg/m3)  or "press/(Rspecific*t)"
//q  := air volume flow (m3/s)
//dt := delta temperature delta [°C]
//press  := absolute pressure (Pa)
//t  := absolute temperature (kelvin)
//Rspecific := specific gas constant for dry air (J/(kg'K)) [Rspec]

var q_pv = flow.get ("vtr300_tilluftsvifte_pv","default");
var t_supply = flow.get ("vtr300_tillufts_temperatur","default");
var t_inlet = flow.get ("vtr300_inntak_temp","default");
var press = 101325;
var t = t_inlet + 273.15;
var Rspec = 287;

// Sjekker for "isNotNumber, ved "isNan" (ved Modbus kom./feil ell. av sensor data) tidlig "exit" (prøv igjen).
  if (isNaN(q_pv) || isNaN(t_supply) || isNaN(t_inlet) || isNaN(t)) {
    return;
  }

var cp = 1.006;
//var p = 1.202; 
var p = press/(Rspec*t);
var q = (q_pv * 3)/3600;  
var dt = t_supply - t_inlet;
var hs = cp*p*q*dt;

// Lagrer watt og kW til memory for videreberegning av faktisk levert estimert effekt
flow.set("vtr300_tilfort_Watt", hs*1000,"default");  // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilfort_kWatt", hs,"default");  // Lagrer tilført kW til "memory/default"

// Estimert levert effekt & energi total
var watt = {payload:(hs*1000).toFixed(0)};
var kW = {payload:(hs).toFixed(2)};
var kW_10s = {payload:(hs/360)}; // kW/10s

// Tilpasser desimaler for attribute visning i HA
flow.set("vtr300_tilfort_Watt_formated", (hs*1000).toFixed(0) +' Watt',"default");  // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilfort_kWatt_formated", (hs).toFixed(2) +' kW',"default");  // Lagrer tilført kW til "memory/default"

return [watt,kW,kW_10s];

Thanks! Now I understand what you are doing: you’re not explicitly trying to calculate the energy recovered from the indoor heat, but rather the total energy delivered, and then subtracting off what the whole unit consumed. I’ll take a look!
-David

@Tempo_Trevis I came up with another way of estimating the energy content of the air heating. If you see any problems with this do let me know! (I ran it by my 14 year-old and she thought it made sense, at least :slight_smile:

I ran the FTX for 20 minutes without the heater on (eco mode) and then 20 minutes with it on. I measured the difference in average power during those times and the difference in temperature delta.

This gave me that adding 403W of additional energy translated into an additional 5C of temperature increase. So I concluded that for my volume of airflow, it takes 81W per °C temperature increase. From this I can work out that I recover 14.8° of temperature via the exchanger, or 1.2kW equivalent heating.

I don’t know the volume of air, but as long as the electrical heater is essentially 100% efficient (it should be) this should be pretty accurate.

The part that isn’t quite perfect is that the heat exchanger was on slightly more (98% vs. 95%) for the non-heat added version.

-David

An interesting approach, if i compare the sensible heat equation with your findings for my VTR300 in normal speed, fan speed reduction is approx 20% (due to cold outdoor temp.), the inlet temp. is -9.0°C and the supply temp is 14,2°C. The sensible heat gives me estimated true power to be 1184 watts (power consumption is subtracted). In this situation the heat exchanger output in that case is 59,3%. Compared to your finding this would give me (14,2-(-9))*81= 1871 Watts

Since your VTR500 provide more airvolume than my smaler VTR300 (609mÂł/h vs 351mÂł/h according to system air), that could cause the main difference in estimated power delivered.

Im not an expert in this area, but including the airvolume as done i the sensible heat equation, you don’t have to repeat your power test for the different speed the ventilation unit is changing between during its operation time.

You mention that you don’t know the air volume, in my case i simply find the actual estimated volume by multiplying the supplyfan output [%] with qv_max (351m³/h in my case).

Does qv_max vary much with the system pressure drop? E.g., I don’t know the resistance in my ducts, and I saw in the manual that the flow volume goes down with increased resistance. Of course I’m not sure how big a deal that is since SystemAir recommend just using a default value on their web tool if you don’t know. -David

Sorry, this is way out of my knowledge, I’ve only integrated ventilation units to SCADA systems.
This is a question for the ventilation system designers/engineers.

If all the dampers (supply and extract air valves in the roof/wall) are properly adjusted, the system pressure should be as optimal as possible.

Thank you for the lovley writeup, i got everyting working. But how do i add a switch/entity in gui to control temp/fan speeds etc, do you have an example?

Here’s what I did.

For temperature, I have an input_number named ftx_temperature_setpoint.

I then have two automations: one that updates the FTX when I change the input_number and one that updates the input_number when the FTX has a temperature change. (The second one is only needed if you want changes on the FTX control panel to show up in HA.)

- alias: FTX set Temperature Setpoint from UI
  id: ftx_set_temperature_setpoint_from_ui
  trigger:
    - platform: state
      entity_id: input_number.ftx_temperature_setpoint
  action:
    - service: modbus.write_register
      data:
        address: 2000
        slave: 1
        value: >
          {{ states('input_number.ftx_temperature_setpoint')|int(1)*10 }}
        hub: "modbus_hub"
    - delay: "00:00:02"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_supply_setpoint_temperature

- alias: FTX update Temperature Setpoint UI
  id: ftx_update_temperature_setpoint_ui
  trigger:
    - platform: state
      entity_id: sensor.ftx_supply_setpoint_temperature
  condition:
    - condition: template
      value_template: >
        {{ states('input_number.ftx_temperature_setpoint')|int(1) != states('sensor.ftx_supply_setpoint_temperature')|int(1) }}
  action:
    - service: input_number.set_value
      entity_id: input_number.ftx_temperature_setpoint
      data:
        value: >
          {{ states('sensor.ftx_supply_setpoint_temperature')|int(1) }}

For changing the fan speed I have an input_select with four options and I use a mapping in the automation to convert it to the number to send:

- alias: FTX set Manual Fan Speed Level from UI and Manual Mode
  id: ftx_set_manual_fan_speed_level_from_ui_and_manual_mode
  description: 'When switched out of Auto, set the speed and switch to manual mode'
  trigger:
    platform: state
    entity_id: input_select.ftx_manual_fan_speed
    not_to: "Auto"
  action:
    - service: input_select.select_option
      entity_id: input_select.ftx_active_user_mode
      data:
        option: 'Manual'
    - delay: "00:00:02"
    - service: modbus.write_register
      data:
        address: 1130
        slave: 1
        value: >
            {% set mapper = 
              {
                'Off':'0',
                'Low':'2',
                'Normal':'3',
                'High':'4'
                }
            %}
            {{ mapper[states('input_select.ftx_manual_fan_speed')] }}

And here is what I have for the mode:

# Note: Stove is tricky. You can't choose it, but it can be activated!
- alias: FTX set Active User Mode from UI
  id: ftx_set_active_user_mode_from_ui
  trigger:
    platform: state
    entity_id: input_select.ftx_active_user_mode
  action:
    - service: modbus.write_register
      data:
        address: 1161
        slave: 1
        value: >
            {% set mapper = 
              {
                'Auto':'1',
                'Manual':'2',
                'Crowded':'3',
                'Refresh':'4',
                'Fireplace':'5',
                'Away':'6',
                'Holiday':'7',
                'Stove':'0'
                }
            %}
            {{ mapper[states('input_select.ftx_active_user_mode')] }}
        hub: "modbus_hub"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_active_user_mode
  

- alias: FTX set UI from Active User Mode
  id: ftx_set_ui_from_active_user_mode
  description: 'If the FTX has changed modes, then check if the UI is in-sync, and, if not, fix it.'
  trigger: 
    platform: state
    entity_id: sensor.ftx_active_user_mode
  condition:
    - condition: template
      value_template: >
            {% set mapper = 
              {
                'Auto':'0',
                'Manual':'1',
                'Crowded':'2',
                'Refresh':'3',
                'Fireplace':'4',
                'Away':'5',
                'Holiday':'6',
                'Stove':'7'
                }
            %}
            {{ mapper[states('input_select.ftx_active_user_mode')] != states('sensor.ftx_active_user_mode')|int(1) }}
  action:
    - service: input_select.select_option
      entity_id: input_select.ftx_active_user_mode
      data:
        option: >
            {% set mapper = 
              {
                '0':'Auto',
                '1':'Manual',
                '2':'Crowded',
                '3':'Refresh',
                '4':'Fireplace',
                '5':'Away',
                '6':'Holiday',
                '7':'Stove'
                }
            %}
            {{ mapper[states('sensor.ftx_active_user_mode')] }}

I hope that is helpful!
-David

Very nice! Thank you. If you list all the helper inputs needed in initial post. others like me (a novice) Would understand that we need helper entitys to get this to work.
Thank you this is exactly what i have been looking for.



This is how it looks with me at the moment, maybe you have a better UI setup. Would you like to share? What i like about this approach is that it is direct from modbus intrigation and not via node-red like i had before.

A modbus Systemair/ventilation intrigation (i would pay someone to make this) :smile:

That’s really cool. It looks like you’ve got them just the way I have them. Here’s my full automation. It’s the same as what I posted earlier with a few more details. Some of the input elements were created by the GUI, so I don’t have a config files for them.

- alias: FTX set Night Away Temperature Offset from UI
  id: ftx_set_night_away_temperature_offset_from_ui
  trigger:
    - platform: state
      entity_id: input_number.ftx_night_away_temperature_offset
  action:
    - service: modbus.write_register
      data:
        address: 5001
        slave: 1
        # Convert to 16-bit 2's complement from the negative number. E.g., 1111 1111 1111 1111 + (neg number) + 1
        value: >
          {{ 65535+(states('input_number.ftx_night_away_temperature_offset')|int(1)*10)+1 }}
        hub: "modbus_hub"
    - delay: "00:00:02"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_schedule_inactive_temperature_offset


- alias: FTX set Night Away Fan Mode from UI
  id: ftx_set_night_away_fan_mode_from_ui
  trigger:
    - platform: state
      entity_id: input_select.ftx_night_away_fan_level
  action:
    - service: modbus.write_register
      data:
        address: 5060
        slave: 1
        value: >
            {% set mapper = 
              {
                'Low':'2',
                'Normal':'3',
                'High':'4'
                }
            %}
            {{ mapper[states('input_select.ftx_night_away_fan_level')] }}
        hub: "modbus_hub"
    - delay: "00:00:02"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_schedule_inactive_fan_level


- alias: FTX set Temperature Setpoint from UI
  id: ftx_set_temperature_setpoint_from_ui
  trigger:
    - platform: state
      entity_id: input_number.ftx_temperature_setpoint
  action:
    - service: modbus.write_register
      data:
        address: 2000
        slave: 1
        value: >
          {{ states('input_number.ftx_temperature_setpoint')|int(1)*10 }}
        hub: "modbus_hub"
    - delay: "00:00:02"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_supply_setpoint_temperature

- alias: FTX update Temperature Setpoint UI
  id: ftx_update_temperature_setpoint_ui
  trigger:
    - platform: state
      entity_id: sensor.ftx_supply_setpoint_temperature
  condition:
    - condition: template
      value_template: >
        {{ states('input_number.ftx_temperature_setpoint')|int(1) != states('sensor.ftx_supply_setpoint_temperature')|int(1) }}
  action:
    - service: input_number.set_value
      entity_id: input_number.ftx_temperature_setpoint
      data:
        value: >
          {{ states('sensor.ftx_supply_setpoint_temperature')|int(1) }}



- alias: FTX set Manual Fan Speed Level from UI and Manual Mode
  id: ftx_set_manual_fan_speed_level_from_ui_and_manual_mode
  description: 'When switched out of Auto, set the speed and switch to manual mode'
  trigger:
    platform: state
    entity_id: input_select.ftx_manual_fan_speed
    not_to: "Auto"
  action:
    - service: input_select.select_option
      entity_id: input_select.ftx_active_user_mode
      data:
        option: 'Manual'
    - delay: "00:00:02"
    - service: modbus.write_register
      data:
        address: 1130
        slave: 1
        value: >
            {% set mapper = 
              {
                'Off':'0',
                'Low':'2',
                'Normal':'3',
                'High':'4'
                }
            %}
            {{ mapper[states('input_select.ftx_manual_fan_speed')] }}
        hub: "modbus_hub"


- alias: FTX unset Manual Fan Speed Level from UI and set Auto
  id: ftx_unset_active_user_mode_from_ui_and_set_auto
  description: 'When switched to Auto, set the mode to auto'
  trigger:
    platform: state
    entity_id: input_select.ftx_manual_fan_speed
    to: "Auto"
  action:
    - service: input_select.select_option
      entity_id: input_select.ftx_active_user_mode
      data:
        option: 'Auto'




# Note: Stove is tricky. You can't choose it, but it can be activated!
- alias: FTX set Active User Mode from UI
  id: ftx_set_active_user_mode_from_ui
  trigger:
    platform: state
    entity_id: input_select.ftx_active_user_mode
  action:
    - service: modbus.write_register
      data:
        address: 1161
        slave: 1
        value: >
            {% set mapper = 
              {
                'Auto':'1',
                'Manual':'2',
                'Crowded':'3',
                'Refresh':'4',
                'Fireplace':'5',
                'Away':'6',
                'Holiday':'7',
                'Stove':'0'
                }
            %}
            {{ mapper[states('input_select.ftx_active_user_mode')] }}
        hub: "modbus_hub"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_active_user_mode

- alias: FTX set UI from Active User Mode
  id: ftx_set_ui_from_active_user_mode
  description: 'If the FTX has changed modes, then check if the UI is in-sync, and, if not, fix it.'
  trigger: 
    platform: state
    entity_id: sensor.ftx_active_user_mode
  condition:
    - condition: template
      value_template: >
            {% set mapper = 
              {
                'Auto':'0',
                'Manual':'1',
                'Crowded':'2',
                'Refresh':'3',
                'Fireplace':'4',
                'Away':'5',
                'Holiday':'6',
                'Stove':'7'
                }
            %}
            {{ mapper[states('input_select.ftx_active_user_mode')] != states('sensor.ftx_active_user_mode')|int(1) }}
  action:
    - service: input_select.select_option
      entity_id: input_select.ftx_active_user_mode
      data:
        option: >
            {% set mapper = 
              {
                '0':'Auto',
                '1':'Manual',
                '2':'Crowded',
                '3':'Refresh',
                '4':'Fireplace',
                '5':'Away',
                '6':'Holiday',
                '7':'Stove'
                }
            %}
            {{ mapper[states('sensor.ftx_active_user_mode')] }}



- alias: FTX set Temperature Control from UI
  id: ftx_set_temperature_control_from_ui
  trigger:
    platform: state
    entity_id: input_select.ftx_temperature_control_mode
  action:
    - if:
        - condition: state
          entity_id: input_select.ftx_temperature_control_mode
          state: Supply
      then:
        - service: modbus.write_register
          data: 
            address: 2030
            slave: 1
            value: 0
            hub: "modbus_hub"
      else:
        - service: modbus.write_register
          data: 
            address: 2030
            slave: 1
            value: 2
            hub: "modbus_hub"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_temperature_control_mode



- alias: FTX set Supply Fan Low from UI
  id: ftx_set_supply_fan_low_from_ui
  trigger:
    - platform: state
      entity_id: input_number.ftx_supply_speed_low
  action:
    - service: modbus.write_register
      data:
        address: 1402
        slave: 1
        value: >
          {{ states('input_number.ftx_supply_speed_low')|int(1) }}
        hub: "modbus_hub"

- alias: FTX set Extract Fan Low from UI
  id: ftx_set_extract_fan_low_from_ui
  trigger:
    - platform: state
      entity_id: input_number.ftx_extract_speed_low
  action:
    - service: modbus.write_register
      data:
        address: 1403
        slave: 1
        value: >
          {{ states('input_number.ftx_extract_speed_low')|int(1) }}
        hub: "modbus_hub"

- alias: FTX set Supply Fan Normal from UI
  id: ftx_set_supply_fan_normal_from_ui
  trigger:
    - platform: state
      entity_id: input_number.ftx_supply_speed_normal
  action:
    - service: modbus.write_register
      data:
        address: 1404
        slave: 1
        value: >
          {{ states('input_number.ftx_supply_speed_normal')|int(1) }}
        hub: "modbus_hub"

- alias: FTX set Extract Fan Normal from UI
  id: ftx_set_extract_fan_normal_from_ui
  trigger:
    - platform: state
      entity_id: input_number.ftx_extract_speed_normal
  action:
    - service: modbus.write_register
      data:
        address: 1405
        slave: 1
        value: >
          {{ states('input_number.ftx_extract_speed_normal')|int(1) }}
        hub: "modbus_hub"

- alias: FTX set Supply Fan High from UI
  id: ftx_set_supply_fan_high_from_ui
  trigger:
    - platform: state
      entity_id: input_number.ftx_supply_speed_high
  action:
    - service: modbus.write_register
      data:
        address: 1406
        slave: 1
        value: >
          {{ states('input_number.ftx_supply_speed_high')|int(1) }}
        hub: "modbus_hub"

- alias: FTX set Extract Fan High from UI
  id: ftx_set_extract_fan_high_from_ui
  trigger:
    - platform: state
      entity_id: input_number.ftx_extract_speed_high
  action:
    - service: modbus.write_register
      data:
        address: 1407
        slave: 1
        value: >
          {{ states('input_number.ftx_extract_speed_high')|int(1) }}
        hub: "modbus_hub"






- alias: FTX set Supply Fan Manual Override from UI
  id: ftx_set_supply_fan_manual_override_from_ui
  description: 'Update the FTX Supply Fan Manual Override from the UI controller'
  trigger:
    - platform: state
      entity_id: input_number.ftx_supply_fan_manual_override_percent
  action:
    - service: modbus.write_register
      data:
        address: 13800
        slave: 1
        value: >
          {{ states('input_number.ftx_supply_fan_manual_override_percent')|int(1) }}
        hub: "modbus_hub"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_supply_fan_manual_override_percent


- alias: FTX set Extract Fan Manual Override from UI
  id: ftx_set_extract_fan_manual_override_from_ui
  description: 'Update the FTX Extract Fan Manual Override from the UI controller'
  trigger:
    - platform: state
      entity_id: input_number.ftx_extract_fan_manual_override_percent
  action:
    - service: modbus.write_register
      data:
        address: 13801
        slave: 1
        value: >
          {{ states('input_number.ftx_extract_fan_manual_override_percent')|int(1) }}
        hub: "modbus_hub"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_extract_fan_manual_override_percent


- alias: FTX set ECO Mode Offset from UI
  id: ftx_set_eco_mode_offset_from_ui
  description: 'Update the FTX ECO Mode Offset from the UI controller.'
  trigger:
    - platform: state
      entity_id: input_number.ftx_eco_mode_temperature_offset
  action:
    - service: modbus.write_register
      data:
        address: 2503
        slave: 1
        value: >
          {{ states('input_number.ftx_eco_mode_temperature_offset')|int(1)*10 }}
        hub: "modbus_hub"
    - delay: "00:00:02"
    - service: homeassistant.update_entity
      entity_id: sensor.ftx_eco_mode_temperature_offset

- alias: FTX update ECO Mode Offset UI
  id: ftx_update_eco_mode_offset_ui
  description: 'Update the UI controller for the ECO mode if the value on the FTX changes and it is not the same.'
  trigger:
    - platform: state
      entity_id: sensor.ftx_eco_mode_temperature_offset
  condition:
    - condition: template
      value_template: >
        {{ states('input_number.ftx_eco_mode_temperature_offset')|int(1) != states('sensor.ftx_eco_mode_temperature_offset')|int(1) }}
  action:
    - service: input_number.set_value
      entity_id: input_number.ftx_eco_mode_temperature_offset
      data:
        value: >
          {{ states('sensor.ftx_eco_mode_temperature_offset')|int(1) }}

The GUI I have uses a bunch of PNGs with transparent areas to make the different parts of the system change color when they are active. (E.g., the heater turns yellow when it is on, the different circuits in the heat pump turn colored when they are on, the lines for the FTX get thicker when it is in higher speed fan modes, etc.)

Great work! Could this setup by any chance be applied to the Systemair Topvex units? They are a bit bigger than the VTR with higher capacity. Modbus TCP is supported.

anyone have any experiences writing a co2 value that can be used for the demand control?