22
33from __future__ import annotations
44
5- from libpyvivotek import VivotekCamera
5+ import logging
6+ from typing import TYPE_CHECKING
7+
8+ from libpyvivotek .vivotek import VivotekCamera
69import voluptuous as vol
710
811from homeassistant .components .camera import (
912 PLATFORM_SCHEMA as CAMERA_PLATFORM_SCHEMA ,
1013 Camera ,
1114 CameraEntityFeature ,
1215)
16+ from homeassistant .config_entries import SOURCE_IMPORT
1317from homeassistant .const import (
1418 CONF_AUTHENTICATION ,
1519 CONF_IP_ADDRESS ,
2125 HTTP_BASIC_AUTHENTICATION ,
2226 HTTP_DIGEST_AUTHENTICATION ,
2327)
24- from homeassistant .core import HomeAssistant
25- from homeassistant .helpers import config_validation as cv
26- from homeassistant .helpers .entity_platform import AddEntitiesCallback
28+ from homeassistant .core import DOMAIN as HOMEASSISTANT_DOMAIN , HomeAssistant
29+ from homeassistant .data_entry_flow import FlowResultType
30+ from homeassistant .helpers import config_validation as cv , issue_registry as ir
31+ from homeassistant .helpers .entity_platform import (
32+ AddConfigEntryEntitiesCallback ,
33+ AddEntitiesCallback ,
34+ )
2735from homeassistant .helpers .typing import ConfigType , DiscoveryInfoType
2836
29- CONF_FRAMERATE = "framerate"
30- CONF_SECURITY_LEVEL = "security_level"
31- CONF_STREAM_PATH = "stream_path"
37+ from . import VivotekConfigEntry
38+ from .const import (
39+ CONF_FRAMERATE ,
40+ CONF_SECURITY_LEVEL ,
41+ CONF_STREAM_PATH ,
42+ DOMAIN ,
43+ INTEGRATION_TITLE ,
44+ )
45+
46+ _LOGGER = logging .getLogger (__name__ )
3247
3348DEFAULT_CAMERA_BRAND = "VIVOTEK"
3449DEFAULT_NAME = "VIVOTEK Camera"
3550DEFAULT_EVENT_0_KEY = "event_i0_enable"
51+ DEFAULT_FRAMERATE = 2
3652DEFAULT_SECURITY_LEVEL = "admin"
3753DEFAULT_STREAM_SOURCE = "live.sdp"
3854
4763 ),
4864 vol .Optional (CONF_SSL , default = False ): cv .boolean ,
4965 vol .Optional (CONF_VERIFY_SSL , default = True ): cv .boolean ,
50- vol .Optional (CONF_FRAMERATE , default = 2 ): cv .positive_int ,
66+ vol .Optional (CONF_FRAMERATE , default = DEFAULT_FRAMERATE ): cv .positive_int ,
5167 vol .Optional (CONF_SECURITY_LEVEL , default = DEFAULT_SECURITY_LEVEL ): cv .string ,
5268 vol .Optional (CONF_STREAM_PATH , default = DEFAULT_STREAM_SOURCE ): cv .string ,
5369 }
5470)
5571
5672
57- def setup_platform (
73+ async def async_setup_platform (
5874 hass : HomeAssistant ,
5975 config : ConfigType ,
60- add_entities : AddEntitiesCallback ,
76+ async_add_entities : AddEntitiesCallback ,
6177 discovery_info : DiscoveryInfoType | None = None ,
6278) -> None :
63- """Set up a Vivotek IP Camera."""
64- creds = f"{ config [CONF_USERNAME ]} :{ config [CONF_PASSWORD ]} "
65- cam = VivotekCamera (
66- host = config [CONF_IP_ADDRESS ],
67- port = (443 if config [CONF_SSL ] else 80 ),
68- verify_ssl = config [CONF_VERIFY_SSL ],
69- usr = config [CONF_USERNAME ],
70- pwd = config [CONF_PASSWORD ],
71- digest_auth = config [CONF_AUTHENTICATION ] == HTTP_DIGEST_AUTHENTICATION ,
72- sec_lvl = config [CONF_SECURITY_LEVEL ],
79+ """Set up the Vivotek camera platform."""
80+ result = await hass .config_entries .flow .async_init (
81+ DOMAIN ,
82+ context = {"source" : SOURCE_IMPORT },
83+ data = config ,
84+ )
85+ if (
86+ result .get ("type" ) is FlowResultType .ABORT
87+ and result .get ("reason" ) != "already_configured"
88+ ):
89+ ir .async_create_issue (
90+ hass ,
91+ DOMAIN ,
92+ f"deprecated_yaml_import_issue_{ result .get ('reason' )} " ,
93+ breaks_in_ha_version = "2026.6.0" ,
94+ is_fixable = False ,
95+ issue_domain = DOMAIN ,
96+ severity = ir .IssueSeverity .WARNING ,
97+ translation_key = f"deprecated_yaml_import_issue_{ result .get ('reason' )} " ,
98+ translation_placeholders = {
99+ "domain" : DOMAIN ,
100+ "integration_title" : INTEGRATION_TITLE ,
101+ },
102+ )
103+ return
104+
105+ ir .async_create_issue (
106+ hass ,
107+ HOMEASSISTANT_DOMAIN ,
108+ "deprecated_yaml" ,
109+ breaks_in_ha_version = "2026.6.0" ,
110+ is_fixable = False ,
111+ issue_domain = DOMAIN ,
112+ severity = ir .IssueSeverity .WARNING ,
113+ translation_key = "deprecated_yaml" ,
114+ translation_placeholders = {
115+ "domain" : DOMAIN ,
116+ "integration_title" : INTEGRATION_TITLE ,
117+ },
73118 )
119+
120+
121+ async def async_setup_entry (
122+ hass : HomeAssistant ,
123+ entry : VivotekConfigEntry ,
124+ async_add_entities : AddConfigEntryEntitiesCallback ,
125+ ) -> None :
126+ """Set up the component from a config entry."""
127+ config = entry .data
128+ creds = f"{ config [CONF_USERNAME ]} :{ config [CONF_PASSWORD ]} "
74129 stream_source = (
75130 f"rtsp://{ creds } @{ config [CONF_IP_ADDRESS ]} :554/{ config [CONF_STREAM_PATH ]} "
76131 )
77- add_entities ([VivotekCam (config , cam , stream_source )], True )
132+ cam_client = entry .runtime_data
133+ if TYPE_CHECKING :
134+ assert entry .unique_id is not None
135+ async_add_entities (
136+ [
137+ VivotekCam (
138+ cam_client ,
139+ stream_source ,
140+ entry .unique_id ,
141+ entry .options [CONF_FRAMERATE ],
142+ entry .title ,
143+ )
144+ ]
145+ )
78146
79147
80148class VivotekCam (Camera ):
@@ -84,14 +152,19 @@ class VivotekCam(Camera):
84152 _attr_supported_features = CameraEntityFeature .STREAM
85153
86154 def __init__ (
87- self , config : ConfigType , cam : VivotekCamera , stream_source : str
155+ self ,
156+ cam_client : VivotekCamera ,
157+ stream_source : str ,
158+ unique_id : str ,
159+ framerate : int ,
160+ name : str ,
88161 ) -> None :
89162 """Initialize a Vivotek camera."""
90163 super ().__init__ ()
91-
92- self ._cam = cam
93- self ._attr_frame_interval = 1 / config [ CONF_FRAMERATE ]
94- self ._attr_name = config [ CONF_NAME ]
164+ self . _cam = cam_client
165+ self ._attr_frame_interval = 1 / framerate
166+ self ._attr_unique_id = unique_id
167+ self ._attr_name = name
95168 self ._stream_source = stream_source
96169
97170 def camera_image (
@@ -117,3 +190,4 @@ def enable_motion_detection(self) -> None:
117190 def update (self ) -> None :
118191 """Update entity status."""
119192 self ._attr_model = self ._cam .model_name
193+ self ._attr_available = self ._attr_model is not None
0 commit comments