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.components import persistent_notification
|
||||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry
|
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.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
@ -18,6 +24,7 @@ from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
SERVICE_REFRESH,
|
SERVICE_REFRESH,
|
||||||
|
SERVICE_SAVE_RECENT_CLIPS,
|
||||||
SERVICE_SAVE_VIDEO,
|
SERVICE_SAVE_VIDEO,
|
||||||
SERVICE_SEND_PIN,
|
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}
|
{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_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:
|
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."""
|
"""Call save video service handler."""
|
||||||
await async_handle_save_video_service(hass, entry, call)
|
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):
|
def send_pin(call):
|
||||||
"""Call blink to send new pin."""
|
"""Call blink to send new pin."""
|
||||||
pin = call.data[CONF_PIN]
|
pin = call.data[CONF_PIN]
|
||||||
|
@ -112,6 +126,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
hass.services.async_register(
|
hass.services.async_register(
|
||||||
DOMAIN, SERVICE_SAVE_VIDEO, async_save_video, schema=SERVICE_SAVE_VIDEO_SCHEMA
|
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(
|
hass.services.async_register(
|
||||||
DOMAIN, SERVICE_SEND_PIN, send_pin, schema=SERVICE_SEND_PIN_SCHEMA
|
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)
|
_LOGGER.error("Can't write %s, no access to path!", video_path)
|
||||||
return
|
return
|
||||||
|
|
||||||
def _write_video(camera_name, video_path):
|
def _write_video(name, file_path):
|
||||||
"""Call video write."""
|
"""Call video write."""
|
||||||
all_cameras = hass.data[DOMAIN][entry.entry_id].cameras
|
all_cameras = hass.data[DOMAIN][entry.entry_id].cameras
|
||||||
if camera_name in all_cameras:
|
if name in all_cameras:
|
||||||
all_cameras[camera_name].video_to_file(video_path)
|
all_cameras[name].video_to_file(file_path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await hass.async_add_executor_job(_write_video, camera_name, video_path)
|
await hass.async_add_executor_job(_write_video, camera_name, video_path)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
_LOGGER.error("Can't write image to file: %s", 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:
|
def update(self) -> None:
|
||||||
"""Update the state of the device."""
|
"""Update the state of the device."""
|
||||||
_LOGGER.debug("Updating Blink Alarm Control Panel %s", self._name)
|
if self.data.check_if_ok_to_update():
|
||||||
self.data.refresh()
|
_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 = (
|
self._attr_state = (
|
||||||
STATE_ALARM_ARMED_AWAY if self.sync.arm else STATE_ALARM_DISARMED
|
STATE_ALARM_ARMED_AWAY if self.sync.arm else STATE_ALARM_DISARMED
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Support for Blink system camera control."""
|
"""Support for Blink system camera control."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorDeviceClass,
|
BinarySensorDeviceClass,
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
|
@ -20,6 +22,8 @@ from .const import (
|
||||||
TYPE_MOTION_DETECTED,
|
TYPE_MOTION_DETECTED,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
BINARY_SENSORS_TYPES: tuple[BinarySensorEntityDescription, ...] = (
|
BINARY_SENSORS_TYPES: tuple[BinarySensorEntityDescription, ...] = (
|
||||||
BinarySensorEntityDescription(
|
BinarySensorEntityDescription(
|
||||||
key=TYPE_BATTERY,
|
key=TYPE_BATTERY,
|
||||||
|
@ -74,8 +78,13 @@ class BlinkBinarySensor(BinarySensorEntity):
|
||||||
|
|
||||||
def update(self) -> None:
|
def update(self) -> None:
|
||||||
"""Update sensor state."""
|
"""Update sensor state."""
|
||||||
self.data.refresh()
|
|
||||||
state = self._camera.attributes[self.entity_description.key]
|
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:
|
if self.entity_description.key == TYPE_BATTERY:
|
||||||
state = state != "ok"
|
state = state != "ok"
|
||||||
self._attr_is_on = state
|
self._attr_is_on = state
|
||||||
|
|
|
@ -23,6 +23,7 @@ TYPE_WIFI_STRENGTH = "wifi_strength"
|
||||||
SERVICE_REFRESH = "blink_update"
|
SERVICE_REFRESH = "blink_update"
|
||||||
SERVICE_TRIGGER = "trigger_camera"
|
SERVICE_TRIGGER = "trigger_camera"
|
||||||
SERVICE_SAVE_VIDEO = "save_video"
|
SERVICE_SAVE_VIDEO = "save_video"
|
||||||
|
SERVICE_SAVE_RECENT_CLIPS = "save_recent_clips"
|
||||||
SERVICE_SEND_PIN = "send_pin"
|
SERVICE_SEND_PIN = "send_pin"
|
||||||
|
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
|
|
|
@ -78,9 +78,14 @@ class BlinkSensor(SensorEntity):
|
||||||
|
|
||||||
def update(self) -> None:
|
def update(self) -> None:
|
||||||
"""Retrieve sensor data from the camera."""
|
"""Retrieve sensor data from the camera."""
|
||||||
self.data.refresh()
|
|
||||||
try:
|
try:
|
||||||
self._attr_native_value = self._camera.attributes[self._sensor_key]
|
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:
|
except KeyError:
|
||||||
self._attr_native_value = None
|
self._attr_native_value = None
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
|
|
|
@ -25,12 +25,31 @@ save_video:
|
||||||
text:
|
text:
|
||||||
filename:
|
filename:
|
||||||
name: File name
|
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
|
required: true
|
||||||
example: "/tmp/video.mp4"
|
example: "/tmp/video.mp4"
|
||||||
selector:
|
selector:
|
||||||
text:
|
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:
|
send_pin:
|
||||||
name: Send pin
|
name: Send pin
|
||||||
description: Send a new PIN to blink for 2FA.
|
description: Send a new PIN to blink for 2FA.
|
||||||
|
|
Loading…
Add table
Reference in a new issue