I implemented switching the coffee machine on and off by means of a relay. It works great. I took a single-channel 5V relay (KY-019), you can use the relay that I suggested above, but it hasn’t arrived yet, and it arrived much earlier than AXICOM IM01 3vdc SMD
I glued the relay itself to the back of the control panel housing, made a divorce for the servo, for esp and for the relay.
I also implemented a water drain to drain water when flushing the coffee machine past the cup, and then you can pour coffee into the cup.
Watch the video how the water drain works and how the full process works, from turning on to making coffee
Connection diagram
You can download the part for printing on a 3d printer here
Important. I designed the hole for the gear for the SG90 Micro Servo. It comes in very tight, but it holds tight and does not jump off. There is no need to expand the hole, just make a good effort when pressing the servo into the hole and the servo will start to enter.
You can set the desired position of the servo to adjust the water discharge. I have set the following values

Code for turning the coffee machine on and off, as well as servo control
substitutions:
board_name: Coffee Philips 5400
node_name: coffee-philips-5400
esphome:
name: ${node_name}
friendly_name: coffee-philips-5400
comment: ESP32 Coffee Philips 5400
esp32:
board: esp32dev
framework:
type: arduino
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: on
reboot_timeout: 10min
ap:
ssid: ESP SmartCoffeePhilips
password: !secret ap_esp_password
captive_portal:
web_server:
port: 80
logger:
level: DEBUG
baud_rate: 0
ota:
password: "esphome"
api:
encryption:
key: !secret api_key
services:
- service: control_servo
variables:
level: float
then:
- servo.write:
id: servo_control
level: !lambda 'return level / 100.0;'
- sensor.template.publish:
id: servo_sensor
state: !lambda 'return (float)level;'
#####################################################################################
################################## External component ################################
external_components:
- source: github://TillFleisch/ESPHome-Philips-Smart-Coffee@main
#####################################################################################
######################################### UART ######################################
uart:
- id: uart_display
rx_pin: GPIO16
tx_pin: GPIO17
baud_rate: 115200
stop_bits: 1
data_bits: 8
parity: NONE
rx_buffer_size: 256
debug:
direction: RX
dummy_receiver: false
- id: uart_mainboard
rx_pin: GPIO3
tx_pin: GPIO1
baud_rate: 115200
stop_bits: 1
data_bits: 8
parity: NONE
rx_buffer_size: 256
debug:
direction: RX
dummy_receiver: false
sequence:
- lambda: |-
UARTDebug::log_hex(direction, bytes, ':');
//AA:AA:AA:B0
if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x06 && bytes[9]==0x00) {
id(idWater).publish_state("There is water");
id(idPallet).publish_state("Inserted");
id(idGrainTray).publish_state("There are grains");
id(idStatus).publish_state("Choose a drink");
id(idCoffeeGroundsContainer).publish_state("Empty");
}
//AA:AA:AA:B0 "Availability of coffee beans"
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x08 && bytes[7]==0x05) { id(idGrainTray).publish_state("The grains have run out"); }
//AA:AA:AA:B0 "Took out the container with water"
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x0E && bytes[9]==0x40) { id(idWater).publish_state("There is no water"); }
//AA:AA:AA:B0 "Removed the pallet"
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x0E && bytes[9]==0x80) { id(idPallet).publish_state("Retrieved"); }
//AA:AA:AA:B0 "The container with coffee grounds is filled"
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x0E && bytes[9]==0x00) { id(idCoffeeGroundsContainer).publish_state("Oporozhe. container for coffee grounds"); }
//AA:AA:AA:B0 "The pallet and the container with water were taken out"
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x0E && bytes[9]==0xC0) {
id(idWater).publish_state("There is no water");
id(idPallet).publish_state("Retrieved");
}
//AA:AA:AA:B0 "Statuses"
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x0C && bytes[7]==0x01) { id(idStatus).publish_state("Enjoy"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x0C && bytes[7]==0x02) { id(idStatus).publish_state("Something (07 0C 02)"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x01 && bytes[7]==0x00) { id(idStatus).publish_state("Something (07 01 00)"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x05 && bytes[7]==0x00) { id(idStatus).publish_state("Off"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x07 && bytes[7]==0x0E) { id(idStatus).publish_state("Water heating"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x07 && bytes[7]==0x0D) { id(idStatus).publish_state("Grinding the grains"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x07 && bytes[7]==0x10) { id(idStatus).publish_state("Pour the milk"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x07 && bytes[7]==0x11) { id(idStatus).publish_state("Pour the coffee"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x07 && bytes[7]==0x12) { id(idStatus).publish_state("Pre-dosing of water"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x07 && bytes[7]==0x13) { id(idStatus).publish_state("Creating steam for milk"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x07 && bytes[7]==0x14) { id(idStatus).publish_state("The brewing unit to the brewing position"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x07 && bytes[7]==0x15) { id(idStatus).publish_state("Enjoy"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x08 && bytes[7]==0x0E) { id(idStatus).publish_state("Heating"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x08 && bytes[7]==0x02) { id(idStatus).publish_state("Flushing"); }
else if (bytes[0]==0xAA && bytes[3]==0xB0 && bytes[5]==0x07 && bytes[6]==0x08 && bytes[7]==0x14) { id(idStatus).publish_state("Something (07 08 14)"); }
//AA:AA:AA:B5 "Error Code"
if (bytes[0]==0xAA && bytes[3]==0xB5 && bytes[5]==0x06 && bytes[10]==0x00 && bytes[11]==0x00) { id(idErrorCode).publish_state("00"); }
else if (bytes[0]==0xAA && bytes[3]==0xB5 && bytes[5]==0x06 && bytes[10]==0x00 && bytes[11]==0x0B) { id(idErrorCode).publish_state("0B"); }
else if (bytes[0]==0xAA && bytes[3]==0xB5 && bytes[5]==0x06 && bytes[10]==0x00 && bytes[11]==0xE6) { id(idErrorCode).publish_state("E6"); }
else if (bytes[0]==0xAA && bytes[3]==0xB5 && bytes[5]==0x06 && bytes[10]==0x00 && bytes[11]==0x80) { id(idErrorCode).publish_state("80"); }
else if (bytes[0]==0xAA && bytes[3]==0xB5 && bytes[5]==0x06 && bytes[10]==0x00 && bytes[11]==0xCB) { id(idErrorCode).publish_state("CB"); }
else if (bytes[0]==0xAA && bytes[3]==0xB5 && bytes[5]==0x06 &&& bytes[10]==0x00 & bytes[11]==0xFF) { id(idErrorCode).publish_state("FF"); }
else if (bytes[0]==0xAA && bytes[3]==0xB5 && bytes[5]==0x06 && bytes[10]==0x00 && bytes[11]==0xA0) { id(idErrorCode).publish_state("A0"); }
if (bytes[0]==0xAA) { id(idPowerStatus).publish_state(true); }
#####################################################################################
############################## Global variables #####################################
globals:
- id: saved_position
type: int
initial_value: '0'
restore_value: true
#####################################################################################
############################### Output Platform #####################################
output:
- platform: ledc
id: pwm_output
pin: GPIO27
frequency: 50 Hz
#####################################################################################
################################ Configuration ######################################
servo:
- id: servo_control
output: pwm_output
auto_detach_time: 5s
transition_length: 5s
restore: true
#####################################################################################
################################## Connected component ##############################
philips_series_2200:
display_uart: uart_display
mainboard_uart: uart_mainboard
power_pin: GPIO12
id: philip
#####################################################################################
##################################### Text Sensor ###################################
text_sensor:
- platform: wifi_info
ip_address:
name: IP
- platform: template
name: "Water"
icon: mdi:cup-water
id: idWater
update_interval: 60s
- platform: template
name: "Coffee Grounds Container"
icon: mdi:train-car-centerbeam-full
id: idCoffeeGroundsContainer
update_interval: 60s
- platform: template
name: "Coffee Pallet"
icon: mdi:spirit-level
id: idPallet
update_interval: 60s
- platform: template
name: "Error Code"
icon: mdi:alert-circle-outline
id: idErrorCode
update_interval: 60s
- platform: template
name: "Grain Tray"
icon: mdi:grain
id: idGrainTray
update_interval: 60s
- platform: template
name: "Status"
icon: mdi:coffee-to-go
id: idStatus
update_interval: 60s
#####################################################################################
################################### Binary sensor ###################################
binary_sensor:
- platform: template
name: "PowerStatus"
id: idPowerStatus
lambda: |-
if (id(idPowerStatus).state) {
return true;
} else {
return false;
}
#####################################################################################
################################## Sensor ###########################################
sensor:
#WiFi signal strength sensor
- platform: wifi_signal
name: RSSI WiFi
icon: mdi:wifi
update_interval: 60s
#Servomotor position sensor
- platform: template
name: Servo Sensor Position
id: servo_sensor
icon: mdi:valve
on_value:
then:
- script.execute: script_servo
- script.execute: record_servo_position
#####################################################################################
#################################### Switch #########################################
switch:
- platform: gpio
pin: GPIO14
name: "Relay Switch"
id: idRelaySwitch
internal: True #Скрыть - true \показать - false
restore_mode: ALWAYS_OFF
#Выключатель сервопривода с начальной и конечной позицией
- platform: template
id: servo_control_switch
name: Valve open/closed
icon: mdi:coffee-maker-outline
restore_state: false
turn_on_action:
- sensor.template.publish:
id: servo_sensor
state: !lambda 'return id(valve_open).state;'
- switch.template.publish:
id: servo_control_switch
state: true
- servo.write:
id: servo_control
level: !lambda 'return (id(valve_open).state/100);'
turn_off_action:
- sensor.template.publish:
id: servo_sensor
state: !lambda 'return id(valve_closed).state;'
- switch.template.publish:
id: servo_control_switch
state: false
- servo.write:
id: servo_control
level: !lambda 'return (id(valve_closed).state/100);'
#####################################################################################
###################################### Number #######################################
#Slider for servo control
number:
- platform: template
name: Valve Position
min_value: -100
max_value: 100
update_interval: 1s
mode: slider #slider/box
lambda: 'return id(servo_sensor).state;'
step: 1
set_action:
then:
- servo.write:
id: servo_control
level: !lambda 'return x / 100.0;'
- sensor.template.publish:
id: servo_sensor
state: !lambda 'return x;'
#Specify the initial position of the servo
- platform: template
name: Valve open
id: valve_open
min_value: -100
max_value: 100
mode: box #slider/box
step: 1
optimistic: true
restore_value: true
#Specify the final position of the servo
- platform: template
name: Valve closed
id: valve_closed
min_value: -100
max_value: 100
mode: box #slider/box
step: 1
optimistic: true
restore_value: true
#####################################################################################
####################################### Script ######################################
script:
- id: script_servo
then:
- if:
condition:
- lambda: 'return id(servo_sensor).state == id(valve_open).state;'
then:
- sensor.template.publish:
id: servo_sensor
state: !lambda 'return (id(valve_open).state/100);'
- switch.turn_on: servo_control_switch
- if:
condition:
and:
- lambda: 'return id(servo_sensor).state == id(valve_closed).state;'
then:
- sensor.template.publish:
id: servo_sensor
state: !lambda 'return id(valve_closed).state;'
- switch.turn_off: servo_control_switch
- id: record_servo_position
then:
- globals.set:
id: saved_position
value: !lambda 'return id(servo_sensor).state;'
#####################################################################################
####################################### Button ######################################
button:
#Restart
- platform: restart
name: Restart
icon: mdi:restart
#Turn on the coffee machine
- platform: template
name: "Power ON"
icon: mdi:power
on_press:
- switch.turn_on: idRelaySwitch
- delay: 1s
- switch.turn_off: idRelaySwitch
#Turn off the coffee machine
- platform: template
name: "Power OFF"
icon: mdi:power
on_press:
- switch.turn_on: idRelaySwitch
- delay: 2s
- switch.turn_off: idRelaySwitch
#####################################################################################
####################################### Time #######################################
time:
- platform: sntp
id: sntp_time
timezone: Europe/Moscow
on_time:
- seconds: 59
then:
- lambda: |-
id(idPowerStatus).publish_state(false);



