Skip to content

Commit

Permalink
Merge pull request #16 from RobertD502/magicube_support
Browse files Browse the repository at this point in the history
Add Air Magicube support
  • Loading branch information
RobertD502 authored Jun 21, 2023
2 parents d8087d3 + 36a986d commit 289bdb6
Show file tree
Hide file tree
Showing 9 changed files with 920 additions and 12 deletions.
38 changes: 33 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,24 @@ Custom Home Assistant component for controlling and monitoring PetKit devices an

### Currently Supported Devices

`Feeders`:
`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`:
`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)

`Purifiers`
- [Air Magicube](https://www.instachew.com/product-page/petkit-air-magicube-smart-odor-eliminator)

`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)

#### Bluetooth only devices that don't use PetKit's BLE relay such as trackers (i.e., PetKit Fit) will not be supported: syncing new data from a bluetooth tracker requires the PetKit mobile app to communicate with the tracker which is not possible when your PetKit account is already in use with this integration.

Expand Down Expand Up @@ -563,6 +567,30 @@ Each litter box has the following entities:
</details>
</details>

## Purifiers
___

<details>
<summary> <b>Air Magicube</b> (<i>click to expand</i>)</summary>
<!---->
<br/>
Each purifier has the following entities:
<br/>

| Entity | Entity Type | Additional Comments |
|-------------------|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `Indicator light` | `Switch` | Only available if your purifier is online (connected to PetKit's servers) |
| `Prompt tone` | `Switch` | Only available if your purifier is online (connected to PetKit's servers). |
| `Purifier` | `Fan` | - Only available if purifier is online (Connected to PetKit's servers). <br/>- Can turn the purifier `On` and `Off` and view/change the current mode to one of the following: `auto`, `silent`, `standard`, `strong`. |
| `Air purified` | `Sensor` | |
| `Humidity` | `Sensor` | |
| `Temperature` | `Sensor` | |
| `Error` | `Sensor` | Displays any error messages. `Note:` Even though it isn't really an error, An error message is displayed by PetKit if the purifier is in standby mode. |
| `Liquid` | `Sensor` | Percent of purifying liquid that remains in the purifier. |
| `RSSI` | `Sensor` | WiFi connection strength. |

</details>

## Pets
___

Expand Down
23 changes: 22 additions & 1 deletion custom_components/petkit/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from aiohttp.client_exceptions import ClientConnectionError
from petkitaio.exceptions import AuthError, PetKitError
from petkitaio.constants import W5Command
from petkitaio.constants import W5Command, PurifierCommand

from homeassistant.const import Platform

Expand All @@ -17,6 +17,7 @@
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.FAN,
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Expand Down Expand Up @@ -68,6 +69,26 @@
5: 'Eversweet Solo 2'
}

PURIFIERS = {
'k2': 'Air Magicube'
}

PURIFIER_MODE_TO_COMMAND = {
'auto': PurifierCommand.AUTO_MODE,
'silent': PurifierCommand.SILENT_MODE,
'standard': PurifierCommand.STANDARD_MODE,
'strong': PurifierCommand.STRONG_MODE
}

PURIFIER_MODES = ['auto', 'silent', 'standard', 'strong']

PURIFIER_MODE_NAMED = {
0: 'auto',
1: 'silent',
2: 'standard',
3: 'strong'
}


FEEDERS = {
'd3': 'Fresh Element Infinity',
Expand Down
154 changes: 154 additions & 0 deletions custom_components/petkit/fan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
"""Fan platform for PetKit integration."""
from __future__ import annotations

from typing import Any
import asyncio

from petkitaio.constants import PurifierCommand
from petkitaio.model import Purifier

from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import (
DOMAIN,
PURIFIERS,
PURIFIER_MODES,
PURIFIER_MODE_NAMED,
PURIFIER_MODE_TO_COMMAND
)

from .coordinator import PetKitDataUpdateCoordinator


async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set Up PetKit Fan Entities."""

coordinator: PetKitDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]

fans = []

for purifier_id, purifier_data in coordinator.data.purifiers.items():
fans.append(
Purifier(coordinator, purifier_id)
)

async_add_entities(fans)


class Purifier(CoordinatorEntity, FanEntity):
"""Representation of a PetKit air purifier."""

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

@property
def purifier_data(self) -> Purifier:
"""Handle coordinator Purifier data."""

return self.coordinator.data.purifiers[self.purifier_id]

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

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

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

return str(self.purifier_data.id) + '_purifier'

@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 "purifier"

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

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

@property
def is_on(self) -> bool:
"""Determine if the purifier is On."""

if self.purifier_data.device_detail['state']['power'] in [1,2]:
return True
else:
return False

@property
def preset_modes(self) -> list:
"""Return the available preset modes."""

return PURIFIER_MODES

@property
def preset_mode(self) -> str:
"""Return the current preset mode."""

mode = self.purifier_data.device_detail['state']['mode']
return PURIFIER_MODE_NAMED[mode]

@property
def supported_features(self) -> int:
"""Return supported features."""

return FanEntityFeature.PRESET_MODE

async def async_turn_on(self, **kwargs) -> None:
"""Turn the air purifier on."""

await self.coordinator.client.control_purifier(self.purifier_data, PurifierCommand.POWER)
self.purifier_data.device_detail['state']['power'] = 1
self.async_write_ha_state()
# Have to wait before refreshing or PetKit will return wrong power state
await asyncio.sleep(1)
await self.coordinator.async_request_refresh()

async def async_turn_off(self, **kwargs) -> None:
"""Turn the air purifier off."""

await self.coordinator.client.control_purifier(self.purifier_data, PurifierCommand.POWER)
self.purifier_data.device_detail['state']['power'] = 0
self.async_write_ha_state()
# Have to wait before refreshing or PetKit will return wrong power state
await asyncio.sleep(1)
await self.coordinator.async_request_refresh()

async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set a preset mode for the purifier."""

command = PURIFIER_MODE_TO_COMMAND[preset_mode]
await self.coordinator.client.control_purifier(self.purifier_data, command)
MODE_TO_VALUE = {v: k for (k, v) in PURIFIER_MODE_NAMED.items()}
value = MODE_TO_VALUE.get(preset_mode)
self.purifier_data.device_detail['state']['mode'] = value
# Have to wait before refreshing or PetKit will return wrong mode state
await asyncio.sleep(1)
await self.coordinator.async_request_refresh()
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.3", "tzlocal>=4.2"],
"version": "0.1.3"
"requirements": ["petkitaio==0.1.4", "tzlocal>=4.2"],
"version": "0.1.4"
}
Loading

0 comments on commit 289bdb6

Please sign in to comment.