Add battery sensor to iCloud (#29818)
* Add battery sensor to iCloud * Update .coveragerc * Review: @balloob & @MartinHjelmare * Review: use f string
This commit is contained in:
parent
003658a3f0
commit
820780996a
5 changed files with 104 additions and 20 deletions
|
@ -321,6 +321,7 @@ omit =
|
||||||
homeassistant/components/iaqualink/switch.py
|
homeassistant/components/iaqualink/switch.py
|
||||||
homeassistant/components/icloud/__init__.py
|
homeassistant/components/icloud/__init__.py
|
||||||
homeassistant/components/icloud/device_tracker.py
|
homeassistant/components/icloud/device_tracker.py
|
||||||
|
homeassistant/components/icloud/sensor.py
|
||||||
homeassistant/components/izone/climate.py
|
homeassistant/components/izone/climate.py
|
||||||
homeassistant/components/izone/discovery.py
|
homeassistant/components/izone/discovery.py
|
||||||
homeassistant/components/izone/__init__.py
|
homeassistant/components/izone/__init__.py
|
||||||
|
|
|
@ -560,11 +560,6 @@ class IcloudDevice:
|
||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
return self._device_id
|
return self._device_id
|
||||||
|
|
||||||
@property
|
|
||||||
def dev_id(self) -> str:
|
|
||||||
"""Return the device ID."""
|
|
||||||
return self._device_id
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
"""Return the Apple device name."""
|
"""Return the Apple device name."""
|
||||||
|
|
|
@ -14,8 +14,7 @@ DEFAULT_GPS_ACCURACY_THRESHOLD = 500 # meters
|
||||||
STORAGE_KEY = DOMAIN
|
STORAGE_KEY = DOMAIN
|
||||||
STORAGE_VERSION = 1
|
STORAGE_VERSION = 1
|
||||||
|
|
||||||
# Next PR will add sensor
|
ICLOUD_COMPONENTS = ["device_tracker", "sensor"]
|
||||||
ICLOUD_COMPONENTS = ["device_tracker"]
|
|
||||||
|
|
||||||
# pyicloud.AppleDevice status
|
# pyicloud.AppleDevice status
|
||||||
DEVICE_BATTERY_LEVEL = "batteryLevel"
|
DEVICE_BATTERY_LEVEL = "batteryLevel"
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
"""Support for tracking for iCloud devices."""
|
"""Support for tracking for iCloud devices."""
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
|
from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
|
||||||
from homeassistant.components.device_tracker.config_entry import TrackerEntity
|
from homeassistant.components.device_tracker.config_entry import TrackerEntity
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_USERNAME
|
from homeassistant.const import CONF_USERNAME
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.typing import HomeAssistantType
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
@ -26,13 +28,15 @@ async def async_setup_scanner(
|
||||||
pass
|
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."""
|
"""Configure a dispatcher connection based on a config entry."""
|
||||||
username = entry.data[CONF_USERNAME]
|
username = entry.data[CONF_USERNAME]
|
||||||
|
|
||||||
for device in hass.data[DOMAIN][username].devices.values():
|
for device in hass.data[DOMAIN][username].devices.values():
|
||||||
if device.location is None:
|
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
|
continue
|
||||||
|
|
||||||
_LOGGER.debug("Adding device_tracker for %s", device.name)
|
_LOGGER.debug("Adding device_tracker for %s", device.name)
|
||||||
|
@ -49,12 +53,12 @@ class IcloudTrackerEntity(TrackerEntity):
|
||||||
self._unsub_dispatcher = None
|
self._unsub_dispatcher = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self) -> str:
|
||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
return f"{self._device.unique_id}_tracker"
|
return self._device.unique_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self) -> str:
|
||||||
"""Return the name of the device."""
|
"""Return the name of the device."""
|
||||||
return self._device.name
|
return self._device.name
|
||||||
|
|
||||||
|
@ -74,36 +78,36 @@ class IcloudTrackerEntity(TrackerEntity):
|
||||||
return self._device.location[DEVICE_LOCATION_LONGITUDE]
|
return self._device.location[DEVICE_LOCATION_LONGITUDE]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self) -> bool:
|
||||||
"""No polling needed."""
|
"""No polling needed."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def battery_level(self):
|
def battery_level(self) -> int:
|
||||||
"""Return the battery level of the device."""
|
"""Return the battery level of the device."""
|
||||||
return self._device.battery_level
|
return self._device.battery_level
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source_type(self):
|
def source_type(self) -> str:
|
||||||
"""Return the source type, eg gps or router, of the device."""
|
"""Return the source type, eg gps or router, of the device."""
|
||||||
return SOURCE_TYPE_GPS
|
return SOURCE_TYPE_GPS
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self):
|
def icon(self) -> str:
|
||||||
"""Return the icon."""
|
"""Return the icon."""
|
||||||
return icon_for_icloud_device(self._device)
|
return icon_for_icloud_device(self._device)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self) -> Dict[str, any]:
|
||||||
"""Return the device state attributes."""
|
"""Return the device state attributes."""
|
||||||
return self._device.state_attributes
|
return self._device.state_attributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self):
|
def device_info(self) -> Dict[str, any]:
|
||||||
"""Return the device information."""
|
"""Return the device information."""
|
||||||
return {
|
return {
|
||||||
"identifiers": {(DOMAIN, self.unique_id)},
|
"identifiers": {(DOMAIN, self._device.unique_id)},
|
||||||
"name": self.name,
|
"name": self._device.name,
|
||||||
"manufacturer": "Apple",
|
"manufacturer": "Apple",
|
||||||
"model": self._device.device_model,
|
"model": self._device.device_model,
|
||||||
}
|
}
|
||||||
|
|
85
homeassistant/components/icloud/sensor.py
Normal file
85
homeassistant/components/icloud/sensor.py
Normal file
|
@ -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,
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue