Add shorthand attribute support to Camera platform (#59837)
This commit is contained in:
parent
57fd632cd9
commit
6b9c2d8295
9 changed files with 52 additions and 51 deletions
|
@ -412,7 +412,7 @@ class AmcrestCam(Camera):
|
|||
f"{serial_number}-{self._resolution}-{self._channel}"
|
||||
)
|
||||
_LOGGER.debug("Assigned unique_id=%s", self._attr_unique_id)
|
||||
self.is_streaming = self._get_video()
|
||||
self._attr_is_streaming = self._get_video()
|
||||
self._is_recording = self._get_recording()
|
||||
self._motion_detection_enabled = self._get_motion_detection()
|
||||
self._audio_enabled = self._get_audio()
|
||||
|
|
|
@ -369,9 +369,21 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
class Camera(Entity):
|
||||
"""The base class for camera entities."""
|
||||
|
||||
# Entity Properties
|
||||
_attr_brand: str | None = None
|
||||
_attr_frame_interval: float = MIN_STREAM_INTERVAL
|
||||
_attr_frontend_stream_type: str | None
|
||||
_attr_is_on: bool = True
|
||||
_attr_is_recording: bool = False
|
||||
_attr_is_streaming: bool = False
|
||||
_attr_model: str | None = None
|
||||
_attr_motion_detection_enabled: bool = False
|
||||
_attr_should_poll: bool = False # No need to poll cameras
|
||||
_attr_state: None = None # State is determined by is_on
|
||||
_attr_supported_features: int = 0
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize a camera."""
|
||||
self.is_streaming: bool = False
|
||||
self.stream: Stream | None = None
|
||||
self.stream_options: dict[str, str] = {}
|
||||
self.content_type: str = DEFAULT_CONTENT_TYPE
|
||||
|
@ -379,45 +391,47 @@ class Camera(Entity):
|
|||
self._warned_old_signature = False
|
||||
self.async_update_token()
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""No need to poll cameras."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def entity_picture(self) -> str:
|
||||
"""Return a link to the camera feed as entity picture."""
|
||||
if self._attr_entity_picture is not None:
|
||||
return self._attr_entity_picture
|
||||
return ENTITY_IMAGE_URL.format(self.entity_id, self.access_tokens[-1])
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Flag supported features."""
|
||||
return 0
|
||||
return self._attr_supported_features
|
||||
|
||||
@property
|
||||
def is_recording(self) -> bool:
|
||||
"""Return true if the device is recording."""
|
||||
return False
|
||||
return self._attr_is_recording
|
||||
|
||||
@property
|
||||
def is_streaming(self) -> bool:
|
||||
"""Return true if the device is streaming."""
|
||||
return self._attr_is_streaming
|
||||
|
||||
@property
|
||||
def brand(self) -> str | None:
|
||||
"""Return the camera brand."""
|
||||
return None
|
||||
return self._attr_brand
|
||||
|
||||
@property
|
||||
def motion_detection_enabled(self) -> bool:
|
||||
"""Return the camera motion detection status."""
|
||||
return False
|
||||
return self._attr_motion_detection_enabled
|
||||
|
||||
@property
|
||||
def model(self) -> str | None:
|
||||
"""Return the camera model."""
|
||||
return None
|
||||
return self._attr_model
|
||||
|
||||
@property
|
||||
def frame_interval(self) -> float:
|
||||
"""Return the interval between frames of the mjpeg stream."""
|
||||
return MIN_STREAM_INTERVAL
|
||||
return self._attr_frame_interval
|
||||
|
||||
@property
|
||||
def frontend_stream_type(self) -> str | None:
|
||||
|
@ -427,6 +441,8 @@ class Camera(Entity):
|
|||
frontend which camera attributes and player to use. The default type
|
||||
is to use HLS, and components can override to change the type.
|
||||
"""
|
||||
if hasattr(self, "_attr_frontend_stream_type"):
|
||||
return self._attr_frontend_stream_type
|
||||
if not self.supported_features & SUPPORT_STREAM:
|
||||
return None
|
||||
return STREAM_TYPE_HLS
|
||||
|
@ -508,6 +524,7 @@ class Camera(Entity):
|
|||
return await self.handle_async_still_stream(request, self.frame_interval)
|
||||
|
||||
@property
|
||||
@final
|
||||
def state(self) -> str:
|
||||
"""Return the camera state."""
|
||||
if self.is_recording:
|
||||
|
@ -519,7 +536,7 @@ class Camera(Entity):
|
|||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if on."""
|
||||
return True
|
||||
return self._attr_is_on
|
||||
|
||||
def turn_off(self) -> None:
|
||||
"""Turn off camera."""
|
||||
|
|
|
@ -24,13 +24,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
class DemoCamera(Camera):
|
||||
"""The representation of a Demo camera."""
|
||||
|
||||
_attr_is_streaming = True
|
||||
_attr_motion_detection_enabled = False
|
||||
_attr_supported_features = SUPPORT_ON_OFF
|
||||
|
||||
def __init__(self, name, content_type):
|
||||
"""Initialize demo camera component."""
|
||||
super().__init__()
|
||||
self._name = name
|
||||
self._attr_name = name
|
||||
self.content_type = content_type
|
||||
self._motion_status = False
|
||||
self.is_streaming = True
|
||||
self._images_index = 0
|
||||
|
||||
async def async_camera_image(
|
||||
|
@ -43,42 +45,24 @@ class DemoCamera(Camera):
|
|||
|
||||
return await self.hass.async_add_executor_job(image_path.read_bytes)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of this camera."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Camera support turn on/off features."""
|
||||
return SUPPORT_ON_OFF
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Whether camera is on (streaming)."""
|
||||
return self.is_streaming
|
||||
|
||||
@property
|
||||
def motion_detection_enabled(self):
|
||||
"""Camera Motion Detection Status."""
|
||||
return self._motion_status
|
||||
|
||||
async def async_enable_motion_detection(self):
|
||||
"""Enable the Motion detection in base station (Arm)."""
|
||||
self._motion_status = True
|
||||
self._attr_motion_detection_enabled = True
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_disable_motion_detection(self):
|
||||
"""Disable the motion detection in base station (Disarm)."""
|
||||
self._motion_status = False
|
||||
self._attr_motion_detection_enabled = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self):
|
||||
"""Turn off camera."""
|
||||
self.is_streaming = False
|
||||
self._attr_is_streaming = False
|
||||
self._attr_is_on = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_on(self):
|
||||
"""Turn on camera."""
|
||||
self.is_streaming = True
|
||||
self._attr_is_streaming = True
|
||||
self._attr_is_on = True
|
||||
self.async_write_ha_state()
|
||||
|
|
|
@ -186,7 +186,7 @@ class HyperionCamera(Camera):
|
|||
return False
|
||||
|
||||
self._image_stream_clients += 1
|
||||
self.is_streaming = True
|
||||
self._attr_is_streaming = True
|
||||
self.async_write_ha_state()
|
||||
return True
|
||||
|
||||
|
@ -196,7 +196,7 @@ class HyperionCamera(Camera):
|
|||
|
||||
if not self._image_stream_clients:
|
||||
await self._client.async_send_image_stream_stop()
|
||||
self.is_streaming = False
|
||||
self._attr_is_streaming = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
@asynccontextmanager
|
||||
|
|
|
@ -159,7 +159,7 @@ class MotionEyeMjpegCamera(MotionEyeEntity, MjpegCamera):
|
|||
self._motion_detection_enabled: bool = camera.get(KEY_MOTION_DETECTION, False)
|
||||
|
||||
# motionEye cameras are always streaming or unavailable.
|
||||
self.is_streaming = True
|
||||
self._attr_is_streaming = True
|
||||
|
||||
MotionEyeEntity.__init__(
|
||||
self,
|
||||
|
|
|
@ -79,7 +79,7 @@ class NestCamera(Camera):
|
|||
self._event_id: str | None = None
|
||||
self._event_image_bytes: bytes | None = None
|
||||
self._event_image_cleanup_unsub: Callable[[], None] | None = None
|
||||
self.is_streaming = CameraLiveStreamTrait.NAME in self._device.traits
|
||||
self._attr_is_streaming = CameraLiveStreamTrait.NAME in self._device.traits
|
||||
self._placeholder_image: bytes | None = None
|
||||
|
||||
@property
|
||||
|
|
|
@ -172,13 +172,13 @@ class NetatmoCamera(NetatmoBase, Camera):
|
|||
|
||||
if data["home_id"] == self._home_id and data["camera_id"] == self._id:
|
||||
if data[WEBHOOK_PUSH_TYPE] in ("NACamera-off", "NACamera-disconnection"):
|
||||
self.is_streaming = False
|
||||
self._attr_is_streaming = False
|
||||
self._status = "off"
|
||||
elif data[WEBHOOK_PUSH_TYPE] in (
|
||||
"NACamera-on",
|
||||
WEBHOOK_NACAMERA_CONNECTION,
|
||||
):
|
||||
self.is_streaming = True
|
||||
self._attr_is_streaming = True
|
||||
self._status = "on"
|
||||
elif data[WEBHOOK_PUSH_TYPE] == WEBHOOK_LIGHT_MODE:
|
||||
self._light_state = data["sub_type"]
|
||||
|
@ -273,7 +273,7 @@ class NetatmoCamera(NetatmoBase, Camera):
|
|||
self._sd_status = camera.get("sd_status")
|
||||
self._alim_status = camera.get("alim_status")
|
||||
self._is_local = camera.get("is_local")
|
||||
self.is_streaming = bool(self._status == "on")
|
||||
self._attr_is_streaming = bool(self._status == "on")
|
||||
|
||||
if self._model == "NACamera": # Smart Indoor Camera
|
||||
self.hass.data[DOMAIN][DATA_EVENTS][self._id] = self.process_events(
|
||||
|
|
|
@ -86,7 +86,7 @@ class UnifiVideoCamera(Camera):
|
|||
self._uuid = uuid
|
||||
self._name = name
|
||||
self._password = password
|
||||
self.is_streaming = False
|
||||
self._attr_is_streaming = False
|
||||
self._connect_addr = None
|
||||
self._camera = None
|
||||
self._motion_status = False
|
||||
|
|
|
@ -75,7 +75,7 @@ async def test_setup_camera(hass: HomeAssistant) -> None:
|
|||
|
||||
entity_state = hass.states.get(TEST_CAMERA_ENTITY_ID)
|
||||
assert entity_state
|
||||
assert entity_state.state == "idle"
|
||||
assert entity_state.state == "streaming"
|
||||
assert entity_state.attributes.get("friendly_name") == TEST_CAMERA_NAME
|
||||
|
||||
|
||||
|
@ -192,7 +192,7 @@ async def test_setup_camera_new_data_without_streaming(hass: HomeAssistant) -> N
|
|||
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||
entity_state = hass.states.get(TEST_CAMERA_ENTITY_ID)
|
||||
assert entity_state
|
||||
assert entity_state.state == "idle"
|
||||
assert entity_state.state == "streaming"
|
||||
|
||||
cameras = copy.deepcopy(TEST_CAMERAS)
|
||||
cameras[KEY_CAMERAS][0][KEY_VIDEO_STREAMING] = False
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue