Skip to content

Commit 55bf551

Browse files
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 <[email protected]> * 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 <[email protected]>
1 parent 29a05a6 commit 55bf551

File tree

4 files changed

+148
-58
lines changed

4 files changed

+148
-58
lines changed

.coveragerc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ omit =
611611
homeassistant/components/roomba/vacuum.py
612612
homeassistant/components/route53/*
613613
homeassistant/components/rova/sensor.py
614-
homeassistant/components/rpi_camera/camera.py
614+
homeassistant/components/rpi_camera/*
615615
homeassistant/components/rpi_gpio/*
616616
homeassistant/components/rpi_gpio/cover.py
617617
homeassistant/components/rpi_gpio_pwm/light.py
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,89 @@
11
"""The rpi_camera component."""
2+
import logging
3+
4+
import voluptuous as vol
5+
6+
from homeassistant.const import CONF_FILE_PATH, CONF_NAME
7+
from homeassistant.helpers import config_validation as cv, discovery
8+
9+
from .const import (
10+
CONF_HORIZONTAL_FLIP,
11+
CONF_IMAGE_HEIGHT,
12+
CONF_IMAGE_QUALITY,
13+
CONF_IMAGE_ROTATION,
14+
CONF_IMAGE_WIDTH,
15+
CONF_OVERLAY_METADATA,
16+
CONF_OVERLAY_TIMESTAMP,
17+
CONF_TIMELAPSE,
18+
CONF_VERTICAL_FLIP,
19+
DEFAULT_HORIZONTAL_FLIP,
20+
DEFAULT_IMAGE_HEIGHT,
21+
DEFAULT_IMAGE_QUALITY,
22+
DEFAULT_IMAGE_ROTATION,
23+
DEFAULT_IMAGE_WIDTH,
24+
DEFAULT_NAME,
25+
DEFAULT_TIMELAPSE,
26+
DEFAULT_VERTICAL_FLIP,
27+
DOMAIN,
28+
)
29+
30+
_LOGGER = logging.getLogger(__name__)
31+
32+
CONFIG_SCHEMA = vol.Schema(
33+
{
34+
DOMAIN: vol.Schema(
35+
{
36+
vol.Optional(CONF_FILE_PATH): cv.isfile,
37+
vol.Optional(
38+
CONF_HORIZONTAL_FLIP, default=DEFAULT_HORIZONTAL_FLIP
39+
): vol.All(vol.Coerce(int), vol.Range(min=0, max=1)),
40+
vol.Optional(
41+
CONF_IMAGE_HEIGHT, default=DEFAULT_IMAGE_HEIGHT
42+
): vol.Coerce(int),
43+
vol.Optional(
44+
CONF_IMAGE_QUALITY, default=DEFAULT_IMAGE_QUALITY
45+
): vol.All(vol.Coerce(int), vol.Range(min=0, max=100)),
46+
vol.Optional(
47+
CONF_IMAGE_ROTATION, default=DEFAULT_IMAGE_ROTATION
48+
): vol.All(vol.Coerce(int), vol.Range(min=0, max=359)),
49+
vol.Optional(CONF_IMAGE_WIDTH, default=DEFAULT_IMAGE_WIDTH): vol.Coerce(
50+
int
51+
),
52+
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
53+
vol.Optional(CONF_OVERLAY_METADATA): vol.All(
54+
vol.Coerce(int), vol.Range(min=4, max=2056)
55+
),
56+
vol.Optional(CONF_OVERLAY_TIMESTAMP): cv.string,
57+
vol.Optional(CONF_TIMELAPSE, default=DEFAULT_TIMELAPSE): vol.Coerce(
58+
int
59+
),
60+
vol.Optional(
61+
CONF_VERTICAL_FLIP, default=DEFAULT_VERTICAL_FLIP
62+
): vol.All(vol.Coerce(int), vol.Range(min=0, max=1)),
63+
}
64+
)
65+
},
66+
extra=vol.ALLOW_EXTRA,
67+
)
68+
69+
70+
def setup(hass, config):
71+
"""Set up the rpi_camera integration."""
72+
config_domain = config[DOMAIN]
73+
hass.data[DOMAIN] = {
74+
CONF_FILE_PATH: config_domain.get(CONF_FILE_PATH),
75+
CONF_HORIZONTAL_FLIP: config_domain.get(CONF_HORIZONTAL_FLIP),
76+
CONF_IMAGE_WIDTH: config_domain.get(CONF_IMAGE_WIDTH),
77+
CONF_IMAGE_HEIGHT: config_domain.get(CONF_IMAGE_HEIGHT),
78+
CONF_IMAGE_QUALITY: config_domain.get(CONF_IMAGE_QUALITY),
79+
CONF_IMAGE_ROTATION: config_domain.get(CONF_IMAGE_ROTATION),
80+
CONF_NAME: config_domain.get(CONF_NAME),
81+
CONF_OVERLAY_METADATA: config_domain.get(CONF_OVERLAY_METADATA),
82+
CONF_OVERLAY_TIMESTAMP: config_domain.get(CONF_OVERLAY_TIMESTAMP),
83+
CONF_TIMELAPSE: config_domain.get(CONF_TIMELAPSE),
84+
CONF_VERTICAL_FLIP: config_domain.get(CONF_VERTICAL_FLIP),
85+
}
86+
87+
discovery.load_platform(hass, "camera", DOMAIN, {}, config)
88+
89+
return True

homeassistant/components/rpi_camera/camera.py

Lines changed: 37 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,53 +5,24 @@
55
import subprocess
66
from tempfile import NamedTemporaryFile
77

8-
import voluptuous as vol
9-
10-
from homeassistant.components.camera import PLATFORM_SCHEMA, Camera
8+
from homeassistant.components.camera import Camera
119
from homeassistant.const import CONF_FILE_PATH, CONF_NAME, EVENT_HOMEASSISTANT_STOP
12-
from homeassistant.helpers import config_validation as cv
13-
14-
_LOGGER = logging.getLogger(__name__)
1510

16-
CONF_HORIZONTAL_FLIP = "horizontal_flip"
17-
CONF_IMAGE_HEIGHT = "image_height"
18-
CONF_IMAGE_QUALITY = "image_quality"
19-
CONF_IMAGE_ROTATION = "image_rotation"
20-
CONF_IMAGE_WIDTH = "image_width"
21-
CONF_TIMELAPSE = "timelapse"
22-
CONF_VERTICAL_FLIP = "vertical_flip"
23-
24-
DEFAULT_HORIZONTAL_FLIP = 0
25-
DEFAULT_IMAGE_HEIGHT = 480
26-
DEFAULT_IMAGE_QUALITY = 7
27-
DEFAULT_IMAGE_ROTATION = 0
28-
DEFAULT_IMAGE_WIDTH = 640
29-
DEFAULT_NAME = "Raspberry Pi Camera"
30-
DEFAULT_TIMELAPSE = 1000
31-
DEFAULT_VERTICAL_FLIP = 0
32-
33-
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
34-
{
35-
vol.Optional(CONF_FILE_PATH): cv.isfile,
36-
vol.Optional(CONF_HORIZONTAL_FLIP, default=DEFAULT_HORIZONTAL_FLIP): vol.All(
37-
vol.Coerce(int), vol.Range(min=0, max=1)
38-
),
39-
vol.Optional(CONF_IMAGE_HEIGHT, default=DEFAULT_IMAGE_HEIGHT): vol.Coerce(int),
40-
vol.Optional(CONF_IMAGE_QUALITY, default=DEFAULT_IMAGE_QUALITY): vol.All(
41-
vol.Coerce(int), vol.Range(min=0, max=100)
42-
),
43-
vol.Optional(CONF_IMAGE_ROTATION, default=DEFAULT_IMAGE_ROTATION): vol.All(
44-
vol.Coerce(int), vol.Range(min=0, max=359)
45-
),
46-
vol.Optional(CONF_IMAGE_WIDTH, default=DEFAULT_IMAGE_WIDTH): vol.Coerce(int),
47-
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
48-
vol.Optional(CONF_TIMELAPSE, default=1000): vol.Coerce(int),
49-
vol.Optional(CONF_VERTICAL_FLIP, default=DEFAULT_VERTICAL_FLIP): vol.All(
50-
vol.Coerce(int), vol.Range(min=0, max=1)
51-
),
52-
}
11+
from .const import (
12+
CONF_HORIZONTAL_FLIP,
13+
CONF_IMAGE_HEIGHT,
14+
CONF_IMAGE_QUALITY,
15+
CONF_IMAGE_ROTATION,
16+
CONF_IMAGE_WIDTH,
17+
CONF_OVERLAY_METADATA,
18+
CONF_OVERLAY_TIMESTAMP,
19+
CONF_TIMELAPSE,
20+
CONF_VERTICAL_FLIP,
21+
DOMAIN,
5322
)
5423

24+
_LOGGER = logging.getLogger(__name__)
25+
5526

5627
def kill_raspistill(*args):
5728
"""Kill any previously running raspistill process.."""
@@ -62,24 +33,18 @@ def kill_raspistill(*args):
6233

6334
def setup_platform(hass, config, add_entities, discovery_info=None):
6435
"""Set up the Raspberry Camera."""
36+
# We only want this platform to be set up via discovery.
37+
# prevent initializing by erroneous platform config section in yaml conf
38+
if discovery_info is None:
39+
return
40+
6541
if shutil.which("raspistill") is None:
6642
_LOGGER.error("'raspistill' was not found")
67-
return False
68-
69-
setup_config = {
70-
CONF_NAME: config.get(CONF_NAME),
71-
CONF_IMAGE_WIDTH: config.get(CONF_IMAGE_WIDTH),
72-
CONF_IMAGE_HEIGHT: config.get(CONF_IMAGE_HEIGHT),
73-
CONF_IMAGE_QUALITY: config.get(CONF_IMAGE_QUALITY),
74-
CONF_IMAGE_ROTATION: config.get(CONF_IMAGE_ROTATION),
75-
CONF_TIMELAPSE: config.get(CONF_TIMELAPSE),
76-
CONF_HORIZONTAL_FLIP: config.get(CONF_HORIZONTAL_FLIP),
77-
CONF_VERTICAL_FLIP: config.get(CONF_VERTICAL_FLIP),
78-
CONF_FILE_PATH: config.get(CONF_FILE_PATH),
79-
}
43+
return
8044

8145
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, kill_raspistill)
8246

47+
setup_config = hass.data[DOMAIN]
8348
file_path = setup_config[CONF_FILE_PATH]
8449

8550
def delete_temp_file(*args):
@@ -100,7 +65,7 @@ def delete_temp_file(*args):
10065
# Check whether the file path has been whitelisted
10166
elif not hass.config.is_allowed_path(file_path):
10267
_LOGGER.error("'%s' is not a whitelisted directory", file_path)
103-
return False
68+
return
10469

10570
add_entities([RaspberryCamera(setup_config)])
10671

@@ -142,6 +107,16 @@ def __init__(self, device_info):
142107
if device_info[CONF_VERTICAL_FLIP]:
143108
cmd_args.append("-vf")
144109

110+
if device_info[CONF_OVERLAY_METADATA]:
111+
cmd_args.append("-a")
112+
cmd_args.append(str(device_info[CONF_OVERLAY_METADATA]))
113+
114+
if device_info[CONF_OVERLAY_TIMESTAMP]:
115+
cmd_args.append("-a")
116+
cmd_args.append("4")
117+
cmd_args.append("-a")
118+
cmd_args.append(str(device_info[CONF_OVERLAY_TIMESTAMP]))
119+
145120
subprocess.Popen(cmd_args, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
146121

147122
def camera_image(self):
@@ -153,3 +128,8 @@ def camera_image(self):
153128
def name(self):
154129
"""Return the name of this camera."""
155130
return self._name
131+
132+
@property
133+
def frame_interval(self):
134+
"""Return the interval between frames of the stream."""
135+
return self._config[CONF_TIMELAPSE] / 1000
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""Consts used by rpi_camera."""
2+
3+
DOMAIN = "rpi_camera"
4+
5+
CONF_HORIZONTAL_FLIP = "horizontal_flip"
6+
CONF_IMAGE_HEIGHT = "image_height"
7+
CONF_IMAGE_QUALITY = "image_quality"
8+
CONF_IMAGE_ROTATION = "image_rotation"
9+
CONF_IMAGE_WIDTH = "image_width"
10+
CONF_OVERLAY_METADATA = "overlay_metadata"
11+
CONF_OVERLAY_TIMESTAMP = "overlay_timestamp"
12+
CONF_TIMELAPSE = "timelapse"
13+
CONF_VERTICAL_FLIP = "vertical_flip"
14+
15+
DEFAULT_HORIZONTAL_FLIP = 0
16+
DEFAULT_IMAGE_HEIGHT = 480
17+
DEFAULT_IMAGE_QUALITY = 7
18+
DEFAULT_IMAGE_ROTATION = 0
19+
DEFAULT_IMAGE_WIDTH = 640
20+
DEFAULT_NAME = "Raspberry Pi Camera"
21+
DEFAULT_TIMELAPSE = 1000
22+
DEFAULT_VERTICAL_FLIP = 0

0 commit comments

Comments
 (0)