Require firmware version 3.1.1 for airgradient (#118744)

This commit is contained in:
Joost Lekkerkerker 2024-06-03 21:08:28 +02:00 committed by GitHub
parent dd1dd4c6a3
commit 2a92f78453
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 68 additions and 3 deletions

View File

@ -3,6 +3,8 @@
from typing import Any from typing import Any
from airgradient import AirGradientClient, AirGradientError, ConfigurationControl from airgradient import AirGradientClient, AirGradientError, ConfigurationControl
from awesomeversion import AwesomeVersion
from mashumaro import MissingField
import voluptuous as vol import voluptuous as vol
from homeassistant.components import zeroconf from homeassistant.components import zeroconf
@ -12,6 +14,8 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN from .const import DOMAIN
MIN_VERSION = AwesomeVersion("3.1.1")
class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN): class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
"""AirGradient config flow.""" """AirGradient config flow."""
@ -38,6 +42,9 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
await self.async_set_unique_id(discovery_info.properties["serialno"]) await self.async_set_unique_id(discovery_info.properties["serialno"])
self._abort_if_unique_id_configured(updates={CONF_HOST: host}) self._abort_if_unique_id_configured(updates={CONF_HOST: host})
if AwesomeVersion(discovery_info.properties["fw_ver"]) < MIN_VERSION:
return self.async_abort(reason="invalid_version")
session = async_get_clientsession(self.hass) session = async_get_clientsession(self.hass)
self.client = AirGradientClient(host, session=session) self.client = AirGradientClient(host, session=session)
await self.client.get_current_measures() await self.client.get_current_measures()
@ -78,6 +85,8 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
current_measures = await self.client.get_current_measures() current_measures = await self.client.get_current_measures()
except AirGradientError: except AirGradientError:
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
except MissingField:
return self.async_abort(reason="invalid_version")
else: else:
await self.async_set_unique_id(current_measures.serial_number) await self.async_set_unique_id(current_measures.serial_number)
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()

View File

@ -15,7 +15,8 @@
} }
}, },
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]" "already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"invalid_version": "This firmware version is unsupported. Please upgrade the firmware of the device to at least version 3.1.1."
}, },
"error": { "error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",

View File

@ -62,7 +62,7 @@ def mock_new_airgradient_client(
def mock_cloud_airgradient_client( def mock_cloud_airgradient_client(
mock_airgradient_client: AsyncMock, mock_airgradient_client: AsyncMock,
) -> Generator[AsyncMock, None, None]: ) -> Generator[AsyncMock, None, None]:
"""Mock a new AirGradient client.""" """Mock a cloud AirGradient client."""
mock_airgradient_client.get_config.return_value = Config.from_json( mock_airgradient_client.get_config.return_value = Config.from_json(
load_fixture("get_config_cloud.json", DOMAIN) load_fixture("get_config_cloud.json", DOMAIN)
) )

View File

@ -4,6 +4,7 @@ from ipaddress import ip_address
from unittest.mock import AsyncMock from unittest.mock import AsyncMock
from airgradient import AirGradientConnectionError, ConfigurationControl from airgradient import AirGradientConnectionError, ConfigurationControl
from mashumaro import MissingField
from homeassistant.components.airgradient import DOMAIN from homeassistant.components.airgradient import DOMAIN
from homeassistant.components.zeroconf import ZeroconfServiceInfo from homeassistant.components.zeroconf import ZeroconfServiceInfo
@ -14,7 +15,7 @@ from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
ZEROCONF_DISCOVERY = ZeroconfServiceInfo( OLD_ZEROCONF_DISCOVERY = ZeroconfServiceInfo(
ip_address=ip_address("10.0.0.131"), ip_address=ip_address("10.0.0.131"),
ip_addresses=[ip_address("10.0.0.131")], ip_addresses=[ip_address("10.0.0.131")],
hostname="airgradient_84fce612f5b8.local.", hostname="airgradient_84fce612f5b8.local.",
@ -29,6 +30,21 @@ ZEROCONF_DISCOVERY = ZeroconfServiceInfo(
}, },
) )
ZEROCONF_DISCOVERY = ZeroconfServiceInfo(
ip_address=ip_address("10.0.0.131"),
ip_addresses=[ip_address("10.0.0.131")],
hostname="airgradient_84fce612f5b8.local.",
name="airgradient_84fce612f5b8._airgradient._tcp.local.",
port=80,
type="_airgradient._tcp.local.",
properties={
"vendor": "AirGradient",
"fw_ver": "3.1.1",
"serialno": "84fce612f5b8",
"model": "I-9PSL",
},
)
async def test_full_flow( async def test_full_flow(
hass: HomeAssistant, hass: HomeAssistant,
@ -119,6 +135,34 @@ async def test_flow_errors(
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
async def test_flow_old_firmware_version(
hass: HomeAssistant,
mock_airgradient_client: AsyncMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test flow with old firmware version."""
mock_airgradient_client.get_current_measures.side_effect = MissingField(
"", object, object
)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "10.0.0.131"},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "invalid_version"
async def test_duplicate( async def test_duplicate(
hass: HomeAssistant, hass: HomeAssistant,
mock_airgradient_client: AsyncMock, mock_airgradient_client: AsyncMock,
@ -197,3 +241,14 @@ async def test_zeroconf_flow_cloud_device(
) )
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
mock_cloud_airgradient_client.set_configuration_control.assert_not_called() mock_cloud_airgradient_client.set_configuration_control.assert_not_called()
async def test_zeroconf_flow_abort_old_firmware(hass: HomeAssistant) -> None:
"""Test zeroconf flow aborts with old firmware."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_ZEROCONF},
data=OLD_ZEROCONF_DISCOVERY,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "invalid_version"