Repository: bdraco/yalexs (language: python)

Issue:
Add support for finger_unlock action in the /houses/<house_id>/activities API response
I have a Yale Keypad Touch configured to work with my August WiFi lock. In Home Assistant, I have created an automation that gets triggered when the August WiFi lock is unlocked. This automation works fine when I enter my PIN on the Yale Keypad. However, when I try to use my fingerprint to unlock the automation doesn't get triggered. 

After reading through the **yalexs** code base, I believe the automation isn't triggered because the `finger_unlock` action isn't expected in the response from the `/houses/<house_id>/activities` API.

Below is a sample event structure where the `finger_unlock` action appears.

```
{
    "id": "*** Redacted ID ***",
    "timestamp": 1726770544000,
    "icon": "https://d33mytkkohwnk6.cloudfront.net/app/ActivityFeedIcons/finger_unlock@3x.png",
    "action": "finger_unlock",
    "deviceID": "*** Redacted Device ID ***",
    "deviceType": "lock",
    "user": {
        "UserID": "*** Redacted User ID ***",
        "FirstName": "Thomas",
        "LastName": "Nelson"
    },
    "title": "<b>Thomas Nelson</b> unlocked <b>Front Door</b> with fingerprint"
}
```

Please let me know if any more details would be helpful.


Discussion / hints:
Here is an example PR on how to add new lock methods
https://github.com/bdraco/yalexs/pull/177

Fix patch:
diff --git a/yalexs/activity.py b/yalexs/activity.py
index ef452fc..0b7d3c7 100644
--- a/yalexs/activity.py
+++ b/yalexs/activity.py
@@ -40,6 +40,8 @@
 ACTION_HOMEKEY_UNLOCK = "homekey_unlock"
 ACTION_LINKED_LOCK = "linked_lock"
 ACTION_LINKED_UNLOCK = "linked_unlock"
+ACTION_LOCK_FINGER_LOCK = "finger_lock"
+ACTION_LOCK_FINGER_UNLOCK = "finger_unlock"
 
 ACTION_DOOR_OPEN = "dooropen"
 ACTION_DOOR_OPEN_2 = "door_open"
@@ -100,6 +102,8 @@
     ACTION_LOCK_MANUAL_LOCK,
     ACTION_LOCK_MANUAL_UNLATCH,
     ACTION_LOCK_MANUAL_UNLOCK,
+    ACTION_LOCK_FINGER_LOCK,
+    ACTION_LOCK_FINGER_UNLOCK,
 }
 
 
@@ -122,6 +126,8 @@
     ACTION_LOCK_MANUAL_LOCK: ("Manual", "Lock"),
     ACTION_LOCK_MANUAL_UNLATCH: ("Manual", "Unlatch"),
     ACTION_LOCK_MANUAL_UNLOCK: ("Manual", "Unlock"),
+    ACTION_LOCK_FINGER_LOCK: ("Fingerprint", "Lock"),
+    ACTION_LOCK_FINGER_UNLOCK: ("Fingerprint", "Unlock"),
 }
 
 
@@ -137,6 +143,8 @@
     ACTION_LOCK_ONETOUCHLOCK_2,
     ACTION_LOCK_PIN_UNLATCH,
     ACTION_LOCK_PIN_UNLOCK,
+    ACTION_LOCK_FINGER_LOCK,
+    ACTION_LOCK_FINGER_UNLOCK,
 }
 REMOTE_ACTIONS = {
     ACTION_LOCK_REMOTE_LOCK,
@@ -196,6 +204,8 @@
     ACTION_LOCK_MANUAL_LOCK: LockStatus.LOCKED,
     ACTION_LOCK_MANUAL_UNLATCH: LockStatus.UNLATCHED,
     ACTION_LOCK_MANUAL_UNLOCK: LockStatus.UNLOCKED,
+    ACTION_LOCK_FINGER_LOCK: LockStatus.LOCKED,
+    ACTION_LOCK_FINGER_UNLOCK: LockStatus.UNLOCKED,
 }
 
 


Test patch:
diff --git a/tests/fixtures/finger_lock_activity.json b/tests/fixtures/finger_lock_activity.json
new file mode 100644
index 0000000..f10a415
--- /dev/null
+++ b/tests/fixtures/finger_lock_activity.json
@@ -0,0 +1,14 @@
+{
+  "id": "e05b1c22-71d1-44b8-8a4c-e729174a8972",
+  "timestamp": 1727126924000,
+  "icon": "https://d33mytkkohwnk6.cloudfront.net/app/ActivityFeedIcons/finger_lock@3x.png",
+  "action": "finger_lock",
+  "deviceID": "3ED27C5DB830439CAA4F381DC8F16444",
+  "deviceType": "lock",
+  "user": {
+    "UserID": "xxxsasdd-230b-4c6a-8a5c-155043492f3b",
+    "FirstName": "Sample",
+    "LastName": "Person"
+  },
+  "title": "<b>Sample Person</b> locked <b>Front Door</b> with fingerprint"
+}
diff --git a/tests/fixtures/finger_unlock_activity.json b/tests/fixtures/finger_unlock_activity.json
new file mode 100644
index 0000000..57728ed
--- /dev/null
+++ b/tests/fixtures/finger_unlock_activity.json
@@ -0,0 +1,14 @@
+{
+  "id": "e05b1c22-71d1-44b8-8a4c-e729174a8972",
+  "timestamp": 1726770544000,
+  "icon": "https://d33mytkkohwnk6.cloudfront.net/app/ActivityFeedIcons/finger_unlock@3x.png",
+  "action": "finger_unlock",
+  "deviceID": "3ED27C5DB830439CAA4F381DC8F16444",
+  "deviceType": "lock",
+  "user": {
+    "UserID": "xxxsasdd-230b-4c6a-8a5c-155043492f3b",
+    "FirstName": "Sample",
+    "LastName": "Person"
+  },
+  "title": "<b>Sample Person</b> unlocked <b>Front Door</b> with fingerprint"
+}
diff --git a/tests/test_activity.py b/tests/test_activity.py
index bef2a4f..1ac8427 100644
--- a/tests/test_activity.py
+++ b/tests/test_activity.py
@@ -29,6 +29,8 @@
     ACTION_LOCK_BLE_UNLATCH,
     ACTION_LOCK_BLE_UNLOCK,
     ACTION_LOCK_DOORBELL_BUTTON_PUSHED,
+    ACTION_LOCK_FINGER_LOCK,
+    ACTION_LOCK_FINGER_UNLOCK,
     ACTION_LOCK_JAMMED,
     ACTION_LOCK_LOCK,
     ACTION_LOCK_LOCKING,
@@ -144,6 +146,8 @@ def test_activity_actions(self):
                 ACTION_LOCK_MANUAL_UNLOCK,
                 ACTION_LINKED_LOCK,
                 ACTION_LINKED_UNLOCK,
+                ACTION_LOCK_FINGER_LOCK,
+                ACTION_LOCK_FINGER_UNLOCK,
             ],
         )
         self.assertCountEqual(
@@ -546,3 +550,39 @@ def test_linked_lock_activity_v4(self):
             manual_lock_activity.operator_thumbnail_url
             == "https://d3osa7xy9vsc0q.cloudfront.net/app/ActivityFeedIcons/linked_lock@3x.png"
         )
+
+    def test_finger_lock_activity(self):
+        keypad_lock_activity = LockOperationActivity(
+            SOURCE_LOG, json.loads(load_fixture("finger_lock_activity.json"))
+        )
+        assert keypad_lock_activity.operated_by == "Sample Person"
+        assert keypad_lock_activity.operated_remote is False
+        assert keypad_lock_activity.operated_keypad is True
+        assert keypad_lock_activity.operated_manual is False
+        assert keypad_lock_activity.operated_tag is False
+        assert (
+            keypad_lock_activity.operator_image_url
+            == "https://d33mytkkohwnk6.cloudfront.net/app/ActivityFeedIcons/finger_lock@3x.png"
+        )
+        assert (
+            keypad_lock_activity.operator_thumbnail_url
+            == "https://d33mytkkohwnk6.cloudfront.net/app/ActivityFeedIcons/finger_lock@3x.png"
+        )
+
+    def test_finger_unlock_activity(self):
+        keypad_lock_activity = LockOperationActivity(
+            SOURCE_LOG, json.loads(load_fixture("finger_unlock_activity.json"))
+        )
+        assert keypad_lock_activity.operated_by == "Sample Person"
+        assert keypad_lock_activity.operated_remote is False
+        assert keypad_lock_activity.operated_keypad is True
+        assert keypad_lock_activity.operated_manual is False
+        assert keypad_lock_activity.operated_tag is False
+        assert (
+            keypad_lock_activity.operator_image_url
+            == "https://d33mytkkohwnk6.cloudfront.net/app/ActivityFeedIcons/finger_unlock@3x.png"
+        )
+        assert (
+            keypad_lock_activity.operator_thumbnail_url
+            == "https://d33mytkkohwnk6.cloudfront.net/app/ActivityFeedIcons/finger_unlock@3x.png"
+        )