Remove old openalpr component (#5406)

* Remove old openalpr component

* update region support
pull/5459/head
Pascal Vizeli 2017-01-20 07:21:25 +01:00 committed by Paulus Schoutsen
parent d1ec422eab
commit fe6a8f3367
5 changed files with 13 additions and 483 deletions

View File

@ -266,7 +266,6 @@ omit =
homeassistant/components/notify/twitter.py
homeassistant/components/notify/xmpp.py
homeassistant/components/nuimo_controller.py
homeassistant/components/openalpr.py
homeassistant/components/remote/harmony.py
homeassistant/components/scene/hunterdouglas_powerview.py
homeassistant/components/sensor/amcrest.py

View File

@ -26,14 +26,18 @@ _LOGGER = logging.getLogger(__name__)
OPENALPR_API_URL = "https://api.openalpr.com/v1/recognize"
OPENALPR_REGIONS = [
'us',
'eu',
'au',
'auwide',
'br',
'eu',
'fr',
'gb',
'kr',
'kr2',
'mx',
'sg',
'us',
'vn2'
]
CONF_REGION = 'region'

View File

@ -31,16 +31,21 @@ ATTR_PLATES = 'plates'
ATTR_VEHICLES = 'vehicles'
OPENALPR_REGIONS = [
'us',
'eu',
'au',
'auwide',
'br',
'eu',
'fr',
'gb',
'kr',
'kr2',
'mx',
'sg',
'us',
'vn2'
]
CONF_REGION = 'region'
CONF_ALPR_BIN = 'alp_bin'

View File

@ -1,472 +0,0 @@
"""
Component that will help set the openalpr for video streams.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/openalpr/
"""
from base64 import b64encode
import logging
import os
from time import time
import requests
import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
CONF_API_KEY, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, ATTR_ENTITY_ID,
EVENT_HOMEASSISTANT_STOP, STATE_UNKNOWN)
from homeassistant.components.ffmpeg import (
get_binary, run_test, CONF_INPUT, CONF_EXTRA_ARGUMENTS)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
DOMAIN = 'openalpr'
DEPENDENCIES = ['ffmpeg']
REQUIREMENTS = [
'https://github.com/pvizeli/cloudapi/releases/download/1.0.2/'
'python-1.0.2.zip#openalpr_api==1.0.2',
'ha-alpr==0.3']
_LOGGER = logging.getLogger(__name__)
SERVICE_SCAN = 'scan'
SERVICE_RESTART = 'restart'
EVENT_FOUND = 'openalpr.found'
ATTR_PLATE = 'plate'
ENGINE_LOCAL = 'local'
ENGINE_CLOUD = 'cloud'
RENDER_IMAGE = 'image'
RENDER_FFMPEG = 'ffmpeg'
OPENALPR_REGIONS = [
'us',
'eu',
'au',
'auwide',
'gb',
'kr',
'mx',
'sg',
]
CONF_RENDER = 'render'
CONF_ENGINE = 'engine'
CONF_REGION = 'region'
CONF_INTERVAL = 'interval'
CONF_ENTITIES = 'entities'
CONF_CONFIDENCE = 'confidence'
CONF_ALPR_BINARY = 'alpr_binary'
DEFAULT_NAME = 'OpenAlpr'
DEFAULT_ENGINE = ENGINE_LOCAL
DEFAULT_RENDER = RENDER_FFMPEG
DEFAULT_BINARY = 'alpr'
DEFAULT_INTERVAL = 10
DEFAULT_CONFIDENCE = 80.0
DEVICE_SCHEMA = vol.Schema({
vol.Required(CONF_INPUT): cv.string,
vol.Optional(CONF_INTERVAL, default=DEFAULT_INTERVAL): cv.positive_int,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_RENDER, default=DEFAULT_RENDER):
vol.In([RENDER_IMAGE, RENDER_FFMPEG]),
vol.Optional(CONF_EXTRA_ARGUMENTS): cv.string,
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_ENGINE): vol.In([ENGINE_LOCAL, ENGINE_CLOUD]),
vol.Required(CONF_REGION): vol.In(OPENALPR_REGIONS),
vol.Optional(CONF_CONFIDENCE, default=DEFAULT_CONFIDENCE):
vol.Coerce(float),
vol.Optional(CONF_API_KEY): cv.string,
vol.Optional(CONF_ALPR_BINARY, default=DEFAULT_BINARY): cv.string,
vol.Required(CONF_ENTITIES):
vol.All(cv.ensure_list, [DEVICE_SCHEMA]),
})
}, extra=vol.ALLOW_EXTRA)
SERVICE_RESTART_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
SERVICE_SCAN_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
def scan(hass, entity_id=None):
"""Scan a image immediately."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_SCAN, data)
def restart(hass, entity_id=None):
"""Restart a ffmpeg process."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_RESTART, data)
def setup(hass, config):
"""Setup the OpenAlpr component."""
engine = config[DOMAIN].get(CONF_ENGINE)
region = config[DOMAIN].get(CONF_REGION)
confidence = config[DOMAIN].get(CONF_CONFIDENCE)
api_key = config[DOMAIN].get(CONF_API_KEY)
binary = config[DOMAIN].get(CONF_ALPR_BINARY)
use_render_fffmpeg = False
_LOGGER.warning("This platform is replaced by 'image_processing' and will "
"be removed in a future version!")
component = EntityComponent(_LOGGER, DOMAIN, hass)
openalpr_device = []
for device in config[DOMAIN].get(CONF_ENTITIES):
input_source = device.get(CONF_INPUT)
render = device.get(CONF_RENDER)
##
# create api
if engine == ENGINE_LOCAL:
alpr_api = OpenalprApiLocal(
confidence=confidence,
region=region,
binary=binary,
)
else:
alpr_api = OpenalprApiCloud(
confidence=confidence,
region=region,
api_key=api_key,
)
##
# Create Alpr device / render engine
if render == RENDER_FFMPEG:
use_render_fffmpeg = True
if not run_test(hass, input_source):
_LOGGER.error("'%s' is not valid ffmpeg input", input_source)
continue
alpr_dev = OpenalprDeviceFFmpeg(
name=device.get(CONF_NAME),
interval=device.get(CONF_INTERVAL),
api=alpr_api,
input_source=input_source,
extra_arguments=device.get(CONF_EXTRA_ARGUMENTS),
)
else:
alpr_dev = OpenalprDeviceImage(
name=device.get(CONF_NAME),
interval=device.get(CONF_INTERVAL),
api=alpr_api,
input_source=input_source,
username=device.get(CONF_USERNAME),
password=device.get(CONF_PASSWORD),
)
# register shutdown event
openalpr_device.append(alpr_dev)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, alpr_dev.shutdown)
component.add_entities(openalpr_device)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
def _handle_service_scan(service):
"""Handle service for immediately scan."""
device_list = component.extract_from_service(service)
for device in device_list:
device.scan()
hass.services.register(DOMAIN, SERVICE_SCAN,
_handle_service_scan,
descriptions[DOMAIN][SERVICE_SCAN],
schema=SERVICE_SCAN_SCHEMA)
# Add restart service only if a device use ffmpeg as render
if not use_render_fffmpeg:
return True
def _handle_service_restart(service):
"""Handle service for restart ffmpeg process."""
device_list = component.extract_from_service(service)
for device in device_list:
device.restart()
hass.services.register(DOMAIN, SERVICE_RESTART,
_handle_service_restart,
descriptions[DOMAIN][SERVICE_RESTART],
schema=SERVICE_RESTART_SCHEMA)
return True
class OpenalprDevice(Entity):
"""Represent a openalpr device object for processing stream/images."""
def __init__(self, name, interval, api):
"""Init image processing."""
self._name = name
self._interval = interval
self._api = api
self._last = {}
@property
def state(self):
"""Return the state of the entity."""
confidence = 0
plate = STATE_UNKNOWN
# search high plate
for i_pl, i_co in self._last.items():
if i_co > confidence:
confidence = i_co
plate = i_pl
return plate
def shutdown(self, event):
"""Close stream."""
if hasattr(self._api, "shutdown"):
self._api.shutdown(event)
def restart(self):
"""Restart stream."""
raise NotImplementedError()
def _process_image(self, image):
"""Callback for processing image."""
self._api.process_image(image, self._process_event)
def _process_event(self, plates):
"""Send event with new plates."""
state_change = False
plates_set = set(plates)
last_set = set(self._last)
new_plates = plates_set - last_set
# send events
for i_plate in new_plates:
self.hass.bus.fire(EVENT_FOUND, {
ATTR_PLATE: i_plate,
ATTR_ENTITY_ID: self.entity_id
})
# update entity store
if last_set <= plates_set:
state_change = True
self._last = plates
# update HA state
if state_change:
self.update_ha_state()
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
return {'plates': self._last}
def scan(self):
"""Immediately scan a image."""
raise NotImplementedError()
@property
def name(self):
"""Return the name of the entity."""
return self._name
class OpenalprDeviceFFmpeg(OpenalprDevice):
"""Represent a openalpr device object for processing stream/images."""
def __init__(self, name, interval, api, input_source,
extra_arguments=None):
"""Init image processing."""
from haffmpeg import ImageStream, ImageSingle
super().__init__(name, interval, api)
self._input_source = input_source
self._extra_arguments = extra_arguments
if self._interval > 0:
self._ffmpeg = ImageStream(get_binary(), self._process_image)
else:
self._ffmpeg = ImageSingle(get_binary())
self._start_ffmpeg()
def shutdown(self, event):
"""Close ffmpeg stream."""
if self._interval > 0:
self._ffmpeg.close()
def restart(self):
"""Restart ffmpeg stream."""
if self._interval > 0:
self._ffmpeg.close()
self._start_ffmpeg()
def scan(self):
"""Immediately scan a image."""
from haffmpeg import IMAGE_PNG
# process single image
if self._interval == 0:
image = self._ffmpeg.get_image(
self._input_source,
output_format=IMAGE_PNG,
extra_cmd=self._extra_arguments
)
return self._process_image(image)
# stream
self._ffmpeg.push_image()
def _start_ffmpeg(self):
"""Start a ffmpeg image stream."""
from haffmpeg import IMAGE_PNG
if self._interval == 0:
return
self._ffmpeg.open_stream(
input_source=self._input_source,
interval=self._interval,
output_format=IMAGE_PNG,
extra_cmd=self._extra_arguments,
)
@property
def should_poll(self):
"""Return True if render is be 'image' or False if 'ffmpeg'."""
return False
@property
def available(self):
"""Return True if entity is available."""
return self._interval == 0 or self._ffmpeg.is_running
class OpenalprDeviceImage(OpenalprDevice):
"""Represent a openalpr device object for processing stream/images."""
def __init__(self, name, interval, api, input_source,
username=None, password=None):
"""Init image processing."""
super().__init__(name, interval, api)
self._next = time()
self._username = username
self._password = password
self._url = input_source
def restart(self):
"""Fake restart with scan a picture."""
self.scan()
def scan(self):
"""Immediately scan a image."""
# send request
if self._username is not None and self._password is not None:
req = requests.get(
self._url, auth=(self._username, self._password), timeout=15)
else:
req = requests.get(self._url, timeout=15)
# process image
image = req.content
self._process_image(image)
@property
def should_poll(self):
"""Return True if render is be 'image' or False if 'ffmpeg'."""
return self._interval > 0
def update(self):
"""Retrieve latest state."""
if self._next > time():
return
self.scan()
self._next = time() + self._interval
class OpenalprApi(object):
"""OpenAlpr api class."""
def __init__(self, region, confidence):
"""Init basic api processing."""
self._region = region
self._confidence = confidence
def process_image(self, image, event_callback):
"""Callback for processing image."""
raise NotImplementedError()
class OpenalprApiCloud(OpenalprApi):
"""Use the cloud openalpr api to parse licences plate."""
def __init__(self, region, confidence, api_key):
"""Init cloud api processing."""
import openalpr_api
super().__init__(region=region, confidence=confidence)
self._api = openalpr_api.DefaultApi()
self._api_key = api_key
def process_image(self, image, event_callback):
"""Callback for processing image."""
result = self._api.recognize_post(
self._api_key,
'plate',
image="",
image_bytes=str(b64encode(image), 'utf-8'),
country=self._region
)
# process result
f_plates = {}
# pylint: disable=no-member
for object_plate in result.plate.results:
plate = object_plate.plate
confidence = object_plate.confidence
if confidence >= self._confidence:
f_plates[plate] = confidence
event_callback(f_plates)
class OpenalprApiLocal(OpenalprApi):
"""Use local openalpr library to parse licences plate."""
def __init__(self, region, confidence, binary):
"""Init local api processing."""
# pylint: disable=import-error
from haalpr import HAAlpr
super().__init__(region=region, confidence=confidence)
self._api = HAAlpr(binary=binary, country=region)
def process_image(self, image, event_callback):
"""Callback for processing image."""
result = self._api.recognize_byte(image)
# process result
f_plates = {}
for found in result:
for plate, confidence in found.items():
if confidence >= self._confidence:
f_plates[plate] = confidence
event_callback(f_plates)

View File

@ -169,9 +169,6 @@ googlemaps==2.4.4
# homeassistant.components.sensor.gpsd
gps3==0.33.3
# homeassistant.components.openalpr
ha-alpr==0.3
# homeassistant.components.ffmpeg
ha-ffmpeg==0.15
@ -244,9 +241,6 @@ https://github.com/nkgilley/python-ecobee-api/archive/4856a704670c53afe1882178a8
# homeassistant.components.notify.joaoapps_join
https://github.com/nkgilley/python-join-api/archive/3e1e849f1af0b4080f551b62270c6d244d5fbcbd.zip#python-join-api==0.0.1
# homeassistant.components.openalpr
https://github.com/pvizeli/cloudapi/releases/download/1.0.2/python-1.0.2.zip#openalpr_api==1.0.2
# homeassistant.components.switch.edimax
https://github.com/rkabadi/pyedimax/archive/365301ce3ff26129a7910c501ead09ea625f3700.zip#pyedimax==0.1