-
Notifications
You must be signed in to change notification settings - Fork 725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for Tuya TS1201 IR blaster #2336
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## dev #2336 +/- ##
==========================================
+ Coverage 88.33% 88.48% +0.15%
==========================================
Files 304 305 +1
Lines 9497 9624 +127
==========================================
+ Hits 8389 8516 +127
Misses 1108 1108 ☔ View full report in Codecov by Sentry. |
@TheJulianJES , I don't know what to test and how to do it. |
@TheJulianJES @ferehcarb did the tests pass on the 6 May per the updated checks? Keen to see this go in so I can use my IR blaster which needs this code to work. Cheers for putting in the effort so the community benefits btw. |
Hi, test for signature is already included in this PR, missing tests are hilighted by codecov, but I don't know what to test. |
As I would love to see it merged, I'm thinking about donating some money to have it finished (as I understand the tests don't cover all new code). If you need I can even order Moes UFO-R11 to you so you can test it onsite. Is there any nice way to find a willing developer and finalize it? |
I would love this aswell! |
Well, I would love this too ! Everything is working fine with ZHA, and I don't really want to move to Z2MQTT. |
zhaquirks/tuya/ts1201.py
Outdated
attributes = {0x0000: ("last_learned_ir_code", t.CharacterString, True)} | ||
|
||
server_commands = { | ||
0x00: foundation.ZCLCommandDef( | ||
"data", | ||
schema={"data": Bytes}, | ||
direction=foundation.Direction.Server_to_Client, | ||
is_manufacturer_specific=True, | ||
), | ||
0x01: foundation.ZCLCommandDef( | ||
"IRLearn", | ||
schema={"on_off": t.Bool}, | ||
direction=foundation.Direction.Server_to_Client, | ||
is_manufacturer_specific=True, | ||
), | ||
0x02: foundation.ZCLCommandDef( | ||
"IRSend", | ||
schema={"code": t.CharacterString}, | ||
direction=foundation.Direction.Server_to_Client, | ||
is_manufacturer_specific=True, | ||
), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, this would be switched to the new zigpy AttributeDefs
and ServerCommandDefs
(similar to this example).
zhaquirks/tuya/ts1201.py
Outdated
**kwargs: Any, | ||
): | ||
"""Override the default Cluster command.""" | ||
if command_id == 1: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new ServerCommandDefs
could be used here (name.id
) instead of repeating the command id again here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's still on my eventual todo-list to try and add tests for this, but I just haven't found the time for it yet.
Those tests should try to send an "IR command" (that the quirk then converts) and can just test if the "Zigbee messages" (packets) are as expected.
A general orientation can be the Tuya test files. (Maybe something similar to like test_singleswitch_requests
)
So, the code in this quirk should really be tested.
Add last_learned_ir_code attribute
ans speed up IR learning ans sending.
_TZ3290_acv1iuslxi3shaaj is an usb powered device, move model_info to the corresponding customdevice.
Enumeration is not available in HA latest release. (2023.6.3) Needs zigpy >= 0.56 This reverts commit becee73.
Same here Logger: zhaquirks
Source: /usr/local/lib/python3.12/site-packages/zhaquirks/__init__.py:461
First occurred: 9:14:15 AM (1 occurrences)
Last logged: 9:14:15 AM
Unexpected exception importing custom quirk 'ts1201'
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/zhaquirks/__init__.py", line 459, in setup
spec.loader.exec_module(module)
File "<frozen importlib._bootstrap_external>", line 995, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "/config/custom_zha_quirks/ts1201.py", line 377, in <module>
class ZosungIRBlaster(CustomDevice):
File "/config/custom_zha_quirks/ts1201.py", line 382, in ZosungIRBlaster
last_learned_ir_code = t.CharacterString()
^^^^^^^^^^^^^^^^^^^
TypeError: CharacterString.__new__() missing 1 required positional argument: 'value' |
Here is a fixed quirk. """Tuya TS1201 IR blaster.
Heavily inspired by work from @mak-42
https://github.com/Koenkk/zigbee-herdsman-converters/blob/9d5e7b902479582581615cbfac3148d66d4c675c/lib/zosung.js
"""
import base64
from typing import Any, Final, Optional, Union
from zigpy.profiles import zgp, zha
from zigpy.quirks import CustomCluster, CustomDevice
import zigpy.types as t
from zigpy.zcl import BaseAttributeDefs, BaseCommandDefs, foundation
from zigpy.zcl.clusters.general import (
Basic,
GreenPowerProxy,
Groups,
Identify,
OnOff,
Ota,
PowerConfiguration,
Scenes,
Time,
)
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
class Bytes(bytes):
"""Bytes serializable class."""
def serialize(self):
"""Serialize Bytes."""
return self
@classmethod
def deserialize(cls, data):
"""Deserialize Bytes."""
return cls(data), b""
class ZosungIRControl(CustomCluster):
"""Zosung IR Control Cluster (0xE004)."""
name = "Zosung IR Control Cluster"
cluster_id = 0xE004
ep_attribute = "zosung_ircontrol"
class AttributeDefs(BaseAttributeDefs):
"""Attribute definitions."""
last_learned_ir_code: Final = foundation.ZCLAttributeDef(
id=0x0000, type=t.CharacterString, access="r", mandatory=True
)
class ServerCommandDefs(BaseCommandDefs):
"""Server command definitions."""
data: Final = foundation.ZCLCommandDef(
id=0x00,
schema={"data": Bytes},
direction=foundation.Direction.Server_to_Client,
is_manufacturer_specific=True,
)
IRLearn: Final = foundation.ZCLCommandDef(
id=0x01,
schema={"on_off": t.Bool},
direction=foundation.Direction.Server_to_Client,
is_manufacturer_specific=True,
)
IRSend: Final = foundation.ZCLCommandDef(
id=0x02,
schema={"code": t.CharacterString},
direction=foundation.Direction.Server_to_Client,
is_manufacturer_specific=True,
)
async def read_attributes(
self, attributes, allow_cache=False, only_cache=False, manufacturer=None
):
"""Read attributes ZCL foundation command."""
if (
self.AttributeDefs.last_learned_ir_code.id in attributes
or "last_learned_ir_code" in attributes
):
return {0: self.endpoint.device.last_learned_ir_code}, {}
else:
return {}, {0: foundation.Status.UNSUPPORTED_ATTRIBUTE}
async def command(
self,
command_id: Union[foundation.GeneralCommand, int, t.uint8_t],
*args,
manufacturer: Optional[Union[int, t.uint16_t]] = None,
expect_reply: bool = True,
tsn: Optional[Union[int, t.uint8_t]] = None,
**kwargs: Any,
):
"""Override the default Cluster command."""
if command_id == self.ServerCommandDefs.IRLearn.id:
if kwargs["on_off"]:
cmd_args = {Bytes(b'{"study":0}')}
else:
cmd_args = {Bytes(b'{"study":1}')}
return await super().command(
0x00,
*cmd_args,
manufacturer=manufacturer,
expect_reply=True,
tsn=tsn,
)
elif command_id == self.ServerCommandDefs.IRSend.id:
ir_msg = f"""{{"key_num":1,"delay":300,"key1":{{"num":1,"freq":38000,"type":1,"key_code":"{kwargs["code"]}"}}}}"""
self.debug("ir_msg to send: %s", ir_msg)
seq = self.endpoint.device.next_seq()
self.endpoint.device.ir_msg_to_send = {seq: ir_msg}
self.create_catching_task(
self.endpoint.zosung_irtransmit.command(
0x00,
seq=seq,
length=len(ir_msg),
unk1=0x00000000,
clusterid=0xE004,
unk2=0x01,
cmd=0x02,
unk3=0x0000,
expect_reply=False,
tsn=tsn,
)
)
else:
return await super().command(
command_id,
*args,
manufacturer=manufacturer,
expect_reply=expect_reply,
tsn=tsn,
)
class ZosungIRTransmit(CustomCluster):
"""Zosung IR Transmit Cluster (0xED00)."""
name = "Zosung IR Transmit Cluster"
cluster_id = 0xED00
ep_attribute = "zosung_irtransmit"
current_position = 0
msg_length = 0
ir_msg = []
class ServerCommandDefs(BaseCommandDefs):
"""Server command definitions."""
receive_ir_frame_00: Final = foundation.ZCLCommandDef(
id=0x00,
schema={
"seq": t.uint16_t,
"length": t.uint32_t,
"unk1": t.uint32_t,
"clusterid": t.uint16_t,
"unk2": t.uint8_t,
"cmd": t.uint8_t,
"unk3": t.uint16_t,
},
direction=foundation.Direction.Server_to_Client,
is_manufacturer_specific=True,
)
receive_ir_frame_01: Final = foundation.ZCLCommandDef(
id=0x01,
schema={
"zero": t.uint8_t,
"seq": t.uint16_t,
"length": t.uint32_t,
"unk1": t.uint32_t,
"clusterid": t.uint16_t,
"unk2": t.uint8_t,
"cmd": t.uint8_t,
"unk3": t.uint16_t,
},
direction=foundation.Direction.Server_to_Client,
is_manufacturer_specific=True,
)
receive_ir_frame_02: Final = foundation.ZCLCommandDef(
id=0x02,
schema={
"seq": t.uint16_t,
"position": t.uint32_t,
"maxlen": t.uint8_t,
},
direction=foundation.Direction.Server_to_Client,
is_manufacturer_specific=True,
)
receive_ir_frame_03: Final = foundation.ZCLCommandDef(
id=0x03,
schema={
"zero": t.uint8_t,
"seq": t.uint16_t,
"position": t.uint32_t,
"msgpart": t.LVBytes,
"msgpartcrc": t.uint8_t,
},
direction=foundation.Direction.Client_to_Server,
is_manufacturer_specific=False,
)
receive_ir_frame_04: Final = foundation.ZCLCommandDef(
id=0x04,
schema={
"zero0": t.uint8_t,
"seq": t.uint16_t,
"zero1": t.uint16_t,
},
direction=foundation.Direction.Server_to_Client,
is_manufacturer_specific=True,
)
receive_ir_frame_05: Final = foundation.ZCLCommandDef(
id=0x05,
schema={
"seq": t.uint16_t,
"zero": t.uint16_t,
},
direction=foundation.Direction.Server_to_Client,
is_manufacturer_specific=True,
)
class ClientCommandDefs(BaseCommandDefs):
"""Client command definitions."""
resp_ir_frame_03: Final = foundation.ZCLCommandDef(
id=0x03,
schema={
"zero": t.uint8_t,
"seq": t.uint16_t,
"position": t.uint32_t,
"msgpart": t.LVBytes,
"msgpartcrc": t.uint8_t,
},
direction=foundation.Direction.Client_to_Server,
is_manufacturer_specific=False,
)
resp_ir_frame_05: Final = foundation.ZCLCommandDef(
id=0x05,
schema={
"seq": t.uint16_t,
"zero": t.uint16_t,
},
direction=foundation.Direction.Server_to_Client,
is_manufacturer_specific=True,
)
def handle_cluster_request(
self,
hdr: foundation.ZCLHeader,
args: list[Any],
*,
dst_addressing: Optional[
Union[t.Addressing.Group, t.Addressing.IEEE, t.Addressing.NWK]
] = None,
):
"""Handle a cluster request."""
# send default response, so avoid repeated zclframe from device
if not hdr.frame_control.disable_default_response:
self.debug("Send default response")
self.send_default_rsp(hdr, status=foundation.Status.SUCCESS)
if hdr.command_id == self.ServerCommandDefs.receive_ir_frame_00.id:
self.debug("hdr.command_id == 0x00")
self.current_position = 0
self.ir_msg.clear()
self.msg_length = args.length
cmd_01_args = {
"zero": 0,
"seq": args.seq,
"length": args.length,
"unk1": args.unk1,
"clusterid": args.clusterid,
"unk2": args.unk2,
"cmd": args.cmd,
"unk3": args.unk3,
}
self.create_catching_task(
super().command(0x01, **cmd_01_args, expect_reply=True)
)
cmd_02_args = {"seq": args.seq, "position": 0, "maxlen": 0x38}
self.create_catching_task(
super().command(0x02, **cmd_02_args, expect_reply=True)
)
elif hdr.command_id == self.ServerCommandDefs.receive_ir_frame_01.id:
self.debug("IR-Message-Code01 received, sequence: %s", args.seq)
self.debug("msg to send: %s", self.endpoint.device.ir_msg_to_send[args.seq])
elif hdr.command_id == self.ServerCommandDefs.receive_ir_frame_02.id:
position = args.position
seq = args.seq
maxlen = args.maxlen
irmsg = self.endpoint.device.ir_msg_to_send[seq]
msgpart = irmsg[position : position + maxlen]
calculated_crc = 0
for x in msgpart:
calculated_crc = (calculated_crc + ord(x)) % 0x100
self.debug(
"hdr.command_id == 0x02 ; msgcrc=%s ; position=%s ; msgpart=%s",
calculated_crc,
position,
msgpart,
)
cmd_03_args = {
"zero": 0,
"seq": seq,
"position": position,
"msgpart": msgpart.encode("utf-8"),
"msgpartcrc": calculated_crc,
}
self.create_catching_task(
super().command(0x03, **cmd_03_args, expect_reply=True)
)
elif hdr.command_id == self.ServerCommandDefs.receive_ir_frame_03.id:
msg_part_crc = args.msgpartcrc
calculated_crc = 0
for x in args.msgpart:
calculated_crc = (calculated_crc + x) % 0x100
self.debug(
"hdr.command_id == 0x03 ; msgcrc=%s ; calculated_crc=%s ; position=%s",
msg_part_crc,
calculated_crc,
args.position,
)
self.ir_msg[args.position :] = args.msgpart
if args.position + len(args.msgpart) < self.msg_length:
cmd_02_args = {
"seq": args.seq,
"position": args.position + len(args.msgpart),
"maxlen": 0x38,
}
self.create_catching_task(
super().command(0x02, **cmd_02_args, expect_reply=False)
)
else:
self.debug("Ir message totally received.")
cmd_04_args = {"zero0": 0, "seq": args.seq, "zero1": 0}
self.create_catching_task(
super().command(0x04, **cmd_04_args, expect_reply=False)
)
elif hdr.command_id == self.ServerCommandDefs.receive_ir_frame_04.id:
seq = args.seq
self.debug("Command 0x04: IRCode has been successfully sent. (seq:%s)", seq)
cmd_05_args = {"seq": seq, "zero": 0}
self.create_catching_task(
super().command(0x05, **cmd_05_args, expect_reply=False)
)
elif hdr.command_id == self.ServerCommandDefs.receive_ir_frame_05.id:
self.endpoint.device.last_learned_ir_code = base64.b64encode(
bytes(self.ir_msg)
).decode()
self.info(
"Command 0x05: Ir message really totally received: %s",
self.endpoint.device.last_learned_ir_code,
)
self.debug("Stopping learning mode on device.")
self.create_catching_task(
self.endpoint.zosung_ircontrol.command(
0x01, on_off=False, expect_reply=False
)
)
else:
self.debug("hdr.command_id: %s", hdr.command_id)
class ZosungIRBlaster(CustomDevice):
"""Zosung IR Blaster."""
seq = -1
ir_msg_to_send = {}
last_learned_ir_code = t.CharacterString(value="")
def __init__(self, *args, **kwargs):
"""Init device."""
self.seq = 0
super().__init__(*args, **kwargs)
def next_seq(self):
"""Next local sequence."""
self.seq = (self.seq + 1) % 0x10000
return self.seq
signature = {
# "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)",
# input_clusters=[0x0000, 0x0001, 0x0003, 0x0004, 0x0005, 0x0006,
# 0xe004, 0xed00]
# output_clusters=[0x000a, 0x0019]
# <SimpleDescriptor endpoint=1, profile=260, device_type=61440
# device_version=1
# input_clusters=[0, 1, 3, 4, 5, 6, 57348, 60672]
# output_clusters=[10, 25]>
MODELS_INFO: [
("_TZ3290_ot6ewjvmejq5ekhl", "TS1201"),
("_TZ3290_j37rooaxrcdcqo5n", "TS1201"),
],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: 0xF000,
INPUT_CLUSTERS: [
Basic.cluster_id,
ZosungIRTransmit.cluster_id,
ZosungIRControl.cluster_id,
Groups.cluster_id,
Identify.cluster_id,
OnOff.cluster_id,
PowerConfiguration.cluster_id,
Scenes.cluster_id,
],
OUTPUT_CLUSTERS: [
Time.cluster_id,
Ota.cluster_id,
],
},
},
}
replacement = {
ENDPOINTS: {
1: {
INPUT_CLUSTERS: [
Basic.cluster_id,
ZosungIRTransmit,
ZosungIRControl,
Groups.cluster_id,
Identify.cluster_id,
OnOff.cluster_id,
PowerConfiguration.cluster_id,
Scenes.cluster_id,
],
OUTPUT_CLUSTERS: [
Time.cluster_id,
Ota.cluster_id,
],
},
},
}
class ZosungIRBlaster_ZS06(ZosungIRBlaster):
"""Zosung IR Blaster ZS06."""
signature = {
# "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress|RxOnWhenIdle|MainsPowered|FullFunctionDevice: 142>, manufacturer_code=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=False, *is_full_function_device=True, *is_mains_powered=True, *is_receiver_on_when_idle=True, *is_router=True, *is_security_capable=False)",
# <SimpleDescriptor endpoint=1, profile=260, device_type=61440
# device_version=1
# input_clusters=[0, 3, 4, 5, 6, 57348, 60672]
# output_clusters=[10, 25]>
# <SimpleDescriptor endpoint=242, profile=41440, device_type=97
# device_version=1
# input_clusters=[]
# output_clusters=[33]>
MODELS_INFO: [
("_TZ3290_7v1k4vufotpowp9z", "TS1201"),
("_TZ3290_acv1iuslxi3shaaj", "TS1201"),
("_TZ3290_gnl5a6a5xvql7c2a", "TS1201"),
],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: 0xF000,
INPUT_CLUSTERS: [
Basic.cluster_id,
ZosungIRTransmit.cluster_id,
ZosungIRControl.cluster_id,
Groups.cluster_id,
Identify.cluster_id,
OnOff.cluster_id,
Scenes.cluster_id,
],
OUTPUT_CLUSTERS: [
Time.cluster_id,
Ota.cluster_id,
],
},
242: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [
GreenPowerProxy.cluster_id,
],
},
},
}
replacement = {
ENDPOINTS: {
1: {
INPUT_CLUSTERS: [
Basic.cluster_id,
ZosungIRTransmit,
ZosungIRControl,
Groups.cluster_id,
Identify.cluster_id,
OnOff.cluster_id,
Scenes.cluster_id,
],
OUTPUT_CLUSTERS: [
Time.cluster_id,
Ota.cluster_id,
],
},
242: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [
GreenPowerProxy.cluster_id,
],
},
},
} Download |
Hi @ferehcarb Have you looked in to the keyerror issue yet? I get a lot if these at times, in particular when I'm testing my IR-codes and send a lot of them without delays betwen or only short delays like 1 sec. I've got an automation that runs through all combinations of hvac-modes, presets, fan-modes and temperatures for my climate configuration. The first 50-100 calls (zha.issue_zigbee_cluster_command) normally goes well, but then the keyerrors starts to show up, and almost for every call: Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/bellows/ezsp/__init__.py", line 505, in handle_callback
handler(*args)
File "/usr/local/lib/python3.12/site-packages/bellows/zigbee/application.py", line 475, in ezsp_callback_handler
self._handle_frame(
File "/usr/local/lib/python3.12/site-packages/bellows/zigbee/application.py", line 558, in _handle_frame
self.packet_received(
File "/usr/local/lib/python3.12/site-packages/zigpy/application.py", line 1024, in packet_received
return device.packet_received(packet)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/device.py", line 497, in packet_received
endpoint.handle_message(
File "/usr/local/lib/python3.12/site-packages/zigpy/endpoint.py", line 247, in handle_message
handler(hdr, args, dst_addressing=dst_addressing)
File "/usr/local/lib/python3.12/site-packages/zigpy/zcl/__init__.py", line 430, in handle_message
self.handle_cluster_request(hdr, args, dst_addressing=dst_addressing)
File "/config/custom_zha_quirks/ts1201.py", line 304, in handle_cluster_request
irmsg = self.endpoint.device.ir_msg_to_send[seq]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
KeyError: 75 It seems like something is building up and then start to fail. |
I can't send commands even sending one code I get the error the item it totally unusable Error doing job: Exception in callback SerialTransport._read_ready() (None)
Traceback (most recent call last):
File "/usr/local/lib/python3.12/asyncio/events.py", line 88, in _run
self._context.run(self._callback, *self._args)
File "/usr/local/lib/python3.12/site-packages/serial_asyncio_fast/__init__.py", line 146, in _read_ready
self._protocol.data_received(data)
File "/usr/local/lib/python3.12/site-packages/zigpy_xbee/uart.py", line 95, in data_received
self.frame_received(frame)
File "/usr/local/lib/python3.12/site-packages/zigpy_xbee/uart.py", line 103, in frame_received
self._api.frame_received(frame)
File "/usr/local/lib/python3.12/site-packages/zigpy_xbee/api.py", line 392, in frame_received
getattr(self, f"_handle_{command}")(*data)
File "/usr/local/lib/python3.12/site-packages/zigpy_xbee/api.py", line 451, in _handle_explicit_rx_indicator
self._app.handle_rx(ieee, nwk, src_ep, dst_ep, cluster, profile, rx_opts, data)
File "/usr/local/lib/python3.12/site-packages/zigpy_xbee/zigbee/application.py", line 382, in handle_rx
self.packet_received(
File "/usr/local/lib/python3.12/site-packages/zigpy/application.py", line 1024, in packet_received
return device.packet_received(packet)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/device.py", line 497, in packet_received
endpoint.handle_message(
File "/usr/local/lib/python3.12/site-packages/zigpy/endpoint.py", line 247, in handle_message
handler(hdr, args, dst_addressing=dst_addressing)
File "/usr/local/lib/python3.12/site-packages/zigpy/zcl/__init__.py", line 430, in handle_message
self.handle_cluster_request(hdr, args, dst_addressing=dst_addressing)
File "/config/custom_zha_quirks/ts1201.py", line 305, in handle_cluster_request
irmsg = self.endpoint.device.ir_msg_to_send[seq]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
KeyError: 3 |
Perfect fix for me; dropped in, reloaded, all my old automations work again. <3 |
Quickly went over the last 3 commits from @ferehcarb and it looks like the inline codespell has been applied as well as some formatting fixes (instead of the ruff/codespell hack from before). @TheJulianJES do you have any other feedback? |
I'm running the dev branch for a week now and seems to be working fine. Weirdly enough some of the IR commands that I had previously "learned" seem to not work anymore and I had to re-learn them. That's when I figured the IR command changed while the remote it learned the command from is the same. FWIW below the old and new IR command as example:
|
This replaces the zigpy logger with a custom quirks logger whilst also logging the IEEE of the device. The formatting is also slightly changed to split long lines into multiple.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All right, I think we can try this. I made some small adjustments that shouldn't break anything, mostly just logging and slightly polishing it up a bit.
Fake clusters aren't ideal, but most Tuya devices already use them and there's nothing we can do to avoid them at moment, so this should be fine.
Thanks for all your work!
for more information, see https://pre-commit.ci
@TheJulianJES any luck with looking at #2336 (comment) this quirk still does not work properly i can use it to learn codes but it never seems to send any |
TS1201 is an IR blaster, this quirk is able to send and to receive raw IR codes using Zigbee commands.
Received IR code is available in a Zigbee attribute.
Device is also known as:
Should fix #1687 fix #1782 and fix #1955