From 9ab2ac766e74f34f107cff94c7aba6c397cc9888 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 26 Aug 2016 14:48:17 +0200 Subject: [PATCH] add motion sensor / rewrite ffmpeg binary sensor (#2969) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐬 * use const flags --- .../components/binary_sensor/ffmpeg.py | 174 ++++++++++++++---- .../components/binary_sensor/services.yaml | 9 + homeassistant/components/camera/ffmpeg.py | 2 +- requirements_all.txt | 2 +- 4 files changed, 151 insertions(+), 36 deletions(-) create mode 100644 homeassistant/components/binary_sensor/services.yaml diff --git a/homeassistant/components/binary_sensor/ffmpeg.py b/homeassistant/components/binary_sensor/ffmpeg.py index 4a59d2693e0..be4d595dcb9 100644 --- a/homeassistant/components/binary_sensor/ffmpeg.py +++ b/homeassistant/components/binary_sensor/ffmpeg.py @@ -5,18 +5,27 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/binary_sensor.ffmpeg/ """ import logging +from os import path import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.binary_sensor import (BinarySensorDevice, - PLATFORM_SCHEMA) -from homeassistant.const import EVENT_HOMEASSISTANT_STOP, CONF_NAME + PLATFORM_SCHEMA, DOMAIN) +from homeassistant.config import load_yaml_config_file +from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, CONF_NAME, + ATTR_ENTITY_ID) -REQUIREMENTS = ["ha-ffmpeg==0.7"] +REQUIREMENTS = ["ha-ffmpeg==0.8"] + +SERVICE_RESTART = 'ffmpeg_restart' + +FFMPEG_SENSOR_NOISE = 'noise' +FFMPEG_SENSOR_MOTION = 'motion' MAP_FFMPEG_BIN = [ - 'noise' + FFMPEG_SENSOR_NOISE, + FFMPEG_SENSOR_MOTION ] CONF_TOOL = 'tool' @@ -27,44 +36,140 @@ CONF_OUTPUT = 'output' CONF_PEAK = 'peak' CONF_DURATION = 'duration' CONF_RESET = 'reset' +CONF_CHANGES = 'changes' +CONF_REPEAT = 'repeat' +CONF_REPEAT_TIME = 'repeat_time' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_TOOL): vol.In(MAP_FFMPEG_BIN), vol.Required(CONF_INPUT): cv.string, - vol.Optional(CONF_NAME, default="FFmpeg"): cv.string, vol.Optional(CONF_FFMPEG_BIN, default="ffmpeg"): cv.string, + vol.Optional(CONF_NAME, default="FFmpeg"): cv.string, vol.Optional(CONF_EXTRA_ARGUMENTS): cv.string, vol.Optional(CONF_OUTPUT): cv.string, vol.Optional(CONF_PEAK, default=-30): vol.Coerce(int), vol.Optional(CONF_DURATION, default=1): vol.All(vol.Coerce(int), vol.Range(min=1)), - vol.Optional(CONF_RESET, default=2): + vol.Optional(CONF_RESET, default=10): vol.All(vol.Coerce(int), vol.Range(min=1)), + vol.Optional(CONF_CHANGES, default=10): + vol.All(vol.Coerce(float), vol.Range(min=0, max=99)), + vol.Optional(CONF_REPEAT, default=0): + vol.All(vol.Coerce(int), vol.Range(min=0)), + vol.Optional(CONF_REPEAT_TIME, default=0): + vol.All(vol.Coerce(int), vol.Range(min=0)), }) +SERVICE_RESTART_SCHEMA = vol.Schema({ + vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, +}) + + +# list of all ffmpeg sensors +DEVICES = [] + _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_entities, discovery_info=None): """Create the binary sensor.""" - if config.get(CONF_TOOL) == "noise": - entity = FFmpegNoise(config) + from haffmpeg import SensorNoise, SensorMotion + + if config.get(CONF_TOOL) == FFMPEG_SENSOR_NOISE: + entity = FFmpegNoise(SensorNoise, config) + else: + entity = FFmpegMotion(SensorMotion, config) hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, entity.shutdown_ffmpeg) + + # add to system add_entities([entity]) + DEVICES.append(entity) + + # exists service? + if hass.services.has_service(DOMAIN, SERVICE_RESTART): + return True + + descriptions = load_yaml_config_file( + path.join(path.dirname(__file__), 'services.yaml')) + + # register service + def _service_handle_restart(service): + """Handle service binary_sensor.ffmpeg_restart.""" + entity_ids = service.data.get('entity_id') + + if entity_ids: + _devices = [device for device in DEVICES + if device.entity_id in entity_ids] + else: + _devices = DEVICES + + for device in _devices: + device.reset_ffmpeg() + + hass.services.register(DOMAIN, SERVICE_RESTART, + _service_handle_restart, + descriptions.get(SERVICE_RESTART), + schema=SERVICE_RESTART_SCHEMA) + return True -class FFmpegNoise(BinarySensorDevice): +class FFmpegBinarySensor(BinarySensorDevice): """A binary sensor which use ffmpeg for noise detection.""" - def __init__(self, config): + def __init__(self, ffobj, config): """Constructor for binary sensor noise detection.""" - from haffmpeg import SensorNoise - self._state = False + self._config = config self._name = config.get(CONF_NAME) - self._ffmpeg = SensorNoise(config.get(CONF_FFMPEG_BIN), self._callback) + self._ffmpeg = ffobj(config.get(CONF_FFMPEG_BIN), self._callback) + self._start_ffmpeg(config) + + def _callback(self, state): + """HA-FFmpeg callback for noise detection.""" + self._state = state + self.update_ha_state() + + def _start_ffmpeg(self, config): + """Start a FFmpeg instance.""" + raise NotImplementedError + + def shutdown_ffmpeg(self, event): + """For STOP event to shutdown ffmpeg.""" + self._ffmpeg.close() + + def reset_ffmpeg(self): + """Restart ffmpeg with new config.""" + self._ffmpeg.close() + self._start_ffmpeg(self._config) + + @property + def is_on(self): + """True if the binary sensor is on.""" + return self._state + + @property + def should_poll(self): + """Return True if entity has to be polled for state.""" + return False + + @property + def name(self): + """Return the name of the entity.""" + return self._name + + @property + def available(self): + """Return True if entity is available.""" + return self._ffmpeg.is_running + + +class FFmpegNoise(FFmpegBinarySensor): + """A binary sensor which use ffmpeg for noise detection.""" + + def _start_ffmpeg(self, config): + """Start a FFmpeg instance.""" # init config self._ffmpeg.set_options( time_duration=config.get(CONF_DURATION), @@ -79,31 +184,32 @@ class FFmpegNoise(BinarySensorDevice): extra_cmd=config.get(CONF_EXTRA_ARGUMENTS), ) - def _callback(self, state): - """HA-FFmpeg callback for noise detection.""" - self._state = state - self.update_ha_state() - - def shutdown_ffmpeg(self, event): - """For STOP event to shutdown ffmpeg.""" - self._ffmpeg.close() - - @property - def is_on(self): - """True if the binary sensor is on.""" - return self._state - @property def sensor_class(self): """Return the class of this sensor, from SENSOR_CLASSES.""" return "sound" - @property - def should_poll(self): - """Return True if entity has to be polled for state.""" - return False + +class FFmpegMotion(FFmpegBinarySensor): + """A binary sensor which use ffmpeg for noise detection.""" + + def _start_ffmpeg(self, config): + """Start a FFmpeg instance.""" + # init config + self._ffmpeg.set_options( + time_reset=config.get(CONF_RESET), + time_repeat=config.get(CONF_REPEAT_TIME), + repeat=config.get(CONF_REPEAT), + changes=config.get(CONF_CHANGES), + ) + + # run + self._ffmpeg.open_sensor( + input_source=config.get(CONF_INPUT), + extra_cmd=config.get(CONF_EXTRA_ARGUMENTS), + ) @property - def name(self): - """Return the name of the entity.""" - return self._name + def sensor_class(self): + """Return the class of this sensor, from SENSOR_CLASSES.""" + return "motion" diff --git a/homeassistant/components/binary_sensor/services.yaml b/homeassistant/components/binary_sensor/services.yaml new file mode 100644 index 00000000000..9be9915e268 --- /dev/null +++ b/homeassistant/components/binary_sensor/services.yaml @@ -0,0 +1,9 @@ +# Describes the format for available binary_sensor services + +ffmpeg_restart: + description: Send a restart command to a ffmpeg based sensor (party mode). + + fields: + entity_id: + description: Name(s) of entites that will restart. Platform dependent. + example: 'binary_sensor.ffmpeg_noise' diff --git a/homeassistant/components/camera/ffmpeg.py b/homeassistant/components/camera/ffmpeg.py index d5055e942e7..1eaabc45195 100644 --- a/homeassistant/components/camera/ffmpeg.py +++ b/homeassistant/components/camera/ffmpeg.py @@ -14,7 +14,7 @@ from homeassistant.components.camera.mjpeg import extract_image_from_mjpeg import homeassistant.helpers.config_validation as cv from homeassistant.const import CONF_NAME, CONF_PLATFORM -REQUIREMENTS = ["ha-ffmpeg==0.7"] +REQUIREMENTS = ["ha-ffmpeg==0.8"] CONF_INPUT = 'input' CONF_FFMPEG_BIN = 'ffmpeg_bin' diff --git a/requirements_all.txt b/requirements_all.txt index fd16391f6e6..8b95fe4541c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -104,7 +104,7 @@ gps3==0.33.2 # homeassistant.components.binary_sensor.ffmpeg # homeassistant.components.camera.ffmpeg -ha-ffmpeg==0.7 +ha-ffmpeg==0.8 # homeassistant.components.mqtt.server hbmqtt==0.7.1