.. _webhooks: Webhooks =================================================== You can set up FindFace Multi to automatically send notifications about specific events, episodes, counter records, and area activations to a given URL. To do so, create and configure a webhook. In this case, when such an event, episode, counter record, or an area activation occurs, FindFace Multi will send an HTTP request to the URL configured for the webhook. You can use webhooks for various purposes, for instance, to notify a user about a specific event, invoke required behavior on a target website, and solve security tasks such as automated access control. .. rubric:: In this section: .. contents:: :local: Configure Webhook ------------------------------------------------ .. important:: You need Administrator privileges to create a webhook. .. note:: To use the webhooks, make sure that at least one of the following parameters is specified in ``/etc/findface-security/config.py``: ``SERVICE_EXTERNAL_ADDRESS`` or ``EXTERNAL_ADDRESS``. To create and configure a webhook, do the following: #. Navigate to the :guilabel:`Preferences` tab. Click :guilabel:`Webhooks`. #. Click :guilabel:`+`. |create_webhook_en| .. |create_webhook_en| image:: /_static/create_webhook_en.png :scale: 60% .. |create_webhook_ru| image:: /_static/create_webhook_ru.png :scale: 60% #. Specify the webhook title. |webhook_en| .. |webhook_en| image:: /_static/webhook_en.png :scale: 80% .. |webhook_ru| image:: /_static/webhook_ru.png :scale: 80% #. Specify URL to automatically send notifications to. #. You can send notifications in batches. Specify the maximum number of notifications in a webhook batch. The actual number may be less. #. Specify the maximum number of attempts to send a notification. The interval between attempts increases exponentially with a maximum of 100 seconds. .. important:: To receive all messages since the connection loss, should it happen, set ``0``. Set ``1`` to omit old messages. #. FindFace Multi will be automatically sending notifications on events, episodes, counter records, and area activations that match given filters. The following filters are available: .. rubric:: Recognition events (face, body, car): Sections: ``face_events``, ``body_events``, ``car_events``. * ``allowed_bs_types``: :ref:`object tracking mode `, possible values: ``overall``, ``realtime``. * ``camera_group_in``: camera group id, number. * ``camera_in``: camera id, number. * ``matched_lists_in``: watch list id, number. * ``matched_card_in``: matched card id, number. * ``matched``: the ``matched`` status. Set ``true`` or ``false`` if only matched or unmatched events must trigger sending a notification, boolean. * ``confidence_gte``: minimum confidence, number. .. rubric:: Episodes with people: Section: ``human_episodes``. * ``allowed_types``: episode status, possible values: an episode opening (``episode_open``), adding a new event into an episode (``episode_event``), an episode closing (``episode_close``). * ``camera_group_in``: camera group id, number. * ``camera_in``: camera id, number. * ``matched_lists_in``: watch list id, number. * ``face_matched``: the ``matched`` status of face events in an episode, boolean. Set ``true`` if only matched faces or ``false`` if only unmatched faces must trigger sending a notification. * ``body_matched``: the ``matched`` status of body events in an episode, boolean. Set ``true`` if only matched bodies or ``false`` if only unmatched bodies must trigger sending a notification. * ``events_count_gte``: minimum number of events in an episode, number. * ``events_count_lte``: maximum number of events in an episode, number. .. rubric:: Episodes with cars: Section: ``car_episodes``. * ``allowed_types``: episode status, possible values: an episode opening (``episode_open``), adding a new event into an episode (``episode_event``), an episode closing (``episode_close``). * ``camera_group_in``: camera group id, number. * ``camera_in``: camera id, number. * ``matched_lists_in``: watch list id, number. * ``car_matched``: the ``matched`` status of car events in an episode, boolean. Set ``true`` if only matched cars or ``false`` if only unmatched cars must trigger sending a notification. * ``events_count_gte``: minimum number of events in an episode, number. * ``events_count_lte``: maximum number of events in an episode, number. .. rubric:: Counter records: Section: ``counters``. * ``counter_in``: counter id, number. * ``camera_group_in``: camera group id, number. * ``camera_in``: camera id, number. * ``faces_gte``: minimum number of faces in a counter record, number. * ``faces_lte``: maximum number of faces in a counter record, number. * ``silhouettes_gte``: minimum number of bodies in a counter record, number. * ``silhouettes_lte``: maximum number of bodies in a counter record, number. * ``cars_gte``: minimum number of cars in a counter record, number. * ``cars_lte``: maximum number of cars in a counter record, number. * ``proximity_min_lte``: send a notification if the minimum detected distance in meters is less than this value, number. * ``proximity_min_gte``: send a notification if the minimum detected distance in meters is greater than this value, number. * ``proximity_avg_lte``: send a notification if the average detected distance in meters is less than this value, number. * ``proximity_avg_gte``: send a notification if the average detected distance in meters is greater than this value, number. * ``proximity_max_lte``: send a notification if the maximum detected distance in meters is less than this value, number. * ``proximity_max_gte``: send a notification if the maximum detected distance in meters is greater than this value, number. .. rubric:: Area activations: Section: ``areas``. * ``area_in``: area id, number. * ``camera_group_in``: camera group id, number. * ``camera_in``: camera id, number. .. code:: { "face_events": { "allowed_bs_types": [ "overall", "realtime" ], "camera_group_in": [], "camera_in": [], "matched_lists_in": [], "matched_card_in": [], "matched": true, "confidence_gte": 0.75 }, "body_events": { "allowed_bs_types": [ "overall", "realtime" ], "camera_group_in": [], "camera_in": [], "matched_lists_in": [], "matched_card_in": [], "matched": true, "confidence_gte": 0.75 }, "car_events": { "allowed_bs_types": [ "overall", "realtime" ], "camera_group_in": [], "camera_in": [], "matched_lists_in": [], "matched_card_in": [], "matched": true, "confidence_gte": 0.75 }, "human_episodes": { "allowed_types": [ "episode_open", "episode_event", "episode_close" ], "camera_group_in": [], "camera_in": [], "matched_lists_in": [], "face_matched": true, "body_matched": true, "events_count_gte": 0, "events_count_lte": 999 }, "car_episodes": { "allowed_types": [ "episode_open", "episode_event", "episode_close" ], "camera_group_in": [], "camera_in": [], "matched_lists_in": [], "car_matched": true, "events_count_gte": 0, "events_count_lte": 999 }, "counters": { "counter_in": [], "camera_group_in": [], "camera_in": [], "faces_gte": 1, "faces_lte": 100, "silhouettes_gte": 1, "silhouettes_lte": 100, "cars_gte": 1, "cars_lte": 100, "proximity_min_lte": 100, "proximity_min_gte": 0, "proximity_avg_lte": 100, "proximity_avg_gte": 0, "proximity_max_lte": 100, "proximity_max_gte": 0 }, "areas": { "area_in": [], "camera_group_in": [], "camera_in": [] } } .. important:: Use only filters which match your search needs. To turn off a filter, remove it from a webhook. Do not leave a filter empty (``[]``) as in this case, the result of filtration will be empty as well. .. note:: To get all notifications, pass only curly braces without any enclosed filters: .. code:: {} .. tip:: Example #1. Get notifications about all car events: .. code:: { "car_events": {} } Example #2. Get notifications of the opening of matched episodes that feature human faces and bodies: .. code:: { "human_episodes": { "allowed_types": ["episode_open"], "face_matched": true, "body_matched": true }} .. note:: You can specify several values for filters with square braces. In this case, the webhook will be triggered once one of the values from this filter has been matched. In the example below, you will get a body event from the camera group ``1`` or ``3`` if a matched card is ``12`` or ``25``. .. code:: { "body_events": { "camera_group_in": [1, 3], "matched_card_in": [12, 25], }, } #. Check :guilabel:`Active`. #. Click :guilabel:`Save`. Webhook in Action ---------------------- Try out a webhook by capturing event notifications with a simple web server in Python: .. code:: from pprint import pprint from aiohttp import web async def handle(request): pprint(await request.json()) return web.Response(status=200) app = web.Application() # for aiohttp v 3.x # app.add_routes([web.post('/', handle)]) # for aiohttp v 2.x app.router.add_post('/', handle) web.run_app(app, port=8888) .. important:: A webhook catcher that you use must return an ``HTTP 200`` response after receiving the webhook request from FindFace Multi, like in the example above. If no filters are configured for a webhook, this web server will be getting notifications about all events, episodes, counter records, and area notifications that occur in the system. The notifications have the following formats: * :download:`Face event <_scripts/face_event.txt>` * :download:`Body event <_scripts/body_event.txt>` * :download:`Car event <_scripts/car_event.txt>` * :download:`Opening of a person episode <_scripts/human_episodes_open.txt>` * :download:`Opening of a car episode <_scripts/car_episodes_open.txt>` * :download:`Closing of a person episode <_scripts/human_episodes_close.txt>` * :download:`Closing of a car episode <_scripts/car_episodes_close.txt>` * :download:`Counter record <_scripts/counter_record.txt>` * :download:`Area activation <_scripts/areas.txt>` To view a webhook pulling status, execute: .. code:: sudo journalctl -u findface-security.service | grep 'Webhook' .. rubric:: Success: .. code:: May 30 14:13:43 ffsecurity[12441]: INFO [Webhook(id=6) worker(type=face_events] Sent batch(len-1, type-"face_events"): ['4355024961160384430'] May 30 14:13:43 ffsecurity[12441]: INFO [SC:OQSrsPV9] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.003450 May 30 14:13:43 ffsecurity[12441]: INFO [Webhook(id=6) worker(type=face_events] Sent batch(len-1, type-"face_events"): ['4355024961658847580'] May 30 14:13:44 ffsecurity[12441]: INFO [SC:JtRz2Vuo] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001263 May 30 14:13:44 ffsecurity[12441]: INFO [Webhook(id=6) worker(type=face_events] Sent batch(len-1, type-"face_events"): ['4355024962087522421'] May 30 14:13:44 ffsecurity[12441]: INFO [SC:9AnzRJwU] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001691 May 30 14:13:44 ffsecurity[12441]: INFO [Webhook(id=6) worker(type=face_events] Sent batch(len-1, type-"face_events"): ['4355024962355957878'] .. rubric:: Failure: .. code:: May 30 14:18:49 ffsecurity[12441]: INFO [SC:sp34rVQR] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001376 May 30 14:18:49 ffsecurity[12441]: WARNING [Webhook(id=6) worker(type=face_events] Error sending webhook: Cannot connect to host 127.0.0.1:8888 ssl:None [Connection refused]. Attempt 1 out of 10. Next attempt in 0.270 seconds. May 30 14:18:50 ffsecurity[12441]: WARNING [Webhook(id=6) worker(type=face_events] Error sending webhook: Cannot connect to host 127.0.0.1:8888 ssl:None [Connection refused]. Attempt 2 out of 10. Next attempt in 0.729 seconds. May 30 14:18:50 ffsecurity[12441]: INFO [SC:zUhLHNxN] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001368 May 30 14:18:50 ffsecurity[12441]: INFO [SC:1Q66tcUS] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001386 May 30 14:18:50 ffsecurity[12441]: WARNING [Webhook(id=6) worker(type=face_events] Error sending webhook: Cannot connect to host 127.0.0.1:8888 ssl:None [Connection refused]. Attempt 3 out of 10. Next attempt in 1.968 seconds. May 30 14:18:52 ffsecurity[12441]: WARNING [Webhook(id=6) worker(type=face_events] Error sending webhook: Cannot connect to host 127.0.0.1:8888 ssl:None [Connection refused]. Attempt 4 out of 10. Next attempt in 5.314 seconds. May 30 14:18:55 ffsecurity[12441]: INFO [SC:5kl6zGrF] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001542 May 30 14:18:58 ffsecurity[12441]: WARNING [Webhook(id=6) worker(type=face_events] Error sending webhook: Cannot connect to host 127.0.0.1:8888 ssl:None [Connection refused]. Attempt 5 out of 10. Next attempt in 14.349 seconds. Verbose Webhooks ------------------------- By default, webhook notifications contain only ids of such entities as cards, watch lists, cameras, and camera groups. It is possible to get the full content of the mentioned entities by switching webhooks to the verbose mode. To do so, open the ``/etc/findface-security/config.py`` configuration file and set ``'VERBOSE_WEBHOOKS': True``: .. code:: sudo vi /etc/findface-security/config.py ... FFSECURITY = { ... # send serialized cards, card-lists, camera and camera groups in webhooks 'VERBOSE_WEBHOOKS': True, ... } ... Be sure to restart the ``findface-security`` service after making changes. .. code:: sudo systemctl restart findface-security.service In the verbose mode, webhook notifications have the following formats: * :download:`Face event (verbose) <_scripts/face_event_VERBOSE.txt>` * :download:`Body event (verbose) <_scripts/body_event_VERBOSE.txt>` * :download:`Car event (verbose) <_scripts/car_event_VERBOSE.txt>` * :download:`Opening of a person episode (verbose) <_scripts/human_episodes_open_VERBOSE.txt>` * :download:`Opening of a car episode (verbose) <_scripts/car_episodes_open_VERBOSE.txt>` * :download:`Closing of a person episode (verbose) <_scripts/human_episodes_close_VERBOSE.txt>` * :download:`Closing of a car episode (verbose) <_scripts/car_episodes_close_VERBOSE.txt>` * :download:`Counter record (verbose) <_scripts/counter_VERBOSE.txt>` * :download:`Area activation (verbose) <_scripts/areas_VERBOSE.txt>`