Open dmulcahey opened 3 months ago
Would love to see this device supported in ZHA! Is there anything I can do? Would like to help, but I’m not a dev!
I copied the py file from the Aqara FP1, changed a few lines to lumi.sensor_occupy.agl1.
Then created the directory config/zha_quirks under the homeassistant folder Copied the file motion_agl1.py to that directory
Added the following lines in the configuration.yaml zha: enable_quirks: true custom_quirks_path: config/zha_quirks
And after a restart and some time (do not know why) motion was detected in HA.
I guess a lot more needs to be changed in the file but for me it is working and can use it like this until somebody with some actual experience can look at this :)
it’s similar to the existing FP1 but the event data appears to be sent on different attributes. I need to find a block of time to sit with the device and decode it.
Make it a +1 that a support would be awesome 🤩
+1,
I am currently trying to use the sensor, but I figured it still needs to be supported.
+1
Also waiting for support for this device...
+1 That would be really great. Thanks
+1 - just got this from really cheap TaoBao store
+1
Confirm https://github.com/klolik/zha-device-handlers/commit/49d66b678ff07f58c0a14e065241b29efea4ec82 is working with my FP1E Aqara Presence Sensor.
Confirm klolik@49d66b6 is working with my FP1E Aqara Presence Sensor.
It works for me as well
For me presence is detected, thx!
Somehow my sensor only detect motions, not presence. If things not moving, after few seconds it's status is back to away
I've made some tweaks to the code in @sandokanalles quirk to expose additional parameter exposed by the device:
I've also removed functions that do not appear to be supported in the FP1E variant (it seems somewhat simplified).
It feels like this might be a good candidate for the quirks v2 format to expose the non-standard configuration and reporting values in the UI, in a similar way that z2m can achieve.
Edit: see my next post for the revised quirk implemented in v2.
I've been able to expose the additional attributes using the quirks v2 API - naming of the entities isn't perfect (we might need to add some translations to the HA/ZHA strings.json, however please do give it a try and let me know of issues.
A heads up @dmulcahey I've encountered an issue in using UnitOfLength units with the v2 API - whilst they're present in zigpy.quirks.v2.homeassistant
, the code is checking valid values against zha.units.UNITS_OF_MEASURE
, in which UnitOfLength is currently absent.
With the unit commented out we have the following warning:
2024-09-29 20:15:03.533 WARNING (MainThread) [homeassistant.components.sensor] Entity sensor.living_room_fp1e_distance (<class 'homeassistant.components.zha.sensor.Sensor'>) is using native unit of measurement 'None' which is not a valid unit for the device class ('distance') it is using; expected one of ['km', 'mm', 'in', 'mi', 'm', 'yd', 'cm', 'ft']; Please update your configuration if your entity is manually configured, otherwise create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+zha%22
Quirk v2 code available for testing:
"""Quirk for aqara lumi.sensor_occupy.agl1."""
from __future__ import annotations
import logging
from typing import Any
from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
from zigpy.quirks.v2 import QuirkBuilder, NumberDeviceClass, SensorDeviceClass, SensorStateClass
from zigpy.quirks.v2.homeassistant import EntityPlatform, EntityType, UnitOfLength
import zigpy.types as types
from zigpy.zcl.clusters.general import Basic, DeviceTemperature, Identify, Ota
from zigpy.zcl.clusters.measurement import OccupancySensing
from zigpy.zcl.foundation import ZCLAttributeDef
from zhaquirks.xiaomi import XiaomiAqaraE1Cluster
APPROACH_DISTANCE_ATTR_ID = 0x015B # UINT32 The configurable maximum detection distance in millimeters (default 600 = 6 meters).
MOTION_SENSITIVITY_ATTR_ID = 0x010C # UINT8 The configurable detection sensitivity (0x01 = low, 0x02 = medium, 0x03 = high, default 0x03 = High)
MOTION_ATTR_ID = 0x0160 # UINT8 Detected motion (0x02 = no movement, 0x03 = large movement, 0x04 = small movement)
MOTION_DISTANCE_ATTR_ID = 0x015f # UINT32 Distance to the detected motion (mm)
OCCUPANCY_ATTR_ID = 0x0142 #UINT8 Occupancy detected 0x0 = absence, 0x01 = presence
RESET_NO_PRESENCE_STATUS_ATTR_ID = 0x0157 # Trigger AI spatial learning
_LOGGER = logging.getLogger(__name__)
class AqaraMotionSensitivity(types.enum8):
"""Aqara motion sensitivity."""
Low = 0x01
Medium = 0x02
High = 0x03
class AqaraMotion(types.enum8):
"""Aqara motion."""
Unknown = 0x00
Unknown_1 = 0x01
Idle = 0x02
Moving = 0x03
Still = 0x04
class OppleCluster(XiaomiAqaraE1Cluster):
"""Aqara manufacturer cluster."""
class AttributeDefs(XiaomiAqaraE1Cluster.AttributeDefs):
"""Aqara occupancy sensor manufacturer specific attributes."""
approach_distance = ZCLAttributeDef(
id=APPROACH_DISTANCE_ATTR_ID,
type=types.uint32_t,
is_manufacturer_specific=True,
)
motion_sensitivity = ZCLAttributeDef(
id=MOTION_SENSITIVITY_ATTR_ID,
type=types.uint8_t,
is_manufacturer_specific=True,
)
motion = ZCLAttributeDef(
id=MOTION_ATTR_ID,
type=types.uint8_t,
is_manufacturer_specific=True,
)
motion_distance = ZCLAttributeDef(
id=MOTION_DISTANCE_ATTR_ID,
type=types.uint32_t,
is_manufacturer_specific=True,
)
occupancy = ZCLAttributeDef(
id=OCCUPANCY_ATTR_ID,
type=types.uint8_t,
is_manufacturer_specific=True,
)
reset_no_presence_status = ZCLAttributeDef(
id=RESET_NO_PRESENCE_STATUS_ATTR_ID,
type=types.uint8_t,
is_manufacturer_specific=True,
)
def _update_attribute(self, attrid: int, value: Any) -> None:
"""Update the OccupancySensing cluster."""
super()._update_attribute(attrid, value)
if attrid == OCCUPANCY_ATTR_ID:
if value != 0xFF:
self.endpoint.occupancy.update_attribute(
OccupancySensing.AttributeDefs.occupancy.id,
OccupancySensing.Occupancy(value)
)
(
QuirkBuilder("aqara", "lumi.sensor_occupy.agl1")
.adds(DeviceTemperature)
.adds(OccupancySensing)
.replaces(OppleCluster)
# Detection distance
.number(
OppleCluster.AttributeDefs.approach_distance.name,
OppleCluster.cluster_id,
min_value=0,
max_value=6,
step=0.1,
#unit=UnitOfLength.METERS,
multiplier=0.01,
device_class=NumberDeviceClass.DISTANCE,
)
# Motion
.enum(
OppleCluster.AttributeDefs.motion.name,
AqaraMotion,
OppleCluster.cluster_id,
entity_platform=EntityPlatform.SENSOR,
entity_type=EntityType.STANDARD,
)
# Motion distance
.sensor(
OppleCluster.AttributeDefs.motion_distance.name,
OppleCluster.cluster_id,
#unit=UnitOfLength.METERS,
multiplier=0.01,
device_class=SensorDeviceClass.DISTANCE,
state_class=SensorStateClass.MEASUREMENT,
)
# Motion sensitivity
.enum(
OppleCluster.AttributeDefs.motion_sensitivity.name,
AqaraMotionSensitivity,
OppleCluster.cluster_id,
)
# Trigger AI spatial learning
.write_attr_button(
OppleCluster.AttributeDefs.reset_no_presence_status.name,
1,
OppleCluster.cluster_id,
)
.add_to_registry()
)
Somehow my sensor only detect motions, not presence. If things not moving, after few seconds it's status is back to
away
The 'Presence status reset' button exposed by the quirk code I've attached in https://github.com/zigpy/zha-device-handlers/issues/3294#issuecomment-2381449768 may help you (it did for me), this triggers the device to use its 'AI Spatial Learning' function. You should do this when the area is unoccupied and ensure nothing moves in the detection zone for at least 30 seconds.
A recent change has been introduced that allows v2 quirks to specify a fallback name for entities that don't yet have a common translation.
@dmulcahey, @puddly There still appear to be some bugs around naming distance device class entities, in addition to their unit not being applied correctly. I'll take a closer look at the logs around these and raise an issue documenting my findings.
"""Quirk for aqara lumi.sensor_occupy.agl1."""
from __future__ import annotations
import logging
from typing import Any
from zigpy.quirks.v2 import (
QuirkBuilder,
NumberDeviceClass,
SensorDeviceClass,
SensorStateClass,
)
from zigpy.quirks.v2.homeassistant import EntityPlatform, EntityType, UnitOfLength
import zigpy.types as types
from zigpy.zcl.clusters.general import DeviceTemperature
from zigpy.zcl.clusters.measurement import OccupancySensing
from zigpy.zcl.foundation import ZCLAttributeDef
from zhaquirks.xiaomi import XiaomiAqaraE1Cluster
APPROACH_DISTANCE_ATTR_ID = 0x015B # UINT32 The configurable maximum detection distance in millimeters (default 600 = 6 meters).
MOTION_ATTR_ID = 0x0160 # UINT8 Detected motion (0x02 = no movement, 0x03 = large movement, 0x04 = small movement)
MOTION_DISTANCE_ATTR_ID = 0x015F # UINT32 Distance to the detected motion (mm)
MOTION_SENSITIVITY_ATTR_ID = 0x010C # UINT8 The configurable detection sensitivity (0x01 = low, 0x02 = medium, 0x03 = high, default 0x03 = High)
OCCUPANCY_ATTR_ID = 0x0142 # UINT8 Occupancy detected 0x0 = absence, 0x01 = presence
RESET_NO_PRESENCE_STATUS_ATTR_ID = 0x0157 # UINT8 Trigger AI spatial learning
RESTART_DEVICE_ATTR_ID = 0x00E8 # BOOL Trigger device restart
_LOGGER = logging.getLogger(__name__)
class AqaraMotionSensitivity(types.enum8):
"""Aqara motion sensitivity."""
Low = 0x01
Medium = 0x02
High = 0x03
class AqaraMotion(types.enum8):
"""Aqara motion."""
Unknown_0 = 0x00
Unknown_1 = 0x01
Idle = 0x02
Moving = 0x03
Still = 0x04
class OppleCluster(XiaomiAqaraE1Cluster):
"""Aqara manufacturer cluster for the FP1E presence sensor."""
class AttributeDefs(XiaomiAqaraE1Cluster.AttributeDefs):
"""Aqara occupancy sensor manufacturer specific attributes."""
approach_distance = ZCLAttributeDef(
id=APPROACH_DISTANCE_ATTR_ID,
type=types.uint32_t,
access="rw",
is_manufacturer_specific=True,
)
motion = ZCLAttributeDef(
id=MOTION_ATTR_ID,
type=types.uint8_t,
access="rp",
is_manufacturer_specific=True,
)
motion_distance = ZCLAttributeDef(
id=MOTION_DISTANCE_ATTR_ID,
type=types.uint32_t,
access="rp",
is_manufacturer_specific=True,
)
motion_sensitivity = ZCLAttributeDef(
id=MOTION_SENSITIVITY_ATTR_ID,
type=types.uint8_t,
access="rw",
is_manufacturer_specific=True,
)
occupancy = ZCLAttributeDef(
id=OCCUPANCY_ATTR_ID,
type=types.uint8_t,
access="rp",
is_manufacturer_specific=True,
)
reset_no_presence_status = ZCLAttributeDef(
id=RESET_NO_PRESENCE_STATUS_ATTR_ID,
type=types.uint8_t,
access="w",
is_manufacturer_specific=True,
)
restart_device = ZCLAttributeDef(
id=RESTART_DEVICE_ATTR_ID,
type=types.Bool,
access="w",
is_manufacturer_specific=True,
)
def _update_attribute(self, attrid: int, value: Any) -> None:
"""Update the OccupancySensing cluster."""
super()._update_attribute(attrid, value)
if attrid == OCCUPANCY_ATTR_ID:
self.endpoint.occupancy.update_attribute(
OccupancySensing.AttributeDefs.occupancy.id,
OccupancySensing.Occupancy(value),
)
(
QuirkBuilder("aqara", "lumi.sensor_occupy.agl1")
.adds(DeviceTemperature)
.adds(OccupancySensing)
.replaces(OppleCluster)
.number(
OppleCluster.AttributeDefs.approach_distance.name,
OppleCluster.cluster_id,
min_value=0,
max_value=6,
step=0.1,
unit=UnitOfLength.METERS,
multiplier=0.01,
device_class=NumberDeviceClass.DISTANCE,
fallback_name="Approach distance",
)
.enum(
OppleCluster.AttributeDefs.motion.name,
AqaraMotion,
OppleCluster.cluster_id,
entity_platform=EntityPlatform.SENSOR,
entity_type=EntityType.STANDARD,
fallback_name="Motion",
)
.sensor(
OppleCluster.AttributeDefs.motion_distance.name,
OppleCluster.cluster_id,
unit=UnitOfLength.METERS,
multiplier=0.01,
device_class=SensorDeviceClass.DISTANCE,
state_class=SensorStateClass.MEASUREMENT,
fallback_name="Motion distance",
)
.enum(
OppleCluster.AttributeDefs.motion_sensitivity.name,
AqaraMotionSensitivity,
OppleCluster.cluster_id,
)
.write_attr_button(
OppleCluster.AttributeDefs.reset_no_presence_status.name,
1,
OppleCluster.cluster_id,
)
.write_attr_button(
OppleCluster.AttributeDefs.restart_device.name,
0,
OppleCluster.cluster_id,
# entity_type=EntityType.DIAGNOSTIC,
fallback_name="Restart device",
)
.add_to_registry()
)
I tried @jeverley quirk at https://github.com/zigpy/zha-device-handlers/issues/3294#issuecomment-2394712555 first because I liked the entites shown in the screenshot but it did not work for me: only the identify button was exposed as entity
https://github.com/klolik/zha-device-handlers/commit/49d66b678ff07f58c0a14e065241b29efea4ec82 is working with my FP1E
I tried @jeverley quirk at https://github.com/zigpy/zha-device-handlers/issues/3294#issuecomment-2394712555 first because I liked the entites shown in the screenshot but it did not work for me: only the identify button was exposed as entity
https://github.com/klolik/zha-device-handlers/commit/49d66b678ff07f58c0a14e065241b29efea4ec82 is working with my FP1E
Thanks for testing, did you get any errors logged at all? Also which version of home assistant core are you running?
Thanks for testing, did you get any errors logged at all? Also which version of home assistant core are you running?
No errors that I could see (xiaomi_miio is causing a lot of noise at the moment, sorry!) core is 2024.9.0
I also tried your v2.0 in https://github.com/zigpy/zha-device-handlers/issues/3294#issuecomment-2381449768, that works!
Thanks for testing, did you get any errors logged at all? Also which version of home assistant core are you running?
No errors that I could see (xiaomi_miio is causing a lot of noise at the moment, sorry!) core is 2024.9.0
I also tried your v2.0 in https://github.com/zigpy/zha-device-handlers/issues/3294#issuecomment-2381449768, that works!
Ahh that would explain it, I think the support for the fallback_name argument was only introduced in 2024.10 (it likely fails on entity creation on previous versions) 🙂
Ahh that would explain it, I think the support for the fallback_name argument was only introduced in 2024.10 (it likely fails on entity creation on previous versions) 🙂
ok, I'll update my HA instance later on in the week and get back to you :+1:
I'm a HA user for years, but I'm not sure where to put this quirk information. I've copied the code, but have no idea where to paste it. Can you guy give me some brief direction on this?
Thanks,
-Scott
I'm a HA user for years, but I'm not sure where to put this quirk information. I've copied the code, but have no idea where to paste it. Can you guy give me some brief direction on this?
Here's a decent write-up on how to set up quirks.
@jeverley your lovely quirks worked perfectly fine, up until just now when updating to HA Core 2024.11.0 seems to have broken something. All entities on the device are now listed as unavailable because they are no longer provided by the integration?! Unfortunately middle of the night here so will have to figure out what went wrong tomorrow and possibly roll back HA Core if required (and even possible)…
I see the same thing, the update broke the functions of the sensor. Everything is unavailable. I tried to re-pair, restart, but no luck.
2024.11.0 Error log
Logger: zhaquirks Source: /usr/local/lib/python3.12/site-packages/zhaquirks/init.py:481 First occurred: 04:52:39 (1 occurrences) Last logged: 04:52:39
Unexpected exception importing custom quirk 'aqara_lumi_sensor_occupy_agl1'
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/zhaquirks/init.py", line 479, in setup
spec.loader.exec_module(module)
File "
Hi all, the revised quirk code below should correct the issue introduced in the latest core release (it had made translation_key mandatory):
"""Quirk for aqara lumi.sensor_occupy.agl1."""
from __future__ import annotations
from typing import Any
from zigpy import types
from zigpy.quirks.v2 import (
NumberDeviceClass,
QuirkBuilder,
SensorDeviceClass,
SensorStateClass,
)
from zigpy.zcl.clusters.general import DeviceTemperature
from zigpy.zcl.clusters.measurement import OccupancySensing
from zigpy.zcl.clusters.security import IasZone
from zigpy.zcl.foundation import BaseAttributeDefs, ZCLAttributeDef
from zhaquirks.xiaomi import XiaomiAqaraE1Cluster
APPROACH_DISTANCE_ATTR_ID = 0x015B # UINT32 The configurable maximum detection distance in millimeters (default 600 = 6 meters).
MOTION_ATTR_ID = 0x0160 # UINT8 Detected motion (0x02 = no movement, 0x03 = large movement, 0x04 = small movement)
MOTION_DISTANCE_ATTR_ID = 0x015F # UINT32 Distance to the detected motion (mm)
MOTION_SENSITIVITY_ATTR_ID = 0x010C # UINT8 The configurable detection sensitivity (0x01 = low, 0x02 = medium, 0x03 = high, default 0x03 = High)
OCCUPANCY_ATTR_ID = 0x0142 # UINT8 Occupancy detected 0x0 = absence, 0x01 = presence
RESET_NO_PRESENCE_STATUS_ATTR_ID = 0x0157 # UINT8 Trigger AI spatial learning
RESTART_DEVICE_ATTR_ID = 0x00E8 # BOOL Trigger device restart
RESET_NO_PRESENCE_STATUS_WRITE_VALUE = 1
RESTART_DEVICE_WRITE_VALUE = 0
class AqaraMotionSensitivity(types.enum8):
"""Aqara motion sensitivity."""
Low = 0x01
Medium = 0x02
High = 0x03
class AqaraMotion(types.enum8):
"""Aqara motion."""
Unknown_0 = 0x00
Unknown_1 = 0x01
Idle = 0x02
Moving = 0x03
Still = 0x04
class OppleCluster(XiaomiAqaraE1Cluster):
"""Aqara manufacturer cluster for the FP1E presence sensor."""
class AttributeDefs(BaseAttributeDefs):
"""Manufacturer specific attributes."""
approach_distance = ZCLAttributeDef(
id=APPROACH_DISTANCE_ATTR_ID,
type=types.uint32_t,
access="rw",
is_manufacturer_specific=True,
)
motion = ZCLAttributeDef(
id=MOTION_ATTR_ID,
type=types.uint8_t,
access="rp",
is_manufacturer_specific=True,
)
motion_distance = ZCLAttributeDef(
id=MOTION_DISTANCE_ATTR_ID,
type=types.uint32_t,
access="rp",
is_manufacturer_specific=True,
)
motion_sensitivity = ZCLAttributeDef(
id=MOTION_SENSITIVITY_ATTR_ID,
type=types.uint8_t,
access="rw",
is_manufacturer_specific=True,
)
occupancy = ZCLAttributeDef(
id=OCCUPANCY_ATTR_ID,
type=types.uint8_t,
access="rp",
is_manufacturer_specific=True,
)
reset_no_presence_status = ZCLAttributeDef(
id=RESET_NO_PRESENCE_STATUS_ATTR_ID,
type=types.uint8_t,
access="w",
is_manufacturer_specific=True,
)
restart_device = ZCLAttributeDef(
id=RESTART_DEVICE_ATTR_ID,
type=types.Bool,
access="w",
is_manufacturer_specific=True,
)
def _update_attribute(self, attrid: int, value: Any) -> None:
super()._update_attribute(attrid, value)
if attrid == OCCUPANCY_ATTR_ID:
self.endpoint.occupancy.update_attribute(
OccupancySensing.AttributeDefs.occupancy.id,
OccupancySensing.Occupancy(value),
)
elif attrid == MOTION_ATTR_ID:
self.endpoint.ias_zone.update_attribute(
IasZone.AttributeDefs.zone_status.id,
IasZone.ZoneStatus(
IasZone.ZoneStatus.Alarm_1 if value == AqaraMotion.Moving else 0
),
)
(
QuirkBuilder("aqara", "lumi.sensor_occupy.agl1")
.friendly_name(model="Presence Sensor FP1E", manufacturer="Aqara")
.adds(DeviceTemperature)
.adds(OccupancySensing)
.adds(
IasZone,
constant_attributes={
IasZone.AttributeDefs.zone_type: IasZone.ZoneType.Motion_Sensor
},
)
.replaces(OppleCluster)
.number(
OppleCluster.AttributeDefs.approach_distance.name,
OppleCluster.cluster_id,
min_value=0,
max_value=6,
step=0.1,
# unit=UnitOfLength.METERS,
multiplier=0.01,
device_class=NumberDeviceClass.DISTANCE,
translation_key="approach_distance",
fallback_name="Approach distance",
)
.sensor(
OppleCluster.AttributeDefs.motion_distance.name,
OppleCluster.cluster_id,
# unit=UnitOfLength.METERS,
multiplier=0.01,
device_class=SensorDeviceClass.DISTANCE,
state_class=SensorStateClass.MEASUREMENT,
translation_key="motion_distance",
fallback_name="Motion distance",
)
.enum(
OppleCluster.AttributeDefs.motion_sensitivity.name,
AqaraMotionSensitivity,
OppleCluster.cluster_id,
translation_key="motion_sensitivity",
fallback_name="Motion sensitivity",
)
.write_attr_button(
OppleCluster.AttributeDefs.reset_no_presence_status.name,
RESET_NO_PRESENCE_STATUS_WRITE_VALUE,
OppleCluster.cluster_id,
translation_key="reset_no_presence_status",
fallback_name="Presence status reset",
)
.write_attr_button(
OppleCluster.AttributeDefs.restart_device.name,
RESTART_DEVICE_WRITE_VALUE,
OppleCluster.cluster_id,
# entity_type=EntityType.DIAGNOSTIC,
translation_key="restart_device",
fallback_name="Restart device",
)
.add_to_registry()
)
I can confirm that your update fixed my 2024.11.0 connectivity issue. Thanks!
@jeverley if you haven't yet, you should submit this quirk to be included with ZHA.
Problem description
Everything on this device is implemented on the apple cluster 0xfcc0
Solution description
Implement the proper quirk providing support for the device
Device signature
Device signature
```json { "node_descriptor": { "logical_type": 2, "complex_descriptor_available": 0, "user_descriptor_available": 0, "reserved": 0, "aps_flags": 0, "frequency_band": 8, "mac_capability_flags": 140, "manufacturer_code": 4660, "maximum_buffer_size": 108, "maximum_incoming_transfer_size": 127, "server_mask": 11264, "maximum_outgoing_transfer_size": 127, "descriptor_capability_field": 0 }, "endpoints": { "1": { "profile_id": "0x0104", "device_type": "0xfff0", "input_clusters": [ "0x0000", "0x0003", "0xfcc0" ], "output_clusters": [ "0x0003", "0x0019" ] } }, "manufacturer": "aqara", "model": "lumi.sensor_occupy.agl1", "class": "zigpy.device.Device" } ```