Bump Blinkpy to 0.22.2 in Blink (#98571)
This commit is contained in:
parent
6b05f51413
commit
eaf6197d43
10 changed files with 144 additions and 107 deletions
|
@ -1,7 +1,9 @@
|
|||
"""Support for Blink Home Camera System."""
|
||||
import asyncio
|
||||
from copy import deepcopy
|
||||
import logging
|
||||
|
||||
from aiohttp import ClientError
|
||||
from blinkpy.auth import Auth
|
||||
from blinkpy.blinkpy import Blink
|
||||
import voluptuous as vol
|
||||
|
@ -16,8 +18,9 @@ from homeassistant.const import (
|
|||
CONF_SCAN_INTERVAL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import (
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
|
@ -40,23 +43,7 @@ SERVICE_SAVE_RECENT_CLIPS_SCHEMA = vol.Schema(
|
|||
)
|
||||
|
||||
|
||||
def _blink_startup_wrapper(hass: HomeAssistant, entry: ConfigEntry) -> Blink:
|
||||
"""Startup wrapper for blink."""
|
||||
blink = Blink()
|
||||
auth_data = deepcopy(dict(entry.data))
|
||||
blink.auth = Auth(auth_data, no_prompt=True)
|
||||
blink.refresh_rate = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
|
||||
|
||||
if blink.start():
|
||||
blink.setup_post_verify()
|
||||
elif blink.auth.check_key_required():
|
||||
_LOGGER.debug("Attempting a reauth flow")
|
||||
_reauth_flow_wrapper(hass, auth_data)
|
||||
|
||||
return blink
|
||||
|
||||
|
||||
def _reauth_flow_wrapper(hass, data):
|
||||
async def _reauth_flow_wrapper(hass, data):
|
||||
"""Reauth flow wrapper."""
|
||||
hass.add_job(
|
||||
hass.config_entries.flow.async_init(
|
||||
|
@ -79,10 +66,10 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
data = {**entry.data}
|
||||
if entry.version == 1:
|
||||
data.pop("login_response", None)
|
||||
await hass.async_add_executor_job(_reauth_flow_wrapper, hass, data)
|
||||
await _reauth_flow_wrapper(hass, data)
|
||||
return False
|
||||
if entry.version == 2:
|
||||
await hass.async_add_executor_job(_reauth_flow_wrapper, hass, data)
|
||||
await _reauth_flow_wrapper(hass, data)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
@ -92,19 +79,32 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
_async_import_options_from_data_if_missing(hass, entry)
|
||||
hass.data[DOMAIN][entry.entry_id] = await hass.async_add_executor_job(
|
||||
_blink_startup_wrapper, hass, entry
|
||||
)
|
||||
session = async_get_clientsession(hass)
|
||||
blink = Blink(session=session)
|
||||
auth_data = deepcopy(dict(entry.data))
|
||||
blink.auth = Auth(auth_data, no_prompt=True, session=session)
|
||||
blink.refresh_rate = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
|
||||
|
||||
if not hass.data[DOMAIN][entry.entry_id].available:
|
||||
try:
|
||||
await blink.start()
|
||||
if blink.auth.check_key_required():
|
||||
_LOGGER.debug("Attempting a reauth flow")
|
||||
raise ConfigEntryAuthFailed("Need 2FA for Blink")
|
||||
except (ClientError, asyncio.TimeoutError) as ex:
|
||||
raise ConfigEntryNotReady("Can not connect to host") from ex
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = blink
|
||||
|
||||
if not blink.available:
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
await blink.refresh(force=True)
|
||||
|
||||
def blink_refresh(event_time=None):
|
||||
async def blink_refresh(event_time=None):
|
||||
"""Call blink to refresh info."""
|
||||
hass.data[DOMAIN][entry.entry_id].refresh(force_cache=True)
|
||||
await hass.data[DOMAIN][entry.entry_id].refresh(force_cache=True)
|
||||
|
||||
async def async_save_video(call):
|
||||
"""Call save video service handler."""
|
||||
|
@ -114,10 +114,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
"""Call save recent clips service handler."""
|
||||
await async_handle_save_recent_clips_service(hass, entry, call)
|
||||
|
||||
def send_pin(call):
|
||||
async def send_pin(call):
|
||||
"""Call blink to send new pin."""
|
||||
pin = call.data[CONF_PIN]
|
||||
hass.data[DOMAIN][entry.entry_id].auth.send_auth_key(
|
||||
await hass.data[DOMAIN][entry.entry_id].auth.send_auth_key(
|
||||
hass.data[DOMAIN][entry.entry_id],
|
||||
pin,
|
||||
)
|
||||
|
@ -176,27 +176,27 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
|||
blink.refresh_rate = entry.options[CONF_SCAN_INTERVAL]
|
||||
|
||||
|
||||
async def async_handle_save_video_service(hass, entry, call):
|
||||
async def async_handle_save_video_service(
|
||||
hass: HomeAssistant, entry: ConfigEntry, call
|
||||
) -> None:
|
||||
"""Handle save video service calls."""
|
||||
camera_name = call.data[CONF_NAME]
|
||||
video_path = call.data[CONF_FILENAME]
|
||||
if not hass.config.is_allowed_path(video_path):
|
||||
_LOGGER.error("Can't write %s, no access to path!", video_path)
|
||||
return
|
||||
|
||||
def _write_video(name, file_path):
|
||||
"""Call video write."""
|
||||
all_cameras = hass.data[DOMAIN][entry.entry_id].cameras
|
||||
if name in all_cameras:
|
||||
all_cameras[name].video_to_file(file_path)
|
||||
|
||||
try:
|
||||
await hass.async_add_executor_job(_write_video, camera_name, video_path)
|
||||
all_cameras = hass.data[DOMAIN][entry.entry_id].cameras
|
||||
if camera_name in all_cameras:
|
||||
await all_cameras[camera_name].video_to_file(video_path)
|
||||
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't write image to file: %s", err)
|
||||
|
||||
|
||||
async def async_handle_save_recent_clips_service(hass, entry, call):
|
||||
async def async_handle_save_recent_clips_service(
|
||||
hass: HomeAssistant, entry: ConfigEntry, call
|
||||
) -> None:
|
||||
"""Save multiple recent clips to output directory."""
|
||||
camera_name = call.data[CONF_NAME]
|
||||
clips_dir = call.data[CONF_FILE_PATH]
|
||||
|
@ -204,13 +204,9 @@ async def async_handle_save_recent_clips_service(hass, entry, call):
|
|||
_LOGGER.error("Can't write to directory %s, no access to path!", clips_dir)
|
||||
return
|
||||
|
||||
def _save_recent_clips(name, output_dir):
|
||||
"""Call save recent clips."""
|
||||
all_cameras = hass.data[DOMAIN][entry.entry_id].cameras
|
||||
if name in all_cameras:
|
||||
all_cameras[name].save_recent_clips(output_dir=output_dir)
|
||||
|
||||
try:
|
||||
await hass.async_add_executor_job(_save_recent_clips, camera_name, clips_dir)
|
||||
all_cameras = hass.data[DOMAIN][entry.entry_id].cameras
|
||||
if camera_name in all_cameras:
|
||||
await all_cameras[camera_name].save_recent_clips(output_dir=clips_dir)
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't write recent clips to directory: %s", err)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
"""Support for Blink Alarm Control Panel."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from blinkpy.blinkpy import Blink
|
||||
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
AlarmControlPanelEntity,
|
||||
AlarmControlPanelEntityFeature,
|
||||
|
@ -16,6 +19,7 @@ from homeassistant.const import (
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from .const import DEFAULT_ATTRIBUTION, DEFAULT_BRAND, DOMAIN
|
||||
|
||||
|
@ -32,11 +36,11 @@ async def async_setup_entry(
|
|||
|
||||
sync_modules = []
|
||||
for sync_name, sync_module in data.sync.items():
|
||||
sync_modules.append(BlinkSyncModule(data, sync_name, sync_module))
|
||||
async_add_entities(sync_modules)
|
||||
sync_modules.append(BlinkSyncModuleHA(data, sync_name, sync_module))
|
||||
async_add_entities(sync_modules, update_before_add=True)
|
||||
|
||||
|
||||
class BlinkSyncModule(AlarmControlPanelEntity):
|
||||
class BlinkSyncModuleHA(AlarmControlPanelEntity):
|
||||
"""Representation of a Blink Alarm Control Panel."""
|
||||
|
||||
_attr_icon = ICON
|
||||
|
@ -44,19 +48,19 @@ class BlinkSyncModule(AlarmControlPanelEntity):
|
|||
_attr_name = None
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, data, name, sync):
|
||||
def __init__(self, data, name: str, sync) -> None:
|
||||
"""Initialize the alarm control panel."""
|
||||
self.data = data
|
||||
self.data: Blink = data
|
||||
self.sync = sync
|
||||
self._name = name
|
||||
self._attr_unique_id = sync.serial
|
||||
self._name: str = name
|
||||
self._attr_unique_id: str = sync.serial
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, sync.serial)},
|
||||
name=f"{DOMAIN} {name}",
|
||||
manufacturer=DEFAULT_BRAND,
|
||||
)
|
||||
|
||||
def update(self) -> None:
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the device."""
|
||||
if self.data.check_if_ok_to_update():
|
||||
_LOGGER.debug(
|
||||
|
@ -64,23 +68,38 @@ class BlinkSyncModule(AlarmControlPanelEntity):
|
|||
self._name,
|
||||
self.data,
|
||||
)
|
||||
self.data.refresh()
|
||||
try:
|
||||
await self.data.refresh(force=True)
|
||||
self._attr_available = True
|
||||
except asyncio.TimeoutError:
|
||||
self._attr_available = False
|
||||
|
||||
_LOGGER.info("Updating State of Blink Alarm Control Panel '%s'", self._name)
|
||||
|
||||
self._attr_state = (
|
||||
STATE_ALARM_ARMED_AWAY if self.sync.arm else STATE_ALARM_DISARMED
|
||||
)
|
||||
self.sync.attributes["network_info"] = self.data.networks
|
||||
self.sync.attributes["associated_cameras"] = list(self.sync.cameras)
|
||||
self.sync.attributes[ATTR_ATTRIBUTION] = DEFAULT_ATTRIBUTION
|
||||
self._attr_extra_state_attributes = self.sync.attributes
|
||||
|
||||
def alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Send disarm command."""
|
||||
self.sync.arm = False
|
||||
self.sync.refresh()
|
||||
@property
|
||||
def state(self) -> StateType:
|
||||
"""Return state of alarm."""
|
||||
return STATE_ALARM_ARMED_AWAY if self.sync.arm else STATE_ALARM_DISARMED
|
||||
|
||||
def alarm_arm_away(self, code: str | None = None) -> None:
|
||||
async def async_alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Send disarm command."""
|
||||
try:
|
||||
await self.sync.async_arm(False)
|
||||
await self.sync.refresh(force=True)
|
||||
self.async_write_ha_state()
|
||||
except asyncio.TimeoutError:
|
||||
self._attr_available = False
|
||||
|
||||
async def async_alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Send arm command."""
|
||||
self.sync.arm = True
|
||||
self.sync.refresh()
|
||||
try:
|
||||
await self.sync.async_arm(True)
|
||||
await self.sync.refresh(force=True)
|
||||
self.async_write_ha_state()
|
||||
except asyncio.TimeoutError:
|
||||
self._attr_available = False
|
||||
|
|
|
@ -52,7 +52,7 @@ async def async_setup_entry(
|
|||
for camera in data.cameras
|
||||
for description in BINARY_SENSORS_TYPES
|
||||
]
|
||||
async_add_entities(entities)
|
||||
async_add_entities(entities, update_before_add=True)
|
||||
|
||||
|
||||
class BlinkBinarySensor(BinarySensorEntity):
|
||||
|
@ -75,15 +75,16 @@ class BlinkBinarySensor(BinarySensorEntity):
|
|||
model=self._camera.camera_type,
|
||||
)
|
||||
|
||||
def update(self) -> None:
|
||||
@property
|
||||
def is_on(self) -> bool | None:
|
||||
"""Update sensor state."""
|
||||
state = self._camera.attributes[self.entity_description.key]
|
||||
is_on = self._camera.attributes[self.entity_description.key]
|
||||
_LOGGER.debug(
|
||||
"'%s' %s = %s",
|
||||
self._camera.attributes["name"],
|
||||
self.entity_description.key,
|
||||
state,
|
||||
is_on,
|
||||
)
|
||||
if self.entity_description.key == TYPE_BATTERY:
|
||||
state = state != "ok"
|
||||
self._attr_is_on = state
|
||||
is_on = is_on != "ok"
|
||||
return is_on
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
"""Support for Blink system camera."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from requests.exceptions import ChunkedEncodingError
|
||||
|
||||
|
@ -29,7 +32,7 @@ async def async_setup_entry(
|
|||
BlinkCamera(data, name, camera) for name, camera in data.cameras.items()
|
||||
]
|
||||
|
||||
async_add_entities(entities)
|
||||
async_add_entities(entities, update_before_add=True)
|
||||
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
platform.async_register_entity_service(SERVICE_TRIGGER, {}, "trigger_camera")
|
||||
|
@ -56,19 +59,25 @@ class BlinkCamera(Camera):
|
|||
_LOGGER.debug("Initialized blink camera %s", self.name)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||
"""Return the camera attributes."""
|
||||
return self._camera.attributes
|
||||
|
||||
def enable_motion_detection(self) -> None:
|
||||
async def async_enable_motion_detection(self) -> None:
|
||||
"""Enable motion detection for the camera."""
|
||||
self._camera.arm = True
|
||||
self.data.refresh()
|
||||
try:
|
||||
await self._camera.async_arm(True)
|
||||
await self.data.refresh(force=True)
|
||||
except asyncio.TimeoutError:
|
||||
self._attr_available = False
|
||||
|
||||
def disable_motion_detection(self) -> None:
|
||||
async def async_disable_motion_detection(self) -> None:
|
||||
"""Disable motion detection for the camera."""
|
||||
self._camera.arm = False
|
||||
self.data.refresh()
|
||||
try:
|
||||
await self._camera.async_arm(False)
|
||||
await self.data.refresh(force=True)
|
||||
except asyncio.TimeoutError:
|
||||
self._attr_available = False
|
||||
|
||||
@property
|
||||
def motion_detection_enabled(self) -> bool:
|
||||
|
@ -76,21 +85,24 @@ class BlinkCamera(Camera):
|
|||
return self._camera.arm
|
||||
|
||||
@property
|
||||
def brand(self):
|
||||
def brand(self) -> str | None:
|
||||
"""Return the camera brand."""
|
||||
return DEFAULT_BRAND
|
||||
|
||||
def trigger_camera(self):
|
||||
async def trigger_camera(self) -> None:
|
||||
"""Trigger camera to take a snapshot."""
|
||||
self._camera.snap_picture()
|
||||
self.data.refresh()
|
||||
try:
|
||||
await self._camera.snap_picture()
|
||||
self.async_schedule_update_ha_state(force_refresh=True)
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
|
||||
def camera_image(
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return a still image response from the camera."""
|
||||
try:
|
||||
return self._camera.image_from_cache.content
|
||||
return self._camera.image_from_cache
|
||||
except ChunkedEncodingError:
|
||||
_LOGGER.debug("Could not retrieve image for %s", self._camera.name)
|
||||
return None
|
||||
|
|
|
@ -16,10 +16,11 @@ from homeassistant.const import (
|
|||
CONF_SCAN_INTERVAL,
|
||||
CONF_USERNAME,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import selector
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.schema_config_entry_flow import (
|
||||
SchemaFlowFormStep,
|
||||
SchemaOptionsFlowHandler,
|
||||
|
@ -49,23 +50,23 @@ OPTIONS_FLOW = {
|
|||
}
|
||||
|
||||
|
||||
def validate_input(auth: Auth) -> None:
|
||||
async def validate_input(auth: Auth) -> None:
|
||||
"""Validate the user input allows us to connect."""
|
||||
try:
|
||||
auth.startup()
|
||||
await auth.startup()
|
||||
except (LoginError, TokenRefreshFailed) as err:
|
||||
raise InvalidAuth from err
|
||||
if auth.check_key_required():
|
||||
raise Require2FA
|
||||
|
||||
|
||||
def _send_blink_2fa_pin(auth: Auth, pin: str | None) -> bool:
|
||||
async def _send_blink_2fa_pin(hass: HomeAssistant, auth: Auth, pin: str) -> bool:
|
||||
"""Send 2FA pin to blink servers."""
|
||||
blink = Blink()
|
||||
blink = Blink(session=async_get_clientsession(hass))
|
||||
blink.auth = auth
|
||||
blink.setup_login_ids()
|
||||
blink.setup_urls()
|
||||
return auth.send_auth_key(blink, pin)
|
||||
return await auth.send_auth_key(blink, pin)
|
||||
|
||||
|
||||
class BlinkConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
@ -91,11 +92,15 @@ class BlinkConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
"""Handle a flow initiated by the user."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
self.auth = Auth({**user_input, "device_id": DEVICE_ID}, no_prompt=True)
|
||||
self.auth = Auth(
|
||||
{**user_input, "device_id": DEVICE_ID},
|
||||
no_prompt=True,
|
||||
session=async_get_clientsession(self.hass),
|
||||
)
|
||||
await self.async_set_unique_id(user_input[CONF_USERNAME])
|
||||
|
||||
try:
|
||||
await self.hass.async_add_executor_job(validate_input, self.auth)
|
||||
await validate_input(self.auth)
|
||||
return self._async_finish_flow()
|
||||
except Require2FA:
|
||||
return await self.async_step_2fa()
|
||||
|
@ -122,12 +127,9 @@ class BlinkConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
"""Handle 2FA step."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
pin: str | None = user_input.get(CONF_PIN)
|
||||
pin: str = str(user_input.get(CONF_PIN))
|
||||
try:
|
||||
assert self.auth
|
||||
valid_token = await self.hass.async_add_executor_job(
|
||||
_send_blink_2fa_pin, self.auth, pin
|
||||
)
|
||||
valid_token = await _send_blink_2fa_pin(self.hass, self.auth, pin)
|
||||
except BlinkSetupError:
|
||||
errors["base"] = "cannot_connect"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
|
|
|
@ -20,5 +20,5 @@
|
|||
"documentation": "https://www.home-assistant.io/integrations/blink",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["blinkpy"],
|
||||
"requirements": ["blinkpy==0.21.0"]
|
||||
"requirements": ["blinkpy==0.22.2"]
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
"""Support for Blink system camera sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
|
@ -17,6 +20,7 @@ from homeassistant.const import (
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from .const import DEFAULT_BRAND, DOMAIN, TYPE_TEMPERATURE, TYPE_WIFI_STRENGTH
|
||||
|
||||
|
@ -28,6 +32,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
|||
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_WIFI_STRENGTH,
|
||||
|
@ -35,6 +40,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
|||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
||||
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -50,7 +56,7 @@ async def async_setup_entry(
|
|||
for description in SENSOR_TYPES
|
||||
]
|
||||
|
||||
async_add_entities(entities)
|
||||
async_add_entities(entities, update_before_add=True)
|
||||
|
||||
|
||||
class BlinkSensor(SensorEntity):
|
||||
|
@ -76,10 +82,11 @@ class BlinkSensor(SensorEntity):
|
|||
model=self._camera.camera_type,
|
||||
)
|
||||
|
||||
def update(self) -> None:
|
||||
@property
|
||||
def native_value(self) -> StateType | date | datetime | Decimal:
|
||||
"""Retrieve sensor data from the camera."""
|
||||
try:
|
||||
self._attr_native_value = self._camera.attributes[self._sensor_key]
|
||||
native_value = self._camera.attributes[self._sensor_key]
|
||||
_LOGGER.debug(
|
||||
"'%s' %s = %s",
|
||||
self._camera.attributes["name"],
|
||||
|
@ -87,7 +94,8 @@ class BlinkSensor(SensorEntity):
|
|||
self._attr_native_value,
|
||||
)
|
||||
except KeyError:
|
||||
self._attr_native_value = None
|
||||
native_value = None
|
||||
_LOGGER.error(
|
||||
"%s not a valid camera attribute. Did the API change?", self._sensor_key
|
||||
)
|
||||
return native_value
|
||||
|
|
|
@ -539,7 +539,7 @@ bleak==0.21.1
|
|||
blebox-uniapi==2.2.0
|
||||
|
||||
# homeassistant.components.blink
|
||||
blinkpy==0.21.0
|
||||
blinkpy==0.22.2
|
||||
|
||||
# homeassistant.components.bitcoin
|
||||
blockchain==1.4.4
|
||||
|
|
|
@ -460,7 +460,7 @@ bleak==0.21.1
|
|||
blebox-uniapi==2.2.0
|
||||
|
||||
# homeassistant.components.blink
|
||||
blinkpy==0.21.0
|
||||
blinkpy==0.22.2
|
||||
|
||||
# homeassistant.components.bluemaestro
|
||||
bluemaestro-ble==0.2.3
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""Test the Blink config flow."""
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
from blinkpy.auth import LoginError
|
||||
from blinkpy.blinkpy import BlinkSetupError
|
||||
|
@ -268,10 +268,10 @@ async def test_options_flow(hass: HomeAssistant) -> None:
|
|||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
mock_auth = Mock(
|
||||
mock_auth = AsyncMock(
|
||||
startup=Mock(return_value=True), check_key_required=Mock(return_value=False)
|
||||
)
|
||||
mock_blink = Mock()
|
||||
mock_blink = AsyncMock()
|
||||
|
||||
with patch("homeassistant.components.blink.Auth", return_value=mock_auth), patch(
|
||||
"homeassistant.components.blink.Blink", return_value=mock_blink
|
||||
|
@ -293,7 +293,6 @@ async def test_options_flow(hass: HomeAssistant) -> None:
|
|||
result["flow_id"],
|
||||
user_input={"scan_interval": 5},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {"scan_interval": 5}
|
||||
await hass.async_block_till_done()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue