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 :-).