Remove onvif from mypy ignore list (#75162)

pull/75169/head
epenet 2022-07-14 11:55:43 +02:00 committed by GitHub
parent b60f6c7cdd
commit debd475a6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 56 additions and 59 deletions

View File

@ -1,42 +1,50 @@
"""Base classes for ONVIF entities."""
from __future__ import annotations
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.entity import DeviceInfo, Entity
from .const import DOMAIN
from .device import ONVIFDevice
from .models import Profile
class ONVIFBaseEntity(Entity):
"""Base class common to all ONVIF entities."""
def __init__(self, device: ONVIFDevice, profile: Profile = None) -> None:
def __init__(self, device: ONVIFDevice) -> None:
"""Initialize the ONVIF entity."""
self.device: ONVIFDevice = device
self.profile: Profile = profile
@property
def available(self):
"""Return True if device is available."""
return self.device.available
@property
def mac_or_serial(self) -> str:
"""Return MAC or serial, for unique_id generation.
MAC address is not always available, and given the number
of non-conformant ONVIF devices we have historically supported,
we can not guarantee serial number either. Due to this, we have
adopted an either/or approach in the config entry setup, and can
guarantee that one or the other will be populated.
See: https://github.com/home-assistant/core/issues/35883
"""
return (
self.device.info.mac
or self.device.info.serial_number # type:ignore[return-value]
)
@property
def device_info(self) -> DeviceInfo:
"""Return a device description for device registry."""
connections = None
connections: set[tuple[str, str]] = set()
if self.device.info.mac:
connections = {(CONNECTION_NETWORK_MAC, self.device.info.mac)}
return DeviceInfo(
connections=connections,
identifiers={
# MAC address is not always available, and given the number
# of non-conformant ONVIF devices we have historically supported,
# we can not guarantee serial number either. Due to this, we have
# adopted an either/or approach in the config entry setup, and can
# guarantee that one or the other will be populated.
# See: https://github.com/home-assistant/core/issues/35883
(DOMAIN, self.device.info.mac or self.device.info.serial_number)
},
identifiers={(DOMAIN, self.mac_or_serial)},
manufacturer=self.device.info.manufacturer,
model=self.device.info.model,
name=self.device.name,

View File

@ -48,15 +48,16 @@ async def async_setup_entry(
device.events.async_add_listener(async_check_entities)
return True
class ONVIFBinarySensor(ONVIFBaseEntity, RestoreEntity, BinarySensorEntity):
"""Representation of a binary ONVIF event."""
_attr_should_poll = False
_attr_unique_id: str
def __init__(self, uid, device: ONVIFDevice, entry: er.RegistryEntry | None = None):
def __init__(
self, uid: str, device: ONVIFDevice, entry: er.RegistryEntry | None = None
) -> None:
"""Initialize the ONVIF binary sensor."""
self._attr_unique_id = uid
if entry is not None:
@ -65,6 +66,7 @@ class ONVIFBinarySensor(ONVIFBaseEntity, RestoreEntity, BinarySensorEntity):
self._attr_name = entry.name
else:
event = device.events.get_uid(uid)
assert event
self._attr_device_class = event.device_class
self._attr_entity_category = event.entity_category
self._attr_entity_registry_enabled_default = event.entity_enabled

View File

@ -30,9 +30,7 @@ class RebootButton(ONVIFBaseEntity, ButtonEntity):
"""Initialize the button entity."""
super().__init__(device)
self._attr_name = f"{self.device.name} Reboot"
self._attr_unique_id = (
f"{self.device.info.mac or self.device.info.serial_number}_reboot"
)
self._attr_unique_id = f"{self.mac_or_serial}_reboot"
async def async_press(self) -> None:
"""Send out a SystemReboot command."""
@ -49,7 +47,7 @@ class SetSystemDateAndTimeButton(ONVIFBaseEntity, ButtonEntity):
"""Initialize the button entity."""
super().__init__(device)
self._attr_name = f"{self.device.name} Set System Date and Time"
self._attr_unique_id = f"{self.device.info.mac or self.device.info.serial_number}_setsystemdatetime"
self._attr_unique_id = f"{self.mac_or_serial}_setsystemdatetime"
async def async_press(self) -> None:
"""Send out a SetSystemDateAndTime command."""

View File

@ -13,6 +13,7 @@ from homeassistant.components.stream import (
CONF_RTSP_TRANSPORT,
CONF_USE_WALLCLOCK_AS_TIMESTAMPS,
)
from homeassistant.components.stream.const import RTSP_TRANSPORTS
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import HTTP_BASIC_AUTHENTICATION
from homeassistant.core import HomeAssistant
@ -46,6 +47,8 @@ from .const import (
ZOOM_IN,
ZOOM_OUT,
)
from .device import ONVIFDevice
from .models import Profile
async def async_setup_entry(
@ -85,20 +88,19 @@ async def async_setup_entry(
[ONVIFCameraEntity(device, profile) for profile in device.profiles]
)
return True
class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
"""Representation of an ONVIF camera."""
_attr_supported_features = CameraEntityFeature.STREAM
def __init__(self, device, profile):
def __init__(self, device: ONVIFDevice, profile: Profile) -> None:
"""Initialize ONVIF camera entity."""
ONVIFBaseEntity.__init__(self, device, profile)
ONVIFBaseEntity.__init__(self, device)
Camera.__init__(self)
self.profile = profile
self.stream_options[CONF_RTSP_TRANSPORT] = device.config_entry.options.get(
CONF_RTSP_TRANSPORT
CONF_RTSP_TRANSPORT, next(iter(RTSP_TRANSPORTS))
)
self.stream_options[
CONF_USE_WALLCLOCK_AS_TIMESTAMPS
@ -118,8 +120,8 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
def unique_id(self) -> str:
"""Return a unique ID."""
if self.profile.index:
return f"{self.device.info.mac or self.device.info.serial_number}_{self.profile.index}"
return self.device.info.mac or self.device.info.serial_number
return f"{self.mac_or_serial}_{self.profile.index}"
return self.mac_or_serial
@property
def entity_registry_enabled_default(self) -> bool:
@ -149,6 +151,7 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
)
if image is None:
assert self._stream_uri
return await ffmpeg.async_get_image(
self.hass,
self._stream_uri,

View File

@ -42,21 +42,21 @@ from .models import PTZ, Capabilities, DeviceInfo, Profile, Resolution, Video
class ONVIFDevice:
"""Manages an ONVIF device."""
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry = None) -> None:
device: ONVIFCamera
events: EventManager
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
"""Initialize the device."""
self.hass: HomeAssistant = hass
self.config_entry: ConfigEntry = config_entry
self.available: bool = True
self.device: ONVIFCamera = None
self.events: EventManager = None
self.info: DeviceInfo = DeviceInfo()
self.capabilities: Capabilities = Capabilities()
self.profiles: list[Profile] = []
self.max_resolution: int = 0
self._dt_diff_seconds: int = 0
self._dt_diff_seconds: float = 0
@property
def name(self) -> str:
@ -99,6 +99,7 @@ class ONVIFDevice:
await self.async_check_date_and_time()
# Create event manager
assert self.config_entry.unique_id
self.events = EventManager(
self.hass, self.device, self.config_entry.unique_id
)
@ -297,7 +298,7 @@ class ONVIFDevice:
"""Obtain media profiles for this device."""
media_service = self.device.create_media_service()
result = await media_service.GetProfiles()
profiles = []
profiles: list[Profile] = []
if not isinstance(result, list):
return profiles
@ -396,7 +397,7 @@ class ONVIFDevice:
req.ProfileToken = profile.token
if move_mode == CONTINUOUS_MOVE:
# Guard against unsupported operation
if not profile.ptz.continuous:
if not profile.ptz or not profile.ptz.continuous:
LOGGER.warning(
"ContinuousMove not supported on device '%s'", self.name
)
@ -419,7 +420,7 @@ class ONVIFDevice:
)
elif move_mode == RELATIVE_MOVE:
# Guard against unsupported operation
if not profile.ptz.relative:
if not profile.ptz or not profile.ptz.relative:
LOGGER.warning(
"RelativeMove not supported on device '%s'", self.name
)
@ -436,7 +437,7 @@ class ONVIFDevice:
await ptz_service.RelativeMove(req)
elif move_mode == ABSOLUTE_MOVE:
# Guard against unsupported operation
if not profile.ptz.absolute:
if not profile.ptz or not profile.ptz.absolute:
LOGGER.warning(
"AbsoluteMove not supported on device '%s'", self.name
)
@ -453,6 +454,11 @@ class ONVIFDevice:
await ptz_service.AbsoluteMove(req)
elif move_mode == GOTOPRESET_MOVE:
# Guard against unsupported operation
if not profile.ptz or not profile.ptz.presets:
LOGGER.warning(
"Absolute Presets not supported on device '%s'", self.name
)
return
if preset_val not in profile.ptz.presets:
LOGGER.warning(
"PTZ preset '%s' does not exist on device '%s'. Available Presets: %s",

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from datetime import date, datetime
from decimal import Decimal
from homeassistant.components.sensor import RestoreSensor
from homeassistant.config_entries import ConfigEntry
@ -47,8 +48,6 @@ async def async_setup_entry(
device.events.async_add_listener(async_check_entities)
return True
class ONVIFSensor(ONVIFBaseEntity, RestoreSensor):
"""Representation of a ONVIF sensor event."""
@ -65,6 +64,7 @@ class ONVIFSensor(ONVIFBaseEntity, RestoreSensor):
self._attr_native_unit_of_measurement = entry.unit_of_measurement
else:
event = device.events.get_uid(uid)
assert event
self._attr_device_class = event.device_class
self._attr_entity_category = event.entity_category
self._attr_entity_registry_enabled_default = event.entity_enabled
@ -75,7 +75,7 @@ class ONVIFSensor(ONVIFBaseEntity, RestoreSensor):
super().__init__(device)
@property
def native_value(self) -> StateType | date | datetime:
def native_value(self) -> StateType | date | datetime | Decimal:
"""Return the value reported by the sensor."""
if (event := self.device.events.get_uid(self._attr_unique_id)) is not None:
return event.value

View File

@ -2689,21 +2689,6 @@ ignore_errors = true
[mypy-homeassistant.components.minecraft_server.sensor]
ignore_errors = true
[mypy-homeassistant.components.onvif.base]
ignore_errors = true
[mypy-homeassistant.components.onvif.binary_sensor]
ignore_errors = true
[mypy-homeassistant.components.onvif.camera]
ignore_errors = true
[mypy-homeassistant.components.onvif.device]
ignore_errors = true
[mypy-homeassistant.components.onvif.sensor]
ignore_errors = true
[mypy-homeassistant.components.sonos]
ignore_errors = true

View File

@ -27,11 +27,6 @@ IGNORED_MODULES: Final[list[str]] = [
"homeassistant.components.minecraft_server",
"homeassistant.components.minecraft_server.helpers",
"homeassistant.components.minecraft_server.sensor",
"homeassistant.components.onvif.base",
"homeassistant.components.onvif.binary_sensor",
"homeassistant.components.onvif.camera",
"homeassistant.components.onvif.device",
"homeassistant.components.onvif.sensor",
"homeassistant.components.sonos",
"homeassistant.components.sonos.alarms",
"homeassistant.components.sonos.binary_sensor",