blueprint: name: CO₂ ventilation alert – Apollo R PRO-1 (LED + buzzer) description: >- Visual and audible CO₂ alerts on an Apollo R PRO-1. **LED**: off below the "ventilation recommended" level. From that level it turns yellow and fades towards red as CO₂ approaches the "ventilation needed" level; at or above it, it is solid red. **Buzzer**: at or above the "start beeping" level the beep action is repeated at the chosen interval, but only while CO₂ is NOT dropping. "Dropping" means the newest CO₂ reading is lower than the reading about one minute earlier (taken from a Statistics helper, see the "CO₂ change" input). Beeping stops as soon as CO₂ is falling, at any level. domain: automation input: co2_sensor: name: CO₂ sensor description: The R PRO-1 CO₂ sensor (ppm). selector: entity: filter: - domain: sensor device_class: carbon_dioxide change_sensor: name: CO₂ change over the last minute description: >- A Statistics helper based on the CO₂ sensor above. Settings: State characteristic = "Change", Max age = 1 minute (use 90 s if your sensor reports slower than every ~20 s). Its state is newest reading minus oldest reading in the window, so a negative value means CO₂ is dropping. selector: entity: filter: - domain: sensor led: name: R PRO-1 RGB LED description: The light entity of the R PRO-1 RGB LED. selector: entity: filter: - domain: light beep_action: name: Beep action description: >- Action(s) that make the R PRO-1 beep. Use the ESPHome action of your device, for example `esphome._play_buzzer` with data `song_str: "co2:d=8,o=6,b=180:c,p,c"`. Keep the tune short, it is repeated at the beep interval. default: [] selector: action: {} recommended_level: name: Ventilation recommended (ppm) description: LED turns on (yellow) at this level. default: 1000 selector: number: min: 400 max: 5000 step: 50 unit_of_measurement: ppm mode: box needed_level: name: Ventilation needed (ppm) description: LED is fully red at this level. default: 1500 selector: number: min: 400 max: 5000 step: 50 unit_of_measurement: ppm mode: box beep_level: name: Start beeping (ppm) description: Beeping starts at this level (unless CO₂ is dropping). default: 2000 selector: number: min: 400 max: 10000 step: 50 unit_of_measurement: ppm mode: box min_drop: name: Minimum drop to count as "dropping" (ppm) description: >- 0 = any decrease stops the beeping. Raise to 5–10 if sensor jitter makes the beeping stop and start at a plateau. default: 0 selector: number: min: 0 max: 100 step: 1 unit_of_measurement: ppm mode: box beep_interval: name: Beep repeat interval default: "/30" selector: select: options: - label: Every 10 seconds value: "/10" - label: Every 15 seconds value: "/15" - label: Every 20 seconds value: "/20" - label: Every 30 seconds value: "/30" brightness: name: LED brightness (%) default: 60 selector: number: min: 1 max: 100 step: 1 unit_of_measurement: "%" mode: slider led_off_when_ok: name: Turn LED off below "recommended" description: >- If enabled, the LED is switched off when CO₂ is below the recommended level. Disable if you use the LED for other things. default: true selector: boolean: {} mode: queued max: 10 max_exceeded: silent variables: co2_sensor: !input co2_sensor change_sensor: !input change_sensor rec_level: !input recommended_level need_level: !input needed_level beep_level: !input beep_level min_drop: !input min_drop led_brightness: !input brightness led_off_when_ok: !input led_off_when_ok triggers: - trigger: state entity_id: !input co2_sensor id: reading - trigger: time_pattern seconds: !input beep_interval id: beep_tick actions: - variables: co2: "{{ states(co2_sensor) | float(none) }}" change: "{{ states(change_sensor) | float(0) }}" dropping: "{{ change < -(min_drop | float(0)) }}" span: "{{ [need_level - rec_level, 1] | max }}" frac: >- {{ ([[(co2 - rec_level) / span, 0] | max, 1] | min) if co2 is not none else 0 }} hue: "{{ (60 * (1 - frac)) | round(0) | int }}" - condition: template value_template: "{{ co2 is not none }}" - choose: # --- LED: update on every new CO2 reading ------------------------- - conditions: - condition: trigger id: reading sequence: - choose: - conditions: - condition: template value_template: "{{ co2 >= rec_level }}" sequence: - action: light.turn_on target: entity_id: !input led data: hs_color: "{{ [hue, 100] }}" brightness_pct: "{{ led_brightness | int }}" - conditions: - condition: template value_template: "{{ led_off_when_ok }}" sequence: - action: light.turn_off target: entity_id: !input led # --- Buzzer: repeat while high AND not dropping ------------------- - conditions: - condition: trigger id: beep_tick - condition: template value_template: "{{ co2 >= beep_level and not dropping }}" sequence: !input beep_action