From 2d833fd6eaa465fcf8a8c08e7ceba2f4980068ba Mon Sep 17 00:00:00 2001 From: Xitee <59659167+Xitee1@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:49:43 +0200 Subject: [PATCH] Add more diagnostic sensors to iRobot (#84995) Co-authored-by: 930913 <3722064+930913@users.noreply.github.com> Co-authored-by: Xitee <59659167+Xitee1@users.noreply.github.com> Co-authored-by: J. Nick Koston Co-authored-by: Joost Lekkerkerker --- .../components/roomba/irobot_base.py | 12 ++ homeassistant/components/roomba/sensor.py | 177 +++++++++++++++++- 2 files changed, 186 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/roomba/irobot_base.py b/homeassistant/components/roomba/irobot_base.py index a48b3638608..561effeb6c5 100644 --- a/homeassistant/components/roomba/irobot_base.py +++ b/homeassistant/components/roomba/irobot_base.py @@ -105,6 +105,18 @@ class IRobotEntity(Entity): """Return the battery level of the vacuum cleaner.""" return self.vacuum_state.get("batPct") + @property + def _run_stats(self): + return self.vacuum_state.get("bbrun") + + @property + def _mission_stats(self): + return self.vacuum_state.get("bbmssn") + + @property + def _battery_stats(self): + return self.vacuum_state.get("bbchg3") + @property def _robot_state(self): """Return the state of the vacuum cleaner.""" diff --git a/homeassistant/components/roomba/sensor.py b/homeassistant/components/roomba/sensor.py index dd74a023ff1..7c34169eb85 100644 --- a/homeassistant/components/roomba/sensor.py +++ b/homeassistant/components/roomba/sensor.py @@ -1,7 +1,11 @@ """Sensor for checking the battery level of Roomba.""" -from homeassistant.components.sensor import SensorDeviceClass, SensorEntity +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorStateClass, +) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, EntityCategory +from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -18,8 +22,31 @@ async def async_setup_entry( domain_data = hass.data[DOMAIN][config_entry.entry_id] roomba = domain_data[ROOMBA_SESSION] blid = domain_data[BLID] + roomba_vac = RoombaBattery(roomba, blid) - async_add_entities([roomba_vac], True) + roomba_battery_cycles = BatteryCycles(roomba, blid) + roomba_cleaning_time = CleaningTime(roomba, blid) + roomba_average_mission_time = AverageMissionTime(roomba, blid) + roomba_total_missions = MissionSensor(roomba, blid, "total", "nMssn") + roomba_success_missions = MissionSensor(roomba, blid, "successful", "nMssnOk") + roomba_canceled_missions = MissionSensor(roomba, blid, "canceled", "nMssnC") + roomba_failed_missions = MissionSensor(roomba, blid, "failed", "nMssnF") + roomba_scrubs_count = ScrubsCount(roomba, blid) + + async_add_entities( + [ + roomba_vac, + roomba_battery_cycles, + roomba_cleaning_time, + roomba_average_mission_time, + roomba_total_missions, + roomba_success_missions, + roomba_canceled_missions, + roomba_failed_missions, + roomba_scrubs_count, + ], + True, + ) class RoombaBattery(IRobotEntity, SensorEntity): @@ -38,3 +65,147 @@ class RoombaBattery(IRobotEntity, SensorEntity): def native_value(self): """Return the state of the sensor.""" return self._battery_level + + +class BatteryCycles(IRobotEntity, SensorEntity): + """Class to hold Roomba Sensor basic info.""" + + _attr_entity_category = EntityCategory.DIAGNOSTIC + _attr_icon = "mdi:counter" + + @property + def name(self): + """Return the name of the sensor.""" + return "Battery cycles" + + @property + def unique_id(self): + """Return the ID of this sensor.""" + return f"battery_cycles_{self._blid}" + + @property + def state_class(self): + """Return the state class of this entity, from STATE_CLASSES, if any.""" + return SensorStateClass.MEASUREMENT + + @property + def native_value(self): + """Return the state of the sensor.""" + return self._battery_stats.get("nLithChrg") or self._battery_stats.get( + "nNimhChrg" + ) + + +class CleaningTime(IRobotEntity, SensorEntity): + """Class to hold Roomba Sensor basic info.""" + + _attr_entity_category = EntityCategory.DIAGNOSTIC + _attr_icon = "mdi:clock" + _attr_native_unit_of_measurement = UnitOfTime.HOURS + + @property + def name(self): + """Return the name of the sensor.""" + return f"{self._name} cleaning time total" + + @property + def unique_id(self): + """Return the ID of this sensor.""" + return f"total_cleaning_time_{self._blid}" + + @property + def native_value(self): + """Return the state of the sensor.""" + return self._run_stats.get("hr") + + +class AverageMissionTime(IRobotEntity, SensorEntity): + """Class to hold Roomba Sensor basic info.""" + + _attr_entity_category = EntityCategory.DIAGNOSTIC + _attr_icon = "mdi:clock" + + @property + def name(self): + """Return the name of the sensor.""" + return "Average mission time" + + @property + def unique_id(self): + """Return the ID of this sensor.""" + return f"average_mission_time_{self._blid}" + + @property + def native_unit_of_measurement(self): + """Return the unit_of_measurement of the device.""" + return UnitOfTime.MINUTES + + @property + def native_value(self): + """Return the state of the sensor.""" + return self._mission_stats.get("aMssnM") + + +class MissionSensor(IRobotEntity, SensorEntity): + """Class to hold the Roomba missions info.""" + + def __init__(self, roomba, blid, mission_type, mission_value_string): + """Initialise iRobot sensor with mission details.""" + super().__init__(roomba, blid) + self._mission_type = mission_type + self._mission_value_string = mission_value_string + + _attr_entity_category = EntityCategory.DIAGNOSTIC + _attr_icon = "mdi:counter" + + @property + def name(self): + """Return the name of the sensor.""" + return f"Missions {self._mission_type}" + + @property + def unique_id(self): + """Return the ID of this sensor.""" + return f"{self._mission_type}_missions_{self._blid}" + + @property + def state_class(self): + """Return the state class of this entity, from STATE_CLASSES, if any.""" + return SensorStateClass.MEASUREMENT + + @property + def native_value(self): + """Return the state of the sensor.""" + return self._mission_stats.get(self._mission_value_string) + + +class ScrubsCount(IRobotEntity, SensorEntity): + """Class to hold Roomba Sensor basic info.""" + + _attr_entity_category = EntityCategory.DIAGNOSTIC + _attr_icon = "mdi:counter" + + @property + def name(self): + """Return the name of the sensor.""" + return "Scrubs count" + + @property + def unique_id(self): + """Return the ID of this sensor.""" + return f"scrubs_count_{self._blid}" + + @property + def state_class(self): + """Return the state class of this entity, from STATE_CLASSES, if any.""" + return SensorStateClass.MEASUREMENT + + @property + def entity_registry_enabled_default(self): + """Disable sensor by default.""" + return False + + @property + def native_value(self): + """Return the state of the sensor.""" + return self._run_stats.get("nScrubs")