Add save clips to Blink services (#84149)
This commit is contained in:
parent
a1a055f618
commit
23ca26ae56
6 changed files with 90 additions and 9 deletions
|
@ -8,7 +8,13 @@ import voluptuous as vol
|
|||
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry
|
||||
from homeassistant.const import CONF_FILENAME, CONF_NAME, CONF_PIN, CONF_SCAN_INTERVAL
|
||||
from homeassistant.const import (
|
||||
CONF_FILE_PATH,
|
||||
CONF_FILENAME,
|
||||
CONF_NAME,
|
||||
CONF_PIN,
|
||||
CONF_SCAN_INTERVAL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
@ -18,6 +24,7 @@ from .const import (
|
|||
DOMAIN,
|
||||
PLATFORMS,
|
||||
SERVICE_REFRESH,
|
||||
SERVICE_SAVE_RECENT_CLIPS,
|
||||
SERVICE_SAVE_VIDEO,
|
||||
SERVICE_SEND_PIN,
|
||||
)
|
||||
|
@ -28,6 +35,9 @@ SERVICE_SAVE_VIDEO_SCHEMA = vol.Schema(
|
|||
{vol.Required(CONF_NAME): cv.string, vol.Required(CONF_FILENAME): cv.string}
|
||||
)
|
||||
SERVICE_SEND_PIN_SCHEMA = vol.Schema({vol.Optional(CONF_PIN): cv.string})
|
||||
SERVICE_SAVE_RECENT_CLIPS_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_NAME): cv.string, vol.Required(CONF_FILE_PATH): cv.string}
|
||||
)
|
||||
|
||||
|
||||
def _blink_startup_wrapper(hass: HomeAssistant, entry: ConfigEntry) -> Blink:
|
||||
|
@ -100,6 +110,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
"""Call save video service handler."""
|
||||
await async_handle_save_video_service(hass, entry, call)
|
||||
|
||||
async def async_save_recent_clips(call):
|
||||
"""Call save recent clips service handler."""
|
||||
await async_handle_save_recent_clips_service(hass, entry, call)
|
||||
|
||||
def send_pin(call):
|
||||
"""Call blink to send new pin."""
|
||||
pin = call.data[CONF_PIN]
|
||||
|
@ -112,6 +126,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SAVE_VIDEO, async_save_video, schema=SERVICE_SAVE_VIDEO_SCHEMA
|
||||
)
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_SAVE_RECENT_CLIPS,
|
||||
async_save_recent_clips,
|
||||
schema=SERVICE_SAVE_RECENT_CLIPS_SCHEMA,
|
||||
)
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SEND_PIN, send_pin, schema=SERVICE_SEND_PIN_SCHEMA
|
||||
)
|
||||
|
@ -164,13 +184,33 @@ async def async_handle_save_video_service(hass, entry, call):
|
|||
_LOGGER.error("Can't write %s, no access to path!", video_path)
|
||||
return
|
||||
|
||||
def _write_video(camera_name, video_path):
|
||||
def _write_video(name, file_path):
|
||||
"""Call video write."""
|
||||
all_cameras = hass.data[DOMAIN][entry.entry_id].cameras
|
||||
if camera_name in all_cameras:
|
||||
all_cameras[camera_name].video_to_file(video_path)
|
||||
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)
|
||||
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):
|
||||
"""Save multiple recent clips to output directory."""
|
||||
camera_name = call.data[CONF_NAME]
|
||||
clips_dir = call.data[CONF_FILE_PATH]
|
||||
if not hass.config.is_allowed_path(clips_dir):
|
||||
_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)
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't write recent clips to directory: %s", err)
|
||||
|
|
|
@ -55,8 +55,15 @@ class BlinkSyncModule(AlarmControlPanelEntity):
|
|||
|
||||
def update(self) -> None:
|
||||
"""Update the state of the device."""
|
||||
_LOGGER.debug("Updating Blink Alarm Control Panel %s", self._name)
|
||||
self.data.refresh()
|
||||
if self.data.check_if_ok_to_update():
|
||||
_LOGGER.debug(
|
||||
"Initiating a blink.refresh() from BlinkSyncModule('%s') (%s)",
|
||||
self._name,
|
||||
self.data,
|
||||
)
|
||||
self.data.refresh()
|
||||
_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
|
||||
)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""Support for Blink system camera control."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
|
@ -20,6 +22,8 @@ from .const import (
|
|||
TYPE_MOTION_DETECTED,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
BINARY_SENSORS_TYPES: tuple[BinarySensorEntityDescription, ...] = (
|
||||
BinarySensorEntityDescription(
|
||||
key=TYPE_BATTERY,
|
||||
|
@ -74,8 +78,13 @@ class BlinkBinarySensor(BinarySensorEntity):
|
|||
|
||||
def update(self) -> None:
|
||||
"""Update sensor state."""
|
||||
self.data.refresh()
|
||||
state = self._camera.attributes[self.entity_description.key]
|
||||
_LOGGER.debug(
|
||||
"'%s' %s = %s",
|
||||
self._camera.attributes["name"],
|
||||
self.entity_description.key,
|
||||
state,
|
||||
)
|
||||
if self.entity_description.key == TYPE_BATTERY:
|
||||
state = state != "ok"
|
||||
self._attr_is_on = state
|
||||
|
|
|
@ -23,6 +23,7 @@ TYPE_WIFI_STRENGTH = "wifi_strength"
|
|||
SERVICE_REFRESH = "blink_update"
|
||||
SERVICE_TRIGGER = "trigger_camera"
|
||||
SERVICE_SAVE_VIDEO = "save_video"
|
||||
SERVICE_SAVE_RECENT_CLIPS = "save_recent_clips"
|
||||
SERVICE_SEND_PIN = "send_pin"
|
||||
|
||||
PLATFORMS = [
|
||||
|
|
|
@ -78,9 +78,14 @@ class BlinkSensor(SensorEntity):
|
|||
|
||||
def update(self) -> None:
|
||||
"""Retrieve sensor data from the camera."""
|
||||
self.data.refresh()
|
||||
try:
|
||||
self._attr_native_value = self._camera.attributes[self._sensor_key]
|
||||
_LOGGER.debug(
|
||||
"'%s' %s = %s",
|
||||
self._camera.attributes["name"],
|
||||
self._sensor_key,
|
||||
self._attr_native_value,
|
||||
)
|
||||
except KeyError:
|
||||
self._attr_native_value = None
|
||||
_LOGGER.error(
|
||||
|
|
|
@ -25,12 +25,31 @@ save_video:
|
|||
text:
|
||||
filename:
|
||||
name: File name
|
||||
description: Filename to writable path (directory may need to be included in whitelist_dirs in config)
|
||||
description: Filename to writable path (directory may need to be included in allowlist_external_dirs in config)
|
||||
required: true
|
||||
example: "/tmp/video.mp4"
|
||||
selector:
|
||||
text:
|
||||
|
||||
save_recent_clips:
|
||||
name: Save recent clips
|
||||
description: 'Save all recent video clips to local directory with file pattern "%Y%m%d_%H%M%S_{name}.mp4"'
|
||||
fields:
|
||||
name:
|
||||
name: Name
|
||||
description: Name of camera to grab recent clips from.
|
||||
required: true
|
||||
example: "Living Room"
|
||||
selector:
|
||||
text:
|
||||
file_path:
|
||||
name: Output directory
|
||||
description: Directory name of writable path (directory may need to be included in allowlist_external_dirs in config)
|
||||
required: true
|
||||
example: "/tmp"
|
||||
selector:
|
||||
text:
|
||||
|
||||
send_pin:
|
||||
name: Send pin
|
||||
description: Send a new PIN to blink for 2FA.
|
||||
|
|
Loading…
Add table
Reference in a new issue