mirror of
https://github.com/home-assistant/core.git
synced 2025-12-20 02:48:57 +00:00
Bump pysma to 1.0.2 and enable type checking (#154977)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
@@ -477,6 +477,7 @@ homeassistant.components.skybell.*
|
||||
homeassistant.components.slack.*
|
||||
homeassistant.components.sleep_as_android.*
|
||||
homeassistant.components.sleepiq.*
|
||||
homeassistant.components.sma.*
|
||||
homeassistant.components.smhi.*
|
||||
homeassistant.components.smlight.*
|
||||
homeassistant.components.smtp.*
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from pysma import SMA
|
||||
from pysma import SMAWebConnect
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
@@ -35,7 +35,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: SMAConfigEntry) -> bool:
|
||||
protocol = "https" if entry.data[CONF_SSL] else "http"
|
||||
url = f"{protocol}://{entry.data[CONF_HOST]}"
|
||||
|
||||
sma = SMA(
|
||||
sma = SMAWebConnect(
|
||||
session=async_get_clientsession(
|
||||
hass=hass, verify_ssl=entry.data[CONF_VERIFY_SSL]
|
||||
),
|
||||
|
||||
@@ -6,7 +6,13 @@ from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import pysma
|
||||
import attrs
|
||||
from pysma import (
|
||||
SmaAuthenticationException,
|
||||
SmaConnectionException,
|
||||
SmaReadException,
|
||||
SMAWebConnect,
|
||||
)
|
||||
import voluptuous as vol
|
||||
from yarl import URL
|
||||
|
||||
@@ -42,7 +48,7 @@ async def validate_input(
|
||||
host = data[CONF_HOST] if data is not None else user_input[CONF_HOST]
|
||||
url = URL.build(scheme=protocol, host=host)
|
||||
|
||||
sma = pysma.SMA(
|
||||
sma = SMAWebConnect(
|
||||
session, str(url), user_input[CONF_PASSWORD], group=user_input[CONF_GROUP]
|
||||
)
|
||||
|
||||
@@ -51,7 +57,7 @@ async def validate_input(
|
||||
device_info = await sma.device_info()
|
||||
await sma.close_session()
|
||||
|
||||
return device_info
|
||||
return attrs.asdict(device_info)
|
||||
|
||||
|
||||
class SmaConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@@ -90,11 +96,11 @@ class SmaConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
device_info = await validate_input(
|
||||
self.hass, user_input=user_input, data=self._data
|
||||
)
|
||||
except pysma.exceptions.SmaConnectionException:
|
||||
except SmaConnectionException:
|
||||
errors["base"] = "cannot_connect"
|
||||
except pysma.exceptions.SmaAuthenticationException:
|
||||
except SmaAuthenticationException:
|
||||
errors["base"] = "invalid_auth"
|
||||
except pysma.exceptions.SmaReadException:
|
||||
except SmaReadException:
|
||||
errors["base"] = "cannot_retrieve_device_info"
|
||||
except Exception:
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
|
||||
@@ -6,13 +6,14 @@ from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from pysma import SMA
|
||||
from pysma.exceptions import (
|
||||
from pysma import (
|
||||
SmaAuthenticationException,
|
||||
SmaConnectionException,
|
||||
SmaReadException,
|
||||
SMAWebConnect,
|
||||
)
|
||||
from pysma.sensor import Sensor
|
||||
from pysma.helpers import DeviceInfo
|
||||
from pysma.sensor import Sensors
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_SCAN_INTERVAL
|
||||
@@ -29,8 +30,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||
class SMACoordinatorData:
|
||||
"""Data class for SMA sensors."""
|
||||
|
||||
sma_device_info: dict[str, str]
|
||||
sensors: list[Sensor]
|
||||
sma_device_info: DeviceInfo
|
||||
sensors: Sensors
|
||||
|
||||
|
||||
class SMADataUpdateCoordinator(DataUpdateCoordinator[SMACoordinatorData]):
|
||||
@@ -42,7 +43,7 @@ class SMADataUpdateCoordinator(DataUpdateCoordinator[SMACoordinatorData]):
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
sma: SMA,
|
||||
sma: SMAWebConnect,
|
||||
) -> None:
|
||||
"""Initialize the SMA Data Update Coordinator."""
|
||||
super().__init__(
|
||||
@@ -57,8 +58,8 @@ class SMADataUpdateCoordinator(DataUpdateCoordinator[SMACoordinatorData]):
|
||||
),
|
||||
)
|
||||
self.sma = sma
|
||||
self._sma_device_info: dict[str, str] = {}
|
||||
self._sensors: list[Sensor] = []
|
||||
self._sma_device_info = DeviceInfo()
|
||||
self._sensors = Sensors()
|
||||
|
||||
async def _async_setup(self) -> None:
|
||||
"""Setup the SMA Data Update Coordinator."""
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/sma",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pysma"],
|
||||
"requirements": ["pysma==0.7.5"]
|
||||
"requirements": ["pysma==1.0.2"]
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pysma
|
||||
from pysma.sensor import Sensor
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
@@ -859,7 +859,7 @@ class SMAsensor(CoordinatorEntity[SMADataUpdateCoordinator], SensorEntity):
|
||||
self,
|
||||
coordinator: SMADataUpdateCoordinator,
|
||||
description: SensorEntityDescription | None,
|
||||
pysma_sensor: pysma.sensor.Sensor,
|
||||
pysma_sensor: Sensor,
|
||||
entry: SMAConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
@@ -873,17 +873,17 @@ class SMAsensor(CoordinatorEntity[SMADataUpdateCoordinator], SensorEntity):
|
||||
url = f"{protocol}://{entry.data[CONF_HOST]}"
|
||||
|
||||
self._sensor = pysma_sensor
|
||||
self._serial = coordinator.data.sma_device_info["serial"]
|
||||
self._serial = coordinator.data.sma_device_info.serial
|
||||
assert entry.unique_id
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
configuration_url=url,
|
||||
identifiers={(DOMAIN, entry.unique_id)},
|
||||
manufacturer=coordinator.data.sma_device_info["manufacturer"],
|
||||
model=coordinator.data.sma_device_info["type"],
|
||||
name=coordinator.data.sma_device_info["name"],
|
||||
sw_version=coordinator.data.sma_device_info["sw_version"],
|
||||
serial_number=coordinator.data.sma_device_info["serial"],
|
||||
manufacturer=coordinator.data.sma_device_info.manufacturer,
|
||||
model=coordinator.data.sma_device_info.type,
|
||||
name=coordinator.data.sma_device_info.name,
|
||||
sw_version=coordinator.data.sma_device_info.sw_version,
|
||||
serial_number=coordinator.data.sma_device_info.serial,
|
||||
)
|
||||
self._attr_unique_id = (
|
||||
f"{entry.unique_id}-{pysma_sensor.key}_{pysma_sensor.key_idx}"
|
||||
@@ -908,7 +908,7 @@ class SMAsensor(CoordinatorEntity[SMADataUpdateCoordinator], SensorEntity):
|
||||
"""Return if the device is available."""
|
||||
return (
|
||||
super().available
|
||||
and self._serial == self.coordinator.data.sma_device_info["serial"]
|
||||
and self._serial == self.coordinator.data.sma_device_info.serial
|
||||
)
|
||||
|
||||
@property
|
||||
|
||||
10
mypy.ini
generated
10
mypy.ini
generated
@@ -4526,6 +4526,16 @@ disallow_untyped_defs = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.sma.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
disallow_subclassing_any = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_decorators = true
|
||||
disallow_untyped_defs = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.smhi.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
|
||||
2
requirements_all.txt
generated
2
requirements_all.txt
generated
@@ -2387,7 +2387,7 @@ pysignalclirestapi==0.3.24
|
||||
pyskyqhub==0.1.4
|
||||
|
||||
# homeassistant.components.sma
|
||||
pysma==0.7.5
|
||||
pysma==1.0.2
|
||||
|
||||
# homeassistant.components.smappee
|
||||
pysmappee==0.2.29
|
||||
|
||||
2
requirements_test_all.txt
generated
2
requirements_test_all.txt
generated
@@ -1993,7 +1993,7 @@ pysiaalarm==3.1.1
|
||||
pysignalclirestapi==0.3.24
|
||||
|
||||
# homeassistant.components.sma
|
||||
pysma==0.7.5
|
||||
pysma==1.0.2
|
||||
|
||||
# homeassistant.components.smappee
|
||||
pysmappee==0.2.29
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Tests for the sma integration."""
|
||||
|
||||
from pysma.helpers import DeviceInfo
|
||||
|
||||
from homeassistant.components.sma.const import CONF_GROUP
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
@@ -12,13 +14,14 @@ from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
MOCK_DEVICE = {
|
||||
"manufacturer": "SMA",
|
||||
"name": "SMA Device Name",
|
||||
"type": "Sunny Boy 3.6",
|
||||
"serial": 123456789,
|
||||
"sw_version": "1.0.0",
|
||||
}
|
||||
MOCK_DEVICE = DeviceInfo(
|
||||
manufacturer="SMA",
|
||||
name="SMA Device Name",
|
||||
type="Sunny Boy 3.6",
|
||||
serial=123456789,
|
||||
sw_version="1.0.0",
|
||||
)
|
||||
|
||||
|
||||
MOCK_USER_INPUT = {
|
||||
CONF_HOST: "1.1.1.1",
|
||||
|
||||
@@ -8,7 +8,7 @@ from pysma.const import (
|
||||
GENERIC_SENSORS,
|
||||
OPTIMIZERS_VIA_INVERTER,
|
||||
)
|
||||
from pysma.definitions import sensor_map
|
||||
from pysma.definitions.webconnect import sensor_map
|
||||
from pysma.sensor import Sensors
|
||||
import pytest
|
||||
|
||||
@@ -26,8 +26,8 @@ def mock_config_entry(hass: HomeAssistant) -> MockConfigEntry:
|
||||
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title=MOCK_DEVICE["name"],
|
||||
unique_id=str(MOCK_DEVICE["serial"]),
|
||||
title=MOCK_DEVICE.name,
|
||||
unique_id=str(MOCK_DEVICE.serial),
|
||||
data=MOCK_USER_INPUT,
|
||||
minor_version=2,
|
||||
entry_id="sma_entry_123",
|
||||
@@ -47,7 +47,7 @@ def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
def mock_sma_client() -> Generator[MagicMock]:
|
||||
"""Mock the SMA client."""
|
||||
with patch(
|
||||
"homeassistant.components.sma.coordinator.SMA", autospec=True
|
||||
"homeassistant.components.sma.coordinator.SMAWebConnect", autospec=True
|
||||
) as sma_cls:
|
||||
sma_instance: MagicMock = sma_cls.return_value
|
||||
sma_instance.device_info = AsyncMock(return_value=MOCK_DEVICE)
|
||||
@@ -80,8 +80,11 @@ def mock_sma_client() -> Generator[MagicMock]:
|
||||
sma_instance.read = AsyncMock(side_effect=_async_mock_read)
|
||||
|
||||
with (
|
||||
patch("homeassistant.components.sma.config_flow.pysma.SMA", new=sma_cls),
|
||||
patch("homeassistant.components.sma.SMA", new=sma_cls),
|
||||
patch("pysma.SMA", new=sma_cls),
|
||||
patch(
|
||||
"homeassistant.components.sma.config_flow.SMAWebConnect",
|
||||
new=sma_cls,
|
||||
),
|
||||
patch("homeassistant.components.sma.SMAWebConnect", new=sma_cls),
|
||||
patch("pysma.SMAWebConnect", new=sma_cls),
|
||||
):
|
||||
yield sma_instance
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from pysma.exceptions import (
|
||||
SmaAuthenticationException,
|
||||
SmaConnectionException,
|
||||
SmaReadException,
|
||||
)
|
||||
from pysma import SmaAuthenticationException, SmaConnectionException, SmaReadException
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.sma.const import DOMAIN
|
||||
@@ -91,7 +87,7 @@ async def test_form_exceptions(
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sma.config_flow.pysma.SMA.new_session",
|
||||
"homeassistant.components.sma.config_flow.SMAWebConnect.new_session",
|
||||
side_effect=exception,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
@@ -211,7 +207,7 @@ async def test_dhcp_exceptions(
|
||||
data=DHCP_DISCOVERY,
|
||||
)
|
||||
|
||||
with patch("homeassistant.components.sma.config_flow.pysma.SMA") as mock_sma:
|
||||
with patch("homeassistant.components.sma.config_flow.SMAWebConnect") as mock_sma:
|
||||
mock_sma_instance = mock_sma.return_value
|
||||
mock_sma_instance.new_session = AsyncMock(side_effect=exception)
|
||||
|
||||
@@ -223,7 +219,7 @@ async def test_dhcp_exceptions(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": error}
|
||||
|
||||
with patch("homeassistant.components.sma.config_flow.pysma.SMA") as mock_sma:
|
||||
with patch("homeassistant.components.sma.config_flow.SMAWebConnect") as mock_sma:
|
||||
mock_sma_instance = mock_sma.return_value
|
||||
mock_sma_instance.new_session = AsyncMock(return_value=True)
|
||||
mock_sma_instance.device_info = AsyncMock(return_value=MOCK_DEVICE)
|
||||
@@ -291,7 +287,7 @@ async def test_reauth_flow_exceptions(
|
||||
|
||||
result = await entry.start_reauth_flow(hass)
|
||||
|
||||
with patch("homeassistant.components.sma.config_flow.pysma.SMA") as mock_sma:
|
||||
with patch("homeassistant.components.sma.config_flow.SMAWebConnect") as mock_sma:
|
||||
mock_sma_instance = mock_sma.return_value
|
||||
mock_sma_instance.new_session = AsyncMock(side_effect=exception)
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
"""Test the sma init file."""
|
||||
|
||||
from collections.abc import AsyncGenerator, Generator
|
||||
from collections.abc import AsyncGenerator
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from pysma.exceptions import (
|
||||
SmaAuthenticationException,
|
||||
SmaConnectionException,
|
||||
SmaReadException,
|
||||
)
|
||||
from pysma import SmaAuthenticationException, SmaConnectionException, SmaReadException
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.sma.const import DOMAIN
|
||||
@@ -26,8 +23,8 @@ async def test_migrate_entry_minor_version_1_2(
|
||||
"""Test migrating a 1.1 config entry to 1.2."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title=MOCK_DEVICE["name"],
|
||||
unique_id=MOCK_DEVICE["serial"], # Not converted to str
|
||||
title=MOCK_DEVICE.name,
|
||||
unique_id=MOCK_DEVICE.serial,
|
||||
data=MOCK_USER_INPUT,
|
||||
source=SOURCE_IMPORT,
|
||||
minor_version=1,
|
||||
@@ -36,7 +33,8 @@ async def test_migrate_entry_minor_version_1_2(
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert entry.version == 1
|
||||
assert entry.minor_version == 2
|
||||
assert entry.unique_id == str(MOCK_DEVICE["serial"])
|
||||
assert isinstance(MOCK_DEVICE.serial, str)
|
||||
assert entry.unique_id == MOCK_DEVICE.serial
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -49,7 +47,7 @@ async def test_migrate_entry_minor_version_1_2(
|
||||
)
|
||||
async def test_setup_exceptions(
|
||||
hass: HomeAssistant,
|
||||
mock_sma_client: Generator,
|
||||
mock_sma_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
exception: Exception,
|
||||
expected_state: ConfigEntryState,
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
"""Test the SMA sensor platform."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pysma.exceptions import (
|
||||
SmaAuthenticationException,
|
||||
SmaConnectionException,
|
||||
SmaReadException,
|
||||
)
|
||||
from pysma import SmaAuthenticationException, SmaConnectionException, SmaReadException
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
@@ -56,7 +52,7 @@ async def test_all_entities(
|
||||
async def test_refresh_exceptions(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_sma_client: Generator,
|
||||
mock_sma_client: MagicMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
@@ -71,4 +67,5 @@ async def test_refresh_exceptions(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("sensor.sma_device_name_battery_capacity_a")
|
||||
assert state
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
Reference in New Issue
Block a user