DIY PetKit feeder local integration to Home Assistant via ESPHome

Ah ok, but I have 2 bowl , and a camera also

This looks like the Petkit FreshElement Solo, not the Mini. Does the hack work on this model too? It seems cheaper and easier to find than the mini. Or is that the one you had to wire to a Shelly ?

I have the “cheap” no name dual bowl wired to a shelly, and also the solo reflashed. I’ll rewire the “cheap” back as it came from factory and return it to order a second solo and also reflash it.


Both have working automations in HA.

Ah got it, thanks! So the Solo can be flashed without the need for an extra esp? Would you mind sharing any link or guidance on how to do that?

Yes yes, it can be reflashed without additional hardware. To be honest I picked 3 or 4 sources made a soup of them and their code to fit my needs.
I’m 2000km away from home, and I’ll fly back home next week.
I’ll try to not forget and get some photos to you and the community, as well the complete code I used.
In last instance I’ll try not forget to do some kind of tutorial on the second Solo. I wish I was half gifted as many of the people I see doing tutorials to us…

1 Like

Some photos from the night I did it.




1 Like

Thanks a lot and happy new year!

So, here I am. I just finished my second unit, it’s working wonderfully.
I’ll add some more photos.

This is not my code, and unfortunately I can’t give credit for it because it took me some days copying around and trying to make sense of it, frankensteining it together.

ota:
  password: "XXX"

captive_portal:

substitutions:
  name: catfeeder
  device_description: "Petkit Solo"
  default_scoops: "4"
  #1 scoop = ~10g

esphome:
  name: $name
  comment: $device_description
  on_boot:
    - light.turn_on:
        id: led
        effect: fast_blink

esp32:
  board: esp32dev
  framework:
    type: arduino

logger:
  level: INFO
  baud_rate: 0

globals:
  - id: default_scoops
    type: int
    initial_value: '${default_scoops}'
  - id: scoops_count
    type: int
  - id: max_scoops
    type: int
  - id: food_sensor_count
    type: int

api:
  encryption:
    key: "XXX"
  services:
    - service: feed_cat
      variables:
        scoops: int
      then:
        - logger.log:
            level: INFO
            format: "feed_cat service called (scoops = %d)"
            args: [ scoops ]
        - lambda: |-
            id(scoops_count) = 0;
            id(food_sensor_count) = 0;
            id(max_scoops) = scoops;
        - switch.turn_on: feed_forward

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "Petkit-Solo Fallback Hotspot"
  manual_ip:
    # Set this to the IP of the ESP
    static_ip: 192.168.69.233
    # Set this to the IP address of the router. Often ends with .1
    gateway: 192.168.69.1
    # The subnet of the network. 255.255.255.0 works for most home networks.
    subnet: 255.255.255.0

light:
  - platform: binary
    name: Status Led
    id: led
    output: led_output
    effects:
      - strobe:
          name: fast_blink
          colors:
            - state: True
              duration: 125ms
            - state: False
              duration: 125ms
    internal: True

output:
  - id: led_output
    platform: gpio
    pin: GPIO5

interval:
  - interval: 1s
    then:
      if:
        condition:
          wifi.connected:
        then:
          - light.turn_on:
              id: led
              effect: None
        else:
          - light.turn_on:
              id: led
              effect: fast_blink

uart:
  tx_pin: GPIO1
  rx_pin: GPIO3
  baud_rate: 9600

binary_sensor:
  - name: "Manual Feed Button"
    id: manual_feed_button
    platform: gpio
    pin: 
      number: GPIO34
      inverted: true
    on_press:
      then:
        - lambda: |-
            id(scoops_count) = 0;
            id(food_sensor_count) = 0;
            id(max_scoops) = id(default_scoops);
        - switch.turn_on: feed_forward
        - logger.log:
            level: INFO
            format: "Serving %d scoops"
            args: [ id(max_scoops) ]
    internal: true

  - name: "Motor Sensor"
    id: motor_sensor
    platform: gpio
    pin: 
      number: GPIO27
      inverted: true
    on_press:
      then:
        - lambda: |-
            id(scoops_count) += 1;
            if (id(scoops_count) == id(max_scoops)) {
              id(feed_forward).turn_off();
            }
        - logger.log:
            level: INFO
            format: "%d/%d scoops served"
            args: [ id(scoops_count), id(max_scoops) ]
    internal: true

  - name: "Infared Feed Sensor"
    id: feed_sensor
    platform: gpio
    pin: 
      number: GPIO14
    on_press:
      then:
        - lambda: |-
            id(food_sensor_count) += 1;
    internal: true

switch:
  - name: "Enable Sensors"
    id: enable_sensors
    platform: gpio
    pin: 
      number: GPIO33
    restore_mode: ALWAYS_ON
    disabled_by_default: true
    internal: true

  - name: "Enable Feeder Motor"
    id: enable_feeder_motor
    platform: gpio
    pin: 
      number: GPIO19
    restore_mode: ALWAYS_OFF
    disabled_by_default: true
    on_turn_off:
      then:
        - if:
            condition:
              lambda: |-
                return id(food_sensor_count) == 0;
            then:
              - homeassistant.event:
                  event: "esphome.${name}_food_dispensed"
                  data:
                    message: "Food not dispensed"
              - logger.log:
                  level: ERROR
                  format: "Food not dispensed"
    internal: true

  - name: "Feed Forward"
    id: feed_forward
    platform: gpio
    pin: 
      number: GPIO18
    restore_mode: ALWAYS_OFF
    on_turn_on:
      then:
        - switch.turn_on: enable_feeder_motor
    on_turn_off:
      then:
        - switch.turn_off: enable_feeder_motor
    internal: true

  - name: "Feeder Reverse"
    id: feed_reverse
    platform: gpio
    pin: 
      number: GPIO17
    restore_mode: ALWAYS_OFF
    internal: true

  - name: "Feed Cat"
    id: feed_cat
    platform: template
    turn_on_action:
      - lambda: |-
            id(scoops_count) = 0;
            id(food_sensor_count) = 0;
            id(max_scoops) = id(default_scoops);
      - switch.turn_on: feed_forward
      - logger.log:
          level: INFO
          format: "Serving %d scoops"
          args: [ id(max_scoops) ]

sensor:
  - platform: wifi_signal
    name: "$name signal"
    update_interval: 60s

button:
  - platform: restart
    name: Restart
type or paste code here
1 Like





4 Likes

Hey thanks for this, to boot into flash mode did you just hold the reset button? I’m having some issues getting it into flash mode and recognized by esphome.

yes, indeed u need to reset to get in flash mode

1 Like

With the newest model above, has anyone figured out a way to dispense less while still relying on the feed sensor? I would like more control than the minimum right now. I added a feed_cats_in_grams service that basically just runs the motor for 2000 ms like the original model from the OP but it doesn’t seem consistent. It does give out a lot less kibbles though.

Did anyone look into getting Petkit water fountains to do the same maybe? I can’t even find an information on what chip they use or any disassembly video, for that matter.

Answering myself here: Petkit Eversweet 3 Pro has Telink TLSR8258, essentially same device as in Mija thermometers. Alternative firmware does exist for the latter, these SoCs are definitely hackable: GitHub - pvvx/ATC_MiThermometer: Custom firmware for the Xiaomi Thermometers and Telink Flasher

I figured out the messaging protocol petkit has used to communicate between the ESP and the cortex M0. it seems like they have used a similar combination on many of their products actually. I havent figured out all the packet types or fields but Ive figured out enough of it to dispense food and blink leds or make beep sounds, detect the charger, detect if the door is shut or the food is running low. and to send commands and be sure they were received correctly. I’m working to replace the onboard esp8266 firmware directly now that we know its “language”. the cortex M0 firmware can stay as it is, their protocol is actually decently implemented.

@Sly.vester any update on that front? I wonder how much different is this feeder from the Solo model which has its ESPHome config complete? Petkit Fresh Element Solo Pet Feeder | devices.esphome.io

following up on the original post from @AndreyShpilevoy

thank you very much for your documentation getting the Fresh Element Feeder “HomeAssist-ified”! Worked on mine last week and it works perfectly well.

One hint: you may put the ESP Board into a heat shrink tube which would give you the option to just lay it on top of the base board which would eliminate the need for disassembling the lower unit. It will be hold in place and fits snugly under the grey cover. Did that for mine - no wireless connection issues (beside the usual low reception of the ESP) or thermal problems.

Greetings
Tom
(anybody has one Fresh Element as a spare parts feeder? I am searching for a motor with gearing which opens/closes the front door. Second one that I have was destroyed by my dog…)