1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-25 13:38:04 +00:00
Files
core/homeassistant/components/pyload/sensor.py

182 lines
5.6 KiB
Python

"""Support for monitoring pyLoad."""
from __future__ import annotations
from datetime import timedelta
from enum import StrEnum
import logging
from time import monotonic
from typing import Any
from aiohttp import CookieJar
from pyloadapi.api import PyLoadAPI
from pyloadapi.exceptions import CannotConnect, InvalidAuth, ParserError
import voluptuous as vol
from homeassistant.components.sensor import (
PLATFORM_SCHEMA,
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.const import (
CONF_HOST,
CONF_MONITORED_VARIABLES,
CONF_NAME,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL,
CONF_USERNAME,
UnitOfDataRate,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.aiohttp_client import async_create_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
from .const import DEFAULT_HOST, DEFAULT_NAME, DEFAULT_PORT
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=15)
class PyLoadSensorEntity(StrEnum):
"""pyLoad Sensor Entities."""
SPEED = "speed"
SENSOR_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key=PyLoadSensorEntity.SPEED,
name="Speed",
device_class=SensorDeviceClass.DATA_RATE,
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
suggested_unit_of_measurement=UnitOfDataRate.MEGABYTES_PER_SECOND,
suggested_display_precision=1,
),
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_MONITORED_VARIABLES, default=["speed"]): vol.All(
cv.ensure_list, [vol.In(PyLoadSensorEntity)]
),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_SSL, default=False): cv.boolean,
vol.Optional(CONF_USERNAME): cv.string,
}
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the pyLoad sensors."""
host = config[CONF_HOST]
port = config[CONF_PORT]
protocol = "https" if config[CONF_SSL] else "http"
name = config[CONF_NAME]
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
url = f"{protocol}://{host}:{port}/"
session = async_create_clientsession(
hass,
verify_ssl=False,
cookie_jar=CookieJar(unsafe=True),
)
pyloadapi = PyLoadAPI(session, api_url=url, username=username, password=password)
try:
await pyloadapi.login()
except CannotConnect as conn_err:
raise PlatformNotReady(
"Unable to connect and retrieve data from pyLoad API"
) from conn_err
except ParserError as e:
raise PlatformNotReady("Unable to parse data from pyLoad API") from e
except InvalidAuth as e:
raise PlatformNotReady(
f"Authentication failed for {config[CONF_USERNAME]}, check your login credentials"
) from e
async_add_entities(
(
PyLoadSensor(
api=pyloadapi, entity_description=description, client_name=name
)
for description in SENSOR_DESCRIPTIONS
),
True,
)
class PyLoadSensor(SensorEntity):
"""Representation of a pyLoad sensor."""
def __init__(
self, api: PyLoadAPI, entity_description: SensorEntityDescription, client_name
) -> None:
"""Initialize a new pyLoad sensor."""
self._attr_name = f"{client_name} {entity_description.name}"
self.type = entity_description.key
self.api = api
self.entity_description = entity_description
self._attr_available = False
self.data: dict[str, Any] = {}
async def async_update(self) -> None:
"""Update state of sensor."""
start = monotonic()
try:
status = await self.api.get_status()
except InvalidAuth:
_LOGGER.info("Authentication failed, trying to reauthenticate")
try:
await self.api.login()
except InvalidAuth:
_LOGGER.error(
"Authentication failed for %s, check your login credentials",
self.api.username,
)
return
else:
_LOGGER.info(
"Unable to retrieve data due to cookie expiration "
"but re-authentication was successful"
)
return
finally:
self._attr_available = False
except CannotConnect:
_LOGGER.debug("Unable to connect and retrieve data from pyLoad API")
self._attr_available = False
return
except ParserError:
_LOGGER.error("Unable to parse data from pyLoad API")
self._attr_available = False
return
else:
self.data = status.to_dict()
_LOGGER.debug(
"Finished fetching pyload data in %.3f seconds",
monotonic() - start,
)
self._attr_available = True
@property
def native_value(self) -> StateType:
"""Return the state of the sensor."""
return self.data.get(self.entity_description.key)