diff --git a/aiolyric/__init__.py b/aiolyric/__init__.py index 7a8d6d3..b76f9a4 100644 --- a/aiolyric/__init__.py +++ b/aiolyric/__init__.py @@ -1,54 +1,63 @@ -"""Lyric: Init""" -from typing import List +"""Lyric.""" + +import logging from aiohttp import ClientResponse +from .client import LyricClient from .const import BASE_URL -from .objects.base import LyricBase from .objects.device import LyricDevice from .objects.location import LyricLocation from .objects.priority import LyricPriority, LyricRoom -class Lyric(LyricBase): +class Lyric: """Handles authentication refresh tokens.""" + logger = logging.getLogger(__name__) + def __init__( self, - client: "LyricClient", + client: LyricClient, client_id: str, ) -> None: """Initialize the token manager class.""" self._client = client self._client_id = client_id - self._devices: List[LyricDevice] = [] + self._devices: list[LyricDevice] = [] self._devices_dict: dict = {} - self._locations: List[LyricLocation] = [] + self._locations: list[LyricLocation] = [] self._locations_dict: dict = {} self._rooms_dict: dict = {} @property def client_id(self) -> str: + """Return the client id.""" return self._client_id @property - def devices(self) -> List[LyricDevice]: + def devices(self) -> list[LyricDevice]: + """Return the devices.""" return self._devices @property def devices_dict(self) -> dict: + """Return the devices dict.""" return self._devices_dict @property - def locations(self) -> List[LyricLocation]: + def locations(self) -> list[LyricLocation]: + """Return the locations.""" return self._locations @property def locations_dict(self) -> dict: + """Return the locations dict.""" return self._locations_dict @property def rooms_dict(self) -> dict[str, dict[str, LyricRoom]]: + """Return the rooms dict.""" return self._rooms_dict async def get_devices( @@ -80,11 +89,7 @@ async def get_locations(self) -> None: for location in self._locations: self._locations_dict[location.locationID] = location - async def get_thermostat_rooms( - self, - location_id: int, - device_id: str - ) -> None: + async def get_thermostat_rooms(self, location_id: int, device_id: str) -> None: """Get Priority, which contains accessory information.""" response: ClientResponse = await self._client.get( f"{BASE_URL}/devices/thermostats/{device_id}/priority?apikey={self.client_id}&locationId={location_id}" @@ -94,23 +99,24 @@ async def get_thermostat_rooms( priority = LyricPriority(json) - macId = priority.deviceId # device id in the priority payload refers to the mac address of the device - self._rooms_dict[macId]: dict = {} + # device id in the priority payload refers to the mac address of the device + mac_id = priority.deviceId + self._rooms_dict[mac_id] = {} # add each room to the room dictionary. Rooms contain motion, temp, and humidity averages for all accessories in a room for room in priority.currentPriority.rooms: - self._rooms_dict[macId][room.id] = room + self._rooms_dict[mac_id][room.id] = room async def update_thermostat( self, location: LyricLocation, device: LyricDevice, mode=None, - heatSetpoint=None, - coolSetpoint=None, - autoChangeoverActive=None, - thermostatSetpointStatus=None, - nextPeriodTime=None, + heat_setpoint=None, + cool_setpoint=None, + auto_changeover_active=None, + thermostat_setpoint_status=None, + next_period_time=None, ) -> ClientResponse: """Update Theremostat.""" self.logger.debug("Update Thermostat") @@ -122,35 +128,35 @@ async def update_thermostat( else: data["mode"] = device.changeableValues.mode - if heatSetpoint is not None: - data["heatSetpoint"] = heatSetpoint + if heat_setpoint is not None: + data["heatSetpoint"] = heat_setpoint else: data["heatSetpoint"] = device.changeableValues.heatSetpoint - if coolSetpoint is not None: - data["coolSetpoint"] = coolSetpoint + if cool_setpoint is not None: + data["coolSetpoint"] = cool_setpoint else: data["coolSetpoint"] = device.changeableValues.coolSetpoint # Only for TCC devices - if autoChangeoverActive is not None: - data["autoChangeoverActive"] = autoChangeoverActive + if auto_changeover_active is not None: + data["autoChangeoverActive"] = auto_changeover_active elif device.changeableValues.autoChangeoverActive is not None: data["autoChangeoverActive"] = device.changeableValues.autoChangeoverActive # Only for LCC devices - if thermostatSetpointStatus is not None: - data["thermostatSetpointStatus"] = thermostatSetpointStatus + if thermostat_setpoint_status is not None: + data["thermostatSetpointStatus"] = thermostat_setpoint_status elif device.changeableValues.thermostatSetpointStatus is not None: if device.changeableValues.thermostatSetpointStatus == "NoHold": data["thermostatSetpointStatus"] = "TemporaryHold" else: - data[ - "thermostatSetpointStatus" - ] = device.changeableValues.thermostatSetpointStatus + data["thermostatSetpointStatus"] = ( + device.changeableValues.thermostatSetpointStatus + ) if data.get("thermostatSetpointStatus", "") == "HoldUntil": - if nextPeriodTime is not None: - data["nextPeriodTime"] = nextPeriodTime + if next_period_time is not None: + data["nextPeriodTime"] = next_period_time elif device.changeableValues.nextPeriodTime == "NoHold" and mode is None: data["nextPeriodTime"] = "TemporaryHold" else: @@ -177,7 +183,7 @@ async def update_fan( if mode is not None: data["mode"] = mode else: - data["mode"] = device.fanMode + data["mode"] = device.settings.fanMode.fan self.logger.debug(data) diff --git a/aiolyric/_version.py b/aiolyric/_version.py index b51424b..05d2cca 100644 --- a/aiolyric/_version.py +++ b/aiolyric/_version.py @@ -7,5 +7,5 @@ from incremental import Version -__version__ = Version("aiolyric", 1, 1, 2, dev=0) +__version__ = Version("aiolyric", 2, 0, 0, dev=0) __all__ = ["__version__"] diff --git a/aiolyric/client/__init__.py b/aiolyric/client/__init__.py index a1618cd..94255cb 100644 --- a/aiolyric/client/__init__.py +++ b/aiolyric/client/__init__.py @@ -1,19 +1,23 @@ -"""Lyric: Client""" +"""Lyric client.""" + from abc import abstractmethod -from asyncio import CancelledError, TimeoutError, get_event_loop +import asyncio import logging -from aiohttp import ClientError, ClientResponse, ClientSession -import async_timeout +from aiohttp import ClientResponse, ClientSession from ..exceptions import LyricAuthenticationException, LyricException -from ..objects.base import LyricBase -class LyricClient(LyricBase): +class LyricClient: """Client to handle API calls.""" - def __init__(self, session: ClientSession) -> None: + logger = logging.getLogger(__name__) + + def __init__( + self, + session: ClientSession, + ) -> None: """Initialize the client.""" self._session = session @@ -21,21 +25,34 @@ def __init__(self, session: ClientSession) -> None: async def async_get_access_token(self) -> str: """Return a valid access token.""" - async def get(self, url: str, **kwargs) -> ClientResponse: + async def get( + self, + url: str, + **kwargs, + ) -> ClientResponse: """Make a GET request.""" return await self.request("GET", url, **kwargs) - async def post(self, url: str, **kwargs) -> ClientResponse: + async def post( + self, + url: str, + **kwargs, + ) -> ClientResponse: """Make a POST request.""" - return await self.request("POST", url, **kwargs) + return await self.request( + "POST", + url, + **kwargs, + ) async def request( - self, method: str in ["GET", "POST"], url: str, **kwargs + self, + method: str, + url: str, + **kwargs, ) -> ClientResponse: """Make a request.""" - headers = kwargs.get("headers") - - if headers is None: + if (headers := kwargs.get("headers")) is None: headers = {} else: headers = dict(headers) @@ -44,7 +61,7 @@ async def request( headers["Authorization"] = f"Bearer {access_token}" headers["Content-Type"] = "application/json" - async with async_timeout.timeout(20): + async with asyncio.timeout(20): response: ClientResponse = await self._session.request( method, url, @@ -65,17 +82,16 @@ async def request( "status": response.status, } ) - else: - raise LyricException( - { - "request": { - "method": method, - "url": url, - "headers": headers, - **kwargs, - }, - "response": await response.json(), - "status": response.status, - } - ) + raise LyricException( + { + "request": { + "method": method, + "url": url, + "headers": headers, + **kwargs, + }, + "response": await response.json(), + "status": response.status, + } + ) return response diff --git a/aiolyric/const.py b/aiolyric/const.py index 9561992..8397528 100644 --- a/aiolyric/const.py +++ b/aiolyric/const.py @@ -1,4 +1,4 @@ -"""Lyric: Constants""" +"""Lyric: Constants.""" AUTH_URL = "https://api.honeywell.com/oauth2/authorize" BASE_URL = "https://api.honeywell.com/v2" diff --git a/aiolyric/exceptions.py b/aiolyric/exceptions.py index 2b6c3a5..67c2a0c 100644 --- a/aiolyric/exceptions.py +++ b/aiolyric/exceptions.py @@ -1,4 +1,4 @@ -"""Lyric: Exceptions""" +"""Lyric: Exceptions.""" class LyricException(BaseException): diff --git a/aiolyric/objects/__init__.py b/aiolyric/objects/__init__.py index 1a548ba..05a7b19 100644 --- a/aiolyric/objects/__init__.py +++ b/aiolyric/objects/__init__.py @@ -1 +1 @@ -"""Lyric: Objects""" +"""Lyric objects.""" diff --git a/aiolyric/objects/base.py b/aiolyric/objects/base.py index dacb0da..6761a74 100644 --- a/aiolyric/objects/base.py +++ b/aiolyric/objects/base.py @@ -1,21 +1,31 @@ -"""Lyric: Base""" +"""Lyric base.""" + import logging +from ..client import LyricClient + -class LyricBase: +class LyricBaseObject: """Base class for Lyric.""" logger = logging.getLogger(__name__) - def __init__(self, attributes) -> None: + def __init__( + self, + attributes: dict, + ) -> None: """Initialize.""" self.attributes = attributes -class LyricBaseClient(LyricBase): +class LyricBaseClient(LyricBaseObject): """Base class for Lyric.""" - def __init__(self, client: "AIOGitHubAPIClient", attributes: dict) -> None: + def __init__( + self, + client: LyricClient, + attributes: dict, + ) -> None: """Initialise.""" super().__init__(attributes) self.client = client diff --git a/aiolyric/objects/device.py b/aiolyric/objects/device.py index 05a4545..1f6964f 100644 --- a/aiolyric/objects/device.py +++ b/aiolyric/objects/device.py @@ -1,305 +1,400 @@ -"""Class object for LyricDevice -Documentation: https://github.com/timmo001/aiolyric +"""Lyric device.""" +# pylint: disable=invalid-name -Generated by generator/generator.py - 2020-08-31 14:06:02.854691 -""" -from .base import LyricBase, LyricBaseClient +from .base import LyricBaseClient, LyricBaseObject -class Vacationhold(LyricBase): +class Vacationhold(LyricBaseObject): + """Vacation hold.""" + @property def enabled(self): + """Return if enabled.""" return self.attributes.get("enabled", False) -class Currentscheduleperiod(LyricBase): +class Currentscheduleperiod(LyricBaseObject): + """Current schedule period.""" + @property def day(self): + """Return the day.""" return self.attributes.get("day", None) @property def period(self): + """Return the period.""" return self.attributes.get("period", None) -class Schedulecapabilities(LyricBase): +class Schedulecapabilities(LyricBaseObject): + """Schedule capabilities.""" + @property def availableScheduleTypes(self): + """Return available schedule types.""" return self.attributes.get("availableScheduleTypes", []) @property def schedulableFan(self): + """Return if fan is schedulable.""" return self.attributes.get("schedulableFan", False) -class Scheduletype(LyricBase): +class Scheduletype(LyricBaseObject): + """Schedule type.""" + @property def scheduleType(self): + """Return the schedule type.""" return self.attributes.get("scheduleType", None) @property def scheduleSubType(self): + """Return the schedule sub type.""" return self.attributes.get("scheduleSubType", None) -class SettingsHardwaresettings(LyricBase): +class SettingsHardwaresettings(LyricBaseObject): + """Hardware settings.""" + @property def brightness(self): + """Return the brightness.""" return self.attributes.get("brightness", None) @property def maxBrightness(self): + """Return the max brightness.""" return self.attributes.get("maxBrightness", None) -class SettingsTemperaturemode(LyricBase): +class SettingsTemperaturemode(LyricBaseObject): + """Temperature mode.""" + @property def air(self): + """Return if air is enabled.""" return self.attributes.get("air", True) -class SettingsSpecialmode(LyricBase): +class SettingsSpecialmode(LyricBaseObject): + """Special mode.""" + @property def _(self): + """Return None.""" return None -class SettingsFan(LyricBase): + +class SettingsFan(LyricBaseObject): + """Fan settings.""" + @property def fan(self): - return self.atributes.get("fan", {}) + """Return the fan settings.""" + return self.attributes.get("fan", {}) + +class Settings(LyricBaseObject): + """Settings.""" -class Settings(LyricBase): @property def hardwareSettings(self): + """Return hardware settings.""" return SettingsHardwaresettings(self.attributes.get("hardwareSettings", {})) @property def temperatureMode(self): + """Return temperature mode.""" return SettingsTemperaturemode(self.attributes.get("temperatureMode", {})) @property def specialMode(self): + """Return special mode.""" return SettingsSpecialmode(self.attributes.get("specialMode", {})) @property def devicePairingEnabled(self): + """Return if device pairing is enabled.""" return self.attributes.get("devicePairingEnabled", True) @property def fanModes(self): + """Return fan modes.""" return SettingsFan(self.attributes.get("allowedModes", [])) @property def fanChangeableValues(self): + """Return fan changeable values.""" return SettingsFan(self.attributes.get("changeableValues", {})) @property def fanMode(self): - return fanChangeableValues(self.attributes.get("mode", None)) + """Return the fan mode.""" + return SettingsFan(self.attributes.get("mode", None)) -class Devicesettings(LyricBase): +class Devicesettings(LyricBaseObject): + """Device settings.""" + @property def _(self): + """Return None.""" return None -class Service(LyricBase): +class Service(LyricBaseObject): + """Service.""" + @property def mode(self): + """Return the mode.""" return self.attributes.get("mode", None) -class Changeablevalues(LyricBase): +class Changeablevalues(LyricBaseObject): + """Changeable values.""" + @property def autoChangeoverActive(self): + """Return if auto changeover is active.""" return self.attributes.get("autoChangeoverActive", None) @property def mode(self): + """Return the mode.""" return self.attributes.get("mode", None) @property def heatSetpoint(self): + """Return the heat setpoint.""" return self.attributes.get("heatSetpoint", None) @property def coolSetpoint(self): + """Return the cool setpoint.""" return self.attributes.get("coolSetpoint", None) @property def thermostatSetpointStatus(self): + """Return the thermostat setpoint status.""" return self.attributes.get("thermostatSetpointStatus", None) @property def nextPeriodTime(self): + """Return the next period time.""" return self.attributes.get("nextPeriodTime", None) @property def heatCoolMode(self): + """Return the heat cool mode.""" return self.attributes.get("heatCoolMode", None) @property def endHeatSetpoint(self): + """Return the end heat setpoint.""" return self.attributes.get("endHeatSetpoint", None) @property def endCoolSetpoint(self): + """Return the end cool setpoint.""" return self.attributes.get("endCoolSetpoint", None) -class Operationstatus(LyricBase): +class Operationstatus(LyricBaseObject): + """Operation status.""" + @property def mode(self): + """Return the mode.""" return self.attributes.get("mode", None) @property def fanRequest(self): + """Return the fan request.""" return self.attributes.get("fanRequest", False) @property def circulationFanRequest(self): + """Return the circulation fan request.""" return self.attributes.get("circulationFanRequest", False) class LyricDevice(LyricBaseClient): + """Lyric Device.""" + @property def locationID(self): + """Return the location ID.""" return self.attributes.get("locationID", None) @property def indoorHumidity(self): + """Return the indoor humidity.""" return self.attributes.get("indoorHumidity", None) @property def displayedOutdoorHumidity(self): + """Return the displayed outdoor humidity.""" return self.attributes.get("displayedOutdoorHumidity", None) @property def vacationHold(self): + """Return the vacation hold.""" return Vacationhold(self.attributes.get("vacationHold", {})) @property def currentSchedulePeriod(self): + """Return the current schedule period.""" return Currentscheduleperiod(self.attributes.get("currentSchedulePeriod", {})) @property def scheduleCapabilities(self): + """Return the schedule capabilities.""" return Schedulecapabilities(self.attributes.get("scheduleCapabilities", {})) @property def scheduleType(self): + """Return the schedule type.""" return Scheduletype(self.attributes.get("scheduleType", {})) @property def scheduleStatus(self): + """Return the schedule status.""" return self.attributes.get("scheduleStatus", None) @property def allowedTimeIncrements(self): + """Return the allowed time increments.""" return self.attributes.get("allowedTimeIncrements", None) @property def settings(self): + """Return the settings.""" return Settings(self.attributes.get("settings", {})) @property def deviceClass(self): + """Return the device class.""" return self.attributes.get("deviceClass", None) @property def deviceType(self): + """Return the device type.""" return self.attributes.get("deviceType", None) @property def deviceID(self): + """Return the device ID.""" return self.attributes.get("deviceID", None) @property def name(self): + """Return the name.""" return self.attributes.get("name", None) @property def isAlive(self): + """Return if the device is alive.""" return self.attributes.get("isAlive", True) @property def isUpgrading(self): + """Return if the device is upgrading.""" return self.attributes.get("isUpgrading", False) @property def isProvisioned(self): + """Return if the device is provisioned.""" return self.attributes.get("isProvisioned", True) @property def macID(self): + """Return the MAC ID.""" return self.attributes.get("macID", None) @property def deviceSettings(self): + """Return the device settings.""" return Devicesettings(self.attributes.get("deviceSettings", {})) @property def service(self): + """Return the service.""" return Service(self.attributes.get("service", {})) @property def deviceRegistrationDate(self): + """Return the device registration date.""" return self.attributes.get("deviceRegistrationDate", None) @property def dataSyncStatus(self): + """Return the data sync status.""" return self.attributes.get("dataSyncStatus", None) @property def units(self): + """Return the units.""" return self.attributes.get("units", None) @property def indoorTemperature(self): + """Return the indoor temperature.""" return self.attributes.get("indoorTemperature", None) @property def outdoorTemperature(self): + """Return the outdoor temperature.""" return self.attributes.get("outdoorTemperature", None) @property def allowedModes(self): + """Return the allowed modes.""" return self.attributes.get("allowedModes", []) @property def deadband(self): + """Return the deadband.""" return self.attributes.get("deadband", None) @property def hasDualSetpointStatus(self): + """Return if the device has dual setpoint status.""" return self.attributes.get("hasDualSetpointStatus", False) @property def minHeatSetpoint(self): + """Return the minimum heat setpoint.""" return self.attributes.get("minHeatSetpoint", None) @property def maxHeatSetpoint(self): + """Return the maximum heat setpoint.""" return self.attributes.get("maxHeatSetpoint", None) @property def minCoolSetpoint(self): + """Return the minimum cool setpoint.""" return self.attributes.get("minCoolSetpoint", None) @property def maxCoolSetpoint(self): + """Return the maximum cool setpoint.""" return self.attributes.get("maxCoolSetpoint", None) @property def changeableValues(self): + """Return the changeable values.""" return Changeablevalues(self.attributes.get("changeableValues", {})) @property def operationStatus(self): + """Return the operation status.""" return Operationstatus(self.attributes.get("operationStatus", {})) @property def deviceModel(self): + """Return the device model.""" return self.attributes.get("deviceModel", None) diff --git a/aiolyric/objects/location.py b/aiolyric/objects/location.py index 78c4afc..51d0734 100644 --- a/aiolyric/objects/location.py +++ b/aiolyric/objects/location.py @@ -1,67 +1,80 @@ -"""Class object for LyricLocation -Documentation: https://github.com/timmo001/aiolyric +"""Lyric location.""" +# pylint: disable=invalid-name -Generated by generator/generator.py - 2020-08-31 13:55:49.220121 -""" -from typing import List - -from .base import LyricBase, LyricBaseClient +from .base import LyricBaseClient, LyricBaseObject from .device import LyricDevice -class Locationrolemapping(LyricBase): +class Locationrolemapping(LyricBaseObject): + """Represents a location role mapping.""" + @property def locationID(self): + """The ID of the location.""" return self.attributes.get("locationID", None) @property def role(self): + """The role of the location.""" return self.attributes.get("role", None) @property def locationName(self): + """The name of the location.""" return self.attributes.get("locationName", None) @property def status(self): + """The status of the location.""" return self.attributes.get("status", None) -class Users(LyricBase): +class Users(LyricBaseObject): + """Represents a user.""" + @property def userID(self): + """The ID of the user.""" return self.attributes.get("userID", None) @property def username(self): + """The username of the user.""" return self.attributes.get("username", None) @property def firstname(self): + """The first name of the user.""" return self.attributes.get("firstname", None) @property def lastname(self): + """The last name of the user.""" return self.attributes.get("lastname", None) @property def created(self): + """The creation date of the user.""" return self.attributes.get("created", None) @property def deleted(self): + """The deletion date of the user.""" return self.attributes.get("deleted", None) @property def activated(self): + """Whether the user is activated.""" return self.attributes.get("activated", True) @property def connectedHomeAccountExists(self): + """Whether a connected home account exists for the user.""" return self.attributes.get("connectedHomeAccountExists", True) @property def locationRoleMapping(self): + """The location role mappings of the user.""" return [ Locationrolemapping(x) for x in self.attributes.get("locationRoleMapping", []) @@ -69,80 +82,107 @@ def locationRoleMapping(self): @property def isOptOut(self): + """Whether the user has opted out.""" return self.attributes.get("isOptOut", None) @property def isCurrentUser(self): + """Whether the user is the current user.""" return self.attributes.get("isCurrentUser", True) -class Time(LyricBase): +class Time(LyricBaseObject): + """Represents a time range.""" + @property def start(self): + """The start time.""" return self.attributes.get("start", None) @property def end(self): + """The end time.""" return self.attributes.get("end", None) -class Schedules(LyricBase): +class Schedules(LyricBaseObject): + """Represents a list of schedules.""" + @property def time(self): + """The time ranges of the schedule.""" return [Time(x) for x in self.attributes.get("time", [])] @property def days(self): + """The days of the schedule.""" return self.attributes.get("days", []) -class ConfigurationFacerecognition(LyricBase): +class ConfigurationFacerecognition(LyricBaseObject): + """Represents the face recognition configuration.""" + @property def enabled(self): + """Whether face recognition is enabled.""" return self.attributes.get("enabled", False) @property def maxPersons(self): + """The maximum number of persons for face recognition.""" return self.attributes.get("maxPersons", None) @property def maxEtas(self): + """The maximum number of ETAs for face recognition.""" return self.attributes.get("maxEtas", None) @property def maxEtaPersons(self): + """The maximum number of ETA persons for face recognition.""" return self.attributes.get("maxEtaPersons", None) @property def schedules(self): + """The schedules for face recognition.""" return [Schedules(x) for x in self.attributes.get("schedules", [])] -class Configuration(LyricBase): +class Configuration(LyricBaseObject): + """Represents the configuration of a location.""" + @property def faceRecognition(self): + """The face recognition configuration.""" return ConfigurationFacerecognition(self.attributes.get("faceRecognition", {})) class LyricLocation(LyricBaseClient): + """Represents a Lyric location.""" + @property def locationID(self): + """The ID of the location.""" return self.attributes.get("locationID", None) @property def name(self): + """The name of the location.""" return self.attributes.get("name", None) @property def country(self): + """The country of the location.""" return self.attributes.get("country", None) @property def zipcode(self): + """The zipcode of the location.""" return self.attributes.get("zipcode", None) @property - def devices(self) -> List[LyricDevice]: + def devices(self) -> list[LyricDevice]: + """The devices associated with the location.""" devices = [] for x in self.attributes.get("devices", []): x["locationID"] = self.locationID @@ -151,6 +191,7 @@ def devices(self) -> List[LyricDevice]: @property def devices_dict(self) -> dict: + """A dictionary of devices associated with the location.""" devices_dict: dict = {} for device in self.devices: devices_dict[device.macID] = device @@ -158,40 +199,50 @@ def devices_dict(self) -> dict: @property def users(self): + """The users associated with the location.""" return [Users(x) for x in self.attributes.get("users", [])] @property def timeZone(self): + """The timezone of the location.""" return self.attributes.get("timeZone", None) @property def ianaTimeZone(self): + """The IANA timezone of the location.""" return self.attributes.get("ianaTimeZone", None) @property def daylightSavingTimeEnabled(self): + """Whether daylight saving time is enabled for the location.""" return self.attributes.get("daylightSavingTimeEnabled", True) @property def geoFenceEnabled(self): + """Whether geofencing is enabled for the location.""" return self.attributes.get("geoFenceEnabled", False) @property def predictiveAIREnabled(self): + """Whether predictive AI is enabled for the location.""" return self.attributes.get("predictiveAIREnabled", False) @property def comfortLevel(self): + """The comfort level of the location.""" return self.attributes.get("comfortLevel", None) @property def geoFenceNotificationEnabled(self): + """Whether geofence notifications are enabled for the location.""" return self.attributes.get("geoFenceNotificationEnabled", False) @property def geoFenceNotificationTypeId(self): + """The ID of the geofence notification type for the location.""" return self.attributes.get("geoFenceNotificationTypeId", None) @property def configuration(self): + """The configuration of the location.""" return Configuration(self.attributes.get("configuration", {})) diff --git a/aiolyric/objects/priority.py b/aiolyric/objects/priority.py index 62dbc45..1c3c53d 100644 --- a/aiolyric/objects/priority.py +++ b/aiolyric/objects/priority.py @@ -1,96 +1,115 @@ -"""Class object for LyricPriority -Documentation: https://github.com/timmo001/aiolyric +"""Lyric priority.""" +# pylint: disable=invalid-name -Generated by generator/generator.py - 2023-07-27 18:30:29.139453 -""" -from .base import LyricBase +from .base import LyricBaseObject -class LyricAccessories(LyricBase): +class LyricAccessories(LyricBaseObject): + """Lyric accessories.""" @property def id(self): + """Get the ID of the accessory.""" return self.attributes.get("id", None) @property def type(self): + """Get the type of the accessory.""" return self.attributes.get("type", "") @property def excludeTemp(self): + """Check if temperature is excluded for the accessory.""" return self.attributes.get("excludeTemp", False) @property def excludeMotion(self): + """Check if motion is excluded for the accessory.""" return self.attributes.get("excludeMotion", False) @property def temperature(self): + """Get the temperature of the accessory.""" return self.attributes.get("temperature", None) @property def status(self): + """Get the status of the accessory.""" return self.attributes.get("status", "") @property def detectMotion(self): + """Check if motion is detected for the accessory.""" return self.attributes.get("detectMotion", False) -class LyricRoom(LyricBase): +class LyricRoom(LyricBaseObject): + """Class representing Lyric rooms.""" @property def id(self): + """Get the ID of the room.""" return self.attributes.get("id", None) @property def roomName(self): + """Get the name of the room.""" return self.attributes.get("roomName", "") @property def roomAvgTemp(self): + """Get the average temperature of the room.""" return self.attributes.get("roomAvgTemp", None) @property def roomAvgHumidity(self): + """Get the average humidity of the room.""" return self.attributes.get("roomAvgHumidity", None) @property def overallMotion(self): + """Check if motion is detected in the room.""" return self.attributes.get("overallMotion", False) @property def accessories(self): + """Get the list of accessories in the room.""" return [LyricAccessories(x) for x in self.attributes.get("accessories", [])] -class CurrentPriority(LyricBase): +class CurrentPriority(LyricBaseObject): + """Class representing the current priority.""" @property def priorityType(self): + """Get the type of the priority.""" return self.attributes.get("priorityType", "") @property def selectedRooms(self): + """Get the list of selected rooms for the priority.""" return self.attributes.get("selectedRooms", []) @property def rooms(self): + """Get the list of rooms for the priority.""" return [LyricRoom(x) for x in self.attributes.get("rooms", [])] -class LyricPriority(LyricBase): +class LyricPriority(LyricBaseObject): + """Class representing Lyric priority.""" @property def deviceId(self): + """Get the ID of the device.""" return self.attributes.get("deviceId", "") @property def status(self): + """Get the status of the priority.""" return self.attributes.get("status", "") @property def currentPriority(self): + """Get the current priority.""" return CurrentPriority(self.attributes.get("currentPriority", {})) - - diff --git a/generator/generator.py b/generator/generator.py deleted file mode 100644 index 8814a8d..0000000 --- a/generator/generator.py +++ /dev/null @@ -1,279 +0,0 @@ -from datetime import datetime -import json -import os -import re - -DATE = datetime.now() - -OUT = """\"\"\" -Class object for {classname} -Documentation: {documentation} - -Generated by generator/generator.py - {date} -\"\"\" -from Lyric.objects.objects.base import LyricBase - -{inherit} -""" - -CLASS = """ -class {classname}(LyricBase): -{properties} -""" - -PROP = """ - @property - def {key}(self): - return self.attributes.get("{key}", {default}) -""" -PROPCLASS = """ - @property - def {key}(self): - return {name}(self.attributes.get("{key}", {default})) -""" -PROPLISTCLASS = """ - @property - def {key}(self): - return [{name}(x) for x in self.attributes.get("{key}", [])] -""" - -FIXSTURE = """\"\"\" -Generated by generator/generator.py - {date} -\"\"\" -import pytest - - -@pytest.fixture() -def {name}(): - return {content} -""" - -TEST = """\"\"\" -Generated by generator/generator.py - {date} -\"\"\" -from {importfile} import {importclass} -from {fixturefile} import {fixture} - -def {testname}({fixture}): - obj = {importclass}({fixture}) -{assertions} -""" - -INHERIT = [] - - -def get_input(): - with open("generator/input.json") as inputdata: - return json.loads(inputdata.read()) - - -def generateclass(name, data, primary=False): - properties = [] - # print(data) - for key in data: - print(key) - if key.startswith("_"): - continue - if isinstance(data[key], list): - if not isinstance(data[key][0], dict) and not isinstance( - data[key][0], list - ): - properties.append(PROP.format(key=key, default=[])) - continue - _name = key.split("_") - _name = "".join([x.title() for x in _name]) - _name = f"{_name}" - INHERIT.append(generateclass(_name, data[key][0])) - properties.append(PROPLISTCLASS.format(name=_name, key=key)) - continue - if isinstance(data[key], dict): - _name = key.split("_") - _name = "".join([x.title() for x in _name]) - _name = f"{name}{_name}" - INHERIT.append(generateclass(_name, data[key])) - properties.append(PROPCLASS.format(name=_name, key=key, default={})) - continue - if isinstance(data[key], bool): - properties.append(PROP.format(key=key, default=data[key])) - continue - if isinstance(data[key], str): - properties.append(PROP.format(key=key, default='""')) - continue - properties.append(PROP.format(key=key, default=None)) - - if not primary: - return CLASS.format(classname=name, properties="".join(properties)) - docs = input("Documentation URL: ") - classname = input("Main Classname: ") - INHERIT.append( - CLASS.format(classname=f"Lyric{classname}", properties="".join(properties)) - ) - - objectfilename = f"aiolyric/objects/{'/'.join([x.lower() for x in re.findall('[A-Z][a-z]*', classname)])}.py" - if not os.path.exists(os.path.dirname(objectfilename)): - os.makedirs(os.path.dirname(objectfilename)) - with open(os.path.join(os.path.dirname(objectfilename), "__init__.py")) as f: - f.write() - - with open( - objectfilename, - "w", - ) as objfile: - objfile.write( - OUT.format( - classname=f"Lyric{classname}", - properties=properties, - documentation=docs, - inherit="".join(INHERIT), - date=DATE, - ) - ) - - fixturefilename = f"tests/responses/{'/'.join([x.lower() for x in re.findall('[A-Z][a-z]*', classname)])}_fixture.py" - fixturename = f"{fixturefilename.split('/')[-1].replace('.py', '')}_response" - - if not os.path.exists(os.path.dirname(fixturefilename)): - os.makedirs(os.path.dirname(fixturefilename)) - with open( - fixturefilename, - "w", - ) as fixturefile: - fixturefile.write( - FIXSTURE.format( - name=fixturename, - content=data, - date=DATE, - ) - ) - - tmpfilename = [x.lower() for x in re.findall("[A-Z][a-z]*", classname)] - tmpr = tmpfilename.pop() - tmpfilename.append(f"test_{tmpr}") - testfilename = f"tests/objects/{'/'.join(tmpfilename)}.py" - testname = f"test_{tmpr}" - assertions = [] - - for key in data: - if key.startswith("_"): - continue - if not isinstance(data[key], (dict, list)): - assertions.append(f" assert obj.{key} == {fixturename}['{key}']") - if isinstance(data[key], list): - if not isinstance(data[key][0], (dict, list)): - assertions.append( - f" assert obj.{key}[0] == {fixturename}['{key}'][0]" - ) - if isinstance(data[key][0], dict): - for sakey in data[key][0]: - assertions.append( - f" assert obj.{key}[0].{sakey} == {fixturename}['{key}'][0]['{sakey}']" - ) - if isinstance(data[key], dict): - for akey in data[key]: - if not isinstance(data[key][akey], (dict, list)): - assertions.append( - f" assert obj.{key}.{akey} == {fixturename}['{key}']['{akey}']" - ) - if isinstance(data[key][akey], list): - if not isinstance(data[key][akey][0], (dict, list)): - assertions.append( - f" assert obj.{key}.{akey}[0] == {fixturename}['{key}']['{akey}'][0]" - ) - if isinstance(data[key][akey][0], dict): - for sakey in data[key][akey][0]: - assertions.append( - f" assert obj.{key}.{akey}[0].{sakey} == {fixturename}['{key}']['{akey}'][0]['{sakey}']" - ) - if isinstance(data[key][akey], dict): - for bkey in data[key][akey]: - if not isinstance(data[key][akey][bkey], (dict, list)): - assertions.append( - f" assert obj.{key}.{akey}.{bkey} == {fixturename}['{key}']['{akey}']['{bkey}']" - ) - if isinstance(data[key][akey][bkey], list): - if not isinstance(data[key][akey][bkey][0], (dict, list)): - assertions.append( - f" assert obj.{key}.{akey}.{bkey}[0] == {fixturename}['{key}']['{akey}']['{bkey}'][0]" - ) - if isinstance(data[key][akey][bkey][0], dict): - for sakey in data[key][akey][bkey][0]: - assertions.append( - f" assert obj.{key}.{akey}.{bkey}[0].{sakey} == {fixturename}['{key}']['{akey}']['{bkey}'][0]['{sakey}']" - ) - if isinstance(data[key][akey][bkey], dict): - for ckey in data[key][akey][bkey]: - if not isinstance( - data[key][akey][bkey][ckey], (dict, list) - ): - assertions.append( - f" assert obj.{key}.{akey}.{bkey}.{ckey} == {fixturename}['{key}']['{akey}']['{bkey}']['{ckey}']" - ) - if isinstance(data[key][akey][bkey][ckey], list): - if not isinstance( - data[key][akey][bkey][ckey][0], (dict, list) - ): - assertions.append( - f" assert obj.{key}.{akey}.{bkey}.{ckey}[0] == {fixturename}['{key}']['{akey}']['{bkey}']['{ckey}'][0]" - ) - if isinstance(data[key][akey][bkey][ckey][0], dict): - for sakey in data[key][akey][bkey][ckey][0]: - assertions.append( - f" assert obj.{key}.{akey}.{bkey}.{ckey}[0].{sakey} == {fixturename}['{key}']['{akey}']['{bkey}']['{ckey}'][0]['{sakey}']" - ) - if isinstance(data[key][akey][bkey][ckey], dict): - for dkey in data[key][akey][bkey][ckey]: - if not isinstance( - data[key][akey][bkey][ckey][dkey], - (dict, list), - ): - - assertions.append( - f" assert obj.{key}.{akey}.{bkey}.{ckey}.{dkey} == {fixturename}['{key}']['{akey}']['{bkey}']['{ckey}']['{dkey}']" - ) - if isinstance( - data[key][akey][bkey][ckey][dkey], list - ): - if not isinstance( - data[key][akey][bkey][ckey][dkey][0], - (dict, list), - ): - assertions.append( - f" assert obj.{key}.{akey}.{bkey}.{ckey}.{dkey}[0] == {fixturename}['{key}']['{akey}']['{bkey}']['{ckey}']['{dkey}'][0]" - ) - if isinstance( - data[key][akey][bkey][ckey][dkey][0], - dict, - ): - for sakey in data[key][akey][bkey][ - ckey - ][dkey][0]: - assertions.append( - f" assert obj.{key}.{akey}.{bkey}.{ckey}.{dkey}[0].{sakey} == {fixturename}['{key}']['{akey}']['{bkey}']['{ckey}']['{dkey}'][0]['{sakey}']" - ) - continue - - if not os.path.exists(os.path.dirname(testfilename)): - os.makedirs(os.path.dirname(testfilename)) - with open( - testfilename, - "w", - ) as fixturefile: - fixturefile.write( - TEST.format( - importfile=".".join(objectfilename.replace(".py", "").split("/")), - importclass=f"Lyric{classname}", - fixturefile=".".join(fixturefilename.replace(".py", "").split("/")), - fixture=fixturename, - testname=testname, - assertions="\n".join(assertions), - date=DATE, - ) - ) - - -def add_object(): - data = get_input() - generateclass("", data, True) - - -add_object() diff --git a/tests/responses/device_fixture.py b/tests/fixtures/device_fixture.py similarity index 96% rename from tests/responses/device_fixture.py rename to tests/fixtures/device_fixture.py index 02819ae..21cab58 100644 --- a/tests/responses/device_fixture.py +++ b/tests/fixtures/device_fixture.py @@ -1,10 +1,11 @@ -"""Generated by generator/generator.py - 2020-08-31 14:06:02.854691 -""" +"""Fixtures for device tests.""" + import pytest @pytest.fixture() def device_fixture_response(): + """Return a fixture response for a device.""" return { "locationID": 123456, "displayedOutdoorHumidity": 51, diff --git a/tests/responses/location_fixture.py b/tests/fixtures/location_fixture.py similarity index 97% rename from tests/responses/location_fixture.py rename to tests/fixtures/location_fixture.py index 3ead353..3d7e6c8 100644 --- a/tests/responses/location_fixture.py +++ b/tests/fixtures/location_fixture.py @@ -1,10 +1,11 @@ -"""Generated by generator/generator.py - 2020-08-31 13:55:49.220121 -""" +"""Fixtures for location responses.""" + import pytest @pytest.fixture() def location_fixture_response(): + """Return a fixture response for a location.""" return { "locationID": 123456, "name": "Home", diff --git a/tests/responses/priority_fixture.py b/tests/fixtures/priority_fixture.py similarity index 84% rename from tests/responses/priority_fixture.py rename to tests/fixtures/priority_fixture.py index fbb883f..6157e37 100644 --- a/tests/responses/priority_fixture.py +++ b/tests/fixtures/priority_fixture.py @@ -1,18 +1,17 @@ -"""Generated by generator/generator.py - 2023-07-27 18:30:29.139453 -""" +"""Fixture for priority response.""" + import pytest @pytest.fixture() def priority_fixture_response(): + """Return a fixture response for a priority.""" return { "deviceId": "00A01AB1ABCD", "status": "NoHold", "currentPriority": { "priorityType": "PickARoom", - "selectedRooms": [ - 0 - ], + "selectedRooms": [0], "rooms": [ { "id": 0, @@ -28,9 +27,9 @@ def priority_fixture_response(): "excludeMotion": False, "temperature": 75.828, "status": "Ok", - "detectMotion": False + "detectMotion": False, } - ] + ], }, { "id": 1, @@ -46,9 +45,9 @@ def priority_fixture_response(): "excludeMotion": False, "temperature": 76, "status": "Ok", - "detectMotion": True + "detectMotion": True, } - ] + ], }, { "id": 2, @@ -64,10 +63,10 @@ def priority_fixture_response(): "excludeMotion": False, "temperature": 76, "status": "Ok", - "detectMotion": True + "detectMotion": True, } - ] - } - ] - } + ], + }, + ], + }, } diff --git a/tests/objects/test_device.py b/tests/objects/test_device.py index c3e409a..22a1875 100644 --- a/tests/objects/test_device.py +++ b/tests/objects/test_device.py @@ -1,10 +1,14 @@ -"""Generated by generator/generator.py - 2020-08-31 14:06:02.854691 -""" +"""Test device object.""" + from aiolyric.objects.device import LyricDevice def test_device(device_fixture_response): - obj = LyricDevice(None, device_fixture_response) + """Test device object.""" + obj = LyricDevice( + None, + device_fixture_response, + ) assert obj.locationID == device_fixture_response["locationID"] assert ( obj.displayedOutdoorHumidity diff --git a/tests/objects/test_location.py b/tests/objects/test_location.py index 7f3224a..c434919 100644 --- a/tests/objects/test_location.py +++ b/tests/objects/test_location.py @@ -1,10 +1,14 @@ -"""Generated by generator/generator.py - 2020-08-31 13:55:49.220121 -""" +"""Tests for the location object.""" + from aiolyric.objects.location import LyricLocation def test_location(location_fixture_response): - obj = LyricLocation(None, location_fixture_response) + """Test location object.""" + obj = LyricLocation( + None, + location_fixture_response, + ) assert obj.locationID == location_fixture_response["locationID"] assert obj.name == location_fixture_response["name"] assert obj.country == location_fixture_response["country"] @@ -27,11 +31,15 @@ def test_location(location_fixture_response): ) assert ( obj.devices[0].scheduleCapabilities.availableScheduleTypes - == location_fixture_response["devices"][0]["scheduleCapabilities"]["availableScheduleTypes"] + == location_fixture_response["devices"][0]["scheduleCapabilities"][ + "availableScheduleTypes" + ] ) assert ( obj.devices[0].scheduleCapabilities.schedulableFan - == location_fixture_response["devices"][0]["scheduleCapabilities"]["schedulableFan"] + == location_fixture_response["devices"][0]["scheduleCapabilities"][ + "schedulableFan" + ] ) assert ( obj.devices[0].scheduleType.scheduleType @@ -51,11 +59,15 @@ def test_location(location_fixture_response): ) assert ( obj.devices[0].settings.hardwareSettings.brightness - == location_fixture_response["devices"][0]["settings"]["hardwareSettings"]["brightness"] + == location_fixture_response["devices"][0]["settings"]["hardwareSettings"][ + "brightness" + ] ) assert ( obj.devices[0].settings.hardwareSettings.maxBrightness - == location_fixture_response["devices"][0]["settings"]["hardwareSettings"]["maxBrightness"] + == location_fixture_response["devices"][0]["settings"]["hardwareSettings"][ + "maxBrightness" + ] ) assert ( obj.devices[0].settings.temperatureMode.air @@ -87,7 +99,10 @@ def test_location(location_fixture_response): == location_fixture_response["devices"][0]["isProvisioned"] ) assert obj.devices[0].macID == location_fixture_response["devices"][0]["macID"] - assert obj.devices[0].service.mode == location_fixture_response["devices"][0]["service"]["mode"] + assert ( + obj.devices[0].service.mode + == location_fixture_response["devices"][0]["service"]["mode"] + ) assert ( obj.devices[0].deviceRegistrationDate == location_fixture_response["devices"][0]["deviceRegistrationDate"] @@ -146,7 +161,9 @@ def test_location(location_fixture_response): ) assert ( obj.devices[0].changeableValues.thermostatSetpointStatus - == location_fixture_response["devices"][0]["changeableValues"]["thermostatSetpointStatus"] + == location_fixture_response["devices"][0]["changeableValues"][ + "thermostatSetpointStatus" + ] ) assert ( obj.devices[0].changeableValues.nextPeriodTime @@ -158,11 +175,15 @@ def test_location(location_fixture_response): ) assert ( obj.devices[0].changeableValues.endHeatSetpoint - == location_fixture_response["devices"][0]["changeableValues"]["endHeatSetpoint"] + == location_fixture_response["devices"][0]["changeableValues"][ + "endHeatSetpoint" + ] ) assert ( obj.devices[0].changeableValues.endCoolSetpoint - == location_fixture_response["devices"][0]["changeableValues"]["endCoolSetpoint"] + == location_fixture_response["devices"][0]["changeableValues"][ + "endCoolSetpoint" + ] ) assert ( obj.devices[0].operationStatus.mode @@ -174,7 +195,9 @@ def test_location(location_fixture_response): ) assert ( obj.devices[0].operationStatus.circulationFanRequest - == location_fixture_response["devices"][0]["operationStatus"]["circulationFanRequest"] + == location_fixture_response["devices"][0]["operationStatus"][ + "circulationFanRequest" + ] ) assert ( obj.devices[0].deviceModel @@ -201,7 +224,9 @@ def test_location(location_fixture_response): ) assert ( obj.users[0].locationRoleMapping[0].locationName - == location_fixture_response["users"][0]["locationRoleMapping"][0]["locationName"] + == location_fixture_response["users"][0]["locationRoleMapping"][0][ + "locationName" + ] ) assert ( obj.users[0].locationRoleMapping[0].status diff --git a/tests/objects/test_priority.py b/tests/objects/test_priority.py index 0a3ef49..17faff4 100644 --- a/tests/objects/test_priority.py +++ b/tests/objects/test_priority.py @@ -1,17 +1,42 @@ -"""Generated by generator/generator.py - 2023-07-27 18:30:29.139453 -""" +"""Tests for the Priority object.""" + from aiolyric.objects.priority import LyricPriority def test_priority(priority_fixture_response): + """Test priority object.""" obj = LyricPriority(priority_fixture_response) assert obj.deviceId == priority_fixture_response["deviceId"] assert obj.status == priority_fixture_response["status"] - assert obj.currentPriority.priorityType == priority_fixture_response["currentPriority"]["priorityType"] - assert obj.currentPriority.selectedRooms[0] == priority_fixture_response["currentPriority"]["selectedRooms"][0] - assert obj.currentPriority.rooms[0].id == priority_fixture_response["currentPriority"]["rooms"][0]["id"] - assert obj.currentPriority.rooms[0].roomName == priority_fixture_response["currentPriority"]["rooms"][0]["roomName"] - assert obj.currentPriority.rooms[0].roomAvgTemp == priority_fixture_response["currentPriority"]["rooms"][0]["roomAvgTemp"] - assert obj.currentPriority.rooms[0].roomAvgHumidity == priority_fixture_response["currentPriority"]["rooms"][0]["roomAvgHumidity"] - assert obj.currentPriority.rooms[0].overallMotion == priority_fixture_response["currentPriority"]["rooms"][0]["overallMotion"] - assert obj.currentPriority.rooms[0].accessories == priority_fixture_response["currentPriority"]["rooms"][0]["accessories"] + assert ( + obj.currentPriority.priorityType + == priority_fixture_response["currentPriority"]["priorityType"] + ) + assert ( + obj.currentPriority.selectedRooms[0] + == priority_fixture_response["currentPriority"]["selectedRooms"][0] + ) + assert ( + obj.currentPriority.rooms[0].id + == priority_fixture_response["currentPriority"]["rooms"][0]["id"] + ) + assert ( + obj.currentPriority.rooms[0].roomName + == priority_fixture_response["currentPriority"]["rooms"][0]["roomName"] + ) + assert ( + obj.currentPriority.rooms[0].roomAvgTemp + == priority_fixture_response["currentPriority"]["rooms"][0]["roomAvgTemp"] + ) + assert ( + obj.currentPriority.rooms[0].roomAvgHumidity + == priority_fixture_response["currentPriority"]["rooms"][0]["roomAvgHumidity"] + ) + assert ( + obj.currentPriority.rooms[0].overallMotion + == priority_fixture_response["currentPriority"]["rooms"][0]["overallMotion"] + ) + assert ( + obj.currentPriority.rooms[0].accessories + == priority_fixture_response["currentPriority"]["rooms"][0]["accessories"] + ) diff --git a/tests/test__version.py b/tests/test__version.py new file mode 100644 index 0000000..bcb78f2 --- /dev/null +++ b/tests/test__version.py @@ -0,0 +1,8 @@ +"""Test __version__ module.""" + +from aiolyric._version import __version__ + + +def test__version(): + """Test the __version__ string.""" + assert isinstance(__version__.public(), str)