Add overlay options wrapper to rpi_camera (#34461)
* add overlay options wrapper to rpi_camera * Refactor to set config yaml section under the top level integration domain key * Remove return values that are not checked Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Remove superfluous debug log messages * Return if not set up via discovery * Add convenience reference to hass.data[DOMAIN] * Black formatting * Isort * Exclude all rpi_camera modules Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
29a05a6a65
commit
55bf5514ad
4 changed files with 148 additions and 58 deletions
|
@ -611,7 +611,7 @@ omit =
|
|||
homeassistant/components/roomba/vacuum.py
|
||||
homeassistant/components/route53/*
|
||||
homeassistant/components/rova/sensor.py
|
||||
homeassistant/components/rpi_camera/camera.py
|
||||
homeassistant/components/rpi_camera/*
|
||||
homeassistant/components/rpi_gpio/*
|
||||
homeassistant/components/rpi_gpio/cover.py
|
||||
homeassistant/components/rpi_gpio_pwm/light.py
|
||||
|
|
|
@ -1 +1,89 @@
|
|||
"""The rpi_camera component."""
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_FILE_PATH, CONF_NAME
|
||||
from homeassistant.helpers import config_validation as cv, discovery
|
||||
|
||||
from .const import (
|
||||
CONF_HORIZONTAL_FLIP,
|
||||
CONF_IMAGE_HEIGHT,
|
||||
CONF_IMAGE_QUALITY,
|
||||
CONF_IMAGE_ROTATION,
|
||||
CONF_IMAGE_WIDTH,
|
||||
CONF_OVERLAY_METADATA,
|
||||
CONF_OVERLAY_TIMESTAMP,
|
||||
CONF_TIMELAPSE,
|
||||
CONF_VERTICAL_FLIP,
|
||||
DEFAULT_HORIZONTAL_FLIP,
|
||||
DEFAULT_IMAGE_HEIGHT,
|
||||
DEFAULT_IMAGE_QUALITY,
|
||||
DEFAULT_IMAGE_ROTATION,
|
||||
DEFAULT_IMAGE_WIDTH,
|
||||
DEFAULT_NAME,
|
||||
DEFAULT_TIMELAPSE,
|
||||
DEFAULT_VERTICAL_FLIP,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_FILE_PATH): cv.isfile,
|
||||
vol.Optional(
|
||||
CONF_HORIZONTAL_FLIP, default=DEFAULT_HORIZONTAL_FLIP
|
||||
): vol.All(vol.Coerce(int), vol.Range(min=0, max=1)),
|
||||
vol.Optional(
|
||||
CONF_IMAGE_HEIGHT, default=DEFAULT_IMAGE_HEIGHT
|
||||
): vol.Coerce(int),
|
||||
vol.Optional(
|
||||
CONF_IMAGE_QUALITY, default=DEFAULT_IMAGE_QUALITY
|
||||
): vol.All(vol.Coerce(int), vol.Range(min=0, max=100)),
|
||||
vol.Optional(
|
||||
CONF_IMAGE_ROTATION, default=DEFAULT_IMAGE_ROTATION
|
||||
): vol.All(vol.Coerce(int), vol.Range(min=0, max=359)),
|
||||
vol.Optional(CONF_IMAGE_WIDTH, default=DEFAULT_IMAGE_WIDTH): vol.Coerce(
|
||||
int
|
||||
),
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_OVERLAY_METADATA): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=4, max=2056)
|
||||
),
|
||||
vol.Optional(CONF_OVERLAY_TIMESTAMP): cv.string,
|
||||
vol.Optional(CONF_TIMELAPSE, default=DEFAULT_TIMELAPSE): vol.Coerce(
|
||||
int
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_VERTICAL_FLIP, default=DEFAULT_VERTICAL_FLIP
|
||||
): vol.All(vol.Coerce(int), vol.Range(min=0, max=1)),
|
||||
}
|
||||
)
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Set up the rpi_camera integration."""
|
||||
config_domain = config[DOMAIN]
|
||||
hass.data[DOMAIN] = {
|
||||
CONF_FILE_PATH: config_domain.get(CONF_FILE_PATH),
|
||||
CONF_HORIZONTAL_FLIP: config_domain.get(CONF_HORIZONTAL_FLIP),
|
||||
CONF_IMAGE_WIDTH: config_domain.get(CONF_IMAGE_WIDTH),
|
||||
CONF_IMAGE_HEIGHT: config_domain.get(CONF_IMAGE_HEIGHT),
|
||||
CONF_IMAGE_QUALITY: config_domain.get(CONF_IMAGE_QUALITY),
|
||||
CONF_IMAGE_ROTATION: config_domain.get(CONF_IMAGE_ROTATION),
|
||||
CONF_NAME: config_domain.get(CONF_NAME),
|
||||
CONF_OVERLAY_METADATA: config_domain.get(CONF_OVERLAY_METADATA),
|
||||
CONF_OVERLAY_TIMESTAMP: config_domain.get(CONF_OVERLAY_TIMESTAMP),
|
||||
CONF_TIMELAPSE: config_domain.get(CONF_TIMELAPSE),
|
||||
CONF_VERTICAL_FLIP: config_domain.get(CONF_VERTICAL_FLIP),
|
||||
}
|
||||
|
||||
discovery.load_platform(hass, "camera", DOMAIN, {}, config)
|
||||
|
||||
return True
|
||||
|
|
|
@ -5,53 +5,24 @@ import shutil
|
|||
import subprocess
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.camera import PLATFORM_SCHEMA, Camera
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.const import CONF_FILE_PATH, CONF_NAME, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
from .const import (
|
||||
CONF_HORIZONTAL_FLIP,
|
||||
CONF_IMAGE_HEIGHT,
|
||||
CONF_IMAGE_QUALITY,
|
||||
CONF_IMAGE_ROTATION,
|
||||
CONF_IMAGE_WIDTH,
|
||||
CONF_OVERLAY_METADATA,
|
||||
CONF_OVERLAY_TIMESTAMP,
|
||||
CONF_TIMELAPSE,
|
||||
CONF_VERTICAL_FLIP,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_HORIZONTAL_FLIP = "horizontal_flip"
|
||||
CONF_IMAGE_HEIGHT = "image_height"
|
||||
CONF_IMAGE_QUALITY = "image_quality"
|
||||
CONF_IMAGE_ROTATION = "image_rotation"
|
||||
CONF_IMAGE_WIDTH = "image_width"
|
||||
CONF_TIMELAPSE = "timelapse"
|
||||
CONF_VERTICAL_FLIP = "vertical_flip"
|
||||
|
||||
DEFAULT_HORIZONTAL_FLIP = 0
|
||||
DEFAULT_IMAGE_HEIGHT = 480
|
||||
DEFAULT_IMAGE_QUALITY = 7
|
||||
DEFAULT_IMAGE_ROTATION = 0
|
||||
DEFAULT_IMAGE_WIDTH = 640
|
||||
DEFAULT_NAME = "Raspberry Pi Camera"
|
||||
DEFAULT_TIMELAPSE = 1000
|
||||
DEFAULT_VERTICAL_FLIP = 0
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_FILE_PATH): cv.isfile,
|
||||
vol.Optional(CONF_HORIZONTAL_FLIP, default=DEFAULT_HORIZONTAL_FLIP): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=0, max=1)
|
||||
),
|
||||
vol.Optional(CONF_IMAGE_HEIGHT, default=DEFAULT_IMAGE_HEIGHT): vol.Coerce(int),
|
||||
vol.Optional(CONF_IMAGE_QUALITY, default=DEFAULT_IMAGE_QUALITY): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=0, max=100)
|
||||
),
|
||||
vol.Optional(CONF_IMAGE_ROTATION, default=DEFAULT_IMAGE_ROTATION): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=0, max=359)
|
||||
),
|
||||
vol.Optional(CONF_IMAGE_WIDTH, default=DEFAULT_IMAGE_WIDTH): vol.Coerce(int),
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_TIMELAPSE, default=1000): vol.Coerce(int),
|
||||
vol.Optional(CONF_VERTICAL_FLIP, default=DEFAULT_VERTICAL_FLIP): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=0, max=1)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def kill_raspistill(*args):
|
||||
"""Kill any previously running raspistill process.."""
|
||||
|
@ -62,24 +33,18 @@ def kill_raspistill(*args):
|
|||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Raspberry Camera."""
|
||||
# We only want this platform to be set up via discovery.
|
||||
# prevent initializing by erroneous platform config section in yaml conf
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
if shutil.which("raspistill") is None:
|
||||
_LOGGER.error("'raspistill' was not found")
|
||||
return False
|
||||
|
||||
setup_config = {
|
||||
CONF_NAME: config.get(CONF_NAME),
|
||||
CONF_IMAGE_WIDTH: config.get(CONF_IMAGE_WIDTH),
|
||||
CONF_IMAGE_HEIGHT: config.get(CONF_IMAGE_HEIGHT),
|
||||
CONF_IMAGE_QUALITY: config.get(CONF_IMAGE_QUALITY),
|
||||
CONF_IMAGE_ROTATION: config.get(CONF_IMAGE_ROTATION),
|
||||
CONF_TIMELAPSE: config.get(CONF_TIMELAPSE),
|
||||
CONF_HORIZONTAL_FLIP: config.get(CONF_HORIZONTAL_FLIP),
|
||||
CONF_VERTICAL_FLIP: config.get(CONF_VERTICAL_FLIP),
|
||||
CONF_FILE_PATH: config.get(CONF_FILE_PATH),
|
||||
}
|
||||
return
|
||||
|
||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, kill_raspistill)
|
||||
|
||||
setup_config = hass.data[DOMAIN]
|
||||
file_path = setup_config[CONF_FILE_PATH]
|
||||
|
||||
def delete_temp_file(*args):
|
||||
|
@ -100,7 +65,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
# Check whether the file path has been whitelisted
|
||||
elif not hass.config.is_allowed_path(file_path):
|
||||
_LOGGER.error("'%s' is not a whitelisted directory", file_path)
|
||||
return False
|
||||
return
|
||||
|
||||
add_entities([RaspberryCamera(setup_config)])
|
||||
|
||||
|
@ -142,6 +107,16 @@ class RaspberryCamera(Camera):
|
|||
if device_info[CONF_VERTICAL_FLIP]:
|
||||
cmd_args.append("-vf")
|
||||
|
||||
if device_info[CONF_OVERLAY_METADATA]:
|
||||
cmd_args.append("-a")
|
||||
cmd_args.append(str(device_info[CONF_OVERLAY_METADATA]))
|
||||
|
||||
if device_info[CONF_OVERLAY_TIMESTAMP]:
|
||||
cmd_args.append("-a")
|
||||
cmd_args.append("4")
|
||||
cmd_args.append("-a")
|
||||
cmd_args.append(str(device_info[CONF_OVERLAY_TIMESTAMP]))
|
||||
|
||||
subprocess.Popen(cmd_args, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
||||
|
||||
def camera_image(self):
|
||||
|
@ -153,3 +128,8 @@ class RaspberryCamera(Camera):
|
|||
def name(self):
|
||||
"""Return the name of this camera."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def frame_interval(self):
|
||||
"""Return the interval between frames of the stream."""
|
||||
return self._config[CONF_TIMELAPSE] / 1000
|
||||
|
|
22
homeassistant/components/rpi_camera/const.py
Normal file
22
homeassistant/components/rpi_camera/const.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
"""Consts used by rpi_camera."""
|
||||
|
||||
DOMAIN = "rpi_camera"
|
||||
|
||||
CONF_HORIZONTAL_FLIP = "horizontal_flip"
|
||||
CONF_IMAGE_HEIGHT = "image_height"
|
||||
CONF_IMAGE_QUALITY = "image_quality"
|
||||
CONF_IMAGE_ROTATION = "image_rotation"
|
||||
CONF_IMAGE_WIDTH = "image_width"
|
||||
CONF_OVERLAY_METADATA = "overlay_metadata"
|
||||
CONF_OVERLAY_TIMESTAMP = "overlay_timestamp"
|
||||
CONF_TIMELAPSE = "timelapse"
|
||||
CONF_VERTICAL_FLIP = "vertical_flip"
|
||||
|
||||
DEFAULT_HORIZONTAL_FLIP = 0
|
||||
DEFAULT_IMAGE_HEIGHT = 480
|
||||
DEFAULT_IMAGE_QUALITY = 7
|
||||
DEFAULT_IMAGE_ROTATION = 0
|
||||
DEFAULT_IMAGE_WIDTH = 640
|
||||
DEFAULT_NAME = "Raspberry Pi Camera"
|
||||
DEFAULT_TIMELAPSE = 1000
|
||||
DEFAULT_VERTICAL_FLIP = 0
|
Loading…
Add table
Reference in a new issue