Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ jobs:
languages: python

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
with:
category: "/language:python"
2 changes: 1 addition & 1 deletion homeassistant/components/google_sheets/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def _append_to_sheet(call: ServiceCall, entry: GoogleSheetsConfigEntry) -> None:
client = Client(Credentials(entry.data[CONF_TOKEN][CONF_ACCESS_TOKEN])) # type: ignore[no-untyped-call]
sheet = client.open_by_key(entry.unique_id)
worksheet = _get_worksheet(sheet, call.data.get(WORKSHEET))
columns: list[str] = next(iter(worksheet.get_values("A1:ZZ1")), [])
columns: list[str] = next(iter(worksheet.get_values("1:1")), [])
add_created_column = call.data[ADD_CREATED_COLUMN]
now = str(dt_util.now())
rows = []
Expand Down
23 changes: 8 additions & 15 deletions homeassistant/components/imou/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from typing import override

from pyimouapi.const import PARAM_RESTART_DEVICE
from pyimouapi.exceptions import ImouException
from pyimouapi.ha_device import ImouHaDevice

from homeassistant.components.button import (
Expand All @@ -12,12 +11,12 @@
ButtonEntityDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import DOMAIN, PTZ_MOVE_DURATION_MS, imou_device_identifier
from .const import PTZ_MOVE_DURATION_MS, imou_device_identifier
from .coordinator import ImouConfigEntry, ImouDataUpdateCoordinator
from .entity import ImouEntity
from .helpers import async_wrap_imou_command

PARALLEL_UPDATES = 1
# Button types not yet exported by pyimouapi (keep module-local).
Expand Down Expand Up @@ -100,18 +99,12 @@ class ImouButton(ImouEntity, ButtonEntity):
entity_description: ButtonEntityDescription

@override
@async_wrap_imou_command("press_button_failed")
async def async_press(self) -> None:
"""Handle button press."""
duration = PTZ_MOVE_DURATION_MS if self._entity_type in PTZ_BUTTON_TYPES else 0
try:
await self.coordinator.device_manager.async_press_button(
self.device,
self._entity_type,
duration,
)
except ImouException as e:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="press_button_failed",
translation_placeholders={"error": e.message},
) from e
await self.coordinator.device_manager.async_press_button(
self.device,
self._entity_type,
duration,
)
39 changes: 13 additions & 26 deletions homeassistant/components/imou/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from typing import override

from pyimouapi.const import PARAM_HD, PARAM_MOTION_DETECT, PARAM_STATE
from pyimouapi.exceptions import ImouException
from pyimouapi.ha_device import ImouHaDevice

from homeassistant.components.camera import (
Expand All @@ -13,12 +12,12 @@
CameraEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import DOMAIN, PARAM_HEADER_DETECT, imou_device_identifier
from .const import PARAM_HEADER_DETECT, imou_device_identifier
from .coordinator import ImouConfigEntry, ImouDataUpdateCoordinator
from .entity import ImouEntity
from .helpers import async_wrap_imou_command

PARALLEL_UPDATES = 0

Expand Down Expand Up @@ -89,37 +88,25 @@ def __init__(
super().__init__(coordinator, description, device)

@override
@async_wrap_imou_command("get_stream_failed")
async def stream_source(self) -> str | None:
"""Return the live stream URL from the Imou cloud."""
try:
return await self.coordinator.device_manager.async_get_device_stream(
self.device,
self.entity_description.resolution,
PYIMOUAPI_LIVE_PROTOCOL,
)
except ImouException as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="get_stream_failed",
translation_placeholders={"error": err.message},
) from err
return await self.coordinator.device_manager.async_get_device_stream(
self.device,
self.entity_description.resolution,
PYIMOUAPI_LIVE_PROTOCOL,
)

@override
@async_wrap_imou_command("get_image_failed")
async def async_camera_image(
self, width: int | None = None, height: int | None = None
) -> bytes | None:
"""Return bytes of camera image."""
try:
return await self.coordinator.device_manager.async_get_device_image(
self.device,
PYIMOUAPI_SNAPSHOT_WAIT_SECONDS,
)
except ImouException as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="get_image_failed",
translation_placeholders={"error": err.message},
) from err
return await self.coordinator.device_manager.async_get_device_image(
self.device,
PYIMOUAPI_SNAPSHOT_WAIT_SECONDS,
)

@property
@override
Expand Down
44 changes: 44 additions & 0 deletions homeassistant/components/imou/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Helpers for Imou."""

from collections.abc import Awaitable, Callable, Coroutine
from functools import wraps
from typing import Any, Concatenate

from pyimouapi.exceptions import ImouException, InvalidAppIdOrSecretException

from homeassistant.exceptions import HomeAssistantError

from .const import DOMAIN
from .entity import ImouEntity


def async_wrap_imou_command[_T: ImouEntity, **_P, _R](
error_key: str,
) -> Callable[
[Callable[Concatenate[_T, _P], Awaitable[_R]]],
Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R]],
]:
"""Wrap an Imou command and start reauthentication when credentials are rejected."""

def decorator(
func: Callable[Concatenate[_T, _P], Awaitable[_R]],
) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R]]:
@wraps(func)
async def wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> _R:
try:
return await func(self, *args, **kwargs)
except InvalidAppIdOrSecretException as err:
self.coordinator.config_entry.async_start_reauth(self.hass)
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="invalid_auth",
) from err
except ImouException as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key=error_key,
) from err

return wrapper

return decorator
23 changes: 8 additions & 15 deletions homeassistant/components/imou/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@
PARAM_NIGHT_VISION_MODE,
PARAM_OPTIONS,
)
from pyimouapi.exceptions import ImouException
from pyimouapi.ha_device import ImouHaDevice

from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import DOMAIN, imou_device_identifier
from .const import imou_device_identifier
from .coordinator import ImouConfigEntry, ImouDataUpdateCoordinator
from .entity import ImouEntity
from .helpers import async_wrap_imou_command

PARALLEL_UPDATES = 0

Expand Down Expand Up @@ -87,18 +86,12 @@ def current_option(self) -> str | None:
return self.device.selects[self._entity_type][PARAM_CURRENT_OPTION]

@override
@async_wrap_imou_command("select_option_failed")
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
try:
await self.coordinator.device_manager.async_select_option(
self.device,
self._entity_type,
option,
)
except ImouException as e:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="select_option_failed",
translation_placeholders={"error": e.message},
) from e
await self.coordinator.device_manager.async_select_option(
self.device,
self._entity_type,
option,
)
await self.coordinator.async_request_refresh()
10 changes: 5 additions & 5 deletions homeassistant/components/imou/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,22 +138,22 @@
},
"exceptions": {
"get_image_failed": {
"message": "Could not get a snapshot from Imou: {error}"
"message": "Could not get a snapshot from Imou"
},
"get_stream_failed": {
"message": "Could not get the live stream URL from Imou: {error}"
"message": "Could not get the live stream URL from Imou"
},
"invalid_auth": {
"message": "Imou rejected the App ID and App secret"
},
"press_button_failed": {
"message": "Imou rejected the button press: {error}"
"message": "Imou rejected the button press"
},
"select_option_failed": {
"message": "Imou rejected the new option: {error}"
"message": "Imou rejected the new option"
},
"switch_operation_failed": {
"message": "Imou rejected the switch change: {error}"
"message": "Imou rejected the switch change"
}
},
"selector": {
Expand Down
22 changes: 7 additions & 15 deletions homeassistant/components/imou/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from typing import Any, override

from pyimouapi.const import PARAM_MOTION_DETECT, PARAM_STATE
from pyimouapi.exceptions import ImouException
from pyimouapi.ha_device import ImouHaDevice

from homeassistant.components.switch import (
Expand All @@ -12,11 +11,9 @@
SwitchEntityDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import (
DOMAIN,
PARAM_AB_ALARM_SOUND,
PARAM_AUDIO_ENCODE_CONTROL,
PARAM_CLOSE_CAMERA,
Expand All @@ -28,6 +25,7 @@
)
from .coordinator import ImouConfigEntry, ImouDataUpdateCoordinator
from .entity import ImouEntity
from .helpers import async_wrap_imou_command

PARALLEL_UPDATES = 0

Expand Down Expand Up @@ -122,18 +120,12 @@ async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
await self._async_switch_operation(False)

@async_wrap_imou_command("switch_operation_failed")
async def _async_switch_operation(self, enable: bool) -> None:
"""Call the vendor library to change switch state."""
try:
await self.coordinator.device_manager.async_switch_operation(
self.device,
self._entity_type,
enable,
)
except ImouException as e:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="switch_operation_failed",
translation_placeholders={"error": e.message},
) from e
await self.coordinator.device_manager.async_switch_operation(
self.device,
self._entity_type,
enable,
)
await self.coordinator.async_request_refresh()
4 changes: 4 additions & 0 deletions homeassistant/components/lyngdorf/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
{
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:2",
"manufacturer": "Lyngdorf"
},
{
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:2",
"manufacturer": "Steinway Lyngdorf"
}
]
}
2 changes: 1 addition & 1 deletion homeassistant/components/midea/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ rules:
log-when-unavailable: todo
parallel-updates: done
reauthentication-flow: todo
test-coverage: todo
test-coverage: done

# Gold
devices: todo
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/miele/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class MieleBinarySensorDefinition:
BINARY_SENSOR_TYPES: Final[tuple[MieleBinarySensorDefinition, ...]] = (
MieleBinarySensorDefinition(
types=(
MieleAppliance.COFFEE_SYSTEM,
MieleAppliance.DISH_WARMER,
MieleAppliance.DISHWASHER,
MieleAppliance.FREEZER,
Expand Down
16 changes: 8 additions & 8 deletions homeassistant/components/nintendo_parental_controls/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,27 +112,27 @@
"fields": {
"bonus_time": {
"description": "The amount of bonus time to add in minutes. Maximum is 30 minutes, minimum is 5.",
"name": "Bonus Time"
"name": "Bonus time"
},
"device_id": {
"description": "The ID of the device to add bonus time to.",
"name": "Device"
}
},
"name": "Add Bonus Time"
"name": "Add bonus time"
},
"device_usage_report": {
"description": "Get today's application usage details for a device.",
"description": "Retrieves today's application usage details for a device.",
"fields": {
"device_id": {
"description": "The ID of the device to get usage details for.",
"name": "Device"
}
},
"name": "Device usage report"
"name": "Get device usage report"
},
"player_usage_report": {
"description": "Get today's application usage details for a specific player.",
"description": "Retrieves today's application usage details for a specific player.",
"fields": {
"device_id": {
"description": "The ID of the device to get player usage details for.",
Expand All @@ -143,10 +143,10 @@
"name": "Player"
}
},
"name": "Player usage report"
"name": "Get player usage report"
},
"update_pin_code": {
"description": "Update the PIN code for the selected Nintendo Switch.",
"description": "Updates the PIN code for the selected Nintendo Switch.",
"fields": {
"device_id": {
"description": "The ID of the device to update the PIN code for.",
Expand All @@ -157,7 +157,7 @@
"name": "PIN"
}
},
"name": "Update PIN Code"
"name": "Update PIN code"
}
}
}
Loading
Loading