I converted a PETKIT Fresh Element Mini dry food dispenser to run locally with ESPHome, removing the need for a cloud connection.

I was inspired by this post on the home assistant forums DIY PetKit feeder local integration to Home Assistant via ESPHome.

The project started with some open heart surgery on the feeder while the bosses watched closely.

PETKIT Fresh Element Mini dry food dispenser opened for ESPHome modification

I piggybacked onto the feeder's test points with an ESP32-S3 Supermini. This gives you access to the cap, food level and motor state, plus a feed action that runs the motor for a fixed amount of time. In my setup, 2000ms is about 4g and 4000ms is about 8g.

ESPHome YAML

substitutions:
  name: feeder-1 # Do not use underscores, which are not fully compatible with mDNS
  friendly_name: Dry Feeder


esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: esp-idf

esphome:
  name: ${name}

logger:
  level: INFO

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: none
  fast_connect: true

api:
  actions:
    - action: feed_amount
      variables:
        amount_g: int
      then:
        - script.execute:
            id: petkit_feed
            amount_g: !lambda "return amount_g;"

ota:
  - platform: esphome


web_server:
  port: 80

binary_sensor:
  - platform: gpio
    name: "PetKit Food level state"
    device_class: problem
    pin: 
      number: GPIO12
      inverted: true
    filters:
      - delayed_off: 120000ms
  - platform: gpio
    name: "PetKit Cap state"
    pin: 
      number: GPIO11
      inverted: true
    filters:
      - delayed_on_off: 100ms
      - delayed_off: 2000ms
  - platform: gpio
    name: "PetKit Motor state"
    device_class: moving
    pin: 
      number: GPIO10
      inverted: true
    filters:
      - delayed_on_off: 100ms
      - delayed_off: 2000ms

number:
  - platform: template
    name: "PetKit Feed Amount"
    id: petkit_feed_amount
    icon: "mdi:scale"
    unit_of_measurement: "g"
    min_value: 4
    max_value: 100
    step: 4
    optimistic: true
    restore_value: true
    initial_value: 8

script:
  - id: petkit_feed
    mode: single
    parameters:
      amount_g: float
    then:
      - switch.turn_on: relay
      # Calibration:
      # 2000ms ~= 4g
      # 4000ms ~= 8g
      - delay: !lambda |-
          return (uint32_t) (amount_g * 500);
      - switch.turn_off: relay

button:
  - platform: template
    name: "PetKit Feed"
    icon: "mdi:shaker"
    on_press:
      - script.execute:
          id: petkit_feed
          amount_g: !lambda "return id(petkit_feed_amount).state;"

switch:
  - platform: gpio
    pin:
      number: GPIO13
      # allow_other_uses: true
      inverted: true
    id: relay
    internal: true

Original Mastodon posts: open heart surgery and ESPHome demo.