From 820780996a30768c33d94d0218892ac27d222f10 Mon Sep 17 00:00:00 2001 From: Quentame <polletquentin74@me.com> Date: Sat, 14 Dec 2019 23:06:00 +0100 Subject: [PATCH] Add battery sensor to iCloud (#29818) * Add battery sensor to iCloud * Update .coveragerc * Review: @balloob & @MartinHjelmare * Review: use f string --- .coveragerc | 1 + homeassistant/components/icloud/__init__.py | 5 -- homeassistant/components/icloud/const.py | 3 +- .../components/icloud/device_tracker.py | 30 ++++--- homeassistant/components/icloud/sensor.py | 85 +++++++++++++++++++ 5 files changed, 104 insertions(+), 20 deletions(-) create mode 100644 homeassistant/components/icloud/sensor.py diff --git a/.coveragerc b/.coveragerc index f4794b59381..c6e7182d326 100644 --- a/.coveragerc +++ b/.coveragerc @@ -321,6 +321,7 @@ omit = homeassistant/components/iaqualink/switch.py homeassistant/components/icloud/__init__.py homeassistant/components/icloud/device_tracker.py + homeassistant/components/icloud/sensor.py homeassistant/components/izone/climate.py homeassistant/components/izone/discovery.py homeassistant/components/izone/__init__.py diff --git a/homeassistant/components/icloud/__init__.py b/homeassistant/components/icloud/__init__.py index 2012f691938..c59f4098951 100644 --- a/homeassistant/components/icloud/__init__.py +++ b/homeassistant/components/icloud/__init__.py @@ -560,11 +560,6 @@ class IcloudDevice: """Return a unique ID.""" return self._device_id - @property - def dev_id(self) -> str: - """Return the device ID.""" - return self._device_id - @property def name(self) -> str: """Return the Apple device name.""" diff --git a/homeassistant/components/icloud/const.py b/homeassistant/components/icloud/const.py index 4e99a378077..ed2fc78fe6d 100644 --- a/homeassistant/components/icloud/const.py +++ b/homeassistant/components/icloud/const.py @@ -14,8 +14,7 @@ DEFAULT_GPS_ACCURACY_THRESHOLD = 500 # meters STORAGE_KEY = DOMAIN STORAGE_VERSION = 1 -# Next PR will add sensor -ICLOUD_COMPONENTS = ["device_tracker"] +ICLOUD_COMPONENTS = ["device_tracker", "sensor"] # pyicloud.AppleDevice status DEVICE_BATTERY_LEVEL = "batteryLevel" diff --git a/homeassistant/components/icloud/device_tracker.py b/homeassistant/components/icloud/device_tracker.py index 4be34728c6d..511ce7f9447 100644 --- a/homeassistant/components/icloud/device_tracker.py +++ b/homeassistant/components/icloud/device_tracker.py @@ -1,8 +1,10 @@ """Support for tracking for iCloud devices.""" import logging +from typing import Dict from homeassistant.components.device_tracker import SOURCE_TYPE_GPS from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_USERNAME from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import HomeAssistantType @@ -26,13 +28,15 @@ async def async_setup_scanner( pass -async def async_setup_entry(hass: HomeAssistantType, entry, async_add_entities): +async def async_setup_entry( + hass: HomeAssistantType, entry: ConfigEntry, async_add_entities +): """Configure a dispatcher connection based on a config entry.""" username = entry.data[CONF_USERNAME] for device in hass.data[DOMAIN][username].devices.values(): if device.location is None: - _LOGGER.debug("No position found for device %s", device.name) + _LOGGER.debug("No position found for %s", device.name) continue _LOGGER.debug("Adding device_tracker for %s", device.name) @@ -49,12 +53,12 @@ class IcloudTrackerEntity(TrackerEntity): self._unsub_dispatcher = None @property - def unique_id(self): + def unique_id(self) -> str: """Return a unique ID.""" - return f"{self._device.unique_id}_tracker" + return self._device.unique_id @property - def name(self): + def name(self) -> str: """Return the name of the device.""" return self._device.name @@ -74,36 +78,36 @@ class IcloudTrackerEntity(TrackerEntity): return self._device.location[DEVICE_LOCATION_LONGITUDE] @property - def should_poll(self): + def should_poll(self) -> bool: """No polling needed.""" return False @property - def battery_level(self): + def battery_level(self) -> int: """Return the battery level of the device.""" return self._device.battery_level @property - def source_type(self): + def source_type(self) -> str: """Return the source type, eg gps or router, of the device.""" return SOURCE_TYPE_GPS @property - def icon(self): + def icon(self) -> str: """Return the icon.""" return icon_for_icloud_device(self._device) @property - def device_state_attributes(self): + def device_state_attributes(self) -> Dict[str, any]: """Return the device state attributes.""" return self._device.state_attributes @property - def device_info(self): + def device_info(self) -> Dict[str, any]: """Return the device information.""" return { - "identifiers": {(DOMAIN, self.unique_id)}, - "name": self.name, + "identifiers": {(DOMAIN, self._device.unique_id)}, + "name": self._device.name, "manufacturer": "Apple", "model": self._device.device_model, } diff --git a/homeassistant/components/icloud/sensor.py b/homeassistant/components/icloud/sensor.py new file mode 100644 index 00000000000..4351d4ffa19 --- /dev/null +++ b/homeassistant/components/icloud/sensor.py @@ -0,0 +1,85 @@ +"""Support for iCloud sensors.""" +import logging +from typing import Dict + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_USERNAME, DEVICE_CLASS_BATTERY +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.icon import icon_for_battery_level +from homeassistant.helpers.typing import HomeAssistantType + +from . import IcloudDevice +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistantType, entry: ConfigEntry, async_add_entities +) -> None: + """Set up iCloud devices sensors based on a config entry.""" + username = entry.data[CONF_USERNAME] + + entities = [] + for device in hass.data[DOMAIN][username].devices.values(): + if device.battery_level is not None: + _LOGGER.debug("Adding battery sensor for %s", device.name) + entities.append(IcloudDeviceBatterySensor(device)) + + async_add_entities(entities, True) + + +class IcloudDeviceBatterySensor(Entity): + """Representation of a iCloud device battery sensor.""" + + def __init__(self, device: IcloudDevice): + """Initialize the battery sensor.""" + self._device = device + + @property + def unique_id(self) -> str: + """Return a unique ID.""" + return f"{self._device.unique_id}_battery" + + @property + def name(self) -> str: + """Sensor name.""" + return f"{self._device.name} battery state" + + @property + def device_class(self) -> str: + """Return the device class of the sensor.""" + return DEVICE_CLASS_BATTERY + + @property + def state(self) -> int: + """Battery state percentage.""" + return self._device.battery_level + + @property + def unit_of_measurement(self) -> str: + """Battery state measured in percentage.""" + return "%" + + @property + def icon(self) -> str: + """Battery state icon handling.""" + return icon_for_battery_level( + battery_level=self._device.battery_level, + charging=self._device.battery_status == "Charging", + ) + + @property + def device_state_attributes(self) -> Dict[str, any]: + """Return default attributes for the iCloud device entity.""" + return self._device.state_attributes + + @property + def device_info(self) -> Dict[str, any]: + """Return the device information.""" + return { + "identifiers": {(DOMAIN, self._device.unique_id)}, + "name": self._device.name, + "manufacturer": "Apple", + "model": self._device.device_model, + } -- GitLab