Skip to content

Commit

Permalink
Merge pull request #15 from RobertD502/gemini_support
Browse files Browse the repository at this point in the history
Add support for Gemini and Eversweet solo 2
  • Loading branch information
RobertD502 authored Jun 20, 2023
2 parents f134e2e + 04f3e8e commit d8087d3
Show file tree
Hide file tree
Showing 12 changed files with 1,321 additions and 24 deletions.
52 changes: 50 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@ ___

Custom Home Assistant component for controlling and monitoring PetKit devices and pets.

`Currently Supported Devices:`
### Currently Supported Devices

`Feeders`:
- [Fresh Element Infinity](https://www.amazon.com/PETKIT-Automatic-Stainless-Programmable-Dispenser/dp/B09JFK8BCQ)
- [Fresh Element Solo](https://www.amazon.com/PETKIT-Automatic-Dispenser-Compatible-Freeze-Dried/dp/B09158J9PF/)
- [Fresh Element Mini Pro](https://www.amazon.com/PETKIT-Automatic-Stainless-Indicator-Dispenser-2-8L/dp/B08GS1CPHH/)
- [Fresh Element Gemini](https://www.amazon.com/PETKIT-Automatic-Combination-Dispenser-Stainless/dp/B0BF56RTQH)

`Water Fountains`:
- [Eversweet Solo 2 Water Fountain](https://www.amazon.com/PETKIT-EVERSWEET-Wireless-Visualization-Dispenser-2L/dp/B0B3RWF653)
- [Eversweet 3 Pro Water Fountain](https://www.amazon.com/PETKIT-Wireless-Fountain-Stainless-Dispenser/dp/B09QRH6L3M/)
- [Eversweet 5 Mini Water Fountain](https://www.petkit.nl/products/eversweet-5-mini-binnen-2-weken-geleverd)

`Litter Boxes`:
- [Pura X Litter Box](https://www.amazon.com/PETKIT-Self-Cleaning-Scooping-Automatic-Multiple/dp/B08T9CCP1M)
- [Pura MAX Litter Box with/without Pura Air deodorizer](https://www.amazon.com/PETKIT-Self-Cleaning-Capacity-Multiple-Automatic/dp/B09KC7Q4YF)

Expand Down Expand Up @@ -185,11 +193,51 @@ Each Feeder has the following entities:
> Friendly Note: Mini feeders have a bug when batteries are installed that has never been addressed by PetKit. This can result in your device locking up until the batteries are removed and feeder is power cycled. I am not sure what triggers this bug, but as a safety measure I don't install batteries into mini feeders.
</details>
<details>
<summary> <b>Fresh Element Gemini</b> (<i>click to expand</i>)</summary>
<!---->
<br/>
Each Feeder has the following entities:
<br/>

| Entity | Entity Type | Additional Comments |
|-----------------------------|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `Cancel manual feed` | `Button` | - Only available if your feeder is online (connected to PetKit's servers). <br/>- Will cancel a manual feeding that is currently in progress. |
| `Indicator light` | `Switch` | Only available if your feeder is online (connected to PetKit's servers). |
| `Manual feed` | `Text` | - Allows setting the amount of portions to dispense immediately from hopper 1 and 2. <br/>- Only available if your feeder is online (connected to PetKit's servers). <br/>- Valid text is in the form `number,number`. For example, setting a value of 1,2 will dispense one portion from hopper 1 and 2 portions from hopper 2. Allowed portion sizes for each hooper range from and include 0 to 10. |
| `Desiccant days remaining` | `Sensor` | Number of days left before the desiccant needs to be replaced. |
| `Food level hopper 1` | `Binary Sensor` | Allows for determining if there is a food shortage in hopper 1. |
| `Food level hopper 2` | `Binary Sensor` | Allows for determining if there is a food shortage in hopper 2. |
| `Child lock` | `Switch` | Only available if your feeder is online (connected to PetKit's servers). |
| `Dispense tone` | `Switch` | Only available if your feeder is online (connected to PetKit's servers). |
| `Food replenished` | `Button` | Tells the PetKit servers that you have replenished hopper 1 and 2 with food. If you don't do this after replenishing food, the food level for hoppers 1 and 2 won't update until a manual feeding is sent or until the next scheduled feeding. |
| `Food shortage alarm` | `Switch` | Only available if your feeder is online (connected to PetKit's servers). |
| `Minimum eating duration` | `Number` | Minimum amount of time, in seconds, pet has to eat food before it is recorded. |
| `Reset desiccant` | `Button` | - Allows you to reset the desiccant back to 30 days after replacing it. <br/>- Only available if your feeder is online (connected to PetKit's servers). |
| `Average eating time` | `Sensor` | Average amount of time pet(s) have spent eating food. |
| `Battery installed` | `Binary Sensor` | If batteries are removed or installed, power cycling the feeder is required for the status to update. |
| `Battery status` | `Sensor` | - Will only become available when feeder is running on batteries. <br/>- Indicates the battery level (Normal or Low). |
| `Dispensed hopper 1` | `Sensor` | Amount of portions dispensed from hopper 1 today. |
| `Dispensed hopper 2` | `Sensor` | Amount of portions dispensed from hopper 2 today. |
| `Error` | `Sensor` | Identifies any errors reported by the feeder. |
| `Manually dispensed hopper 1` | `Sensor` | Amount of portions manually dispensed from hopper 1 today. |
| `Manually dispensed hopper 2` | `Sensor` | Amount of portions manually dispensed from hopper 2 today. |
| `Planned dispensed hopper 1` | `Sensor` | Of the planned amount that is to be dispensed today, amount of portions that have been dispensed from hopper 1. |
| `Planned dispensed hopper 2` | `Sensor` | Of the planned amount that is to be dispensed today, amount of portions that have been dispensed from hopper 2. |
| `Planned hopper 1` | `Sensor` | Amount of portions that the feeder plans to dispense from hopper 1 today. |
| `Planned hopper 2` | `Sensor` | Amount of portions that the feeder plans to dispense from hopper 2 today. |
| `RSSI` | `Sensor` | WiFi connection strength. |
| `Status` | `Sensor` | `Normal` = Feeder is connected to PetKit's servers <br/>`Offline` = Feeder is not connected to PetKit servers <br/>`On Batteries` = If installed, feeder is currently being powered by the batteries. |
| `Times dispensed` | `Sensor` | Number of times food has been dispensed today. |
| `Times eaten` | `Sensor` | Number of times pet(s) ate today. |

</details>

## Water Fountains
___

<details>
<summary> <b>Eversweet 3 Pro/5 Mini</b> (<i>click to expand</i>)</summary>
<summary> <b>Eversweet 3 Pro/5 Mini/Solo 2</b> (<i>click to expand</i>)</summary>
<!---->
<br/>
Each water fountain has the following entities:
Expand Down
159 changes: 152 additions & 7 deletions custom_components/petkit/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,26 @@ async def async_setup_entry(
)

for feeder_id, feeder_data in coordinator.data.feeders.items():
# All Feeders
binary_sensors.append(
FoodLevel(coordinator, feeder_id)
)

# D4 Feeder
if feeder_data.type == 'd4':
#All feeders except D4s
if feeder_data.type != 'd4s':
binary_sensors.append(
FoodLevel(coordinator, feeder_id)
)

# D4 and D4s feeders
if feeder_data.type in ['d4', 'd4s']:
binary_sensors.append(
BatteryInstalled(coordinator, feeder_id)
)

# D4s Feeder
if feeder_data.type == 'd4s':
binary_sensors.extend((
FoodLevelHopper1(coordinator, feeder_id),
FoodLevelHopper2(coordinator, feeder_id)
))

# D3 Feeder
if feeder_data.type == 'd3':
binary_sensors.append(
Expand Down Expand Up @@ -270,7 +279,7 @@ def entity_category(self) -> EntityCategory:

@property
def is_on(self) -> bool:
"""Return True if food needs to be added."""
"""Return True if battery installed."""

if self.feeder_data.data['state']['batteryPower'] == 1:
return True
Expand Down Expand Up @@ -627,3 +636,139 @@ def is_on(self) -> bool:
"""Return True if deodorizer is empty."""

return self.lb_data.manually_paused


class FoodLevelHopper1(CoordinatorEntity, BinarySensorEntity):
"""Representation of Feeder lack of food warning for Hopper 1."""

def __init__(self, coordinator, feeder_id):
super().__init__(coordinator)
self.feeder_id = feeder_id

@property
def feeder_data(self) -> Feeder:
"""Handle coordinator Feeder data."""

return self.coordinator.data.feeders[self.feeder_id]

@property
def device_info(self) -> dict[str, Any]:
"""Return device registry information for this entity."""

return {
"identifiers": {(DOMAIN, self.feeder_data.id)},
"name": self.feeder_data.data['name'],
"manufacturer": "PetKit",
"model": FEEDERS[self.feeder_data.type],
"sw_version": f'{self.feeder_data.data["firmware"]}'
}

@property
def unique_id(self) -> str:
"""Sets unique ID for this entity."""

return str(self.feeder_data.id) + '_food_level_hopper_1'

@property
def has_entity_name(self) -> bool:
"""Indicate that entity has name defined."""

return True

@property
def translation_key(self) -> str:
"""Translation key for this entity."""

return "food_level_hopper_one"

@property
def device_class(self) -> BinarySensorDeviceClass:
"""Return entity device class."""

return BinarySensorDeviceClass.PROBLEM

@property
def is_on(self) -> bool:
"""Return True if food needs to be added."""

if self.feeder_data.data['state']['food1'] < 1:
return True
else:
return False

@property
def icon(self) -> str:
"""Set icon."""

if self.feeder_data.data['state']['food1'] == 0:
return 'mdi:food-drumstick-off'
else:
return 'mdi:food-drumstick'


class FoodLevelHopper2(CoordinatorEntity, BinarySensorEntity):
"""Representation of Feeder lack of food warning for Hopper 2."""

def __init__(self, coordinator, feeder_id):
super().__init__(coordinator)
self.feeder_id = feeder_id

@property
def feeder_data(self) -> Feeder:
"""Handle coordinator Feeder data."""

return self.coordinator.data.feeders[self.feeder_id]

@property
def device_info(self) -> dict[str, Any]:
"""Return device registry information for this entity."""

return {
"identifiers": {(DOMAIN, self.feeder_data.id)},
"name": self.feeder_data.data['name'],
"manufacturer": "PetKit",
"model": FEEDERS[self.feeder_data.type],
"sw_version": f'{self.feeder_data.data["firmware"]}'
}

@property
def unique_id(self) -> str:
"""Sets unique ID for this entity."""

return str(self.feeder_data.id) + '_food_level_hopper_2'

@property
def has_entity_name(self) -> bool:
"""Indicate that entity has name defined."""

return True

@property
def translation_key(self) -> str:
"""Translation key for this entity."""

return "food_level_hopper_two"

@property
def device_class(self) -> BinarySensorDeviceClass:
"""Return entity device class."""

return BinarySensorDeviceClass.PROBLEM

@property
def is_on(self) -> bool:
"""Return True if food needs to be added."""

if self.feeder_data.data['state']['food2'] < 1:
return True
else:
return False

@property
def icon(self) -> str:
"""Set icon."""

if self.feeder_data.data['state']['food2'] == 0:
return 'mdi:food-drumstick-off'
else:
return 'mdi:food-drumstick'
79 changes: 77 additions & 2 deletions custom_components/petkit/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ async def async_setup_entry(
ResetDesiccant(coordinator, feeder_id)
)

# D3 and D4
if feeder_data.type in ['d3', 'd4']:
# D3, D4, and D4s
if feeder_data.type in ['d3', 'd4', 'd4s']:
buttons.append(
CancelManualFeed(coordinator, feeder_id)
)
Expand All @@ -54,6 +54,12 @@ async def async_setup_entry(
CallPet(coordinator, feeder_id)
)

# D4s
if feeder_data.type == 'd4s':
buttons.append(
FoodReplenished(coordinator, feeder_id)
)

# Litter boxes
for lb_id, lb_data in coordinator.data.litter_boxes.items():
# Pura X & Pura MAX
Expand Down Expand Up @@ -1294,3 +1300,72 @@ async def async_press(self) -> None:
await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.RESUME_LITTER_DUMP)
await asyncio.sleep(1.5)
await self.coordinator.async_request_refresh()


class FoodReplenished(CoordinatorEntity, ButtonEntity):
"""Representation of food replenished command button."""

def __init__(self, coordinator, feeder_id):
super().__init__(coordinator)
self.feeder_id = feeder_id

@property
def feeder_data(self) -> Feeder:
"""Handle coordinator Feeder data."""

return self.coordinator.data.feeders[self.feeder_id]

@property
def device_info(self) -> dict[str, Any]:
"""Return device registry information for this entity."""

return {
"identifiers": {(DOMAIN, self.feeder_data.id)},
"name": self.feeder_data.data['name'],
"manufacturer": "PetKit",
"model": FEEDERS[self.feeder_data.type],
"sw_version": f'{self.feeder_data.data["firmware"]}'
}

@property
def unique_id(self) -> str:
"""Sets unique ID for this entity."""

return str(self.feeder_data.id) + '_food_replenished'

@property
def has_entity_name(self) -> bool:
"""Indicate that entity has name defined."""

return True

@property
def translation_key(self) -> str:
"""Translation key for this entity."""

return "food_replenished"

@property
def available(self) -> bool:
"""Only make available if device is online."""

if self.feeder_data.data['state']['pim'] != 0:
return True
else:
return False

@property
def entity_category(self) -> EntityCategory:
"""Set category to config."""

return EntityCategory.CONFIG

async def async_press(self) -> None:
"""Handle the button press."""

await self.coordinator.client.food_replenished(self.feeder_data)

self.feeder_data.data['state']['food1'] = 1
self.feeder_data.data['state']['food2'] = 1
self.async_write_ha_state()
await self.coordinator.async_request_refresh()
3 changes: 3 additions & 0 deletions custom_components/petkit/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
Platform.TEXT
]

DEFAULT_NAME = "PetKit"
Expand Down Expand Up @@ -64,12 +65,14 @@
WATER_FOUNTAINS = {
2: 'Eversweet 5 Mini',
4: 'Eversweet 3 Pro',
5: 'Eversweet Solo 2'
}


FEEDERS = {
'd3': 'Fresh Element Infinity',
'd4': 'Fresh Element Solo',
'd4s': 'Fresh Element Gemini',
'feedermini': 'Fresh Element Mini Pro',
}

Expand Down
4 changes: 2 additions & 2 deletions custom_components/petkit/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/RobertD502/home-assistant-petkit/issues",
"requirements": ["petkitaio==0.1.2", "tzlocal>=4.2"],
"version": "0.1.2"
"requirements": ["petkitaio==0.1.3", "tzlocal>=4.2"],
"version": "0.1.3"
}
Loading

0 comments on commit d8087d3

Please sign in to comment.