Last active
February 16, 2026 15:10
-
-
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}')
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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