Zwift/heartrate/power controlled fan

I use to use a traininig/virtual cycling app called zwift and as anyone who ever tried a stationary bike knows you sweat a lot, so a fan should be part of the kit :-). On the winter I don’t need the fan on the first few minutes but later I need. I was used to use a wifi socket switch so I could turn on/off the fan without have to get off the bike. A few weeks ago I found a post about controlling a fan based on values you could obtain from the game API, like heart rate, speed or power! So this is my take on doing the same, but putting HA in the middle.

The first step was to make my 3 speed fan “smarter” and it was easier than i thought thanks to sonoff 4ch pro. This part is documented here: Tasmota the desktop fan

To interface with the game API this guy made a nodejs client which was later “translated” to python by this other project. Now I should only “glue” the things together. I wrote a small python zwift-mqtt.xml (2.0 KB)
which use the python zwift client to get game data and send it to a mqtt topic which is read using the mqtt and template sensors component:

sensor:
 - platform: mqtt
   name: zwift
   state_topic: "home/zwift/state"
   value_template: '{{ value_json.is_online }}'
   availability_topic: "home/zwift/LWT"
   payload_available: "Online"
   payload_not_available: "Offline"
   json_attributes:
     - is_online
     - hr
     - speed
     - power

 - platform: template
   sensors:
     zwift_hr:
       value_template: '{{ states.sensor.zwift.attributes["hr"] }}'
     zwift_speed:
       value_template: '{{ states.sensor.zwift.attributes["speed"] }}'
     zwift_power:
       value_template: '{{ states.sensor.zwift.attributes["power"] }}'

binary_sensor:
  - platform: template
    sensors:
      zwift_online:
        value_template: "{{ states('sensor.zwift')|int > 0 }}"

This project also introduced a nice idea about how to control the fan, which is to only activate it after a given heart rate (so you “warmed up” enough) and then select the speed of the fan based on how much force you are applying to the pedals (power). To make it easier to select those parameters I just added a few input_number sliders as this:

input_number:
    min_heartrate:
      name: Freq. Cardiaca Min.
      min: 50
      max: 150
      step: 1
    power_level1:
      name: Potencia L1
      min: 0
      max: 800
      step: 1
    power_level2:
      name: Potencia L2
      min: 0
      max: 800
      step: 1
    power_level3:
      name: Potencia L3
      min: 0
      max: 800
      step: 1

And finally I could just configure the needed automations to turn on/off and select the speed during the workout:

automation:
   - alias: liga ventilador no nivel baixo
     trigger:
       platform: template
       value_template: "{{ (states('sensor.zwift_power')|int >= states('input_number.power_level1')|int) and (states('sensor.zwift_power')|int < states('input_number.power_level2')|int) }}"
     condition:
       - condition: template
         value_template: "{{ states('sensor.zwift_hr') > states('input_number.min_heartrate') }}"
     action:
       - service: fan.turn_on
         data_template:
           entity_id: fan.zwift_fan
       - service: fan.turn_on
         data_template:
           entity_id: fan.zwift_fan
       - service: fan.set_speed
         data_template:
           entity_id: fan.zwift_fan
           speed: low

   - alias: liga ventilador no nivel medio
     trigger:
       platform: template
       value_template: "{{ (states('sensor.zwift_power')|int >= states('input_number.power_level2')|int) and (states('sensor.zwift_power')|int < states('input_number.power_level3')|int) }}"
     condition:
       - condition: template
         value_template: "{{ states('sensor.zwift_hr') > states('input_number.min_heartrate') }}"
 action:
       - service: fan.turn_on
         data_template:
           entity_id: fan.zwift_fan
       - service: fan.set_speed
         data_template:
           entity_id: fan.zwift_fan
           speed: medium

   - alias: liga ventilador no nivel alto
     trigger:
       platform: template
       value_template: "{{ states('sensor.zwift_power')|int >= states('input_number.power_level3')|int }}"
     condition:
       - condition: template
         value_template: "{{ states('sensor.zwift_hr') > states('input_number.min_heartrate') }}"
     action:
       - service: fan.turn_on
         data_template:
           entity_id: fan.zwift_fan
       - service: fan.set_speed
         data_template:
           entity_id: fan.zwift_fan
           speed: high

   - alias: desliga ventilador ao final do treino
     trigger:
       platform: state
       entity_id: binary_sensor.zwift_online
       to: 'off'
     action:
       - service: fan.turn_off
         data_template:
           entity_id: fan.zwift_fan

The interface, using lovelace, looks like this:

It’s fun to have the fan responding to the effort during the workout :-).

5 Likes

Interesting project. I tried it, but ran into some problems with the integration. Fan is working with sonos, but doesn’t update.
Do you have a github.com with you setup file.
Thanks for the project.

Hi,

I do have a GitHub but this project is not there yet. I will upload it, meanwhile what problem you ran into the integration?

Thanks. I’m not an expert with the python integration. I think my problem come from there. As for HA, I’m ok. I’ll keep reading how to manipulate these files and where to put it.

Are you using docker? I just did a docker image of the zwift part.

Ok… just uploaded the script/docker part to GitHub. Grab it here: https://github.com/clyra/mqtt-zwift

I will improve the docs and upload the HA automations later.

Btw… there’s one “catch” that is not documented here, but here: Tasmota the desktop fan

I use a few Tasmosta “rules” to be able to select the desired speed on the fan.

Thanks again. I think I have to start over. I had some problems with tasmota, but I think I’m ok for that part. I’ll have a look this weekend and see if I can at least have some data.

Thanks again for your help.

Hi @clyra. Just wondering any chance we could see a mqtt-zwift addon for hass.io? Or am I missing the way how to install this?

hi,

I don’t use hass.io so I have no clue how to make a addon, but it shouldnt be difficult… mqtt-zwift works as a standalone program and you have two basic options to run it: install it on python virtual env or build a container with the included Dockerfile. I do the docker way and I have a couple of automations to start/stop this cointainer when I’m about to zwift.

Ok will try to investigate. Basically hass.io addons are docker containers, so as you said should be rather simple. Unfortunately python way is not possible for hass.io.

Ok it seems all it is missing a repository configuration file like described here:
https://developers.home-assistant.io/docs/en/hassio_addon_repository.html

I saw this yesterday and decided to take my first whack at creating a custom component for HomeAssistant. It can be found here:

1 Like

sign me up. Will install the custom component as soon as I get home.

Well done! Thanks! I was lazy as I should wrote the sensor :-). btw, there’s a little problem with long connections to the game API that I guess my script didnt handled very well.

I have added the custom component and I can see populated sensors. Unfortunately I have done my riding today, but will have another tomorrow. Will report my findings, perhaps to your original topic.