from copy import deepcopy import json import logging from aiohttp import ClientError, ClientSession from aiohttp.client_exceptions import ClientConnectorError from .model import AltruistDeviceModel from .errors import AltruistError _LOGGER: logging.Logger = logging.getLogger(__name__) class AltruistClient: """Class for handling the data retrieval.""" def __init__(self, session: ClientSession, device: AltruistDeviceModel) -> None: """Initialize the data object.""" self._session = session self._resource = f"http://{device.ip_address}/data.json" self.device = device self._last_data: list | None = None @classmethod async def from_ip_address( cls, session: ClientSession, ip_address: str ) -> "AltruistClient": """Create client from given IP address.""" try: response = await session.get(f"http://{ip_address}/data.json") except ClientConnectorError: _LOGGER.error("Can't connect to Altruist Sensor on %s", ip_address) raise AltruistError responseData = await response.text() _LOGGER.debug("Received data: %s", str(responseData)) parsed_json = json.loads(responseData) if "software_version" in parsed_json: device = AltruistDeviceModel( id=parsed_json.get("sensor_id", ip_address), ip_address=ip_address, fw_version=parsed_json["software_version"], ) return cls(session, device) raise AltruistError @property def fw_version(self) -> str | None: """The firmware version of the sensor.""" return self.device.fw_version @property def device_id(self) -> str: """The mac address of the sensor.""" return self.device.id @property def sensor_names(self) -> list[str]: """Get the list of the sensor measurements names.""" if self._last_data: return [ item["value_type"] for item in self._last_data if self._valid_sensor_name(item["value_type"]) ] return [] def _valid_sensor_name(self, sensor_name: str) -> bool: """Return True if the sensor name is supported.""" if "GPS" in sensor_name: return False if "samples" in sensor_name: return False if "micro" in sensor_name: return False if "interval" in sensor_name: return False return True async def fetch_data(self) -> dict: """Get the latest data from Altruist device.""" responseData = None try: _LOGGER.debug("Get data from %s", str(self._resource)) response = await self._session.get(self._resource) responseData = await response.text() _LOGGER.debug("Received data: %s", str(responseData)) except ClientError as err: _LOGGER.warning("REST request error: %s", err) raise AltruistError from err except TimeoutError: _LOGGER.warning("REST request timeout") raise AltruistError from TimeoutError try: parsed_json = json.loads(responseData) if not isinstance(parsed_json, dict): _LOGGER.warning("JSON result was not a dictionary") parsed_json = {} self.device.fw_version = parsed_json.get("software_version") data = parsed_json.get("sensordatavalues", {}) except ValueError: _LOGGER.warning("REST result could not be parsed as JSON") raise AltruistError from ValueError self._last_data = deepcopy(data) return data