From 75cda779c0ca963b6b63d5cc8ffcbaaf9e1c763f Mon Sep 17 00:00:00 2001 From: Felipe Castillo Date: Sat, 6 Apr 2024 03:14:46 +0000 Subject: [PATCH 1/6] build: update home assistant dependencies --- .devcontainer.json | 16 ++-- .pre-commit-config.yaml | 10 +-- .ruff.toml | 61 ++++++++-------- custom_components/snowtire/manifest.json | 2 +- hacs.json | 2 +- pyproject.toml | 93 +++++++++--------------- requirements-dev.txt | 2 +- requirements-test.txt | 4 +- requirements.txt | 4 +- 9 files changed, 87 insertions(+), 107 deletions(-) diff --git a/.devcontainer.json b/.devcontainer.json index 9fda616..5cec644 100644 --- a/.devcontainer.json +++ b/.devcontainer.json @@ -1,6 +1,6 @@ { "name": "ludeeus/integration_blueprint", - "image": "mcr.microsoft.com/vscode/devcontainers/python:0-3.10-bullseye", + "image": "mcr.microsoft.com/devcontainers/python:1-3.12", "postCreateCommand": "scripts/setup", "forwardPorts": [ 8123 @@ -17,17 +17,21 @@ "ms-python.python", "github.vscode-pull-request-github", "ryanluker.vscode-coverage-gutters", - "ms-python.vscode-pylance" + "ms-python.vscode-pylance", + "ms-python.black-formatter", + "ms-python.pylint" ], "settings": { "files.eol": "\n", "editor.tabSize": 4, "python.pythonPath": "/usr/bin/python3", "python.analysis.autoSearchPaths": false, - "python.linting.pylintEnabled": true, - "python.linting.enabled": true, - "python.formatting.provider": "black", - "python.formatting.blackPath": "/usr/local/py-utils/bin/black", + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "black-formatter.path": [ + "/usr/local/py-utils/bin/black" + ], "editor.formatOnPaste": false, "editor.formatOnSave": true, "editor.formatOnType": true, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ea74596..97b27ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,20 +7,20 @@ repos: language: script files: ^(custom_components/.+/const\.py|requirements\.txt)$ - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.241 + rev: v0.3.5 hooks: - id: ruff args: - --fix files: ^(custom_components|bin|tests)/.+\.py$ - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.15.2 hooks: - id: pyupgrade - args: [ --py310-plus ] + args: [ --py312-plus ] stages: [manual] - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 24.3.0 hooks: - id: black args: @@ -37,7 +37,7 @@ repos: files: ^(custom_components|bin|tests)/.+\.py$ stages: [manual] - repo: https://github.com/PyCQA/bandit - rev: 1.7.4 + rev: 1.7.8 hooks: - id: bandit args: diff --git a/.ruff.toml b/.ruff.toml index 7a8331a..b1e25c1 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -1,41 +1,42 @@ # The contents of this file is based on https://github.com/home-assistant/core/blob/dev/pyproject.toml -target-version = "py310" +target-version = "py312" +[lint] select = [ - "B007", # Loop control variable {name} not used within loop body - "B014", # Exception handler with duplicate exception - "C", # complexity - "D", # docstrings - "E", # pycodestyle - "F", # pyflakes/autoflake - "ICN001", # import concentions; {name} should be imported as {asname} + "B007", # Loop control variable {name} not used within loop body + "B014", # Exception handler with duplicate exception + "C", # complexity + "D", # docstrings + "E", # pycodestyle + "F", # pyflakes/autoflake + "ICN001", # import concentions; {name} should be imported as {asname} "PGH004", # Use specific rule codes when using noqa "PLC0414", # Useless import alias. Import alias does not rename original package. - "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass - "SIM117", # Merge with-statements that use the same scope - "SIM118", # Use {key} in {dict} instead of {key} in {dict}.keys() - "SIM201", # Use {left} != {right} instead of not {left} == {right} - "SIM212", # Use {a} if {a} else {b} instead of {b} if not {a} else {a} - "SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'. - "SIM401", # Use get from dict with default instead of an if block - "T20", # flake8-print - "TRY004", # Prefer TypeError exception for invalid type - "RUF006", # Store a reference to the return value of asyncio.create_task - "UP", # pyupgrade - "W", # pycodestyle + "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass + "SIM117", # Merge with-statements that use the same scope + "SIM118", # Use {key} in {dict} instead of {key} in {dict}.keys() + "SIM201", # Use {left} != {right} instead of not {left} == {right} + "SIM212", # Use {a} if {a} else {b} instead of {b} if not {a} else {a} + "SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'. + "SIM401", # Use get from dict with default instead of an if block + "T20", # flake8-print + "TRY004", # Prefer TypeError exception for invalid type + "RUF006", # Store a reference to the return value of asyncio.create_task + "UP", # pyupgrade + "W", # pycodestyle ] ignore = [ - "D202", # No blank lines allowed after function docstring - "D203", # 1 blank line required before class docstring - "D213", # Multi-line docstring summary should start at the second line - "D404", # First word of the docstring should not be This - "D406", # Section name should end with a newline - "D407", # Section name underlining - "D411", # Missing blank line before section - "E501", # line too long - "E731", # do not assign a lambda expression, use a def + "D202", # No blank lines allowed after function docstring + "D203", # 1 blank line required before class docstring + "D213", # Multi-line docstring summary should start at the second line + "D404", # First word of the docstring should not be This + "D406", # Section name should end with a newline + "D407", # Section name underlining + "D411", # Missing blank line before section + "E501", # line too long + "E731", # do not assign a lambda expression, use a def ] [flake8-pytest-style] @@ -45,4 +46,4 @@ fixture-parentheses = false keep-runtime-typing = true [mccabe] -max-complexity = 25 \ No newline at end of file +max-complexity = 25 diff --git a/custom_components/snowtire/manifest.json b/custom_components/snowtire/manifest.json index 4e63664..5a4052f 100644 --- a/custom_components/snowtire/manifest.json +++ b/custom_components/snowtire/manifest.json @@ -13,7 +13,7 @@ "issue_tracker": "https://github.com/Limych/ha-snowtire/issues", "requirements": [ "colorlog==6.7.0", - "ruff==0.1.1" + "ruff>=0.3.5" ], "version": "1.4.7-alpha" } \ No newline at end of file diff --git a/hacs.json b/hacs.json index d0f8f0a..5c3e030 100644 --- a/hacs.json +++ b/hacs.json @@ -2,7 +2,7 @@ "name": "Snowtire Sensor", "filename": "snowtire.zip", "hide_default_branch": true, - "homeassistant": "2023.1.0", + "homeassistant": "2024.4.0", "render_readme": true, "zip_release": true } diff --git a/pyproject.toml b/pyproject.toml index cb14c1b..c0aac64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,8 @@ +[project] +requires-python = ">=3.12.0" + [tool.black] -target-version = ["py310"] +target-version = ["py312"] extend-exclude = "/generated/" [tool.isort] @@ -7,20 +10,13 @@ extend-exclude = "/generated/" profile = "black" # will group `import x` and `from x import` of the same module. force_sort_within_sections = true -known_first_party = [ - "homeassistant", - "tests", -] -forced_separate = [ - "tests", -] +known_first_party = ["homeassistant", "tests"] +forced_separate = ["tests"] combine_as_imports = true [tool.pylint.MAIN] py-version = "3.10" -ignore = [ - "tests", -] +ignore = ["tests"] # Use a conservative default here; 2 should speed up most setups and not hurt # any too bad. Override on command line as appropriate. jobs = 2 @@ -50,24 +46,11 @@ extension-pkg-allow-list = [ "orjson", "cv2", ] -fail-on = [ - "I", -] +fail-on = ["I"] [tool.pylint.BASIC] class-const-naming-style = "any" -good-names = [ - "_", - "ev", - "ex", - "fp", - "i", - "id", - "j", - "k", - "Run", - "ip", -] +good-names = ["_", "ev", "ex", "fp", "i", "id", "j", "k", "Run", "ip"] [tool.pylint."MESSAGES CONTROL"] # Reasons disabled: @@ -123,7 +106,7 @@ score = false [tool.pylint.TYPECHECK] ignored-classes = [ - "_CountingAttr", # for attrs + "_CountingAttr", # for attrs ] mixin-class-rgx = ".*[Mm]ix[Ii]n" @@ -147,50 +130,42 @@ max-line-length-suggestions = 72 # hass-component-root-import: Tests test non-public APIs # protected-access: Tests do often test internals a lot # redefined-outer-name: Tests reference fixtures in the test function -"/tests/"="hass-component-root-import,protected-access,redefined-outer-name" +"/tests/" = "hass-component-root-import,protected-access,redefined-outer-name" [tool.pytest.ini_options] -testpaths = [ - "tests", -] -norecursedirs = [ - ".git", - "testing_config", -] +testpaths = ["tests"] +norecursedirs = [".git", "testing_config"] log_format = "%(asctime)s.%(msecs)03d %(levelname)-8s %(threadName)s %(name)s:%(filename)s:%(lineno)s %(message)s" log_date_format = "%Y-%m-%d %H:%M:%S" asyncio_mode = "auto" -[tool.ruff] -target-version = "py310" - select = [ - "C", # complexity - "D", # docstrings - "E", # pycodestyle - "F", # pyflakes/autoflake + "C", # complexity + "D", # docstrings + "E", # pycodestyle + "F", # pyflakes/autoflake "PGH004", # Use specific rule codes when using noqa "PLC0414", # Useless import alias. Import alias does not rename original package. - "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass - "SIM117", # Merge with-statements that use the same scope - "SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'. - "SIM401", # Use get from dict with default instead of an if block - "T20", # flake8-print - "TRY004", # Prefer TypeError exception for invalid type - "UP", # pyupgrade - "W", # pycodestyle + "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass + "SIM117", # Merge with-statements that use the same scope + "SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'. + "SIM401", # Use get from dict with default instead of an if block + "T20", # flake8-print + "TRY004", # Prefer TypeError exception for invalid type + "UP", # pyupgrade + "W", # pycodestyle ] ignore = [ - "D202", # No blank lines allowed after function docstring - "D203", # 1 blank line required before class docstring - "D213", # Multi-line docstring summary should start at the second line - "D404", # First word of the docstring should not be This - "D406", # Section name should end with a newline - "D407", # Section name underlining - "D411", # Missing blank line before section - "E501", # line too long - "E731", # do not assign a lambda expression, use a def + "D202", # No blank lines allowed after function docstring + "D203", # 1 blank line required before class docstring + "D213", # Multi-line docstring summary should start at the second line + "D404", # First word of the docstring should not be This + "D406", # Section name should end with a newline + "D407", # Section name underlining + "D411", # Missing blank line before section + "E501", # line too long + "E731", # do not assign a lambda expression, use a def ] [tool.ruff.flake8-pytest-style] diff --git a/requirements-dev.txt b/requirements-dev.txt index 1e05453..551c0b0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ -r requirements-test.txt -black==23.9.1 +black>=24.3.0 packaging==23.2 pre-commit~=3.5 PyGithub~=2.1 diff --git a/requirements-test.txt b/requirements-test.txt index 08cb07a..fb87e1d 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,9 +3,9 @@ asynctest~=0.13 flake8~=6.1 flake8-docstrings~=1.7 mypy==1.5.1 -pylint~=3.0 +pylint~=3.1.0 pylint-strict-informational==0.1 pytest>=7.2 pytest-cov>=3.0 -pytest-homeassistant-custom-component>=0.12 +pytest-homeassistant-custom-component>=0.13.111 tzdata diff --git a/requirements.txt b/requirements.txt index 580b053..36de10b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ colorlog==6.7.0 -homeassistant>=2023.1.0 +homeassistant>=2024.4.0 pip>=21.0,<23.3 -ruff==0.1.1 +ruff>=0.3.5 From 39e0a748f5d1bdcab303f64b0396684839790ba3 Mon Sep 17 00:00:00 2001 From: Felipe Castillo Date: Sat, 6 Apr 2024 03:16:33 +0000 Subject: [PATCH 2/6] fix: use service call to get weather forecast --- custom_components/snowtire/binary_sensor.py | 27 +++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/custom_components/snowtire/binary_sensor.py b/custom_components/snowtire/binary_sensor.py index 125799f..9a805c4 100644 --- a/custom_components/snowtire/binary_sensor.py +++ b/custom_components/snowtire/binary_sensor.py @@ -11,20 +11,21 @@ from collections.abc import Callable from datetime import datetime import logging -from typing import Optional import voluptuous as vol from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.components.weather import ( - ATTR_FORECAST, ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME, ATTR_WEATHER_TEMPERATURE, DOMAIN as WEATHER, + SERVICE_GET_FORECASTS, + WeatherEntityFeature, ) from homeassistant.const import ( + ATTR_SUPPORTED_FEATURES, CONF_NAME, CONF_UNIQUE_ID, EVENT_HOMEASSISTANT_START, @@ -89,7 +90,7 @@ class SnowtireBinarySensor(BinarySensorEntity): def __init__( self, - unique_id: Optional[str], + unique_id: str | None, friendly_name: str, weather_entity: str, days: int, @@ -140,8 +141,8 @@ def icon(self): @staticmethod def _temp2c( - temperature: Optional[float], temperature_unit: Optional[str] - ) -> Optional[float]: + temperature: float | None, temperature_unit: str | None + ) -> float | None: """Convert weather temperature to Celsius degree.""" if temperature is not None and temperature_unit != TEMP_CELSIUS: temperature = TemperatureConverter.convert( @@ -161,9 +162,21 @@ async def async_update( f"Unable to find an entity called {self._weather_entity}" ) + if ( + wdata.attributes.get(ATTR_SUPPORTED_FEATURES) + is not WeatherEntityFeature.FORECAST_DAILY + ): + raise HomeAssistantError("Weather entity doesn't support 'daily' forecast") + tmpu = self.hass.config.units.temperature_unit temp = wdata.attributes.get(ATTR_WEATHER_TEMPERATURE) - forecast = wdata.attributes.get(ATTR_FORECAST) + forecast = await self.hass.services.async_call( + WEATHER, + SERVICE_GET_FORECASTS, + {"type": "daily", "entity_id": [self._weather_entity]}, + blocking=True, + return_response=True, + ) if forecast is None: raise HomeAssistantError( @@ -180,7 +193,7 @@ async def async_update( _LOGGER.debug("Inspect weather forecast from %s till %s", cur_date, stop_date) temp = [self._temp2c(temp, tmpu)] - for fcast in forecast: + for fcast in forecast[self._weather_entity]["forecast"]: fc_date = fcast.get(ATTR_FORECAST_TIME) if isinstance(fc_date, int): fc_date = dt_util.as_local( From d65c7bbc6f87885f56f538867fbe975d78c2e89f Mon Sep 17 00:00:00 2001 From: Felipe Castillo Date: Thu, 18 Apr 2024 12:30:29 -0400 Subject: [PATCH 3/6] ci: use python 3.12 --- .github/workflows/lint.yml | 4 ++-- .github/workflows/py-dead-code.yml | 6 +++--- .github/workflows/py-test.yml | 12 ++++++------ .github/workflows/release.yml | 6 +++--- pyproject.toml | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 88fc142..9308388 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,9 +17,9 @@ jobs: uses: "actions/checkout@v4" - name: "Set up Python" - uses: actions/setup-python@v4.6.1 + uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version-file: 'pyproject.toml' cache: "pip" - name: "Install requirements" diff --git a/.github/workflows/py-dead-code.yml b/.github/workflows/py-dead-code.yml index 1453ec6..5e3744b 100644 --- a/.github/workflows/py-dead-code.yml +++ b/.github/workflows/py-dead-code.yml @@ -15,12 +15,12 @@ jobs: echo "package=$(ls -F | grep \/$ | grep -v "scripts\|examples\|tests\|config" | sed -n "s/\///g;1p")" >> $GITHUB_ENV - name: "Set up Python" - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version-file: 'pyproject.toml' - name: "Cache pip" - uses: actions/cache@v3 + uses: actions/cache@v4 with: # This path is specific to Ubuntu path: ~/.cache/pip diff --git a/.github/workflows/py-test.yml b/.github/workflows/py-test.yml index f6c3288..0950eb0 100644 --- a/.github/workflows/py-test.yml +++ b/.github/workflows/py-test.yml @@ -23,12 +23,12 @@ jobs: echo "package=$(ls -F | grep \/$ | grep -v "bin\|examples\|tests" | sed -n "s/\///g;1p")" >> $GITHUB_ENV - name: "Set up Python" - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version-file: 'pyproject.toml' - name: "Cache pip" - uses: actions/cache@v3 + uses: actions/cache@v4 with: # This path is specific to Ubuntu path: ~/.cache/pip @@ -66,18 +66,18 @@ jobs: strategy: max-parallel: 3 matrix: - python-version: ['3.9', '3.10', '3.11'] + python-version: ['3.12'] steps: - name: "Checkout code" uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: "Cache pip" - uses: actions/cache@v3 + uses: actions/cache@v4 with: # This path is specific to Ubuntu path: ~/.cache/pip diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ebdada0..6a18104 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,13 +54,13 @@ jobs: - name: "Set up Python" if: env.release_version != '' && success() - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version-file: 'pyproject.toml' - name: "Cache pip" if: env.release_version != '' && success() - uses: actions/cache@v3 + uses: actions/cache@v4 with: # This path is specific to Ubuntu path: ~/.cache/pip diff --git a/pyproject.toml b/pyproject.toml index c0aac64..4229e51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -requires-python = ">=3.12.0" +requires-python = ">=3.12" [tool.black] target-version = ["py312"] From 70cbbc78919923f1f7d4ed3a9a82416ab27ac486 Mon Sep 17 00:00:00 2001 From: Felipe Castillo Date: Thu, 18 Apr 2024 20:18:15 +0000 Subject: [PATCH 4/6] build: fix dependencies --- .github/workflows/py-test.yml | 8 ++--- .ruff.toml | 6 ++-- hacs.json | 2 +- requirements-test.txt | 2 +- requirements.txt | 4 +-- tests/test_binary_sensor.py | 65 +++++++++++++++++++---------------- 6 files changed, 44 insertions(+), 43 deletions(-) diff --git a/.github/workflows/py-test.yml b/.github/workflows/py-test.yml index 0950eb0..d3fabf7 100644 --- a/.github/workflows/py-test.yml +++ b/.github/workflows/py-test.yml @@ -63,18 +63,14 @@ jobs: name: "Test package" needs: lint runs-on: ubuntu-latest - strategy: - max-parallel: 3 - matrix: - python-version: ['3.12'] steps: - name: "Checkout code" uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version }} + python-version-file: 'pyproject.toml' - name: "Cache pip" uses: actions/cache@v4 diff --git a/.ruff.toml b/.ruff.toml index b1e25c1..879a9d9 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -39,11 +39,11 @@ ignore = [ "E731", # do not assign a lambda expression, use a def ] -[flake8-pytest-style] +[lint.flake8-pytest-style] fixture-parentheses = false -[pyupgrade] +[lint.pyupgrade] keep-runtime-typing = true -[mccabe] +[lint.mccabe] max-complexity = 25 diff --git a/hacs.json b/hacs.json index 5c3e030..3416dad 100644 --- a/hacs.json +++ b/hacs.json @@ -2,7 +2,7 @@ "name": "Snowtire Sensor", "filename": "snowtire.zip", "hide_default_branch": true, - "homeassistant": "2024.4.0", + "homeassistant": "2024.4", "render_readme": true, "zip_release": true } diff --git a/requirements-test.txt b/requirements-test.txt index fb87e1d..e800335 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -7,5 +7,5 @@ pylint~=3.1.0 pylint-strict-informational==0.1 pytest>=7.2 pytest-cov>=3.0 -pytest-homeassistant-custom-component>=0.13.111 +pytest-homeassistant-custom-component>=0.13 tzdata diff --git a/requirements.txt b/requirements.txt index 36de10b..2121e1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ colorlog==6.7.0 -homeassistant>=2024.4.0 -pip>=21.0,<23.3 +homeassistant>=2024.4 +pip>=24.0 ruff>=0.3.5 diff --git a/tests/test_binary_sensor.py b/tests/test_binary_sensor.py index 8ba01a6..f3c9919 100644 --- a/tests/test_binary_sensor.py +++ b/tests/test_binary_sensor.py @@ -1,4 +1,5 @@ """The test for the snowtire binary sensor platform.""" + # pylint: disable=redefined-outer-name from typing import Final from unittest.mock import MagicMock @@ -17,16 +18,19 @@ ICON_WINTER, ) from homeassistant.components.weather import ( - ATTR_FORECAST, - ATTR_FORECAST_TEMP, - ATTR_FORECAST_TEMP_LOW, - ATTR_FORECAST_TIME, ATTR_WEATHER_TEMPERATURE, + WeatherEntityFeature, +) +from homeassistant.const import ( + ATTR_SUPPORTED_FEATURES, + CONF_PLATFORM, + TEMP_CELSIUS, + TEMP_FAHRENHEIT, ) -from homeassistant.const import CONF_PLATFORM, TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.core import HomeAssistant, State from homeassistant.exceptions import HomeAssistantError -from homeassistant.util import dt as dt_util + +# from homeassistant.util import dt as dt_util MOCK_UNIQUE_ID: Final = "test_id" MOCK_NAME: Final = "test_name" @@ -93,6 +97,7 @@ async def test__temp2c(temp1, temp2): assert SnowtireBinarySensor._temp2c(None, TEMP_CELSIUS) is None +@pytest.mark.skip(reason="Can't mock a weather integration") async def test_async_update(hass: HomeAssistant, default_sensor): """Test sensor update.""" hass.states._states[MOCK_WEATHER_ENTITY] = State(MOCK_WEATHER_ENTITY, None) @@ -105,34 +110,34 @@ async def test_async_update(hass: HomeAssistant, default_sensor): with raises(HomeAssistantError): await default_sensor.async_update() - today = dt_util.start_of_local_day() - today_ts = int(today.timestamp() * 1000) - day = days = 86400000 - - forecast = [ - { - ATTR_FORECAST_TIME: today_ts - day, - }, - { - ATTR_FORECAST_TIME: today, - ATTR_FORECAST_TEMP: 9, - }, - { - ATTR_FORECAST_TIME: today_ts + day, - ATTR_FORECAST_TEMP_LOW: 1, - ATTR_FORECAST_TEMP: 8, - }, - { - ATTR_FORECAST_TIME: today_ts + (MOCK_DAYS + 1) * days, - }, - ] + # today = dt_util.start_of_local_day() + # today_ts = int(today.timestamp() * 1000) + # day = days = 86400000 + + # forecast = [ + # { + # ATTR_FORECAST_TIME: today_ts - day, + # }, + # { + # ATTR_FORECAST_TIME: today, + # ATTR_FORECAST_TEMP: 9, + # }, + # { + # ATTR_FORECAST_TIME: today_ts + day, + # ATTR_FORECAST_TEMP_LOW: 1, + # ATTR_FORECAST_TEMP: 8, + # }, + # { + # ATTR_FORECAST_TIME: today_ts + (MOCK_DAYS + 1) * days, + # }, + # ] hass.states.async_set( MOCK_WEATHER_ENTITY, "State", attributes={ ATTR_WEATHER_TEMPERATURE: -1, - ATTR_FORECAST: forecast, + ATTR_SUPPORTED_FEATURES: WeatherEntityFeature.FORECAST_DAILY, }, ) @@ -146,7 +151,7 @@ async def test_async_update(hass: HomeAssistant, default_sensor): "State", attributes={ ATTR_WEATHER_TEMPERATURE: 9.9, - ATTR_FORECAST: forecast, + ATTR_SUPPORTED_FEATURES: WeatherEntityFeature.FORECAST_DAILY, }, ) @@ -160,7 +165,7 @@ async def test_async_update(hass: HomeAssistant, default_sensor): "State", attributes={ ATTR_WEATHER_TEMPERATURE: 10, - ATTR_FORECAST: forecast, + ATTR_SUPPORTED_FEATURES: WeatherEntityFeature.FORECAST_DAILY, }, ) From 9894f1c1834e3ac7bde8d5ddeee8142016bc9e5a Mon Sep 17 00:00:00 2001 From: Felipe Castillo Date: Wed, 24 Apr 2024 18:27:06 +0000 Subject: [PATCH 5/6] test: fix unit test --- pylintrc | 2 +- tests/bandit.yaml | 1 - tests/test_binary_sensor.py | 69 +++++++++++++++++++++---------------- 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/pylintrc b/pylintrc index 850e50d..7b6263c 100644 --- a/pylintrc +++ b/pylintrc @@ -3,7 +3,7 @@ ignore=tests # Use a conservative default here; 2 should speed up most setups and not hurt # any too bad. Override on command line as appropriate. jobs=2 -load-plugins=pylint_strict_informational +fail-on=I persistent=no extension-pkg-whitelist=ciso8601 diff --git a/tests/bandit.yaml b/tests/bandit.yaml index ebd284e..dcacabd 100644 --- a/tests/bandit.yaml +++ b/tests/bandit.yaml @@ -12,6 +12,5 @@ tests: - B318 - B319 - B320 - - B325 - B602 - B604 diff --git a/tests/test_binary_sensor.py b/tests/test_binary_sensor.py index f3c9919..13e9603 100644 --- a/tests/test_binary_sensor.py +++ b/tests/test_binary_sensor.py @@ -6,6 +6,7 @@ import pytest from pytest import raises +from pytest_homeassistant_custom_component.common import async_mock_service from custom_components.snowtire.binary_sensor import ( SnowtireBinarySensor, @@ -18,19 +19,21 @@ ICON_WINTER, ) from homeassistant.components.weather import ( + ATTR_FORECAST_TEMP, + ATTR_FORECAST_TEMP_LOW, + ATTR_FORECAST_TIME, ATTR_WEATHER_TEMPERATURE, + SERVICE_GET_FORECASTS, WeatherEntityFeature, ) from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, CONF_PLATFORM, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, State from homeassistant.exceptions import HomeAssistantError - -# from homeassistant.util import dt as dt_util +from homeassistant.util import dt as dt_util MOCK_UNIQUE_ID: Final = "test_id" MOCK_NAME: Final = "test_name" @@ -92,12 +95,14 @@ async def test_async_added_to_hass(default_sensor): ) async def test__temp2c(temp1, temp2): """Test temperature conversions.""" - assert SnowtireBinarySensor._temp2c(temp1, TEMP_CELSIUS) == temp1 - assert round(SnowtireBinarySensor._temp2c(temp1, TEMP_FAHRENHEIT), 2) == temp2 - assert SnowtireBinarySensor._temp2c(None, TEMP_CELSIUS) is None + assert SnowtireBinarySensor._temp2c(temp1, UnitOfTemperature.CELSIUS) == temp1 + assert ( + round(SnowtireBinarySensor._temp2c(temp1, UnitOfTemperature.FAHRENHEIT), 2) + == temp2 + ) + assert SnowtireBinarySensor._temp2c(None, UnitOfTemperature.CELSIUS) is None -@pytest.mark.skip(reason="Can't mock a weather integration") async def test_async_update(hass: HomeAssistant, default_sensor): """Test sensor update.""" hass.states._states[MOCK_WEATHER_ENTITY] = State(MOCK_WEATHER_ENTITY, None) @@ -110,27 +115,33 @@ async def test_async_update(hass: HomeAssistant, default_sensor): with raises(HomeAssistantError): await default_sensor.async_update() - # today = dt_util.start_of_local_day() - # today_ts = int(today.timestamp() * 1000) - # day = days = 86400000 - - # forecast = [ - # { - # ATTR_FORECAST_TIME: today_ts - day, - # }, - # { - # ATTR_FORECAST_TIME: today, - # ATTR_FORECAST_TEMP: 9, - # }, - # { - # ATTR_FORECAST_TIME: today_ts + day, - # ATTR_FORECAST_TEMP_LOW: 1, - # ATTR_FORECAST_TEMP: 8, - # }, - # { - # ATTR_FORECAST_TIME: today_ts + (MOCK_DAYS + 1) * days, - # }, - # ] + today = dt_util.start_of_local_day() + today_ts = int(today.timestamp() * 1000) + day = days = 86400000 + + forecast = { + MOCK_WEATHER_ENTITY: { + "forecast": [ + { + ATTR_FORECAST_TIME: today_ts - day, + }, + { + ATTR_FORECAST_TIME: today, + ATTR_FORECAST_TEMP: 9, + }, + { + ATTR_FORECAST_TIME: today_ts + day, + ATTR_FORECAST_TEMP_LOW: 1, + ATTR_FORECAST_TEMP: 8, + }, + { + ATTR_FORECAST_TIME: today_ts + (MOCK_DAYS + 1) * days, + }, + ] + } + } + + async_mock_service(hass, CONF_WEATHER, SERVICE_GET_FORECASTS, response=forecast) hass.states.async_set( MOCK_WEATHER_ENTITY, From add682ffa8708317128cea5a2c6e4929afa52b50 Mon Sep 17 00:00:00 2001 From: Felipe Castillo Date: Wed, 24 Apr 2024 19:04:32 +0000 Subject: [PATCH 6/6] build: remove unused requirements --- custom_components/snowtire/manifest.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/custom_components/snowtire/manifest.json b/custom_components/snowtire/manifest.json index 5a4052f..1a91a55 100644 --- a/custom_components/snowtire/manifest.json +++ b/custom_components/snowtire/manifest.json @@ -11,9 +11,6 @@ "documentation": "https://github.com/Limych/ha-snowtire", "iot_class": "calculated", "issue_tracker": "https://github.com/Limych/ha-snowtire/issues", - "requirements": [ - "colorlog==6.7.0", - "ruff>=0.3.5" - ], + "requirements": [], "version": "1.4.7-alpha" -} \ No newline at end of file +}