diff --git a/.devcontainer.json b/.devcontainer.json index 9a91ab6..d57259c 100644 --- a/.devcontainer.json +++ b/.devcontainer.json @@ -1,6 +1,6 @@ { "name": "ludeeus/integration_blueprint", - "image": "mcr.microsoft.com/devcontainers/python:3.12", + "image": "mcr.microsoft.com/devcontainers/python:3.13", "postCreateCommand": "scripts/setup", "forwardPorts": [ 8123 diff --git a/.ruff.toml b/.ruff.toml index dc816da..d4faf23 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -1,6 +1,6 @@ # The contents of this file is based on https://github.com/home-assistant/core/blob/dev/pyproject.toml -target-version = "py312" +target-version = "py313" [lint] select = [ @@ -19,8 +19,10 @@ select = [ "B017", # pytest.raises(BaseException) should be considered evil "B018", # Found useless attribute access. Either assign it to a variable or remove it. "B023", # Function definition does not bind loop variable {name} + "B024", # `{name}` is an abstract base class, but it has no abstract methods or properties "B026", # Star-arg unpacking after a keyword argument is strongly discouraged "B032", # Possible unintentional type annotation (using :). Did you mean to assign (using =)? + "B035", # Dictionary comprehension uses static key "B904", # Use raise from to specify exception cause "B905", # zip() without an explicit strict= parameter "BLE", @@ -54,12 +56,27 @@ select = [ "RSE", # flake8-raise "RUF005", # Consider iterable unpacking instead of concatenation "RUF006", # Store a reference to the return value of asyncio.create_task + "RUF007", # Prefer itertools.pairwise() over zip() when iterating over successive pairs + "RUF008", # Do not use mutable default values for dataclass attributes "RUF010", # Use explicit conversion flag "RUF013", # PEP 484 prohibits implicit Optional + "RUF016", # Slice in indexed access to type {value_type} uses type {index_type} instead of an integer "RUF017", # Avoid quadratic list summation "RUF018", # Avoid assignment expressions in assert statements "RUF019", # Unnecessary key check before dictionary access - # "RUF100", # Unused `noqa` directive; temporarily every now and then to clean them up + "RUF020", # {never_like} | T is equivalent to T + "RUF021", # Parenthesize a and b expressions when chaining and and or together, to make the precedence clear + "RUF022", # Sort __all__ + "RUF023", # Sort __slots__ + "RUF024", # Do not pass mutable objects as values to dict.fromkeys + "RUF026", # default_factory is a positional-only argument to defaultdict + "RUF030", # print() call in assert statement is likely unintentional + "RUF032", # Decimal() called with float literal argument + "RUF033", # __post_init__ method with argument defaults + "RUF034", # Useless if-else condition + "RUF100", # Unused `noqa` directive + "RUF101", # noqa directives that use redirected rule codes + "RUF200", # Failed to parse pyproject.toml: {message} "S102", # Use of exec detected "S103", # bad-file-permissions "S108", # hardcoded-temp-file @@ -138,7 +155,6 @@ ignore = [ "Q", "COM812", "COM819", - "ISC001", # Disabled because ruff does not understand type of __all__ generated by a function "PLE0605" diff --git a/custom_components/pirateweather/config_flow.py b/custom_components/pirateweather/config_flow.py index d27b64a..d2acbb4 100644 --- a/custom_components/pirateweather/config_flow.py +++ b/custom_components/pirateweather/config_flow.py @@ -1,12 +1,19 @@ """Config flow for Pirate Weather.""" +from __future__ import annotations + import logging from datetime import timedelta import aiohttp import homeassistant.helpers.config_validation as cv import voluptuous as vol -from homeassistant import config_entries +from homeassistant.config_entries import ( + ConfigEntry, + ConfigFlow, + ConfigFlowResult, + OptionsFlow, +) from homeassistant.const import ( CONF_API_KEY, CONF_LATITUDE, @@ -44,18 +51,20 @@ CONF_HOURLY_FORECAST = "hourly_forecast" -class PirateWeatherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): +class PirateWeatherConfigFlow(ConfigFlow, domain=DOMAIN): """Config flow for PirateWeather.""" VERSION = CONFIG_FLOW_VERSION @staticmethod @callback - def async_get_options_flow(config_entry): + def async_get_options_flow( + config_entry: ConfigEntry, + ) -> PirateWeatherOptionsFlow: """Get the options flow for this handler.""" - return PirateWeatherOptionsFlow(config_entry) + return PirateWeatherOptionsFlow() - async def async_step_user(self, user_input=None): + async def async_step_user(self, user_input=None) -> ConfigFlowResult: """Handle a flow initialized by the user.""" errors = {} @@ -178,14 +187,10 @@ async def async_step_import(self, import_input=None): return await self.async_step_user(config) -class PirateWeatherOptionsFlow(config_entries.OptionsFlow): +class PirateWeatherOptionsFlow(OptionsFlow): """Handle options.""" - def __init__(self, config_entry): - """Initialize options flow.""" - self.config_entry = config_entry - - async def async_step_init(self, user_input=None): + async def async_step_init(self, user_input: dict | None = None) -> ConfigFlowResult: """Manage the options.""" if user_input is not None: # if self.config_entry.options: @@ -199,97 +204,100 @@ async def async_step_init(self, user_input=None): return self.async_show_form( step_id="init", - data_schema=vol.Schema( - { - vol.Optional( + data_schema=self._get_options_schema(), + ) + + def _get_options_schema(self): + return vol.Schema( + { + vol.Optional( + CONF_NAME, + default=self.config_entry.options.get( CONF_NAME, - default=self.config_entry.options.get( - CONF_NAME, - self.config_entry.data.get(CONF_NAME, DEFAULT_NAME), - ), - ): str, - vol.Optional( + self.config_entry.data.get(CONF_NAME, DEFAULT_NAME), + ), + ): str, + vol.Optional( + CONF_LATITUDE, + default=self.config_entry.options.get( CONF_LATITUDE, - default=self.config_entry.options.get( - CONF_LATITUDE, - self.config_entry.data.get( - CONF_LATITUDE, self.hass.config.latitude - ), + self.config_entry.data.get( + CONF_LATITUDE, self.hass.config.latitude ), - ): cv.latitude, - vol.Optional( + ), + ): cv.latitude, + vol.Optional( + CONF_LONGITUDE, + default=self.config_entry.options.get( CONF_LONGITUDE, - default=self.config_entry.options.get( - CONF_LONGITUDE, - self.config_entry.data.get( - CONF_LONGITUDE, self.hass.config.longitude - ), + self.config_entry.data.get( + CONF_LONGITUDE, self.hass.config.longitude ), - ): cv.longitude, - vol.Optional( + ), + ): cv.longitude, + vol.Optional( + CONF_SCAN_INTERVAL, + default=self.config_entry.options.get( CONF_SCAN_INTERVAL, - default=self.config_entry.options.get( - CONF_SCAN_INTERVAL, - self.config_entry.data.get( - CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL - ), + self.config_entry.data.get( + CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL ), - ): int, - vol.Required( + ), + ): int, + vol.Required( + PW_PLATFORM, + default=self.config_entry.options.get( PW_PLATFORM, - default=self.config_entry.options.get( - PW_PLATFORM, - self.config_entry.data.get(PW_PLATFORM, []), - ), - ): cv.multi_select(PW_PLATFORMS), - vol.Optional( + self.config_entry.data.get(PW_PLATFORM, []), + ), + ): cv.multi_select(PW_PLATFORMS), + vol.Optional( + CONF_LANGUAGE, + default=self.config_entry.options.get( CONF_LANGUAGE, - default=self.config_entry.options.get( - CONF_LANGUAGE, - self.config_entry.data.get(CONF_LANGUAGE, DEFAULT_LANGUAGE), - ), - ): vol.In(LANGUAGES), - vol.Optional( - CONF_FORECAST, - default=str( - self.config_entry.options.get( - CONF_FORECAST, - self.config_entry.data.get(CONF_FORECAST, ""), - ), + self.config_entry.data.get(CONF_LANGUAGE, DEFAULT_LANGUAGE), + ), + ): vol.In(LANGUAGES), + vol.Optional( + CONF_FORECAST, + default=str( + self.config_entry.options.get( + CONF_FORECAST, + self.config_entry.data.get(CONF_FORECAST, ""), ), - ): str, - vol.Optional( - CONF_HOURLY_FORECAST, - default=str( - self.config_entry.options.get( - CONF_HOURLY_FORECAST, - self.config_entry.data.get(CONF_HOURLY_FORECAST, ""), - ), + ), + ): str, + vol.Optional( + CONF_HOURLY_FORECAST, + default=str( + self.config_entry.options.get( + CONF_HOURLY_FORECAST, + self.config_entry.data.get(CONF_HOURLY_FORECAST, ""), ), - ): str, - vol.Optional( + ), + ): str, + vol.Optional( + CONF_MONITORED_CONDITIONS, + default=self.config_entry.options.get( CONF_MONITORED_CONDITIONS, - default=self.config_entry.options.get( - CONF_MONITORED_CONDITIONS, - self.config_entry.data.get(CONF_MONITORED_CONDITIONS, []), - ), - ): cv.multi_select(ALL_CONDITIONS), - vol.Optional( + self.config_entry.data.get(CONF_MONITORED_CONDITIONS, []), + ), + ): cv.multi_select(ALL_CONDITIONS), + vol.Optional( + CONF_UNITS, + default=self.config_entry.options.get( CONF_UNITS, - default=self.config_entry.options.get( - CONF_UNITS, - self.config_entry.data.get(CONF_UNITS, DEFAULT_UNITS), - ), - ): vol.In(["si", "us", "ca", "uk"]), - vol.Optional( + self.config_entry.data.get(CONF_UNITS, DEFAULT_UNITS), + ), + ): vol.In(["si", "us", "ca", "uk"]), + vol.Optional( + PW_ROUND, + default=self.config_entry.options.get( PW_ROUND, - default=self.config_entry.options.get( - PW_ROUND, - self.config_entry.data.get(PW_ROUND, "No"), - ), - ): vol.In(["Yes", "No"]), - } - ), + self.config_entry.options.get(PW_ROUND, "No"), + ), + ): vol.In(["Yes", "No"]), + } ) diff --git a/custom_components/pirateweather/manifest.json b/custom_components/pirateweather/manifest.json index 59f1124..2ec2935 100644 --- a/custom_components/pirateweather/manifest.json +++ b/custom_components/pirateweather/manifest.json @@ -9,5 +9,5 @@ "documentation": "https://github.com/alexander0042/pirate-weather-ha", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/alexander0042/pirate-weather-ha/issues", - "version": "1.6.3" -} + "version": "1.7" +} \ No newline at end of file diff --git a/hacs.json b/hacs.json index dff24bf..b2ddc45 100644 --- a/hacs.json +++ b/hacs.json @@ -1,5 +1,5 @@ { - "name": "Pirate Weather", + "name": "Pirate Weather", "render_readme": true, - "homeassistant": "2023.10.0" -} + "homeassistant": "2024.12.0" +} \ No newline at end of file