Skip to content

Conversation

candeodevelopment
Copy link
Contributor

Proposed change

Adds support for the following Candeo devices:

C-ZB-LC20-Dim / C-ZB-LC20-DIM
C-ZB-LC20-CCT
C-ZB-LC20-RGB
C-ZB-LC20-RGBW
C-ZB-LC20-RGBCCT

C-ZB-RD1
C-ZB-RD1P-DIM
C-ZB-RD1P-REM
C-ZB-RD1P-DPM

Additional information

@TheJulianJES would you mind giving us some pointers should there be any need for unit tests for these?

Checklist

  • The changes are tested and work correctly
  • pre-commit checks pass / the code has been formatted using Black
  • Tests have been added to verify that the new code works

candeodevelopment and others added 2 commits August 5, 2025 10:55
…ry dimmer switches

Adds support for the following Candeo devices:

C-ZB-LC20-Dim / C-ZB-LC20-DIM
C-ZB-LC20-CCT
C-ZB-LC20-RGB
C-ZB-LC20-RGBW
C-ZB-LC20-RGBCCT

C-ZB-RD1
C-ZB-RD1P-DIM
C-ZB-RD1P-REM
C-ZB-RD1P-DPM

@TheJulianJES would you mind giving us some pointers should there be any need for unit tests for these?
Copy link

codecov bot commented Aug 5, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.15%. Comparing base (ad0dc81) to head (d4faed3).
⚠️ Report is 25 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #4234      +/-   ##
==========================================
+ Coverage   92.05%   92.15%   +0.10%     
==========================================
  Files         350      360      +10     
  Lines       11602    11963     +361     
==========================================
+ Hits        10680    11025     +345     
- Misses        922      938      +16     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@puddly
Copy link
Contributor

puddly commented Aug 5, 2025

You can remove direction= from all of the command definitions, it's not needed.

@candeodevelopment
Copy link
Contributor Author

@puddly I thought direction was a required field, If I remove it then I get an error:

2025-08-06 09:54:53.961 DEBUG (bellows.thread_0) [bellows.ash] Received data 3747b1a97d312a15b658924a27ab1593499c03e767c1a99874fec7438af8127d
2025-08-06 09:54:53.969 DEBUG (bellows.thread_0) [bellows.ash] Received data 387e
2025-08-06 09:54:53.970 DEBUG (bellows.thread_0) [bellows.ash] Received frame DataFrame(frm_num=3, re_tx=0, ack_num=7, ezsp_frame=b'\x05\x90\x01E\x00\x00\x04\x01\x06\x00\x02\x01@\x01\x00\x00M\xc0\xcc,g\xff\xff\x03\x01 \x03\x04')
2025-08-06 09:54:53.971 DEBUG (bellows.thread_0) [bellows.ash] Sending frame AckFrame(res=0, ncp_ready=0, ack_num=4) + FLAG
2025-08-06 09:54:53.971 DEBUG (bellows.thread_0) [bellows.ash] Sending data 8430fc7e
2025-08-06 09:54:53.979 DEBUG (MainThread) [bellows.ezsp.protocol] Received command incomingMessageHandler: {'type': <EmberIncomingMessageType.INCOMING_UNICAST: 0>, 'apsFrame': EmberApsFrame(profileId=260, clusterId=6, sourceEndpoint=2, destinationEndpoint=1, options=<EmberApsOption.APS_OPTION_RETRY|APS_OPTION_ENABLE_ROUTE_DISCOVERY: 320>, groupId=0, sequence=77), 'lastHopLqi': 192, 'lastHopRssi': -52, 'sender': 0x672C, 'bindingIndex': 255, 'addressIndex': 255, 'messageContents': b'\x01 \x03'}
2025-08-06 09:54:53.980 DEBUG (MainThread) [bellows.ezsp.protocol] Frame contains trailing data: b'\x04'
2025-08-06 09:54:53.981 DEBUG (MainThread) [bellows.zigbee.application] Received incomingMessageHandler frame with [<EmberIncomingMessageType.INCOMING_UNICAST: 0>, EmberApsFrame(profileId=260, clusterId=6, sourceEndpoint=2, destinationEndpoint=1, options=<EmberApsOption.APS_OPTION_RETRY|APS_OPTION_ENABLE_ROUTE_DISCOVERY: 320>, groupId=0, sequence=77), 192, -52, 0x672C, 255, 255, b'\x01 \x03']
2025-08-06 09:54:53.983 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(timestamp=datetime.datetime(2025, 8, 6, 8, 54, 53, 983650, tzinfo=datetime.timezone.utc), priority=0, src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x672C), src_ep=2, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=77, profile_id=260, cluster_id=6, data=Serialized[b'\x01 \x03'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=192, rssi=-52)
2025-08-06 09:54:53.991 DEBUG (MainThread) [zigpy.zcl] [0x672C:2:0x0006] Received ZCL frame: b'\x01 \x03'
2025-08-06 09:54:53.997 DEBUG (MainThread) [zigpy.zcl] [0x672C:2:0x0006] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl<0x01>(frame_type=<FrameType.CLUSTER_COMMAND: 1>, is_manufacturer_specific=0, direction=<Direction.Client_to_Server: 0>, disable_default_response=0, reserved=0, *is_cluster=True, *is_general=False), tsn=32, command_id=3, *direction=<Direction.Client_to_Server: 0>)
2025-08-06 09:54:54.005 DEBUG (MainThread) [zigpy.device] [0x672c] Failed to parse packet ZigbeePacket(timestamp=datetime.datetime(2025, 8, 6, 8, 54, 53, 983650, tzinfo=datetime.timezone.utc), priority=0, src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x672C), src_ep=2, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=77, profile_id=260, cluster_id=6, data=Serialized[b'\x01 \x03'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=192, rssi=-52)
Traceback (most recent call last):
File "/usr/local/lib/python3.13/site-packages/zigpy/device.py", line 488, in packet_received
hdr, args = endpoint.deserialize(packet.cluster_id, data)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/zigpy/endpoint.py", line 228, in deserialize
return cluster.deserialize(data)
~~~~~~~~~~~~~~~~~~~^^^^^^
File "/usr/local/lib/python3.13/site-packages/zigpy/zcl/init.py", line 292, in deserialize
hdr.frame_control = hdr.frame_control.replace(direction=command.direction)
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/zigpy/types/struct.py", line 344, in replace
instance = type(self)(**d)
File "/usr/local/lib/python3.13/site-packages/zigpy/types/struct.py", line 511, in new
underlying_int, rest = cls._int_type.deserialize(temp_instance.serialize())
~~~~~~~~~~~~~~~~~~~~~~~^^
File "/usr/local/lib/python3.13/site-packages/zigpy/types/struct.py", line 236, in serialize
for field, value in self.assigned_fields(strict=True):
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/zigpy/types/struct.py", line 182, in assigned_fields
raise ValueError(
f"Value for field {field.name!r} is required: {self!r}"
)
ValueError: Value for field 'direction' is required: FrameControl<0x00>(frame_type=<FrameType.CLUSTER_COMMAND: 1>, is_manufacturer_specific=0, disable_default_response=0, reserved=0, *is_cluster=True, *is_general=False)

The above exception was the direct cause of the following exception:

zigpy.exceptions.ParsingError

Apologies if I've misunderstood what you meant!

@puddly
Copy link
Contributor

puddly commented Aug 6, 2025

Make sure you're using the latest version of zigpy and all of the other packages.

@candeodevelopment
Copy link
Contributor Author

Make sure you're using the latest version of zigpy and all of the other packages.

Ah right, we're just on the latest HA release (2025.7.4), we'll switch to the beta channel and recheck.

@candeodevelopment
Copy link
Contributor Author

@puddly we've updated the code, please let us know if anything further is required.

Thanks for your help!

@TheJulianJES TheJulianJES changed the title Add support for Candeo LC20 smart LED controllers and RD1 / RD1P rotary dimmer switches Add Candeo LC20 smart LED controllers, RD1/RD1P rotary dimmers Aug 11, 2025
@TheJulianJES TheJulianJES added needs review This PR should be reviewed soon, as it generally looks good. needs reviewer answer An answer from a reviewer is needed (e.g. why a PR isn't acceptable in the current state). labels Aug 11, 2025
Use actual data value for device automation triggers rather than enum.

Using an enum throws an "unsupported schema data type" error in HA itself when creating an automation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs review This PR should be reviewed soon, as it generally looks good. needs reviewer answer An answer from a reviewer is needed (e.g. why a PR isn't acceptable in the current state).
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants