Skip to content

Instantly share code, notes, and snippets.

@zry98
Last active February 16, 2026 15:10
Show Gist options
  • Select an option

  • Save zry98/b08e0f94faeaf3a1b3bc109df24c7310 to your computer and use it in GitHub Desktop.

Select an option

Save zry98/b08e0f94faeaf3a1b3bc109df24c7310 to your computer and use it in GitHub Desktop.
ESPHome config for Frienhund ACF90W pet feeder (Product: '{"p":"qyilqivtx4uhd5r4","v":"2.1.2","m":0,"n":256}')
substitutions:
device_name: 'Cat Feeder'
timezone: 'Europe/Madrid'
esphome:
name: cat-feeder
comment: 'Frienhund ACF90W pet feeder'
platformio_options:
board_build.f_cpu: 80000000L # 80 MHz
board_build.flash_mode: dio
esp32:
variant: esp32c3
board: esp32-c3-devkitm-1 # ESP-C3-12F
flash_size: 4MB
framework:
type: esp-idf
# network, api, ota, ...
logger:
level: DEBUG
baud_rate: 0 # disable logging to the default UART port to avoid occupying it
time: # get time from HASS
- platform: homeassistant
id: hass_time
timezone: '${timezone}'
on_time:
- cron: '0 0 8,13,18,23 * * *'
then:
- if:
condition:
switch.is_on: switch_cron_feeding
then:
- button.press: button_manual_feed
uart: # for tuya MCU
tx_pin: 21
rx_pin: 20
baud_rate: 9600
data_bits: 8
parity: NONE
stop_bits: 1
# Tuya datapoints:
# 1: raw, meal plan (https://developer.tuya.com/en/docs/iot/pet-feeder-product-function-definition?id=K9tlinn9lgmrq#title-2-Meal%20Plan)
# bit 0-7: day of week (0b0000101 means on Friday and Sunday)
# bit 8-15: hour of day (0-23)
# bit 16-23: minute (0-59)
# bit 24-31: servings (1-12)
# bit 32-39: enabled (0 or 1)
# Example: 0x0112320c01 represents Sunday+18:50+12 servings+On
# 3: int (1-12), trigger manual feeding by sending the number of servings
# 4: enum, feeder state: 0 standby, 4 feeding, other values unknown
# 10: int, unknown, might be battery percentage
# 14: bitmask (7), fault
# 15: int, last feeding servings (motor rotation count)
# 23: switch, unknown, might be "slow feed"
# Product: '{"p":"qyilqivtx4uhd5r4","v":"2.1.2","m":0,"n":256}'
tuya: # tuya MCU
time_id: hass_time
on_datapoint_update:
- sensor_datapoint: 4
datapoint_type: enum # uint8_t
then:
- lambda: |-
ESP_LOGI("tuya", "datapoint 4 updated to: %s", format_hex_pretty(x).c_str());
switch (x) {
case 0:
id(text_sensor_state).publish_state("Standby");
break;
case 4:
id(text_sensor_state).publish_state("Feeding");
break;
default:
id(text_sensor_state).publish_state("Unknown");
}
return;
sensor:
# feeder last feeding servings
- platform: tuya
name: '${device_name} Last Feeding Servings'
id: sensor_last_feeding_servings
sensor_datapoint: 15
state_class: MEASUREMENT
accuracy_decimals: 0
text_sensor:
- platform: template
name: '${device_name} State'
id: text_sensor_state
update_interval: never
number:
# DP 4: feeder state
- platform: tuya
id: number_feed_state
number_datapoint: 4
min_value: 0
max_value: 4
step: 1
internal: true
# DP 3: trigger manual feeding by sending the number of servings
- platform: tuya
id: number_trigger_manual_feed_servings
number_datapoint: 3
min_value: 0
max_value: 12
step: 1
internal: true
datapoint_hidden:
datapoint_type: int
restore_value: no
# configuration number for manual feeding servings, used as input for the feed button, not sent to the MCU
- platform: template
name: '${device_name} Manual Feed Servings'
id: number_config_manual_feed_servings
mode: BOX
entity_category: CONFIG
icon: 'mdi:food-drumstick'
optimistic: true
min_value: 1
max_value: 12
step: 1
initial_value: 1
restore_value: true
update_interval: never
button:
# for hard restart
- platform: restart
id: button_restart
name: '${device_name} Restart'
entity_category: DIAGNOSTIC
# manual feed button, sends the number of servings from the config number to the MCU, then resets it to 0 after a short delay
- platform: template
name: '${device_name} Manual Feed'
id: button_manual_feed
on_press:
- number.set:
id: number_trigger_manual_feed_servings
value: !lambda 'return int(id(number_config_manual_feed_servings).state);'
- number.set:
id: number_trigger_manual_feed_servings
value: 0
# also reset the config number to 1 after feeding
- number.set:
id: number_config_manual_feed_servings
value: 1
switch:
- platform: template
name: '${device_name} Cron Feeding'
id: switch_cron_feeding
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF
entity_category: CONFIG
icon: 'mdi:calendar-clock'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment