Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions homeassistant/components/litterrobot/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ async def async_step_user(self, user_input=None):
hub = LitterRobotHub(self.hass, user_input)
try:
await hub.login()
return self.async_create_entry(
title=user_input[CONF_USERNAME], data=user_input
)
except LitterRobotLoginException:
errors["base"] = "invalid_auth"
except LitterRobotException:
Expand All @@ -46,6 +43,11 @@ async def async_step_user(self, user_input=None):
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"

if not errors:
return self.async_create_entry(
title=user_input[CONF_USERNAME], data=user_input
)

return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
)
21 changes: 10 additions & 11 deletions homeassistant/components/litterrobot/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from types import MethodType
from typing import Any, Optional

from pylitterbot import Account, Robot
import pylitterbot
from pylitterbot.exceptions import LitterRobotException, LitterRobotLoginException

from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
Expand Down Expand Up @@ -49,7 +49,7 @@ async def _async_update_data():
async def login(self, load_robots: bool = False):
"""Login to Litter-Robot."""
self.logged_in = False
self.account = Account()
self.account = pylitterbot.Account()
try:
await self.account.connect(
username=self._data[CONF_USERNAME],
Expand All @@ -69,11 +69,11 @@ async def login(self, load_robots: bool = False):
class LitterRobotEntity(CoordinatorEntity):
"""Generic Litter-Robot entity representing common data and methods."""

def __init__(self, robot: Robot, entity_type: str, hub: LitterRobotHub):
def __init__(self, robot: pylitterbot.Robot, entity_type: str, hub: LitterRobotHub):
"""Pass coordinator to CoordinatorEntity."""
super().__init__(hub.coordinator)
self.robot = robot
self.entity_type = entity_type if entity_type else ""
self.entity_type = entity_type
self.hub = hub

@property
Expand All @@ -89,22 +89,21 @@ def unique_id(self):
@property
def device_info(self):
"""Return the device information for a Litter-Robot."""
model = "Litter-Robot 3 Connect"
if not self.robot.serial.startswith("LR3C"):
model = "Other Litter-Robot Connected Device"
return {
"identifiers": {(DOMAIN, self.robot.serial)},
"name": self.robot.name,
"manufacturer": "Litter-Robot",
"model": model,
"model": self.robot.model,
}

async def perform_action_and_refresh(self, action: MethodType, *args: Any):
"""Perform an action and initiates a refresh of the robot data after a few seconds."""

async def async_call_later_callback(*_) -> None:
await self.hub.coordinator.async_request_refresh()

await action(*args)
async_call_later(
self.hass, REFRESH_WAIT_TIME, self.hub.coordinator.async_request_refresh
)
async_call_later(self.hass, REFRESH_WAIT_TIME, async_call_later_callback)
Comment thread
natekspencer marked this conversation as resolved.

@staticmethod
def parse_time_at_default_timezone(time_str: str) -> Optional[time]:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/litterrobot/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"name": "Litter-Robot",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/litterrobot",
"requirements": ["pylitterbot==2021.2.5"],
"requirements": ["pylitterbot==2021.2.8"],
"codeowners": ["@natekspencer"]
}
93 changes: 63 additions & 30 deletions homeassistant/components/litterrobot/sensor.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,44 @@
"""Support for Litter-Robot sensors."""
from homeassistant.const import PERCENTAGE
from typing import Optional

from pylitterbot.robot import Robot

from homeassistant.const import DEVICE_CLASS_TIMESTAMP, PERCENTAGE
from homeassistant.helpers.entity import Entity

from .const import DOMAIN
from .hub import LitterRobotEntity

WASTE_DRAWER = "Waste Drawer"
from .hub import LitterRobotEntity, LitterRobotHub


async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Litter-Robot sensors using config entry."""
hub = hass.data[DOMAIN][config_entry.entry_id]

entities = []
for robot in hub.account.robots:
entities.append(LitterRobotSensor(robot, WASTE_DRAWER, hub))
def icon_for_gauge_level(gauge_level: Optional[int] = None, offset: int = 0) -> str:
"""Return a gauge icon valid identifier."""
if gauge_level is None or gauge_level <= 0 + offset:
return "mdi:gauge-empty"
if gauge_level > 70 + offset:
return "mdi:gauge-full"
if gauge_level > 30 + offset:
return "mdi:gauge"
return "mdi:gauge-low"

if entities:
async_add_entities(entities, True)

class LitterRobotPropertySensor(LitterRobotEntity, Entity):
"""Litter-Robot property sensors."""

class LitterRobotSensor(LitterRobotEntity, Entity):
"""Litter-Robot sensors."""
def __init__(
self, robot: Robot, entity_type: str, hub: LitterRobotHub, sensor_attribute: str
):
"""Pass coordinator to CoordinatorEntity."""
super().__init__(robot, entity_type, hub)
self.sensor_attribute = sensor_attribute

@property
def state(self):
"""Return the state."""
return self.robot.waste_drawer_gauge
return getattr(self.robot, self.sensor_attribute)


class LitterRobotWasteSensor(LitterRobotPropertySensor, Entity):
"""Litter-Robot sensors."""

@property
def unit_of_measurement(self):
Expand All @@ -36,19 +48,40 @@ def unit_of_measurement(self):
@property
def icon(self):
"""Return the icon to use in the frontend, if any."""
if self.robot.waste_drawer_gauge <= 10:
return "mdi:gauge-empty"
if self.robot.waste_drawer_gauge < 50:
return "mdi:gauge-low"
if self.robot.waste_drawer_gauge <= 90:
return "mdi:gauge"
return "mdi:gauge-full"
return icon_for_gauge_level(self.state, 10)


class LitterRobotSleepTimeSensor(LitterRobotPropertySensor, Entity):
"""Litter-Robot sleep time sensors."""

@property
def state(self):
"""Return the state."""
if self.robot.sleep_mode_active:
return super().state.isoformat()
return None

@property
def device_state_attributes(self):
"""Return device specific state attributes."""
return {
"cycle_count": self.robot.cycle_count,
"cycle_capacity": self.robot.cycle_capacity,
"cycles_after_drawer_full": self.robot.cycles_after_drawer_full,
}
def device_class(self):
"""Return the device class, if any."""
return DEVICE_CLASS_TIMESTAMP


ROBOT_SENSORS = [
(LitterRobotWasteSensor, "Waste Drawer", "waste_drawer_gauge"),
(LitterRobotSleepTimeSensor, "Sleep Mode Start Time", "sleep_mode_start_time"),
(LitterRobotSleepTimeSensor, "Sleep Mode End Time", "sleep_mode_end_time"),
]


async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Litter-Robot sensors using config entry."""
hub = hass.data[DOMAIN][config_entry.entry_id]

entities = []
for robot in hub.account.robots:
for (sensor_class, entity_type, sensor_attribute) in ROBOT_SENSORS:
entities.append(sensor_class(robot, entity_type, hub, sensor_attribute))

if entities:
async_add_entities(entities, True)
6 changes: 3 additions & 3 deletions homeassistant/components/litterrobot/switch.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""Support for Litter-Robot switches."""
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.components.switch import SwitchEntity

from .const import DOMAIN
from .hub import LitterRobotEntity


class LitterRobotNightLightModeSwitch(LitterRobotEntity, ToggleEntity):
class LitterRobotNightLightModeSwitch(LitterRobotEntity, SwitchEntity):
"""Litter-Robot Night Light Mode Switch."""

@property
Expand All @@ -27,7 +27,7 @@ async def async_turn_off(self, **kwargs):
await self.perform_action_and_refresh(self.robot.set_night_light, False)


class LitterRobotPanelLockoutSwitch(LitterRobotEntity, ToggleEntity):
class LitterRobotPanelLockoutSwitch(LitterRobotEntity, SwitchEntity):
"""Litter-Robot Panel Lockout Switch."""

@property
Expand Down
37 changes: 10 additions & 27 deletions homeassistant/components/litterrobot/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
VacuumEntity,
)
from homeassistant.const import STATE_OFF
import homeassistant.util.dt as dt_util

from .const import DOMAIN
from .hub import LitterRobotEntity
Expand Down Expand Up @@ -54,27 +53,22 @@ def supported_features(self):
def state(self):
"""Return the state of the cleaner."""
switcher = {
Robot.UnitStatus.CCP: STATE_CLEANING,
Robot.UnitStatus.EC: STATE_CLEANING,
Robot.UnitStatus.CCC: STATE_DOCKED,
Robot.UnitStatus.CST: STATE_DOCKED,
Robot.UnitStatus.DF1: STATE_DOCKED,
Robot.UnitStatus.DF2: STATE_DOCKED,
Robot.UnitStatus.RDY: STATE_DOCKED,
Robot.UnitStatus.CLEAN_CYCLE: STATE_CLEANING,
Robot.UnitStatus.EMPTY_CYCLE: STATE_CLEANING,
Robot.UnitStatus.CLEAN_CYCLE_COMPLETE: STATE_DOCKED,
Robot.UnitStatus.CAT_SENSOR_TIMING: STATE_DOCKED,
Robot.UnitStatus.DRAWER_FULL_1: STATE_DOCKED,
Robot.UnitStatus.DRAWER_FULL_2: STATE_DOCKED,
Robot.UnitStatus.READY: STATE_DOCKED,
Robot.UnitStatus.OFF: STATE_OFF,
}

return switcher.get(self.robot.unit_status, STATE_ERROR)

@property
def error(self):
"""Return the error associated with the current state, if any."""
return self.robot.unit_status.value

@property
def status(self):
"""Return the status of the cleaner."""
return f"{self.robot.unit_status.value}{' (Sleeping)' if self.robot.is_sleeping else ''}"
return f"{self.robot.unit_status.label}{' (Sleeping)' if self.robot.is_sleeping else ''}"

async def async_turn_on(self, **kwargs):
"""Turn the cleaner on, starting a clean cycle."""
Expand Down Expand Up @@ -119,22 +113,11 @@ async def async_send_command(self, command, params=None, **kwargs):
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
[sleep_mode_start_time, sleep_mode_end_time] = [None, None]

if self.robot.sleep_mode_active:
sleep_mode_start_time = dt_util.as_local(
self.robot.sleep_mode_start_time
).strftime("%H:%M:00")
sleep_mode_end_time = dt_util.as_local(
self.robot.sleep_mode_end_time
).strftime("%H:%M:00")

return {
"clean_cycle_wait_time_minutes": self.robot.clean_cycle_wait_time_minutes,
"is_sleeping": self.robot.is_sleeping,
"sleep_mode_start_time": sleep_mode_start_time,
"sleep_mode_end_time": sleep_mode_end_time,
"sleep_mode_active": self.robot.sleep_mode_active,
"power_status": self.robot.power_status,
"unit_status_code": self.robot.unit_status.name,
"unit_status_code": self.robot.unit_status.value,
"last_seen": self.robot.last_seen,
}
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1504,7 +1504,7 @@ pylibrespot-java==0.1.0
pylitejet==0.3.0

# homeassistant.components.litterrobot
pylitterbot==2021.2.5
pylitterbot==2021.2.8

# homeassistant.components.loopenergy
pyloopenergy==0.2.1
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ pylibrespot-java==0.1.0
pylitejet==0.3.0

# homeassistant.components.litterrobot
pylitterbot==2021.2.5
pylitterbot==2021.2.8

# homeassistant.components.lutron_caseta
pylutron-caseta==0.9.0
Expand Down
Loading