mirror of
https://github.com/home-assistant/core.git
synced 2025-11-25 10:37:59 +00:00
Compare commits
1 Commits
area_regis
...
sql_adjust
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c0df09dc9 |
@@ -9,5 +9,5 @@
|
||||
},
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["apyhiveapi"],
|
||||
"requirements": ["pyhive-integration==1.0.7"]
|
||||
"requirements": ["pyhive-integration==1.0.6"]
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ from typing import Any
|
||||
from aiohttp import web
|
||||
from hyperion import client
|
||||
from hyperion.const import (
|
||||
KEY_DATA,
|
||||
KEY_IMAGE,
|
||||
KEY_IMAGE_STREAM,
|
||||
KEY_LEDCOLORS,
|
||||
@@ -156,8 +155,7 @@ class HyperionCamera(Camera):
|
||||
"""Update Hyperion components."""
|
||||
if not img:
|
||||
return
|
||||
# Prefer KEY_DATA (Hyperion server >= 2.1.1); fall back to KEY_RESULT for older server versions
|
||||
img_data = img.get(KEY_DATA, img.get(KEY_RESULT, {})).get(KEY_IMAGE)
|
||||
img_data = img.get(KEY_RESULT, {}).get(KEY_IMAGE)
|
||||
if not img_data or not img_data.startswith(IMAGE_STREAM_JPG_SENTINEL):
|
||||
return
|
||||
async with self._image_cond:
|
||||
|
||||
@@ -19,13 +19,11 @@ from homeassistant.core import (
|
||||
)
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.trigger_template_entity import ValueTemplate
|
||||
from homeassistant.util.json import JsonValueType
|
||||
|
||||
from .const import CONF_QUERY, DOMAIN
|
||||
from .util import (
|
||||
async_create_sessionmaker,
|
||||
check_and_render_sql_query,
|
||||
convert_value,
|
||||
generate_lambda_stmt,
|
||||
redact_credentials,
|
||||
@@ -39,9 +37,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
SERVICE_QUERY = "query"
|
||||
SERVICE_QUERY_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_QUERY): vol.All(
|
||||
cv.template, ValueTemplate.from_template, validate_sql_select
|
||||
),
|
||||
vol.Required(CONF_QUERY): vol.All(cv.string, validate_sql_select),
|
||||
vol.Optional(CONF_DB_URL): cv.string,
|
||||
}
|
||||
)
|
||||
@@ -76,9 +72,8 @@ async def _async_query_service(
|
||||
def _execute_and_convert_query() -> list[JsonValueType]:
|
||||
"""Execute the query and return the results with converted types."""
|
||||
sess: Session = sessmaker()
|
||||
rendered_query = check_and_render_sql_query(call.hass, query_str)
|
||||
try:
|
||||
result: Result = sess.execute(generate_lambda_stmt(rendered_query))
|
||||
result: Result = sess.execute(generate_lambda_stmt(query_str))
|
||||
except SQLAlchemyError as err:
|
||||
_LOGGER.debug(
|
||||
"Error executing query %s: %s",
|
||||
|
||||
@@ -18,7 +18,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components.recorder import SupportedDialect, get_instance
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.core import Event, HomeAssistant, async_get_hass, callback
|
||||
from homeassistant.exceptions import HomeAssistantError, TemplateError
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.template import Template
|
||||
@@ -46,11 +46,15 @@ def resolve_db_url(hass: HomeAssistant, db_url: str | None) -> str:
|
||||
return get_instance(hass).db_url
|
||||
|
||||
|
||||
def validate_sql_select(value: Template) -> Template:
|
||||
def validate_sql_select(value: Template | str) -> Template | str:
|
||||
"""Validate that value is a SQL SELECT query."""
|
||||
hass: HomeAssistant
|
||||
if isinstance(value, str):
|
||||
hass = async_get_hass()
|
||||
else:
|
||||
hass = value.hass # type: ignore[assignment]
|
||||
try:
|
||||
assert value.hass
|
||||
check_and_render_sql_query(value.hass, value)
|
||||
check_and_render_sql_query(hass, value)
|
||||
except (TemplateError, InvalidSqlQuery) as err:
|
||||
raise vol.Invalid(str(err)) from err
|
||||
return value
|
||||
|
||||
@@ -75,7 +75,6 @@ PLATFORMS_BY_TYPE = {
|
||||
SupportedModels.HUBMINI_MATTER.value: [Platform.SENSOR],
|
||||
SupportedModels.CIRCULATOR_FAN.value: [Platform.FAN, Platform.SENSOR],
|
||||
SupportedModels.S10_VACUUM.value: [Platform.VACUUM, Platform.SENSOR],
|
||||
SupportedModels.S20_VACUUM.value: [Platform.VACUUM, Platform.SENSOR],
|
||||
SupportedModels.K10_VACUUM.value: [Platform.VACUUM, Platform.SENSOR],
|
||||
SupportedModels.K10_PRO_VACUUM.value: [Platform.VACUUM, Platform.SENSOR],
|
||||
SupportedModels.K10_PRO_COMBO_VACUUM.value: [Platform.VACUUM, Platform.SENSOR],
|
||||
@@ -103,10 +102,6 @@ PLATFORMS_BY_TYPE = {
|
||||
SupportedModels.RELAY_SWITCH_2PM.value: [Platform.SWITCH, Platform.SENSOR],
|
||||
SupportedModels.GARAGE_DOOR_OPENER.value: [Platform.COVER, Platform.SENSOR],
|
||||
SupportedModels.CLIMATE_PANEL.value: [Platform.SENSOR, Platform.BINARY_SENSOR],
|
||||
SupportedModels.SMART_THERMOSTAT_RADIATOR.value: [
|
||||
Platform.CLIMATE,
|
||||
Platform.SENSOR,
|
||||
],
|
||||
}
|
||||
CLASS_BY_DEVICE = {
|
||||
SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight,
|
||||
@@ -124,7 +119,6 @@ CLASS_BY_DEVICE = {
|
||||
SupportedModels.ROLLER_SHADE.value: switchbot.SwitchbotRollerShade,
|
||||
SupportedModels.CIRCULATOR_FAN.value: switchbot.SwitchbotFan,
|
||||
SupportedModels.S10_VACUUM.value: switchbot.SwitchbotVacuum,
|
||||
SupportedModels.S20_VACUUM.value: switchbot.SwitchbotVacuum,
|
||||
SupportedModels.K10_VACUUM.value: switchbot.SwitchbotVacuum,
|
||||
SupportedModels.K10_PRO_VACUUM.value: switchbot.SwitchbotVacuum,
|
||||
SupportedModels.K10_PRO_COMBO_VACUUM.value: switchbot.SwitchbotVacuum,
|
||||
@@ -142,7 +136,6 @@ CLASS_BY_DEVICE = {
|
||||
SupportedModels.PLUG_MINI_EU.value: switchbot.SwitchbotRelaySwitch,
|
||||
SupportedModels.RELAY_SWITCH_2PM.value: switchbot.SwitchbotRelaySwitch2PM,
|
||||
SupportedModels.GARAGE_DOOR_OPENER.value: switchbot.SwitchbotGarageDoorOpener,
|
||||
SupportedModels.SMART_THERMOSTAT_RADIATOR.value: switchbot.SwitchbotSmartThermostatRadiator,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
"""Support for Switchbot Climate devices."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import switchbot
|
||||
from switchbot import (
|
||||
ClimateAction as SwitchBotClimateAction,
|
||||
ClimateMode as SwitchBotClimateMode,
|
||||
)
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
ClimateEntity,
|
||||
ClimateEntityFeature,
|
||||
HVACAction,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .coordinator import SwitchbotConfigEntry
|
||||
from .entity import SwitchbotEntity, exception_handler
|
||||
|
||||
SWITCHBOT_CLIMATE_TO_HASS_HVAC_MODE = {
|
||||
SwitchBotClimateMode.HEAT: HVACMode.HEAT,
|
||||
SwitchBotClimateMode.OFF: HVACMode.OFF,
|
||||
}
|
||||
|
||||
HASS_HVAC_MODE_TO_SWITCHBOT_CLIMATE = {
|
||||
HVACMode.HEAT: SwitchBotClimateMode.HEAT,
|
||||
HVACMode.OFF: SwitchBotClimateMode.OFF,
|
||||
}
|
||||
|
||||
SWITCHBOT_ACTION_TO_HASS_HVAC_ACTION = {
|
||||
SwitchBotClimateAction.HEATING: HVACAction.HEATING,
|
||||
SwitchBotClimateAction.IDLE: HVACAction.IDLE,
|
||||
SwitchBotClimateAction.OFF: HVACAction.OFF,
|
||||
}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchbotConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Switchbot climate based on a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
async_add_entities([SwitchBotClimateEntity(coordinator)])
|
||||
|
||||
|
||||
class SwitchBotClimateEntity(SwitchbotEntity, ClimateEntity):
|
||||
"""Representation of a Switchbot Climate device."""
|
||||
|
||||
_device: switchbot.SwitchbotDevice
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.PRESET_MODE
|
||||
| ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_target_temperature_step = 0.5
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_translation_key = "climate"
|
||||
_attr_name = None
|
||||
|
||||
@property
|
||||
def min_temp(self) -> float:
|
||||
"""Return the minimum temperature."""
|
||||
return self._device.min_temperature
|
||||
|
||||
@property
|
||||
def max_temp(self) -> float:
|
||||
"""Return the maximum temperature."""
|
||||
return self._device.max_temperature
|
||||
|
||||
@property
|
||||
def preset_modes(self) -> list[str] | None:
|
||||
"""Return the list of available preset modes."""
|
||||
return self._device.preset_modes
|
||||
|
||||
@property
|
||||
def preset_mode(self) -> str | None:
|
||||
"""Return the current preset mode."""
|
||||
return self._device.preset_mode
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode | None:
|
||||
"""Return the current HVAC mode."""
|
||||
return SWITCHBOT_CLIMATE_TO_HASS_HVAC_MODE.get(
|
||||
self._device.hvac_mode, HVACMode.OFF
|
||||
)
|
||||
|
||||
@property
|
||||
def hvac_modes(self) -> list[HVACMode]:
|
||||
"""Return the list of available HVAC modes."""
|
||||
return [
|
||||
SWITCHBOT_CLIMATE_TO_HASS_HVAC_MODE[mode]
|
||||
for mode in self._device.hvac_modes
|
||||
]
|
||||
|
||||
@property
|
||||
def hvac_action(self) -> HVACAction | None:
|
||||
"""Return the current HVAC action."""
|
||||
return SWITCHBOT_ACTION_TO_HASS_HVAC_ACTION.get(
|
||||
self._device.hvac_action, HVACAction.OFF
|
||||
)
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float | None:
|
||||
"""Return the current temperature."""
|
||||
return self._device.current_temperature
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float | None:
|
||||
"""Return the temperature we try to reach."""
|
||||
return self._device.target_temperature
|
||||
|
||||
@exception_handler
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set new HVAC mode."""
|
||||
return await self._device.set_hvac_mode(
|
||||
HASS_HVAC_MODE_TO_SWITCHBOT_CLIMATE[hvac_mode]
|
||||
)
|
||||
|
||||
@exception_handler
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set new preset mode."""
|
||||
return await self._device.set_preset_mode(preset_mode)
|
||||
|
||||
@exception_handler
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
temperature = kwargs.get(ATTR_TEMPERATURE)
|
||||
return await self._device.set_target_temperature(temperature)
|
||||
@@ -58,8 +58,6 @@ class SupportedModels(StrEnum):
|
||||
K11_PLUS_VACUUM = "k11+_vacuum"
|
||||
GARAGE_DOOR_OPENER = "garage_door_opener"
|
||||
CLIMATE_PANEL = "climate_panel"
|
||||
SMART_THERMOSTAT_RADIATOR = "smart_thermostat_radiator"
|
||||
S20_VACUUM = "s20_vacuum"
|
||||
|
||||
|
||||
CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
||||
@@ -80,7 +78,6 @@ CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
||||
SwitchbotModel.CIRCULATOR_FAN: SupportedModels.CIRCULATOR_FAN,
|
||||
SwitchbotModel.K20_VACUUM: SupportedModels.K20_VACUUM,
|
||||
SwitchbotModel.S10_VACUUM: SupportedModels.S10_VACUUM,
|
||||
SwitchbotModel.S20_VACUUM: SupportedModels.S20_VACUUM,
|
||||
SwitchbotModel.K10_VACUUM: SupportedModels.K10_VACUUM,
|
||||
SwitchbotModel.K10_PRO_VACUUM: SupportedModels.K10_PRO_VACUUM,
|
||||
SwitchbotModel.K10_PRO_COMBO_VACUUM: SupportedModels.K10_PRO_COMBO_VACUUM,
|
||||
@@ -98,7 +95,6 @@ CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
||||
SwitchbotModel.K11_VACUUM: SupportedModels.K11_PLUS_VACUUM,
|
||||
SwitchbotModel.GARAGE_DOOR_OPENER: SupportedModels.GARAGE_DOOR_OPENER,
|
||||
SwitchbotModel.CLIMATE_PANEL: SupportedModels.CLIMATE_PANEL,
|
||||
SwitchbotModel.SMART_THERMOSTAT_RADIATOR: SupportedModels.SMART_THERMOSTAT_RADIATOR,
|
||||
}
|
||||
|
||||
NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
||||
@@ -136,7 +132,6 @@ ENCRYPTED_MODELS = {
|
||||
SwitchbotModel.PLUG_MINI_EU,
|
||||
SwitchbotModel.RELAY_SWITCH_2PM,
|
||||
SwitchbotModel.GARAGE_DOOR_OPENER,
|
||||
SwitchbotModel.SMART_THERMOSTAT_RADIATOR,
|
||||
}
|
||||
|
||||
ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[
|
||||
@@ -158,7 +153,6 @@ ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[
|
||||
SwitchbotModel.PLUG_MINI_EU: switchbot.SwitchbotRelaySwitch,
|
||||
SwitchbotModel.RELAY_SWITCH_2PM: switchbot.SwitchbotRelaySwitch2PM,
|
||||
SwitchbotModel.GARAGE_DOOR_OPENER: switchbot.SwitchbotRelaySwitch,
|
||||
SwitchbotModel.SMART_THERMOSTAT_RADIATOR: switchbot.SwitchbotSmartThermostatRadiator,
|
||||
}
|
||||
|
||||
HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL = {
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
{
|
||||
"entity": {
|
||||
"climate": {
|
||||
"climate": {
|
||||
"state_attributes": {
|
||||
"preset_mode": {
|
||||
"state": {
|
||||
"manual": "mdi:hand-back-right",
|
||||
"off": "mdi:hvac-off",
|
||||
"schedule": "mdi:calendar-clock"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fan": {
|
||||
"air_purifier": {
|
||||
"default": "mdi:air-purifier",
|
||||
|
||||
@@ -100,19 +100,6 @@
|
||||
"name": "Unlocked alarm"
|
||||
}
|
||||
},
|
||||
"climate": {
|
||||
"climate": {
|
||||
"state_attributes": {
|
||||
"preset_mode": {
|
||||
"state": {
|
||||
"manual": "[%key:common::state::manual%]",
|
||||
"off": "[%key:common::state::off%]",
|
||||
"schedule": "Schedule"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cover": {
|
||||
"cover": {
|
||||
"state_attributes": {
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
"""Support for VELUX KLF 200 devices."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pyvlx import PyVLX, PyVLXException
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from .const import DOMAIN, LOGGER, PLATFORMS
|
||||
|
||||
type VeluxConfigEntry = ConfigEntry[PyVLX]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: VeluxConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up the velux component."""
|
||||
host = entry.data[CONF_HOST]
|
||||
password = entry.data[CONF_PASSWORD]
|
||||
@@ -30,21 +27,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: VeluxConfigEntry) -> boo
|
||||
|
||||
entry.runtime_data = pyvlx
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, f"gateway_{entry.entry_id}")},
|
||||
name="KLF 200 Gateway",
|
||||
manufacturer="Velux",
|
||||
model="KLF 200",
|
||||
hw_version=(
|
||||
str(pyvlx.klf200.version.hardwareversion) if pyvlx.klf200.version else None
|
||||
),
|
||||
sw_version=(
|
||||
str(pyvlx.klf200.version.softwareversion) if pyvlx.klf200.version else None
|
||||
),
|
||||
)
|
||||
|
||||
async def on_hass_stop(event):
|
||||
"""Close connection when hass stops."""
|
||||
LOGGER.debug("Velux interface terminated")
|
||||
@@ -64,6 +46,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: VeluxConfigEntry) -> boo
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: VeluxConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -24,14 +24,14 @@ SCAN_INTERVAL = timedelta(minutes=5) # Use standard polling
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: VeluxConfigEntry,
|
||||
config: VeluxConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up rain sensor(s) for Velux platform."""
|
||||
pyvlx = config_entry.runtime_data
|
||||
pyvlx = config.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
VeluxRainSensor(node, config_entry.entry_id)
|
||||
VeluxRainSensor(node, config.entry_id)
|
||||
for node in pyvlx.nodes
|
||||
if isinstance(node, Window) and node.rain_sensor
|
||||
)
|
||||
|
||||
@@ -32,13 +32,13 @@ PARALLEL_UPDATES = 1
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: VeluxConfigEntry,
|
||||
config: VeluxConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up cover(s) for Velux platform."""
|
||||
pyvlx = config_entry.runtime_data
|
||||
pyvlx = config.runtime_data
|
||||
async_add_entities(
|
||||
VeluxCover(node, config_entry.entry_id)
|
||||
VeluxCover(node, config.entry_id)
|
||||
for node in pyvlx.nodes
|
||||
if isinstance(node, OpeningDevice)
|
||||
)
|
||||
|
||||
@@ -18,23 +18,22 @@ class VeluxEntity(Entity):
|
||||
def __init__(self, node: Node, config_entry_id: str) -> None:
|
||||
"""Initialize the Velux device."""
|
||||
self.node = node
|
||||
unique_id = (
|
||||
self._attr_unique_id = (
|
||||
node.serial_number
|
||||
if node.serial_number
|
||||
else f"{config_entry_id}_{node.node_id}"
|
||||
)
|
||||
self._attr_unique_id = unique_id
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={
|
||||
(
|
||||
DOMAIN,
|
||||
unique_id,
|
||||
node.serial_number
|
||||
if node.serial_number
|
||||
else f"{config_entry_id}_{node.node_id}",
|
||||
)
|
||||
},
|
||||
name=node.name if node.name else f"#{node.node_id}",
|
||||
serial_number=node.serial_number,
|
||||
via_device=(DOMAIN, f"gateway_{config_entry_id}"),
|
||||
)
|
||||
|
||||
@callback
|
||||
|
||||
@@ -18,13 +18,13 @@ PARALLEL_UPDATES = 1
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: VeluxConfigEntry,
|
||||
config: VeluxConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up light(s) for Velux platform."""
|
||||
pyvlx = config_entry.runtime_data
|
||||
pyvlx = config.runtime_data
|
||||
async_add_entities(
|
||||
VeluxLight(node, config_entry.entry_id)
|
||||
VeluxLight(node, config.entry_id)
|
||||
for node in pyvlx.nodes
|
||||
if isinstance(node, LighteningDevice)
|
||||
)
|
||||
|
||||
@@ -15,11 +15,11 @@ PARALLEL_UPDATES = 1
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: VeluxConfigEntry,
|
||||
config: VeluxConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the scenes for Velux platform."""
|
||||
pyvlx = config_entry.runtime_data
|
||||
pyvlx = config.runtime_data
|
||||
|
||||
entities = [VeluxScene(scene) for scene in pyvlx.scenes]
|
||||
async_add_entities(entities)
|
||||
|
||||
@@ -58,7 +58,6 @@ from .utils import (
|
||||
get_compressors,
|
||||
get_device_serial,
|
||||
is_supported,
|
||||
normalize_state,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -1087,7 +1086,7 @@ COMPRESSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
|
||||
ViCareSensorEntityDescription(
|
||||
key="compressor_phase",
|
||||
translation_key="compressor_phase",
|
||||
value_getter=lambda api: normalize_state(api.getPhase()),
|
||||
value_getter=lambda api: api.getPhase(),
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -213,18 +213,7 @@
|
||||
"name": "Compressor hours load class 5"
|
||||
},
|
||||
"compressor_phase": {
|
||||
"name": "Compressor phase",
|
||||
"state": {
|
||||
"cooling": "[%key:component::climate::entity_component::_::state_attributes::hvac_action::state::cooling%]",
|
||||
"defrost": "[%key:component::climate::entity_component::_::state_attributes::hvac_action::state::defrosting%]",
|
||||
"heating": "[%key:component::climate::entity_component::_::state_attributes::hvac_action::state::heating%]",
|
||||
"off": "[%key:common::state::off%]",
|
||||
"passive_defrost": "Passive defrosting",
|
||||
"pause": "[%key:common::state::idle%]",
|
||||
"preparing": "Preparing",
|
||||
"preparing_defrost": "Preparing defrost",
|
||||
"ready": "[%key:common::state::idle%]"
|
||||
}
|
||||
"name": "Compressor phase"
|
||||
},
|
||||
"compressor_starts": {
|
||||
"name": "Compressor starts"
|
||||
|
||||
@@ -133,8 +133,3 @@ def get_compressors(device: PyViCareDevice) -> list[PyViCareHeatingDeviceCompone
|
||||
def filter_state(state: str) -> str | None:
|
||||
"""Return the state if not 'nothing' or 'unknown'."""
|
||||
return None if state in ("nothing", "unknown") else state
|
||||
|
||||
|
||||
def normalize_state(state: str) -> str:
|
||||
"""Return the state with underscores instead of hyphens."""
|
||||
return state.replace("-", "_")
|
||||
|
||||
@@ -76,12 +76,12 @@ class EventAreaRegistryUpdatedData(TypedDict):
|
||||
class AreaEntry(NormalizedNameBaseRegistryEntry):
|
||||
"""Area Registry Entry."""
|
||||
|
||||
aliases: frozenset[str]
|
||||
aliases: set[str]
|
||||
floor_id: str | None
|
||||
humidity_entity_id: str | None
|
||||
icon: str | None
|
||||
id: str
|
||||
labels: frozenset[str] = field(default_factory=frozenset)
|
||||
labels: set[str] = field(default_factory=set)
|
||||
picture: str | None
|
||||
temperature_entity_id: str | None
|
||||
_cache: dict[str, Any] = field(default_factory=dict, compare=False, init=False)
|
||||
@@ -295,12 +295,12 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
|
||||
_validate_temperature_entity(self.hass, temperature_entity_id)
|
||||
|
||||
area = AreaEntry(
|
||||
aliases=frozenset(aliases) if aliases else frozenset(),
|
||||
aliases=aliases or set(),
|
||||
floor_id=floor_id,
|
||||
humidity_entity_id=humidity_entity_id,
|
||||
icon=icon,
|
||||
id=self._generate_id(name),
|
||||
labels=frozenset(labels) if labels else frozenset(),
|
||||
labels=labels or set(),
|
||||
name=name,
|
||||
picture=picture,
|
||||
temperature_entity_id=temperature_entity_id,
|
||||
@@ -338,11 +338,11 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
|
||||
self,
|
||||
area_id: str,
|
||||
*,
|
||||
aliases: frozenset[str] | set[str] | UndefinedType = UNDEFINED,
|
||||
aliases: set[str] | UndefinedType = UNDEFINED,
|
||||
floor_id: str | None | UndefinedType = UNDEFINED,
|
||||
humidity_entity_id: str | None | UndefinedType = UNDEFINED,
|
||||
icon: str | None | UndefinedType = UNDEFINED,
|
||||
labels: frozenset[str] | set[str] | UndefinedType = UNDEFINED,
|
||||
labels: set[str] | UndefinedType = UNDEFINED,
|
||||
name: str | UndefinedType = UNDEFINED,
|
||||
picture: str | None | UndefinedType = UNDEFINED,
|
||||
temperature_entity_id: str | None | UndefinedType = UNDEFINED,
|
||||
@@ -374,11 +374,11 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
|
||||
self,
|
||||
area_id: str,
|
||||
*,
|
||||
aliases: frozenset[str] | set[str] | UndefinedType = UNDEFINED,
|
||||
aliases: set[str] | UndefinedType = UNDEFINED,
|
||||
floor_id: str | None | UndefinedType = UNDEFINED,
|
||||
humidity_entity_id: str | None | UndefinedType = UNDEFINED,
|
||||
icon: str | None | UndefinedType = UNDEFINED,
|
||||
labels: frozenset[str] | set[str] | UndefinedType = UNDEFINED,
|
||||
labels: set[str] | UndefinedType = UNDEFINED,
|
||||
name: str | UndefinedType = UNDEFINED,
|
||||
picture: str | None | UndefinedType = UNDEFINED,
|
||||
temperature_entity_id: str | None | UndefinedType = UNDEFINED,
|
||||
@@ -389,23 +389,17 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
|
||||
new_values: dict[str, Any] = {
|
||||
attr_name: value
|
||||
for attr_name, value in (
|
||||
("aliases", aliases),
|
||||
("floor_id", floor_id),
|
||||
("humidity_entity_id", humidity_entity_id),
|
||||
("icon", icon),
|
||||
("labels", labels),
|
||||
("picture", picture),
|
||||
("temperature_entity_id", temperature_entity_id),
|
||||
)
|
||||
if value is not UNDEFINED and value != getattr(old, attr_name)
|
||||
}
|
||||
|
||||
for attr_name, value in (
|
||||
("aliases", aliases),
|
||||
("labels", labels),
|
||||
):
|
||||
if value is UNDEFINED or value == getattr(old, attr_name):
|
||||
continue
|
||||
new_values[attr_name] = frozenset(value)
|
||||
|
||||
if "humidity_entity_id" in new_values and humidity_entity_id is not None:
|
||||
_validate_humidity_entity(self.hass, new_values["humidity_entity_id"])
|
||||
|
||||
@@ -438,12 +432,12 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
|
||||
for area in data["areas"]:
|
||||
assert area["name"] is not None and area["id"] is not None
|
||||
areas[area["id"]] = AreaEntry(
|
||||
aliases=frozenset(area["aliases"]),
|
||||
aliases=set(area["aliases"]),
|
||||
floor_id=area["floor_id"],
|
||||
humidity_entity_id=area["humidity_entity_id"],
|
||||
icon=area["icon"],
|
||||
id=area["id"],
|
||||
labels=frozenset(area["labels"]),
|
||||
labels=set(area["labels"]),
|
||||
name=area["name"],
|
||||
picture=area["picture"],
|
||||
temperature_entity_id=area["temperature_entity_id"],
|
||||
|
||||
2
requirements_all.txt
generated
2
requirements_all.txt
generated
@@ -2050,7 +2050,7 @@ pyhaversion==22.8.0
|
||||
pyheos==1.0.6
|
||||
|
||||
# homeassistant.components.hive
|
||||
pyhive-integration==1.0.7
|
||||
pyhive-integration==1.0.6
|
||||
|
||||
# homeassistant.components.homematic
|
||||
pyhomematic==0.1.77
|
||||
|
||||
@@ -32,7 +32,7 @@ pytest-timeout==2.4.0
|
||||
pytest-unordered==0.7.0
|
||||
pytest-picked==0.5.1
|
||||
pytest-xdist==3.8.0
|
||||
pytest==9.0.0
|
||||
pytest==8.4.2
|
||||
requests-mock==1.12.1
|
||||
respx==0.22.0
|
||||
syrupy==5.0.0
|
||||
|
||||
2
requirements_test_all.txt
generated
2
requirements_test_all.txt
generated
@@ -1709,7 +1709,7 @@ pyhaversion==22.8.0
|
||||
pyheos==1.0.6
|
||||
|
||||
# homeassistant.components.hive
|
||||
pyhive-integration==1.0.7
|
||||
pyhive-integration==1.0.6
|
||||
|
||||
# homeassistant.components.homematic
|
||||
pyhomematic==0.1.77
|
||||
|
||||
@@ -6,11 +6,9 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
from homeassistant.helpers import aiohttp_client, config_entry_oauth2_flow
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
ImplementationUnavailableError,
|
||||
OAuth2Session,
|
||||
async_get_config_entry_implementation,
|
||||
)
|
||||
|
||||
from . import api
|
||||
@@ -28,13 +26,17 @@ type New_NameConfigEntry = ConfigEntry[api.AsyncConfigEntryAuth]
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: New_NameConfigEntry) -> bool:
|
||||
"""Set up NEW_NAME from a config entry."""
|
||||
try:
|
||||
implementation = await async_get_config_entry_implementation(hass, entry)
|
||||
implementation = (
|
||||
await config_entry_oauth2_flow.async_get_config_entry_implementation(
|
||||
hass, entry
|
||||
)
|
||||
)
|
||||
except ImplementationUnavailableError as err:
|
||||
raise ConfigEntryNotReady(
|
||||
"OAuth2 implementation temporarily unavailable, will retry"
|
||||
) from err
|
||||
|
||||
session = OAuth2Session(hass, entry, implementation)
|
||||
session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)
|
||||
|
||||
# If using a requests-based API lib
|
||||
entry.runtime_data = api.ConfigEntryAuth(hass, session)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# serializer version: 1
|
||||
# name: test_alarm_control_panel[amax_3000-None][alarm_control_panel.area1-entry]
|
||||
# name: test_alarm_control_panel[None-amax_3000][alarm_control_panel.area1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -34,7 +34,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_alarm_control_panel[amax_3000-None][alarm_control_panel.area1-state]
|
||||
# name: test_alarm_control_panel[None-amax_3000][alarm_control_panel.area1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'changed_by': None,
|
||||
@@ -51,7 +51,7 @@
|
||||
'state': 'disarmed',
|
||||
})
|
||||
# ---
|
||||
# name: test_alarm_control_panel[b5512-None][alarm_control_panel.area1-entry]
|
||||
# name: test_alarm_control_panel[None-b5512][alarm_control_panel.area1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -86,7 +86,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_alarm_control_panel[b5512-None][alarm_control_panel.area1-state]
|
||||
# name: test_alarm_control_panel[None-b5512][alarm_control_panel.area1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'changed_by': None,
|
||||
@@ -103,7 +103,7 @@
|
||||
'state': 'disarmed',
|
||||
})
|
||||
# ---
|
||||
# name: test_alarm_control_panel[solution_3000-None][alarm_control_panel.area1-entry]
|
||||
# name: test_alarm_control_panel[None-solution_3000][alarm_control_panel.area1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -138,7 +138,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_alarm_control_panel[solution_3000-None][alarm_control_panel.area1-state]
|
||||
# name: test_alarm_control_panel[None-solution_3000][alarm_control_panel.area1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'changed_by': None,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
# serializer version: 1
|
||||
# name: test_sensor[amax_3000-None][sensor.area1_burglary_alarm_issues-entry]
|
||||
# name: test_sensor[None-amax_3000][sensor.area1_burglary_alarm_issues-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -34,7 +34,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[amax_3000-None][sensor.area1_burglary_alarm_issues-state]
|
||||
# name: test_sensor[None-amax_3000][sensor.area1_burglary_alarm_issues-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Burglary alarm issues',
|
||||
@@ -47,7 +47,7 @@
|
||||
'state': 'no_issues',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[amax_3000-None][sensor.area1_faulting_points-entry]
|
||||
# name: test_sensor[None-amax_3000][sensor.area1_faulting_points-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -82,7 +82,7 @@
|
||||
'unit_of_measurement': 'points',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[amax_3000-None][sensor.area1_faulting_points-state]
|
||||
# name: test_sensor[None-amax_3000][sensor.area1_faulting_points-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Faulting points',
|
||||
@@ -96,7 +96,7 @@
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[amax_3000-None][sensor.area1_fire_alarm_issues-entry]
|
||||
# name: test_sensor[None-amax_3000][sensor.area1_fire_alarm_issues-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -131,7 +131,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[amax_3000-None][sensor.area1_fire_alarm_issues-state]
|
||||
# name: test_sensor[None-amax_3000][sensor.area1_fire_alarm_issues-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Fire alarm issues',
|
||||
@@ -144,7 +144,7 @@
|
||||
'state': 'no_issues',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[amax_3000-None][sensor.area1_gas_alarm_issues-entry]
|
||||
# name: test_sensor[None-amax_3000][sensor.area1_gas_alarm_issues-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -179,7 +179,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[amax_3000-None][sensor.area1_gas_alarm_issues-state]
|
||||
# name: test_sensor[None-amax_3000][sensor.area1_gas_alarm_issues-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Gas alarm issues',
|
||||
@@ -192,7 +192,7 @@
|
||||
'state': 'no_issues',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[b5512-None][sensor.area1_burglary_alarm_issues-entry]
|
||||
# name: test_sensor[None-b5512][sensor.area1_burglary_alarm_issues-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -227,7 +227,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[b5512-None][sensor.area1_burglary_alarm_issues-state]
|
||||
# name: test_sensor[None-b5512][sensor.area1_burglary_alarm_issues-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Burglary alarm issues',
|
||||
@@ -240,7 +240,7 @@
|
||||
'state': 'no_issues',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[b5512-None][sensor.area1_faulting_points-entry]
|
||||
# name: test_sensor[None-b5512][sensor.area1_faulting_points-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -275,7 +275,7 @@
|
||||
'unit_of_measurement': 'points',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[b5512-None][sensor.area1_faulting_points-state]
|
||||
# name: test_sensor[None-b5512][sensor.area1_faulting_points-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Faulting points',
|
||||
@@ -289,7 +289,7 @@
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[b5512-None][sensor.area1_fire_alarm_issues-entry]
|
||||
# name: test_sensor[None-b5512][sensor.area1_fire_alarm_issues-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -324,7 +324,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[b5512-None][sensor.area1_fire_alarm_issues-state]
|
||||
# name: test_sensor[None-b5512][sensor.area1_fire_alarm_issues-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Fire alarm issues',
|
||||
@@ -337,7 +337,7 @@
|
||||
'state': 'no_issues',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[b5512-None][sensor.area1_gas_alarm_issues-entry]
|
||||
# name: test_sensor[None-b5512][sensor.area1_gas_alarm_issues-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -372,7 +372,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[b5512-None][sensor.area1_gas_alarm_issues-state]
|
||||
# name: test_sensor[None-b5512][sensor.area1_gas_alarm_issues-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Gas alarm issues',
|
||||
@@ -385,7 +385,7 @@
|
||||
'state': 'no_issues',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[solution_3000-None][sensor.area1_burglary_alarm_issues-entry]
|
||||
# name: test_sensor[None-solution_3000][sensor.area1_burglary_alarm_issues-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -420,7 +420,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[solution_3000-None][sensor.area1_burglary_alarm_issues-state]
|
||||
# name: test_sensor[None-solution_3000][sensor.area1_burglary_alarm_issues-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Burglary alarm issues',
|
||||
@@ -433,7 +433,7 @@
|
||||
'state': 'no_issues',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[solution_3000-None][sensor.area1_faulting_points-entry]
|
||||
# name: test_sensor[None-solution_3000][sensor.area1_faulting_points-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -468,7 +468,7 @@
|
||||
'unit_of_measurement': 'points',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[solution_3000-None][sensor.area1_faulting_points-state]
|
||||
# name: test_sensor[None-solution_3000][sensor.area1_faulting_points-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Faulting points',
|
||||
@@ -482,7 +482,7 @@
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[solution_3000-None][sensor.area1_fire_alarm_issues-entry]
|
||||
# name: test_sensor[None-solution_3000][sensor.area1_fire_alarm_issues-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -517,7 +517,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[solution_3000-None][sensor.area1_fire_alarm_issues-state]
|
||||
# name: test_sensor[None-solution_3000][sensor.area1_fire_alarm_issues-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Fire alarm issues',
|
||||
@@ -530,7 +530,7 @@
|
||||
'state': 'no_issues',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[solution_3000-None][sensor.area1_gas_alarm_issues-entry]
|
||||
# name: test_sensor[None-solution_3000][sensor.area1_gas_alarm_issues-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -565,7 +565,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[solution_3000-None][sensor.area1_gas_alarm_issues-state]
|
||||
# name: test_sensor[None-solution_3000][sensor.area1_gas_alarm_issues-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Area1 Gas alarm issues',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# serializer version: 1
|
||||
# name: test_switch[amax_3000-None][switch.main_door_locked-entry]
|
||||
# name: test_switch[None-amax_3000][switch.main_door_locked-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -34,7 +34,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[amax_3000-None][switch.main_door_locked-state]
|
||||
# name: test_switch[None-amax_3000][switch.main_door_locked-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Main Door Locked',
|
||||
@@ -47,7 +47,7 @@
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[amax_3000-None][switch.main_door_momentarily_unlocked-entry]
|
||||
# name: test_switch[None-amax_3000][switch.main_door_momentarily_unlocked-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -82,7 +82,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[amax_3000-None][switch.main_door_momentarily_unlocked-state]
|
||||
# name: test_switch[None-amax_3000][switch.main_door_momentarily_unlocked-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Main Door Momentarily unlocked',
|
||||
@@ -95,7 +95,7 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[amax_3000-None][switch.main_door_secured-entry]
|
||||
# name: test_switch[None-amax_3000][switch.main_door_secured-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -130,7 +130,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[amax_3000-None][switch.main_door_secured-state]
|
||||
# name: test_switch[None-amax_3000][switch.main_door_secured-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Main Door Secured',
|
||||
@@ -143,7 +143,7 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[amax_3000-None][switch.output_a-entry]
|
||||
# name: test_switch[None-amax_3000][switch.output_a-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -178,7 +178,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[amax_3000-None][switch.output_a-state]
|
||||
# name: test_switch[None-amax_3000][switch.output_a-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Output A',
|
||||
@@ -191,7 +191,7 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[b5512-None][switch.main_door_locked-entry]
|
||||
# name: test_switch[None-b5512][switch.main_door_locked-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -226,7 +226,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[b5512-None][switch.main_door_locked-state]
|
||||
# name: test_switch[None-b5512][switch.main_door_locked-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Main Door Locked',
|
||||
@@ -239,7 +239,7 @@
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[b5512-None][switch.main_door_momentarily_unlocked-entry]
|
||||
# name: test_switch[None-b5512][switch.main_door_momentarily_unlocked-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -274,7 +274,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[b5512-None][switch.main_door_momentarily_unlocked-state]
|
||||
# name: test_switch[None-b5512][switch.main_door_momentarily_unlocked-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Main Door Momentarily unlocked',
|
||||
@@ -287,7 +287,7 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[b5512-None][switch.main_door_secured-entry]
|
||||
# name: test_switch[None-b5512][switch.main_door_secured-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -322,7 +322,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[b5512-None][switch.main_door_secured-state]
|
||||
# name: test_switch[None-b5512][switch.main_door_secured-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Main Door Secured',
|
||||
@@ -335,7 +335,7 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[b5512-None][switch.output_a-entry]
|
||||
# name: test_switch[None-b5512][switch.output_a-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -370,7 +370,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[b5512-None][switch.output_a-state]
|
||||
# name: test_switch[None-b5512][switch.output_a-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Output A',
|
||||
@@ -383,7 +383,7 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[solution_3000-None][switch.main_door_locked-entry]
|
||||
# name: test_switch[None-solution_3000][switch.main_door_locked-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -418,7 +418,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[solution_3000-None][switch.main_door_locked-state]
|
||||
# name: test_switch[None-solution_3000][switch.main_door_locked-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Main Door Locked',
|
||||
@@ -431,7 +431,7 @@
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[solution_3000-None][switch.main_door_momentarily_unlocked-entry]
|
||||
# name: test_switch[None-solution_3000][switch.main_door_momentarily_unlocked-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -466,7 +466,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[solution_3000-None][switch.main_door_momentarily_unlocked-state]
|
||||
# name: test_switch[None-solution_3000][switch.main_door_momentarily_unlocked-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Main Door Momentarily unlocked',
|
||||
@@ -479,7 +479,7 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[solution_3000-None][switch.main_door_secured-entry]
|
||||
# name: test_switch[None-solution_3000][switch.main_door_secured-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -514,7 +514,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[solution_3000-None][switch.main_door_secured-state]
|
||||
# name: test_switch[None-solution_3000][switch.main_door_secured-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Main Door Secured',
|
||||
@@ -527,7 +527,7 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[solution_3000-None][switch.output_a-entry]
|
||||
# name: test_switch[None-solution_3000][switch.output_a-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -562,7 +562,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switch[solution_3000-None][switch.output_a-state]
|
||||
# name: test_switch[None-solution_3000][switch.output_a-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Output A',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from pathlib import Path
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from freezegun import freeze_time
|
||||
import httpx
|
||||
from openai import PermissionDeniedError
|
||||
import pytest
|
||||
@@ -211,7 +212,7 @@ async def test_generate_data_with_attachments(
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_init_component")
|
||||
@pytest.mark.freeze_time("2025-06-14 22:59:00")
|
||||
@freeze_time("2025-06-14 22:59:00")
|
||||
@pytest.mark.parametrize("image_model", ["gpt-image-1", "gpt-image-1-mini"])
|
||||
async def test_generate_image(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import json
|
||||
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
@@ -13,7 +14,7 @@ from tests.common import async_fire_mqtt_message
|
||||
from tests.typing import MqttMockHAClient
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2024-02-26 01:21:34")
|
||||
@freeze_time("2024-02-26 01:21:34")
|
||||
@pytest.mark.parametrize(
|
||||
"sensor_suffix",
|
||||
[
|
||||
|
||||
@@ -1 +1,55 @@
|
||||
"""Tests for the Plaato integration."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun import freeze_time
|
||||
from pyplaato.models.airlock import PlaatoAirlock
|
||||
from pyplaato.models.device import PlaatoDeviceType
|
||||
from pyplaato.models.keg import PlaatoKeg
|
||||
|
||||
from homeassistant.components.plaato.const import (
|
||||
CONF_DEVICE_NAME,
|
||||
CONF_DEVICE_TYPE,
|
||||
CONF_USE_WEBHOOK,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.const import CONF_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
# Note: It would be good to replace this test data
|
||||
# with actual data from the API
|
||||
AIRLOCK_DATA = {}
|
||||
KEG_DATA = {}
|
||||
|
||||
|
||||
@freeze_time("2024-05-24 12:00:00", tz_offset=0)
|
||||
async def init_integration(
|
||||
hass: HomeAssistant, device_type: PlaatoDeviceType
|
||||
) -> MockConfigEntry:
|
||||
"""Mock integration setup."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.plaato.coordinator.Plaato.get_airlock_data",
|
||||
return_value=PlaatoAirlock(AIRLOCK_DATA),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.plaato.coordinator.Plaato.get_keg_data",
|
||||
return_value=PlaatoKeg(KEG_DATA),
|
||||
),
|
||||
):
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_USE_WEBHOOK: False,
|
||||
CONF_TOKEN: "valid_token",
|
||||
CONF_DEVICE_TYPE: device_type,
|
||||
CONF_DEVICE_NAME: "device_name",
|
||||
},
|
||||
entry_id="123456",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
return entry
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
"""Test fixtures for the Plaato integration."""
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyplaato.models.airlock import PlaatoAirlock
|
||||
from pyplaato.models.device import PlaatoDeviceType
|
||||
from pyplaato.models.keg import PlaatoKeg
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.plaato.const import (
|
||||
CONF_DEVICE_NAME,
|
||||
CONF_DEVICE_TYPE,
|
||||
CONF_USE_WEBHOOK,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.const import CONF_TOKEN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
# Note: It would be good to replace this test data
|
||||
# with actual data from the API
|
||||
AIRLOCK_DATA = {}
|
||||
KEG_DATA = {}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def init_integration(
|
||||
hass: HomeAssistant,
|
||||
device_type: PlaatoDeviceType,
|
||||
platform: Platform,
|
||||
) -> AsyncGenerator[MockConfigEntry]:
|
||||
"""Mock integration setup."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.plaato.PLATFORMS",
|
||||
[platform],
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.plaato.coordinator.Plaato.get_airlock_data",
|
||||
return_value=PlaatoAirlock(AIRLOCK_DATA),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.plaato.coordinator.Plaato.get_keg_data",
|
||||
return_value=PlaatoKeg(KEG_DATA),
|
||||
),
|
||||
):
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_USE_WEBHOOK: False,
|
||||
CONF_TOKEN: "valid_token",
|
||||
CONF_DEVICE_TYPE: device_type,
|
||||
CONF_DEVICE_NAME: "device_name",
|
||||
},
|
||||
entry_id="123456",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
yield entry
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Tests for the plaato binary sensors."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyplaato.models.device import PlaatoDeviceType
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
@@ -8,23 +10,24 @@ from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
from . import init_integration
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def platform() -> Platform:
|
||||
"""Fixture to specify platform."""
|
||||
return Platform.BINARY_SENSOR
|
||||
from tests.common import snapshot_platform
|
||||
|
||||
|
||||
# note: PlaatoDeviceType.Airlock does not provide binary sensors
|
||||
@pytest.mark.parametrize("device_type", [PlaatoDeviceType.Keg])
|
||||
@pytest.mark.freeze_time("2024-05-24 12:00:00", tz_offset=0)
|
||||
async def test_binary_sensors(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
init_integration: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
device_type: PlaatoDeviceType,
|
||||
) -> None:
|
||||
"""Test binary sensors."""
|
||||
await snapshot_platform(hass, entity_registry, snapshot, init_integration.entry_id)
|
||||
with patch(
|
||||
"homeassistant.components.plaato.PLATFORMS",
|
||||
[Platform.BINARY_SENSOR],
|
||||
):
|
||||
entry = await init_integration(hass, device_type)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Tests for the plaato sensors."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyplaato.models.device import PlaatoDeviceType
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
@@ -8,24 +10,25 @@ from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
from . import init_integration
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def platform() -> Platform:
|
||||
"""Fixture to specify platform."""
|
||||
return Platform.SENSOR
|
||||
from tests.common import snapshot_platform
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"device_type", [PlaatoDeviceType.Airlock, PlaatoDeviceType.Keg]
|
||||
)
|
||||
@pytest.mark.freeze_time("2024-05-24 12:00:00", tz_offset=0)
|
||||
async def test_sensors(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
init_integration: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
device_type: PlaatoDeviceType,
|
||||
) -> None:
|
||||
"""Test sensors."""
|
||||
await snapshot_platform(hass, entity_registry, snapshot, init_integration.entry_id)
|
||||
with patch(
|
||||
"homeassistant.components.plaato.PLATFORMS",
|
||||
[Platform.SENSOR],
|
||||
):
|
||||
entry = await init_integration(hass, device_type)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from collections.abc import AsyncGenerator
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from freezegun.api import freeze_time
|
||||
from psnawp_api.core.psnawp_exceptions import (
|
||||
PSNAWPClientError,
|
||||
PSNAWPForbiddenError,
|
||||
@@ -62,7 +63,7 @@ async def test_notify_platform(
|
||||
"notify.testuser_direct_message_publicuniversalfriend",
|
||||
],
|
||||
)
|
||||
@pytest.mark.freeze_time("2025-07-28T00:00:00+00:00")
|
||||
@freeze_time("2025-07-28T00:00:00+00:00")
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_send_message(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freezegun import freeze_time
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
@@ -26,7 +27,7 @@ from homeassistant.util import dt as dt_util
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2022-03-12T15:24:26+00:00")
|
||||
@freeze_time("2022-03-12T15:24:26+00:00")
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
@pytest.mark.parametrize(
|
||||
"load_platforms",
|
||||
|
||||
@@ -9,6 +9,7 @@ import math
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import freeze_time
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import sensor
|
||||
@@ -476,7 +477,7 @@ async def test_restore_sensor_save_state(
|
||||
assert type(extra_data["native_value"]) is native_value_type
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2020-02-08 15:00:00")
|
||||
@freeze_time("2020-02-08 15:00:00")
|
||||
async def test_restore_sensor_save_state_frozen_time_datetime(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
@@ -504,7 +505,7 @@ async def test_restore_sensor_save_state_frozen_time_datetime(
|
||||
assert type(extra_data["native_value"]) is dict
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2020-02-08 15:00:00")
|
||||
@freeze_time("2020-02-08 15:00:00")
|
||||
async def test_restore_sensor_save_state_frozen_time_date(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
|
||||
@@ -4,6 +4,7 @@ from collections.abc import Generator
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import freeze_time
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
@@ -27,7 +28,7 @@ def event_only() -> Generator[None]:
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
@pytest.mark.freeze_time("2025-01-01T03:30:00.000Z")
|
||||
@freeze_time("2025-01-01T03:30:00.000Z")
|
||||
async def test_setup(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
@@ -128,7 +129,7 @@ async def test_setup(
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
@pytest.mark.freeze_time("2025-01-01T03:30:00.000+00:00")
|
||||
@freeze_time("2025-01-01T03:30:00.000+00:00")
|
||||
async def test_webhook_event(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freezegun import freeze_time
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pysmhi import (
|
||||
SMHIFirePointForecast,
|
||||
@@ -65,7 +66,7 @@ async def test_setup_hass(
|
||||
"to_load",
|
||||
[1],
|
||||
)
|
||||
@pytest.mark.freeze_time(datetime(2023, 8, 7, 1, tzinfo=dt_util.UTC))
|
||||
@freeze_time(datetime(2023, 8, 7, 1, tzinfo=dt_util.UTC))
|
||||
async def test_clear_night(
|
||||
hass: HomeAssistant,
|
||||
mock_client: SMHIPointForecast,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from freezegun import freeze_time
|
||||
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -11,7 +11,7 @@ from . import async_init_integration, find_update_callback
|
||||
from .const import MOCK_SNOO_DATA
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2025-01-01 12:00:00")
|
||||
@freeze_time("2025-01-01 12:00:00")
|
||||
async def test_events(hass: HomeAssistant, bypass_api: AsyncMock) -> None:
|
||||
"""Test events and check test values are correctly set."""
|
||||
await async_init_integration(hass)
|
||||
@@ -26,7 +26,7 @@ async def test_events(hass: HomeAssistant, bypass_api: AsyncMock) -> None:
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2025-01-01 12:00:00")
|
||||
@freeze_time("2025-01-01 12:00:00")
|
||||
async def test_events_data_on_startup(
|
||||
hass: HomeAssistant, bypass_api: AsyncMock
|
||||
) -> None:
|
||||
|
||||
@@ -1200,51 +1200,3 @@ CLIMATE_PANEL_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||
connectable=True,
|
||||
tx_power=-127,
|
||||
)
|
||||
|
||||
|
||||
SMART_THERMOSTAT_RADIATOR_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||
name="Smart Thermostat Radiator",
|
||||
manufacturer_data={2409: b"\xb0\xe9\xfe\xa2T|6\xe4\x00\x9c\xa3A\x00"},
|
||||
service_data={
|
||||
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00 d\x00\x116@",
|
||||
},
|
||||
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||
address="AA:BB:CC:DD:EE:FF",
|
||||
rssi=-60,
|
||||
source="local",
|
||||
advertisement=generate_advertisement_data(
|
||||
local_name="Smart Thermostat Radiator",
|
||||
manufacturer_data={2409: b"\xb0\xe9\xfe\xa2T|6\xe4\x00\x9c\xa3A\x00"},
|
||||
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00 d\x00\x116@"},
|
||||
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||
),
|
||||
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Smart Thermostat Radiator"),
|
||||
time=0,
|
||||
connectable=True,
|
||||
tx_power=-127,
|
||||
)
|
||||
|
||||
|
||||
S20_VACUUM_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||
name="S20 Vacuum",
|
||||
manufacturer_data={2409: b"\xb0\xe9\xfe\xc3\x1a!:\x01\x11\x1e\x00\x00d\x03"},
|
||||
service_data={
|
||||
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00\x00d\x00\x10\xe0P",
|
||||
},
|
||||
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||
address="AA:BB:CC:DD:EE:FF",
|
||||
rssi=-60,
|
||||
source="local",
|
||||
advertisement=generate_advertisement_data(
|
||||
local_name="S20 Vacuum",
|
||||
manufacturer_data={2409: b"\xb0\xe9\xfe\xc3\x1a!:\x01\x11\x1e\x00\x00d\x03"},
|
||||
service_data={
|
||||
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00\x00d\x00\x10\xe0P",
|
||||
},
|
||||
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||
),
|
||||
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "S20 Vacuum"),
|
||||
time=0,
|
||||
connectable=True,
|
||||
tx_power=-127,
|
||||
)
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
"""Tests for the Switchbot climate integration."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from switchbot import SwitchbotOperationError
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
DOMAIN as CLIMATE_DOMAIN,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from . import SMART_THERMOSTAT_RADIATOR_SERVICE_INFO
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service", "service_data", "mock_method"),
|
||||
[
|
||||
(SERVICE_SET_HVAC_MODE, {"hvac_mode": HVACMode.HEAT}, "set_hvac_mode"),
|
||||
(SERVICE_SET_PRESET_MODE, {"preset_mode": "manual"}, "set_preset_mode"),
|
||||
(SERVICE_SET_TEMPERATURE, {"temperature": 22}, "set_target_temperature"),
|
||||
],
|
||||
)
|
||||
async def test_smart_thermostat_radiator_controlling(
|
||||
hass: HomeAssistant,
|
||||
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
|
||||
service: str,
|
||||
service_data: dict,
|
||||
mock_method: str,
|
||||
) -> None:
|
||||
"""Test controlling the smart thermostat radiator with different services."""
|
||||
inject_bluetooth_service_info(hass, SMART_THERMOSTAT_RADIATOR_SERVICE_INFO)
|
||||
|
||||
entry = mock_entry_encrypted_factory("smart_thermostat_radiator")
|
||||
entity_id = "climate.test_name"
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
mocked_instance = AsyncMock(return_value=True)
|
||||
mocked_none_instance = AsyncMock(return_value=None)
|
||||
with patch.multiple(
|
||||
"homeassistant.components.switchbot.climate.switchbot.SwitchbotSmartThermostatRadiator",
|
||||
get_basic_info=mocked_none_instance,
|
||||
update=mocked_none_instance,
|
||||
**{mock_method: mocked_instance},
|
||||
):
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
service,
|
||||
{**service_data, ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mocked_instance.assert_awaited_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service", "service_data", "mock_method"),
|
||||
[
|
||||
(SERVICE_SET_HVAC_MODE, {"hvac_mode": HVACMode.HEAT}, "set_hvac_mode"),
|
||||
(SERVICE_SET_PRESET_MODE, {"preset_mode": "manual"}, "set_preset_mode"),
|
||||
(SERVICE_SET_TEMPERATURE, {"temperature": 22}, "set_target_temperature"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "error_message"),
|
||||
[
|
||||
(
|
||||
SwitchbotOperationError("Operation failed"),
|
||||
"An error occurred while performing the action: Operation failed",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_exception_handling_smart_thermostat_radiator_service(
|
||||
hass: HomeAssistant,
|
||||
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
|
||||
service: str,
|
||||
service_data: dict,
|
||||
mock_method: str,
|
||||
exception: Exception,
|
||||
error_message: str,
|
||||
) -> None:
|
||||
"""Test exception handling for smart thermostat radiator service with exception."""
|
||||
inject_bluetooth_service_info(hass, SMART_THERMOSTAT_RADIATOR_SERVICE_INFO)
|
||||
|
||||
entry = mock_entry_encrypted_factory("smart_thermostat_radiator")
|
||||
entry.add_to_hass(hass)
|
||||
entity_id = "climate.test_name"
|
||||
|
||||
mocked_none_instance = AsyncMock(return_value=None)
|
||||
with patch.multiple(
|
||||
"homeassistant.components.switchbot.climate.switchbot.SwitchbotSmartThermostatRadiator",
|
||||
get_basic_info=mocked_none_instance,
|
||||
update=mocked_none_instance,
|
||||
**{mock_method: AsyncMock(side_effect=exception)},
|
||||
):
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with pytest.raises(HomeAssistantError, match=error_message):
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
service,
|
||||
{**service_data, ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
@@ -21,7 +21,6 @@ from . import (
|
||||
K11_PLUS_VACUUM_SERVICE_INFO,
|
||||
K20_VACUUM_SERVICE_INFO,
|
||||
S10_VACUUM_SERVICE_INFO,
|
||||
S20_VACUUM_SERVICE_INFO,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
@@ -37,7 +36,6 @@ from tests.components.bluetooth import inject_bluetooth_service_info
|
||||
("k10_vacuum", K10_VACUUM_SERVICE_INFO),
|
||||
("k10_pro_vacuum", K10_PRO_VACUUM_SERVICE_INFO),
|
||||
("k11+_vacuum", K11_PLUS_VACUUM_SERVICE_INFO),
|
||||
("s20_vacuum", S20_VACUUM_SERVICE_INFO),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from datetime import datetime
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from freezegun.api import freeze_time
|
||||
from telegram import Chat, Message
|
||||
from telegram.constants import ChatType, ParseMode
|
||||
|
||||
@@ -19,7 +19,7 @@ from homeassistant.core import Context, HomeAssistant
|
||||
from tests.common import async_capture_events
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2025-01-09T12:00:00+00:00")
|
||||
@freeze_time("2025-01-09T12:00:00+00:00")
|
||||
async def test_send_message(
|
||||
hass: HomeAssistant,
|
||||
webhook_platform: None,
|
||||
|
||||
@@ -244,7 +244,7 @@ async def test_v4_weather_legacy_entities(hass: HomeAssistant) -> None:
|
||||
("service"),
|
||||
[SERVICE_GET_FORECASTS],
|
||||
)
|
||||
@pytest.mark.freeze_time(datetime(2021, 3, 6, 23, 59, 59, tzinfo=dt_util.UTC))
|
||||
@freeze_time(datetime(2021, 3, 6, 23, 59, 59, tzinfo=dt_util.UTC))
|
||||
async def test_v4_forecast_service(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
|
||||
@@ -1,22 +1,58 @@
|
||||
"""Test Utility Meter diagnostics."""
|
||||
|
||||
from aiohttp.test_utils import TestClient
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
from syrupy.filters import props
|
||||
|
||||
from homeassistant.auth.models import Credentials
|
||||
from homeassistant.components.utility_meter.const import DOMAIN
|
||||
from homeassistant.components.utility_meter.sensor import ATTR_LAST_RESET
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
|
||||
from tests.common import MockConfigEntry, mock_restore_cache_with_extra_data
|
||||
from tests.common import (
|
||||
CLIENT_ID,
|
||||
MockConfigEntry,
|
||||
MockUser,
|
||||
mock_restore_cache_with_extra_data,
|
||||
)
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2024-04-06 00:00:00+00:00")
|
||||
async def generate_new_hass_access_token(
|
||||
hass: HomeAssistant, hass_admin_user: MockUser, hass_admin_credential: Credentials
|
||||
) -> str:
|
||||
"""Return an access token to access Home Assistant."""
|
||||
await hass.auth.async_link_user(hass_admin_user, hass_admin_credential)
|
||||
|
||||
refresh_token = await hass.auth.async_create_refresh_token(
|
||||
hass_admin_user, CLIENT_ID, credential=hass_admin_credential
|
||||
)
|
||||
return hass.auth.async_create_access_token(refresh_token)
|
||||
|
||||
|
||||
def _get_test_client_generator(
|
||||
hass: HomeAssistant, aiohttp_client: ClientSessionGenerator, new_token: str
|
||||
):
|
||||
"""Return a test client generator.""."""
|
||||
|
||||
async def auth_client() -> TestClient:
|
||||
return await aiohttp_client(
|
||||
hass.http.app, headers={"Authorization": f"Bearer {new_token}"}
|
||||
)
|
||||
|
||||
return auth_client
|
||||
|
||||
|
||||
@freeze_time("2024-04-06 00:00:00+00:00")
|
||||
@pytest.mark.usefixtures("socket_enabled")
|
||||
async def test_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
aiohttp_client: ClientSessionGenerator,
|
||||
hass_admin_user: MockUser,
|
||||
hass_admin_credential: Credentials,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test generating diagnostics for a config entry."""
|
||||
@@ -94,6 +130,15 @@ async def test_diagnostics(
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
|
||||
# Since we are freezing time only when we enter this test, we need to
|
||||
# manually create a new token and clients since the token created by
|
||||
# the fixtures would not be valid.
|
||||
new_token = await generate_new_hass_access_token(
|
||||
hass, hass_admin_user, hass_admin_credential
|
||||
)
|
||||
|
||||
diag = await get_diagnostics_for_config_entry(
|
||||
hass, _get_test_client_generator(hass, aiohttp_client, new_token), config_entry
|
||||
)
|
||||
|
||||
assert diag == snapshot(exclude=props("entry_id", "created_at", "modified_at"))
|
||||
|
||||
@@ -5,7 +5,6 @@ from unittest.mock import MagicMock, patch
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.velux import DOMAIN
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceRegistry
|
||||
@@ -96,11 +95,3 @@ async def test_rain_sensor_device_association(
|
||||
# Verify device has correct identifiers
|
||||
assert ("velux", mock_window.serial_number) in device_entry.identifiers
|
||||
assert device_entry.name == mock_window.name
|
||||
|
||||
# Verify via_device is gateway
|
||||
assert device_entry.via_device_id is not None
|
||||
via_device_entry = device_registry.async_get(device_entry.via_device_id)
|
||||
assert via_device_entry is not None
|
||||
assert via_device_entry.identifiers == {
|
||||
(DOMAIN, f"gateway_{mock_config_entry.entry_id}")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ from functools import partial
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import wake_word
|
||||
@@ -169,7 +170,7 @@ async def test_config_entry_unload(
|
||||
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2023-06-22 10:30:00+00:00")
|
||||
@freeze_time("2023-06-22 10:30:00+00:00")
|
||||
@pytest.mark.parametrize(
|
||||
("wake_word_id", "expected_ww", "expected_phrase"),
|
||||
[
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
from yalesmartalarmclient.exceptions import UnknownError
|
||||
@@ -17,7 +18,7 @@ from homeassistant.helpers import entity_registry as er
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2024-04-29T18:00:00.612351+00:00")
|
||||
@freeze_time("2024-04-29T18:00:00.612351+00:00")
|
||||
@pytest.mark.parametrize(
|
||||
"load_platforms",
|
||||
[[Platform.BUTTON]],
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from collections.abc import Callable, Coroutine
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
from zigpy.const import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
|
||||
from zigpy.device import Device
|
||||
@@ -45,29 +46,23 @@ def button_platform_only():
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def speed_up_radio_mgr():
|
||||
"""Speed up the radio manager connection time by removing delays.
|
||||
async def setup_zha_integration(
|
||||
hass: HomeAssistant, setup_zha: Callable[..., Coroutine[None]]
|
||||
):
|
||||
"""Set up ZHA component."""
|
||||
|
||||
This fixture replaces the fixture in conftest.py by patching the connect
|
||||
and shutdown delays to 0 to allow waiting for the patched delays when
|
||||
running tests with time frozen, which otherwise blocks forever.
|
||||
"""
|
||||
with (
|
||||
patch("homeassistant.components.zha.radio_manager.CONNECT_DELAY_S", 0),
|
||||
patch("zha.application.gateway.SHUT_DOWN_DELAY_S", 0),
|
||||
):
|
||||
yield
|
||||
# if we call this in the test itself the test hangs forever
|
||||
await setup_zha()
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2021-11-04 17:37:00", tz_offset=-1)
|
||||
@freeze_time("2021-11-04 17:37:00", tz_offset=-1)
|
||||
async def test_button(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
setup_zha: Callable[..., Coroutine[None]],
|
||||
setup_zha_integration, # pylint: disable=unused-argument
|
||||
zigpy_device_mock: Callable[..., Device],
|
||||
) -> None:
|
||||
"""Test ZHA button platform."""
|
||||
await setup_zha()
|
||||
|
||||
gateway = get_zha_gateway(hass)
|
||||
gateway_proxy: ZHAGatewayProxy = get_zha_gateway_proxy(hass)
|
||||
|
||||
@@ -8,6 +8,7 @@ from copy import deepcopy
|
||||
from typing import TYPE_CHECKING
|
||||
from unittest.mock import ANY, AsyncMock, MagicMock, call, patch
|
||||
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
import voluptuous as vol
|
||||
from zha.application.const import (
|
||||
@@ -93,21 +94,6 @@ def required_platform_only():
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def speed_up_radio_mgr():
|
||||
"""Speed up the radio manager connection time by removing delays.
|
||||
|
||||
This fixture replaces the fixture in conftest.py by patching the connect
|
||||
and shutdown delays to 0 to allow waiting for the patched delays when
|
||||
running tests with time frozen, which otherwise blocks forever.
|
||||
"""
|
||||
with (
|
||||
patch("homeassistant.components.zha.radio_manager.CONNECT_DELAY_S", 0),
|
||||
patch("zha.application.gateway.SHUT_DOWN_DELAY_S", 0),
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def zha_client(
|
||||
hass: HomeAssistant,
|
||||
@@ -231,7 +217,7 @@ async def test_device_cluster_commands(zha_client) -> None:
|
||||
assert command[TYPE] is not None
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2023-09-23 20:16:00+00:00")
|
||||
@freeze_time("2023-09-23 20:16:00+00:00")
|
||||
async def test_list_devices(zha_client) -> None:
|
||||
"""Test getting ZHA devices."""
|
||||
await zha_client.send_json({ID: 5, TYPE: "zha/devices"})
|
||||
|
||||
Reference in New Issue
Block a user