Air refreshener ESPHome/Wi-Fi controlled

Very clean setup. For this particular Air Wick model without converting it to smart, how does it activate?

For my model, it spray once motion is detected AND every 30 minutes( something like that) throughout the day whether it detects motion or not. Marketing scheme to make you constantly buy the refills.

This particular model sprays 15 seconds after activated, then every 30min or so. This initial spray is what made this clean setup possible. From Home Assistant I simply turn the device on when no one is in the room and turn it off some time after. If you ignore the 15 seconds you could say I have direct control of it spraying.

Here’s a simplified copy of my rules.

On:

  - trigger:
      - platform: state
        entity_id: binary_sensor.bathroom_door
        to: "off"
        for:
          seconds: 5
    condition:
      - condition: state
        entity_id: light.bathroom
        state: "off"
    action:
      - service: switch.turn_on
        entity_id: switch.airwick

Off:

  - trigger:
      - platform: state
        entity_id: light.bathroom
        to: "on"
      - platform: state
        entity_id: switch.airwick
        to: "on"
        for:
          minutes: 30
    condition:
      - condition: state
        entity_id: switch.airwick
        state: "on"
    action:
      - service: switch.turn_off
        entity_id: switch.airwick

No magic on the ESP either. It’s running standard Tasmota with one “Switch” GPIO.

Hey Daxi,

This is great, thank you so much for sharing. I’d love to see more pictures of the inside as well as the reassembled Air freshener. For example, where does the USB cable enter the air freshener?

Thank you in advance!

@rodri2566: Project is updated and pictures of the final air freshener added:

  • Added spray counter as readonly data
  • Added reset button to set spraycounter back to 0
  • Changed timings for glade cans (previously worked fine for airwick cans but not glade cans)
  • Spraying logic moved to a script
  • Avoiding multiple script executions by the button which avoids motor stress.
  • Moved ESPHome YAML to its own file

Thank you so much for sharing @Daxi . It’s a very elegant solution, both in terms of hardware as well as software.

Hey Daxi.
Added a lock to your code at the end of 2400 sprays and the presence of a spray in the can.

script:

  - id: do_spray
    then:
      - if:
          condition: 
            lambda: 'return id(spray_counter).state < 2400 ;'
          then:
            - number.increment: spray_counter
            - component.update: spray_counter
            - fan.hbridge.brake: spray_motor
            - fan.turn_on:
                id: spray_motor
                speed: 80
                direction: forward
            - delay: 350ms
            - fan.hbridge.brake: spray_motor
            - fan.turn_on:
                id: spray_motor
                speed: 80
                direction: reverse
            - delay: 200ms
            - fan.hbridge.brake: spray_motor
            - fan.turn_off:
                id: spray_motor
            - delay: 500ms

sensor:

  - platform: template
    name: ${friendly_name} ${friendly_suffix_counter}
    icon: mdi:counter
    id: spray_counter_template
    state_class: measurement
    unit_of_measurement: ""
    accuracy_decimals: 0
    lambda: |-
      return id(spray_counter).state;
    update_interval: 1s
    on_value:
      - if:
          condition:
            - lambda: |-
                return id(spray_counter).state <= 2399 ;
          then:
            - text_sensor.template.publish:
                id: ballon
                state: "The balloon is ok"
      - if:
          condition:
            - lambda: |-
                return id(spray_counter).state >= 2400 ;
          then:
            - text_sensor.template.publish:
                id: ballon
                state: "empty balloon"


text_sensor:

  - platform: template
    name: ${friendly_suffix_button_spray} Ballon
    icon: mdi:spray
    id: ballon
    update_interval: 60s

If it is not, change the GPIO pins at forward and reverse pin.

I’ve received all my parts and have done soldering. When I test the motor, it is turning the opposite direction. I am confused of what to change as I have tried a couple of ways, but the motor does not change direction.

I have tried swapping the gpio pins on the d1 mini. Change the pin id labels in the config file.

You can change the direction by changing the pins in the yaml and reinstall the firmware.

Original part:

output:
  - platform: esp8266_pwm
    id: motor_forward_pin
    pin: GPIO4
  - platform: esp8266_pwm
    id: motor_reverse_pin
    pin: GPIO5

Swapped pins:

output:
  - platform: esp8266_pwm
    id: motor_forward_pin
    pin: GPIO5
  - platform: esp8266_pwm
    id: motor_reverse_pin
    pin: GPIO4

so strange. Swapped the pins as you suggest above, but not change in direction.

this is my config:


substitutions:
  devicename: lr-airfreshener
  friendly_name: LR Airfreshener
  friendly_suffix_counter: Spray counter
  friendly_suffix_button_spray: Spray
  friendly_suffix_button_reset: Reset spray counter

esphome:
  name: $devicename

esp8266:
  board: esp01_1m

logger:

api:
  password: !secret ota_pass
ota:
  password: !secret ota_pass

wifi:
  ssid: !secret ssid3
  password: !secret ssid_pass
  fast_connect : true
  manual_ip:
    static_ip: 192.168.1.119
    subnet: 255.255.255.0
    gateway: 192.168.1.1

output:
  - platform: esp8266_pwm
    id: motor_forward_pin
    pin: GPIO5
  - platform: esp8266_pwm
    id: motor_reverse_pin
    pin: GPIO4

fan:
  - platform: hbridge
    id: spray_motor
    name: ${friendly_name} Motor
    pin_a: motor_forward_pin
    pin_b: motor_reverse_pin
    # enable_pin: motor_enable
    decay_mode: slow
    internal: true

number:
  - platform: template
    #name: ${friendly_name} ${friendly_suffix_counter}
    icon: mdi:counter
    id: spray_counter
    restore_value: true
    update_interval: 30s
    min_value: 0
    max_value: 3000
    mode: box
    step: 1
    optimistic: true
    internal: true

sensor:
  - platform: template
    name: ${friendly_name} ${friendly_suffix_counter}
    icon: mdi:counter
    id: spray_counter_template
    state_class: measurement
    unit_of_measurement: ""
    accuracy_decimals: 0
    lambda: |-
      return id(spray_counter).state;
    update_interval: 60s

  - platform: wifi_signal
    name: ${friendly_name} wifi signal
    update_interval: 15s

#  - platform: adc
#    pin: A0
#    name: '${friendly_name} illuminance'
#    unit_of_measurement: lux
#    update_interval: 60s
#    filters:
#      - lambda: |-
#          return (x / 10000.0) * 2000000.0;

binary_sensor:
  - platform: status
    name: ${friendly_name} status

button:
  - platform: template
    name: ${friendly_name} ${friendly_suffix_button_spray}
    id: btn_spray
    icon: mdi:spray
    on_press:
      then:
        if:
          condition:
            not:
              script.is_running: do_spray
          then:
            script.execute: do_spray
  - platform: template
    name: ${friendly_name} ${friendly_suffix_button_reset}
    icon: mdi:restart
    on_press:
      then:
        - number.set:
            id: spray_counter
            value: 0

script:
  - id: do_spray
    then:
      - number.increment: spray_counter
      - component.update: spray_counter
      - component.update: spray_counter_template
      - fan.hbridge.brake: spray_motor
      - fan.turn_on:
          id: spray_motor
          speed: 80
          direction: forward
      - delay: 350ms
      - fan.hbridge.brake: spray_motor
      - fan.turn_on:
          id: spray_motor
          speed: 80
          direction: reverse
      - delay: 200ms
      - fan.hbridge.brake: spray_motor
      - fan.turn_off:
          id: spray_motor
      - delay: 500ms

Strange.
The motor moves in both directions but the opposite direction, right?
Could you make a picture of your wiring?

Overall of how this Glade air freshener looks like.

brown/purple wire → IN1/IN2

The red/white single set wires go to drv8266 vcc/ground.

Brown/gray → motor

Hey guys,
just out of curiosity and because someone might have missed it. I did show my solution to this problem a couple of responses earlier in this thread. It is a fully satisfying technical solution with minimal effort.

Why do you guys bother to go through massive rebuilds of the devices internals? Is it only because you already have your other air freshener model at home and it wouldn’t work that way? The Air Wick Freshmatic model I own costs 5-10€ and the modifications are straight forward :face_with_raised_eyebrow:

The wiring should be ok.
At the picture with the dc motor, I see a red an black wire. Not sure if they are connected to the motor or not.
The motor should be only connected to the DRV board.

@ThomDietrich yes, there are multiple solutions.
The reason why I was using my approach was because of full control. For the Airwick, I would prefer your solution.

Not everybody has the same tools like a 3D printer.

Isn‘t it the nice wonderful part on open source and communities? You get different ideas and solutions and you can pick that one you want. :wink:

My solution is only one, not intended to be the best but one way you can do it. For me it is and was fun to get a little bit of deep dive into dc motors and how to control it with ESPHome. Learning by doing.

The motor should be only connected to the DRV board.

Hmmm. The red/black wire from the motor is stillconnected to the stock pcb board and not the to drv board. I left it as is just in case I want to revert back to the original. Should I disconnect the red/black wires from the motor so just have the wires go to the drv board only??

The Air Wick Freshmatic model I own costs 5-10€ and the modifications are straight forward

Not every countries carry the same model. Where I live, I can only find this particalur one and it isn’t your model or daxi’s.

1 Like

Please try it with disconnected wires from the original pcb. Thats the only thing I can see for now.

Isn‘t it the nice wonderful part on open source and communities? … For me it is and was fun to get a little bit of deep dive into dc motors and how to control it with ESPHome. Learning by doing.

Love this! It’s exactly why I did a lot of what I did. Not because it was desperately necessary, but because I wanted to try :slight_smile: In fact, you might enjoy one of my previous projects, which is quite similar to your approach with the air freshener: SenseoWifi/README.md at master · ThomDietrich/SenseoWifi · GitHub

Not everybody has the same tools like a 3D printer.

Good point :slight_smile: I got mine over the Covid time, a wonderful time waster :smiley:

I will remove those stock wires from motor when I get the chance. During my testing, I noticed the counter resets to 0 when the esp device loses power. Is there a way to keep the counter from resetting during a power outage/disconnect?

Yes, there is a way to keep it, I have to add this like in post Air refreshener ESPHome/Wi-Fi controlled - #12 by DivanX10

The yaml is placed in an expandable part of the post.

/EDIT: It was easier than I thought.
Code is updated. These are the changes at the YAML:
Commit:

esp8266:
  board: esp01_1m
  restore_from_flash: true

preferences:
  flash_write_interval: 1min

You will see a new log output at ESPHome like that (first line):

[22:08:14][D][esp8266.preferences:238]: Saving preferences to flash...
[22:08:17][D][sensor:126]: 'Bad Lufterfrischer Sprühzähler': Sending state 6.00000  with 0 decimals of accuracy
[22:09:17][D][sensor:126]: 'Bad Lufterfrischer Sprühzähler': Sending state 6.00000  with 0 decimals of accuracy
[22:10:17][D][sensor:126]: 'Bad Lufterfrischer Sprühzähler': Sending state 6.00000  with 0 decimals of accuracy
[22:11:17][D][sensor:126]: 'Bad Lufterfrischer Sprühzähler': Sending state 6.00000  with 0 decimals of accuracy
[22:11:30][D][button:013]: 'Bad Lufterfrischer Spray' Pressed.
[...]
[22:12:14][D][esp8266.preferences:238]: Saving preferences to flash...
[22:12:17][D][sensor:126]: 'Bad Lufterfrischer Sprühzähler': Sending state 7.00000  with 0 decimals of accuracy
[22:13:17][D][sensor:126]: 'Bad Lufterfrischer Sprühzähler': Sending state 7.00000  with 0 decimals of accuracy
[22:14:17][D][sensor:126]: 'Bad Lufterfrischer Sprühzähler': Sending state 7.00000  with 0 decimals of accuracy
[22:14:47][D][button:013]: 'Bad Lufterfrischer Sprühzähler zurücksetzen' Pressed.
[22:14:47][D][number:054]: 'spray_counter' - Setting number value
[22:14:47][D][number:113]:   New number value: 0.000000
[22:14:47][D][number:012]: 'spray_counter': Sending state 0.000000
[22:15:14][D][esp8266.preferences:238]: Saving preferences to flash...
[22:15:17][D][sensor:126]: 'Bad Lufterfrischer Sprühzähler': Sending state 0.00000  with 0 decimals of accuracy

It seems that the interval of 1min is only applied if a value has changed.
The sending state lines are the Home Assistant API updates to send the state to Home Assistant.
After the test I have done a reset and checked if this will lso work.

Found an additional thing: The reset counter does not instant update of the state like the spray button does. Changed that too with this commit:

added these lines at the reset button yaml:

        - component.update: spray_counter
        - component.update: spray_counter_template

FYI: The spray counter also survive a firmware update now.

ok. Thanks will update the code when get the chance.

The reset counter does not instant update of the state like the spray button does.

I also noticed this as well, but thought that is the way it is.

I’ve removed the stock motor wires and just have the wires coming from the drv. No change. I am still unable to reverse the direction.

The drv led is in green state. I noticed from web post that the color could also be red??