Compare commits

...

11 Commits

Author SHA1 Message Date
J. Nick Koston e7f3e5637f Bump aioshelly to 13.26.1 (#172160) 2026-05-25 10:31:21 -05:00
Paul Bottein 80cefc74ec Update rf-protocols to 4.0.0 (#172131) 2026-05-25 17:17:53 +02:00
J. Nick Koston 2f33b4b7f9 Bump aioharmony to 1.0.8 (#172116) 2026-05-25 17:16:47 +02:00
J. Nick Koston cf52a7a509 Bump bluetooth-adapters to 2.2.0 (#172120) 2026-05-25 10:10:54 -05:00
Mattias Arrelid f5835f849a Update anyio to 4.13.0 (#172138) 2026-05-25 09:36:53 -05:00
J. Nick Koston ec5210dca8 Bump led-ble to 1.1.11 (#172154) 2026-05-25 09:35:11 -05:00
Michael 422ea1a9b1 Bump wakeonlan to 3.3.0 (#172150) 2026-05-25 16:13:38 +02:00
Artur Pragacz b6f69f6b99 Clean up should_expose in google assistant (#171937) 2026-05-25 13:48:49 +02:00
Tom a2a3819241 Extract ProxmoxVE TOKEN_ID from full token string (#172129) 2026-05-25 12:57:05 +02:00
Erwin Douna 3ce33b0ac6 Proxmox fix duplicate const (#171352) 2026-05-25 12:56:19 +02:00
bkobus-bbx e507a97d8b Bump blebox_uniapi to v2.5.4 (#172130) 2026-05-25 12:44:25 +02:00
35 changed files with 119 additions and 185 deletions
@@ -7,6 +7,6 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["blebox_uniapi"],
"requirements": ["blebox-uniapi==2.5.3"],
"requirements": ["blebox-uniapi==2.5.4"],
"zeroconf": ["_bbxsrv._tcp.local."]
}
@@ -17,7 +17,7 @@
"requirements": [
"bleak==3.0.2",
"bleak-retry-connector==4.6.1",
"bluetooth-adapters==2.1.0",
"bluetooth-adapters==2.2.0",
"bluetooth-auto-recovery==1.6.4",
"bluetooth-data-tools==1.29.18",
"dbus-fast==5.0.9",
@@ -275,9 +275,13 @@ class CloudGoogleConfig(AbstractConfig):
)
)
def should_expose(self, state: State) -> bool:
"""If a state object should be exposed."""
return self._should_expose_entity_id(state.entity_id)
def should_expose(self, entity_id: str) -> bool:
"""If an entity should be exposed."""
entity_filter: EntityFilter = self._config[CONF_FILTER]
if not entity_filter.empty_filter:
return entity_filter(entity_id)
return async_should_expose(self.hass, CLOUD_GOOGLE, entity_id)
def _should_expose_legacy(self, entity_id: str) -> bool:
"""If an entity ID should be exposed."""
@@ -308,14 +312,6 @@ class CloudGoogleConfig(AbstractConfig):
and _supported_legacy(self.hass, entity_id)
)
def _should_expose_entity_id(self, entity_id: str) -> bool:
"""If an entity should be exposed."""
entity_filter: EntityFilter = self._config[CONF_FILTER]
if not entity_filter.empty_filter:
return entity_filter(entity_id)
return async_should_expose(self.hass, CLOUD_GOOGLE, entity_id)
@property
def agent_user_id(self) -> str:
"""Return Agent User Id to use for query responses."""
@@ -467,7 +463,7 @@ class CloudGoogleConfig(AbstractConfig):
entity_id = event.data["entity_id"]
if not self._should_expose_entity_id(entity_id):
if not self.should_expose(entity_id):
return
self.async_schedule_google_sync_all()
@@ -490,8 +486,7 @@ class CloudGoogleConfig(AbstractConfig):
# Check if any exposed entity uses the device area
if not any(
entity_entry.area_id is None
and self._should_expose_entity_id(entity_entry.entity_id)
entity_entry.area_id is None and self.should_expose(entity_entry.entity_id)
for entity_entry in er.async_entries_for_device(
er.async_get(self.hass), event.data["device_id"]
)
@@ -185,7 +185,7 @@ class AbstractConfig(ABC):
"""
@abstractmethod
def should_expose(self, state) -> bool:
def should_expose(self, entity_id: str) -> bool:
"""Return if entity should be exposed."""
@abstractmethod
@@ -532,7 +532,7 @@ class GoogleEntity:
def __repr__(self) -> str:
"""Return the representation."""
return f"<GoogleEntity {self.state.entity_id}: {self.state.name}>"
return f"<GoogleEntity {self.entity_id}: {self.state.name}>"
@callback
def traits(self) -> list[trait._Trait]:
@@ -549,7 +549,7 @@ class GoogleEntity:
@callback
def should_expose(self):
"""If entity should be exposed."""
return self.config.should_expose(self.state)
return self.config.should_expose(self.entity_id)
@callback
def should_expose_local(self) -> bool:
@@ -733,7 +733,7 @@ class GoogleEntity:
if not executed:
raise SmartHomeError(
ERR_FUNCTION_NOT_SUPPORTED,
f"Unable to execute {command} for {self.state.entity_id}",
f"Unable to execute {command} for {self.entity_id}",
)
@callback
@@ -12,7 +12,7 @@ import jwt
from homeassistant.components import webhook
from homeassistant.components.http import KEY_HASS, HomeAssistantView
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant, callback, split_entity_id
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@@ -157,17 +157,13 @@ class GoogleConfig(AbstractConfig):
return None
def should_expose(self, state) -> bool:
def should_expose(self, entity_id: str) -> bool:
"""Return if entity should be exposed."""
expose_by_default = self._config.get(CONF_EXPOSE_BY_DEFAULT)
exposed_domains = self._config.get(CONF_EXPOSED_DOMAINS)
if state.attributes.get("view") is not None:
# Ignore entities that are views
return False
entity_registry = er.async_get(self.hass)
registry_entry = entity_registry.async_get(state.entity_id)
registry_entry = entity_registry.async_get(entity_id)
if registry_entry:
auxiliary_entity = (
registry_entry.entity_category is not None
@@ -176,10 +172,10 @@ class GoogleConfig(AbstractConfig):
else:
auxiliary_entity = False
explicit_expose = self.entity_config.get(state.entity_id, {}).get(CONF_EXPOSE)
explicit_expose = self.entity_config.get(entity_id, {}).get(CONF_EXPOSE)
domain_exposed_by_default = (
expose_by_default and state.domain in exposed_domains
expose_by_default and split_entity_id(entity_id)[0] in exposed_domains
)
# Expose an entity by default if the entity's domain is exposed by default
@@ -73,7 +73,7 @@ def async_enable_report_state(
return bool(
hass.is_running
and (new_state := data["new_state"])
and google_config.should_expose(new_state)
and google_config.should_expose(new_state.entity_id)
and async_get_google_entity_if_supported_cached(
hass, google_config, new_state
)
@@ -8,7 +8,7 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["aioharmony", "slixmpp"],
"requirements": ["aioharmony==1.0.3"],
"requirements": ["aioharmony==1.0.8"],
"ssdp": [
{
"deviceType": "urn:myharmony-com:device:harmony:1",
@@ -36,5 +36,5 @@
"documentation": "https://www.home-assistant.io/integrations/led_ble",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["bluetooth-data-tools==1.29.18", "led-ble==1.1.8"]
"requirements": ["bluetooth-data-tools==1.29.18", "led-ble==1.1.11"]
}
@@ -8,6 +8,6 @@
"integration_type": "service",
"iot_class": "local_push",
"quality_scale": "silver",
"requirements": ["mcp==1.26.0", "aiohttp_sse==2.2.0", "anyio==4.10.0"],
"requirements": ["mcp==1.26.0", "aiohttp_sse==2.2.0", "anyio==4.13.0"],
"single_config_entry": true
}
@@ -1,7 +0,0 @@
"""Command names for the Novy Cooker Hood RF codes."""
from typing import Final
COMMAND_LIGHT: Final = "light"
COMMAND_PLUS: Final = "plus"
COMMAND_MINUS: Final = "minus"
@@ -3,7 +3,7 @@
import asyncio
from typing import Any
from rf_protocols.codes.novy.cooker_hood import get_codes_for_code
from rf_protocols.codes.novy.cooker_hood import NovyCookerHoodButton
import voluptuous as vol
from homeassistant.components.radio_frequency import (
@@ -19,7 +19,6 @@ from homeassistant.const import CONF_CODE
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er, selector
from .commands import COMMAND_LIGHT
from .const import (
CODE_MAX,
CODE_MIN,
@@ -128,10 +127,8 @@ class NovyCookerHoodConfigFlow(ConfigFlow, domain=DOMAIN):
) -> ConfigFlowResult:
"""Toggle the hood light on then off so it ends in its starting state."""
assert self._transmitter_entity_id is not None
command = NovyCookerHoodButton.LIGHT.to_command(channel=self._code)
try:
command = await get_codes_for_code(self._code).async_load_command(
COMMAND_LIGHT
)
await async_send_command(self.hass, self._transmitter_entity_id, command)
await asyncio.sleep(_TOGGLE_GAP)
await async_send_command(self.hass, self._transmitter_entity_id, command)
@@ -3,7 +3,8 @@
import math
from typing import Any
from rf_protocols.codes.novy.cooker_hood import get_codes_for_code
from rf_protocols.codes.novy.cooker_hood import NovyCookerHoodButton
from rf_protocols.commands.novy import NovyCookerHoodCommand
from homeassistant.components.fan import ATTR_PERCENTAGE, FanEntity, FanEntityFeature
from homeassistant.components.radio_frequency import async_send_command
@@ -17,7 +18,6 @@ from homeassistant.util.percentage import (
ranged_value_to_percentage,
)
from .commands import COMMAND_MINUS, COMMAND_PLUS
from .const import SPEED_COUNT
from .entity import NovyCookerHoodEntity
@@ -49,7 +49,7 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
def __init__(self, entry: ConfigEntry) -> None:
"""Initialize the fan."""
super().__init__(entry)
self._codes = get_codes_for_code(entry.data[CONF_CODE])
self._code: int = entry.data[CONF_CODE]
self._level = 0
self._attr_unique_id = entry.entry_id
@@ -103,7 +103,7 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
async def async_increase_speed(self, percentage_step: int | None = None) -> None:
"""Bump speed up by N hardware levels (no recalibration)."""
steps = self._steps_from_percentage(percentage_step)
plus = await self._codes.async_load_command(COMMAND_PLUS)
plus = NovyCookerHoodButton.PLUS.to_command(channel=self._code)
for _ in range(steps):
await self._async_send(plus)
self._level = min(SPEED_COUNT, self._level + steps)
@@ -112,7 +112,7 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
async def async_decrease_speed(self, percentage_step: int | None = None) -> None:
"""Bump speed down by N hardware levels (no recalibration)."""
steps = self._steps_from_percentage(percentage_step)
minus = await self._codes.async_load_command(COMMAND_MINUS)
minus = NovyCookerHoodButton.MINUS.to_command(channel=self._code)
for _ in range(steps):
await self._async_send(minus)
self._level = max(0, self._level - steps)
@@ -127,17 +127,17 @@ class NovyCookerHoodFan(NovyCookerHoodEntity, FanEntity, RestoreEntity):
async def _async_set_level(self, level: int) -> None:
"""Reset to off with `SPEED_COUNT` minus presses, then climb to level."""
minus = await self._codes.async_load_command(COMMAND_MINUS)
minus = NovyCookerHoodButton.MINUS.to_command(channel=self._code)
for _ in range(SPEED_COUNT):
await self._async_send(minus)
if level > 0:
plus = await self._codes.async_load_command(COMMAND_PLUS)
plus = NovyCookerHoodButton.PLUS.to_command(channel=self._code)
for _ in range(level):
await self._async_send(plus)
self._level = level
self.async_write_ha_state()
async def _async_send(self, command: Any) -> None:
async def _async_send(self, command: NovyCookerHoodCommand) -> None:
"""Send a single RF command via the configured transmitter."""
await async_send_command(
self.hass, self._transmitter, command, context=self._context
@@ -2,7 +2,7 @@
from typing import Any
from rf_protocols.codes.novy.cooker_hood import get_codes_for_code
from rf_protocols.codes.novy.cooker_hood import NovyCookerHoodButton
from homeassistant.components.light import ColorMode, LightEntity
from homeassistant.components.radio_frequency import async_send_command
@@ -12,7 +12,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
from .commands import COMMAND_LIGHT
from .entity import NovyCookerHoodEntity
PARALLEL_UPDATES = 1
@@ -37,7 +36,7 @@ class NovyCookerHoodLight(NovyCookerHoodEntity, LightEntity, RestoreEntity):
def __init__(self, entry: ConfigEntry) -> None:
"""Initialize the light."""
super().__init__(entry)
self._codes = get_codes_for_code(entry.data[CONF_CODE])
self._code = entry.data[CONF_CODE]
self._attr_unique_id = entry.entry_id
async def async_added_to_hass(self) -> None:
@@ -48,19 +47,19 @@ class NovyCookerHoodLight(NovyCookerHoodEntity, LightEntity, RestoreEntity):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on by sending the toggle command."""
await self._async_send_command(COMMAND_LIGHT)
await self._async_send_light()
self._attr_is_on = True
self.async_write_ha_state()
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off by sending the toggle command."""
await self._async_send_command(COMMAND_LIGHT)
await self._async_send_light()
self._attr_is_on = False
self.async_write_ha_state()
async def _async_send_command(self, name: str) -> None:
"""Load the named command and send it via the configured transmitter."""
command = await self._codes.async_load_command(name)
async def _async_send_light(self) -> None:
"""Send the light toggle command via the configured transmitter."""
command = NovyCookerHoodButton.LIGHT.to_command(channel=self._code)
await async_send_command(
self.hass, self._transmitter, command, context=self._context
)
@@ -9,6 +9,7 @@ from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_TOKEN,
CONF_USERNAME,
CONF_VERIFY_SSL,
Platform,
@@ -31,7 +32,6 @@ from .const import (
CONF_NODE,
CONF_NODES,
CONF_REALM,
CONF_TOKEN,
CONF_TOKEN_ID,
CONF_TOKEN_SECRET,
CONF_VMS,
+4 -1
View File
@@ -5,7 +5,7 @@ from typing import Any
from homeassistant.const import CONF_USERNAME
from .const import AUTH_OTHER, CONF_AUTH_METHOD, CONF_REALM
from .const import AUTH_OTHER, CONF_AUTH_METHOD, CONF_REALM, CONF_TOKEN_ID
def sanitize_config_entry(input_data: Mapping[str, Any]) -> dict[str, Any]:
@@ -21,4 +21,7 @@ def sanitize_config_entry(input_data: Mapping[str, Any]) -> dict[str, Any]:
data[CONF_REALM] = realm
data[CONF_USERNAME] = f"{username}@{realm}"
if CONF_TOKEN_ID in data and "!" in data[CONF_TOKEN_ID]:
data[CONF_TOKEN_ID] = data[CONF_TOKEN_ID].split("!")[1]
return data
@@ -15,6 +15,7 @@ from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_TOKEN,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
@@ -35,7 +36,6 @@ from .const import (
CONF_NODE,
CONF_NODES,
CONF_REALM,
CONF_TOKEN,
CONF_TOKEN_ID,
CONF_TOKEN_SECRET,
CONF_VMS,
@@ -7,8 +7,6 @@ CONF_AUTH_METHOD = "auth_method"
CONF_REALM = "realm"
CONF_NODE = "node"
CONF_NODES = "nodes"
# pylint: disable-next=home-assistant-duplicate-const
CONF_TOKEN = "token"
CONF_TOKEN_ID = "token_id"
CONF_TOKEN_SECRET = "token_value"
CONF_VMS = "vms"
@@ -16,6 +16,7 @@ from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_TOKEN,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
@@ -26,7 +27,6 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .common import sanitize_config_entry
from .const import (
CONF_NODE,
CONF_TOKEN,
CONF_TOKEN_ID,
CONF_TOKEN_SECRET,
DEFAULT_VERIFY_SSL,
@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/radio_frequency",
"integration_type": "entity",
"quality_scale": "internal",
"requirements": ["rf-protocols==3.2.0"]
"requirements": ["rf-protocols==4.0.0"]
}
@@ -39,7 +39,7 @@
"getmac==0.9.5",
"samsungctl[websocket]==0.7.1",
"samsungtvws[async,encrypted]==2.7.2",
"wakeonlan==3.1.0",
"wakeonlan==3.3.0",
"async-upnp-client==0.46.2"
],
"ssdp": [
@@ -17,7 +17,7 @@
"iot_class": "local_push",
"loggers": ["aioshelly"],
"quality_scale": "platinum",
"requirements": ["aioshelly==13.26.0"],
"requirements": ["aioshelly==13.26.1"],
"zeroconf": [
{
"name": "shelly*",
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/wake_on_lan",
"integration_type": "service",
"iot_class": "local_push",
"requirements": ["wakeonlan==3.1.0"]
"requirements": ["wakeonlan==3.3.0"]
}
+2 -2
View File
@@ -22,7 +22,7 @@ awesomeversion==25.8.0
bcrypt==5.0.0
bleak-retry-connector==4.6.1
bleak==3.0.2
bluetooth-adapters==2.1.0
bluetooth-adapters==2.2.0
bluetooth-auto-recovery==1.6.4
bluetooth-data-tools==1.29.18
cached-ipaddress==1.1.1
@@ -113,7 +113,7 @@ uuid==1000000000.0.0
# even newer versions seem to introduce new issues, it's useful
# for us to pin all these
# requirements so we can directly link HA versions to these library versions.
anyio==4.10.0
anyio==4.13.0
h11==0.16.0
httpcore==1.0.9
+1 -1
View File
@@ -47,7 +47,7 @@ python-slugify==8.0.4
PyTurboJPEG==1.8.3
PyYAML==6.0.3
requests==2.34.2
rf-protocols==3.2.0
rf-protocols==4.0.0
securetar==2026.4.1
SQLAlchemy==2.0.49
standard-aifc==3.13.0
+8 -8
View File
@@ -279,7 +279,7 @@ aiogithubapi==26.0.0
aioguardian==2026.01.1
# homeassistant.components.harmony
aioharmony==1.0.3
aioharmony==1.0.8
# homeassistant.components.hassio
aiohasupervisor==0.4.3
@@ -405,7 +405,7 @@ aiorussound==5.0.1
aioruuvigateway==0.1.0
# homeassistant.components.shelly
aioshelly==13.26.0
aioshelly==13.26.1
# homeassistant.components.skybell
aioskybell==22.7.0
@@ -522,7 +522,7 @@ anthemav==1.4.1
anthropic==0.96.0
# homeassistant.components.mcp_server
anyio==4.10.0
anyio==4.13.0
# homeassistant.components.weatherkit
apple_weatherkit==1.1.3
@@ -660,7 +660,7 @@ bleak-retry-connector==4.6.1
bleak==3.0.2
# homeassistant.components.blebox
blebox-uniapi==2.5.3
blebox-uniapi==2.5.4
# homeassistant.components.blink
blinkpy==0.25.2
@@ -675,7 +675,7 @@ bluecurrent-api==1.3.2
bluemaestro-ble==0.4.1
# homeassistant.components.bluetooth
bluetooth-adapters==2.1.0
bluetooth-adapters==2.2.0
# homeassistant.components.bluetooth
bluetooth-auto-recovery==1.6.4
@@ -1447,7 +1447,7 @@ ld2410-ble==0.1.1
leaone-ble==0.3.0
# homeassistant.components.led_ble
led-ble==1.1.8
led-ble==1.1.11
# homeassistant.components.lektrico
lektricowifi==0.1
@@ -2874,7 +2874,7 @@ renson-endura-delta==1.7.2
reolink-aio==0.20.0
# homeassistant.components.radio_frequency
rf-protocols==3.2.0
rf-protocols==4.0.0
# homeassistant.components.idteck_prox
rfk101py==0.0.1
@@ -3311,7 +3311,7 @@ vtjp==0.2.1
# homeassistant.components.samsungtv
# homeassistant.components.wake_on_lan
wakeonlan==3.1.0
wakeonlan==3.3.0
# homeassistant.components.wallbox
wallbox==0.9.0
+1 -1
View File
@@ -97,7 +97,7 @@ uuid==1000000000.0.0
# even newer versions seem to introduce new issues, it's useful
# for us to pin all these
# requirements so we can directly link HA versions to these library versions.
anyio==4.10.0
anyio==4.13.0
h11==0.16.0
httpcore==1.0.9
+2 -3
View File
@@ -377,14 +377,13 @@ async def test_google_config_expose_entity(
)
cloud_client = hass.data[DATA_CLOUD].client
state = State(entity_entry.entity_id, "on")
gconf = await cloud_client.get_google_config()
assert gconf.should_expose(state)
assert gconf.should_expose(entity_entry.entity_id)
async_expose_entity(hass, "cloud.google_assistant", entity_entry.entity_id, False)
assert not gconf.should_expose(state)
assert not gconf.should_expose(entity_entry.entity_id)
@pytest.mark.usefixtures("mock_cloud_setup", "mock_cloud_login")
+10 -18
View File
@@ -27,7 +27,7 @@ from homeassistant.const import (
EVENT_HOMEASSISTANT_STARTED,
EntityCategory,
)
from homeassistant.core import CoreState, HomeAssistant, State
from homeassistant.core import CoreState, HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow
@@ -431,32 +431,24 @@ async def test_google_config_expose_entity_prefs(
expose_new(hass, True)
expose_entity(hass, entity_entry5.entity_id, False)
state = State("light.kitchen", "on")
state_config = State(entity_entry1.entity_id, "on")
state_diagnostic = State(entity_entry2.entity_id, "on")
state_hidden_integration = State(entity_entry3.entity_id, "on")
state_hidden_user = State(entity_entry4.entity_id, "on")
state_not_exposed = State(entity_entry5.entity_id, "on")
state_exposed_default = State(entity_entry6.entity_id, "on")
# an entity which is not in the entity registry can be exposed
expose_entity(hass, "light.kitchen", True)
assert mock_conf.should_expose(state)
assert mock_conf.should_expose("light.kitchen")
# categorized and hidden entities should not be exposed
assert not mock_conf.should_expose(state_config)
assert not mock_conf.should_expose(state_diagnostic)
assert not mock_conf.should_expose(state_hidden_integration)
assert not mock_conf.should_expose(state_hidden_user)
assert not mock_conf.should_expose(entity_entry1.entity_id)
assert not mock_conf.should_expose(entity_entry2.entity_id)
assert not mock_conf.should_expose(entity_entry3.entity_id)
assert not mock_conf.should_expose(entity_entry4.entity_id)
# this has been hidden
assert not mock_conf.should_expose(state_not_exposed)
assert not mock_conf.should_expose(entity_entry5.entity_id)
# exposed by default
assert mock_conf.should_expose(state_exposed_default)
assert mock_conf.should_expose(entity_entry6.entity_id)
expose_entity(hass, entity_entry5.entity_id, True)
assert mock_conf.should_expose(state_not_exposed)
assert mock_conf.should_expose(entity_entry5.entity_id)
expose_entity(hass, entity_entry5.entity_id, None)
assert not mock_conf.should_expose(state_not_exposed)
assert not mock_conf.should_expose(entity_entry5.entity_id)
@pytest.mark.usefixtures("mock_expired_cloud_login")
@@ -60,9 +60,9 @@ class MockConfig(http.GoogleConfig):
"""Get agent user ID making request."""
return context.user_id
def should_expose(self, state):
def should_expose(self, entity_id):
"""Expose it all."""
return self._should_expose is None or self._should_expose(state)
return self._should_expose is None or self._should_expose(entity_id)
@property
def should_report_state(self):
+1 -21
View File
@@ -14,7 +14,6 @@ import pytest
from homeassistant.components.google_assistant import GOOGLE_ASSISTANT_SCHEMA
from homeassistant.components.google_assistant.const import (
DOMAIN,
EVENT_COMMAND_RECEIVED,
HOMEGRAPH_TOKEN_URL,
REPORT_STATE_BASE_URL,
@@ -29,7 +28,7 @@ from homeassistant.components.google_assistant.http import (
async_get_users,
)
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STARTED
from homeassistant.core import CoreState, HomeAssistant, State
from homeassistant.core import CoreState, HomeAssistant
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
@@ -344,25 +343,6 @@ async def test_secure_device_pin_config(hass: HomeAssistant) -> None:
assert config.secure_devices_pin == secure_pin
async def test_should_expose(hass: HomeAssistant) -> None:
"""Test the google config should expose method."""
config = GoogleConfig(hass, DUMMY_CONFIG)
await config.async_initialize()
with patch.object(config, "async_call_homegraph_api"):
# Wait for google_assistant.helpers.async_initialize.sync_google to be called
await hass.async_block_till_done()
assert (
config.should_expose(State(DOMAIN + ".mock", "mock", {"view": "not None"}))
is False
)
with patch.object(config, "async_call_homegraph_api"):
# Wait for google_assistant.helpers.async_initialize.sync_google to be called
await hass.async_block_till_done()
async def test_missing_service_account(hass: HomeAssistant) -> None:
"""Test the google config _async_request_sync_devices."""
incorrect_config = GOOGLE_ASSISTANT_SCHEMA(
@@ -84,7 +84,7 @@ def registries(
async def test_async_handle_message(hass: HomeAssistant) -> None:
"""Test the async handle message method."""
config = MockConfig(
should_expose=lambda state: state.entity_id != "light.not_expose",
should_expose=lambda entity_id: entity_id != "light.not_expose",
entity_config={
"light.demo_light": {
const.CONF_ROOM_HINT: "Living Room",
@@ -172,7 +172,7 @@ async def test_sync_message(hass: HomeAssistant, registries) -> None:
hass.states.async_set("light.not_expose", "on")
config = MockConfig(
should_expose=lambda state: state.entity_id != "light.not_expose",
should_expose=lambda entity_id: entity_id != "light.not_expose",
entity_config={
"light.demo_light": {
const.CONF_ROOM_HINT: "Living Room",
@@ -1392,7 +1392,7 @@ async def test_reachable_devices(hass: HomeAssistant) -> None:
hass.states.async_set("lock.has_2fa", "on")
config = MockConfig(
should_expose=lambda state: state.entity_id != "light.not_expose",
should_expose=lambda entity_id: entity_id != "light.not_expose",
)
user_agent_id = "mock-user-id"
+1 -32
View File
@@ -1,10 +1,6 @@
"""Common fixtures for the Novy Cooker Hood tests."""
from collections.abc import Iterator
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from rf_protocols.loader import CodeCollection
from homeassistant.components.novy_cooker_hood.const import CONF_TRANSMITTER, DOMAIN
from homeassistant.const import CONF_CODE
@@ -12,38 +8,11 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry
from tests.components.radio_frequency.common import (
MockRadioFrequencyCommand,
MockRadioFrequencyEntity,
)
from tests.components.radio_frequency.common import MockRadioFrequencyEntity
TRANSMITTER_ENTITY_ID = "radio_frequency.test_rf_transmitter"
@pytest.fixture(autouse=True)
def mock_get_codes() -> Iterator[MagicMock]:
"""Patch the bundled-codes loader so tests don't hit the filesystem."""
fake_collection = MagicMock(spec=CodeCollection)
fake_collection.async_load_command = AsyncMock(
side_effect=lambda name: MockRadioFrequencyCommand()
)
with (
patch(
"homeassistant.components.novy_cooker_hood.light.get_codes_for_code",
return_value=fake_collection,
),
patch(
"homeassistant.components.novy_cooker_hood.fan.get_codes_for_code",
return_value=fake_collection,
),
patch(
"homeassistant.components.novy_cooker_hood.config_flow.get_codes_for_code",
return_value=fake_collection,
),
):
yield fake_collection
@pytest.fixture
def mock_config_entry(
mock_rf_entity: MockRadioFrequencyEntity,
@@ -1,11 +1,11 @@
"""Test the Novy Hood config flow."""
from collections.abc import Iterator
from unittest.mock import MagicMock, patch
from unittest.mock import patch
import pytest
from rf_protocols.codes.novy.cooker_hood import NovyCookerHoodButton
from homeassistant.components.novy_cooker_hood.commands import COMMAND_LIGHT
from homeassistant.components.novy_cooker_hood.const import CONF_TRANSMITTER, DOMAIN
from homeassistant.components.radio_frequency import DATA_COMPONENT, DOMAIN as RF_DOMAIN
from homeassistant.config_entries import SOURCE_USER
@@ -49,7 +49,6 @@ async def _start_user_flow(hass: HomeAssistant, code: str = "1") -> dict:
async def test_user_flow_test_then_finish(
hass: HomeAssistant,
mock_get_codes: MagicMock,
mock_rf_entity: MockRadioFrequencyEntity,
entity_registry: er.EntityRegistry,
) -> None:
@@ -58,8 +57,10 @@ async def test_user_flow_test_then_finish(
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "test_light"
mock_get_codes.async_load_command.assert_awaited_with(COMMAND_LIGHT)
assert len(mock_rf_entity.send_command_calls) == 2
sent = mock_rf_entity.send_command_calls[0].command
assert sent.key == NovyCookerHoodButton.LIGHT.code
assert sent.channel == 3
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "finish"}
@@ -77,7 +78,6 @@ async def test_user_flow_test_then_finish(
async def test_user_flow_retry_picks_different_code(
hass: HomeAssistant,
mock_get_codes: MagicMock,
mock_rf_entity: MockRadioFrequencyEntity,
entity_registry: er.EntityRegistry,
) -> None:
@@ -99,9 +99,13 @@ async def test_user_flow_retry_picks_different_code(
},
)
assert result["type"] is FlowResultType.MENU
# One load per test x two tests; two sends per test x two tests.
assert mock_get_codes.async_load_command.await_count == 2
assert len(mock_rf_entity.send_command_calls) == 4
assert [c.command.channel for c in mock_rf_entity.send_command_calls] == [
1,
1,
7,
7,
]
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "finish"}
@@ -127,7 +131,6 @@ async def test_user_flow_test_transmit_failure(
async def test_recover_after_transmit_failure(
hass: HomeAssistant,
mock_get_codes: MagicMock,
mock_rf_entity: MockRadioFrequencyEntity,
) -> None:
"""The user can Retry from test_failed and complete the flow."""
@@ -183,7 +186,6 @@ async def test_unique_id_already_configured(
async def test_same_transmitter_different_code_is_allowed(
hass: HomeAssistant,
mock_get_codes: MagicMock,
mock_config_entry: MockConfigEntry,
mock_rf_entity: MockRadioFrequencyEntity,
entity_registry: er.EntityRegistry,
@@ -205,7 +207,6 @@ async def test_same_transmitter_different_code_is_allowed(
async def test_reconfigure_updates_entry(
hass: HomeAssistant,
mock_get_codes: MagicMock,
init_novy_cooker_hood: MockConfigEntry,
mock_rf_entity: MockRadioFrequencyEntity,
entity_registry: er.EntityRegistry,
@@ -224,7 +225,9 @@ async def test_reconfigure_updates_entry(
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "test_light"
mock_get_codes.async_load_command.assert_awaited_with(COMMAND_LIGHT)
sent = mock_rf_entity.send_command_calls[-1].command
assert sent.key == NovyCookerHoodButton.LIGHT.code
assert sent.channel == 4
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "finish"}
@@ -239,7 +242,6 @@ async def test_reconfigure_updates_entry(
async def test_reconfigure_frees_old_unique_id(
hass: HomeAssistant,
mock_get_codes: MagicMock,
init_novy_cooker_hood: MockConfigEntry,
mock_rf_entity: MockRadioFrequencyEntity,
) -> None:
@@ -295,7 +297,6 @@ async def test_reconfigure_aborts_on_collision(
async def test_reconfigure_retry_returns_to_picker(
hass: HomeAssistant,
mock_get_codes: MagicMock,
init_novy_cooker_hood: MockConfigEntry,
mock_rf_entity: MockRadioFrequencyEntity,
) -> None:
@@ -326,7 +327,6 @@ async def test_no_transmitters(hass: HomeAssistant) -> None:
async def test_recover_after_no_transmitters(
hass: HomeAssistant,
mock_get_codes: MagicMock,
) -> None:
"""User can re-init the flow after the radio_frequency integration loads."""
result = await hass.config_entries.flow.async_init(
@@ -1,13 +1,12 @@
"""Tests for the Novy Hood light platform."""
from unittest.mock import MagicMock, call
from rf_protocols.codes.novy.cooker_hood import NovyCookerHoodButton
from homeassistant.components.light import (
DOMAIN as LIGHT_DOMAIN,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
from homeassistant.components.novy_cooker_hood.commands import COMMAND_LIGHT
from homeassistant.const import (
ATTR_ASSUMED_STATE,
ATTR_ENTITY_ID,
@@ -28,7 +27,6 @@ ENTITY_ID = "light.novy_cooker_hood_light"
async def test_turn_on_and_off_send_light_once_each(
hass: HomeAssistant,
mock_get_codes: MagicMock,
mock_rf_entity: MockRadioFrequencyEntity,
init_novy_cooker_hood: MockConfigEntry,
) -> None:
@@ -66,11 +64,11 @@ async def test_turn_on_and_off_send_light_once_each(
state = hass.states.get(ENTITY_ID)
assert state is not None
assert state.state == STATE_OFF
assert mock_get_codes.async_load_command.await_args_list == [
call(COMMAND_LIGHT),
call(COMMAND_LIGHT),
]
assert len(mock_rf_entity.send_command_calls) == 2
assert [c.command.key for c in mock_rf_entity.send_command_calls] == [
NovyCookerHoodButton.LIGHT.code,
NovyCookerHoodButton.LIGHT.code,
]
async def test_restore_state(
+17 -2
View File
@@ -13,13 +13,18 @@ from homeassistant.components.proxmoxve import CONF_AUTH_METHOD, CONF_HOST, CONF
from homeassistant.components.proxmoxve.const import (
CONF_NODE,
CONF_NODES,
CONF_TOKEN,
CONF_TOKEN_ID,
CONF_TOKEN_SECRET,
DOMAIN,
)
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER, ConfigEntryState
from homeassistant.const import CONF_PASSWORD, CONF_PORT, CONF_USERNAME, CONF_VERIFY_SSL
from homeassistant.const import (
CONF_PASSWORD,
CONF_PORT,
CONF_TOKEN,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
@@ -57,6 +62,11 @@ MOCK_USER_AUTH_STEP_TOKEN = {
CONF_TOKEN_SECRET: "test_token_secret",
}
MOCK_USER_AUTH_STEP_TOKEN_FULL_ID = {
CONF_TOKEN_ID: "test_user@pam!test_token_id",
CONF_TOKEN_SECRET: "test_token_secret",
}
# Other authentication method (e.g. LDAP) with realm
MOCK_USER_STEP_OTHER = {
**MOCK_USER_STEP,
@@ -92,6 +102,11 @@ MOCK_USER_FINAL = {
[
(MOCK_USER_STEP, MOCK_USER_AUTH_STEP_PASSWORD, MOCK_TEST_CONFIG),
(MOCK_USER_STEP_TOKEN, MOCK_USER_AUTH_STEP_TOKEN, MOCK_TEST_TOKEN_CONFIG),
(
MOCK_USER_STEP_TOKEN,
MOCK_USER_AUTH_STEP_TOKEN_FULL_ID,
MOCK_TEST_TOKEN_CONFIG,
),
(MOCK_USER_STEP_OTHER, MOCK_USER_AUTH_STEP_OTHER, MOCK_TEST_OTHER_CONFIG),
(
MOCK_USER_STEP_OTHER_TOKEN,