Skip to content

Commit

Permalink
Merge pull request #9 from RobertD502/scanner_tag_support
Browse files Browse the repository at this point in the history
Add LavvieScanner, LavvieTag, and Cat activity support
  • Loading branch information
RobertD502 authored Feb 7, 2024
2 parents ceff00e + e96cb91 commit d6397ab
Show file tree
Hide file tree
Showing 11 changed files with 870 additions and 102 deletions.
62 changes: 43 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# LavvieBot S [![hacs_badge](https://img.shields.io/badge/HACS-Custom-orange.svg)](https://github.com/custom-components/hacs) ![GitHub manifest version (path)](https://img.shields.io/github/manifest-json/v/RobertD502/home-assistant-lavviebot?filename=custom_components%2Fpurrsong%2Fmanifest.json)
# Purrsong [![hacs_badge](https://img.shields.io/badge/HACS-Custom-orange.svg)](https://github.com/custom-components/hacs) ![GitHub manifest version (path)](https://img.shields.io/github/manifest-json/v/RobertD502/home-assistant-lavviebot?filename=custom_components%2Fpurrsong%2Fmanifest.json)
<a href="https://www.buymeacoffee.com/RobertD502" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="100" width="424"></a>
<a href="https://liberapay.com/RobertD502/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="100" width="300"></a>

Expand All @@ -8,40 +8,41 @@
***All proceeds go towards helping a local animal rescue.**


### Custom component for Home Assistant for monitoring LavvieBot S litter boxes and associated cats
### Custom component for Home Assistant for monitoring LavvieBot S litter boxes, LavvieScanners, LavvieTags, and associated cats

## Prior To Installation

You will need to create a new account in the PurrSong app and then share an invite from your primary account to the newly created account. If you use your primary account with this custom component, the primary account will get logged out off the app on your mobile device. This can be avoided by creating a dedicated account as previously mentioned.
> [!CAUTION]
> You will need to create a new account in the PurrSong app and then share an invite from your primary account to the newly created account. If you use your primary account with this custom component, the primary account will get logged out of the app on your mobile device.
> This can be avoided by creating a dedicated account as previously mentioned.
## Installation

**Minimum Home Assistant version requirement:** `2022.11.0`

### With HACS
#### With HACS (Recommended)
1. Open HACS Settings and add this repository (https://github.com/RobertD502/home-assistant-lavviebot)
as a Custom Repository (use **Integration** as the category).
2. The `LavvieBot S` page should automatically load (or find it in the HACS Store)
2. The `Purrsong` page should automatically load (or find it in the HACS Store)
3. Click `Install`

### Manual
#### Manual
From this repo, copy the `purrsong` directory from `custom_components` and place it inside of your Home Assistant Core installation's `custom_components` directory.

`Note`: If installing manually, in order to be alerted about new releases, you will need to subscribe to releases from this repository.
> [!WARNING]
> If installing manually, in order to be alerted about new releases, you will need to subscribe to releases from this repository.
## Setup
1. Install this integration.
2. Navigate to the Home Assistant Integrations page (Settings --> Devices & Services)
3. Click the `+ ADD INTEGRATION` button in the lower right-hand corner
4. Search for `Lavviebot` or `PurrSong`

Alternatively, click on the button below to add the integration:

[![Open your Home Assistant instance and start setting up a new integration.](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start/?domain=purrsong)
> [!Tip]
> If you are unable to use the button above, follow the steps below:
> 1. Navigate to the Home Assistant Integrations page (Settings --> Devices & Services)
> 2. Click the `+ ADD INTEGRATION` button in the lower right-hand corner
> 3. Search for `PurrSong`

## Features

Litter boxes and cats are exposed as devices along with their associated entities. See below for entities available.
Litter boxes, scanners, tags, and cats are exposed as devices along with their associated entities. See below for entities available.

##

Expand All @@ -50,7 +51,7 @@ Litter boxes and cats are exposed as devices along with their associated entitie

| Entity | Entity type | Description |
| --- | --- | --- |
| `Beacon battery` | `sensor` | Battery level for [LavvieBeacon Antenna Module](https://www.robotshop.com/en/lavviebeacon-antenna-module-lavvietag-lavviebot-s.html). State is `Unknown` if there is no LavvieBeacon associated with the litter box. |
| `Beacon battery` | `sensor` | Battery level for [LavvieBeacon Antenna Module](https://www.robotshop.com/en/lavviebeacon-antenna-module-lavvietag-lavviebot-s.html). State is `0` if there is no LavvieBeacon associated with the litter box. |
| `Error time` | `sensor` | When the error, displayed in the `Latest error` sensor, occurred. |
| `Humidity` | `sensor` | Humidity as reported by the litter box. |
| `Last cat used` | `sensor` | Name of the last cat that used the litter box. Value will be "Unknown" if cat named "Unknown" used the litter box last. |
Expand All @@ -68,7 +69,25 @@ Litter boxes and cats are exposed as devices along with their associated entitie
| `Wait time` | `sensor` | Minutes litter box is set to wait, after it has been used, before scooping. |
| `Waste drawer full` | `binary_sensor` | `On` if the waste drawer is full. Otherwise `Off`. Can be used to set up alerts. |
| `Waste status` | `sensor` | Descriptive status of the waste level in the waste drawer. Possible states include: <ul><li>Full</li><li>Almost Full</li><li>Empty or Piled</li> |
| `Firmware update` | `update` | If Lavviebot has a firmware update available, the version of the new firmware will be shown. If Lavviebot firmware is up-to-date, "Up-to-date" will be shown. |
| `Firmware update` | `update` | If Lavviebot has a firmware update available, the version of the new firmware will be shown. If Lavviebot firmware is up-to-date, "Up-to-date" will be shown. Use the PurrSong app to update firmware. |


### LavvieScanner

| Entity | Entity type | Description |
| --- | --- | --- |
| `WiFi status` | `binary_sensor` | Shows connection status between the LavvieScanner and your WiFi network. |
| `Firmware update` | `update` | If a firmware update is available, the version of the new firmware will be shown. If firmware is up-to-date, "Up-to-date" will be shown. Use the PurrSong app to update firmware. |
| `Last seen` | `sensor` | Displays date and time of the last time LavvieScanner communicated with PurrSong servers. |


### LavvieTag

| Entity | Entity type | Description |
| --- | --- | --- |
| `Battery` | `sensor` | Current battery percentage. |
| `Firmware update` | `update` | If a firmware update is available, the version of the new firmware will be shown. If firmware is up-to-date, "Up-to-date" will be shown. Use the PurrSong app to update firmware. |
| `Last seen` | `sensor` | Displays date and time of the last time LavvieTag communicated with PurrSong servers via LavvieScanner or LavvieBeacon. |


### Cat
Expand All @@ -77,6 +96,11 @@ Litter boxes and cats are exposed as devices along with their associated entitie
| --- | --- | --- |
| `Litter box use count` | `sensor` | Total number of times cat has used the litter box today. |
| `Litter box use duration` | `sensor` | Total length of time cat has used the litter box today (in seconds). |
| `Weight` | `sensor` | Most recent cat weight obtained for the current day |
| `Weight` | `sensor` | Most recent cat weight obtained for the current day. |
| `Resting` | `sensor` | `Only available if cat is using a LavvieTag` |
| `Running` | `sensor` | `Only available if cat is using a LavvieTag` |
| `Sleeping` | `sensor` | `Only available if cat is using a LavvieTag` |
| `Walking` | `sensor` | `Only available if cat is using a LavvieTag` |
| `Zoomies` | `sensor` | `Only available if cat is using a LavvieTag` |


26 changes: 14 additions & 12 deletions custom_components/purrsong/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
from lavviebot.exceptions import LavviebotAuthError

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL, CONF_USERNAME, CONF_PASSWORD, CONF_UNIQUE_ID
from homeassistant.const import CONF_EMAIL, CONF_USERNAME, CONF_PASSWORD
from homeassistant.core import HomeAssistant

from .const import DOMAIN, LOGGER, PLATFORMS
from .coordinator import LavviebotDataUpdateCoordinator
from .util import NoLitterBoxesError, NoUserIdError, async_validate_api
from .util import NoDevicesError, async_validate_api

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up PurrSong from a config entry."""
Expand All @@ -33,29 +33,31 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate old entry."""
# Also set PurrSong account user_id as config unique_id
if entry.version == 1:
email = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD]
if entry.version in [1,2]:
if entry.version == 1:
email = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD]
if entry.version ==2:
email = entry.data[CONF_EMAIL]
password = entry.data[CONF_PASSWORD]

try:
unique_id = await async_validate_api(hass, email, password)
except (LavviebotAuthError, ConnectionError, NoLitterBoxesError, NoUserIdError):
await async_validate_api(email, password)
except (LavviebotAuthError, ConnectionError, NoDevicesError):
return False

entry.version = 2
entry.version = 3

LOGGER.debug(f'Migrating PurrSong config entry unique id to {unique_id}')
LOGGER.debug(f'Migrating PurrSong config entry unique id to {email}')
hass.config_entries.async_update_entry(
entry,
title='PurrSong',
data={
**entry.data,
CONF_EMAIL: email,
CONF_PASSWORD: password,
CONF_UNIQUE_ID: unique_id

},
unique_id=unique_id,
unique_id=email
)

return True
81 changes: 77 additions & 4 deletions custom_components/purrsong/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
""" Binary Sensor platform for PurrSong integration."""
from __future__ import annotations

from lavviebot.model import LitterBox, Cat
from lavviebot.model import Cat, LavvieScanner, LitterBox

from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.components.binary_sensor import BinarySensorEntity, BinarySensorDeviceClass
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
Expand All @@ -21,8 +21,14 @@ async def async_setup_entry(

binary_sensors = []
for device_id, device_data in coordinator.data.litterboxes.items():
binary_sensors.append(StorageRefill(coordinator, device_id))
binary_sensors.append(WasteFull(coordinator, device_id))
binary_sensors.extend((
StorageRefill(coordinator, device_id),
WasteFull(coordinator, device_id)
))

# LavvieScanner
for device_id, device_data in coordinator.data.lavvie_scanners.items():
binary_sensors.append(ScannerWiFiStatus(coordinator, device_id))

async_add_entities(binary_sensors)

Expand Down Expand Up @@ -150,3 +156,70 @@ def icon(self) -> str:
return 'mdi:alert-octagram'
else:
return 'mdi:octagram-outline'


class ScannerWiFiStatus(CoordinatorEntity, BinarySensorEntity):
""" Representation of LavvieScanner WiFi Status Alert """

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


@property
def device_data(self) -> LavvieScanner:
""" Handle coordinator LavvieScanner data """

return self.coordinator.data.lavvie_scanners[self.device_id]

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

return {
"identifiers": {(DOMAIN, self.device_data.device_id), (DOMAIN, self.device_data.iot_code_tail)},
"name": self.device_data.device_name,
"manufacturer": "PurrSong",
"model": "LavvieScanner",
"sw_version": self.device_data.current_firmware
}

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

return str(self.device_data.device_id) + '_scanner_wifi_status'

@property
def name(self) -> str:
""" Return name of the entity """

return "WiFi status"

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

return True

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

return BinarySensorDeviceClass.PROBLEM

@property
def is_on(self) -> bool:
"""Return True if wifi problem."""

if self.device_data.wifi_status == False:
return True
else:
return False


@property
def icon(self) -> str:
"""Set icon based on storage level"""

return 'mdi:wifi'
22 changes: 9 additions & 13 deletions custom_components/purrsong/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import homeassistant.helpers.config_validation as cv

from .const import DEFAULT_NAME, DOMAIN
from .util import NoLitterBoxesError, NoUserIdError, async_validate_api
from .util import NoDevicesError, async_validate_api

DATA_SCHEMA = vol.Schema(
{
Expand All @@ -25,7 +25,7 @@
class LavviebotConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
""" Handle a config flow for Purrsong integration """

VERSION = 2
VERSION = 3

entry: config_entries.ConfigEntry | None

Expand All @@ -46,15 +46,13 @@ async def async_step_reauth_confirm(
email = user_input[CONF_EMAIL]
password = user_input[CONF_PASSWORD]
try:
user_id = await async_validate_api(self.hass, email, password)
await async_validate_api(email, password)
except LavviebotAuthError:
errors["base"] = "invalid_auth"
except ConnectionError:
errors["base"] = "cannot_connect"
except NoLitterBoxesError:
errors["base"] = "no_litter_boxes"
except NoUserIdError:
errors["base"] = "no_user_id"
except NoDevicesError:
errors["base"] = "no_devices"
else:
assert self.entry is not None

Expand Down Expand Up @@ -89,17 +87,15 @@ async def async_step_user(
email = user_input[CONF_EMAIL]
password = user_input[CONF_PASSWORD]
try:
user_id = await async_validate_api(self.hass, email, password)
await async_validate_api(email, password)
except LavviebotAuthError:
errors["base"] = "invalid_auth"
except ConnectionError:
errors["base"] = "cannot_connect"
except NoLitterBoxesError:
errors["base"] = "no_litter_boxes"
except NoUserIdError:
errors["base"] = "no_user_id"
except NoDevicesError:
errors["base"] = "no_devices"
else:
await self.async_set_unique_id(user_id)
await self.async_set_unique_id(email)
self._abort_if_unique_id_configured()

return self.async_create_entry(
Expand Down
5 changes: 1 addition & 4 deletions custom_components/purrsong/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,4 @@ async def _async_update_data(self) -> LavviebotData:
self.client.token = None
return await self._async_update_data()
else:
if not data.litterboxes:
raise UpdateFailed("No Litter Boxes found")
else:
return data
return data
6 changes: 3 additions & 3 deletions custom_components/purrsong/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"domain": "purrsong",
"name": "LavvieBot S",
"name": "Purrsong",
"codeowners": [
"@RobertD502"
],
Expand All @@ -11,7 +11,7 @@
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/RobertD502/home-assistant-lavviebot/issues",
"requirements": [
"lavviebotaio==0.2.2.1"
"lavviebotaio==0.3.0"
],
"version": "0.2.0"
"version": "0.3.0"
}
Loading

0 comments on commit d6397ab

Please sign in to comment.