HLK-FM22x Face Recognition Module

The hlk_fm22x component allows you to use your HLK-FM225 and HLK-FM223 face recognition modules with ESPHome.

HLK-FM225 Face Recognition Module
HLK-FM225 Face Recognition Module (datasheet, AliExpress). Image by AliExpress.
HLK-FM223 Face Recognition Module
HLK-FM223 Face Recognition Module (datasheet, AliExpress). Image by AliExpress.

Component/Hub

The module can be powered by the 5V output. As the communication with the reader is done using UART (default baud rate is 115200), you need to have an UART bus in your configuration with the rx_pin connected to the reader’s TX and the tx_pin connected to the reader’s RX.

# Example configuration entry
hlk_fm22x:
  on_face_scan_matched:
    ...
  on_face_scan_unmatched:
    ...
  on_face_scan_invalid:
    ...
  on_face_info:
    ...
  on_enrollment_done:
    ...
  on_enrollment_failed:
    ...

Configuration variables

The configuration is made up of three parts: The central component, optional individual sensors, the optional enrolling binary sensor, and the optional version text sensor.

Base Configuration:

  • uart_id (Optional, ID): Manually specify the ID of the UART hub.
  • id (Optional, ID): Manually specify the ID used for code generation.
  • on_face_scan_matched (Optional, Automation): An action to be performed when an enrolled face is scanned and recognized. See on_face_scan_matched.
  • on_face_scan_unmatched (Optional, Automation): An action to be performed when an unknown face is scanned. See on_face_scan_unmatched.
  • on_face_scan_invalid (Optional, Automation): An action to be performed when the face scan failed. See on_face_scan_invalid.
  • on_face_info (Optional, Automation): An action to be performed when face information is available. See on_face_info.
  • on_enrollment_done (Optional, Automation): An action to be performed when a face enrollment step is successful. See on_enrollment_done.
  • on_enrollment_failed (Optional, Automation): An action to be performed when a face enrollment step failed. See on_enrollment_failed.

Binary Sensor

Configuration variables:

Sensor

  • face_count: The number of enrolled faces stored on the module.

  • All options from Sensor.

  • last_face_id: The last matched enrolled face as set by on_face_scan_matched.

  • All options from Sensor.

  • status: The integer representation of the internal status register of the module.

  • All options from Sensor.

Text Sensor

on_face_scan_matched Trigger

With this configuration option you can write complex automations whenever a face scan is matched to an enrolled face. To use the variables, use a lambda template, the matched face id is available inside that lambda under the variable named face_id and the face name under the variable named name.

on_face_scan_matched:
  - text_sensor.template.publish:
      id: face_state
      state: !lambda 'return "Authorized face " + name + " (" + to_string(face_id) + ")";'
  # Pushing a tag_scanned event based on face_id
  - homeassistant.tag_scanned: !lambda |-
      switch (face_id) {
        case 0:
          return "person_a";
        case 1:
          return "person_b";
        ...
        default:
          return "person_unknown";
      }

on_face_scan_unmatched Trigger

With this configuration option you can write complex automations whenever an unknown face is scanned.

on_face_scan_unmatched:
  - text_sensor.template.publish:
      id: face_state
      state: "Unauthorized face"

on_face_scan_invalid Trigger

With this configuration option you can write complex automations whenever a scan fails, e.g. when no face is visible. This is different from on_face_scan_unmatched which is triggered when an unknown face is scanned. To use the variable, use a lambda template, the error number is available inside that lambda under the variable named error.

on_face_scan_invalid:
  - text_sensor.template.publish:
      id: face_state
      state: !lambda 'return "Invalid face. Error number: " + to_string(error);'

on_face_info Trigger

With this configuration option you can write complex automations whenever face information is available. The module sends face info during enrollment and scanning, and it’s mostly useful for debugging. To use the variables, use a lambda template, the status is available inside that lambda under the variable named status. A zero value means normal, and the datasheet contains various error status codes (e.g. 6 for the face being too far). There are additional values to determine the position (left, top, right, bottom) of the face in the frame as well as its rotation (yaw, pitch, roll).

on_face_info:
  - text_sensor.template.publish:
      id: face_info
      state: !lambda |-
        switch (status) {
          case 0:
            return "Normal";
          case 1:
            return "No face detected";
          case 2:
            return "Face too high";
          case 3:
            return "Face too low";
          ...
          default:
            return "Unknown status " + to_string(status);
        }

on_enrollment_done Trigger

With this configuration option you can write complex automations whenever an enrollment step for a face is successful. To use the variables, use a lambda template, the slot number enrolled into is available inside that lambda under the variable named face_id. Note that the value is only valid after the face has been enrolled in all directions (otherwise it will be -1). The direction value is a bitmask representing the directions that have been captured so far. A value of 0x1f means all directions have been captured and the face id should be valid.

on_enrollment_done:
  - text_sensor.template.publish:
      id: face_state
      state: !lambda 'return "Enrolled into slot " + to_string(face_id);'

on_enrollment_failed Trigger

With this configuration option you can write complex automations whenever a face failed to be enrolled. To use the variable, use a lambda template, the error number is available inside that lambda under the variable named error.

on_enrollment_failed:
  - text_sensor.template.publish:
      id: face_state
      state: !lambda 'return "Failed to enroll face. Error: " + to_string(error);'

hlk_fm22x.enroll Action

Starts the face enrollment process with a name and direction. To successfully enroll a face, you need to successfully and consecutively scan the face from all directions. A failure in one direction will require enrolling the face again from the start.

on_...:
  then:
    - hlk_fm22x.enroll:
        name: "My name"
        direction: 1
    # Update the template text sensor for visual feedback
    - text_sensor.template.publish:
        id: face_state
        state: "Look directly at the camera"

Configuration options:

  • name (Required, string, templatable): The name associated with the face. Up to 32 ASCII characters.
  • direction (Required, int, templatable): The direction to scan the face for. 1 for center, 2 for right, 4 for left, 8 for down, and 16 for up.

hlk_fm22x.scan Action

Scans and tries to match to an enrolled face. Triggers one of the on_face_scan triggers.

on_...:
  then:
    - hlk_fm22x.scan:

hlk_fm22x.delete Action

Removes the enrolled face from the slot number defined.

on_...:
  then:
    - hlk_fm22x.delete:
        face_id: 0
    # Shorthand
    - hlk_fm22x.delete: 0

Configuration options:

  • face_id (Required, int, templatable): The slot number of the enrolled face to delete.

hlk_fm22x.delete_all Action

Removes all enrolled faces.

on_...:
  then:
    - hlk_fm22x.delete_all:

hlk_fm22x.reset Action

Resets the module. Can be useful after a failed enrollment or scan if the module isn’t responding correctly. If this command fails it will mark the module as failed.

on_...:
  then:
    - hlk_fm22x.reset:

All actions

  • id (Optional, ID): Manually specify the ID of the HLK-FM22x reader if you have multiple components.

Test setup

With the following code you can quickly setup a node and use Home Assistant’s action in the developer tools. E.g. for calling hlk_fm22x.enroll select the action esphome.test_node_enroll and in action data enter

{ "name": "My name", "direction": 1 }

Sample code

uart:
  rx_pin: GPIOXX
  tx_pin: GPIOXX
  baud_rate: 115200

hlk_fm22x:
  on_face_scan_invalid:
    - homeassistant.event:
        event: esphome.test_node_face_scan_invalid
        data:
          error: !lambda 'return error;'
  on_face_scan_matched:
    - homeassistant.event:
        event: esphome.test_node_face_scan_matched
        data:
          face_id: !lambda 'return face_id;'
          name: !lambda 'return name;'
  on_face_scan_unmatched:
    - homeassistant.event:
        event: esphome.test_node_face_scan_unmatched
  on_face_info:
    - homeassistant.event:
        event: esphome.test_node_face_info
        data:
          status: !lambda 'return status;'
          left: !lambda 'return left;'
          top: !lambda 'return top;'
          right: !lambda 'return right;'
          bottom: !lambda 'return bottom;'
          yaw: !lambda 'return yaw;'
          pitch: !lambda 'return pitch;'
          roll: !lambda 'return roll;'
  on_enrollment_done:
    - homeassistant.event:
        event: esphome.test_node_enrollment_done
        data:
          face_id: !lambda 'return face_id;'
          direction: !lambda 'return direction;'
  on_enrollment_failed:
    - homeassistant.event:
        event: esphome.test_node_enrollment_failed
        data:
          error: !lambda 'return error;'

api:
  actions:
  - action: enroll
    variables:
      name: string
      direction: int
    then:
      - hlk_fm22x.enroll:
          name: !lambda 'return name;'
          direction: !lambda 'return direction;'
  - action: scan
    then:
      - hlk_fm22x.scan:
  - action: delete
    variables:
      face_id: int
    then:
      - hlk_fm22x.delete:
          face_id: !lambda 'return face_id;'
  - action: delete_all
    then:
      - hlk_fm22x.delete_all:
  - action: reset
    then:
      - hlk_fm22x.reset:

See Also