"""Support for Vivotek IP Cameras.""" from __future__ import annotations import logging from typing import TYPE_CHECKING from libpyvivotek.vivotek import VivotekCamera import voluptuous as vol from homeassistant.components.camera import ( PLATFORM_SCHEMA as CAMERA_PLATFORM_SCHEMA, Camera, CameraEntityFeature, ) from homeassistant.config_entries import SOURCE_IMPORT from homeassistant.const import ( CONF_AUTHENTICATION, CONF_IP_ADDRESS, CONF_NAME, CONF_PASSWORD, CONF_SSL, CONF_USERNAME, CONF_VERIFY_SSL, HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION, ) from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant from homeassistant.data_entry_flow import FlowResultType from homeassistant.helpers import config_validation as cv, issue_registry as ir from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, AddEntitiesCallback, ) from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import VivotekConfigEntry from .const import ( CONF_FRAMERATE, CONF_SECURITY_LEVEL, CONF_STREAM_PATH, DOMAIN, INTEGRATION_TITLE, ) _LOGGER = logging.getLogger(__name__) DEFAULT_CAMERA_BRAND = "VIVOTEK" DEFAULT_NAME = "VIVOTEK Camera" DEFAULT_EVENT_0_KEY = "event_i0_enable" DEFAULT_FRAMERATE = 2 DEFAULT_SECURITY_LEVEL = "admin" DEFAULT_STREAM_SOURCE = "live.sdp" PLATFORM_SCHEMA = CAMERA_PLATFORM_SCHEMA.extend( { vol.Required(CONF_IP_ADDRESS): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, vol.Required(CONF_USERNAME): cv.string, vol.Optional(CONF_AUTHENTICATION, default=HTTP_BASIC_AUTHENTICATION): vol.In( [HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION] ), vol.Optional(CONF_SSL, default=False): cv.boolean, vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, vol.Optional(CONF_FRAMERATE, default=DEFAULT_FRAMERATE): cv.positive_int, vol.Optional(CONF_SECURITY_LEVEL, default=DEFAULT_SECURITY_LEVEL): cv.string, vol.Optional(CONF_STREAM_PATH, default=DEFAULT_STREAM_SOURCE): cv.string, } ) async def async_setup_platform( hass: HomeAssistant, config: ConfigType, async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Vivotek camera platform.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, data=config, ) if ( result.get("type") is FlowResultType.ABORT and result.get("reason") != "already_configured" ): ir.async_create_issue( hass, DOMAIN, f"deprecated_yaml_import_issue_{result.get('reason')}", breaks_in_ha_version="2026.6.0", is_fixable=False, issue_domain=DOMAIN, severity=ir.IssueSeverity.WARNING, translation_key=f"deprecated_yaml_import_issue_{result.get('reason')}", translation_placeholders={ "domain": DOMAIN, "integration_title": INTEGRATION_TITLE, }, ) return ir.async_create_issue( hass, HOMEASSISTANT_DOMAIN, "deprecated_yaml", breaks_in_ha_version="2026.6.0", is_fixable=False, issue_domain=DOMAIN, severity=ir.IssueSeverity.WARNING, translation_key="deprecated_yaml", translation_placeholders={ "domain": DOMAIN, "integration_title": INTEGRATION_TITLE, }, ) async def async_setup_entry( hass: HomeAssistant, entry: VivotekConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the component from a config entry.""" config = entry.data creds = f"{config[CONF_USERNAME]}:{config[CONF_PASSWORD]}" stream_source = ( f"rtsp://{creds}@{config[CONF_IP_ADDRESS]}:554/{config[CONF_STREAM_PATH]}" ) cam_client = entry.runtime_data if TYPE_CHECKING: assert entry.unique_id is not None async_add_entities( [ VivotekCam( cam_client, stream_source, entry.unique_id, entry.options[CONF_FRAMERATE], entry.title, ) ] ) class VivotekCam(Camera): """A Vivotek IP camera.""" _attr_brand = DEFAULT_CAMERA_BRAND _attr_supported_features = CameraEntityFeature.STREAM def __init__( self, cam_client: VivotekCamera, stream_source: str, unique_id: str, framerate: int, name: str, ) -> None: """Initialize a Vivotek camera.""" super().__init__() self._cam = cam_client self._attr_frame_interval = 1 / framerate self._attr_unique_id = unique_id self._attr_name = name self._stream_source = stream_source def camera_image( self, width: int | None = None, height: int | None = None ) -> bytes | None: """Return bytes of camera image.""" return self._cam.snapshot() async def stream_source(self) -> str: """Return the source of the stream.""" return self._stream_source def disable_motion_detection(self) -> None: """Disable motion detection in camera.""" response = self._cam.set_param(DEFAULT_EVENT_0_KEY, 0) self._attr_motion_detection_enabled = int(response) == 1 def enable_motion_detection(self) -> None: """Enable motion detection in camera.""" response = self._cam.set_param(DEFAULT_EVENT_0_KEY, 1) self._attr_motion_detection_enabled = int(response) == 1 def update(self) -> None: """Update entity status.""" self._attr_model = self._cam.model_name self._attr_available = self._attr_model is not None