Ventilation fans are pretty common in newer buildings, at least in Germany.
We have E2 series ventilation fans from Lunos installed in our house and I wanted to get them integrated into HA. The only published way I was able to find is emulating the switch usually connected to the 5UNI/FT fan controller. However, looking into the data sheets, all Lunos fan controller, including the 5UNI/FT, can be controlled using an analog 0-10V signal. This has the advantage of having 8 speed settings available instead of 4 and being able to activate the summer ventilation setting (deactivating heat recovery).
After some trial and error, I am now using an I2C 0-10V DAC and an ESP8266 running ESPHome to control my fans in HA. Within ESPHome, I am using the SpeedFan component and two custom outputs. One custom float output for the speed state of the fans and one binary output for the oscillation state of the fans (heat recovery or no heat recovery).
Using the SpeedFan component and the two custom outputs probably is not the most elegant way to implement this, but it works
Notes:
Initially I was using different PWM to 0-10V modules bought from Amazon and Aliexpress to generate the 0-10V signal, but found them to be unreliable and a pain to calibrate, since their output voltages are highly dependent on the stability of the input voltage. Since the actual input voltage can change quite significantly, depending on the load of the fans and cable length if using the 12V fan voltage to power the PWM module, I switched to the I2C DAC from DFRobot which is much more stable.
The two files below are needed if you want to copy what I did.
Custom ESPHome outputs (CustomLunosOutput10V.h):
#include "esphome.h"
using namespace esphome;
#include "DFRobot_GP8403.h"
DFRobot_GP8403 dac(&Wire,0x58);
// Custom float output for exposing Lunos float state via DAC, taking the binary oscillation state (CustomBinaryLunosFanOutput) into account
class CustomFloatLunosFanOutput : public Component, public FloatOutput {
public:
void setup() override {
// set the DAC output range as 0-10V
dac.setDACOutRange(dac.eOutputRange10V);
}
void write_state(float state) override {
int DAC0 = 0;
// fans not active and/or summer ventilation not active (heat recovery active)
if (id(lunos_oscillating_bool) == true || state == 0) {
// Convert state to DAC0 output in mV
DAC0 = (4000 * state + 750);
}
// active summer ventilation (no heat recovery)
else {
// Convert state to DAC0 output in mV
DAC0 = (4000 * state + 5750);
}
// set DAC0 and save value to chip
dac.setDACOutVoltage(DAC0,0);
// write state to global variable lunos_state_float
id(lunos_state_float) = state;
}
};
// Custom binary output for exposing Lunos binary oscillation state
class CustomBinaryLunosFanOutput : public Component, public BinaryOutput {
public:
void setup() override {
}
void write_state(bool oscillating) override {
// write current oscillation state to global variable lunos_oscillating_bool
id(lunos_oscillating_bool) = oscillating;
int DAC0 = 0;
// fans not active and/or summer ventilation not active (heat recovery active)
if (id(lunos_oscillating_bool) == true || id(lunos_state_float) == 0) {
// Convert state to DAC0 output in mV
DAC0 = (4000 * id(lunos_state_float) + 750);
}
// active summer ventilation (no heat recovery)
else {
// Convert state to DAC0 output in mV
DAC0 = (4000 * id(lunos_state_float) + 5750);
}
// set DAC0 and save value to chip
dac.setDACOutVoltage(DAC0,0);
}
};
ESPHome .yaml file
esphome:
name: lunos-dac
libraries:
- "Wire"
- "DFRobot_GP8403"
includes:
- CustomLunosOutput10V.h
!!!!!...Enter further ESPHome config here..!!!!!
i2c:
sda: 4
scl: 5
scan: false
id: i2c_bus
globals:
- id: lunos_oscillating_bool
type: bool
restore_value: no
initial_value: 'true'
- id: lunos_state_float
type: float
restore_value: no
initial_value: '0.25'
output:
- platform: custom
type: float
lambda: |-
auto custom_float_lunos_fan_output = new CustomFloatLunosFanOutput();
App.register_component(custom_float_lunos_fan_output);
return {custom_float_lunos_fan_output};
outputs:
id: lunos_dac_float
- platform: custom
type: binary
lambda: |-
auto custom_binary_lunos_fan_output = new CustomBinaryLunosFanOutput();
App.register_component(custom_binary_lunos_fan_output);
return {custom_binary_lunos_fan_output};
outputs:
id: lunos_oscillation_binary
fan:
- platform: speed
output: lunos_dac_float
name: "Lunos"
speed_count: 8
oscillation_output: lunos_oscillation_binary