Just wanted to contribute something simple. I had been getting frustrated by my Medify Air Purifiers having absolutely zero smarts, so I decided to rip my MA-40s and my MA-112 open to get them integrated into home assistant while making no permanent changes to the devices. This guide is very basic, but gets the job done. These changes will disable the front panel on the device, making it 100% managed via Home Assistant/ESPHome. This adds a filter life field as well. I plan to do a guide for the MA-112 as well since it uses an analogue output for controlling fan speed, and also includes a tachometer for fan speed feedback.
This guide has more images to it, but I am limited to one image for this post
Start by opening the top of the MA-40 unit, exposing the IO Panel and the Control Board Box. There should be a couple screws near the top holding this one, and the full panel snaps on/off.
Open the Control Board Box to expose the Control Board.
Unplug the cable connecting the Interface Panel and Control Board from the Control Board. We will be using the following pins on this header:
5V - ESP32 Dev Board Power
GND - ESP32 Dev Board GND
ION - DIO for ION control
H - DIO for Fan High
M - DIO for Fan Medium
L - DIO for Fan Low
Plug Jumper Cables into this header for the listed pins:
Lastly, close up the Control Board Box and plug these cables into the ESP32 Dev Board using the following pins:
gpio26 to H
gpio25 to M
gpio33 to L
gpio27 to ION
5V to 5V
GND to GND
Use the following code for ESPHome:
esphome:
name: ma-40
friendly_name: MA-40
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key:
ota:
password:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Ma-40"
password:
captive_portal:
globals:
- id: filter_time
type: int
restore_value: yes
initial_value: '0'
interval:
- interval: 1min
then:
if:
condition:
fan.is_on: ma40
then:
- lambda: |-
id(filter_time) += 1;
sensor:
- platform: template
name: Filter On Time
id: filter_time_disp
unit_of_measurement: "minutes"
device_class: "duration"
state_class: "measurement"
accuracy_decimals: 0
lambda: |-
return id(filter_time);
binary_sensor:
- platform: template
name: "Filter End of Life"
lambda: |-
if (id(filter_time) > 180000){
return true;
} else {
return false;
}
button:
- platform: template
name: "Reset Filter Life"
on_press:
lambda: |-
id(filter_time) = 0;
switch:
- platform: gpio
pin: 26
name: "High"
id: high
interlock: [medium, low]
internal: true
- platform: gpio
pin: 25
name: "Medium"
id: medium
interlock: [high, low]
internal: true
- platform: gpio
pin: 33
name: "Low"
id: low
interlock: [high, medium]
internal: true
- platform: gpio
pin: 27
name: "Ion"
id: ion
output:
- platform: template
id: custom_fan
type: float
write_action:
- if:
condition:
lambda: return ((state == 0));
then:
# action for off
- switch.turn_off: high
- switch.turn_off: medium
- switch.turn_off: low
- switch.turn_off: ion
- if:
condition:
lambda: return ((state > 0) && (state < 0.4));
then:
# action for speed 1
- switch.turn_off: high
- switch.turn_off: medium
- switch.turn_on: low
- if:
condition:
lambda: return ((state > 0.4) && (state < 0.7));
then:
# action for speed 2
- switch.turn_off: high
- switch.turn_off: low
- switch.turn_on: medium
- if:
condition:
lambda: return ((state > 0.7));
then:
# action for speed 3
- switch.turn_off: low
- switch.turn_off: medium
- switch.turn_on: high
fan:
- platform: speed
id: ma40
output: custom_fan
name: "Fan"
speed_count: 3