Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,7 @@ omit =
homeassistant/components/reolink/entity.py
homeassistant/components/reolink/host.py
homeassistant/components/reolink/number.py
homeassistant/components/reolink/select.py
homeassistant/components/reolink/siren.py
homeassistant/components/reolink/switch.py
homeassistant/components/reolink/update.py
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/reolink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
Platform.BUTTON,
Platform.CAMERA,
Platform.NUMBER,
Platform.SELECT,
Platform.SIREN,
Platform.SWITCH,
Platform.UPDATE,
Expand Down
123 changes: 123 additions & 0 deletions homeassistant/components/reolink/select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""Component providing support for Reolink select entities."""
from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass
from typing import Any

from reolink_aio.api import DayNightEnum, Host, SpotlightModeEnum

from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkCoordinatorEntity


@dataclass
class ReolinkSelectEntityDescriptionMixin:
"""Mixin values for Reolink select entities."""

method: Callable[[Host, int, str], Any]
get_options: list[str] | Callable[[Host, int], list[str]]


@dataclass
class ReolinkSelectEntityDescription(
SelectEntityDescription, ReolinkSelectEntityDescriptionMixin
):
"""A class that describes select entities."""

supported: Callable[[Host, int], bool] = lambda api, ch: True
value: Callable[[Host, int], str] | None = None


SELECT_ENTITIES = (
ReolinkSelectEntityDescription(
key="floodlight_mode",
name="Floodlight mode",
icon="mdi:spotlight-beam",
entity_category=EntityCategory.CONFIG,
translation_key="floodlight_mode",
get_options=[mode.name for mode in SpotlightModeEnum],
supported=lambda api, ch: api.supported(ch, "floodLight"),
value=lambda api, ch: SpotlightModeEnum(api.whiteled_mode(ch)).name,
method=lambda api, ch, name: api.set_whiteled(ch, mode=name),
),
ReolinkSelectEntityDescription(
key="day_night_mode",
name="Day night mode",
icon="mdi:theme-light-dark",
entity_category=EntityCategory.CONFIG,
translation_key="day_night_mode",
get_options=[mode.name for mode in DayNightEnum],
supported=lambda api, ch: api.supported(ch, "dayNight"),
value=lambda api, ch: DayNightEnum(api.daynight_state(ch)).name,
method=lambda api, ch, name: api.set_daynight(ch, DayNightEnum[name].value),
),
ReolinkSelectEntityDescription(
key="ptz_preset",
name="PTZ preset",
icon="mdi:pan",
get_options=lambda api, ch: list(api.ptz_presets(ch)),
supported=lambda api, ch: api.supported(ch, "ptz_presets"),
method=lambda api, ch, name: api.set_ptz_command(ch, preset=name),
),
)


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up a Reolink select entities."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id]

async_add_entities(
ReolinkSelectEntity(reolink_data, channel, entity_description)
for entity_description in SELECT_ENTITIES
for channel in reolink_data.host.api.channels
if entity_description.supported(reolink_data.host.api, channel)
)


class ReolinkSelectEntity(ReolinkCoordinatorEntity, SelectEntity):
"""Base select entity class for Reolink IP cameras."""

entity_description: ReolinkSelectEntityDescription

def __init__(
self,
reolink_data: ReolinkData,
channel: int,
entity_description: ReolinkSelectEntityDescription,
) -> None:
"""Initialize Reolink select entity."""
super().__init__(reolink_data, channel)
self.entity_description = entity_description

self._attr_unique_id = (
f"{self._host.unique_id}_{channel}_{entity_description.key}"
)

if callable(entity_description.get_options):
self._attr_options = entity_description.get_options(self._host.api, channel)
else:
self._attr_options = entity_description.get_options

@property
def current_option(self) -> str | None:
"""Return the current option."""
if self.entity_description.value is None:
return None

return self.entity_description.value(self._host.api, self._channel)

async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
await self.entity_description.method(self._host.api, self._channel, option)
18 changes: 18 additions & 0 deletions homeassistant/components/reolink/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,23 @@
"title": "Reolink webhook URL uses HTTPS (SSL)",
"description": "Reolink products can not push motion events to an HTTPS address (SSL), please configure a (local) HTTP address under \"Home Assistant URL\" in the [network settings]({network_link}). The current (local) address is: `{base_url}`"
}
},
"entity": {
"select": {
"floodlight_mode": {
"state": {
"off": "Off",
"auto": "Auto",
"schedule": "Schedule"
}
},
"day_night_mode": {
"state": {
"auto": "Auto",
"color": "Color",
"blackwhite": "Black&White"
}
}
}
}
}