Răsfoiți Sursa

fix(nedis_pilldispenser): correct dp 102 masks, add alarm status sensors (#6082)

**I have this device (Nedis WIFIPD10WT) and tested all of this against
it.**

The dp 102 masks have Byte0 on the right (little-endian) but the dp had
no `endianness: little`, so it was read big-endian and every field came
from the alarm-status bytes at the *end* of the payload instead of the
status bytes at the start. Everything decoded as 0 or off. Fix is adding
`endianness: little`, masks unchanged.

Real dp 102 payload from my device:

`00002b001c0805030000b302000000000000000000070000000000000000`

| field | before | after |
|---|---|---|
| total compartments | 0 | 28 |
| loaded compartments | 0 | 8 |
| current compartment | 0 | 5 |
| remaining compartments | 0 | 3 |
| battery | 0 | 43 % |
| plug / lid | off | correct |

I also added sensors for the per-alarm status bytes (B21–B29). On my
device dp 103 and dp 120 never arrive over the LAN, and are not answered
when requested explicitly, so these bytes are the only local way to see
whether a dose was taken. Confirmed live: alarm 2 went `waiting` →
`missed` → `taken_after_missing` across a real dose.

Finally, the battery binary_sensor: its constraint compares integers
(`2`, `[0, 1, 3]`) against `battery_state`, but that dp maps its values
to strings first, so nothing ever matched and the entity stayed
`unknown`. It now compares the mapped names, and reads `off` with
`battery_state: full` on my device.

The config was originally created from the cloud data model in #3357
without hardware to test against, which is presumably why the byte order
was never caught.

<img width="394" height="622" alt="Screenshot 2026-09-05 at 21 26 50"
src="https://github.com/user-attachments/assets/1fcc7b11-ecb0-4562-9667-3bf45b52a78d"
/>
<img width="393" height="535" alt="Screenshot 2026-09-05 at 21 26 37"
src="https://github.com/user-attachments/assets/867be5e8-9953-4927-a4c7-9028c57f884e"
/>
<img width="395" height="574" alt="Screenshot 2026-09-05 at 21 26 26"
src="https://github.com/user-attachments/assets/c600b41f-2073-46b6-89f8-09c6afe6131a"
/>
<img width="399" height="483" alt="Screenshot 2026-09-05 at 21 26 17"
src="https://github.com/user-attachments/assets/66c186f7-be0e-42f8-86d8-6f7b4e91c6d0"
/>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Martin Nuc 4 ore în urmă
părinte
comite
1cc53d3ad5
1 a modificat fișierele cu 263 adăugiri și 2 ștergeri
  1. 263 2
      custom_components/tuya_local/devices/nedis_pilldispenser.yaml

+ 263 - 2
custom_components/tuya_local/devices/nedis_pilldispenser.yaml

@@ -25,6 +25,7 @@ entities:
         optional: true
         mask: >-
           0000000000000000000000000000000000000000000000000000000000000001
+        endianness: little
         name: sensor
   - entity: binary_sensor
     class: battery
@@ -38,15 +39,16 @@ entities:
           - dps_val: null
             constraint: battery_state
             conditions:
-              - dps_val: 2
+              - dps_val: low
                 value: true
-              - dps_val: [0, 1, 3]
+              - dps_val: [normal, full, charging]
                 value: false
       - id: 102
         type: base64
         optional: true
         mask: >-
           0000000000000000000000000000000000000000000000000000000000000700
+        endianness: little
         name: battery_state
         mapping:
           - dps_val: 0
@@ -68,6 +70,7 @@ entities:
         optional: true
         mask: >-
           0000000000000000000000000000000000000000000000000000000000000700
+        endianness: little
         name: sensor
         mapping:
           - dps_val: 3
@@ -82,6 +85,7 @@ entities:
         optional: true
         mask: >-
           0000000000000000000000000000000000000000000000000000000000FF0000
+        endianness: little
         name: sensor
         unit: "%"
         class: measurement
@@ -95,6 +99,7 @@ entities:
         optional: true
         mask: >-
           0000000000000000000000000000000000000000000000000000000001000000
+        endianness: little
         name: sensor
   - entity: sensor
     name: Total compartments
@@ -106,6 +111,7 @@ entities:
         optional: true
         mask: >-
           000000000000000000000000000000000000000000000000000000FF00000000
+        endianness: little
         name: sensor
   - entity: sensor
     name: Loaded compartments
@@ -117,6 +123,7 @@ entities:
         optional: true
         mask: >-
           0000000000000000000000000000000000000000000000000000FF0000000000
+        endianness: little
         name: sensor
         class: measurement
   - entity: sensor
@@ -129,6 +136,7 @@ entities:
         optional: true
         mask: >-
           00000000000000000000000000000000000000000000000000FF000000000000
+        endianness: little
         name: sensor
         class: measurement
   - entity: sensor
@@ -141,6 +149,7 @@ entities:
         optional: true
         mask: >-
           000000000000000000000000000000000000000000000000FF00000000000000
+        endianness: little
         name: sensor
         class: measurement
   - entity: event
@@ -785,3 +794,255 @@ entities:
           - dps_val: 2
             value: false
           - value: null
+  - entity: sensor
+    name: Alarm 1 status
+    class: enum
+    icon: "mdi:alarm"
+    dps:
+      - id: 102
+        type: base64
+        optional: true
+        mask: "00000000000000000000FF000000000000000000000000000000000000000000"
+        endianness: little
+        name: sensor
+        mapping:
+          - dps_val: 0
+            value: none
+          - dps_val: 1
+            value: waiting
+          - dps_val: 2
+            value: overdue
+          - dps_val: 3
+            value: taken_early
+          - dps_val: 4
+            value: taken_on_time
+          - dps_val: 5
+            value: taken_late
+          - dps_val: 6
+            value: missed
+          - dps_val: 7
+            value: taken_after_missing
+  - entity: sensor
+    name: Alarm 2 status
+    class: enum
+    icon: "mdi:alarm"
+    dps:
+      - id: 102
+        type: base64
+        optional: true
+        mask: "000000000000000000FF00000000000000000000000000000000000000000000"
+        endianness: little
+        name: sensor
+        mapping:
+          - dps_val: 0
+            value: none
+          - dps_val: 1
+            value: waiting
+          - dps_val: 2
+            value: overdue
+          - dps_val: 3
+            value: taken_early
+          - dps_val: 4
+            value: taken_on_time
+          - dps_val: 5
+            value: taken_late
+          - dps_val: 6
+            value: missed
+          - dps_val: 7
+            value: taken_after_missing
+  - entity: sensor
+    name: Alarm 3 status
+    class: enum
+    icon: "mdi:alarm"
+    dps:
+      - id: 102
+        type: base64
+        optional: true
+        mask: "0000000000000000FF0000000000000000000000000000000000000000000000"
+        endianness: little
+        name: sensor
+        mapping:
+          - dps_val: 0
+            value: none
+          - dps_val: 1
+            value: waiting
+          - dps_val: 2
+            value: overdue
+          - dps_val: 3
+            value: taken_early
+          - dps_val: 4
+            value: taken_on_time
+          - dps_val: 5
+            value: taken_late
+          - dps_val: 6
+            value: missed
+          - dps_val: 7
+            value: taken_after_missing
+  - entity: sensor
+    name: Alarm 4 status
+    class: enum
+    icon: "mdi:alarm"
+    dps:
+      - id: 102
+        type: base64
+        optional: true
+        mask: "00000000000000FF000000000000000000000000000000000000000000000000"
+        endianness: little
+        name: sensor
+        mapping:
+          - dps_val: 0
+            value: none
+          - dps_val: 1
+            value: waiting
+          - dps_val: 2
+            value: overdue
+          - dps_val: 3
+            value: taken_early
+          - dps_val: 4
+            value: taken_on_time
+          - dps_val: 5
+            value: taken_late
+          - dps_val: 6
+            value: missed
+          - dps_val: 7
+            value: taken_after_missing
+  - entity: sensor
+    name: Alarm 5 status
+    class: enum
+    icon: "mdi:alarm"
+    dps:
+      - id: 102
+        type: base64
+        optional: true
+        mask: "000000000000FF00000000000000000000000000000000000000000000000000"
+        endianness: little
+        name: sensor
+        mapping:
+          - dps_val: 0
+            value: none
+          - dps_val: 1
+            value: waiting
+          - dps_val: 2
+            value: overdue
+          - dps_val: 3
+            value: taken_early
+          - dps_val: 4
+            value: taken_on_time
+          - dps_val: 5
+            value: taken_late
+          - dps_val: 6
+            value: missed
+          - dps_val: 7
+            value: taken_after_missing
+  - entity: sensor
+    name: Alarm 6 status
+    class: enum
+    icon: "mdi:alarm"
+    dps:
+      - id: 102
+        type: base64
+        optional: true
+        mask: "0000000000FF0000000000000000000000000000000000000000000000000000"
+        endianness: little
+        name: sensor
+        mapping:
+          - dps_val: 0
+            value: none
+          - dps_val: 1
+            value: waiting
+          - dps_val: 2
+            value: overdue
+          - dps_val: 3
+            value: taken_early
+          - dps_val: 4
+            value: taken_on_time
+          - dps_val: 5
+            value: taken_late
+          - dps_val: 6
+            value: missed
+          - dps_val: 7
+            value: taken_after_missing
+  - entity: sensor
+    name: Alarm 7 status
+    class: enum
+    icon: "mdi:alarm"
+    dps:
+      - id: 102
+        type: base64
+        optional: true
+        mask: "00000000FF000000000000000000000000000000000000000000000000000000"
+        endianness: little
+        name: sensor
+        mapping:
+          - dps_val: 0
+            value: none
+          - dps_val: 1
+            value: waiting
+          - dps_val: 2
+            value: overdue
+          - dps_val: 3
+            value: taken_early
+          - dps_val: 4
+            value: taken_on_time
+          - dps_val: 5
+            value: taken_late
+          - dps_val: 6
+            value: missed
+          - dps_val: 7
+            value: taken_after_missing
+  - entity: sensor
+    name: Alarm 8 status
+    class: enum
+    icon: "mdi:alarm"
+    dps:
+      - id: 102
+        type: base64
+        optional: true
+        mask: "000000FF00000000000000000000000000000000000000000000000000000000"
+        endianness: little
+        name: sensor
+        mapping:
+          - dps_val: 0
+            value: none
+          - dps_val: 1
+            value: waiting
+          - dps_val: 2
+            value: overdue
+          - dps_val: 3
+            value: taken_early
+          - dps_val: 4
+            value: taken_on_time
+          - dps_val: 5
+            value: taken_late
+          - dps_val: 6
+            value: missed
+          - dps_val: 7
+            value: taken_after_missing
+  - entity: sensor
+    name: Alarm 9 status
+    class: enum
+    icon: "mdi:alarm"
+    dps:
+      - id: 102
+        type: base64
+        optional: true
+        mask: "0000FF0000000000000000000000000000000000000000000000000000000000"
+        endianness: little
+        name: sensor
+        mapping:
+          - dps_val: 0
+            value: none
+          - dps_val: 1
+            value: waiting
+          - dps_val: 2
+            value: overdue
+          - dps_val: 3
+            value: taken_early
+          - dps_val: 4
+            value: taken_on_time
+          - dps_val: 5
+            value: taken_late
+          - dps_val: 6
+            value: missed
+          - dps_val: 7
+            value: taken_after_missing