From 4ec7c8604e956f106c79fe87f4a4e2389e72111d Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 8 Feb 2024 13:53:26 +0000 Subject: [PATCH] ci: uses ruff for linting --- .github/dependabot.yml | 15 +++++ .github/workflows/linting.yaml | 6 +- .pre-commit-config.yaml | 43 ------------- .../dual_smart_thermostat/climate.py | 23 ++++--- .../dual_smart_thermostat/const.py | 2 +- .../dual_smart_thermostat/opening_manager.py | 36 ++++------- requirements.txt | 3 +- tests/conftest.py | 1 - tests/test_thermostat.py | 63 +++++-------------- 9 files changed, 56 insertions(+), 136 deletions(-) create mode 100644 .github/dependabot.yml delete mode 100644 .pre-commit-config.yaml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..04f2d40 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + ignore: + # Dependabot should not update Home Assistant as that should match the homeassistant key in hacs.json + - dependency-name: "homeassistant" \ No newline at end of file diff --git a/.github/workflows/linting.yaml b/.github/workflows/linting.yaml index 9288d8a..a6d110d 100644 --- a/.github/workflows/linting.yaml +++ b/.github/workflows/linting.yaml @@ -18,12 +18,12 @@ jobs: uses: actions/setup-python@v5 with: python-version: 3.11 + cache: "pip" - name: Install dependencies run: | pip install -r requirements.txt - # pip install --pre -r requirements-dev.txt - name: isort run: isort . --recursive --diff - - name: Black - run: black --check . + - name: Ruff + run: python3 -m ruff check . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index b4761f2..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,43 +0,0 @@ -repos: - - repo: https://github.com/asottile/pyupgrade - rev: v2.6.2 - hooks: - - id: pyupgrade - stages: [manual] - args: - - "--py38-plus" - - - repo: https://github.com/psf/black - rev: 20.8b1 - hooks: - - id: black - stages: [manual] - args: - - --safe - - --quiet - files: ^((custom_components|script|tests)/.+)?[^/]+\.py$ - - - repo: https://github.com/codespell-project/codespell - rev: v1.17.1 - hooks: - - id: codespell - stages: [manual] - args: - - --quiet-level=2 - - --ignore-words-list=hass,ba,fo - - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.1.0 - hooks: - - id: check-executables-have-shebangs - stages: [manual] - - id: check-json - stages: [manual] - - id: requirements-txt-fixer - stages: [manual] - - id: check-ast - stages: [manual] - - id: mixed-line-ending - stages: [manual] - args: - - --fix=lf \ No newline at end of file diff --git a/custom_components/dual_smart_thermostat/climate.py b/custom_components/dual_smart_thermostat/climate.py index 3b127f9..7e27146 100644 --- a/custom_components/dual_smart_thermostat/climate.py +++ b/custom_components/dual_smart_thermostat/climate.py @@ -77,7 +77,6 @@ CONF_TARGET_TEMP_LOW, CONF_TEMP_STEP, ATTR_TIMEOUT, - DEFAULT_MAX_FLOOR_TEMP, DEFAULT_NAME, DEFAULT_TOLERANCE, TIMED_OPENING_SCHEMA, @@ -673,7 +672,7 @@ def extra_state_attributes(self): return attributes async def async_set_hvac_mode(self, hvac_mode): - """Call climate mode based on current mode""" + """Call climate mode based on current mode.""" _LOGGER.debug("Setting hvac mode: %s", hvac_mode) match hvac_mode: case HVACMode.HEAT: @@ -1258,7 +1257,7 @@ def _set_presets_when_have_preset_mode(self, preset_mode: str): self._preset_mode = preset_mode def set_self_active(self): - """checks if active state needs to be set true""" + """Checks if active state needs to be set true.""" if ( not self._active and None not in (self._cur_temp, self._target_temp) @@ -1273,7 +1272,7 @@ def set_self_active(self): ) def _needs_control(self, time=None, force=False, *, dual=False, cool=False): - """checks if the controller needs to continue""" + """Checks if the controller needs to continue.""" if not self._active or self._hvac_mode == HVACMode.OFF: return False @@ -1295,12 +1294,12 @@ def _needs_cycle(self, dual=False, cool=False): return long_enough and long_enough_cooler def _is_too_cold(self, target_attr="_target_temp") -> bool: - """checks if the current temperature is below target""" + """Checks if the current temperature is below target.""" target_temp = getattr(self, target_attr) return target_temp >= self._cur_temp + self._cold_tolerance def _is_too_hot(self, target_attr="_target_temp") -> bool: - """checks if the current temperature is above target""" + """Checks if the current temperature is above target.""" target_temp = getattr(self, target_attr) return self._cur_temp >= target_temp + self._hot_tolerance @@ -1320,7 +1319,7 @@ def _is_cold_or_hot(self): return too_cold, too_hot, tolerance_device def _is_configured_for_heat_cool(self) -> bool: - """checks if the configuration is complete for heat/cool mode""" + """Checks if the configuration is complete for heat/cool mode.""" return self._heat_cool_mode or ( self._target_temp_high is not None and self._target_temp_low is not None ) @@ -1387,7 +1386,7 @@ def _set_default_temps_range_mode(self): self._target_temp_high += PRECISION_WHOLE def _set_support_flags(self) -> None: - """set the correct support flags based on configuration""" + """Set the correct support flags based on configuration.""" if self._hvac_mode == HVACMode.OFF: return @@ -1412,7 +1411,7 @@ def _set_support_flags(self) -> None: self._set_default_target_temps() def _ran_long_enough(self, cooler_entity=False): - """determines if a switch with the passed property name has run long enough""" + """Determines if a switch with the passed property name has run long enough.""" if cooler_entity and self.cooler_entity_id is not None: switch_entity_id = self.cooler_entity_id is_active = self._is_cooler_active @@ -1435,7 +1434,7 @@ def _ran_long_enough(self, cooler_entity=False): return long_enough def _first_stage_heating_timed_out(self, timeout=None): - """determines if the heater switch has been on for the timeout period""" + """Determines if the heater switch has been on for the timeout period.""" if timeout is None: timeout = self.secondary_heater_timeout @@ -1449,7 +1448,7 @@ def _first_stage_heating_timed_out(self, timeout=None): return timed_out def _has_secondary_heating_ran_today(self): - """determines if the secondary heater has been used today""" + """Determines if the secondary heater has been used today.""" if not self._is_secondary_heating_configured(): return False @@ -1462,7 +1461,7 @@ def _has_secondary_heating_ran_today(self): return False def _is_secondary_heating_configured(self): - """determines if the secondary heater is configured""" + """Determines if the secondary heater is configured.""" if self.secondary_heater_entity_id is None: return False diff --git a/custom_components/dual_smart_thermostat/const.py b/custom_components/dual_smart_thermostat/const.py index f2186c3..9828c2c 100644 --- a/custom_components/dual_smart_thermostat/const.py +++ b/custom_components/dual_smart_thermostat/const.py @@ -1,4 +1,4 @@ -"""const""" +"""const.""" from homeassistant.backports.enum import StrEnum from homeassistant.const import ATTR_ENTITY_ID import homeassistant.helpers.config_validation as cv diff --git a/custom_components/dual_smart_thermostat/opening_manager.py b/custom_components/dual_smart_thermostat/opening_manager.py index f1cacce..9d3001b 100644 --- a/custom_components/dual_smart_thermostat/opening_manager.py +++ b/custom_components/dual_smart_thermostat/opening_manager.py @@ -1,8 +1,7 @@ -"""Opening Manager for Dual Smart Thermostat""" +"""Opening Manager for Dual Smart Thermostat.""" import logging -from typing import List from homeassistant.const import ( ATTR_ENTITY_ID, @@ -26,7 +25,7 @@ class OpeningManager: - """Opening Manager for Dual Smart Thermostat""" + """Opening Manager for Dual Smart Thermostat.""" def __init__(self, hass: HomeAssistant, openings): self.hass = hass @@ -37,20 +36,15 @@ def __init__(self, hass: HomeAssistant, openings): @staticmethod def conform_openings_list(openings: list) -> list: - """Return a list of openings from a list of entities""" - return list( - map( - lambda entry: entry + """Return a list of openings from a list of entities.""" + return [entry if isinstance(entry, dict) - else {ATTR_ENTITY_ID: entry, ATTR_TIMEOUT: None}, - openings, - ) - ) + else {ATTR_ENTITY_ID: entry, ATTR_TIMEOUT: None} for entry in openings] @staticmethod - def conform_opnening_entities(openings: [TIMED_OPENING_SCHEMA]) -> List: - """Return a list of entities from a list of openings""" - return list(map(lambda entry: entry[ATTR_ENTITY_ID], openings)) + def conform_opnening_entities(openings: [TIMED_OPENING_SCHEMA]) -> list: + """Return a list of entities from a list of openings.""" + return [entry[ATTR_ENTITY_ID] for entry in openings] @property def any_opening_open(self) -> bool: @@ -79,9 +73,7 @@ def _is_opening_open(self, opening: TIMED_OPENING_SCHEMA): ): _is_open = True _LOGGER.debug( - "Have timeout mode for opening %s, is open: %s", - opening, - _is_open, + "Have timeout mode for opening %s, is open: %s", opening, _is_open, ) else: if self.hass.states.is_state( @@ -99,15 +91,9 @@ def _is_opening_timed_out(self, opening: TIMED_OPENING_SCHEMA) -> bool: opening_entity = opening[ATTR_ENTITY_ID] _is_open = False if condition.state( - self.hass, - opening_entity, - STATE_OPEN, - opening[ATTR_TIMEOUT], + self.hass, opening_entity, STATE_OPEN, opening[ATTR_TIMEOUT], ) or condition.state( - self.hass, - opening_entity, - STATE_ON, - opening[ATTR_TIMEOUT], + self.hass, opening_entity, STATE_ON, opening[ATTR_TIMEOUT], ): _is_open = True return _is_open diff --git a/requirements.txt b/requirements.txt index fcbb0f2..6d000c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,5 +27,4 @@ isort # voluptuous-serialize==2.5.0 # yarl==1.7.2 # pre-commit -# isort -black \ No newline at end of file +# isort \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 3d6f6c2..c044eb0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,7 +14,6 @@ # # See here for more info: https://docs.pytest.org/en/latest/fixture.html (note that # pytest includes fixtures OOB which you can use as defined on this page) -from unittest.mock import patch import pytest diff --git a/tests/test_thermostat.py b/tests/test_thermostat.py index d61ecec..182ae0f 100644 --- a/tests/test_thermostat.py +++ b/tests/test_thermostat.py @@ -2,7 +2,6 @@ import asyncio from datetime import timedelta import logging -import time from typing import Final from unittest.mock import patch @@ -26,9 +25,7 @@ from homeassistant.util.unit_system import METRIC_SYSTEM from custom_components.dual_smart_thermostat.const import * -from custom_components.dual_smart_thermostat import ( - DOMAIN as DUAL_SMART_THERMOSTAT, -) +from custom_components.dual_smart_thermostat import DOMAIN as DUAL_SMART_THERMOSTAT ENT_SWITCH = "switch.test" HEAT_ENTITY = "climate.test_heat" @@ -384,10 +381,7 @@ async def test_heater_mode_floor_temp(hass, setup_comp_1): @pytest.mark.parametrize( ["duration", "result_state"], - [ - (timedelta(seconds=10), STATE_ON), - (timedelta(seconds=30), STATE_OFF), - ], + [(timedelta(seconds=10), STATE_ON), (timedelta(seconds=30), STATE_OFF),], ) @pytest.mark.asyncio async def test_heater_mode_cycle(hass, duration, result_state, setup_comp_1): @@ -601,10 +595,7 @@ async def test_cooler_mode_tolerance(hass, setup_comp_1): @pytest.mark.parametrize( ["duration", "result_state"], - [ - (timedelta(seconds=10), STATE_ON), - (timedelta(seconds=30), STATE_OFF), - ], + [(timedelta(seconds=10), STATE_ON), (timedelta(seconds=30), STATE_OFF),], ) @pytest.mark.asyncio async def test_cooler_mode_cycle(hass, duration, result_state, setup_comp_1): @@ -667,9 +658,7 @@ async def test_cooler_mode_dual(hass, setup_comp_1): heater_switch = "input_boolean.heater" cooler_switch = "input_boolean.cooler" assert await async_setup_component( - hass, - input_boolean.DOMAIN, - {"input_boolean": {"heater": None, "cooler": None}}, + hass, input_boolean.DOMAIN, {"input_boolean": {"heater": None, "cooler": None}}, ) temp_input = "input_number.temp" @@ -717,10 +706,7 @@ async def test_cooler_mode_dual(hass, setup_comp_1): @pytest.mark.parametrize( ["duration", "result_state"], - [ - (timedelta(seconds=10), STATE_ON), - (timedelta(seconds=30), STATE_OFF), - ], + [(timedelta(seconds=10), STATE_ON), (timedelta(seconds=30), STATE_OFF),], ) @pytest.mark.asyncio async def test_cooler_mode_dual_cycle(hass, duration, result_state, setup_comp_1): @@ -729,9 +715,7 @@ async def test_cooler_mode_dual_cycle(hass, duration, result_state, setup_comp_1 heater_switch = "input_boolean.heater" cooler_switch = "input_boolean.cooler" assert await async_setup_component( - hass, - input_boolean.DOMAIN, - {"input_boolean": {"heater": None, "cooler": None}}, + hass, input_boolean.DOMAIN, {"input_boolean": {"heater": None, "cooler": None}}, ) temp_input = "input_number.temp" @@ -871,9 +855,7 @@ async def test_heater_cooler_mode(hass, setup_comp_1): heater_switch = "input_boolean.heater" cooler_switch = "input_boolean.cooler" assert await async_setup_component( - hass, - input_boolean.DOMAIN, - {"input_boolean": {"heater": None, "cooler": None}}, + hass, input_boolean.DOMAIN, {"input_boolean": {"heater": None, "cooler": None}}, ) temp_input = "input_number.temp" @@ -967,9 +949,7 @@ async def test_heater_cooler_mode_floor_temp(hass, setup_comp_1): heater_switch = "input_boolean.heater" cooler_switch = "input_boolean.cooler" assert await async_setup_component( - hass, - input_boolean.DOMAIN, - {"input_boolean": {"heater": None, "cooler": None}}, + hass, input_boolean.DOMAIN, {"input_boolean": {"heater": None, "cooler": None}}, ) temp_input = "input_number.temp" @@ -1065,10 +1045,7 @@ async def test_heater_cooler_mode_floor_temp(hass, setup_comp_1): @pytest.mark.parametrize( ["duration", "result_state"], - [ - (timedelta(seconds=10), STATE_ON), - (timedelta(seconds=30), STATE_OFF), - ], + [(timedelta(seconds=10), STATE_ON), (timedelta(seconds=30), STATE_OFF),], ) @pytest.mark.asyncio async def test_heater_cooler_mode_cycle_heat( @@ -1139,10 +1116,7 @@ async def test_heater_cooler_mode_cycle_heat( @pytest.mark.parametrize( ["duration", "result_state"], - [ - (timedelta(seconds=10), STATE_ON), - (timedelta(seconds=30), STATE_OFF), - ], + [(timedelta(seconds=10), STATE_ON), (timedelta(seconds=30), STATE_OFF),], ) @pytest.mark.asyncio async def test_heater_cooler_mode_cycle_cool( @@ -1220,9 +1194,7 @@ async def test_heater_cooler_switch_hvac_modes(hass, setup_comp_1): heater_switch = "input_boolean.heater" cooler_switch = "input_boolean.cooler" assert await async_setup_component( - hass, - input_boolean.DOMAIN, - {"input_boolean": {"heater": None, "cooler": None}}, + hass, input_boolean.DOMAIN, {"input_boolean": {"heater": None, "cooler": None}}, ) temp_input = "input_number.temp" @@ -1273,9 +1245,7 @@ async def test_heater_cooler_mode_tolerances(hass, setup_comp_1): heater_switch = "input_boolean.heater" cooler_switch = "input_boolean.cooler" assert await async_setup_component( - hass, - input_boolean.DOMAIN, - {"input_boolean": {"heater": None, "cooler": None}}, + hass, input_boolean.DOMAIN, {"input_boolean": {"heater": None, "cooler": None}}, ) temp_input = "input_number.temp" @@ -1409,17 +1379,12 @@ async def async_climate_set_temperature( async def async_set_hvac_mode( - hass, - entity_id="all", - hvac_mode=HVACMode.OFF, + hass, entity_id="all", hvac_mode=HVACMode.OFF, ): """Set new HVAC mode.""" kwargs = { key: value - for key, value in [ - (ATTR_ENTITY_ID, entity_id), - (ATTR_HVAC_MODE, hvac_mode), - ] + for key, value in [(ATTR_ENTITY_ID, entity_id), (ATTR_HVAC_MODE, hvac_mode),] if value is not None } _LOGGER.debug("%s start data=%s", SERVICE_SET_HVAC_MODE, kwargs)