home-assistant-core/homeassistant/components/flo/device.py

228 lines
7.9 KiB
Python

"""Flo device object."""
from __future__ import annotations
from datetime import datetime, timedelta
from typing import Any
from aioflo.api import API
from aioflo.errors import RequestError
from async_timeout import timeout
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
import homeassistant.util.dt as dt_util
from .const import DOMAIN as FLO_DOMAIN, LOGGER
class FloDeviceDataUpdateCoordinator(DataUpdateCoordinator):
"""Flo device object."""
def __init__(
self, hass: HomeAssistant, api_client: API, location_id: str, device_id: str
) -> None:
"""Initialize the device."""
self.hass: HomeAssistant = hass
self.api_client: API = api_client
self._flo_location_id: str = location_id
self._flo_device_id: str = device_id
self._manufacturer: str = "Flo by Moen"
self._device_information: dict[str, Any] = {}
self._water_usage: dict[str, Any] = {}
super().__init__(
hass,
LOGGER,
name=f"{FLO_DOMAIN}-{device_id}",
update_interval=timedelta(seconds=60),
)
async def _async_update_data(self):
"""Update data via library."""
try:
async with timeout(20):
await self.send_presence_ping()
await self._update_device()
await self._update_consumption_data()
except RequestError as error:
raise UpdateFailed(error) from error
@property
def location_id(self) -> str:
"""Return Flo location id."""
return self._flo_location_id
@property
def id(self) -> str:
"""Return Flo device id."""
return self._flo_device_id
@property
def device_name(self) -> str:
"""Return device name."""
return self._device_information.get(
"nickname", f"{self.manufacturer} {self.model}"
)
@property
def manufacturer(self) -> str:
"""Return manufacturer for device."""
return self._manufacturer
@property
def mac_address(self) -> str:
"""Return ieee address for device."""
return self._device_information["macAddress"]
@property
def model(self) -> str:
"""Return model for device."""
return self._device_information["deviceModel"]
@property
def rssi(self) -> float:
"""Return rssi for device."""
return self._device_information["connectivity"]["rssi"]
@property
def last_heard_from_time(self) -> str:
"""Return lastHeardFromTime for device."""
return self._device_information["lastHeardFromTime"]
@property
def device_type(self) -> str:
"""Return the device type for the device."""
return self._device_information["deviceType"]
@property
def available(self) -> bool:
"""Return True if device is available."""
return self.last_update_success and self._device_information["isConnected"]
@property
def current_system_mode(self) -> str:
"""Return the current system mode."""
return self._device_information["systemMode"]["lastKnown"]
@property
def target_system_mode(self) -> str:
"""Return the target system mode."""
return self._device_information["systemMode"]["target"]
@property
def current_flow_rate(self) -> float:
"""Return current flow rate in gpm."""
return self._device_information["telemetry"]["current"]["gpm"]
@property
def current_psi(self) -> float:
"""Return the current pressure in psi."""
return self._device_information["telemetry"]["current"]["psi"]
@property
def temperature(self) -> float:
"""Return the current temperature in degrees F."""
return self._device_information["telemetry"]["current"]["tempF"]
@property
def humidity(self) -> float:
"""Return the current humidity in percent (0-100)."""
return self._device_information["telemetry"]["current"]["humidity"]
@property
def consumption_today(self) -> float:
"""Return the current consumption for today in gallons."""
return self._water_usage["aggregations"]["sumTotalGallonsConsumed"]
@property
def firmware_version(self) -> str:
"""Return the firmware version for the device."""
return self._device_information["fwVersion"]
@property
def serial_number(self) -> str:
"""Return the serial number for the device."""
return self._device_information["serialNumber"]
@property
def pending_info_alerts_count(self) -> int:
"""Return the number of pending info alerts for the device."""
return self._device_information["notifications"]["pending"]["infoCount"]
@property
def pending_warning_alerts_count(self) -> int:
"""Return the number of pending warning alerts for the device."""
return self._device_information["notifications"]["pending"]["warningCount"]
@property
def pending_critical_alerts_count(self) -> int:
"""Return the number of pending critical alerts for the device."""
return self._device_information["notifications"]["pending"]["criticalCount"]
@property
def has_alerts(self) -> bool:
"""Return True if any alert counts are greater than zero."""
return bool(
self.pending_info_alerts_count
or self.pending_warning_alerts_count
or self.pending_warning_alerts_count
)
@property
def water_detected(self) -> bool:
"""Return whether water is detected, for leak detectors."""
return self._device_information["fwProperties"]["telemetry_water"]
@property
def last_known_valve_state(self) -> str:
"""Return the last known valve state for the device."""
return self._device_information["valve"]["lastKnown"]
@property
def target_valve_state(self) -> str:
"""Return the target valve state for the device."""
return self._device_information["valve"]["target"]
@property
def battery_level(self) -> float:
"""Return the battery level for battery-powered device, e.g. leak detectors."""
return self._device_information["battery"]["level"]
async def send_presence_ping(self):
"""Send Flo a presence ping."""
await self.api_client.presence.ping()
async def async_set_mode_home(self):
"""Set the Flo location to home mode."""
await self.api_client.location.set_mode_home(self._flo_location_id)
async def async_set_mode_away(self):
"""Set the Flo location to away mode."""
await self.api_client.location.set_mode_away(self._flo_location_id)
async def async_set_mode_sleep(self, sleep_minutes, revert_to_mode):
"""Set the Flo location to sleep mode."""
await self.api_client.location.set_mode_sleep(
self._flo_location_id, sleep_minutes, revert_to_mode
)
async def async_run_health_test(self):
"""Run a Flo device health test."""
await self.api_client.device.run_health_test(self._flo_device_id)
async def _update_device(self, *_) -> None:
"""Update the device information from the API."""
self._device_information = await self.api_client.device.get_info(
self._flo_device_id
)
LOGGER.debug("Flo device data: %s", self._device_information)
async def _update_consumption_data(self, *_) -> None:
"""Update water consumption data from the API."""
today = dt_util.now().date()
start_date = datetime(today.year, today.month, today.day, 0, 0)
end_date = datetime(today.year, today.month, today.day, 23, 59, 59, 999000)
self._water_usage = await self.api_client.water.get_consumption_info(
self._flo_location_id, start_date, end_date
)
LOGGER.debug("Updated Flo consumption data: %s", self._water_usage)