Skip to content

Commit 09ac057

Browse files
author
Marco Berghoff
committed
Copy code from #2109
1 parent aeca76d commit 09ac057

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed

zhaquirks/xiaomi/aqara/thermostat_agl001.py

+173
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from functools import reduce
55
import math
66
import struct
7+
import time
78
from typing import Any
89

910
from zigpy.profiles import zha
@@ -48,6 +49,10 @@
4849
SENSOR = 0x027E
4950
BATTERY_PERCENTAGE = 0x040A
5051

52+
SENSOR_TEMP = 0x1392 # Fake address to pass external sensor temperature
53+
SENSOR_ATTR = 0xFFF2
54+
SENSOR_ATTR_NAME = "sensor_attr"
55+
5156
XIAOMI_CLUSTER_ID = 0xFCC0
5257

5358
DAYS_MAP = {
@@ -379,6 +384,8 @@ class AqaraThermostatSpecificCluster(XiaomiAqaraE1Cluster):
379384
SCHEDULE_SETTINGS: ("schedule_settings", ScheduleSettings, True),
380385
SENSOR: ("sensor", t.uint8_t, True),
381386
BATTERY_PERCENTAGE: ("battery_percentage", t.uint8_t, True),
387+
SENSOR_TEMP: ("sensor_temp", t.uint32_t, True),
388+
SENSOR_ATTR: (SENSOR_ATTR_NAME, t.LVBytes, True),
382389
}
383390
)
384391

@@ -393,6 +400,172 @@ def _update_attribute(self, attrid, value):
393400
)
394401
super()._update_attribute(attrid, value)
395402

403+
def aqaraHeader(
404+
self, counter: int, params: bytearray, action: int
405+
) -> bytearray:
406+
"""Create Aqara header for setting external sensor."""
407+
header = bytes([0xAA, 0x71, len(params) + 3, 0x44, counter])
408+
integrity = 512 - sum(header)
409+
410+
return header + bytes([integrity, action, 0x41, len(params)])
411+
412+
def _float_to_hex(self, f):
413+
"""Convert float to hex."""
414+
return hex(struct.unpack("<I", struct.pack("<f", f))[0])
415+
416+
async def write_attributes(
417+
self, attributes: dict[str | int, Any], manufacturer: int | None = None
418+
) -> list:
419+
"""Write attributes to device with internal 'attributes' validation."""
420+
sensor = bytearray.fromhex("00158d00019d1b98")
421+
attrs = {}
422+
423+
for attr, value in attributes.items():
424+
# implemented with help from https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/devices/xiaomi.js
425+
426+
if attr == SENSOR_TEMP:
427+
# set external sensor temp. this function expect value to be passed multiplied by 100
428+
temperatureBuf = bytearray.fromhex(
429+
self._float_to_hex(round(float(value)))[2:]
430+
)
431+
432+
params = sensor
433+
params += bytes([0x00, 0x01, 0x00, 0x55])
434+
params += temperatureBuf
435+
436+
attrs = {}
437+
attrs[SENSOR_ATTR_NAME] = (
438+
self.aqaraHeader(0x12, params, 0x05) + params
439+
)
440+
441+
elif attr == SENSOR:
442+
# set internal/external temperature sensor
443+
device = bytearray.fromhex(
444+
("%s" % (self.endpoint.device.ieee)).replace(":", "")
445+
)
446+
timestamp = bytes(
447+
reversed(t.uint32_t(int(time.time())).serialize())
448+
)
449+
450+
if value == 0:
451+
# internal sensor
452+
params1 = timestamp
453+
params1 += bytes([0x3D, 0x05])
454+
params1 += device
455+
params1 += bytes(
456+
[
457+
0x00,
458+
0x00,
459+
0x00,
460+
0x00,
461+
0x00,
462+
0x00,
463+
0x00,
464+
0x00,
465+
0x00,
466+
0x00,
467+
0x00,
468+
0x00,
469+
]
470+
)
471+
472+
params2 = timestamp
473+
params2 += bytes([0x3D, 0x04])
474+
params2 += device
475+
params2 += bytes(
476+
[
477+
0x00,
478+
0x00,
479+
0x00,
480+
0x00,
481+
0x00,
482+
0x00,
483+
0x00,
484+
0x00,
485+
0x00,
486+
0x00,
487+
0x00,
488+
0x00,
489+
]
490+
)
491+
492+
attrs1 = {}
493+
attrs1[SENSOR_ATTR_NAME] = (
494+
self.aqaraHeader(0x12, params1, 0x04) + params1
495+
)
496+
attrs[SENSOR_ATTR_NAME] = (
497+
self.aqaraHeader(0x13, params2, 0x04) + params2
498+
)
499+
500+
result = await super().write_attributes(attrs1, manufacturer)
501+
else:
502+
# external sensor
503+
params1 = timestamp
504+
params1 += bytes([0x3D, 0x04])
505+
params1 += device
506+
params1 += sensor
507+
params1 += bytes([0x00, 0x01, 0x00, 0x55])
508+
params1 += bytes(
509+
[
510+
0x13,
511+
0x0A,
512+
0x02,
513+
0x00,
514+
0x00,
515+
0x64,
516+
0x04,
517+
0xCE,
518+
0xC2,
519+
0xB6,
520+
0xC8,
521+
]
522+
)
523+
params1 += bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3D])
524+
params1 += bytes([0x64])
525+
params1 += bytes([0x65])
526+
527+
params2 = timestamp
528+
params2 += bytes([0x3D, 0x05])
529+
params2 += device
530+
params2 += sensor
531+
params2 += bytes([0x08, 0x00, 0x07, 0xFD])
532+
params2 += bytes(
533+
[
534+
0x16,
535+
0x0A,
536+
0x02,
537+
0x0A,
538+
0xC9,
539+
0xE8,
540+
0xB1,
541+
0xB8,
542+
0xD4,
543+
0xDA,
544+
0xCF,
545+
0xDF,
546+
0xC0,
547+
0xEB,
548+
]
549+
)
550+
params2 += bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3D])
551+
params2 += bytes([0x04])
552+
params2 += bytes([0x65])
553+
554+
attrs1 = {}
555+
attrs1[SENSOR_ATTR_NAME] = (
556+
self.aqaraHeader(0x12, params1, 0x02) + params1
557+
)
558+
attrs[SENSOR_ATTR_NAME] = (
559+
self.aqaraHeader(0x13, params2, 0x02) + params2
560+
)
561+
562+
result = await super().write_attributes(attrs1, manufacturer)
563+
else:
564+
attrs[attr] = value
565+
566+
result = await super().write_attributes(attrs, manufacturer)
567+
return result
568+
396569

397570
class AGL001(XiaomiCustomDevice):
398571
"""Aqara E1 Radiator Thermostat (AGL001) Device."""

0 commit comments

Comments
 (0)