mirror of
https://github.com/home-assistant/core.git
synced 2026-01-14 02:58:14 +00:00
Compare commits
2 Commits
matter_rem
...
knx-expose
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a166da552b | ||
|
|
31ae6951db |
@@ -407,7 +407,6 @@ homeassistant.components.person.*
|
||||
homeassistant.components.pi_hole.*
|
||||
homeassistant.components.ping.*
|
||||
homeassistant.components.plugwise.*
|
||||
homeassistant.components.pooldose.*
|
||||
homeassistant.components.portainer.*
|
||||
homeassistant.components.powerfox.*
|
||||
homeassistant.components.powerwall.*
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["datadog"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["datadog==0.52.0"]
|
||||
}
|
||||
|
||||
@@ -112,7 +112,6 @@ AIR_QUALITY_SENSOR_TYPES: tuple[AirQualitySensorEntityDescription, ...] = (
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
device_class=SensorDeviceClass.CO,
|
||||
native_unit_of_measurement_fn=lambda x: x.pollutants.co.concentration.units,
|
||||
exists_fn=lambda x: "co" in {p.code for p in x.pollutants},
|
||||
value_fn=lambda x: x.pollutants.co.concentration.value,
|
||||
),
|
||||
AirQualitySensorEntityDescription(
|
||||
@@ -144,7 +143,6 @@ AIR_QUALITY_SENSOR_TYPES: tuple[AirQualitySensorEntityDescription, ...] = (
|
||||
translation_key="nitrogen_dioxide",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement_fn=lambda x: x.pollutants.no2.concentration.units,
|
||||
exists_fn=lambda x: "no2" in {p.code for p in x.pollutants},
|
||||
value_fn=lambda x: x.pollutants.no2.concentration.value,
|
||||
),
|
||||
AirQualitySensorEntityDescription(
|
||||
@@ -152,7 +150,6 @@ AIR_QUALITY_SENSOR_TYPES: tuple[AirQualitySensorEntityDescription, ...] = (
|
||||
translation_key="ozone",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement_fn=lambda x: x.pollutants.o3.concentration.units,
|
||||
exists_fn=lambda x: "o3" in {p.code for p in x.pollutants},
|
||||
value_fn=lambda x: x.pollutants.o3.concentration.value,
|
||||
),
|
||||
AirQualitySensorEntityDescription(
|
||||
@@ -160,7 +157,6 @@ AIR_QUALITY_SENSOR_TYPES: tuple[AirQualitySensorEntityDescription, ...] = (
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
device_class=SensorDeviceClass.PM10,
|
||||
native_unit_of_measurement_fn=lambda x: x.pollutants.pm10.concentration.units,
|
||||
exists_fn=lambda x: "pm10" in {p.code for p in x.pollutants},
|
||||
value_fn=lambda x: x.pollutants.pm10.concentration.value,
|
||||
),
|
||||
AirQualitySensorEntityDescription(
|
||||
@@ -168,7 +164,6 @@ AIR_QUALITY_SENSOR_TYPES: tuple[AirQualitySensorEntityDescription, ...] = (
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
device_class=SensorDeviceClass.PM25,
|
||||
native_unit_of_measurement_fn=lambda x: x.pollutants.pm25.concentration.units,
|
||||
exists_fn=lambda x: "pm25" in {p.code for p in x.pollutants},
|
||||
value_fn=lambda x: x.pollutants.pm25.concentration.value,
|
||||
),
|
||||
AirQualitySensorEntityDescription(
|
||||
@@ -176,7 +171,6 @@ AIR_QUALITY_SENSOR_TYPES: tuple[AirQualitySensorEntityDescription, ...] = (
|
||||
translation_key="sulphur_dioxide",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement_fn=lambda x: x.pollutants.so2.concentration.units,
|
||||
exists_fn=lambda x: "so2" in {p.code for p in x.pollutants},
|
||||
value_fn=lambda x: x.pollutants.so2.concentration.value,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -116,6 +116,7 @@ class KnxExposeOptions:
|
||||
dpt: type[DPTBase]
|
||||
respond_to_read: bool
|
||||
cooldown: float
|
||||
periodic_send: float
|
||||
default: Any | None
|
||||
value_template: Template | None
|
||||
|
||||
@@ -130,12 +131,17 @@ def _yaml_config_to_expose_options(config: ConfigType) -> KnxExposeOptions:
|
||||
else:
|
||||
dpt = DPTBase.parse_transcoder(config[ExposeSchema.CONF_KNX_EXPOSE_TYPE]) # type: ignore[assignment] # checked by schema validation
|
||||
ga = parse_device_group_address(config[KNX_ADDRESS])
|
||||
cooldown_seconds = config[ExposeSchema.CONF_KNX_EXPOSE_COOLDOWN].total_seconds()
|
||||
periodic_send_seconds = config[
|
||||
ExposeSchema.CONF_KNX_EXPOSE_PERIODIC_SEND
|
||||
].total_seconds()
|
||||
return KnxExposeOptions(
|
||||
attribute=config.get(ExposeSchema.CONF_KNX_EXPOSE_ATTRIBUTE),
|
||||
group_address=ga,
|
||||
dpt=dpt,
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
cooldown=config[ExposeSchema.CONF_KNX_EXPOSE_COOLDOWN],
|
||||
cooldown=cooldown_seconds,
|
||||
periodic_send=periodic_send_seconds,
|
||||
default=config.get(ExposeSchema.CONF_KNX_EXPOSE_DEFAULT),
|
||||
value_template=config.get(CONF_VALUE_TEMPLATE),
|
||||
)
|
||||
@@ -167,6 +173,7 @@ class KnxExposeEntity:
|
||||
respond_to_read=option.respond_to_read,
|
||||
value_type=option.dpt,
|
||||
cooldown=option.cooldown,
|
||||
periodic_send=option.periodic_send,
|
||||
),
|
||||
)
|
||||
for option in options
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
from collections import OrderedDict
|
||||
from datetime import timedelta
|
||||
import math
|
||||
from typing import ClassVar, Final
|
||||
|
||||
@@ -538,6 +539,7 @@ class ExposeSchema(KNXPlatformSchema):
|
||||
CONF_KNX_EXPOSE_ATTRIBUTE = "attribute"
|
||||
CONF_KNX_EXPOSE_BINARY = "binary"
|
||||
CONF_KNX_EXPOSE_COOLDOWN = "cooldown"
|
||||
CONF_KNX_EXPOSE_PERIODIC_SEND = "periodic_send"
|
||||
CONF_KNX_EXPOSE_DEFAULT = "default"
|
||||
CONF_TIME = "time"
|
||||
CONF_DATE = "date"
|
||||
@@ -554,7 +556,12 @@ class ExposeSchema(KNXPlatformSchema):
|
||||
)
|
||||
EXPOSE_SENSOR_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_KNX_EXPOSE_COOLDOWN, default=0): cv.positive_float,
|
||||
vol.Optional(
|
||||
CONF_KNX_EXPOSE_COOLDOWN, default=timedelta(0)
|
||||
): cv.positive_time_period,
|
||||
vol.Optional(
|
||||
CONF_KNX_EXPOSE_PERIODIC_SEND, default=timedelta(0)
|
||||
): cv.positive_time_period,
|
||||
vol.Optional(CONF_RESPOND_TO_READ, default=True): cv.boolean,
|
||||
vol.Required(CONF_KNX_EXPOSE_TYPE): vol.Any(
|
||||
CONF_KNX_EXPOSE_BINARY, sensor_type_validator
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["london_tube_status"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["london-tube-status==0.5"],
|
||||
"single_config_entry": true
|
||||
}
|
||||
|
||||
@@ -528,10 +528,7 @@ DISCOVERY_SCHEMAS = [
|
||||
),
|
||||
),
|
||||
entity_class=MatterBinarySensor,
|
||||
required_attributes=(
|
||||
clusters.Thermostat.Attributes.RemoteSensing,
|
||||
clusters.Thermostat.Attributes.OutdoorTemperature,
|
||||
),
|
||||
required_attributes=(clusters.Thermostat.Attributes.RemoteSensing,),
|
||||
allow_multi=True,
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
|
||||
@@ -9,7 +9,7 @@ from aiohttp import ClientError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_DOMAIN, CONF_HOST, CONF_NAME, CONF_PASSWORD
|
||||
from homeassistant.const import CONF_DOMAIN, CONF_HOST, CONF_PASSWORD
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.selector import (
|
||||
@@ -37,16 +37,6 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
STEP_RECONFIGURE_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PASSWORD): TextSelector(
|
||||
TextSelectorConfig(
|
||||
type=TextSelectorType.PASSWORD, autocomplete="current-password"
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class NamecheapDnsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Namecheap DynamicDNS."""
|
||||
@@ -99,41 +89,3 @@ class NamecheapDnsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
deprecate_yaml_issue(self.hass, import_success=True)
|
||||
return result
|
||||
|
||||
async def async_step_reconfigure(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle reconfigure flow."""
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
entry = self._get_reconfigure_entry()
|
||||
|
||||
if user_input is not None:
|
||||
session = async_get_clientsession(self.hass)
|
||||
try:
|
||||
if not await update_namecheapdns(
|
||||
session,
|
||||
entry.data[CONF_HOST],
|
||||
entry.data[CONF_DOMAIN],
|
||||
user_input[CONF_PASSWORD],
|
||||
):
|
||||
errors["base"] = "update_failed"
|
||||
except ClientError:
|
||||
_LOGGER.debug("Cannot connect", exc_info=True)
|
||||
errors["base"] = "cannot_connect"
|
||||
except Exception:
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
|
||||
if not errors:
|
||||
return self.async_update_reload_and_abort(
|
||||
entry,
|
||||
data_updates=user_input,
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="reconfigure",
|
||||
data_schema=STEP_RECONFIGURE_DATA_SCHEMA,
|
||||
errors=errors,
|
||||
description_placeholders={CONF_NAME: entry.title},
|
||||
)
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
|
||||
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
@@ -10,15 +9,6 @@
|
||||
"update_failed": "Updating DNS failed"
|
||||
},
|
||||
"step": {
|
||||
"reconfigure": {
|
||||
"data": {
|
||||
"password": "[%key:component::namecheapdns::config::step::user::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"password": "[%key:component::namecheapdns::config::step::user::data_description::password%]"
|
||||
},
|
||||
"title": "Re-configure {name}"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"domain": "[%key:common::config_flow::data::username%]",
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/nederlandse_spoorwegen",
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["nsapi==3.1.3"]
|
||||
}
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pyrail"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["pyrail==0.4.1"]
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable, Coroutine
|
||||
from typing import Any, Literal
|
||||
from typing import Literal
|
||||
|
||||
from pooldose.type_definitions import DeviceInfoDict, ValueDict
|
||||
|
||||
@@ -81,10 +80,7 @@ class PooldoseEntity(CoordinatorEntity[PooldoseCoordinator]):
|
||||
return platform_data.get(self.entity_description.key)
|
||||
|
||||
async def _async_perform_write(
|
||||
self,
|
||||
api_call: Callable[[str, Any], Coroutine[Any, Any, bool]],
|
||||
key: str,
|
||||
value: bool | str | float,
|
||||
self, api_call, key: str, value: bool | str | float
|
||||
) -> None:
|
||||
"""Perform a write call to the API with unified error handling.
|
||||
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/pooldose",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "platinum",
|
||||
"quality_scale": "gold",
|
||||
"requirements": ["python-pooldose==0.8.2"]
|
||||
}
|
||||
|
||||
@@ -71,4 +71,4 @@ rules:
|
||||
# Platinum
|
||||
async-dependency: done
|
||||
inject-websession: done
|
||||
strict-typing: done
|
||||
strict-typing: todo
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["prowl"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["prowlpy==1.1.1"]
|
||||
}
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["wsdot"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["wsdot==0.0.1"]
|
||||
}
|
||||
|
||||
10
mypy.ini
generated
10
mypy.ini
generated
@@ -3826,16 +3826,6 @@ disallow_untyped_defs = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.pooldose.*]
|
||||
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.portainer.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
|
||||
@@ -41,20 +41,7 @@ from homeassistant.setup import async_setup_component
|
||||
from tests.common import get_fixture_path
|
||||
|
||||
VALUES = [17, 20, 15.3]
|
||||
|
||||
STATES_ONE_ERROR = ["17", "string", "15.3"]
|
||||
STATES_ONE_MISSING = ["17", None, "15.3"]
|
||||
STATES_ONE_UNKNOWN = ["17", STATE_UNKNOWN, "15.3"]
|
||||
STATES_ONE_UNAVAILABLE = ["17", STATE_UNAVAILABLE, "15.3"]
|
||||
STATES_ALL_ERROR = ["string", "string", "string"]
|
||||
STATES_ALL_MISSING = [None, None, None]
|
||||
STATES_ALL_UNKNOWN = [STATE_UNKNOWN, STATE_UNKNOWN, STATE_UNKNOWN]
|
||||
STATES_ALL_UNAVAILABLE = [STATE_UNAVAILABLE, STATE_UNAVAILABLE, STATE_UNAVAILABLE]
|
||||
STATES_MIX_MISSING_UNAVAILABLE_UNKNOWN = [None, STATE_UNAVAILABLE, STATE_UNKNOWN]
|
||||
STATES_MIX_MISSING_UNAVAILABLE = [None, STATE_UNAVAILABLE, STATE_UNAVAILABLE]
|
||||
STATES_MIX_MISSING_UNKNOWN = [None, STATE_UNKNOWN, STATE_UNKNOWN]
|
||||
STATES_MIX_UNAVAILABLE_UNKNOWN = [STATE_UNAVAILABLE, STATE_UNKNOWN, STATE_UNKNOWN]
|
||||
|
||||
VALUES_ERROR = [17, "string", 15.3]
|
||||
COUNT = len(VALUES)
|
||||
MIN_VALUE = min(VALUES)
|
||||
MAX_VALUE = max(VALUES)
|
||||
@@ -66,18 +53,6 @@ SUM_VALUE = sum(VALUES)
|
||||
PRODUCT_VALUE = prod(VALUES)
|
||||
|
||||
|
||||
def set_or_remove_state(
|
||||
hass: HomeAssistant,
|
||||
entity_id: str,
|
||||
state: str | None,
|
||||
) -> None:
|
||||
"""Set or remove the state of an entity."""
|
||||
if state is None:
|
||||
hass.states.async_remove(entity_id)
|
||||
else:
|
||||
hass.states.async_set(entity_id, state)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("sensor_type", "result", "attributes"),
|
||||
[
|
||||
@@ -115,7 +90,7 @@ async def test_sensors2(
|
||||
for entity_id, value in dict(zip(entity_ids, VALUES, strict=False)).items():
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
str(value),
|
||||
value,
|
||||
{
|
||||
ATTR_DEVICE_CLASS: SensorDeviceClass.VOLUME,
|
||||
ATTR_STATE_CLASS: SensorStateClass.TOTAL,
|
||||
@@ -165,7 +140,7 @@ async def test_sensors_attributes_defined(hass: HomeAssistant) -> None:
|
||||
for entity_id, value in dict(zip(entity_ids, VALUES, strict=False)).items():
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
str(value),
|
||||
value,
|
||||
{
|
||||
ATTR_DEVICE_CLASS: SensorDeviceClass.VOLUME,
|
||||
ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT,
|
||||
@@ -210,7 +185,7 @@ async def test_not_enough_sensor_value(hass: HomeAssistant) -> None:
|
||||
assert state.attributes.get("min_entity_id") is None
|
||||
assert state.attributes.get("max_entity_id") is None
|
||||
|
||||
hass.states.async_set(entity_ids[1], str(VALUES[1]))
|
||||
hass.states.async_set(entity_ids[1], VALUES[1])
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("sensor.test_max")
|
||||
@@ -235,8 +210,8 @@ async def test_not_enough_sensor_value(hass: HomeAssistant) -> None:
|
||||
|
||||
async def test_reload(hass: HomeAssistant) -> None:
|
||||
"""Verify we can reload sensors."""
|
||||
hass.states.async_set("sensor.test_1", "12345")
|
||||
hass.states.async_set("sensor.test_2", "45678")
|
||||
hass.states.async_set("sensor.test_1", 12345)
|
||||
hass.states.async_set("sensor.test_2", 45678)
|
||||
|
||||
await async_setup_component(
|
||||
hass,
|
||||
@@ -274,28 +249,8 @@ async def test_reload(hass: HomeAssistant) -> None:
|
||||
assert hass.states.get("sensor.second_test")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("states_list", "expected_group_state"),
|
||||
[
|
||||
(STATES_ONE_ERROR, "17.0"),
|
||||
(STATES_ONE_MISSING, "17.0"),
|
||||
(STATES_ONE_UNKNOWN, "17.0"),
|
||||
(STATES_ONE_UNAVAILABLE, "17.0"),
|
||||
(STATES_ALL_ERROR, STATE_UNAVAILABLE),
|
||||
(STATES_ALL_MISSING, STATE_UNAVAILABLE),
|
||||
(STATES_ALL_UNKNOWN, STATE_UNAVAILABLE),
|
||||
(STATES_ALL_UNAVAILABLE, STATE_UNAVAILABLE),
|
||||
(STATES_MIX_MISSING_UNAVAILABLE, STATE_UNAVAILABLE),
|
||||
(STATES_MIX_MISSING_UNKNOWN, STATE_UNAVAILABLE),
|
||||
(STATES_MIX_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE),
|
||||
(STATES_MIX_MISSING_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE),
|
||||
],
|
||||
)
|
||||
async def test_sensor_incorrect_state_with_ignore_non_numeric(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
states_list: list[str | None],
|
||||
expected_group_state: str,
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""Test that non numeric values are ignored in a group."""
|
||||
config = {
|
||||
@@ -316,48 +271,27 @@ async def test_sensor_incorrect_state_with_ignore_non_numeric(
|
||||
entity_ids = config["sensor"]["entities"]
|
||||
|
||||
# Check that the final sensor value ignores the non numeric input
|
||||
for entity_id, value in dict(zip(entity_ids, states_list, strict=False)).items():
|
||||
set_or_remove_state(hass, entity_id, value)
|
||||
for entity_id, value in dict(zip(entity_ids, VALUES_ERROR, strict=False)).items():
|
||||
hass.states.async_set(entity_id, value)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("sensor.test_ignore_non_numeric")
|
||||
assert state.state == expected_group_state
|
||||
assert state.state == "17.0"
|
||||
assert (
|
||||
"Unable to use state. Only numerical states are supported," not in caplog.text
|
||||
)
|
||||
|
||||
# Check that the final sensor value with all numeric inputs
|
||||
for entity_id, value in dict(zip(entity_ids, VALUES, strict=False)).items():
|
||||
hass.states.async_set(entity_id, str(value))
|
||||
hass.states.async_set(entity_id, value)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("sensor.test_ignore_non_numeric")
|
||||
assert state.state == "20.0"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("states_list", "expected_group_state", "error_count"),
|
||||
[
|
||||
(STATES_ONE_ERROR, STATE_UNKNOWN, 1),
|
||||
(STATES_ONE_MISSING, "17.0", 0),
|
||||
(STATES_ONE_UNKNOWN, STATE_UNKNOWN, 1),
|
||||
(STATES_ONE_UNAVAILABLE, STATE_UNKNOWN, 1),
|
||||
(STATES_ALL_ERROR, STATE_UNAVAILABLE, 3),
|
||||
(STATES_ALL_MISSING, STATE_UNAVAILABLE, 0),
|
||||
(STATES_ALL_UNKNOWN, STATE_UNAVAILABLE, 3),
|
||||
(STATES_ALL_UNAVAILABLE, STATE_UNAVAILABLE, 3),
|
||||
(STATES_MIX_MISSING_UNKNOWN, STATE_UNAVAILABLE, 2),
|
||||
(STATES_MIX_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE, 3),
|
||||
(STATES_MIX_MISSING_UNAVAILABLE, STATE_UNAVAILABLE, 2),
|
||||
(STATES_MIX_MISSING_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE, 2),
|
||||
],
|
||||
)
|
||||
async def test_sensor_incorrect_state_with_not_ignore_non_numeric(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
states_list: list[str | None],
|
||||
expected_group_state: str,
|
||||
error_count: int,
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""Test that non numeric values cause a group to be unknown."""
|
||||
config = {
|
||||
@@ -378,46 +312,24 @@ async def test_sensor_incorrect_state_with_not_ignore_non_numeric(
|
||||
entity_ids = config["sensor"]["entities"]
|
||||
|
||||
# Check that the final sensor value is unavailable if a non numeric input exists
|
||||
for entity_id, value in dict(zip(entity_ids, states_list, strict=False)).items():
|
||||
set_or_remove_state(hass, entity_id, value)
|
||||
for entity_id, value in dict(zip(entity_ids, VALUES_ERROR, strict=False)).items():
|
||||
hass.states.async_set(entity_id, value)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("sensor.test_failure")
|
||||
assert state.state == expected_group_state
|
||||
assert (
|
||||
caplog.text.count("Unable to use state. Only numerical states are supported")
|
||||
== error_count
|
||||
)
|
||||
assert state.state == "unknown"
|
||||
assert "Unable to use state. Only numerical states are supported" in caplog.text
|
||||
|
||||
# Check that the final sensor value is correct with all numeric inputs
|
||||
for entity_id, value in dict(zip(entity_ids, VALUES, strict=False)).items():
|
||||
hass.states.async_set(entity_id, str(value))
|
||||
hass.states.async_set(entity_id, value)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("sensor.test_failure")
|
||||
assert state.state == "20.0"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("states_list", "expected_group_state"),
|
||||
[
|
||||
(STATES_ONE_ERROR, STATE_UNKNOWN),
|
||||
(STATES_ONE_MISSING, "32.3"),
|
||||
(STATES_ONE_UNKNOWN, STATE_UNKNOWN),
|
||||
(STATES_ONE_UNAVAILABLE, STATE_UNKNOWN),
|
||||
(STATES_ALL_ERROR, STATE_UNAVAILABLE),
|
||||
(STATES_ALL_MISSING, STATE_UNAVAILABLE),
|
||||
(STATES_ALL_UNKNOWN, STATE_UNAVAILABLE),
|
||||
(STATES_ALL_UNAVAILABLE, STATE_UNAVAILABLE),
|
||||
(STATES_MIX_MISSING_UNAVAILABLE, STATE_UNAVAILABLE),
|
||||
(STATES_MIX_MISSING_UNKNOWN, STATE_UNAVAILABLE),
|
||||
(STATES_MIX_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE),
|
||||
(STATES_MIX_MISSING_UNAVAILABLE_UNKNOWN, STATE_UNAVAILABLE),
|
||||
],
|
||||
)
|
||||
async def test_sensor_require_all_states(
|
||||
hass: HomeAssistant, states_list: list[str | None], expected_group_state: str
|
||||
) -> None:
|
||||
async def test_sensor_require_all_states(hass: HomeAssistant) -> None:
|
||||
"""Test the sum sensor with missing state require all."""
|
||||
config = {
|
||||
SENSOR_DOMAIN: {
|
||||
@@ -436,13 +348,13 @@ async def test_sensor_require_all_states(
|
||||
|
||||
entity_ids = config["sensor"]["entities"]
|
||||
|
||||
for entity_id, value in dict(zip(entity_ids, states_list, strict=False)).items():
|
||||
set_or_remove_state(hass, entity_id, value)
|
||||
for entity_id, value in dict(zip(entity_ids, VALUES_ERROR, strict=False)).items():
|
||||
hass.states.async_set(entity_id, value)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("sensor.test_sum")
|
||||
|
||||
assert state.state == expected_group_state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_sensor_calculated_properties(hass: HomeAssistant) -> None:
|
||||
@@ -461,7 +373,7 @@ async def test_sensor_calculated_properties(hass: HomeAssistant) -> None:
|
||||
|
||||
hass.states.async_set(
|
||||
entity_ids[0],
|
||||
str(VALUES[0]),
|
||||
VALUES[0],
|
||||
{
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL,
|
||||
@@ -470,7 +382,7 @@ async def test_sensor_calculated_properties(hass: HomeAssistant) -> None:
|
||||
)
|
||||
hass.states.async_set(
|
||||
entity_ids[1],
|
||||
str(VALUES[1]),
|
||||
VALUES[1],
|
||||
{
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL,
|
||||
@@ -479,7 +391,7 @@ async def test_sensor_calculated_properties(hass: HomeAssistant) -> None:
|
||||
)
|
||||
hass.states.async_set(
|
||||
entity_ids[2],
|
||||
str(VALUES[2]),
|
||||
VALUES[2],
|
||||
{
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL,
|
||||
@@ -501,7 +413,7 @@ async def test_sensor_calculated_properties(hass: HomeAssistant) -> None:
|
||||
# is converted correctly by the group sensor
|
||||
hass.states.async_set(
|
||||
entity_ids[2],
|
||||
str(VALUES[2]),
|
||||
VALUES[2],
|
||||
{
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL,
|
||||
@@ -534,7 +446,7 @@ async def test_sensor_with_uoms_but_no_device_class(
|
||||
|
||||
hass.states.async_set(
|
||||
entity_ids[0],
|
||||
str(VALUES[0]),
|
||||
VALUES[0],
|
||||
{
|
||||
"device_class": SensorDeviceClass.POWER,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
@@ -543,7 +455,7 @@ async def test_sensor_with_uoms_but_no_device_class(
|
||||
)
|
||||
hass.states.async_set(
|
||||
entity_ids[1],
|
||||
str(VALUES[1]),
|
||||
VALUES[1],
|
||||
{
|
||||
"device_class": SensorDeviceClass.POWER,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
@@ -552,7 +464,7 @@ async def test_sensor_with_uoms_but_no_device_class(
|
||||
)
|
||||
hass.states.async_set(
|
||||
entity_ids[2],
|
||||
str(VALUES[2]),
|
||||
VALUES[2],
|
||||
{
|
||||
"unit_of_measurement": "W",
|
||||
},
|
||||
@@ -575,7 +487,7 @@ async def test_sensor_with_uoms_but_no_device_class(
|
||||
|
||||
hass.states.async_set(
|
||||
entity_ids[0],
|
||||
str(VALUES[0]),
|
||||
VALUES[0],
|
||||
{
|
||||
"device_class": SensorDeviceClass.POWER,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
@@ -596,7 +508,7 @@ async def test_sensor_with_uoms_but_no_device_class(
|
||||
|
||||
hass.states.async_set(
|
||||
entity_ids[0],
|
||||
str(VALUES[0]),
|
||||
VALUES[0],
|
||||
{
|
||||
"device_class": SensorDeviceClass.POWER,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
@@ -629,7 +541,7 @@ async def test_sensor_calculated_properties_not_same(
|
||||
|
||||
hass.states.async_set(
|
||||
entity_ids[0],
|
||||
str(VALUES[0]),
|
||||
VALUES[0],
|
||||
{
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL,
|
||||
@@ -638,7 +550,7 @@ async def test_sensor_calculated_properties_not_same(
|
||||
)
|
||||
hass.states.async_set(
|
||||
entity_ids[1],
|
||||
str(VALUES[1]),
|
||||
VALUES[1],
|
||||
{
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL,
|
||||
@@ -647,7 +559,7 @@ async def test_sensor_calculated_properties_not_same(
|
||||
)
|
||||
hass.states.async_set(
|
||||
entity_ids[2],
|
||||
str(VALUES[2]),
|
||||
VALUES[2],
|
||||
{
|
||||
"device_class": SensorDeviceClass.CURRENT,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
@@ -692,7 +604,7 @@ async def test_sensor_calculated_result_fails_on_uom(hass: HomeAssistant) -> Non
|
||||
|
||||
hass.states.async_set(
|
||||
entity_ids[0],
|
||||
str(VALUES[0]),
|
||||
VALUES[0],
|
||||
{
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL,
|
||||
@@ -701,7 +613,7 @@ async def test_sensor_calculated_result_fails_on_uom(hass: HomeAssistant) -> Non
|
||||
)
|
||||
hass.states.async_set(
|
||||
entity_ids[1],
|
||||
str(VALUES[1]),
|
||||
VALUES[1],
|
||||
{
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL,
|
||||
@@ -710,7 +622,7 @@ async def test_sensor_calculated_result_fails_on_uom(hass: HomeAssistant) -> Non
|
||||
)
|
||||
hass.states.async_set(
|
||||
entity_ids[2],
|
||||
str(VALUES[2]),
|
||||
VALUES[2],
|
||||
{
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL,
|
||||
@@ -730,7 +642,7 @@ async def test_sensor_calculated_result_fails_on_uom(hass: HomeAssistant) -> Non
|
||||
|
||||
hass.states.async_set(
|
||||
entity_ids[2],
|
||||
"12",
|
||||
12,
|
||||
{
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL,
|
||||
@@ -765,7 +677,7 @@ async def test_sensor_calculated_properties_not_convertible_device_class(
|
||||
|
||||
hass.states.async_set(
|
||||
entity_ids[0],
|
||||
str(VALUES[0]),
|
||||
VALUES[0],
|
||||
{
|
||||
"device_class": SensorDeviceClass.HUMIDITY,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
@@ -774,7 +686,7 @@ async def test_sensor_calculated_properties_not_convertible_device_class(
|
||||
)
|
||||
hass.states.async_set(
|
||||
entity_ids[1],
|
||||
str(VALUES[1]),
|
||||
VALUES[1],
|
||||
{
|
||||
"device_class": SensorDeviceClass.HUMIDITY,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
@@ -783,7 +695,7 @@ async def test_sensor_calculated_properties_not_convertible_device_class(
|
||||
)
|
||||
hass.states.async_set(
|
||||
entity_ids[2],
|
||||
str(VALUES[2]),
|
||||
VALUES[2],
|
||||
{
|
||||
"device_class": SensorDeviceClass.HUMIDITY,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
@@ -808,7 +720,7 @@ async def test_sensor_calculated_properties_not_convertible_device_class(
|
||||
|
||||
hass.states.async_set(
|
||||
entity_ids[2],
|
||||
str(VALUES[2]),
|
||||
VALUES[2],
|
||||
{
|
||||
"device_class": SensorDeviceClass.HUMIDITY,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
@@ -848,7 +760,7 @@ async def test_last_sensor(hass: HomeAssistant) -> None:
|
||||
entity_ids = config["sensor"]["entities"]
|
||||
|
||||
for entity_id, value in dict(zip(entity_ids, VALUES, strict=False)).items():
|
||||
hass.states.async_set(entity_id, str(value))
|
||||
hass.states.async_set(entity_id, value)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.test_last")
|
||||
assert str(float(value)) == state.state
|
||||
@@ -885,7 +797,7 @@ async def test_sensors_attributes_added_when_entity_info_available(
|
||||
for entity_id, value in dict(zip(entity_ids, VALUES, strict=False)).items():
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
str(value),
|
||||
value,
|
||||
{
|
||||
ATTR_DEVICE_CLASS: SensorDeviceClass.VOLUME,
|
||||
ATTR_STATE_CLASS: SensorStateClass.TOTAL,
|
||||
@@ -931,9 +843,9 @@ async def test_sensor_state_class_no_uom_not_available(
|
||||
"unit_of_measurement": PERCENTAGE,
|
||||
}
|
||||
|
||||
hass.states.async_set(entity_ids[0], str(VALUES[0]), input_attributes)
|
||||
hass.states.async_set(entity_ids[1], str(VALUES[1]), input_attributes)
|
||||
hass.states.async_set(entity_ids[2], str(VALUES[2]), input_attributes)
|
||||
hass.states.async_set(entity_ids[0], VALUES[0], input_attributes)
|
||||
hass.states.async_set(entity_ids[1], VALUES[1], input_attributes)
|
||||
hass.states.async_set(entity_ids[2], VALUES[2], input_attributes)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert await async_setup_component(hass, "sensor", config)
|
||||
@@ -952,7 +864,7 @@ async def test_sensor_state_class_no_uom_not_available(
|
||||
# sensor.test_3 drops the unit of measurement
|
||||
hass.states.async_set(
|
||||
entity_ids[2],
|
||||
str(VALUES[2]),
|
||||
VALUES[2],
|
||||
{
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
},
|
||||
@@ -1002,7 +914,7 @@ async def test_sensor_different_attributes_ignore_non_numeric(
|
||||
test_cases = [
|
||||
{
|
||||
"entity": entity_ids[0],
|
||||
"value": str(VALUES[0]),
|
||||
"value": VALUES[0],
|
||||
"attributes": {
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
"unit_of_measurement": PERCENTAGE,
|
||||
@@ -1014,7 +926,7 @@ async def test_sensor_different_attributes_ignore_non_numeric(
|
||||
},
|
||||
{
|
||||
"entity": entity_ids[1],
|
||||
"value": str(VALUES[1]),
|
||||
"value": VALUES[1],
|
||||
"attributes": {
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
"device_class": SensorDeviceClass.HUMIDITY,
|
||||
@@ -1027,7 +939,7 @@ async def test_sensor_different_attributes_ignore_non_numeric(
|
||||
},
|
||||
{
|
||||
"entity": entity_ids[2],
|
||||
"value": str(VALUES[2]),
|
||||
"value": VALUES[2],
|
||||
"attributes": {
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
"device_class": SensorDeviceClass.TEMPERATURE,
|
||||
@@ -1040,7 +952,7 @@ async def test_sensor_different_attributes_ignore_non_numeric(
|
||||
},
|
||||
{
|
||||
"entity": entity_ids[2],
|
||||
"value": str(VALUES[2]),
|
||||
"value": VALUES[2],
|
||||
"attributes": {
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
"device_class": SensorDeviceClass.HUMIDITY,
|
||||
@@ -1054,7 +966,7 @@ async def test_sensor_different_attributes_ignore_non_numeric(
|
||||
},
|
||||
{
|
||||
"entity": entity_ids[0],
|
||||
"value": str(VALUES[0]),
|
||||
"value": VALUES[0],
|
||||
"attributes": {
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
"device_class": SensorDeviceClass.HUMIDITY,
|
||||
@@ -1068,7 +980,7 @@ async def test_sensor_different_attributes_ignore_non_numeric(
|
||||
},
|
||||
{
|
||||
"entity": entity_ids[0],
|
||||
"value": str(VALUES[0]),
|
||||
"value": VALUES[0],
|
||||
"attributes": {
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
},
|
||||
|
||||
@@ -78,6 +78,11 @@ async def test_expose_attribute(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
await hass.async_block_till_done()
|
||||
await knx.assert_write("1/1/8", (1,))
|
||||
|
||||
# Change attribute below resolution of DPT; expect no telegram
|
||||
hass.states.async_set(entity_id, "on", {attribute: 1.2})
|
||||
await hass.async_block_till_done()
|
||||
await knx.assert_no_telegram()
|
||||
|
||||
# Read in between
|
||||
await knx.receive_read("1/1/8")
|
||||
await knx.assert_response("1/1/8", (1,))
|
||||
@@ -251,6 +256,32 @@ async def test_expose_cooldown(
|
||||
await knx.assert_write("1/1/8", (3,))
|
||||
|
||||
|
||||
async def test_expose_periodic_send(
|
||||
hass: HomeAssistant, knx: KNXTestKit, freezer: FrozenDateTimeFactory
|
||||
) -> None:
|
||||
"""Test an expose with periodic send."""
|
||||
entity_id = "fake.entity"
|
||||
await knx.setup_integration(
|
||||
{
|
||||
CONF_KNX_EXPOSE: {
|
||||
CONF_TYPE: "percentU8",
|
||||
KNX_ADDRESS: "1/1/8",
|
||||
CONF_ENTITY_ID: entity_id,
|
||||
ExposeSchema.CONF_KNX_EXPOSE_PERIODIC_SEND: {"minutes": 1},
|
||||
}
|
||||
},
|
||||
)
|
||||
# Initialize state
|
||||
hass.states.async_set(entity_id, "15", {})
|
||||
await hass.async_block_till_done()
|
||||
await knx.assert_write("1/1/8", (15,))
|
||||
# Wait for time to pass
|
||||
freezer.tick(timedelta(seconds=60))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
await knx.assert_write("1/1/8", (15,))
|
||||
|
||||
|
||||
async def test_expose_value_template(
|
||||
hass: HomeAssistant, knx: KNXTestKit, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
|
||||
@@ -439,6 +439,54 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[eve_thermo_v4][binary_sensor.eve_thermo_20ebp1701_outdoor_temperature_remote_sensing-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.eve_thermo_20ebp1701_outdoor_temperature_remote_sensing',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Outdoor temperature remote sensing',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'thermostat_remote_sensing_outdoor_temperature',
|
||||
'unique_id': '00000000000004D2-0000000000000021-MatterNodeDevice-1-ThermostatRemoteSensing_OutdoorTemperature-513-26',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[eve_thermo_v4][binary_sensor.eve_thermo_20ebp1701_outdoor_temperature_remote_sensing-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Eve Thermo 20EBP1701 Outdoor temperature remote sensing',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.eve_thermo_20ebp1701_outdoor_temperature_remote_sensing',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[eve_thermo_v5][binary_sensor.eve_thermo_20ecd1701_local_temperature_remote_sensing-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -487,6 +535,54 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[eve_thermo_v5][binary_sensor.eve_thermo_20ecd1701_outdoor_temperature_remote_sensing-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.eve_thermo_20ecd1701_outdoor_temperature_remote_sensing',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Outdoor temperature remote sensing',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'thermostat_remote_sensing_outdoor_temperature',
|
||||
'unique_id': '00000000000004D2-000000000000000C-MatterNodeDevice-1-ThermostatRemoteSensing_OutdoorTemperature-513-26',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[eve_thermo_v5][binary_sensor.eve_thermo_20ecd1701_outdoor_temperature_remote_sensing-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Eve Thermo 20ECD1701 Outdoor temperature remote sensing',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.eve_thermo_20ecd1701_outdoor_temperature_remote_sensing',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[heiman_motion_sensor_m1][binary_sensor.smart_motion_sensor_occupancy-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -7,7 +7,6 @@ import pytest
|
||||
|
||||
from homeassistant.components.namecheapdns.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
|
||||
from homeassistant.const import CONF_PASSWORD
|
||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
@@ -15,8 +14,6 @@ from homeassistant.setup import async_setup_component
|
||||
|
||||
from .conftest import TEST_USER_INPUT
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_namecheap")
|
||||
async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
||||
@@ -143,68 +140,3 @@ async def test_init_import_flow(
|
||||
)
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_namecheap")
|
||||
async def test_reconfigure(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test reconfigure flow."""
|
||||
config_entry.add_to_hass(hass)
|
||||
result = await config_entry.start_reconfigure_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reconfigure"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_PASSWORD: "new-password"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
assert config_entry.data[CONF_PASSWORD] == "new-password"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("side_effect", "text_error"),
|
||||
[
|
||||
(ValueError, "unknown"),
|
||||
(False, "update_failed"),
|
||||
(ClientError, "cannot_connect"),
|
||||
],
|
||||
)
|
||||
async def test_reconfigure_errors(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
mock_namecheap: AsyncMock,
|
||||
side_effect: Exception | bool,
|
||||
text_error: str,
|
||||
) -> None:
|
||||
"""Test we handle errors."""
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
result = await config_entry.start_reconfigure_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reconfigure"
|
||||
|
||||
mock_namecheap.side_effect = [side_effect]
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_PASSWORD: "new-password"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": text_error}
|
||||
|
||||
mock_namecheap.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_PASSWORD: "new-password"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
|
||||
assert config_entry.data[CONF_PASSWORD] == "new-password"
|
||||
|
||||
Reference in New Issue
Block a user