mirror of
https://github.com/home-assistant/core.git
synced 2025-12-25 05:26:47 +00:00
Remove rpi_camera (#157245)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"domain": "raspberry_pi",
|
||||
"name": "Raspberry Pi",
|
||||
"integrations": ["raspberry_pi", "rpi_camera", "rpi_power", "remote_rpi_gpio"]
|
||||
"integrations": ["raspberry_pi", "rpi_power", "remote_rpi_gpio"]
|
||||
}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
"""The rpi_camera component."""
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_FILE_PATH, CONF_NAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv, discovery
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
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: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""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, Platform.CAMERA, DOMAIN, {}, config)
|
||||
|
||||
return True
|
||||
@@ -1,156 +0,0 @@
|
||||
"""Camera platform that has a Raspberry Pi camera."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from tempfile import NamedTemporaryFile
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.const import CONF_FILE_PATH, CONF_NAME, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
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__)
|
||||
|
||||
|
||||
def kill_raspistill(*args):
|
||||
"""Kill any previously running raspistill process.."""
|
||||
with subprocess.Popen(
|
||||
["killall", "raspistill"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.STDOUT,
|
||||
close_fds=False, # required for posix_spawn
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> 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
|
||||
|
||||
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):
|
||||
"""Delete the temporary file to prevent saving multiple temp images.
|
||||
|
||||
Only used when no path is defined
|
||||
"""
|
||||
os.remove(file_path)
|
||||
|
||||
# If no file path is defined, use a temporary file
|
||||
if file_path is None:
|
||||
with NamedTemporaryFile(suffix=".jpg", delete=False) as temp_file:
|
||||
file_path = temp_file.name
|
||||
setup_config[CONF_FILE_PATH] = file_path
|
||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, delete_temp_file)
|
||||
|
||||
# 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
|
||||
|
||||
add_entities([RaspberryCamera(setup_config)])
|
||||
|
||||
|
||||
class RaspberryCamera(Camera):
|
||||
"""Representation of a Raspberry Pi camera."""
|
||||
|
||||
def __init__(self, device_info: dict[str, Any]) -> None:
|
||||
"""Initialize Raspberry Pi camera component."""
|
||||
super().__init__()
|
||||
|
||||
self._attr_name = device_info[CONF_NAME]
|
||||
self._config = device_info
|
||||
|
||||
# Kill if there's raspistill instance
|
||||
kill_raspistill()
|
||||
|
||||
cmd_args = [
|
||||
"raspistill",
|
||||
"--nopreview",
|
||||
"-o",
|
||||
device_info[CONF_FILE_PATH],
|
||||
"-t",
|
||||
"0",
|
||||
"-w",
|
||||
str(device_info[CONF_IMAGE_WIDTH]),
|
||||
"-h",
|
||||
str(device_info[CONF_IMAGE_HEIGHT]),
|
||||
"-tl",
|
||||
str(device_info[CONF_TIMELAPSE]),
|
||||
"-q",
|
||||
str(device_info[CONF_IMAGE_QUALITY]),
|
||||
"-rot",
|
||||
str(device_info[CONF_IMAGE_ROTATION]),
|
||||
]
|
||||
if device_info[CONF_HORIZONTAL_FLIP]:
|
||||
cmd_args.append("-hf")
|
||||
|
||||
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]))
|
||||
|
||||
# The raspistill process started below must run "forever" in
|
||||
# the background until killed when Home Assistant is stopped.
|
||||
# Therefore it must not be wrapped with "with", since that
|
||||
# waits for the subprocess to exit before continuing.
|
||||
subprocess.Popen( # pylint: disable=consider-using-with
|
||||
cmd_args,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.STDOUT,
|
||||
close_fds=False, # required for posix_spawn
|
||||
)
|
||||
|
||||
def camera_image(
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return raspistill image response."""
|
||||
with open(self._config[CONF_FILE_PATH], "rb") as file:
|
||||
return file.read()
|
||||
|
||||
@property
|
||||
def frame_interval(self) -> float:
|
||||
"""Return the interval between frames of the stream."""
|
||||
return self._config[CONF_TIMELAPSE] / 1000
|
||||
@@ -1,22 +0,0 @@
|
||||
"""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
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"domain": "rpi_camera",
|
||||
"name": "Raspberry Pi Camera",
|
||||
"codeowners": [],
|
||||
"documentation": "https://www.home-assistant.io/integrations/rpi_camera",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "legacy"
|
||||
}
|
||||
@@ -5413,12 +5413,6 @@
|
||||
"config_flow": false,
|
||||
"name": "Raspberry Pi"
|
||||
},
|
||||
"rpi_camera": {
|
||||
"integration_type": "hub",
|
||||
"config_flow": false,
|
||||
"iot_class": "local_polling",
|
||||
"name": "Raspberry Pi Camera"
|
||||
},
|
||||
"rpi_power": {
|
||||
"integration_type": "hub",
|
||||
"config_flow": true,
|
||||
|
||||
@@ -831,7 +831,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [
|
||||
"roon",
|
||||
"route53",
|
||||
"rova",
|
||||
"rpi_camera",
|
||||
"rpi_power",
|
||||
"rss_feed_template",
|
||||
"rtorrent",
|
||||
@@ -1856,7 +1855,6 @@ INTEGRATIONS_WITHOUT_SCALE = [
|
||||
"roon",
|
||||
"route53",
|
||||
"rova",
|
||||
"rpi_camera",
|
||||
"rpi_power",
|
||||
"rss_feed_template",
|
||||
"rtorrent",
|
||||
|
||||
Reference in New Issue
Block a user