Skip to content

Commit

Permalink
Reolink ptz service to specify move speed (#104350)
Browse files Browse the repository at this point in the history
  • Loading branch information
starkillerOG committed Nov 24, 2023
1 parent 9ed7456 commit e9dd158
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 1 deletion.
42 changes: 41 additions & 1 deletion homeassistant/components/reolink/button.py
Expand Up @@ -7,22 +7,31 @@

from reolink_aio.api import GuardEnum, Host, PtzEnum
from reolink_aio.exceptions import ReolinkError
import voluptuous as vol

from homeassistant.components.button import (
ButtonDeviceClass,
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.components.camera import CameraEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import (
AddEntitiesCallback,
async_get_current_platform,
)

from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkChannelCoordinatorEntity, ReolinkHostCoordinatorEntity

ATTR_SPEED = "speed"
SUPPORT_PTZ_SPEED = CameraEntityFeature.STREAM


@dataclass(kw_only=True)
class ReolinkButtonEntityDescription(
Expand All @@ -33,6 +42,7 @@ class ReolinkButtonEntityDescription(
enabled_default: Callable[[Host, int], bool] | None = None
method: Callable[[Host, int], Any]
supported: Callable[[Host, int], bool] = lambda api, ch: True
ptz_cmd: str | None = None


@dataclass(kw_only=True)
Expand Down Expand Up @@ -60,27 +70,31 @@ class ReolinkHostButtonEntityDescription(ButtonEntityDescription):
icon="mdi:pan",
supported=lambda api, ch: api.supported(ch, "pan"),
method=lambda api, ch: api.set_ptz_command(ch, command=PtzEnum.left.value),
ptz_cmd=PtzEnum.left.value,
),
ReolinkButtonEntityDescription(
key="ptz_right",
translation_key="ptz_right",
icon="mdi:pan",
supported=lambda api, ch: api.supported(ch, "pan"),
method=lambda api, ch: api.set_ptz_command(ch, command=PtzEnum.right.value),
ptz_cmd=PtzEnum.right.value,
),
ReolinkButtonEntityDescription(
key="ptz_up",
translation_key="ptz_up",
icon="mdi:pan",
supported=lambda api, ch: api.supported(ch, "tilt"),
method=lambda api, ch: api.set_ptz_command(ch, command=PtzEnum.up.value),
ptz_cmd=PtzEnum.up.value,
),
ReolinkButtonEntityDescription(
key="ptz_down",
translation_key="ptz_down",
icon="mdi:pan",
supported=lambda api, ch: api.supported(ch, "tilt"),
method=lambda api, ch: api.set_ptz_command(ch, command=PtzEnum.down.value),
ptz_cmd=PtzEnum.down.value,
),
ReolinkButtonEntityDescription(
key="ptz_zoom_in",
Expand All @@ -89,6 +103,7 @@ class ReolinkHostButtonEntityDescription(ButtonEntityDescription):
entity_registry_enabled_default=False,
supported=lambda api, ch: api.supported(ch, "zoom_basic"),
method=lambda api, ch: api.set_ptz_command(ch, command=PtzEnum.zoomin.value),
ptz_cmd=PtzEnum.zoomin.value,
),
ReolinkButtonEntityDescription(
key="ptz_zoom_out",
Expand All @@ -97,6 +112,7 @@ class ReolinkHostButtonEntityDescription(ButtonEntityDescription):
entity_registry_enabled_default=False,
supported=lambda api, ch: api.supported(ch, "zoom_basic"),
method=lambda api, ch: api.set_ptz_command(ch, command=PtzEnum.zoomout.value),
ptz_cmd=PtzEnum.zoomout.value,
),
ReolinkButtonEntityDescription(
key="ptz_calibrate",
Expand Down Expand Up @@ -158,6 +174,14 @@ async def async_setup_entry(
)
async_add_entities(entities)

platform = async_get_current_platform()
platform.async_register_entity_service(
"ptz_move",
{vol.Required(ATTR_SPEED): cv.positive_int},
"async_ptz_move",
[SUPPORT_PTZ_SPEED],
)


class ReolinkButtonEntity(ReolinkChannelCoordinatorEntity, ButtonEntity):
"""Base button entity class for Reolink IP cameras."""
Expand All @@ -182,13 +206,29 @@ def __init__(
entity_description.enabled_default(self._host.api, self._channel)
)

if (
self._host.api.supported(channel, "ptz_speed")
and entity_description.ptz_cmd is not None
):
self._attr_supported_features = SUPPORT_PTZ_SPEED

async def async_press(self) -> None:
"""Execute the button action."""
try:
await self.entity_description.method(self._host.api, self._channel)
except ReolinkError as err:
raise HomeAssistantError(err) from err

async def async_ptz_move(self, **kwargs) -> None:
"""PTZ move with speed."""
speed = kwargs[ATTR_SPEED]
try:
await self._host.api.set_ptz_command(
self._channel, command=self.entity_description.ptz_cmd, speed=speed
)
except ReolinkError as err:
raise HomeAssistantError(err) from err


class ReolinkHostButtonEntity(ReolinkHostCoordinatorEntity, ButtonEntity):
"""Base button entity class for Reolink IP cameras."""
Expand Down
18 changes: 18 additions & 0 deletions homeassistant/components/reolink/services.yaml
@@ -0,0 +1,18 @@
# Describes the format for available reolink services

ptz_move:
target:
entity:
integration: reolink
domain: button
supported_features:
- camera.CameraEntityFeature.STREAM
fields:
speed:
required: true
default: 10
selector:
number:
min: 1
max: 64
step: 1
12 changes: 12 additions & 0 deletions homeassistant/components/reolink/strings.json
Expand Up @@ -61,6 +61,18 @@
"description": "\"{name}\" with model \"{model}\" and hardware version \"{hw_version}\" is running a old firmware version \"{current_firmware}\", while at least firmware version \"{required_firmware}\" is required for proper operation of the Reolink integration. The latest firmware can be downloaded from the [Reolink download center]({download_link})."
}
},
"services": {
"ptz_move": {
"name": "PTZ move",
"description": "Move the camera with a specific speed.",
"fields": {
"speed": {
"name": "Speed",
"description": "PTZ move speed."
}
}
}
},
"entity": {
"binary_sensor": {
"face": {
Expand Down

0 comments on commit e9dd158

Please sign in to comment.