Open Mikey887 opened 1 year ago
fan:
- platform: speed
output: my_output
name: "Office Air Purifier"
id: my_fan
speed_count: 3
on_speed_set:
then:
- if:
condition:
lambda: 'return ((id(my_fan).speed == 1) && (id(led1).state == 0));'
then:
- logger.log: "mismatch 1 0"
- output.turn_on: output1
- logger.log: "ammended"
- if:
condition:
lambda: 'return ((id(my_fan).speed == 1) && (id(led1).state == 66));'
then:
- logger.log: "mismatch 1 66"
- output.turn_on: output1
- delay: 0.1s
- output.turn_on: output1
- delay: 0.1s
- output.turn_on: output1
- delay: 0.1s
- logger.log: "ammended"
- if:
condition:
lambda: 'return ((id(my_fan).speed == 1) && (id(led1).state == 100));'
then:
- logger.log: "mismatch 1 100"
- output.turn_on: output1
- delay: 0.1s
- output.turn_on: output1
- delay: 0.1s
- logger.log: "ammended"
- if:
condition:
lambda: 'return ((id(my_fan).speed == 2) && (id(led1).state == 33));'
then:
- logger.log: "mismatch 2 33"
- output.turn_on: output1
- logger.log: "ammended"
- if:
condition:
lambda: 'return ((id(my_fan).speed == 2) && (id(led1).state == 100));'
then:
- logger.log: "mismatch 2 100"
- output.turn_on: output1
- delay: 0.1s
- output.turn_on: output1
- delay: 0.1s
- output.turn_on: output1
- delay: 0.1s
- logger.log: "ammended"
- if:
condition:
lambda: 'return ((id(my_fan).speed == 2) && (id(led1).state == 0));'
then:
- logger.log: "mismatch 2 0"
- output.turn_on: output1
- delay: 0.1s
- output.turn_on: output1
- delay: 0.1s
- logger.log: "ammended"
- if:
condition:
lambda: 'return ((id(my_fan).speed == 3) && (id(led1).state == 66));'
then:
- logger.log: "mismatch 3 66"
- output.turn_on: output1
- logger.log: "ammended"
- if:
condition:
lambda: 'return ((id(my_fan).speed == 3) && (id(led1).state == 0));'
then:
- logger.log: "mismatch 3 0"
- output.turn_on: output1
- delay: 0.1s
- output.turn_on: output1
- delay: 0.1s
- output.turn_on: output1
- delay: 0.1s
- logger.log: "ammended"
- if:
condition:
lambda: 'return ((id(my_fan).speed == 3) && (id(led1).state == 33));'
then:
- logger.log: "mismatch 3 33"
- output.turn_on: output1
- delay: 0.1s
- output.turn_on: output1
- delay: 0.1s
- logger.log: "ammended"
sensor:
- platform: pulse_width
pin: GPIO18
id: led1
name: UPPÅVIND Fan Speed LED
update_interval: 0.1s
accuracy_decimals: 0
unit_of_measurement: ""
icon: mdi:led-outline
filters:
- multiply: 1000000
- lambda: !lambda |-
switch (int(x)) {
case 20:
return 33;
case 100:
return 66;
case 320:
return 100;
default:
return 0;
}
- max:
window_size: 3
send_every: 3
send_first_at: 1
- or:
- delta: 0.5
- throttle: 60s
on_value_range:
- below: 1
then:
- fan.turn_off:
id: my_fan
- above: 5
below: 40
then:
- fan.turn_on:
id: my_fan
speed: 1
- above: 40
below: 70
then:
- fan.turn_on:
id: my_fan
speed: 2
- above: 70
then:
- fan.turn_on:
id: my_fan
speed: 3
output:
- platform: gpio
pin:
number: GPIO5
inverted: true
# "Open drain" used so the Wemos does't pull the pin
# up or down when it's not being pressed.
mode: OUTPUT_OPEN_DRAIN
id: output1
- platform: template
id: my_output
type: float
write_action:
- if:
condition:
lambda: return (state = 99999);
then:
- output.turn_off: output1
button:
- platform: output
name: "UPPÅTVIND Button"
output: output1
duration: 100ms
id: button_press
This doesn't really work for me. I don't know if it's because it's unfinished due to what you mentioned in the OP: It mostly works to show the status (off/low/med/high) in the Fan UI based on the led readings after pressing the button via UI/finger, but selecting a speed does nothing or does crazy things (random speeds, turns off, etc.).
I guess it goes into a loop?
OK it seems I made progress, although I am ashamed of how many hours this took me...
I prioritised the change from the fan UI to then press the button to get there (press, wait 2 seconds for LED status, see if needs to press again to get to target), and ultimately sync the statuses after 10s.
If the button is pressed manually, it takes those 10 seconds to update the fan status. Otherwise, changing the fan status/speed in the UI will cause a loop.
This is of course a workaround and there's probably a right way to do this, but my ESPHome knowledge is mostly zero so this was a lot of trial and error and google...
Problem: the led doesn't report 0 many times when off, so sometimes it takes a few loops to get to 0 :cry:
removed. see next comments
It seems I got it! @Mikey887 🥳 Here's my proposal based on yours (I don't understand certain things like the my_output part you added but it seems necessary).
Seems to work great. It took some thinking to make the bi-directional sync work...
It relies on the PWM monitoring explained in the fan speed detection thread, but it should work with the LED fan speed (adjust the sensor section), provided that it reports the speed correctly...
I mostly moved the actions and sync to scripts to allow for mode: restart
which is crucial to give it time to stabilize, then sync, and avoid a loop.
Here's my full code (including other improvements like Power Off button, Restart button):
# *** THIS FIRST SECTION IS INCLUDED PURELY FOR REFERENCE. USE YOURS! ***
esphome:
name: purificador-habitacion
friendly_name: Purificador Habitación
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
wifi:
networks:
- ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "Purificador Habitacion"
password: "antonio1098"
#captive_portal:
# web_server:
# local: true
# esp32_ble_tracker:
# scan_parameters:
# active: false
# Enable Home Assistant API
api:
encryption:
key: "xxx"
ota:
password: "xxx"
# *** HERE THE ACTUAL CODE STARTS ***
output:
- platform: gpio
pin:
number: GPIO18
inverted: true
mode: OUTPUT_OPEN_DRAIN # "Open drain" used so the Wemos does't pull the pin up or down when it's not being pressed.
id: output1
- platform: template
id: my_output
type: float
write_action:
- if:
condition:
lambda: return (state = 99999);
then:
- output.turn_off: output1
button:
- platform: output
name: "Increase speed"
output: output1
duration: 100ms
id: button_press
icon: mdi:fan-chevron-up
- platform: output
name: "Power Off"
output: output1
duration: 2s
id: power_off
icon: mdi:fan-off
- platform: restart
name: "Purifier Board Restart"
id: reboot
icon: mdi:restart
sensor:
- platform: pulse_counter
pin: 21
name: "Fan Speed PWM"
update_interval: 0.5s
accuracy_decimals: 0
unit_of_measurement: ''
icon: mdi:speedometer
id: pwm
filters:
- lambda: !lambda |-
int poff = 0;
int y = int(x);
if (y < 100) {
return 0;
} else if (y <7700) {
return 1;
} else if (y <12100) {
return 2;
} else if (y < 17000) {
return 3;
} else {
return 0;
}
- median:
window_size: 3
send_every: 3
send_first_at: 1
- delta: 0.5
on_value:
script.execute: sync_fan # see script below
fan:
- platform: speed
name: "Air Purifier"
icon: mdi:air-purifier
id: my_fan
speed_count: 3
output: my_output
on_turn_off:
if:
condition:
fan.is_off: my_fan
then:
button.press: power_off
on_speed_set:
if:
condition:
fan.is_on: my_fan
then:
script.execute: set_speed # see script below
script:
- id: set_speed
mode: restart # handle the accidental presses or quick changes of mind
then:
- if:
condition:
lambda: return (id(my_fan).speed > id(pwm).state);
then:
- repeat:
count: !lambda 'return (id(my_fan).speed - id(pwm).state);'
then:
- button.press: button_press
- delay: 1s
- if:
condition:
lambda: return (id(my_fan).speed < id(pwm).state);
then:
- button.press: power_off
- delay: 2.2s # allow the 2s of the power_off button to finish
- repeat:
count: !lambda 'return (id(my_fan).speed - id(pwm).state);'
then:
- button.press: button_press
- delay: 1s
- id: sync_fan # sync the real-life status of the purifier back to the ESP/HA fan. This both confirms that all changes were executed (provides the real status) and also syncs the button presses done by your finger in HA or real life.
mode: restart # see comment below as to why restart
then:
- delay: 5s # allow the PWM speed detection to stabilize and only act after that. This way it skips the transition speeds.
- if:
condition:
lambda: 'return id(pwm).state == 0;'
then:
- fan.turn_off:
id: my_fan
else:
- fan.turn_on:
id: my_fan
speed: !lambda return id(pwm).state;
@jonathonlui can you review the code and give your opinion? I did it all via googling and trial&error, so I might have done things improperly or inneficiently, especially the filters and the scripts. I also don't know how to create PRs so feel absolutely free to take these ideas and add them to your project if you wish! I'd be honored 😄
Re-uploaded the code in previous comment (removed the throttle which caused issues) and changed the script section, to make it more stable and not cycle through high speeds if eg. it's in speed 2 and want 1. In this scenario, turn off, then press as button X times, without needing to wait and check the current real speed, which wasn't robust. Requires power_off button (which is there)
This should now be more robust, and also snappier. Note that I've been very conservartive with all the update_intervals, delays, etc, as I believe that for this device precision and stability of the changes is better than speed. They could be adjusted.
Last yaml work perfectly for me, you can also add the Change filter LED and do a sensor for it
@antonio1475 how did you retrieve the PWM? What TP did you use on the UPPÅTVIND board?
@antonio1475 how did you retrieve the PWM? What TP did you use on the UPPÅTVIND board?
Hi. It's all explained between my posts here and in the other issue (first link in my long comment above). As I mention there, it's not a TP but the 4th pin in the connector (2nd from the top) that is unused in the original procedure, which allows you to insert a cable in that pin, and take it to a pin in the ESP board. jonathonlui guessed that it was PWM sensor and he was right.
@antonio1475 how did you retrieve the PWM? What TP did you use on the UPPÅTVIND board?
Hi. It's all explained between my posts here and in the other issue (first link in my long comment above). As I mention there, it's not a TP but the 4th pin in the connector (2nd from the top) that is unused in the original procedure, which allows you to insert a cable in that pin, and take it to a pin in the ESP board. jonathonlui guessed that it was PWM sensor and he was right.
I managed to use TP9 to get the PWM, I didn't quite get how to use the 4th pin when the connector is already occupied.
I managed to use TP9 to get the PWM, I didn't quite get how to use the 4th pin when the connector is already occupied.
Cool, thanks for sharing @hex! More consistent with the rest of the setup.
Regarding using the connection pin, since the Dupont cables are very thin (not sure if you're using those or thicker cables), I just peeled one side and stuck it around the metal connector (which I think I pulled out from the white plastic to do this). Sorry that I don't have a picture, but the end result was something like this (note: not sure if I used the right connector in this painting!)
I did this because I didn't have a soldering iron at the time that I did this second part regarding PWM, plus I didn't know about TP9.
Late gamer here! Managed to soldered the wires to the pins on my ESP8266MOD (di_mini). To get the PWM to the board, which pin on my ESP8266 should that be soldered on? Or it has to be a ESP32 to connect with PWM?
Late gamer here! Managed to soldered the wires to the pins on my ESP8266MOD (di_mini). To get the PWM to the board, which pin on my ESP8266 should that be soldered on? Or it has to be a ESP32 to connect with PWM?
Late gamer here too: I've just managed to get my wemos D1 mini (esp8266) working by connecting the PWM signal coming from the connector to the D7 pin. Seems there's no need for ESP32 for the pulse_counter component.
Hi, I have been trying to build on this code to make the purifier show as a fan type entity, I have got it so if you change with the manual button on the device or via the button exposed by the code in this repo it updates a fan entity correctly but getting the feedback from when you alter the fan in the home assistant ui is getting a bit tricky , I think I also may to implement the pwm fix in one of the other issues which may be tripping me up too.