Unifi G4 doorbell automation

I am trying to use the finger print sensor on my G4 POE to open the garage door so that I can put my bicycle away without needing to get my phone out. This automation works OK, except that the door opens itself randomly

alias: 16b. G4 Doorbell Fingerprint operate Garage Door Left
description: ""
triggers:
  - trigger: state
    entity_id:
      - event.g4_doorbell_pro_poe_fingerprint
conditions:
  - condition: template
    value_template: >-
      {{state_attr('event.g4_doorbell_pro_poe_fingerprint','full_name')=="Phil
      Gilbert"}}
  - condition: time
    after: "08:00:00"
  - condition: time
    before: "22:00:00"
actions:
  - action: script.turn_on
    metadata: {}
    data: {}
    target:
      entity_id: script.garage_door_left_operate
mode: queued
trace:
  stored_traces: 20

In the HA documentation there is a worked example on how to use Finger Print in Automations UniFi Protect - Home Assistant with a complicated conditional template

condition:
  - condition: template
    value_template: >
      {{
         not trigger.event.data.old_state.attributes.get('restored', false) and
         not trigger.event.data.old_state.state == 'unavailable' and
         trigger.event.data.new_state is not none and
         trigger.event.data.new_state.attributes.event_type == 'identified' and
         (trigger.event.data.new_state.attributes.ulp_id|default('')) != '' and
         trigger.event.data.new_state.attributes.ulp_id in ['ALLOWED_ID1', 'ALLOWED_ID2']
       }}

It’s not crystal clear but I can see how this condition is trying to filter out spurious events. I’d really like to use this logic in conjunction with my finger_print attributes approach, but I have no way of testing something dynamic like this.
Is there anybody who can explain how I can re-phrase this to use finger_print instead of ulp_id? Thanks.

I have a G4 doorbell pro and I use the fingerprint scanner to disarm my alarm system. I noticed that when the G4 doorbell reboots (like after a firmware upgrade, power outage, network disconnect event, etc.) I get a ā€œValid Fingerprintā€ event in HA (attributes.event_type='identified'); therefore, my alarm system would disarm if it was armed.

I performed some tests and discovered that the G4 Doorbell, as part of the startup process, is sending the last valid (cached) fingerprint. I’m not sure why but I think the same is happening for you and others - that is why the above template was provided. The template is very comprehensive and I can see how it is meant to cover all cases where the problematic restart condition could cause an issue.

I solved my issue by ignoring the valid fingerprint event if the previous state (old_state.state) was unavailable.

1 Like

Thanks for that really useful reply. I tried adding the condition:
{{ not trigger.event.data.old_state.state == "unavailable"}} but this generarates an error "Error: In 'template' condition: UndefinedError: 'event' is undefined" . Any chance you can show me your template condition? TIA

Sorry, I don’t use templates for my automations - I use Node-RED.

1 Like

I still can’t work out the example condition, but this code looks like it will filter out the ā€œunavailableā€ state problem.

alias: 16b. G4 Doorbell Fingerprint operate Garage Door Left
description: ""
triggers:
  - trigger: state
    entity_id:
      - event.g4_doorbell_pro_poe_fingerprint
    not_from:
      - unknown
      - unavailable
    for:
      hours: 0
      minutes: 0
      seconds: 1
conditions:
  - condition: state
    entity_id: event.g4_doorbell_pro_poe_fingerprint
    attribute: full_name
    state: Phil Gilbert
  - condition: time
    after: "08:00:00"
  - condition: time
    before: "22:00:00"
actions:
  - action: script.turn_on
    metadata: {}
    data: {}
    target:
      entity_id: script.garage_door_left_operate
  - data:
      title: Garage Door Left
      message: Door is Open
    action: notify.mobile_app_xq_bt52
mode: queued
trace:
  stored_traces: 20

Event triggers are getting an overhaul soon (actually all triggers); the devs are aware that using events to trigger automations is a painful and tedious process because of the various issues.

For now, I recommend using the following template condition which will only pass events which are new (within the past 5 seconds):

{{ trigger.to_state.state | as_datetime(0|as_datetime) > now() - timedelta(seconds=5) }}
3 Likes

What means this line:

trigger.event.data.new_state.attributes.ulp_id in [ā€˜ALLOWED_ID1’, ā€˜ALLOWED_ID2’]

What is Allow_ID1 or 2 and where can i find it?

Is it my fingerprint id in the G4 Doorbell Pro ?

I was struggling to get this working but figured it out with the help of Claude. I could not get the example in documentation to work.

One can get the ULP ID from the Unrecognized Fingerprint Scan notification.

I’m not a programmer and know very little yaml but Claude is pretty good with the correct prompts.

alias: Fingerprint Scan - Garage Door Control
description: |2-
  **Last Modified**: 2025/12/18
  
  **UniFi Protect G4 Doorbell Fingerprint Authentication**
  
  **Summary:**
  - Monitors fingerprint scans from UniFi Protect G4 Doorbell and opens garage door for authorized users
  - Uses defensive filtering to prevent false triggers from restored states or stale events
  - Filters by unique `ulp_id` (GUID) for robust user identification, displays `full_name` in notifications
  - Notifies on unrecognized fingerprints for security monitoring
  - Single mode prevents rapid re-scans from triggering multiple door operations
  - Authorized users configured in action variables for easy maintenance (visible in UI)
  - Supports user-specific actions for customized behavior per user
  
  **Triggers:**
  - `state_changed` event: Fires when `event.camera_doorbell_fingerprint` entity state changes
  
  **Conditions:**
  - **Defensive Filter**: Ensures event is valid (not restored, not unavailable, has ulp_id, event_type is 'identified')
  - **5 Second Recency Check**: Only processes events that occurred within last 5 seconds to prevent stale triggers
  
  **Notifications:**
  - **User 1 Confirmation**: Sends confirmation notification when User 1 scans fingerprint
    - Target: `notify.notify_phone` (customize to your notification service)
    - Example: "[11:21:58 PM] Garage door opened by fingerprint scan."
  - **Unrecognized Fingerprint**: When identified fingerprint doesn't match authorized users
    - Target: `notify.notify_phone` (customize to your notification service)
    - Example: "[11:21:58 PM] Fingerprint identified - Name: Unknown User, ULP ID: abc123..., Status: ACTIVE - No assigned action"
  
  **Actions:**
  - **User-Specific Actions**: Checks for specific user IDs first for custom behaviors
    - User 1: Opens garage door + sends confirmation notification
    - User 2: Opens garage door only (no notification)
  - **General Authorized User Match**: Opens garage door when scanned `ulp_id` matches any ID in `authorized_users` list
  - **Unauthorized/Unknown User**: Sends security notification with full fingerprint details including name, ID, and user status
  
  **Variables:**
  - `authorized_users`: List of authorized ULP IDs (GUIDs) that can trigger garage door opening (defined in action block)
  - `scanned_ulp_id`: The ULP ID from the current fingerprint scan event (defined in action block)
  - `user1_ulp_id`: User 1's specific ULP ID for user-specific conditions (defined in action block)
  - `user2_ulp_id`: User 2's specific ULP ID for user-specific conditions (defined in action block)
  
  **Dependencies:**
  - `event.camera_doorbell_fingerprint`: UniFi Protect fingerprint event entity
  - `cover.garage_door_door`: Garage door cover entity
  - `notify.notify_phone`: Notification service (customize to your setup)
  
  **Mode:** single
  **Trace Storage:** 15 days
  
  **Update Log:**
  - 2025/12/18: Initial version with defensive filtering per UniFi Protect integration docs

variables:
  # Extract the scanned fingerprint ULP ID from the trigger event
  scanned_ulp_id: "{{ trigger.event.data.new_state.attributes.ulp_id }}"

triggers:
  - alias: Fingerprint Scan Event
    event_type: state_changed
    event_data:
      entity_id: event.camera_doorbell_fingerprint  # Change to your fingerprint entity
    trigger: event

conditions:
  - alias: Defensive Filter - Valid Identified Event
    condition: template
    # This filter prevents false triggers from:
    # - Home Assistant restarts (restored states)
    # - Unavailable/stale states
    # - Events without a valid ULP ID
    # - Events that aren't "identified" type
    value_template: |-
      {{
        trigger.event.data.old_state is not none and
        trigger.event.data.new_state is not none and
        not trigger.event.data.old_state.attributes.get('restored', false) and
        trigger.event.data.old_state.state != 'unavailable' and
        trigger.event.data.new_state.attributes.event_type == 'identified' and
        (trigger.event.data.new_state.attributes.ulp_id | default('')) != ''
      }}
  - alias: 5 Second Recency Filter
    condition: template
    # Only process fingerprint scans that occurred within the last 5 seconds
    # This prevents stale events from triggering the automation
    value_template: >-
      {{ trigger.event.data.new_state.state | as_datetime(as_datetime(0)) >
      now() - timedelta(seconds=5) }}

actions:
  - alias: Set User Configuration Variables
    variables:
      # IMPORTANT: These MUST be actual ULP IDs (GUIDs), not names!
      # To get ULP IDs: Have each user scan their fingerprint, then check the
      # notification or trace for their unique ulp_id GUID
      # The comments after each ID are just for YOUR reference - HA ignores them
      authorized_users:
        - 7d71cc87-7993-4775-914f-7be3ab056d7c  # User 1 - Replace with actual ULP ID
        - abc123xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  # User 2 - Replace with actual ULP ID
        # - def456yy-yyyy-yyyy-yyyy-yyyyyyyyyyyy  # Additional User (add more as needed)
      
      # Individual user IDs for user-specific actions
      # These allow you to give different users different behaviors
      user1_ulp_id: 7d71cc87-7993-4775-914f-7be3ab056d7c  # Replace with User 1's actual ULP ID
      user2_ulp_id: abc123xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  # Replace with User 2's actual ULP ID
  
  - alias: Check User and Execute Actions
    choose:
      # ==========================================
      # USER-SPECIFIC ACTIONS
      # Add custom behaviors for individual users here
      # ==========================================
      
      - conditions:
          - alias: User 1 - Specific Actions
            condition: template
            value_template: "{{ scanned_ulp_id == user1_ulp_id }}"
        sequence:
          - alias: Open Garage Door
            action: cover.open_cover
            metadata: {}
            target:
              entity_id: cover.garage_door_door  # Change to your garage door entity
            data: {}
          
          - alias: Send User 1 Confirmation Notification
            action: notify.notify_phone  # Change to your notification service
            metadata: {}
            data:
              title: šŸš— Garage Door Opened
              message: >-
                [{{ trigger.event.data.new_state.state | as_datetime | as_local | as_timestamp | timestamp_custom('%I:%M:%S %p') }}]
                Garage door opened by fingerprint scan.
      
      - conditions:
          - alias: User 2 - Specific Actions
            condition: template
            value_template: "{{ scanned_ulp_id == user2_ulp_id }}"
        sequence:
          - alias: Open Garage Door
            action: cover.open_cover
            metadata: {}
            target:
              entity_id: cover.garage_door_door  # Change to your garage door entity
            data: {}
          # User 2 gets no notification - customize actions here as needed
          # Examples you could add:
          #   - Turn on entry lights
          #   - Disarm alarm system
          #   - Send notification to different phone
          #   - Log entry to a file/spreadsheet
      
      # ==========================================
      # CATCH-ALL FOR OTHER AUTHORIZED USERS
      # Any user in authorized_users list but not specified above
      # ==========================================
      - conditions:
          - alias: Any Other Authorized User
            condition: template
            # Checks if scanned ULP ID is in the authorized_users list
            value_template: "{{ scanned_ulp_id in authorized_users }}"
        sequence:
          - alias: Open Garage Door
            action: cover.open_cover
            metadata: {}
            target:
              entity_id: cover.garage_door_door  # Change to your garage door entity
            data: {}
    
    # ==========================================
    # UNAUTHORIZED/UNRECOGNIZED FINGERPRINT
    # Security alert for fingerprints not in authorized list
    # ==========================================
    default:
      - alias: Notify Unrecognized Fingerprint
        action: notify.notify_phone  # Change to your notification service
        metadata: {}
        data:
          title: šŸ” Unrecognized Fingerprint Scan
          message: >-
            [{{ trigger.event.data.new_state.state | as_datetime | as_local | as_timestamp | timestamp_custom('%I:%M:%S %p') }}]
            Fingerprint identified but no assigned action:
            
            Name: {{ trigger.event.data.new_state.attributes.full_name }}
            ULP ID: {{ trigger.event.data.new_state.attributes.ulp_id }}
            Status: {{ trigger.event.data.new_state.attributes.user_status }}
            Event ID: {{ trigger.event.data.new_state.attributes.event_id }}

mode: single  # Prevents multiple simultaneous executions - important for door security!
trace:
  stored_traces: 15  # Keep 15 days of execution history for debugging

As a rule of thumb for Home Assistant: Filter by ID (ulp_id), but use Name (full_name) for notifications.

Filter by ID: The ulp_id is a unique GUID. Even if you change your name in UniFi Protect or fix a typo, the ID stays the same. It is the most robust way to ensure the door only opens for the right person.

Use Name for UI/Alerts: The full_name is ā€œhuman-readable.ā€ It’s perfect for passing into a notification message so you don’t have to look up who a string of hex characters belongs to.