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
5 changes: 5 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,11 @@ omit =
homeassistant/components/roku/__init__.py
homeassistant/components/roku/media_player.py
homeassistant/components/roku/remote.py
homeassistant/components/roomba/binary_sensor.py
homeassistant/components/roomba/braava.py
homeassistant/components/roomba/irobot_base.py
homeassistant/components/roomba/roomba.py
homeassistant/components/roomba/sensor.py
homeassistant/components/roomba/vacuum.py
homeassistant/components/route53/*
homeassistant/components/rova/sensor.py
Expand Down
2 changes: 1 addition & 1 deletion CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ homeassistant/components/rfxtrx/* @danielhiversen
homeassistant/components/ring/* @balloob
homeassistant/components/rmvtransport/* @cgtobi
homeassistant/components/roku/* @ctalkington
homeassistant/components/roomba/* @pschmitt @cyr-ius
homeassistant/components/roomba/* @pschmitt @cyr-ius @shenxn
homeassistant/components/safe_mode/* @home-assistant/core
homeassistant/components/saj/* @fredericvl
homeassistant/components/salt/* @bjornorri
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/roomba/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ async def async_setup_entry(hass, config_entry):
continuous=config_entry.options[CONF_CONTINUOUS],
delay=config_entry.options[CONF_DELAY],
)
roomba.exclude = "wifistat" # ignore wifistat to avoid unnecessary updates

try:
if not await async_connect_or_timeout(hass, roomba):
Expand Down
34 changes: 6 additions & 28 deletions homeassistant/components/roomba/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from . import roomba_reported_state
from .const import BLID, DOMAIN, ROOMBA_SESSION
from .irobot_base import IRobotEntity

_LOGGER = logging.getLogger(__name__)

Expand All @@ -17,23 +18,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
status = roomba_reported_state(roomba).get("bin", {})
if "full" in status:
roomba_vac = RoombaBinStatus(roomba, blid)
roomba_vac.register_callback()
Comment thread
shenxn marked this conversation as resolved.
async_add_entities([roomba_vac], True)


class RoombaBinStatus(BinarySensorDevice):
class RoombaBinStatus(IRobotEntity, BinarySensorDevice):
"""Class to hold Roomba Sensor basic info."""

ICON = "mdi:delete-variant"

def __init__(self, roomba, blid):
"""Initialize the sensor object."""
self.vacuum = roomba
self.vacuum_state = roomba_reported_state(roomba)
self._blid = blid
self._name = self.vacuum_state.get("name")
self._identifier = f"roomba_{self._blid}"
self._bin_status = None

@property
def name(self):
"""Return the name of the sensor."""
Expand All @@ -52,23 +45,8 @@ def icon(self):
@property
def state(self):
"""Return the state of the sensor."""
return self._bin_status

@property
def device_info(self):
"""Return the device info of the vacuum cleaner."""
return {
"identifiers": {(DOMAIN, self._identifier)},
"name": str(self._name),
}

async def async_update(self):
"""Return the update info of the vacuum cleaner."""
# No data, no update
if not self.vacuum.master_state:
_LOGGER.debug("Roomba %s has no data yet. Skip update", self.name)
return
self._bin_status = (
bin_status = (
roomba_reported_state(self.vacuum).get("bin", {}).get("full", False)
)
_LOGGER.debug("Update Full Bin status from the vacuum: %s", self._bin_status)
_LOGGER.debug("Update Full Bin status from the vacuum: %s", bin_status)
Comment thread
shenxn marked this conversation as resolved.
return bin_status
135 changes: 135 additions & 0 deletions homeassistant/components/roomba/braava.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""Class for Braava devices."""
import logging

from homeassistant.components.vacuum import SUPPORT_FAN_SPEED

from .irobot_base import SUPPORT_IROBOT, IRobotVacuum

_LOGGER = logging.getLogger(__name__)

ATTR_DETECTED_PAD = "detected_pad"
ATTR_LID_CLOSED = "lid_closed"
ATTR_TANK_PRESENT = "tank_present"
ATTR_TANK_LEVEL = "tank_level"
ATTR_PAD_WETNESS = "spray_amount"

OVERLAP_STANDARD = 67
OVERLAP_DEEP = 85
OVERLAP_EXTENDED = 25
MOP_STANDARD = "Standard"
MOP_DEEP = "Deep"
MOP_EXTENDED = "Extended"
BRAAVA_MOP_BEHAVIORS = [MOP_STANDARD, MOP_DEEP, MOP_EXTENDED]
BRAAVA_SPRAY_AMOUNT = [1, 2, 3]

# Braava Jets can set mopping behavior through fanspeed
SUPPORT_BRAAVA = SUPPORT_IROBOT | SUPPORT_FAN_SPEED


class BraavaJet(IRobotVacuum):
"""Braava Jet."""

def __init__(self, roomba, blid):
"""Initialize the Roomba handler."""
super().__init__(roomba, blid)

# Initialize fan speed list
speed_list = []
for behavior in BRAAVA_MOP_BEHAVIORS:
for spray in BRAAVA_SPRAY_AMOUNT:
speed_list.append(f"{behavior}-{spray}")
self._speed_list = speed_list

@property
def supported_features(self):
"""Flag vacuum cleaner robot features that are supported."""
return SUPPORT_BRAAVA

@property
def fan_speed(self):
"""Return the fan speed of the vacuum cleaner."""
# Mopping behavior and spray amount as fan speed
rank_overlap = self.vacuum_state.get("rankOverlap", {})
behavior = None
if rank_overlap == OVERLAP_STANDARD:
behavior = MOP_STANDARD
elif rank_overlap == OVERLAP_DEEP:
behavior = MOP_DEEP
elif rank_overlap == OVERLAP_EXTENDED:
behavior = MOP_EXTENDED
pad_wetness = self.vacuum_state.get("padWetness", {})
# "disposable" and "reusable" values are always the same
pad_wetness_value = pad_wetness.get("disposable")
return f"{behavior}-{pad_wetness_value}"

@property
def fan_speed_list(self):
"""Get the list of available fan speed steps of the vacuum cleaner."""
return self._speed_list

async def async_set_fan_speed(self, fan_speed, **kwargs):
"""Set fan speed."""
try:
split = fan_speed.split("-", 1)
behavior = split[0]
spray = int(split[1])
if behavior.capitalize() in BRAAVA_MOP_BEHAVIORS:
behavior = behavior.capitalize()
except IndexError:
_LOGGER.error(
"Fan speed error: expected {behavior}-{spray_amount}, got '%s'",
fan_speed,
)
return
except ValueError:
_LOGGER.error("Spray amount error: expected integer, got '%s'", split[1])
return
if behavior not in BRAAVA_MOP_BEHAVIORS:
_LOGGER.error(
"Mop behavior error: expected one of %s, got '%s'",
str(BRAAVA_MOP_BEHAVIORS),
behavior,
)
return
if spray not in BRAAVA_SPRAY_AMOUNT:
_LOGGER.error(
"Spray amount error: expected one of %s, got '%d'",
str(BRAAVA_SPRAY_AMOUNT),
spray,
)
return

overlap = 0
if behavior == MOP_STANDARD:
overlap = OVERLAP_STANDARD
elif behavior == MOP_DEEP:
overlap = OVERLAP_DEEP
else:
overlap = OVERLAP_EXTENDED
await self.hass.async_add_executor_job(
self.vacuum.set_preference, "rankOverlap", overlap
)
await self.hass.async_add_executor_job(
self.vacuum.set_preference,
"padWetness",
{"disposable": spray, "reusable": spray},
)

@property
def device_state_attributes(self):
"""Return the state attributes of the device."""
state_attrs = super().device_state_attributes

# Get Braava state
state = self.vacuum_state
detected_pad = state.get("detectedPad")
mop_ready = state.get("mopReady", {})
lid_closed = mop_ready.get("lidClosed")
tank_present = mop_ready.get("tankPresent")
tank_level = state.get("tankLvl")
state_attrs[ATTR_DETECTED_PAD] = detected_pad
state_attrs[ATTR_LID_CLOSED] = lid_closed
state_attrs[ATTR_TANK_PRESENT] = tank_present
state_attrs[ATTR_TANK_LEVEL] = tank_level

return state_attrs
Loading