Systemair SAVECare Ventilation unit

Systemair is providing ventilation units that i realy would like to see suport for i HA.

There has been some atempts to make integration for this units but som conflicts arrise in this github pr. Whit the harse feedback from HA devs in regards to this attempts i doubt the original author will give it a try again. I hope some else can pick up the project.

I know there is a pylib made for systemair cloud that can be used for this project.

I hope i will see support for it soon

Hi,
I’ve recently via yaml integrated my Save VTR300 using modbus TCP, and hope this will help others as this forum has helped me alot by reading other posts from this forum. Im new to HA so i believe my way of doing this could be improved a lot as im in a steep HA learning phase.
At this point i’m considering trying Node-Red for automations and further integration for the schedule variables when i find time.

VTR300 features for this integration listed below (more or less):

  • Control of all modes like:
    *Manual Off/Manual Lo/Man Normal/Manual Hi
    *Auto Scehdule
    *Boost/Party/Fireplace/Vacation (Time setpoint)
    *HA ppm regulated mode “PPM Auto” by using Netatmo ppm value in automation + recovery to Auto
    if netatmo fails/is offline (a few locally ppm sensors are to be installed later at some time)
  • Persistent notifications for A, B, C alarms + some more
  • Flow chart with relevant sensor and state info.
  • Calculated rotating heater recycling rate (0-100%)
  • Relevant setpoints and states like:
    *Eco mode on/off + setpoint
    • Kitchen Hood/Low/Norma/Hi/Boost Fireplace Etc.
      *Temperature
    • El.Heater output
      *Time setpoint for Party/Boost/Fireplace/Vacation (displayed on mode button)
    • Change between supply and extract air temp. regulation.
    • Energy consumption by using TP link HS110
    • Internal clock, nice to know as the time must be correct for Auto mode to operate as given in the
      time schedule.
      The following is not yet integrated:
  • Detailed schedule integration, will probably try Node-Red as there is a lot of variables to write to and
    because i don’t see any other option (as far as i know) than using automations for each variable to be
    updated if there is a change done on the VTR300 HMI panel.
  • Summer/Winter Fan compensation (only read in UI for now)
  • Automation for changing between supply/Extract temp. regulation and/or temp setpoint from avg.
    outdoor temp. (last 3 days or so).
  • Improve UI in general/cleanup when fully integrated at some point later.

The HA integration has been in operation for a about 2 months and works great, Low speed is set if no one is home and the PPM is low enough according to setpoint when in “PPM Auto”, speed is increased according to automations and it is easy to see overall status from the flow chart.

The modbus documentation has it flaws as most modbus documentation from my experience, not straight forward…

NOTE! The modbus adr. listed below will not be the same for older Systemair ventilation units.
My current Save VTR300 software is:

  • Circuit board: 1.11.0
  • HMI: 1.13.0
  • IAM: 1.6.0

If using modbus TCP, the IAM cloud will not work. You must choose between the IAM cloud or Modbus.
Modbus integration gives a lot of new possibilities.
Far from all yaml config is listed because it was too long and not allowed or/and i’m doing something wrong when posting, sorry about that, but i hope it will give an idea and help out others.

modbus:
  - name: VTR300
    type: tcp
    host: IP Adr
    port: 502
    timeout: 3  #Timeout for slave response in seconds (default=3)
    delay: 3  #Time to sleep in seconds after connecting and before sending messages (defualt=0)
  
    climates:
       - name: Ventilasjon
         slave: 1
         data_type: uint
         data_count: 1
         scale: 0.1
         offset: 0
         precision: 1
         max_temp: 30
         min_temp: 15
         temp_step: 1
         target_temp_register: 2000
         current_temp_register: 12102
sensor:
  - platform: modbus
    scan_interval: 10
    registers:      
      - name: vtr300_inntaks_temperatur
        hub: VTR300
        device_class: temperature
        slave: 1
        register: 12101
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1
        data_type: int
      - name: vtr300_avtrekks_temperatur
        hub: VTR300
        device_class: temperature
        slave: 1
        register: 12543 #2050   #NB!EAT/RAT used for extract air controller
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1
        data_type: uint
      - name: vtr300_avkast_temperatur
        hub: VTR300
        device_class: temperature
        slave: 1
        register: 12106
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1
        data_type: uint    
      - name: vtr300_overhetingstemperatur_tilluft
        hub: VTR300
        device_class: temperature
        slave: 1
        register: 12107
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1
        data_type: uint        
      - name: vtr300_relativ_fukt_avtrekk
        hub: VTR300
        device_class: humidity
        slave: 1
        register: 12135
        register_type: holding
        unit_of_measurement: '%RF'
        count: 1
        data_type: uint
      - name: vtr300_kalkulert_fukt_avtrekk
        hub: VTR300
        device_class: humidity
        slave: 1
        register: 2210
        register_type: holding
        unit_of_measurement: '%RF'
        count: 1
        data_type: uint
      - name: vtr300_kalkulert_fukt_inntak
        hub: VTR300
        device_class: humidity
        slave: 1
        register: 2211
        register_type: holding
        unit_of_measurement: '%RF'
        count: 1
        data_type: uint
      - name: vtr300_tilluftsvifte 
        hub: VTR300
        device_class: power_factor
        slave: 1
        register: 14000
        register_type: holding
        unit_of_measurement: '%'
        count: 1
        data_type: uint
      - name: vtr300_avtrekksvifte 
        hub: VTR300
        device_class: power_factor        
        slave: 1
        register: 14001
        register_type: holding
        unit_of_measurement: '%'
        count: 1
        data_type: uint
      - name: vtr300_sommerdrift_vinterdrift #0=sommerdrift, 1=Vinterdrift (status basert pĂĽ fukt i inntak)
        hub: VTR300
        slave: 1
        register: 1038
        register_type: holding
        count: 1
        data_type: uint
      - name: vtr300_komfyravtrekk_trykkvakt_av_pa 
        hub: VTR300
        slave: 1
        register: 12020
        register_type: holding
        count: 1
        data_type: uint
      - name: vtr300_el_kolbe_padrag #TRIAC pĂĽdrag
        hub: VTR300
        device_class: power_factor        
        slave: 1
        register: 2148
        register_type: holding
        unit_of_measurement: '%'
        count: 1
        data_type: uint
      - name: vtr300_el_kolbe_av_pa  #TRIAC
        hub: VTR300
        slave: 1
        register: 14380
        register_type: holding
        count: 1
        data_type: uint
      - name: vtr300_varmegjenvinner
        hub: VTR300
        device_class: power_factor        
        slave: 1
        register: 14102
        register_type: holding
        unit_of_measurement: '%'
        count: 1
        data_type: uint
      - name: vtr300_filterbytte_alm
        hub: VTR300
        slave: 1
        register: 7006
        register_type: holding
        count: 1
        data_type: uint
      - name: vtr300_filterbytte_md_intervall
        hub: VTR300
        slave: 1
        register: 7000
        register_type: holding
        unit_of_measurement: 'md'
        count: 1
        data_type: int
      - name: vtr300_tid_til_filter_bytte_s
        hub: VTR300
        slave: 1
        register: 7005
        register_type: holding
        unit_of_measurement: 's'
        count: 2
        data_type: uint
      - name: vtr300_modus_status_register
        hub: VTR300
        slave: 1
        register: 1160 #0=Auto,1=Man,2=Crowded,3=Refresh,4=Fireplace,5=Away,6=Holiday,7-12 not applicable
        register_type: holding
        count: 1
        data_type: uint
      - name: vtr300_manuell_modus_kommando_register
        hub: VTR300
        slave: 1
        register: 1130 #0=Man Stopp,1=Man ???,2=Man Lav,3=Man Normal,4=Man Høy
        register_type: holding
        count: 1
        data_type: uint
      - name: vtr300_tilluft_sp
        hub: VTR300
        device_class: temperature       
        slave: 1
        register: 2000
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1
        data_type: uint
      - name: vtr300_avtrekksluft_sp
        hub: VTR300
        device_class: temperature
        slave: 1
        register: 2012
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1
        data_type: uint
      - name: vtr300_avtrekksluft_min_sp
        hub: VTR300
        device_class: temperature
        slave: 1
        register: 2020
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1
        data_type: uint
      - name: vtr300_avtrekksluft_max_sp
        hub: VTR300
        device_class: temperature
        slave: 1
        register: 2021
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1
        data_type: uint
# 0 = Tillufts reg., 1 = Rom reg.(krever rom temp sensor, derfor NA), 2 = Avtrekks reg.
      - name: vtr300_tilluft_rom_avtrekk_reg
        hub: VTR300
        slave: 1
        register: 2030
        register_type: holding
        count: 1
        data_type: uint
      - name: vtr300_eco_varme_offset
        hub: VTR300
        device_class: temperature
        slave: 1
        register: 2503 #Temp. offset før varme element starter i Eco modus
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1
        data_type: uint
      - name: vtr300_nedtelling_modus_tid_s
        hub: VTR300
        slave: 1
        register: 1110
        register_type: holding
        unit_of_measurement: s
        count: 1
        data_type: uint           
      - name: vtr300_ferie_modus_varighet
        hub: VTR300
        slave: 1
        register: 1100
        register_type: holding
        unit_of_measurement: d
        count: 1
        data_type: uint   
      - name: vtr300_borte_modus_varighet
        hub: VTR300
        slave: 1
        register: 1101
        register_type: holding
        unit_of_measurement: t
        count: 1
        data_type: uint   
      - name: vtr300_ildsted_modus_varighet
        hub: VTR300
        slave: 1
        register: 1102
        register_type: holding
        unit_of_measurement: min
        count: 1
        data_type: uint   
      - name: vtr300_boost_modus_varighet
        hub: VTR300
        slave: 1
        register: 1103
        register_type: holding
        unit_of_measurement: min
        count: 1
        data_type: uint    
      - name: vtr300_party_modus_varighet
        hub: VTR300
        slave: 1
        register: 1104
        register_type: holding
        unit_of_measurement: t
        count: 1
        data_type: uint    
      - name: vtr300_fukt_avtrekk_sp
        hub: VTR300
        device_class: humidity
        slave: 1
        register: 2202
        register_type: holding
        unit_of_measurement: '%RF'
        count: 1
        data_type: uint          
      - name: vtr300_vifte_hast_komp_avlest
        hub: VTR300
        device_class: power_factor
        slave: 1
        register: 1254
        register_type: holding
        unit_of_measurement: '%'
        count: 1
        data_type: int 
      - name: vtr300_vifte_hast_komp_vinter
        hub: VTR300
        device_class: power_factor
        slave: 1
        register: 1251
        register_type: holding
        unit_of_measurement: '%'
        count: 1
        data_type: int  
      - name: vtr300_vifte_hast_komp_vinter_start_temp
        hub: VTR300
        device_class: power_factor
        slave: 1
        register: 1255
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1  
        data_type: int 
      - name: vtr300_vifte_hast_komp_vinter_max_temp
        hub: VTR300
        device_class: power_factor
        slave: 1
        register: 1253
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1  
        data_type: int 
      - name: vtr300_vifte_hast_komp_sjekkes
        hub: VTR300
        device_class: power_factor
        slave: 1
        register: 1252
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1  
        data_type: int 
      - name: vtr300_vifte_hast_komp_sommer
        hub: VTR300
        device_class: power_factor
        slave: 1
        register: 1258
        register_type: holding
        unit_of_measurement: '%'
        count: 1
        data_type: int 
      - name: vtr300_vifte_hast_komp_sommer_start_temp
        hub: VTR300
        device_class: power_factor
        slave: 1
        register: 1256
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1  
        data_type: int 
      - name: vtr300_vifte_hast_komp_sommer_max_temp
        hub: VTR300
        device_class: power_factor
        slave: 1
        register: 1257
        register_type: holding
        unit_of_measurement: °C
        count: 1
        scale: 0.1
        offset: 0
        precision: 1  
        data_type: int 
      - name: vtr300_a_alm
        hub: VTR300
        slave: 1
        register: 15900
        register_type: holding
        count: 1
        data_type: uint      
      - name: vtr300_b_alm
        hub: VTR300
        slave: 1
        register: 15901
        register_type: holding
        count: 1
        data_type: uint    
      - name: vtr300_c_alm
        hub: VTR300
        slave: 1
        register: 15902
        register_type: holding
        count: 1
        data_type: uint   
      - name: vtr300_filter_wrn_alm
        hub: VTR300
        slave: 1
        register: 15543
        register_type: holding
        count: 1
        data_type: uint   
      - name: vtr300_filter_alm
        hub: VTR300
        slave: 1
        register: 15141
        register_type: holding
        count: 1
        data_type: uint 
      - name: vtr300_tilluftstemp_lav_alm
        hub: VTR300
        slave: 1
        register: 15176
        register_type: holding
        count: 1
        data_type: uint        
      - name: vtr300_filter_wrn_cnt_alm
        hub: VTR300
        slave: 1
        register: 15548
        register_type: holding
        count: 1
        data_type: uint   
  - platform: template
    sensors:
      vtr300_tilluft_rf:
        value_template: '{{((states.sensor.vtr300_tilluftsvifte.state | int) * 3) | round(0) }}'
        friendly_name: "Tilluftsmengde"
        unit_of_measurement: 'mÂł/t'
  - platform: template
    sensors:
      vtr300_avtrekk_rf:
        value_template: '{{((states.sensor.vtr300_avtrekksvifte.state | int) * 3) | round(0) }}'
        friendly_name: "Avtrekksmengde"
        unit_of_measurement: 'mÂł/t'
  - platform: template
    sensors:
      vtr300_gjenvinningsgrad:
        value_template: '{{((((states.sensor.vtr300_tillufts_temperatur.state | float ) - (states.sensor.vtr300_inntaks_temperatur.state | float)) / ((states.sensor.vtr300_avtrekks_temperatur.state | float ) - (states.sensor.vtr300_inntaks_temperatur.state | float))) * 100) | round(1) }}'
        unit_of_measurement: '%'
        friendly_name: "Gjenvinningsgrad"
     
      vtr300_tid_til_filterbytte:
        friendly_name: 'Neste filter bytte'
        value_template: >- 
          {% if states('sensor.vtr300_tid_til_filter_bytte_s')|int > 1 %}
            {% set time = states.sensor.vtr300_tid_til_filter_bytte_s.state | int %}
            {% set minutes = ((time % 3600) / 60) | int %}
            {% set hours = ((time % 86400) / 3600) | int %}
            {% set days = (time / 86400) | int %}
            {%- if days > 548 -%}
              Mer en 1.5 ĂĽr
              {%- else -%}
              {%- if days > 0 -%}
                {{ days }}d
              {%- endif -%}
              {%- if hours > 0 -%}
                {%- if days > 0 -%}
                  {{ ' ' }}
                {%- endif -%}
                {{ hours }}t
              {%- endif -%}
              {%- if minutes > 0 -%}
                {%- if days > 0 or hours > 0 -%}
                  {{ ' ' }}
                {%- endif -%}
                {{ minutes }}m
              {%- endif -%}
            {%- endif -%}    
          {% else %}
            Bytt Filter!
          {% endif %}     

      vtr300_borte_nedtelling_tid_t:
        friendly_name: 'GjenstĂĽende tid nĂĽr aktiv'
        value_template: >- 
          {% if states('sensor.vtr300_modus_status_register')|int == 5 %}
            {% set time = states.sensor.vtr300_nedtelling_modus_tid_s.state | int %}
            {% set minutes = ((time % 3600) / 60) | int %}
            {% set hours = ((time % 86400) / 3600) | int %}

            {%- if time < 60 -%}
              Mindre en 1 minutt
              {%- else -%}
              {%- if hours > 0 -%}
                  {{ hours }}t
                {%- endif -%}
              {%- if minutes > 0 -%}
                {%- if hours > 0 -%}
                  {{ ' ' }}
                {%- endif -%}
                {{ minutes }}m
              {%- endif -%}
            {%- endif -%}
          {% else %}
            Inaktiv
          {% endif %}             

      vtr300_sommer_vinterd_driftstatus:
        friendly_name: "Aktiv sesong"
        value_template: >-
          {% if states('sensor.vtr300_sommerdrift_vinterdrift')|int == 0 %}
            Sommerdrift
          {% elif states('sensor.vtr300_sommerdrift_vinterdrift')|int == 1 %}
            Vinterdrift
          {% else %}
             Stein dau
          {% endif %}

      vtr300_reg_mode:
        friendly_name: "Regulering"
        value_template: >-
          {% if states('sensor.vtr300_tilluft_rom_avtrekk_reg')|int == 0 %}
            Tilluftsregulering
          {% elif states('sensor.vtr300_tilluft_rom_avtrekk_reg')|int == 1 %}
            Rom reg.
          {% elif states('sensor.vtr300_tilluft_rom_avtrekk_reg')|int == 2 %}
            Avtrekksregulering
          {% else %}
            Stein dau
          {% endif %}

      vtr300_modus_status:
        friendly_name: "Modus"
        value_template: >-
          {% if states('sensor.vtr300_modus_status_register')|int == 0 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 2 %}
            Auto schedule - Lav
          {% elif states('sensor.vtr300_modus_status_register')|int == 0 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 3 %}
            Auto schedule - Normal           
          {% elif states('sensor.vtr300_modus_status_register')|int == 0 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 4 %}
            Auto schedule - Høy                       
          {% elif states('sensor.vtr300_modus_status_register')|int == 1 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 0 %}
            Manuell STOPP 
          {% elif states('sensor.vtr300_modus_status_register')|int == 1 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 1 %}
            Manuell Shitstorm? 
          {% elif states('sensor.vtr300_modus_status_register')|int == 1 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 2 and is_state('input_boolean.vtr300_ppm_mode_auto_enable_modus_status_dummy', 'off') %}
            Manuell Lav  
          {% elif states('sensor.vtr300_modus_status_register')|int == 1 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 3 and is_state('input_boolean.vtr300_ppm_mode_auto_enable_modus_status_dummy', 'off') %}
            Manuell Normal
          {% elif states('sensor.vtr300_modus_status_register')|int == 1 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 4 and is_state('input_boolean.vtr300_ppm_mode_auto_enable_modus_status_dummy', 'off') %}
            Manuell Høy                     
          {% elif states('sensor.vtr300_modus_status_register')|int == 3  %}
            Boost
          {% elif states('sensor.vtr300_modus_status_register')|int == 2  %}
            Party
          {% elif states('sensor.vtr300_modus_status_register')|int == 4  %}
            Ildsted
          {% elif states('sensor.vtr300_modus_status_register')|int == 5  %}
            Borte
          {% elif states('sensor.vtr300_modus_status_register')|int == 6  %}
            ferie
          {% elif states('sensor.vtr300_modus_status_register')|int == 7 %}
            Cooker
          {% elif states('sensor.vtr300_modus_status_register')|int == 8 %}
            Hood
          {% elif states('sensor.vtr300_modus_status_register')|int == 9 %}
            Støvsuger
          {% elif states('sensor.vtr300_modus_status_register')|int == 10 %}
            CDI1
          {% elif states('sensor.vtr300_modus_status_register')|int == 11 %}
            CDI2
          {% elif states('sensor.vtr300_modus_status_register')|int == 12 %}
            Komfyravtrekk          
          {% elif states('sensor.vtr300_modus_status_register')|int == 1 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 2 and is_state('input_boolean.vtr300_ppm_mode_auto_enable_modus_status_dummy', 'on') %}
            PPM Auto - Lav  
          {% elif states('sensor.vtr300_modus_status_register')|int == 1 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 3 and is_state('input_boolean.vtr300_ppm_mode_auto_enable_modus_status_dummy', 'on') %}
            PPM Auto - Normal
          {% elif states('sensor.vtr300_modus_status_register')|int == 1 and states('sensor.vtr300_manuell_modus_kommando_register')|int == 4 and is_state('input_boolean.vtr300_ppm_mode_auto_enable_modus_status_dummy', 'on') %}
            PPM Auto - Høy   
          {% elif states('sensor.vtr300_modus_status_register')|int == 3 and is_state('input_boolean.vtr300_ppm_mode_auto_enable_modus_status_dummy', 'on') %}
            PPM Auto - BOOST            
          {% else %}
            WTF
          {% endif %}    

      vtr300_manuell_button_modus_status:
        friendly_name: "Manuell button modus status"
        value_template: >-
          {% if states('sensor.vtr300_modus_status_register')|int == 0 and states('sensor.vtr300_manuell_modus_kommando_register')|int > 0 %}
            Auto schedule
          {% elif states('sensor.vtr300_modus_status_register')|int == 1 and states('sensor.vtr300_manuell_modus_kommando_register')|int > 0 and is_state('input_boolean.vtr300_ppm_mode_auto_enable_modus_status_dummy', 'off') %}
            Manuell
          {% elif states('sensor.vtr300_modus_status_register')|int == 1 and states('sensor.vtr300_manuell_modus_kommando_register')|int > 0 and is_state('input_boolean.vtr300_ppm_mode_auto_enable_modus_status_dummy', 'on') %}
            PPM Auto         
          {% elif states('sensor.vtr300_modus_status_register')|int > 0 and states('sensor.vtr300_manuell_modus_kommando_register')|int > 0 and is_state('input_boolean.vtr300_ppm_mode_auto_enable_modus_status_dummy', 'off') %}
            Standby            
          {% else %}
            Av
          {% endif %}     

input_number:
  vtr300_eco_offset_temp_sp:
    name: Eco offset
    min: 0
    max: 10
    step: 1
    mode: box
    unit_of_measurement: °C
    icon: mdi:temperature

  vtr300_ferie_modus_varighet_sp:
    name: Ferie modus - Ønsket varighet 
    min: 1
    max: 365
    step: 1
    mode: box
    unit_of_measurement: d
    icon: mdi:timelapse   

  vtr300_borte_modus_varighet_sp:
    name: Borte modus - Ønsket varighet
    min: 1
    max: 72
    step: 1
    mode: box
    unit_of_measurement: t
    icon: mdi:timelapse       

  vtr300_ildsted_modus_varighet_sp:
    name: Ildsted modus - Ønsket varighet
    min: 1
    max: 60
    step: 1
    mode: box
    unit_of_measurement: m
    icon: mdi:timelapse  

switch:
    - platform: template
      switches:
        vtr300_auto_modus:
          friendly_name: Auto modus 
          value_template: "{{ is_state('sensor.vtr300_modus_status', 'Auto schedule - Lav') or is_state('sensor.vtr300_modus_status', 'Auto schedule - Normal') or is_state('sensor.vtr300_modus_status', 'Auto schedule - Høy')  }}"
          turn_on:
            - service: script.vtr300_auto_modus
              data:
                action: "on"
          turn_off:
            service: script.vtr300_auto_dummy_av
            data:
              action: "off"

        vtr300_man_stopp_modus:
          friendly_name: Manuell stopp        
          value_template: "{{ is_state('sensor.vtr300_modus_status', 'Manuell STOPP') }}" 
          turn_on:
            - service: script.vtr300_man_stopp_modus
              data:
                action: "on"    
          turn_off:
            service: script.vtr300_man_stopp_dummy_av
            data:
              action: "off"
        vtr300_man_lav_modus:
          friendly_name: Lav hastighet        
          value_template: "{{ is_state('sensor.vtr300_modus_status', 'Manuell Lav') or is_state('sensor.vtr300_modus_status', 'Auto schedule - Lav') or is_state('sensor.vtr300_modus_status', 'PPM Auto - Lav') }}"
          turn_on:
            - service: script.vtr300_man_lav_modus  
              data:
                action: "on"
          turn_off:
            service: script.vtr300_man_lav_dummy_av
            data:
              action: "off"
        vtr300_man_normal_modus:
          friendly_name: Normal hastighet    
          value_template: "{{ is_state('sensor.vtr300_modus_status', 'Manuell Normal') or is_state('sensor.vtr300_modus_status', 'Auto schedule - Normal') or is_state('sensor.vtr300_modus_status', 'PPM Auto - Normal') }}"          
          turn_on:
            - service: script.vtr300_man_normal_modus 
              data:
                action: "on"    
          turn_off:
            service: script.vtr300_man_normal_dummy_av
            data:
              action: "off"              
        vtr300_man_hoy_modus:
          friendly_name: Høy hastighet       
          value_template: "{{ is_state('sensor.vtr300_modus_status', 'Manuell Høy') or is_state('sensor.vtr300_modus_status', 'Auto schedule - Høy') or is_state('sensor.vtr300_modus_status', 'PPM Auto - Høy') }}"           
          turn_on:
            service: script.vtr300_man_hoy_modus
            data:
              action: "on"    
          turn_off:
            service: script.vtr300_man_hoy_dummy_av
            data:
              action: "off"     
 
    - platform: modbus
      scan_interval: 10
      registers:
        - name: vtr300_eco_modus
          hub: VTR300
          slave: 1
          register: 2504
          command_on: 1  #Aktiver Eco drift basert pĂĽ temp. offset
          command_off: 0  #Deaktiver Eco drift
          verify_state: true
          register_type: holding
          verify_register: 2504
          state_on: 1
          state_off: 0

input_boolean:
  vtr300_auto_modus_status_dummy:
    name: VTR300 Auto modus status dummy
    icon: mdi:fan  
  vtr300_party_modus_status_dummy:
    name: VTR300 Party modus status dummy
    icon: mdi:fan
  vtr300_boost_modus_status_dummy:
    name: VTR300 Boost modus status dummy
    icon: mdi:fan
  vtr300_ildsted_modus_status_dummy:
    name: VTR300 Ildsted modus status dummy
    icon: mdi:fan
  vtr300_borte_modus_status_dummy:
    name: VTR300 Borte modus status dummy
    icon: mdi:fan
  vtr300_ferie_modus_status_dummy:
    name: VTR300 ferie modus status dummy
    icon: mdi:fan
  vtr300_man_stopp_modus_status_dummy:
    name: VTR300 Manuell Stopp modus status dummy
    icon: mdi:fan  
  vtr300_man_lav_modus_status_dummy:
    name: VTR300 Manuell Lav modus status dummy
    icon: mdi:fan  
  vtr300_man_normal_modus_status_dummy:
    name: VTR300 Manuell Normal modus status dummy
    icon: mdi:fan  
  vtr300_man_hoy_modus_status_dummy:
    name: VTR300 Manuell Høy modus status dummy
    icon: mdi:fan  
  vtr300_ppm_mode_auto_enable_modus_status_dummy:
    name: VTR300 PPM Auto Enable modus status dummy
    icon: mdi:fan  
script:
  vtr300_man_normal_modus:
    sequence:
      - service: input_boolean.turn_on
        entity_id: input_boolean.vtr300_man_normal_modus_status_dummy
#Aktiver manuell modus før hastighet
      - service: modbus.write_register
        data_template:
          hub: VTR300
          unit: 1
          address: 1161
          value: 2
      - service: switch.turn_off
        entity_id: switch.vtr300_man_stopp_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_man_lav_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_man_hoy_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_auto_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_party_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_boost_modus 
      - service: switch.turn_off
        entity_id: switch.vtr300_ildsted_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_borte_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_ferie_modus
      - delay: '00:00:02'
      - service: modbus.write_register
        data_template:
          hub: VTR300
          unit: 1
          address: 1130
          value: 3
  vtr300_man_normal_dummy_av:
    sequence:
      service: input_boolean.turn_off
      entity_id: input_boolean.vtr300_man_normal_modus_status_dummy
  vtr300_man_hoy_modus:
    sequence: 
      - service: input_boolean.turn_on
        entity_id: input_boolean.vtr300_man_hoy_modus_status_dummy
#Aktiver manuell modus før hastighet
      - service: modbus.write_register
        data_template:
          hub: VTR300
          unit: 1
          address: 1161
          value: 2
      - service: switch.turn_off
        entity_id: switch.vtr300_man_stopp_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_man_lav_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_man_normal_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_auto_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_party_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_boost_modus 
      - service: switch.turn_off
        entity_id: switch.vtr300_ildsted_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_borte_modus
      - service: switch.turn_off
        entity_id: switch.vtr300_ferie_modus
      - delay: '00:00:02'
      - service: modbus.write_register
        data_template:
          hub: VTR300
          unit: 1
          address: 1130
          value: 4
  vtr300_man_hoy_dummy_av:
    sequence:
      service: input_boolean.turn_off
      entity_id: input_boolean.vtr300_man_hoy_modus_status_dummy    
5 Likes

Welcome to Home assistant forum Glenn Martin Trevland.

This is amazing work, i am truly impressed what you have made.

It would have been realy cool if you could get the rest of the code presented also, maybe some of the fanzy dashboards you have there, iam shure there are many users that would apreciate it.

nice job ! can you send us the full config file ?

Wow. Great work. The configuration you posted worked “straight out of the box”. Thank you!

Can you please post your full configuration for Systemair (on Github maybe?). That screenshot sure looks awsome

1 Like

Hi,

Thanks and a happy new year!
All the config files for the VTR300 will be posted when finished, it will take some time due to regular work, family things and because the modbus integration is not straight forward and requires trail and error.
The schedule and fan compensation is now implemented in node red but are not fully finished yet.
Almost all setpoints readback (updating “input_number” and “input_datetime” if locally changed on the HMI panel) and modbus write will be handled through node red, if not it would require 112 automations only for the time schedule because each “hour” and “min” has its own register and because it is nice/best to have the correct value in HA if values are locally changed from the panel on the unit.

image

image

4 Likes

Any update on this one?

Slow progress due to lack of time and some serious RAM performance issues as the HA integrations are growing rapidly…
Not so much left dough.

This is done:

  • All Modbus R/W is moved to Node-Red.
  • Entities are now created in node red instead of loooong yaml files and to get better overview of the integration in general.
  • Most automations is moved to Node-Red.
  • Flow diagrams/pictures are adapted better.

This is to be done:

  • Integrate enable for each time schedule/channel + some testing to verify functionality.
  • Split Sch.1 and Sch.2 as the setup now is not mobile friendly
  • Look further into the generic thermostat.
  • Move the remaining automations to Node-Red.
  • This is what i recall at this time, there is probably a few more things.


image
image

MInd sharing the code even though it’s not complete?
Would love to see some fresh ideas. Have an older VSR300 unit in my apartment. To make it look neat used MQTT climate component

Now got a new VSR 500 in country house to play around. Just pasted your older code without going through yet. Get some errors like missing scripts e.t.c

P.S. Also what is the use for the compensation variable like the one vifte_hast_komp_sommer_max_temp?

The files are too big to share in the forum, send me a pm and i will send my previous config.
The fan compensation is not a must, i just added it mostly because it’s possible. It might be useful if the outdoor temp is extreemly cold as reducing the fan speed with the fan compensation will reduce the load on the el.heater (lower the cost if wanted) and maybe reduce possibility for condensation inside the ventilation unit if it’s very cold outside (a guess from my side). During the summer i believe the compensation works opposite of the winter compensation by increasing the fan speed if it is hot by setting the summer fan comp. (summer comp is not tested yet, only winter). I hope that answered your questions.

The compensation also helps with regulating the humidity. I have my temperature setpoint set at 25C and ECO offset at 10C so my heater doesn’t start until supply air falls to 15C. I don’t feel the cold draught even when directly under the supply valves and it is much cheaper to heat the air using your main heating system. But I use compensation to lower the volume of air when the temperatures fall below 0C to avoid drying up the house, as absolute humidity is very low in cold air. In the summer it can also be used to avoid high humidity buildup.

1 Like

Hi,

I have a SAVE VSR 300
To integrate it in HA i used an USB to RS485 stick (https://www.aliexpress.com/item/32428596578.html)

# configuration.yaml entry for a serial connection
modbus:
  name: hub1
  type: serial
  method: rtu
  port: /dev/ttyUSB0
  baudrate: 9600
  stopbits: 1
  bytesize: 8
  parity: E
  timeout: 10


#sensors.yaml

#MODBUS
- platform: modbus
  scan_interval: 100
  registers:
    - name: air_extract_air_temp
      hub: hub1
      slave: 1
      register: 12543
      register_type: holding
      unit_of_measurement: °C
      count: 1
      scale: 0.1
    #   data_type: float
      precision: 1
      
- platform: modbus
  scan_interval: 120
  registers:
    - name: air_outdoor_air_temp
      hub: hub1
      slave: 1
      register: 12101
      register_type: holding
      unit_of_measurement: °C
      count: 1
      scale: 0.1
    #   data_type: float
      precision: 1
      
- platform: modbus
  scan_interval: 40
  registers:
    - name: air_supply_air_temp
      hub: hub1
      slave: 1
      register: 12102
      register_type: holding
      unit_of_measurement: °C
      count: 1
      scale: 0.1
    #   data_type: float
      precision: 1      
      
- platform: modbus
  scan_interval: 35
  registers:
    - name: air_overheat_temp
      hub: hub1
      slave: 1
      register: 12107
      register_type: holding
      unit_of_measurement: °C
      count: 1
      scale: 0.1
    #   data_type: float
      precision: 1            
      
- platform: modbus
  scan_interval: 10
  registers:
    - name: air_supply_fan
      hub: hub1
      slave: 1
      register: 12400
      register_type: input
      unit_of_measurement: rpm
      count: 1
    #   scale: 0.1
    #   data_type: float
      precision: 1            
      
- platform: modbus
  scan_interval: 10
  registers:
    - name: air_extract_fan
      hub: hub1
      slave: 1
      register: 12401
      register_type: input
      unit_of_measurement: rpm
      count: 1
    #   scale: 0.1
    #   data_type: float
      precision: 1            
      
- platform: modbus
  scan_interval: 60
  registers:
    - name: air_rh_sensor
      hub: hub1
      slave: 1
      register: 12135
      register_type: holding
      unit_of_measurement: 
      count: 1
    #   data_type: float
      precision: 0

- platform: modbus
  scan_interval: 3
  registers:
    - name: hrv_active_user_mode
      hub: hub1
      slave: 1
      register: 1160
      register_type: input
      count: 1
    #   scale: 0.1
    #   data_type: float
      precision: 0

- platform: modbus
  scan_interval: 5
  registers:
    - name: hrv_active_fan_mode
      hub: hub1
      slave: 1
      register: 1130
      register_type: input
      count: 1
    #   scale: 0.1
    #   data_type: float
      precision: 0

- platform: modbus
  scan_interval: 5
  registers:
    - name: hrv_active_temperature
      hub: hub1
      slave: 1
      register: 2053
      register_type: input
      count: 1
      scale: 0.1
    #   data_type: float
      precision: 0

- platform: modbus
  scan_interval: 9
  registers:
    - name: triac_output
      hub: hub1
      slave: 1
      register: 2148
      register_type: input
      unit_of_measurement: 
      count: 1
      # data_type: binary
      precision: 0
            
- platform: modbus
  scan_interval: 7
  registers:
    - name: heater_active
      hub: hub1
      slave: 1
      register: 14380
      register_type: input
      # unit_of_measurement: 
      count: 1
      # data_type: binary
      precision: 0
      
- platform: modbus
  scan_interval: 11
  registers:
    - name: exchanger_active
      hub: hub1
      slave: 1
      register: 14103
      register_type: input
      # unit_of_measurement: 
      count: 1
      # data_type: binary
      precision: 0

- platform: modbus
  scan_interval: 10
  registers:
    - name:  humidity_transfer
      hub: hub1
      slave: 1
      register: 2146
      register_type: input
      count: 1
      precision: 0

NodeRed:

[{"id":"d420ee28.a1795","type":"tab","label":"HRV","disabled":false,"info":""},{"id":"95a074eb.9f83c8","type":"server-state-changed","z":"d420ee28.a1795","name":"desired_user_mode","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_select.hrv_desired_user_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":190,"y":80,"wires":[["13548516.dd7a7b"]]},{"id":"13548516.dd7a7b","type":"function","z":"d420ee28.a1795","name":"","func":"switch(msg.payload){\n    case \"None\":\n        msg.payload = 0;\n        break;\n    case \"Auto\":\n        msg.payload = 1;\n        break;\n    case \"Manual\":\n        msg.payload = 2;\n        break;\n    case \"Crowded\":\n        msg.payload = 3;\n        break;\n    case \"Refresh\":\n        msg.payload = 4;\n        break;\n    case \"Fireplace\":\n        msg.payload = 5;\n        break;\n    case \"Away\":\n        msg.payload = 6;\n        break;\n    case \"Holiday\":\n        msg.payload = 7;\n        break;\n    default:\n        msg.payload = 0;\n}\n\n//msg.payload = { value: msg.payload, 'fc': 6, 'unitid': 1, 'address': 1161 , 'quantity': 1 };\n\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":80,"wires":[["c4001308.2278c"]]},{"id":"c4001308.2278c","type":"api-call-service","z":"d420ee28.a1795","name":"Set desired_user_mode","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"modbus","service":"write_register","entityId":"","data":"{\"address\":1161,\"unit\":1,\"value\":\"{{payload}}\",\"hub\":\"hub1\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":550,"y":80,"wires":[[]]},{"id":"e688476d.119478","type":"comment","z":"d420ee28.a1795","name":"Set desired_user_mode to HRV","info":"","x":230,"y":40,"wires":[]},{"id":"e5eb1534.546788","type":"server-state-changed","z":"d420ee28.a1795","name":"active_user_mode","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.hrv_active_user_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":790,"y":540,"wires":[["7f43431e.622dfc"]]},{"id":"7f43431e.622dfc","type":"function","z":"d420ee28.a1795","name":"","func":"switch(msg.payload){\n    case \"0\":\n        msg.payload = \"Auto\";\n        break;\n    case \"1\":\n        msg.payload = \"Manual\";\n        break;\n    case \"2\":\n        msg.payload = \"Crowded\";\n        break;\n    case \"3\":\n        msg.payload = \"Refresh\";\n        break;\n    case \"4\":\n        msg.payload = \"Fireplace\";\n        break;\n    case \"5\":\n        msg.payload = \"Away\";\n        break;\n    case \"6\":\n        msg.payload = \"Holiday\";\n        break;\n    case \"7\":\n        msg.payload = \"Cooker Hood\";\n        break;\n    case \"8\":\n        msg.payload = \"Vacuum Cleaner\";\n        break;\n    case \"9\":\n        msg.payload = \"CDI1\";\n        break;\n    case \"10\":\n        msg.payload = \"CDI2\";\n        break;\n    case \"11\":\n        msg.payload = \"CDI3\";\n        break;\n    case \"12\":\n        msg.payload = \"PressureGuard\";\n        break;\n    default:\n        msg.payload = \"Error\";\n}\n\n//msg.payload = { value: msg.payload, 'fc': 6, 'unitid': 1, 'address': 1161 , 'quantity': 1 };\n\nreturn msg;","outputs":1,"noerr":0,"x":1020,"y":540,"wires":[["bd6163e7.6f971"]]},{"id":"c20c703e.58438","type":"comment","z":"d420ee28.a1795","name":"Update UI based on HRV info","info":"Some mode are active for a specific time. After this period USER_MODE return to previous value.","x":220,"y":480,"wires":[]},{"id":"bd6163e7.6f971","type":"api-call-service","z":"d420ee28.a1795","name":"Set desired_user_mode","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"input_select","service":"select_option","entityId":"input_select.hrv_desired_user_mode","data":"{\"option\":\"{{payload}}\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1230,"y":540,"wires":[[]]},{"id":"54e9ade.53c3854","type":"server-state-changed","z":"d420ee28.a1795","name":"fan_mode","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_select.hrv_fan_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":160,"y":200,"wires":[["635f286f.219e38"]]},{"id":"635f286f.219e38","type":"function","z":"d420ee28.a1795","name":"","func":"switch(msg.payload){\n    case \"Off\":\n        msg.payload = 0;\n        break;\n    case \"Low\":\n        msg.payload = 2;\n        break;\n    case \"Normal\":\n        msg.payload = 3;\n        break;\n    case \"High\":\n        msg.payload = 4;\n        break;\n    default:\n        msg.payload = 3;\n}\n\n//msg.payload = { value: msg.payload, 'fc': 6, 'unitid': 1, 'address': 1161 , 'quantity': 1 };\n\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":200,"wires":[["99d27bff.ee2238"]]},{"id":"99d27bff.ee2238","type":"api-call-service","z":"d420ee28.a1795","name":"Set fan_mode","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"modbus","service":"write_register","entityId":"","data":"{\"address\":1130,\"unit\":1,\"value\":\"{{payload}}\",\"hub\":\"hub1\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":520,"y":200,"wires":[[]]},{"id":"74c6591d.222978","type":"comment","z":"d420ee28.a1795","name":"Set fan_mode to HRV","info":"","x":200,"y":160,"wires":[]},{"id":"431ab7dc.42d818","type":"server-state-changed","z":"d420ee28.a1795","name":"active_fan_mode","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.hrv_active_fan_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":780,"y":660,"wires":[["1c190b23.960205"]]},{"id":"1c190b23.960205","type":"function","z":"d420ee28.a1795","name":"","func":"switch(msg.payload){\n    case \"0\":\n        msg.payload = \"Off\";\n        break;\n    case \"2\":\n        msg.payload = \"Low\";\n        break;\n    case \"3\":\n        msg.payload = \"Normal\";\n        break;\n    case \"4\":\n        msg.payload = \"High\";\n        break;\n    default:\n        msg.payload = \"Error\";\n}\n\n//msg.payload = { value: msg.payload, 'fc': 6, 'unitid': 1, 'address': 1161 , 'quantity': 1 };\n\nreturn msg;","outputs":1,"noerr":0,"x":1020,"y":660,"wires":[["dc35d906.905f18"]]},{"id":"dc35d906.905f18","type":"api-call-service","z":"d420ee28.a1795","name":"Set desired_fan_mode","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"input_select","service":"select_option","entityId":"input_select.hrv_fan_mode","data":"{\"option\":\"{{payload}}\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1220,"y":660,"wires":[[]]},{"id":"bc2e9915.41f348","type":"server-state-changed","z":"d420ee28.a1795","name":"temperature","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_number.hrv_temperature","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":170,"y":320,"wires":[["6656e916.035688"]]},{"id":"6656e916.035688","type":"function","z":"d420ee28.a1795","name":"","func":"msg.payload = msg.payload * 10;\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":320,"wires":[["7e0d4f49.6c978"]]},{"id":"7e0d4f49.6c978","type":"api-call-service","z":"d420ee28.a1795","name":"Set temperature","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"modbus","service":"write_register","entityId":"","data":"{\"address\":2000,\"unit\":1,\"value\":\"{{payload}}\",\"hub\":\"hub1\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":520,"y":320,"wires":[[]]},{"id":"49be9ca4.80aef4","type":"comment","z":"d420ee28.a1795","name":"Set Temperature to HRV","info":"","x":210,"y":280,"wires":[]},{"id":"2a011ba.bae88e4","type":"server-state-changed","z":"d420ee28.a1795","name":"active_temperature","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.hrv_active_temperature","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":790,"y":780,"wires":[["d9027d79.df8c7"]]},{"id":"d9027d79.df8c7","type":"function","z":"d420ee28.a1795","name":"","func":"\nreturn msg;","outputs":1,"noerr":0,"x":1020,"y":780,"wires":[["e1768475.c79418"]]},{"id":"e1768475.c79418","type":"api-call-service","z":"d420ee28.a1795","name":"Set desired_active_temperature","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"input_number","service":"set_value","entityId":"input_number.hrv_temperature","data":"{\"value\":\"{{payload}}\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1250,"y":780,"wires":[[]]},{"id":"8849a34f.df6b4","type":"api-current-state","z":"d420ee28.a1795","name":"hrv_active_temperature","server":"6cc1c546.4430dc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"sensor.hrv_active_temperature","state_type":"num","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":810,"y":720,"wires":[["d9027d79.df8c7"]]},{"id":"ae5bc303.c2a4f","type":"api-current-state","z":"d420ee28.a1795","name":"active_user_mode","server":"6cc1c546.4430dc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"sensor.hrv_active_user_mode","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":790,"y":480,"wires":[["7f43431e.622dfc"]]},{"id":"160ed3f2.3e675c","type":"api-current-state","z":"d420ee28.a1795","name":"active_fan_mode","server":"6cc1c546.4430dc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"sensor.hrv_active_fan_mode","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":790,"y":600,"wires":[["1c190b23.960205"]]},{"id":"bde452f0.74f07","type":"server-events","z":"d420ee28.a1795","name":"","server":"6cc1c546.4430dc","event_type":"home_assistant_client","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"x":220,"y":600,"wires":[["57a15fbe.f93f6"]]},{"id":"57a15fbe.f93f6","type":"switch","z":"d420ee28.a1795","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"running","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":410,"y":600,"wires":[["f44558b1.ee8f48"]]},{"id":"f44558b1.ee8f48","type":"delay","z":"d420ee28.a1795","name":"","pauseType":"delay","timeout":"10","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":540,"y":600,"wires":[["ae5bc303.c2a4f","160ed3f2.3e675c","8849a34f.df6b4"]]},{"id":"6cc1c546.4430dc","type":"server","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

HA (basic info):

Maybe someone will help us with graphical interface :slight_smile:

1 Like

Hi! What modbus are you using?

Hi,
I’m using Modbus TCP/IP.
Ethernet directly from the IAM gateway to my switch.
More or less the same as Modbus RTU except that Modbus RTU is a serial interface. Modbus addr. are the same. (there is some advantages using modbus TCP/IP, i’m not an expert, just mentioning it.).
If i didn’t have the IAM gateway, i would use the Modbus RTU. But it all depends on whats easiest for each installation.

1 Like

Bra jobb! :+1:

Honestly I can’t even call myself new in the HA community since I made an account several minutes ago :sweat_smile:

I bumped into this topic when I tried to look into the possibility to connect IAM to Smartthings. The whole idea of even purchasing IAM is the possibility to hook it up to a movement/water sensor in bathroom so each time we take a shower ventilation goes automatically to Boost.

Systemair not offering something like this is very disturbing, and that cloud server is like an island, nothing outside can get connected to it. But Modbus is another option of course. However, I’m kinda reluctant to spend over 2000 NOK (200€) on a device if I’m not going to use it’s main functionality, cloud!

So I was thinking something like a RS485 to Wifi adapter and connect it to that top JB plug, or even as simple as RS485 to ethernet RJ45 converter. Is this doable?

But I’m long way from there! I guess first I have to read and master some basics of HA. Then actually install HA on Raspberry PI presumably and go forward from there.

Just an extremely noob question, you are able to control your vent on both HA Android app and Windows 10 app or web server? And no problem to integrate another sensor in the loop, like movement detecttor as mentioned?

As an automation engineer I’m absolutely ashamed I have to ask such noob questions but you have to start somewhere! :sweat_smile:

Hi,

Welcome, I’m quite a noob with HA myself but i’ll try to help out:)
RS485 to Wifi
I have no experience using RS485 --> Wifi, but i see there there is RS485/Modbus --> Wifi/Ethernet converters, someone else may have experience? I would prefer wired interface, there is RS485–>USB converters available, which seems to be common to use. If i didn’t have the IAM, the RS485–>USB would be my approach.
Other sensors
I’m using netatmo ppm sensor and device tracker to control my VTR300, so there is no problem using other sensors to control the ventilation unit.
Humidity control
There is already a demand control in the unit where it uses it’s own humidity(RH) sensor by increasing the fan speed based on the RH setpoint vs actual RH, maybe that feature is good enough?
I was thinking of trying out that feature, but so far i dont find it nessecary in my case as the humidity from the bathrom already is vented quick and efficient without the humidity control enabled.
Remote control
I’m using the Nabu Casa HA app for remote control, i dont mind paying 5 dollar a month for such a great free open source and well supported integration system, there is others ways to this free but you have to op en a port on your router. My HA runs inside Virtualbox on a 10 year old Win10 laptop, wich soon is to be relocated to a Intel Nuc with Linux and Ubuntu UI.
I hope that answers some of your questions.

That’s what I meant, Modbus RTU/RS485 to Modbus TCP/WiFi :slight_smile:
Effectively, that should be the same thing as with IAM.
RS485 to USB converters are extremely cheap, maybe a bit inconvenient since Pi would have to be placed near the ventilation unit.

Btw. I decided to give IAM a chance and purchased it yesterday! Frankly quite dissapointed. Setup is super easy, however the cloud solution is average at best. Really slow refresh rate and quite basic. It’s a totally closed system that doesn’t let anything in/out, meaning, no possibility for 3rd pary sensor integration for programming an event based boost function. And if you wan’t that, you need to set to Modbus/TCP but than you lose the cloud functionality that you’re actually paying such a hefty price for!
There’s no universe in which this thing is worth 200€ and I’m happily returning it early next week … at least I got my FW’s updated :slight_smile:

The RH sensor you’re refering to is placed on the exhaust (avtrekk) line. I tested this function and I’m not sure if it’s good. In my case RH was ca 35% inside, with 50% (winter) setpoint. Checking the fan speed in rpm it was set to approx. low. Following some logic, it should measure both inside and outside RH, compare it with setpoint, and if you want to increase it, like in my case, auto mode should provide more flow and eventually lowering the setpoint for inlet air heating to prevent blowing somewhat dried air.

Thanks a lot! I see that I should start with some basics of HA first, for example I don’t even know what API is! From programming languages, I’m familiar with general concepts, C and PLC programming (in which I’m very experienced), but that’s a bit specific. That’s it, I don’t have a clue about object based programming for example.

So, I have a huge wish to learn something and start actually making something useful, just need some time, not so easy with a small child and one more coming soon, but I’ll hope I’ll find some, because this looks like so much fun!

If you don’t mind sharing, do you have background in coding? Or did you just start with it? I hope I can find all the things I need on HA page, not having to wonder arround.

Yes, you can retrieve the same data from both the RS485 serial interface and Modbus TCP using the IAM gateway. If only using the IAM cloud feature you will have “full” control and can change most parameters, functionality/features etc. so you will have more or less full control using the systemair app, but without any options as you say with integrating the system with other systems… If the app is running in the background and it’s being re-opened, the data/values was not updated, maybe that is fixed in the latest release, I’m not sure since I’m not using it.

It would expect that the built in humidity demand control regards the outdoor humidity as well (ref. modbus adr. List as its referring to calculated outdoor humidity as well), don’t have more to say about the feature since I’m not using it but I would expect at least the fan level to respond well in humidity changes to lower the indoor humidity.

Having the same background as you, both HA is and Node Red recently was totally new for me as well, the background is helping a lot. But it’s a new language to learn anyway, and yes it’s fun:) (but only if you have the time available).

If you check the post above from 2’nd December 2020, you will get an idea of how the Modbus integration works in HA. Since I wanted to have HA to be updated if settings was updates locally on the unit panel I had to create automations for each parameter, which would result in a lot of automations (especially the Auto schedule integration), I decided to move the integration to node red (running as a HA addon) which I prefer when writing automations.

My ventilation unit is integrated and works well but I have a few more details to finish before the code I shared on github. Im a little to hung up in details and don’t have much time available so it may take some time…

The main struggle is find the correct modbus registers to read, since the modbus adr. list has flaws… But that should be 98% sorted out now, now I need to find the registers for the Auto schedule Fan speed, though I had that, but that was wrong… And the timestamp of last filter change also has some strange behavior I’m not figuring out, tried byteswapping the registers but it seems like the register isn’t being updated when replacing the filter, maybe a bug or maybe it needs to be set by external command, not sure… (time to next filter change is OK) The timestamp issue is now solved by providing the actual timestamp when the filter alarm is deactivated and it logs the 10 latest filter changes with actual timestamp. Since I’m a noob using Node Red there was a few things to figure out how to make this possible.The function node code (node red) is shared below, as I believe you are curious about that:)
This function node is triggered by falling edge using the filteralarm as input.
There is more, but it will be shared later some at some point.

I don’t mind sharing the code (old HA config and latest Node Red config), send me a pm and I’ll send the code in return since it’s too much to share in the forum (not possible to share due to the size…).

Filter timestamp code in HA: (edit post/forgot this)
image
Filter timestamp code in node red:

var Recent_change = global.get ("TimeNow_YearDateHour"); 
//Flytter siste filterskift til forrige filterskifte
var Previous_change_1 = flow.get ("vtr300_forrige_filterskift_0","storeInFile");
var Previous_change_2 = flow.get ("vtr300_forrige_filterskift_1","storeInFile");
var Previous_change_3 = flow.get ("vtr300_forrige_filterskift_2","storeInFile");
var Previous_change_4 = flow.get ("vtr300_forrige_filterskift_3","storeInFile");
var Previous_change_5 = flow.get ("vtr300_forrige_filterskift_4","storeInFile");
var Previous_change_6 = flow.get ("vtr300_forrige_filterskift_5","storeInFile");
var Previous_change_7 = flow.get ("vtr300_forrige_filterskift_6","storeInFile");
var Previous_change_8 = flow.get ("vtr300_forrige_filterskift_7","storeInFile");
var Previous_change_9 = flow.get ("vtr300_forrige_filterskift_8","storeInFile");
var Previous_change_10 = flow.get ("vtr300_forrige_filterskift_9","storeInFile");
var Previous_change_0 = flow.get ("vtr300_siste_filterskift","storeInFile");

//Initilialiser verdi dersom udefinert
if (typeof Previous_change_0 == "undefined") //Forrige_0
Previous_change_0 = 'Not set';
if (typeof Previous_change_1 == "undefined") //Forrige_1
Previous_change_1 = 'Not set';
if (typeof Previous_change_2 == "undefined") //Forrige_2
Previous_change_2 = 'Not set';
if (typeof Previous_change_3 == "undefined") //Forrige_3
Previous_change_3 = 'Not set';
if (typeof Previous_change_4 == "undefined") //Forrige_4
Previous_change_4 = 'Not set';
if (typeof Previous_change_5 == "undefined") //Forrige_5
Previous_change_5 = 'Not set';
if (typeof Previous_change_6 == "undefined") //Forrige_6
Previous_change_6 = 'Not set';
if (typeof Previous_change_7 == "undefined") //Forrige_7
Previous_change_7 = 'Not set';
if (typeof Previous_change_8 == "undefined") //Forrige_8
Previous_change_8 = 'Not set';
if (typeof Previous_change_9 == "undefined") //Forrige_9
Previous_change_9 = 'Not set';
if (typeof Previous_change_10 == "undefined") //Forrige_10
Previous_change_10 = 'Not set';

//Lagrer status til fil for ĂĽ beholde status "permanent"
flow.set("vtr300_forrige_filterskift_0", Previous_change_0,"storeInFile");
flow.set("vtr300_forrige_filterskift_1", Previous_change_1,"storeInFile");
flow.set("vtr300_forrige_filterskift_2", Previous_change_2,"storeInFile");
flow.set("vtr300_forrige_filterskift_3", Previous_change_3,"storeInFile");
flow.set("vtr300_forrige_filterskift_4", Previous_change_4,"storeInFile");
flow.set("vtr300_forrige_filterskift_5", Previous_change_5,"storeInFile");
flow.set("vtr300_forrige_filterskift_6", Previous_change_6,"storeInFile");
flow.set("vtr300_forrige_filterskift_7", Previous_change_7,"storeInFile");
flow.set("vtr300_forrige_filterskift_8", Previous_change_8,"storeInFile");
flow.set("vtr300_forrige_filterskift_9", Previous_change_9,"storeInFile");
flow.set("vtr300_forrige_filterskift_10", Previous_change_10,"storeInFile");
flow.set("vtr300_siste_filterskift", Recent_change,"storeInFile");

msg.payload = Previous_change_0;
return msg;




1 Like

Thanks a lot!

It’s funny how I understand in principle the stuff you’re talking about, but don’t have a slightest clue about implementation … if only it would be as “easy” as programming Siemens PLC hehe :slight_smile:

I don’t even have the slightest clue what “yaml” and “code red” even means, so I guess I have a loong way to go, hope I’ll be able to find some time soon, we moved into the newbuilt house several weeks ago and now the things are starting to settle in place finally!

I do have some experience with both Modbus RTU/TCP but everything through S7/Tia Portal so basically you already get the user freindly blocks to connect. But really, in industrial/marine/offshore automation Profibus and more and more Ethernet are the way to go!

Thanks a lot for you willing to share the code! I’m sure I’ll get back to you once I master the basics and have some clue :slight_smile:

Oh! And one general tip regrding the vent unit filters. There is a guy I know that was working for one of those ventilation companies (can’t remember which one but it was the one I had before in appartment, Flexit maybe?) and he revealed the dirty little secret, which I kinda already assumed.

You can wash/vacume/blow the filters and as long as the fabric is intact you can re-use them many many times over. It was allways suspicious to me why every single company is pointing out that they are one-time use … well, for 50-60€ it surely is a very lucrative business to them :wink:
I think the same applies to Systemair.

And regarding Systemair, I find their business practices extremely lame! In order to connect wirelessly you need to spit out 200€, their cloud is an island, closed system and their sulutions are quite lame as well! They do have a motion sensor, but guess what, only the wired option, for a hefy price of some 150€ as I remember. And Modbus TCP functionality can be achieved with 20€ adapter, Modbus RTU to USB for 1€! And that’s probably the way I’ll be aiming. Pi with HA on it could be placed on the top of vent unit, why not :slight_smile:

Delivered power + energy & Outdoor compensated supply temperature:

Delivered power:
From i got the unit installed late last summer i somehow wondered how much power and energy the unit deliver compared to the power and enrgy it uses. After some googling it seemed like “Sensible heat” is a way of calculating delivered power, after some head scratching/Node Red learning i managed to make the calculations. This is a new area for me so id like to share my findings/way of estimate added power+energy and true added power+energy. I was looking for a estimate of how efficient the unit is, by that i have calculated estimated delivered kW/10s and accumulate kW/10s each 10 sec. To give the “true delivered” energy i just subtracted the energy consumption. Do anyone have the knowledge if “Sensible heat” and “Energy consumption” is comparable?
Code etc. shared further below.
Ref. pictures below: The accumulation was not reset during last night and continued counting from yesterdays value because the Windows host rebooted at about 23:00 yesterday… Thats why the reset failed and there is a straight line in the trends. To be “fixed” by relocating HA to Intel Nuc sometime in the future.

Outdoor compensated supply temperature:
Instead of manually setting the temperature setpoint Id like the supply temperature to be adjusted automatically according to outdoor temperature changes, its probably not gona have much to say with the indoor temperature but it will prevent the heat exchanger from running when it’s not nessecary and maybe extend its lifetime. So i did some plots on the calculator (Stat) and made the calculator find the formula and constants. Then some more head scratching/Node Red learning had to be done. What i want is to have the X and Y plots as temperature setpoints, does anyone know how this is achievable with Node Red?
Code below:

Delivered power:
image
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" (opretting av Modbus kom./feil 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_tilført_Watt", hs*1000,"default");  // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_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_tilført_Watt_formated", (hs*1000).toFixed(0) +' Watt',"default");  // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWatt_formated", (hs).toFixed(2) +' kW',"default");  // Lagrer tilført kW til "memory/default"

return [watt,kW,kW_10s];

Delivered energy:
image

// Polles hvert 10s pga. payload = "kW/10s"

var kW_10s = msg.payload;
var Previous_kWh_tot = flow.get ("vtr300_tilført_kWh_cnt_tot","default"); // Total eneregiteller - Flytter siste akkumulerte verdi til forrige verdi
var Previous_kWh_tot_daily = flow.get ("vtr300_tilført_kWh_cnt_tot_daily","default"); // Daglig energi
var Previous_kWh_tot_weekly = flow.get ("vtr300_tilført_kWh_cnt_tot_weekly","default"); // Ukentlig energi
var Previous_kWh_tot_monthly = flow.get ("vtr300_tilført_kWh_cnt_tot_monthly","default"); // Münedlig energi
var Previous_kWh_tot_yearly = flow.get ("vtr300_tilført_kWh_cnt_tot_yearly","default"); // Årlig energi

var kWh_tot;
var kWh_tot_daily;
var kWh_tot_weekly;
var kWh_tot_monthly;
var kWh_tot_yearly;

// Totalteller energi - Initilialiser verdi dersom udefinert/første gang verdi settes
if (Previous_kWh_tot == undefined) { 
  kWh_tot = 0.0;
}
// Totalteller energi - Akkumulerer forrige verdi med ny verdi
else {
  kWh_tot = Previous_kWh_tot + kW_10s;
}

// Daglig energi - Initilialiser verdi dersom udefinert/første gang verdi settes
if (Previous_kWh_tot_daily == undefined) { 
  kWh_tot_daily = 0.0;
}
// Daglig energi - Akkumulerer forrige verdi med ny verdi
else {
  kWh_tot_daily = Previous_kWh_tot_daily + kW_10s;
}

// Ukentlig energi - Initilialiser verdi dersom udefinert/første gang verdi settes
if (Previous_kWh_tot_weekly == undefined) { 
  kWh_tot_weekly = 0.0;
}
// Ukentlig energi - Akkumulerer forrige verdi med ny verdi
else {
  kWh_tot_weekly = Previous_kWh_tot_weekly + kW_10s;
}

// Münedlig energi - Initilialiser verdi dersom udefinert/første gang verdi settes
if (Previous_kWh_tot_monthly == undefined) { 
  kWh_tot_monthly = 0.0;
}
// MĂĽnedlig energi - Akkumulerer forrige verdi med ny verdi
else {
  kWh_tot_monthly = Previous_kWh_tot_monthly + kW_10s;
}

// Årlig energi - Initilialiser verdi dersom udefinert/første gang verdi settes
if (Previous_kWh_tot_yearly == undefined) { 
  kWh_tot_yearly = 0.0;
}
// Årlig energi - Akkumulerer forrige verdi med ny verdi
else {
  kWh_tot_yearly = Previous_kWh_tot_yearly + kW_10s;
}

//Oppdaterer med nyeste akkumulerte verdi (Scantid mĂĽ vĂŚre 10s pga. mottatt verdi er kW/10s)
flow.set("vtr300_tilført_kWh_cnt_tot", kWh_tot,"default");                 // Total eneregiteller - Akkumulering i "memory/default", restore fra "file"
flow.set("vtr300_tilført_kWh_cnt_tot_daily", kWh_tot_daily,"default");     // Daglig energi - Akkumulert
flow.set("vtr300_tilført_kWh_cnt_tot_weekly", kWh_tot_weekly,"default");   // Ukentlig energi - Akkumulert
flow.set("vtr300_tilført_kWh_cnt_tot_monthly", kWh_tot_monthly,"default"); // Mý�nedlig energi - Akkumulert
flow.set("vtr300_tilført_kWh_cnt_tot_yearly", kWh_tot_yearly,"default");   // Årlig energi - Akkumulert

var kW_10s_out = {payload:kW_10s};
var kWh_tot_daily_out = {payload:(kWh_tot_daily).toFixed(2)};
var kWh_tot_weekly_out = {payload:(kWh_tot_weekly).toFixed(2)};
var kWh_tot_monthly_out = {payload:(kWh_tot_monthly).toFixed(2)};
var kWh_tot_yearly_out = {payload:(kWh_tot_yearly).toFixed(2)};
var kWh_tot_out = {payload:(kWh_tot).toFixed(2)};

// Tilpasser desimaler og benevnelse for attribute visning i HA
flow.set("vtr300_tilført_kWh_cnt_tot_formated", (kWh_tot).toFixed(2) +' kWh',"default");  // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWh_cnt_tot_daily_formated", (kWh_tot_daily).toFixed(2) +' kWh',"default");  // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWh_cnt_tot_weekly_formated", (kWh_tot_weekly).toFixed(2) +' kWh',"default");  // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWh_cnt_tot_monthly_formated", (kWh_tot_monthly).toFixed(2) +' kWh',"default");  // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWh_cnt_tot_yearly_formated", (kWh_tot_yearly).toFixed(2) +' kWh',"default");  // Lagrer tilført kW til "memory/default"

//return [kW_10s_out,kWh_tot_daily_out,kWh_tot_weekly_out,kWh_tot_monthly_out,kWh_tot_yearly_out,kWh_tot_out];

True power:

var watts_consumption = msg.payload;
var Watt_delivered = flow.get ("vtr300_tilført_Watt","default");// Tilført effekt Watt estimert
var kWatt_delivered = flow.get ("vtr300_tilført_kWatt","default"); // Tilført effekt kW estimert

// Faktisk effekt produsert (produsert - levert)
var true_produced_Watts = Watt_delivered - watts_consumption;
var true_produced_kW = kWatt_delivered - (watts_consumption/1000);

// Faktisk effekt produsert, tilpasse data ut
var true_produced_Watts_out = {payload:(true_produced_Watts).toFixed(0)};
var true_produced_kW_out = {payload:(true_produced_kW).toFixed(2)};

return [true_produced_Watts_out,true_produced_kW_out];

Outdoor compensated supply temperature:


var x= msg.payload; // utetemp.

// X (ute temp) og Y (ønsket tilluft) plot:
//  X1 :7,5,	Y1 :22,0
//  X2 :10,0,	Y2 :20,0
//  X3 :12,5,	Y3 :18,0
//  X4 :15,0,	Y4 :17,0
//  X5 :17,5,	Y5 :15,0
//  X6 :20,0,	Y6 :15,0

//Fjerdgradslikning for best treff av X og Y plot
//  𝑦=𝑎𝑥^4+𝑏𝑥^3+𝑐𝑥^2+𝑑𝑥+𝑒
// Graf & konstanter ses ved ĂĽ plotte X & Y pĂĽ kalkulator med "Stat"->"Grph"->"Gph"->"X^4"

//gitte variable basert pĂĽ X og Y plot:
var a =	(1.0666e-03);
var b =	(-0.0562962);
var c =	(1.09555555);
var d =	(-9.9465608);
var e =	(55.3809523);
var y; //Ønsket tilluftstemp.

//Nødvendige regler for ønsket funksjosnalitet (pga. avvik utenfor ytterpunker)
if (x <= 7.5) { 
  y = 22.0;
}

else if (x >= 20.0) { 
  y= 15.0;
}

// Beregner plot
else {
  y = (a * Math.pow(x, 4)+b*Math.pow(x, 3)+c*Math.pow(x,2)+d*x+e);
}

msg.payload = parseFloat((y).toFixed(1));
return msg;