diff --git a/.github/workflows/hassfest.yaml b/.github/workflows/hassfest.yaml index b9be3a52..e76ecadc 100644 --- a/.github/workflows/hassfest.yaml +++ b/.github/workflows/hassfest.yaml @@ -10,5 +10,5 @@ jobs: hassfest_custom: runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v6.0.2" - - uses: home-assistant/actions/hassfest@master + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + - uses: home-assistant/actions/hassfest@f4ca6f671bd429efb108c0f2fa0ae8af0215986c diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ec72a1d1..771721d7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,7 @@ name: Test against HA-core env: - CACHE_VERSION: 2 + CACHE_VERSION: 5 DEFAULT_PYTHON: "3.14" VENV: venv @@ -34,15 +34,15 @@ jobs: python-version: ${{ steps.python.outputs.python-version }} steps: - name: Check out committed code - uses: actions/checkout@v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v6 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Fetch HA pyproject id: core-version - run: wget -O ha_pyproject.toml "https://raw.githubusercontent.com/home-assistant/core/refs/heads/dev/pyproject.toml" + run: curl --fail --location --proto '=https' -o ha_pyproject.toml "https://raw.githubusercontent.com/home-assistant/core/refs/heads/dev/pyproject.toml" - name: Compute cache key id: set-key run: echo "cache-key=${{ runner.os }}--${{ env.CACHE_VERSION }}-${{ hashFiles('pyproject.toml', 'requirements_test.txt', '.pre-commit-config.yaml', 'ha_pyproject.toml') }}" >> "$GITHUB_OUTPUT" @@ -55,7 +55,7 @@ jobs: steps: - name: Prepare code checkout and python/prek/pre-commit setup id: cache-reuse - uses: plugwise/gh-actions/prepare-python-and-code@v2 + uses: plugwise/gh-actions/prepare-python-and-code@6b33ca47b5b47b3324e081221da2baa499905914 with: cache-key: ${{ needs.cache.outputs.cache-key }} fail-on-miss: false # First time create cache (if not already exists) @@ -63,7 +63,6 @@ jobs: venv-dir: ${{ env.VENV }} clone-core: "true" - # Prepare default python version environment ha-core-release: runs-on: ubuntu-latest name: Setup for HA-core (release/master) @@ -72,15 +71,15 @@ jobs: - prepare steps: - name: Check out committed code - uses: actions/checkout@v6 - - name: Restore cached environment + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + - name: Prepare code checkout and python/prek/pre-commit setup id: cache-reuse - uses: plugwise/gh-actions/restore-venv@v2 + uses: plugwise/gh-actions/prepare-python-and-code@6b33ca47b5b47b3324e081221da2baa499905914 with: cache-key: ${{ needs.cache.outputs.cache-key }} + fail-on-miss: false # First time create cache (if not already exists) python-version: ${{ needs.cache.outputs.python-version }} venv-dir: ${{ env.VENV }} - precommit-home: ${{ env.PRE_COMMIT_HOME }} - name: Test through HA-core (master/release) id: ha_core_release_tests run: | @@ -94,6 +93,6 @@ jobs: name: Shellcheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e63e7355..fc9c520c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ default_language_version: repos: # Run manually in CI skipping the branch checks - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.14 + rev: v0.16.6 hooks: - id: ruff name: "Ruff-ing code" @@ -27,7 +27,7 @@ repos: name: "Checking pyupgrade" args: [--py39-plus] - repo: https://github.com/codespell-project/codespell - rev: v2.4.2 + rev: v2.4.3 hooks: - id: codespell name: "Verifying/updating code for spelling issues" @@ -52,7 +52,7 @@ repos: - id: yamllint name: "Linting yaml" - repo: https://github.com/biomejs/pre-commit - rev: v2.4.15 + rev: v2.5.12 hooks: - id: biome-lint additional_dependencies: ["@biomejs/biome@2.0.4"] @@ -79,7 +79,7 @@ repos: language: script pass_filenames: false - repo: https://github.com/jackdewinter/pymarkdown - rev: v0.9.37 + rev: v0.9.39 hooks: - id: pymarkdown name: "MarkDown Lint" diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f8a8aa3..d0226917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ # Changelog -## Ongoing +## v0.59.3 +- General environment and code updates, improve (test)files structure - Bump python to 3.14 +- Link to plugwise_usb [v0.47.8](https://github.com/plugwise/python-plugwise-usb/releases/tag/v0.47.8), rework to using the HA USB platform ## v0.59.2 diff --git a/custom_components/plugwise_usb/binary_sensor.py b/custom_components/plugwise_usb/binary_sensor.py index 0650447b..47d78dbc 100644 --- a/custom_components/plugwise_usb/binary_sensor.py +++ b/custom_components/plugwise_usb/binary_sensor.py @@ -1,7 +1,5 @@ """Plugwise USB Binary Sensor component for Home Assistant.""" -from __future__ import annotations - from dataclasses import dataclass from datetime import timedelta import logging diff --git a/custom_components/plugwise_usb/button.py b/custom_components/plugwise_usb/button.py index 4151e205..616f52c2 100644 --- a/custom_components/plugwise_usb/button.py +++ b/custom_components/plugwise_usb/button.py @@ -1,7 +1,5 @@ """Plugwise USB Button component for HomeAssistant.""" -from __future__ import annotations - from dataclasses import dataclass from datetime import timedelta import logging diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index a1647b90..ad0cc527 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -1,7 +1,5 @@ """Config flow for Plugwise USB integration.""" -from __future__ import annotations - from typing import Any from plugwise_usb import Stick @@ -13,7 +11,6 @@ from homeassistant.const import CONF_BASE from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult -import serial.tools.list_ports from .const import CONF_MANUAL_PATH, CONF_USB_PATH, DOMAIN, MANUAL_PATH @@ -72,11 +69,15 @@ async def async_step_user( ) -> FlowResult: """Step when user initializes a integration.""" errors: dict[str, str] = {} - ports = await self.hass.async_add_executor_job(serial.tools.list_ports.comports) + ports = [ + port + for port in await usb.async_scan_serial_ports(self.hass) + if isinstance(port, usb.USBDevice) + ] list_of_ports = [ - f"{p}, s/n: {p.serial_number or 'n/a'}" - + (f" - {p.manufacturer}" if p.manufacturer else "") - for p in ports + f"{port.device}, s/n: {port.serial_number or 'n/a'}" + + (f" - {port.manufacturer}" if port.manufacturer else "") + for port in ports ] list_of_ports.append(CONF_MANUAL_PATH) @@ -87,9 +88,7 @@ async def async_step_user( return await self.async_step_manual_path() port = ports[list_of_ports.index(user_selection)] - device_path = await self.hass.async_add_executor_job( - usb.get_serial_by_id, port.device - ) + device_path = port.device errors, mac_stick = await validate_usb_connection(self.hass, device_path) if not errors: await self.async_set_unique_id( diff --git a/custom_components/plugwise_usb/coordinator.py b/custom_components/plugwise_usb/coordinator.py index 06010071..8f98bc61 100644 --- a/custom_components/plugwise_usb/coordinator.py +++ b/custom_components/plugwise_usb/coordinator.py @@ -54,6 +54,7 @@ def __init__( super().__init__( hass, _LOGGER, + config_entry=config_entry, name=node.node_info.name, update_interval=timedelta(seconds=15), update_method=self.async_node_update, diff --git a/custom_components/plugwise_usb/entity.py b/custom_components/plugwise_usb/entity.py index 045da4bb..3d5ae77d 100644 --- a/custom_components/plugwise_usb/entity.py +++ b/custom_components/plugwise_usb/entity.py @@ -1,13 +1,15 @@ """Plugwise USB stick base entity.""" -from __future__ import annotations - from dataclasses import dataclass import logging from plugwise_usb.api import NodeFeature, NodeInfo -from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE, DeviceInfo +from homeassistant.helpers.device_registry import ( + CONNECTION_ZIGBEE, + DeviceInfo, + async_get_device_id_by_identifier, +) from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -38,9 +40,9 @@ def __init__( super().__init__(node_duc, context=entity_description.node_feature) self.node_duc = node_duc self.entity_description = entity_description + self.entry = node_duc.config_entry self._node_info: NodeInfo = node_duc.node.node_info self._attr_unique_id = f"{self._node_info.mac}-{entity_description.key}" - self._via_device = (DOMAIN, str(node_duc.api_stick.mac_stick)) @property def available(self) -> bool: @@ -61,7 +63,11 @@ def device_info(self) -> DeviceInfo: model_id=self._node_info.model_type, name=str(self._node_info.name), sw_version=str(self._node_info.firmware), - via_device=self._via_device, + via_device_id=async_get_device_id_by_identifier( + self.node_duc.hass, + (DOMAIN, str(self.node_duc.api_stick.mac_stick)), + config_entry_id=self.entry.entry_id, + ), ) async def async_added_to_hass(self): diff --git a/custom_components/plugwise_usb/event.py b/custom_components/plugwise_usb/event.py index 26eb059e..981fda37 100644 --- a/custom_components/plugwise_usb/event.py +++ b/custom_components/plugwise_usb/event.py @@ -1,7 +1,5 @@ """Plugwise USB Event component for HomeAssistant.""" -from __future__ import annotations - from dataclasses import dataclass from datetime import timedelta import logging diff --git a/custom_components/plugwise_usb/manifest.json b/custom_components/plugwise_usb/manifest.json index 1b14d83a..ace55a6e 100644 --- a/custom_components/plugwise_usb/manifest.json +++ b/custom_components/plugwise_usb/manifest.json @@ -9,6 +9,6 @@ "iot_class": "local_polling", "issue_tracker": "https://github.com/plugwise/python-plugwise-usb/issues", "loggers": ["plugwise_usb"], - "requirements": ["plugwise-usb==0.47.6"], - "version": "0.59.2" + "requirements": ["plugwise-usb==0.47.8"], + "version": "0.59.3" } diff --git a/custom_components/plugwise_usb/number.py b/custom_components/plugwise_usb/number.py index 267486cf..9b3d0934 100644 --- a/custom_components/plugwise_usb/number.py +++ b/custom_components/plugwise_usb/number.py @@ -1,7 +1,5 @@ """Plugwise USB Number component for HomeAssistant.""" -from __future__ import annotations - from dataclasses import dataclass from datetime import timedelta import logging diff --git a/custom_components/plugwise_usb/select.py b/custom_components/plugwise_usb/select.py index 3764e86d..a500635b 100644 --- a/custom_components/plugwise_usb/select.py +++ b/custom_components/plugwise_usb/select.py @@ -1,7 +1,5 @@ """Plugwise USB Select component for HomeAssistant.""" -from __future__ import annotations - from dataclasses import dataclass from datetime import timedelta from enum import Enum diff --git a/custom_components/plugwise_usb/sensor.py b/custom_components/plugwise_usb/sensor.py index 3210b6d9..1a4e37d7 100644 --- a/custom_components/plugwise_usb/sensor.py +++ b/custom_components/plugwise_usb/sensor.py @@ -1,7 +1,5 @@ """Plugwise USB Sensor component for Home Assistant.""" -from __future__ import annotations - from dataclasses import dataclass from datetime import timedelta import logging diff --git a/custom_components/plugwise_usb/switch.py b/custom_components/plugwise_usb/switch.py index 0ea745b5..c485d07a 100644 --- a/custom_components/plugwise_usb/switch.py +++ b/custom_components/plugwise_usb/switch.py @@ -1,7 +1,5 @@ """Plugwise USB Switch component for HomeAssistant.""" -from __future__ import annotations - from dataclasses import dataclass from datetime import timedelta import logging diff --git a/pyproject.toml b/pyproject.toml index 1d5885df..a1f89188 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "plugwise_usb-beta" -version = "0.59.2" +version = "0.59.3" description = "Plugwise USB custom_component (BETA)" readme = "README.md" requires-python = ">=3.14" diff --git a/scripts/ci-core-testing.sh b/scripts/ci-core-testing.sh index 0f871856..48222210 100755 --- a/scripts/ci-core-testing.sh +++ b/scripts/ci-core-testing.sh @@ -9,7 +9,6 @@ CWARN="\x1B[93m" # yellow # Repository name (for reuse betweeh plugwise network and usb REPO_NAME="plugwise_usb" -VENV_DIR=".venv" # By default assumes running against 'master' branch of Core-HA # as requested by @bouwew for on-par development with the releases @@ -60,6 +59,25 @@ which jq || ( echo -e "${CFAIL}You should have jq installed, exiting${CNORM}"; e my_path=$(git rev-parse --show-toplevel) +venv_and_uv() { + # shellcheck disable=SC1091 + source venv/bin/activate + if ! [ -x "$(command -v uv)" ]; then + echo -e "${CINFO}Ensure uv presence${CWARN}" + python3 -m pip install uv + fi + if ! [ -x "$(command -v prek)" ]; then + echo -e "${CINFO}Ensure prek presence${CWARN}" + uv pip install -r "${my_path}/requirements_commit.txt" + fi + if ! uv pip list | grep -q bcrypt; then + script/setup + fi + if ! [ -x "$(command -v pytest)" ]; then + uv pip install --only-binary :all: pytest + fi +} + # Ensure environment is set-up # 20250613 Copied from HA-core and shell-check adjusted and modified for local use @@ -67,12 +85,12 @@ set -e if [ -z "$VIRTUAL_ENV" ]; then if [ -x "$(command -v uv)" ]; then - uv venv --seed "${VENV_DIR}" + uv venv --seed venv else - python3 -m venv "${VENV_DIR}" + python3 -m venv venv fi # shellcheck disable=SC1091 # ingesting virtualenv - source "${VENV_DIR}/bin/activate" + source venv/bin/activate fi if ! [ -x "$(command -v uv)" ]; then @@ -81,13 +99,7 @@ fi # /20250613 # Install commit requirements -if ! [ -x "$(command -v prek)" ]; then - uv pip install prek -fi - uv pip install -r "${my_path}/requirements_commit.txt" - -# Install pre-commit hook prek install # i.e. args used for functions, not directions @@ -111,7 +123,7 @@ mkdir -p "${coredir}" if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "core_prep" ] ; then # If only dir exists, but not cloned yet - if [ ! -f "${coredir}/requirements_test_all.txt" ]; then + if [[ ! -f "${coredir}/requirements_test.txt" ]]; then if [ -d "${manualdir}" ]; then echo "" echo -e "${CINFO} ** Reusing copy, rebasing and copy to HA core**${CNORM}" @@ -131,7 +143,7 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "core_prep" ] ; then git clone https://github.com/home-assistant/core.git "${coredir}" cp -a "${coredir}." "${manualdir}" fi - if [ ! -f "${coredir}/requirements_test_all.txt" ]; then + if [[ ! -f "${coredir}/requirements_test.txt" ]]; then echo "" echo -e "${CFAIL}Cloning failed .. make sure ${coredir} exists and is an empty directory${CNORM}" echo "" @@ -168,27 +180,14 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "core_prep" ] ; then # Fake branch git checkout -b fake_branch - echo "" - echo -e "${CINFO}Ensure HA-core venv${CWARN}" - if [ -x "$(command -v uv)" ]; then - uv venv --seed "${VENV_DIR}" - else - python3 -m venv "${VENV_DIR}" + if [[ ! -d "venv" ]]; then + echo -e "${CINFO}Ensure HA-core venv${CWARN}" + uv venv --seed venv fi + echo -e "${CINFO}(Re)setup HA-core ${CWARN}" + script/setup # shellcheck disable=SC1091 - source "${VENV_DIR}/bin/activate" - - if ! [ -x "$(command -v uv)" ]; then - echo -e "${CINFO}Ensure uv presence${CWARN}" - uv pip install -r "${my_path}/requirements_commit.txt" - fi - if ! [ -x "$(command -v pytest)" ]; then - echo -e "${CINFO}Ensure pytest presence${CWARN}" - uv pip install pytest - fi - - echo -e "${CINFO}Bootstrap pip parts of HA-core${CWARN}" - script/setup + source venv/bin/activate echo "" echo -e "${CINFO}Cleaning existing ${REPO_NAME} from HA core${CNORM}" @@ -198,12 +197,8 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "core_prep" ] ; then echo -e "${CINFO}Overwriting with ${REPO_NAME}-beta${CNORM}" echo "" cp -r ../custom_components/${REPO_NAME} ./homeassistant/components/ - mkdir -p ./tests/components/${REPO_NAME}/ - cp -r ../tests/*py ./tests/components/${REPO_NAME}/ - # Rework pytest from custom_component to core - sed -i".sedbck" 's/pytest_homeassistant_custom_component.common/tests.common/g' ./tests/components/${REPO_NAME}/*py - sed -i".sedbck" 's/custom_components/homeassistant.components/g' ./tests/components/${REPO_NAME}/*py - echo "" + cp -r ../tests/${REPO_NAME} ./tests/components/ + fi # core_prep set +u @@ -211,22 +206,12 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "pip_prep" ] ; then cd "${coredir}" || exit echo "" echo -e "${CINFO}Ensure HA-core venv${CNORM}" - # shellcheck disable=SC1091 - source "${VENV_DIR}/bin/activate" + venv_and_uv mkdir -p ./tmp echo "" echo -e "${CINFO}Ensure translations are there${CNORM}" echo "" python3 -m script.translations develop --all > /dev/null 2>&1 - echo "" - if ! [ -x "$(command -v uv)" ]; then - echo -e "${CINFO}Ensure uv is there${CNORM}" - python3 -m pip install uv - fi - echo -e "${CINFO}Installing pip modules (using uv)${CNORM}" - echo "" - script/setup - echo "" # When using test.py prettier makes multi-line, so use jq module=$(jq '.requirements[]' ../custom_components/${REPO_NAME}/manifest.json | tr -d '"') #module=$(grep require ../custom_components/${REPO_NAME}/manifest.json | cut -f 4 -d '"') @@ -240,8 +225,7 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "testing" ] ; then cd "${coredir}" || exit echo "" echo -e "${CINFO}Ensure HA-core venv${CNORM}" - # shellcheck disable=SC1091 - source "${VENV_DIR}/bin/activate" + venv_and_uv echo "" echo -e "${CINFO}Test commencing ...${CNORM}" echo "" @@ -257,8 +241,7 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "quality" ] ; then cd "${coredir}" || exit echo "" echo -e "${CINFO}Ensure HA-core venv${CNORM}" - # shellcheck disable=SC1091 - source "${VENV_DIR}/bin/activate" + venv_and_uv echo "" set +e echo -e "${CINFO}... ruff-ing component...${CNORM}" @@ -283,16 +266,12 @@ if [ -z "${GITHUB_ACTIONS}" ]; then cd "${coredir}" || exit echo "" echo "Ensure HA-core venv${CNORM}" - # shellcheck disable=SC1091 - source "${VENV_DIR}/bin/activate" + venv_and_uv echo "" echo -e "${CINFO}Copy back modified files ...${CNORM}" echo "" - sed -i".sedbck" 's/tests.common/pytest_homeassistant_custom_component.common/g' ./tests/components/${REPO_NAME}/*py - sed -i".sedbck" 's/homeassistant.components/custom_components/g' ./tests/components/${REPO_NAME}/*py - rm ./tests/components/${REPO_NAME}/*sedbck cp -r ./homeassistant/components/${REPO_NAME} ../custom_components/ - cp -r ./tests/components/${REPO_NAME}/*py ../tests/ + cp -r ./tests/components/${REPO_NAME}/ ../tests/ echo -e "${CINFO}Removing 'version' from manifest for hassfest-ing, version not allowed in core components${CNORM}" echo "" # shellcheck disable=SC2090 diff --git a/scripts/local-testing.sh b/scripts/local-testing.sh index 0bc6e64b..56cf2160 100755 --- a/scripts/local-testing.sh +++ b/scripts/local-testing.sh @@ -34,22 +34,22 @@ fi # /20250613 # Install commit requirements -uv pip install -q --upgrade pre-commit +uv pip install --no-build -q --upgrade prek # Install pre-commit hook -pre-commit install +prek install echo -e "${CINFO}Installing pip modules (using uv)${CNORM}" echo "" echo -e "${CINFO} - HA requirements (core and test)${CNORM}" -uv pip install -q --upgrade -r requirements_commit.txt -r requirements_test.txt +uv pip install --no-build -q --upgrade -r requirements_commit.txt -r requirements_test.txt # When using test.py prettier makes multi-line, so use jq module=$(jq '.requirements[]' custom_components/${REPO_NAME}/manifest.json | tr -d '"') echo -e "${CINFO}Checking manifest for current python-${REPO_NAME} to install: ${module}${CNORM}" echo "" -uv pip install -q --upgrade "${module}" +uv pip install --no-build -q --upgrade "${module}" debug_params="" if [ -n "${DEBUG}" ] ; then debug_params="-rpP --log-cli-level=DEBUG" @@ -71,4 +71,4 @@ echo -e "${CFAIL}... SKIPPING mypy ...${CNORM}" #mypy custom_components/${REPO_NAME}/*.py || exit #echo -e "${CINFO}... markdownlint ...${CNORM}" -#pre-commit run --all-files --hook-stage manual markdownlint +#prek run --all-files --hook-stage manual markdownlint diff --git a/tests/__init__.py b/tests/plugwise_usb/__init__.py similarity index 100% rename from tests/__init__.py rename to tests/plugwise_usb/__init__.py diff --git a/tests/conftest.py b/tests/plugwise_usb/conftest.py similarity index 79% rename from tests/conftest.py rename to tests/plugwise_usb/conftest.py index 781e980c..d21dbb5d 100644 --- a/tests/conftest.py +++ b/tests/plugwise_usb/conftest.py @@ -1,7 +1,5 @@ """Setup mocks for the Plugwise USB integration tests.""" -from __future__ import annotations - from collections.abc import Generator from typing import Final from unittest.mock import AsyncMock, MagicMock, patch @@ -9,11 +7,13 @@ from plugwise_usb.exceptions import StickError import pytest -from custom_components.plugwise_usb.const import CONF_USB_PATH, DOMAIN +from homeassistant.components.plugwise_usb.const import CONF_USB_PATH, DOMAIN from homeassistant.core import HomeAssistant -from pytest_homeassistant_custom_component.common import MockConfigEntry -STICK_IMPORT_MOCK: Final[str] = "custom_components.plugwise_usb.config_flow.Stick" +# from pytest_homeassistant_custom_component.common import MockConfigEntry +from tests.common import MockConfigEntry + +STICK_IMPORT_MOCK: Final[str] = "homeassistant.components.plugwise_usb.config_flow.Stick" TEST_MAC: Final[str] = "01:23:45:67:AB" TEST_USB_PATH: Final[str] = "/dev/ttyUSB1" @@ -22,7 +22,7 @@ def mock_setup_entry() -> Generator[AsyncMock]: """Override async_setup_entry.""" with patch( - "custom_components.plugwise_usb.async_setup_entry", + "homeassistant.components.plugwise_usb.async_setup_entry", return_value=True, ) as mock_setup: yield mock_setup @@ -30,7 +30,7 @@ def mock_setup_entry() -> Generator[AsyncMock]: @pytest.fixture def mock_config_entry() -> MockConfigEntry: - """Return a mocked v1.2 config entry.""" # pw-beta only + """Return a mocked v1.2 config entry.""" return MockConfigEntry( domain=DOMAIN, data={CONF_USB_PATH: TEST_USB_PATH}, @@ -53,20 +53,6 @@ async def init_integration( return mock_config_entry -# @pytest.fixture -# def mock_comport() -> Generator[MagicMock]: -# """Return a mocked comport.""" -# with patch( -# "serial.tools.list_ports.comports", -# ) as port: -# port = serial.tools.list_ports_common.ListPortInfo(TEST_USBPORT) -# port.serial_number = "1234" -# port.manufacturer = "Virtual serial port" -# port.device = TEST_USBPORT -# port.description = "Some serial port" -# yield [port] - - @pytest.fixture def mock_usb_stick_not_setup() -> Generator[MagicMock]: """Return a mocked usb_mock.""" diff --git a/tests/test_config_flow.py b/tests/plugwise_usb/test_config_flow.py similarity index 87% rename from tests/test_config_flow.py rename to tests/plugwise_usb/test_config_flow.py index 14a8b93f..a04a2786 100644 --- a/tests/test_config_flow.py +++ b/tests/plugwise_usb/test_config_flow.py @@ -1,19 +1,24 @@ """Test the Plugwise config flow.""" +from collections.abc import Generator from typing import Final from unittest.mock import AsyncMock, MagicMock, patch from plugwise_usb.exceptions import StickError import pytest -from custom_components.plugwise_usb.config_flow import CONF_MANUAL_PATH -from custom_components.plugwise_usb.const import CONF_USB_PATH, DOMAIN +from homeassistant.components.plugwise_usb.config_flow import CONF_MANUAL_PATH +from homeassistant.components.plugwise_usb.const import CONF_USB_PATH, DOMAIN +from homeassistant.components.usb import USBDevice from homeassistant.config_entries import SOURCE_USER, ConfigFlowResult from homeassistant.const import CONF_SOURCE from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType, InvalidData -from pytest_homeassistant_custom_component.common import MockConfigEntry -import serial.tools.list_ports + +# from pytest_homeassistant_custom_component.common import MockConfigEntry +from tests.common import MockConfigEntry + +type MockFixture = Generator[MagicMock | AsyncMock] TEST_MAC: Final[str] = "01:23:45:67:AB" TEST_MAC2: Final[str] = "02:23:45:67:AB" @@ -21,22 +26,32 @@ TEST_USB2_PATH: Final[str] = "/dev/ttyUSB2" -def com_port(): - """Mock of a serial port.""" +@pytest.fixture(name="serial_ports", autouse=True) +def usb_comports() -> MockFixture: + """Mock scan_serial_ports.""" + with patch( + "homeassistant.components.plugwise_usb.config_flow.usb.async_scan_serial_ports", + AsyncMock(return_value=[mocked_com_port()]), + ) as comports_mock: + yield comports_mock - port = serial.tools.list_ports_common.ListPortInfo(TEST_USB_PATH) - port.serial_number = "1234" - port.manufacturer = "Virtual serial port" - port.device = TEST_USB_PATH - port.description = "Some serial port" - return port + +def mocked_com_port()-> USBDevice: + """Mock of a serial port.""" + return USBDevice( + device=TEST_USB_PATH, + vid="04D2", + pid="162E", + serial_number="1234", + manufacturer="Virtual serial port", + description="Some serial port", + ) -@patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()])) async def test_user_flow_select(hass, mock_usb_stick: MagicMock): """Test user flow when USB-stick is selected from list.""" - port = com_port() - port_select = f"{port}, s/n: {port.serial_number} - {port.manufacturer}" + port = mocked_com_port() + port_select = f"{port.device}, s/n: {port.serial_number} - {port.manufacturer}" result = await hass.config_entries.flow.async_init( DOMAIN, @@ -118,7 +133,7 @@ async def test_invalid_connection(hass): result = await hass.config_entries.flow.async_configure( result["flow_id"], - {CONF_USB_PATH: "/dev/null"}, + user_input={CONF_USB_PATH: "null"}, ) await hass.async_block_till_done() assert result.get("type") is FlowResultType.FORM @@ -137,14 +152,11 @@ async def test_empty_connection(hass): ) await hass.async_block_till_done() - try: + with pytest.raises(InvalidData): result = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_USB_PATH: None}, ) - pytest.fail("Empty connection was accepted") - except InvalidData: - assert True assert result.get("type") is FlowResultType.FORM assert result.get("errors") == {}