Automation of Environmental sensor SEN55

Hi,

I have a question for experienced ESPhomers. I have environmental sensor SEN55 (particle measurement, NOx, VOC, temp, humidity). This sensor with ESP32 is measuring air quality in the workshop and if needed, it can turn on and control the speed of connected exhaust FAN.
It is working quite well for a year. But, I have all the automation on the HA side. Issue is, when ESP loose its connection, fan did not turn on when some of the value gets over the limit. So, I decided to make the automation independent and make it directly on ESP.

Question is, how do you recommend to approach this task. I started with simple on_value_range trigger. But crash in to problem with Circular dependency as I have a lot of conditions. Why? Because, if some of the value gets over certain level, it has to get priority over others. I tweak this recipe over HA.
You can see what I mean in yaml:

sensor:
  - platform: sen5x
    id: sen55
    pm_1_0:
      name: " PM <1µm Weight concentration"
      id: pm_1_0
      accuracy_decimals: 0
    pm_2_5:
      name: " PM <2.5µm Weight concentration"
      id: pm_2_5
      accuracy_decimals: 0
    pm_4_0:
      name: " PM <4µm Weight concentration"
      id: pm_4_0
      accuracy_decimals: 0
    pm_10_0:
      name: " PM <10µm Weight concentration"
      id: pm_10_0
      accuracy_decimals: 0
      on_value_range:
        - below: 50
          then:
            - fan.turn_on:
                id: FAN_speed
                speed: 20
        - above: 50
          then:
            - fan.turn_on:
                id: FAN_speed
                speed: 30
        - above: 60
          then:
            - fan.turn_on:
                id: FAN_speed
                speed: 40
        - above: 70
          then:
            - fan.turn_on:
                id: FAN_speed
                speed: 50
        - above: 80
          then:
            - fan.turn_on:
                id: FAN_speed
                speed: 60
        - above: 100
          then:
            - fan.turn_on:
                id: FAN_speed
                speed: 70
        - above: 120
          then:
            - fan.turn_on:
                id: FAN_speed
                speed: 80
        - above: 140
          then:
            - fan.turn_on:
                id: FAN_speed
                speed: 90
        - above: 160
          then:
            - fan.turn_on:
                id: FAN_speed
                speed: 100
        - below: 80
          then:
            - if:
                condition:
                  and:
                    - sensor.in_range:
                        id: nox_sensor
                        above: 20
                        below: 100
                then:
                  - fan.turn_on:
                      id: FAN_speed
                      speed: 50
            - if:
                condition:
                  sensor.in_range:
                    id: nox_sensor
                    above: 100
                then:
                  - fan.turn_on:
                      id: FAN_speed
                      speed: 100
        - below: 100
          then:
            - if:
                condition:
                  and:
                    - sensor.in_range:
                        id: nox_sensor
                        below: 20
                    - sensor.in_range:
                        id: voc_sensor
                        above: 200
                        below: 300
                then:
                  - fan.turn_on:
                      id: FAN_speed
                      speed: 30
            - if:
                condition:
                  and:
                    - sensor.in_range:
                        id: nox_sensor
                        below: 20
                    - sensor.in_range:
                        id: voc_sensor
                        above: 300
                        below: 400
                then:
                  - fan.turn_on:
                      id: FAN_speed
                      speed: 40
            - if:
                condition:
                  and:
                    - sensor.in_range:
                        id: nox_sensor
                        below: 20
                    - sensor.in_range:
                        id: voc_sensor
                        above: 400
                then:
                  - fan.turn_on:
                      id: FAN_speed
                      speed: 60
    temperature:
      name: "Temperature"
      accuracy_decimals: 0
    humidity:
      name: "Humidity"
      accuracy_decimals: 0
    voc:
      name: "VOC"
      id: voc_sensor
      algorithm_tuning:
        index_offset: 100
        learning_time_offset_hours: 12
        learning_time_gain_hours: 12
        gating_max_duration_minutes: 180
        std_initial: 50
        gain_factor: 230
    nox:
      name: "NOx"
      id: nox_sensor
      algorithm_tuning:
        index_offset: 100
        learning_time_offset_hours: 12
        learning_time_gain_hours: 12
        gating_max_duration_minutes: 180
        std_initial: 50
        gain_factor: 230
    temperature_compensation:
      offset: 0
      normalized_offset_slope: 0
      time_constant: 0
    acceleration_mode: low
    store_baseline: true
    address: 0x69
    i2c_id: bus_a
    update_interval: 10s   

output:
  - platform: ledc
    pin: GPIO1
    id: FAN

fan:
  - platform: speed
    output: FAN
    id: FAN_speed
    name: "Exhaust fan"
    speed_count: 10

I know that this is not bulletproof and has some flaws. If you know about better way how to approach this better, let me know. This is my approach (with AI) to get over the circular dependencies, where I wrote one big automatiton. But I used to have that “on_value_range” for each different measurements. But, when I call the nox_sensor in condition, the compiler doesn’t like it, with circular dependencies. I get a hint somewhere on internet to translate the measurements on “template” sensors, where it might work.

I don’t know what is the science behind your sensor thresholds, but you could for example map all three sensor outputs to fan speed.

for example for PM sensor:

filters:
  - calibrate_linear:
      - 0 -> 0
      - 160 -> 100
  - clamp:
      min_value: 20
      max_value: 100

would map the sensor readings 0…160 linearly to fanspeed 0-100
Do the same for other sensors and use the highest or use some other conditions.

Thanks for the suggestion. That will work in terms of setting the fan speed. However, the conditions are the problem.

I solved the circular dependencies by using a templated sensor for the NOx value.

If anyone else encounters the same issue, where automating the SEN5x sensor leads to a compiler error due to circular dependencies, a simple solution is to extrapolate the NOx values to a templated sensor. This way, everything works as it should.

- platform: template
    name: "NOx templated"
    id: nox_tmp
    accuracy_decimals: 0
    lambda: 'return (id(nox_sensor).state);'

I would just move the whole logic to a seperate script. And just run through every threshold that should turn it on. And only turn it off if non of these are met.

After that I would just run the script on_value of one of the sensors (as you all update them with the same interval).

You are right. That is more neat solution. Everything on one place. I will probably do that. Thanks.