From b176aef2b51451984c02afde0cf872346df1be89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Nenz=C3=A9n?= Date: Thu, 15 Aug 2024 22:10:21 +0000 Subject: [PATCH 1/2] Code format --- .github/release-drafter.yml | 2 +- .github/workflows/release-drafter.yaml | 2 - .github/workflows/release.yaml | 2 +- .pre-commit-config.yaml | 38 ++++ README.md | 6 +- custom_components/wellbeing/__init__.py | 17 +- custom_components/wellbeing/api.py | 194 ++++++++---------- custom_components/wellbeing/binary_sensor.py | 6 +- custom_components/wellbeing/config_flow.py | 72 ++----- custom_components/wellbeing/entity.py | 7 +- custom_components/wellbeing/fan.py | 18 +- custom_components/wellbeing/sensor.py | 7 +- custom_components/wellbeing/switch.py | 15 +- .../wellbeing/translations/en.json | 2 +- .../wellbeing/translations/fr.json | 2 +- .../wellbeing/translations/nb.json | 2 +- info.md | 6 +- manage/update_manifest.py | 7 +- requirements.txt | 2 +- 19 files changed, 195 insertions(+), 212 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 157c976..4483ee2 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -32,4 +32,4 @@ template: | [![Downloads for this release](https://img.shields.io/github/downloads/JohNan/homeassistant-wellbeing/v$RESOLVED_VERSION/total.svg)](https://github.com/JohNan/homeassistant-wellbeing/releases/v$RESOLVED_VERSION) ## Changes - $CHANGES \ No newline at end of file + $CHANGES diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml index fc3be14..573168d 100644 --- a/.github/workflows/release-drafter.yaml +++ b/.github/workflows/release-drafter.yaml @@ -14,5 +14,3 @@ jobs: uses: release-drafter/release-drafter@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 8826291..67ffc44 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -32,4 +32,4 @@ jobs: file: ./custom_components/wellbeing/wellbeing.zip asset_name: wellbeing.zip tag: ${{ github.ref }} - overwrite: true \ No newline at end of file + overwrite: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ea10020 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,38 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-added-large-files + - id: check-json + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: no-commit-to-branch + args: [ '--branch', 'main', '--branch', 'master' ] + - id: requirements-txt-fixer + - id: trailing-whitespace + + - repo: https://github.com/psf/black + rev: 24.8.0 + hooks: + - id: black + args: [ '--target-version', 'py312', '--line-length=120', '.' ] + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.5.6 + hooks: + - id: ruff + args: [ --fix ] + + - repo: https://github.com/asottile/pyupgrade + rev: v2.31.0 + hooks: + - id: pyupgrade + args: [ '--py311-plus' ] + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.11.1 + hooks: + - id: mypy diff --git a/README.md b/README.md index a8ca46b..11f35f8 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ A custom component designed for [Home Assistant](https://www.home-assistant.io) - WA71-302GY - WA71-302DG - WA71-304GY - + - Electrolux Pure A9 Purifiers - PA91-406GY - PA91-606DG @@ -29,11 +29,11 @@ A custom component designed for [Home Assistant](https://www.home-assistant.io) - AEG AX5 Air Purifiers - AX51-304WT - + - AEG AX7 Air Purifiers - AX71-304GY - AX71-304DG - + - AEG AX9 Air Purifiers - AX91-404GY - AX91-404DG diff --git a/custom_components/wellbeing/__init__.py b/custom_components/wellbeing/__init__.py index 49bd235..068b8e7 100644 --- a/custom_components/wellbeing/__init__.py +++ b/custom_components/wellbeing/__init__.py @@ -4,6 +4,7 @@ For more details about this integration, please refer to https://github.com/JohNan/homeassistant-wellbeing """ + import logging from datetime import timedelta @@ -24,6 +25,7 @@ _LOGGER: logging.Logger = logging.getLogger(__package__) PLATFORMS = [Platform.SENSOR, Platform.FAN, Platform.BINARY_SENSOR, Platform.SWITCH] + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up this integration using UI.""" if hass.data.get(DOMAIN) is None: @@ -36,10 +38,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): token_manager = WellBeingTokenManager(hass, entry) try: - hub = ElectroluxHubAPI( - session=async_get_clientsession(hass), - token_manager=token_manager - ) + hub = ElectroluxHubAPI(session=async_get_clientsession(hass), token_manager=token_manager) except Exception as exception: raise ConfigEntryAuthFailed("Failed to setup API") from exception @@ -86,9 +85,7 @@ async def _async_update_data(self): """Update data via library.""" try: appliances = await self.api.async_get_appliances() - return { - "appliances": appliances - } + return {"appliances": appliances} except Exception as exception: raise UpdateFailed(exception) from exception @@ -111,9 +108,5 @@ def update(self, access_token: str, refresh_token: str, api_key: str | None = No self._hass.config_entries.async_update_entry( self._entry, - data={ - CONF_API_KEY: self.api_key, - CONF_REFRESH_TOKEN: refresh_token, - CONF_ACCESS_TOKEN: access_token - }, + data={CONF_API_KEY: self.api_key, CONF_REFRESH_TOKEN: refresh_token, CONF_ACCESS_TOKEN: access_token}, ) diff --git a/custom_components/wellbeing/api.py b/custom_components/wellbeing/api.py index 196830a..8a57bf0 100644 --- a/custom_components/wellbeing/api.py +++ b/custom_components/wellbeing/api.py @@ -1,11 +1,19 @@ """Sample API Client.""" + import logging from enum import Enum from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass -from homeassistant.const import UnitOfTemperature, PERCENTAGE, CONCENTRATION_PARTS_PER_MILLION, \ - CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, Platform, EntityCategory +from homeassistant.const import ( + UnitOfTemperature, + PERCENTAGE, + CONCENTRATION_PARTS_PER_MILLION, + CONCENTRATION_PARTS_PER_BILLION, + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + Platform, + EntityCategory, +) from homeassistant.helpers.typing import UNDEFINED from pyelectroluxgroup.api import ElectroluxHubAPI from pyelectroluxgroup.appliance import Appliance as ApiAppliance @@ -23,7 +31,7 @@ 99: "Breeze 360 filter", 100: "Fresh 360 filter", 192: "FRESH Odour protect filter", - 0: "Filter" + 0: "Filter", } _LOGGER: logging.Logger = logging.getLogger(__package__) @@ -37,15 +45,15 @@ class Mode(str, Enum): class ApplianceEntity: - entity_type: int = None + entity_type: int | None = None def __init__( - self, - name, - attr, - device_class=None, - entity_category: EntityCategory = UNDEFINED, - state_class: SensorStateClass | str | None = None + self, + name, + attr, + device_class=None, + entity_category: EntityCategory = UNDEFINED, + state_class: SensorStateClass | str | None = None, ) -> None: self.attr = attr self.name = name @@ -70,13 +78,13 @@ class ApplianceSensor(ApplianceEntity): entity_type: int = Platform.SENSOR def __init__( - self, - name, - attr, - unit="", - device_class=None, - entity_category: EntityCategory = UNDEFINED, - state_class: SensorStateClass | str | None = None + self, + name, + attr, + unit="", + device_class=None, + entity_category: EntityCategory = UNDEFINED, + state_class: SensorStateClass | str | None = None, ) -> None: super().__init__(name, attr, device_class, entity_category, state_class) self.unit = unit @@ -97,7 +105,7 @@ def __init__(self, name, attr, device_class=None, entity_category: EntityCategor @property def state(self): - return self._state in ['enabled', True, 'Connected', 'on'] + return self._state in ["enabled", True, "Connected", "on"] class Appliance: @@ -106,8 +114,8 @@ class Appliance: device: str firmware: str mode: Mode - entities: [] - capabilities: {} + entities: list + capabilities: dict def __init__(self, name, pnc_id, model) -> None: self.model = model @@ -119,175 +127,145 @@ def _create_entities(data): pure500_entities = [ ApplianceSensor( name="PM2.5", - attr='PM2_5_approximate', + attr="PM2_5_approximate", unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, device_class=SensorDeviceClass.PM25, - state_class=SensorStateClass.MEASUREMENT + state_class=SensorStateClass.MEASUREMENT, ), ApplianceBinary( - name='UV State', - attr='UVState', + name="UV State", + attr="UVState", entity_category=EntityCategory.DIAGNOSTIC, - ) + ), ] a7_entities = [ ApplianceSensor( name=f"{FILTER_TYPE.get(data.get('FilterType_1', 0), 'Unknown filter')} Life", - attr='FilterLife_1', - unit=PERCENTAGE + attr="FilterLife_1", + unit=PERCENTAGE, ), ApplianceSensor( name=f"{FILTER_TYPE.get(data.get('FilterType_2', 0), 'Unknown filter')} Life", - attr='FilterLife_2', - unit=PERCENTAGE - ), - ApplianceSensor( - name='State', - attr='State', - entity_category=EntityCategory.DIAGNOSTIC + attr="FilterLife_2", + unit=PERCENTAGE, ), + ApplianceSensor(name="State", attr="State", entity_category=EntityCategory.DIAGNOSTIC), ApplianceBinary( - name='PM Sensor State', - attr='PMSensState', + name="PM Sensor State", + attr="PMSensState", entity_category=EntityCategory.DIAGNOSTIC, - ) + ), ] a9_entities = [ ApplianceSensor( name=f"{FILTER_TYPE.get(data.get('FilterType', 0), 'Unknown filter')} Life", - attr='FilterLife', - unit=PERCENTAGE + attr="FilterLife", + unit=PERCENTAGE, ), ApplianceSensor( name="CO2", - attr='CO2', + attr="CO2", unit=CONCENTRATION_PARTS_PER_MILLION, device_class=SensorDeviceClass.CO2, - state_class=SensorStateClass.MEASUREMENT - ) + state_class=SensorStateClass.MEASUREMENT, + ), ] common_entities = [ ApplianceFan( name="Fan Speed", - attr='Fanspeed', + attr="Fanspeed", ), ApplianceSensor( name="Temperature", - attr='Temp', + attr="Temp", unit=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT + state_class=SensorStateClass.MEASUREMENT, ), ApplianceSensor( - name="TVOC", - attr='TVOC', - unit=CONCENTRATION_PARTS_PER_BILLION, - state_class=SensorStateClass.MEASUREMENT + name="TVOC", attr="TVOC", unit=CONCENTRATION_PARTS_PER_BILLION, state_class=SensorStateClass.MEASUREMENT ), ApplianceSensor( name="eCO2", - attr='ECO2', + attr="ECO2", unit=CONCENTRATION_PARTS_PER_MILLION, device_class=SensorDeviceClass.CO2, - state_class=SensorStateClass.MEASUREMENT + state_class=SensorStateClass.MEASUREMENT, ), ApplianceSensor( name="PM1", - attr='PM1', + attr="PM1", unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, device_class=SensorDeviceClass.PM1, - state_class=SensorStateClass.MEASUREMENT + state_class=SensorStateClass.MEASUREMENT, ), ApplianceSensor( name="PM2.5", - attr='PM2_5', + attr="PM2_5", unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, device_class=SensorDeviceClass.PM25, - state_class=SensorStateClass.MEASUREMENT + state_class=SensorStateClass.MEASUREMENT, ), ApplianceSensor( name="PM10", - attr='PM10', + attr="PM10", unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, device_class=SensorDeviceClass.PM10, - state_class=SensorStateClass.MEASUREMENT + state_class=SensorStateClass.MEASUREMENT, ), ApplianceSensor( name="Humidity", - attr='Humidity', + attr="Humidity", unit=PERCENTAGE, device_class=SensorDeviceClass.HUMIDITY, - state_class=SensorStateClass.MEASUREMENT - ), - ApplianceSensor( - name="Mode", - attr='Workmode', - device_class=SensorDeviceClass.ENUM + state_class=SensorStateClass.MEASUREMENT, ), + ApplianceSensor(name="Mode", attr="Workmode", device_class=SensorDeviceClass.ENUM), ApplianceSensor( name="Signal Strength", - attr='SignalStrength', + attr="SignalStrength", device_class=SensorDeviceClass.ENUM, - entity_category=EntityCategory.DIAGNOSTIC - ), - ApplianceBinary( - name="Ionizer", - attr='Ionizer' - ), - ApplianceBinary( - name="UI Light", - attr='UILight' + entity_category=EntityCategory.DIAGNOSTIC, ), + ApplianceBinary(name="Ionizer", attr="Ionizer"), + ApplianceBinary(name="UI Light", attr="UILight"), ApplianceBinary( name="Door Open", - attr='DoorOpen', + attr="DoorOpen", device_class=BinarySensorDeviceClass.DOOR, - entity_category=EntityCategory.DIAGNOSTIC + entity_category=EntityCategory.DIAGNOSTIC, ), ApplianceBinary( name="Connection State", - attr='connectionState', + attr="connectionState", device_class=BinarySensorDeviceClass.CONNECTIVITY, - entity_category=EntityCategory.DIAGNOSTIC - ), - ApplianceBinary( - name="Status", - attr='status', - entity_category=EntityCategory.DIAGNOSTIC + entity_category=EntityCategory.DIAGNOSTIC, ), - ApplianceBinary( - name="Safety Lock", - attr='SafetyLock', - device_class=BinarySensorDeviceClass.LOCK - ) + ApplianceBinary(name="Status", attr="status", entity_category=EntityCategory.DIAGNOSTIC), + ApplianceBinary(name="Safety Lock", attr="SafetyLock", device_class=BinarySensorDeviceClass.LOCK), ] return common_entities + a9_entities + a7_entities + pure500_entities def get_entity(self, entity_type, entity_attr): return next( - entity - for entity in self.entities - if entity.attr == entity_attr and entity.entity_type == entity_type + entity for entity in self.entities if entity.attr == entity_attr and entity.entity_type == entity_type ) def has_capability(self, capability) -> bool: - return capability in self.capabilities and self.capabilities[capability]['access'] == 'readwrite' + return capability in self.capabilities and self.capabilities[capability]["access"] == "readwrite" def clear_mode(self): self.mode = Mode.UNDEFINED def setup(self, data, capabilities): - self.firmware = data.get('FrmVer_NIU') - self.mode = Mode(data.get('Workmode')) + self.firmware = data.get("FrmVer_NIU") + self.mode = Mode(data.get("Workmode")) self.capabilities = capabilities - self.entities = [ - entity.setup(data) - for entity in Appliance._create_entities(data) if entity.attr in data - ] + self.entities = [entity.setup(data) for entity in Appliance._create_entities(data) if entity.attr in data] @property def speed_range(self) -> tuple[int, int]: @@ -324,14 +302,14 @@ class WellbeingApiClient: def __init__(self, hub: ElectroluxHubAPI) -> None: """Sample API Client.""" - self._api_appliances: {str: ApiAppliance} = None + self._api_appliances: dict[str, ApiAppliance] = {} self._hub = hub async def async_get_appliances(self) -> Appliances: """Get data from the API.""" - appliances: [ApiAppliance] = await self._hub.async_get_appliances() - self._api_appliances = dict((appliance.id, appliance) for appliance in appliances) + appliances: list[ApiAppliance] = await self._hub.async_get_appliances() + self._api_appliances = {appliance.id: appliance for appliance in appliances} found_appliances = {} for appliance in (appliance for appliance in appliances): @@ -349,12 +327,12 @@ async def async_get_appliances(self) -> Appliances: app.serialNumber = appliance.serial_number app.device = appliance.device_type - if app.device != 'AIR_PURIFIER': + if app.device != "AIR_PURIFIER": continue data = appliance.state - data['status'] = appliance.state_data.get('status', 'unknown') - data['connectionState'] = appliance.state_data.get('connectionState', 'unknown') + data["status"] = appliance.state_data.get("status", "unknown") + data["connectionState"] = appliance.state_data.get("connectionState", "unknown") app.setup(data, appliance.capabilities_data) found_appliances[app.pnc_id] = app @@ -362,9 +340,7 @@ async def async_get_appliances(self) -> Appliances: return Appliances(found_appliances) async def set_fan_speed(self, pnc_id: str, level: int): - data = { - "Fanspeed": level - } + data = {"Fanspeed": level} appliance = self._api_appliances.get(pnc_id, None) if appliance is None: _LOGGER.error(f"Failed to set fan speed for appliance with id {pnc_id}") @@ -374,9 +350,7 @@ async def set_fan_speed(self, pnc_id: str, level: int): _LOGGER.debug(f"Set Fan Speed: {result}") async def set_work_mode(self, pnc_id: str, mode: Mode): - data = { - "Workmode": mode.value - } + data = {"Workmode": mode.value} appliance = self._api_appliances.get(pnc_id, None) if appliance is None: _LOGGER.error(f"Failed to set work mode for appliance with id {pnc_id}") diff --git a/custom_components/wellbeing/binary_sensor.py b/custom_components/wellbeing/binary_sensor.py index 3d3c7ed..ccc2de1 100644 --- a/custom_components/wellbeing/binary_sensor.py +++ b/custom_components/wellbeing/binary_sensor.py @@ -1,4 +1,5 @@ """Binary sensor platform for Wellbeing.""" + from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.const import Platform @@ -9,14 +10,15 @@ async def async_setup_entry(hass, entry, async_add_devices): """Setup binary sensor platform.""" coordinator = hass.data[DOMAIN][entry.entry_id] - appliances = coordinator.data.get('appliances', None) + appliances = coordinator.data.get("appliances", None) if appliances is not None: for pnc_id, appliance in appliances.appliances.items(): async_add_devices( [ WellbeingBinarySensor(coordinator, entry, pnc_id, entity.entity_type, entity.attr) - for entity in appliance.entities if entity.entity_type == Platform.BINARY_SENSOR + for entity in appliance.entities + if entity.entity_type == Platform.BINARY_SENSOR ] ) diff --git a/custom_components/wellbeing/config_flow.py b/custom_components/wellbeing/config_flow.py index a539af7..e611a8f 100644 --- a/custom_components/wellbeing/config_flow.py +++ b/custom_components/wellbeing/config_flow.py @@ -1,11 +1,12 @@ """Adds config flow for Wellbeing.""" + import logging from typing import Mapping, Any import homeassistant.helpers.config_validation as cv import voluptuous as vol from homeassistant import config_entries -from homeassistant.config_entries import ConfigFlowResult, ConfigEntry +from homeassistant.config_entries import ConfigFlowResult, ConfigEntry, ConfigFlow from homeassistant.const import CONF_API_KEY, CONF_ACCESS_TOKEN from homeassistant.core import callback from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -18,30 +19,28 @@ _LOGGER: logging.Logger = logging.getLogger(__package__) -class WellbeingFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): + +class WellbeingFlowHandler(ConfigFlow, domain=DOMAIN): # type: ignore[call-arg] """Config flow for wellbeing.""" + entry: ConfigEntry + VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL def __init__(self): """Initialize.""" - self.entry: ConfigEntry self._errors = {} self._token_manager = WellBeingConfigFlowTokenManager() - async def async_step_user( - self, user_input: dict[str, Any] | None = None - ) -> ConfigFlowResult: + async def async_step_user(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult: """Handle a flow initialized by the user.""" self._errors = {} _LOGGER.debug(user_input) if user_input is not None: try: await self._test_credentials( - user_input[CONF_ACCESS_TOKEN], - user_input[CONF_REFRESH_TOKEN], - user_input[CONF_API_KEY] + user_input[CONF_ACCESS_TOKEN], user_input[CONF_REFRESH_TOKEN], user_input[CONF_API_KEY] ) # Copy the maybe possibly credentials @@ -52,31 +51,24 @@ async def async_step_user( self._errors["base"] = "auth" return await self._show_config_form(user_input) - return self.async_create_entry( - title=CONFIG_FLOW_TITLE, - data=user_input - ) + return self.async_create_entry(title=CONFIG_FLOW_TITLE, data=user_input) return await self._show_config_form(user_input) - async def async_step_reauth( - self, entry_data: Mapping[str, Any] - ) -> ConfigFlowResult: + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> ConfigFlowResult: """Handle configuration by re-auth.""" if entry := self.hass.config_entries.async_get_entry(self.context["entry_id"]): self.entry = entry return await self.async_step_reauth_validate() - async def async_step_reauth_validate(self, user_input=None): + async def async_step_reauth_validate(self, user_input=None) -> ConfigFlowResult: """Handle reauth and validation.""" errors: dict[str, str] = {} if user_input is not None: try: await self._test_credentials( - user_input[CONF_ACCESS_TOKEN], - user_input[CONF_REFRESH_TOKEN], - user_input[CONF_API_KEY] + user_input[CONF_ACCESS_TOKEN], user_input[CONF_REFRESH_TOKEN], user_input[CONF_API_KEY] ) # Copy the maybe possibly credentials @@ -87,31 +79,16 @@ async def async_step_reauth_validate(self, user_input=None): return self.async_update_reload_and_abort( self.entry, - data={**user_input} , + data={**user_input}, ) return self.async_show_form( step_id="reauth_validate", data_schema=vol.Schema( { - vol.Required( - CONF_API_KEY, - default=self.entry.data.get( - CONF_API_KEY, "" - ) - ): str, - vol.Required( - CONF_ACCESS_TOKEN, - default=self.entry.data.get( - CONF_ACCESS_TOKEN, "" - ) - ): str, - vol.Required( - CONF_REFRESH_TOKEN, - default=self.entry.data.get( - CONF_REFRESH_TOKEN, "" - ) - ): str, + vol.Required(CONF_API_KEY, default=self.entry.data.get(CONF_API_KEY, "")): str, + vol.Required(CONF_ACCESS_TOKEN, default=self.entry.data.get(CONF_ACCESS_TOKEN, "")): str, + vol.Required(CONF_REFRESH_TOKEN, default=self.entry.data.get(CONF_REFRESH_TOKEN, "")): str, } ), errors=errors, @@ -140,14 +117,12 @@ async def _test_credentials(self, access_token: str, refresh_token: str, api_key """Return true if credentials is valid.""" self._token_manager.update(access_token, refresh_token, api_key) - client = ElectroluxHubAPI( - session=async_get_clientsession(self.hass), - token_manager=self._token_manager - ) + client = ElectroluxHubAPI(session=async_get_clientsession(self.hass), token_manager=self._token_manager) await client.async_get_appliances() + class WellBeingConfigFlowTokenManager(TokenManager): - """TokenManager implementation for config flow """ + """TokenManager implementation for config flow""" def __init__(self): pass @@ -180,9 +155,7 @@ async def async_step_user(self, user_input=None): { vol.Optional( CONF_SCAN_INTERVAL, - default=self.config_entry.options.get( - CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL - ), + default=self.config_entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL), ): cv.positive_int, } ), @@ -190,7 +163,4 @@ async def async_step_user(self, user_input=None): async def _update_options(self): """Update config entry options.""" - return self.async_create_entry( - title=CONFIG_FLOW_TITLE, - data=self.options - ) + return self.async_create_entry(title=CONFIG_FLOW_TITLE, data=self.options) diff --git a/custom_components/wellbeing/entity.py b/custom_components/wellbeing/entity.py index fd9981b..37b9fc5 100644 --- a/custom_components/wellbeing/entity.py +++ b/custom_components/wellbeing/entity.py @@ -1,4 +1,5 @@ """WellbeingEntity class""" + from homeassistant.components.sensor import ENTITY_ID_FORMAT from homeassistant.const import EntityCategory from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -30,7 +31,7 @@ def get_entity(self) -> ApplianceEntity: @property def get_appliance(self) -> Appliance: - return self.coordinator.data['appliances'].get_appliance(self.pnc_id) + return self.coordinator.data["appliances"].get_appliance(self.pnc_id) @property def unique_id(self): @@ -52,7 +53,9 @@ def extra_state_attributes(self): """Return the state attributes.""" return { "integration": DOMAIN, - "capabilities": [key for key, value in self.get_appliance.capabilities.items() if value['access'] == 'readwrite'] + "capabilities": [ + key for key, value in self.get_appliance.capabilities.items() if value["access"] == "readwrite" + ], } @property diff --git a/custom_components/wellbeing/fan.py b/custom_components/wellbeing/fan.py index b2bde44..5198592 100644 --- a/custom_components/wellbeing/fan.py +++ b/custom_components/wellbeing/fan.py @@ -1,4 +1,5 @@ """Sensor platform for Wellbeing.""" + import asyncio import logging import math @@ -13,26 +14,25 @@ _LOGGER: logging.Logger = logging.getLogger(__package__) -SUPPORTED_FEATURES = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE | FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON +SUPPORTED_FEATURES = ( + FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE | FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON +) -PRESET_MODES = [ - Mode.OFF, - Mode.AUTO, - Mode.MANUAL -] +PRESET_MODES = [Mode.OFF, Mode.AUTO, Mode.MANUAL] async def async_setup_entry(hass, entry, async_add_devices): """Setup sensor platform.""" coordinator = hass.data[DOMAIN][entry.entry_id] - appliances = coordinator.data.get('appliances', None) + appliances = coordinator.data.get("appliances", None) if appliances is not None: for pnc_id, appliance in appliances.appliances.items(): async_add_devices( [ WellbeingFan(coordinator, entry, pnc_id, entity.entity_type, entity.attr) - for entity in appliance.entities if entity.entity_type == Platform.FAN + for entity in appliance.entities + if entity.entity_type == Platform.FAN ] ) @@ -114,7 +114,7 @@ async def async_set_preset_mode(self, preset_mode: str) -> None: def is_on(self): return self.preset_mode is not Mode.OFF - async def async_turn_on(self, speed: str = None, percentage: int = None, preset_mode: str = None, **kwargs) -> None: + async def async_turn_on(self, percentage: int | None = None, preset_mode: str | None = None, **kwargs) -> None: self._preset_mode = Mode(preset_mode or Mode.AUTO.value) # Handle incorrect percentage diff --git a/custom_components/wellbeing/sensor.py b/custom_components/wellbeing/sensor.py index 75f9620..9c95d41 100644 --- a/custom_components/wellbeing/sensor.py +++ b/custom_components/wellbeing/sensor.py @@ -1,5 +1,5 @@ """Sensor platform for Wellbeing.""" -from sys import platform + from typing import cast from homeassistant.components.sensor import SensorEntity, SensorStateClass @@ -13,14 +13,15 @@ async def async_setup_entry(hass, entry, async_add_devices): """Setup sensor platform.""" coordinator = hass.data[DOMAIN][entry.entry_id] - appliances = coordinator.data.get('appliances', None) + appliances = coordinator.data.get("appliances", None) if appliances is not None: for pnc_id, appliance in appliances.appliances.items(): async_add_devices( [ WellbeingSensor(coordinator, entry, pnc_id, entity.entity_type, entity.attr) - for entity in appliance.entities if entity.entity_type == Platform.SENSOR + for entity in appliance.entities + if entity.entity_type == Platform.SENSOR ] ) diff --git a/custom_components/wellbeing/switch.py b/custom_components/wellbeing/switch.py index 1d5b071..f55d32c 100644 --- a/custom_components/wellbeing/switch.py +++ b/custom_components/wellbeing/switch.py @@ -1,4 +1,5 @@ """Switch platform for Wellbeing.""" + from homeassistant.components.switch import SwitchEntity from .const import DOMAIN @@ -8,16 +9,20 @@ async def async_setup_entry(hass, entry, async_add_devices): """Setup switch platform.""" coordinator = hass.data[DOMAIN][entry.entry_id] - appliances = coordinator.data.get('appliances', None) + appliances = coordinator.data.get("appliances", None) capabilities = ["Ionizer", "UILight", "SafetyLock"] if appliances is not None: for pnc_id, appliance in appliances.appliances.items(): # Assuming that the appliance supports these features - async_add_devices([ - WellbeingSwitch(coordinator, entry, pnc_id, capability) - for capability in capabilities if appliance.has_capability(capability) - ]) + async_add_devices( + [ + WellbeingSwitch(coordinator, entry, pnc_id, capability) + for capability in capabilities + if appliance.has_capability(capability) + ] + ) + class WellbeingSwitch(WellbeingEntity, SwitchEntity): """Wellbeing Switch class.""" diff --git a/custom_components/wellbeing/translations/en.json b/custom_components/wellbeing/translations/en.json index fa04d5c..cc6695a 100644 --- a/custom_components/wellbeing/translations/en.json +++ b/custom_components/wellbeing/translations/en.json @@ -40,4 +40,4 @@ } } } -} \ No newline at end of file +} diff --git a/custom_components/wellbeing/translations/fr.json b/custom_components/wellbeing/translations/fr.json index bc966b8..cb4047f 100644 --- a/custom_components/wellbeing/translations/fr.json +++ b/custom_components/wellbeing/translations/fr.json @@ -34,4 +34,4 @@ } } } -} \ No newline at end of file +} diff --git a/custom_components/wellbeing/translations/nb.json b/custom_components/wellbeing/translations/nb.json index f87a4aa..eb48113 100644 --- a/custom_components/wellbeing/translations/nb.json +++ b/custom_components/wellbeing/translations/nb.json @@ -34,4 +34,4 @@ } } } -} \ No newline at end of file +} diff --git a/info.md b/info.md index 370d7cd..89eadbd 100644 --- a/info.md +++ b/info.md @@ -20,7 +20,7 @@ A custom component designed for [Home Assistant](https://www.home-assistant.io) - WA71-302GY - WA71-302DG - WA71-304GY - + - Electrolux Pure A9 Purifiers - PA91-406GY - PA91-606DG @@ -29,11 +29,11 @@ A custom component designed for [Home Assistant](https://www.home-assistant.io) - AEG AX5 Air Purifiers - AX51-304WT - + - AEG AX7 Air Purifiers - AX71-304GY - AX71-304DG - + - AEG AX9 Air Purifiers - AX91-404GY - AX91-404DG diff --git a/manage/update_manifest.py b/manage/update_manifest.py index c5d15c7..c35e182 100644 --- a/manage/update_manifest.py +++ b/manage/update_manifest.py @@ -1,4 +1,5 @@ """Update the manifest file.""" + import sys import json import os @@ -16,10 +17,8 @@ def update_manifest(): manifest["version"] = version - with open( - f"{os.getcwd()}/custom_components/wellbeing/manifest.json", "w" - ) as manifestfile: + with open(f"{os.getcwd()}/custom_components/wellbeing/manifest.json", "w") as manifestfile: manifestfile.write(json.dumps(manifest, indent=4, sort_keys=True)) -update_manifest() \ No newline at end of file +update_manifest() diff --git a/requirements.txt b/requirements.txt index dae17b2..6f4bc2f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -homeassistant==2024.8.0 aiohttp +homeassistant==2024.8.0 pyelectroluxgroup==0.2.1 From 852bbae705b8e366811fbfcf68ecdda1ab7f1522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Nenz=C3=A9n?= Date: Thu, 15 Aug 2024 22:13:46 +0000 Subject: [PATCH 2/2] Updates info.md --- info.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/info.md b/info.md index 89eadbd..a3306f4 100644 --- a/info.md +++ b/info.md @@ -46,7 +46,10 @@ A custom component designed for [Home Assistant](https://www.home-assistant.io) ## Installation 1. Click install. -1. In the HA UI go to "Configuration" -> "Integrations" click "+" and search for "Wellbeing". +2. In the HA UI go to "Configuration" -> "Integrations" click "+" and search for "Wellbeing". + +### Configuration +To use this integration you need to login in to https://developer.electrolux.one/ using your Electrolux account details and generate an API key, Access Token and Refresh Token that is needed in the configuration. {% endif %}