-
Notifications
You must be signed in to change notification settings - Fork 679
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
[BUG] Curtain motor _TZE200_r0jdjrvi has no entities #1953
Comments
Sorry for closing, but not, not fixed yet. If you don't mind, go for a fresh start and focused just in your device. Lets begin... My proposed quirk: ts0601_cover_2.py"""Tuya MCU based cover and blinds."""
from typing import Dict, Optional, Union
from zigpy.profiles import zha
import zigpy.types as t
from zigpy.zcl import foundation
from zigpy.zcl.clusters.closures import WindowCovering
from zigpy.zcl.clusters.general import (
Basic,
GreenPowerProxy,
Groups,
Identify,
OnOff,
Ota,
Scenes,
Time,
)
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import (
NoManufacturerCluster,
TUYA_MCU_COMMAND,
TuyaLocalCluster,
TuyaManufacturerWindowCover,
TuyaManufCluster,
TuyaWindowCover,
TuyaWindowCoverControl,
)
from zhaquirks.tuya.mcu import (
DPToAttributeMapping,
TuyaClusterData,
TuyaDPType,
TuyaMCUCluster,
TuyaOnOffNM,
)
# Maps OPEN/CLOSE/STOP cover commands from Tuya to Zigbee
# https://github.com/zigpy/zigpy/blob/master/zigpy/zcl/clusters/closures.py#L558
# https://developer.tuya.com/en/docs/iot-device-dev/zigbee-curtain-switch-access-standard?id=K9ik6zvra3twv#title-7-DP1%20and%20DP4%20Curtain%20switch%201%20and%202
TUYA2ZB_COMMANDS = {
0x0000: 0x0000,
0x0001: 0x0002,
0x0002: 0x0001,
}
class TuyaWindowCovering(NoManufacturerCluster, WindowCovering, TuyaLocalCluster):
"""Tuya MCU WindowCovering cluster."""
"""Add additional attributes for direction"""
attributes = WindowCovering.attributes.copy()
attributes.update(
{
0xF000: ("curtain_switch", t.enum8, True), # 0: open, 1: stop, 2: close
0xF001: (
"accurate_calibration",
t.enum8,
True,
), # 0: calibration started, 1: calibration finished
0xF002: ("motor_steering", t.enum8, True), # 0: default, 1: reverse
0xF003: ("travel", t.uint16_t, True), # 30 to 9000 (units of 0.1 seconds)
}
)
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,
):
"""Override the default Cluster command."""
# if manufacturer is None:
# manufacturer = self.endpoint.device.manufacturer
self.debug(
"Sending Tuya Cluster Command. Cluster Command is %x, Arguments are %s",
command_id,
args,
)
# (upopen, downclose, stop)
if command_id in (0x0002): # ¿0x0003: continue?
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_attr="curtain_switch",
attr_value=TUYA2ZB_COMMANDS[command_id], # convert tuya2zigbee command
expect_reply=expect_reply,
manufacturer=manufacturer,
)
self.endpoint.device.command_bus.listener_event(
TUYA_MCU_COMMAND,
cluster_data,
)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.SUCCESS)
# (go_to_lift_percentage)
elif command_id in (0x0000, 0x0001, 0x0005):
if command_id == 0x0000:
lift_value = 100
elif command_id == 0x0001:
lift_value = 0
elif command_id == 0x0005:
lift_value = args[0]
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_attr="current_position_lift_percentage",
attr_value=lift_value,
expect_reply=expect_reply,
manufacturer=manufacturer,
)
self.endpoint.device.command_bus.listener_event(
TUYA_MCU_COMMAND,
cluster_data,
)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.SUCCESS)
# # Custom Command
# elif command_id == 0x0006: # ¿doc reference?
# tuya_payload.status = args[0]
# tuya_payload.tsn = args[1]
# tuya_payload.command_id = args[2]
# tuya_payload.function = args[3]
# tuya_payload.data = args[4]
self.warning("Unsupported command_id: %s", command_id)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.UNSUP_CLUSTER_COMMAND)
class TuyaWindowCoverManufCluster(TuyaMCUCluster):
"""Tuya with WindowCover data points."""
attributes = TuyaMCUCluster.attributes.copy()
attributes.update(
{
0x5000: ("backlight_mode", t.enum8, True), # 0: off, 1: on
0x8001: (
"indicator_status",
t.enum8,
True,
), # 0: status, 1: position, 2: off (¿backlight_mode?)
}
)
dp_to_attribute: Dict[int, DPToAttributeMapping] = {
1: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"on_off",
dp_type=TuyaDPType.ENUM,
),
2: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"current_position_lift_percentage",
dp_type=TuyaDPType.VALUE,
),
3: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"accurate_calibration",
dp_type=TuyaDPType.ENUM,
),
4: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"on_off",
dp_type=TuyaDPType.ENUM,
endpoint_id=2,
),
5: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"current_position_lift_percentage",
dp_type=TuyaDPType.VALUE,
endpoint_id=2,
),
6: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"accurate_calibration",
dp_type=TuyaDPType.ENUM,
endpoint_id=2,
),
7: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"backlight_mode",
dp_type=TuyaDPType.ENUM,
),
8: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"motor_steering",
dp_type=TuyaDPType.ENUM,
),
9: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"motor_steering",
dp_type=TuyaDPType.ENUM,
endpoint_id=2,
),
10: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"quick_calibration",
dp_type=TuyaDPType.ENUM,
),
11: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"quick_calibration",
dp_type=TuyaDPType.ENUM,
endpoint_id=2,
),
14: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"indicator_status",
dp_type=TuyaDPType.ENUM,
),
}
data_point_handlers = {
1: "_dp_2_attr_update",
2: "_dp_2_attr_update",
3: "_dp_2_attr_update",
4: "_dp_2_attr_update",
5: "_dp_2_attr_update",
6: "_dp_2_attr_update",
7: "_dp_2_attr_update",
8: "_dp_2_attr_update",
9: "_dp_2_attr_update",
10: "_dp_2_attr_update",
11: "_dp_2_attr_update",
14: "_dp_2_attr_update",
}
class TuyaCover0601_GP(TuyaWindowCover):
"""Tuya blind controller device."""
signature = {
MODELS_INFO: [
("_TZE200_r0jdjrvi", "TS0601"),
],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaWindowCoverManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaWindowCoverManufCluster,
TuyaOnOffNM,
TuyaWindowCovering,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
}
} There is a good guide to create your local quirk: Just create inside your local quirk folder a new file (ie: After any future changes to the quirk you will need to remove any I will try to help as much as I can, but it will take a lot of tests and a lot of patience (my time is limited). If you have a remote control for the cover, it will also be useful to obtain information on the commands. One last request, please, put the logs and code examples between
|
Thanks for your time and help :)
After that i noticed that the dasboard buttons aren't working. Also shortened the log to the only thing thats happening while the motor is active: Remote: Open + Stop:
Remote Open without stop till fully open:
Remote Close + Stop:
Remote Close till fully closed:
Dashboard up button:
Dasboard down button:
|
Sorry but this seems to be a completely diferent cover from the quirk. The previous quirk was for a 2 cover device. From your logs I would say that cover reports the status position in some way. I would expect to have one report from every remote command but I only can see one from your logs. My assumption is that the device will report the 'target status' when pushing the Let's try with a 'classical' approach. Replace completely the local quirk with this one: ts0601_cover_2.py"""Tuya based cover and blinds."""
from zigpy.profiles import zha
from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import (
TuyaManufacturerWindowCover,
TuyaManufCluster,
TuyaWindowCover,
TuyaWindowCoverControl,
)
class TuyaCover0601_GP(TuyaWindowCover):
"""Tuya cover controller device."""
signature = {
MODELS_INFO: [
("_TZE200_r0jdjrvi", "TS0601"),
],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaManufacturerWindowCover,
TuyaWindowCoverControl,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
}
}
} Save changes, delete any |
It is this device: https://www.aliexpress.com/item/1005003796260309.html As EU Plug, Motor only variant. Pressing the stop button on the remote without the curtain moving no log output is generated. Log for Stop in HA:
Log for UP in HA:
|
Hi @javicalle,
With this custom quirk active #1953 (comment) And with this one #1953 (comment) I also setup ZHA logging you mentioned before and tried to check my log but it is pretty overcrowded since I have dozens of devices connected to HA so if you specify which part is interresting for you I will try to find it or be able at least to clean it a little bit. Thanks a lot for all your work. |
Probably we need to map the motion commands. |
After checking it, I will return to the new implementation, so please use this quirk to test: ts0601_cover_2.py"""Tuya MCU based cover and blinds."""
from typing import Dict, Optional, Union
from zigpy.profiles import zha
import zigpy.types as t
from zigpy.zcl import foundation
from zigpy.zcl.clusters.closures import WindowCovering
from zigpy.zcl.clusters.general import (
Basic,
GreenPowerProxy,
Groups,
Identify,
OnOff,
Ota,
Scenes,
Time,
)
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import (
NoManufacturerCluster,
TUYA_MCU_COMMAND,
TuyaLocalCluster,
TuyaManufacturerWindowCover,
TuyaManufCluster,
TuyaWindowCover,
TuyaWindowCoverControl,
)
from zhaquirks.tuya.mcu import (
DPToAttributeMapping,
TuyaClusterData,
TuyaDPType,
TuyaMCUCluster,
TuyaOnOffNM,
)
# Maps OPEN/CLOSE/STOP cover commands from Tuya to Zigbee
# https://github.com/zigpy/zigpy/blob/master/zigpy/zcl/clusters/closures.py#L558
# https://developer.tuya.com/en/docs/iot-device-dev/zigbee-curtain-switch-access-standard?id=K9ik6zvra3twv#title-7-DP1%20and%20DP4%20Curtain%20switch%201%20and%202
TUYA2ZB_COMMANDS = {
0x0000: 0x0000,
0x0001: 0x0002,
0x0002: 0x0001,
}
class TuyaWindowCovering(NoManufacturerCluster, WindowCovering, TuyaLocalCluster):
"""Tuya MCU WindowCovering cluster."""
"""Add additional attributes for direction"""
attributes = WindowCovering.attributes.copy()
attributes.update(
{
0xF000: ("curtain_switch", t.enum8, True), # 0: open, 1: stop, 2: close
0xF001: (
"accurate_calibration",
t.enum8,
True,
), # 0: calibration started, 1: calibration finished
0xF002: ("motor_steering", t.enum8, True), # 0: default, 1: reverse
0xF003: ("travel", t.uint16_t, True), # 30 to 9000 (units of 0.1 seconds)
}
)
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,
):
"""Override the default Cluster command."""
# if manufacturer is None:
# manufacturer = self.endpoint.device.manufacturer
self.debug(
"Sending Tuya Cluster Command. Cluster Command is %x, Arguments are %s",
command_id,
args,
)
# (upopen, downclose, stop)
if command_id == 0x0002: # ¿0x0003: continue?
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_attr="curtain_switch",
attr_value=TUYA2ZB_COMMANDS[command_id], # convert tuya2zigbee command
expect_reply=expect_reply,
manufacturer=manufacturer,
)
self.endpoint.device.command_bus.listener_event(
TUYA_MCU_COMMAND,
cluster_data,
)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.SUCCESS)
# (go_to_lift_percentage)
elif command_id in (0x0000, 0x0001, 0x0005):
if command_id == 0x0000:
lift_value = 100
elif command_id == 0x0001:
lift_value = 0
elif command_id == 0x0005:
lift_value = args[0]
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_attr="current_position_lift_percentage",
attr_value=lift_value,
expect_reply=expect_reply,
manufacturer=manufacturer,
)
self.endpoint.device.command_bus.listener_event(
TUYA_MCU_COMMAND,
cluster_data,
)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.SUCCESS)
# # Custom Command
# elif command_id == 0x0006: # ¿doc reference?
# tuya_payload.status = args[0]
# tuya_payload.tsn = args[1]
# tuya_payload.command_id = args[2]
# tuya_payload.function = args[3]
# tuya_payload.data = args[4]
self.warning("Unsupported command_id: %s", command_id)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.UNSUP_CLUSTER_COMMAND)
class TuyaWindowCoverManufCluster(TuyaMCUCluster):
"""Tuya with WindowCover data points."""
attributes = TuyaMCUCluster.attributes.copy()
attributes.update(
{
0x5000: ("backlight_mode", t.enum8, True), # 0: off, 1: on
0x8001: (
"indicator_status",
t.enum8,
True,
), # 0: status, 1: position, 2: off (¿backlight_mode?)
}
)
dp_to_attribute: Dict[int, DPToAttributeMapping] = {
1: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"on_off",
dp_type=TuyaDPType.ENUM,
),
3: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"current_position_lift_percentage",
dp_type=TuyaDPType.VALUE,
),
# 3: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "accurate_calibration",
# dp_type=TuyaDPType.ENUM,
# ),
# 4: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "on_off",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 5: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "current_position_lift_percentage",
# dp_type=TuyaDPType.VALUE,
# endpoint_id=2,
# ),
# 6: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "accurate_calibration",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 7: DPToAttributeMapping(
# TuyaMCUCluster.ep_attribute,
# "backlight_mode",
# dp_type=TuyaDPType.ENUM,
# ),
# 8: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "motor_steering",
# dp_type=TuyaDPType.ENUM,
# ),
# 9: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "motor_steering",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 10: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "quick_calibration",
# dp_type=TuyaDPType.ENUM,
# ),
# 11: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "quick_calibration",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 14: DPToAttributeMapping(
# TuyaMCUCluster.ep_attribute,
# "indicator_status",
# dp_type=TuyaDPType.ENUM,
# ),
}
data_point_handlers = {
1: "_dp_2_attr_update",
2: "_dp_2_attr_update",
3: "_dp_2_attr_update",
4: "_dp_2_attr_update",
5: "_dp_2_attr_update",
6: "_dp_2_attr_update",
7: "_dp_2_attr_update",
8: "_dp_2_attr_update",
9: "_dp_2_attr_update",
10: "_dp_2_attr_update",
11: "_dp_2_attr_update",
14: "_dp_2_attr_update",
}
class TuyaCover0601_GP(TuyaWindowCover):
"""Tuya blind controller device."""
signature = {
# "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=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752,
# maximum_outgoing_transfer_size=66, 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
# )
MODELS_INFO: [
("_TZE200_r0jdjrvi", "TS0601"),
],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaWindowCoverManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaWindowCoverManufCluster,
TuyaOnOffNM,
TuyaWindowCovering,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
}
} Most of the functionallity has been commented, I will enable it later. Is expected that lift_percentage would be updated. I don't know what switch is this that you refer, any info will be welcomed. @RowCZ if you can attach the logs related to the switch operation will be helpfull. |
@javicalle thanks for quick response switch ON - switch OFF (motor starts runnik CW) rowcz__TZE200_r0jdjrvi_home-assistant.log With new quirk I also noticed that I have finally some messages in Logbook on device page but there are only messages from yesterday (about 15 hours ago and older) so something in past worked at least that much that made some records there and new quirk was at least able to load that records but wasn't able to made new one. I hope this info will help. |
I can confirm that lift_percantage is now showing up and updating correctly after the curtain moved.
I can't confirm that. The Switch entity does nothing for me. |
I have uppdated this part of the code: # (upopen, downclose, stop)
if command_id == 0x0002: # ¿0x0003: continue?
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_attr="curtain_switch",
attr_value=TUYA2ZB_COMMANDS[command_id], # convert tuya2zigbee command
expect_reply=expect_reply,
manufacturer=manufacturer,
) Can you fix it and repeat the tests? |
Thanks for the update. Percentage update is still working. UP:
Down:
Stop:
EDIT: Switch On (Rotating CW):
Off (Stop Rotating CW):
|
@javicalle hi and thanks for the update switch is without any change Up - no error this time, in log book I can see message that Cover is opening but no movement on motor (button change to inactive state) log: Here is a also a part of the screen with controls and logbook |
Good News i got the arrows working with the following: """Tuya MCU based cover and blinds."""
from typing import Dict, Optional, Union
from zigpy.profiles import zha
import zigpy.types as t
from zigpy.zcl import foundation
from zigpy.zcl.clusters.closures import WindowCovering
from zigpy.zcl.clusters.general import (
Basic,
GreenPowerProxy,
Groups,
Identify,
Ota,
Scenes,
Time,
)
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import (
NoManufacturerCluster,
TUYA_MCU_COMMAND,
TuyaLocalCluster,
TuyaManufacturerWindowCover,
TuyaManufCluster,
TuyaWindowCover,
TuyaWindowCoverControl,
)
from zhaquirks.tuya.mcu import (
DPToAttributeMapping,
TuyaClusterData,
TuyaDPType,
TuyaMCUCluster,
)
# Maps OPEN/CLOSE/STOP cover commands from Tuya to Zigbee
# https://github.com/zigpy/zigpy/blob/master/zigpy/zcl/clusters/closures.py#L558
# https://developer.tuya.com/en/docs/iot-device-dev/zigbee-curtain-switch-access-standard?id=K9ik6zvra3twv#title-7-DP1%20and%20DP4%20Curtain%20switch%201%20and%202
TUYA2ZB_COMMANDS = {
0x0000: 0x0000,
0x0001: 0x0002,
0x0002: 0x0001,
}
class TuyaWindowCovering(NoManufacturerCluster, WindowCovering, TuyaLocalCluster):
"""Tuya MCU WindowCovering cluster."""
"""Add additional attributes for direction"""
attributes = WindowCovering.attributes.copy()
attributes.update(
{
0xF000: ("curtain_switch", t.enum8, True), # 0: open, 1: stop, 2: close
0xF001: ("accurate_calibration", t.enum8, True), # 0: calibration started, 1: calibration finished
0xF002: ("motor_steering", t.enum8, True), # 0: default, 1: reverse
0xF003: ("travel", t.uint16_t, True), # 30 to 9000 (units of 0.1 seconds)
}
)
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,
):
"""Override the default Cluster command."""
# if manufacturer is None:
# manufacturer = self.endpoint.device.manufacturer
self.debug(
"Sending Tuya Cluster Command. Cluster Command is %x, Arguments are %s",
command_id,
args,
)
# (upopen, downclose, stop)
if command_id in (0x0002, 0x0000, 0x0001): # ¿0x0003: continue?
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_attr="curtain_switch",
attr_value=TUYA2ZB_COMMANDS[command_id], # convert tuya2zigbee command
expect_reply=expect_reply,
manufacturer=-1,
)
self.endpoint.device.command_bus.listener_event(
TUYA_MCU_COMMAND,
cluster_data,
)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.SUCCESS)
# (go_to_lift_percentage)
elif command_id == 0x0005:
lift_value = args[0]
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_attr="current_position_lift_percentage",
attr_value=lift_value,
expect_reply=expect_reply,
manufacturer=-1,
)
self.endpoint.device.command_bus.listener_event(
TUYA_MCU_COMMAND,
cluster_data,
)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.SUCCESS)
# # Custom Command
# elif command_id == 0x0006: # ¿doc reference?
# tuya_payload.status = args[0]
# tuya_payload.tsn = args[1]
# tuya_payload.command_id = args[2]
# tuya_payload.function = args[3]
# tuya_payload.data = args[4]
self.warning("Unsupported command_id: %s", command_id)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.UNSUP_CLUSTER_COMMAND)
class TuyaWindowCoverManufCluster(TuyaMCUCluster):
"""Tuya with WindowCover data points."""
attributes = TuyaMCUCluster.attributes.copy()
attributes.update(
{
0x5000: ("backlight_mode", t.enum8, True), # 0: off, 1: on
0x8001: ("indicator_status", t.enum8, True), # 0: status, 1: position, 2: off (¿backlight_mode?)
}
)
dp_to_attribute: Dict[int, DPToAttributeMapping] = {
1: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"curtain_switch",
dp_type=TuyaDPType.ENUM,
),
3: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"current_position_lift_percentage",
dp_type=TuyaDPType.VALUE,
),
# 3: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "accurate_calibration",
# dp_type=TuyaDPType.ENUM,
# ),
# 4: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "on_off",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 5: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "current_position_lift_percentage",
# dp_type=TuyaDPType.VALUE,
# endpoint_id=2,
# ),
# 6: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "accurate_calibration",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 7: DPToAttributeMapping(
# TuyaMCUCluster.ep_attribute,
# "backlight_mode",
# dp_type=TuyaDPType.ENUM,
# ),
# 8: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "motor_steering",
# dp_type=TuyaDPType.ENUM,
# ),
# 9: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "motor_steering",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 10: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "quick_calibration",
# dp_type=TuyaDPType.ENUM,
# ),
# 11: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "quick_calibration",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 14: DPToAttributeMapping(
# TuyaMCUCluster.ep_attribute,
# "indicator_status",
# dp_type=TuyaDPType.ENUM,
# ),
}
data_point_handlers = {
1: "_dp_2_attr_update",
2: "_dp_2_attr_update",
3: "_dp_2_attr_update",
4: "_dp_2_attr_update",
5: "_dp_2_attr_update",
6: "_dp_2_attr_update",
7: "_dp_2_attr_update",
8: "_dp_2_attr_update",
9: "_dp_2_attr_update",
10: "_dp_2_attr_update",
11: "_dp_2_attr_update",
14: "_dp_2_attr_update",
}
class TuyaCover0601_GP(TuyaWindowCover):
"""Tuya blind controller device."""
signature = {
# "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=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752,
# maximum_outgoing_transfer_size=66, 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
# )
MODELS_INFO: [
("_TZE200_r0jdjrvi", "TS0601"),
],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaWindowCoverManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaWindowCoverManufCluster,
TuyaWindowCovering,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
}
} Key was to set following in commands: manufacturer=-1, Unfortunally the percentage slider is only a display but doesn't turn the motor:
|
@caradas Nice! Here is log from version that cardas provided: |
@RowCZ my usual way after changing the quirk was:
|
A lot of updates here. The switch wasn't really necessary, just an attempt to understand things. Once resolved it is not necessary and should not return. That The lift percentage is mapped against the DP_3. The device don't complain about it but it can be that is not the good one. According to Z2M it could be the DP_2, so I would try to replace the Let's see if we can fix the cover functions and then I try to see what happens to the missing slider. |
@javicalle
|
I've only just started with the zigbee stuff, but upon testing zigbee2mqtt, it appears that all of the buttons as well as position control work pretty well. For position control to work, I had to first set endstop positions by letting the motor move in one direction and physically stopping rotation to signal the end of the track. Repeat for the other direction and the position slider starts working. Is there any way to bring the config from zigbee2mqtt into the custom quirk here so we can get all of the info required to get this working with zha? |
That can be the reason why I have no slider, I didn't bother with instalation since I was unable to make it work at all. :D |
clarification: this was position control with zigbee2mqtt. Edit: using the quirk by cardas, the slider does indeed show up once the endstops have been configured. To do this:
The position of the slider will update if you stop the motor, but there is no control via the slider yet. |
so i have this model _TZE200_65moe6rh This code works strangely with mine. Mine came with a remote. It seems the code works only if the motor doesn't hit its "endstops" Once is it hits the endstop HA stops responding. But if its in the middle I can control the motor in HA. All the commands work Up, Down and stop but only if its in-between both endstops. Once it hits the endstop nothing responds... Also the slider reports the correct position but I cant move it around. Edit: EDIT2 lol |
@javicalle
|
That quirk is a little old now. # (upopen, downclose, stop)
if command_id in (0x0002, 0x0000, 0x0001): # ¿0x0003: continue?
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_name=TuyaMCUCluster.ep_attribute,
cluster_attr="curtain_switch",
attr_value=TUYA2ZB_COMMANDS[command_id], # convert tuya2zigbee command
expect_reply=expect_reply,
manufacturer=-1,
) and # (go_to_lift_percentage)
elif command_id == 0x0005:
lift_value = args[0]
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_name=TuyaMCUCluster.ep_attribute,
cluster_attr="current_position_lift_percentage",
attr_value=lift_value,
expect_reply=expect_reply,
manufacturer=-1,
) IIRC that quirk is very incomplete |
@javicalle Thanks! I've fixed a slider control by changing ts0601_cover.py"""Tuya MCU based cover and blinds."""
from typing import Dict, Optional, Union
from zigpy.profiles import zha
import zigpy.types as t
from zigpy.zcl import foundation
from zigpy.zcl.clusters.closures import WindowCovering
from zigpy.zcl.clusters.general import (
Basic,
GreenPowerProxy,
Groups,
Identify,
Ota,
Scenes,
Time,
)
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import (
NoManufacturerCluster,
TUYA_MCU_COMMAND,
TuyaLocalCluster,
TuyaManufacturerWindowCover,
TuyaManufCluster,
TuyaWindowCover,
TuyaWindowCoverControl,
)
from zhaquirks.tuya.mcu import (
DPToAttributeMapping,
TuyaClusterData,
TuyaDPType,
TuyaMCUCluster,
)
# Maps OPEN/CLOSE/STOP cover commands from Tuya to Zigbee
# https://github.com/zigpy/zigpy/blob/master/zigpy/zcl/clusters/closures.py#L558
# https://developer.tuya.com/en/docs/iot-device-dev/zigbee-curtain-switch-access-standard?id=K9ik6zvra3twv#title-7-DP1%20and%20DP4%20Curtain%20switch%201%20and%202
TUYA2ZB_COMMANDS = {
0x0000: 0x0002,
0x0001: 0x0000,
0x0002: 0x0001,
}
class TuyaWindowCovering(NoManufacturerCluster, WindowCovering, TuyaLocalCluster):
"""Tuya MCU WindowCovering cluster."""
"""Add additional attributes for direction"""
attributes = WindowCovering.attributes.copy()
attributes.update(
{
0xF000: ("curtain_switch", t.enum8, True), # 0: open, 1: stop, 2: close
0xF001: ("accurate_calibration", t.enum8, True), # 0: calibration started, 1: calibration finished
0xF002: ("motor_steering", t.enum8, True), # 0: default, 1: reverse
0xF003: ("travel", t.uint16_t, True), # 30 to 9000 (units of 0.1 seconds)
}
)
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,
):
"""Override the default Cluster command."""
# if manufacturer is None:
# manufacturer = self.endpoint.device.manufacturer
self.debug(
"Sending Tuya Cluster Command. Cluster Command is %x, Arguments are %s",
command_id,
args,
)
# (upopen, downclose, stop)
if command_id in (0x0002, 0x0000, 0x0001): # ¿0x0003: continue?
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_name=TuyaMCUCluster.ep_attribute,
cluster_attr="curtain_switch",
attr_value=TUYA2ZB_COMMANDS[command_id], # convert tuya2zigbee command
expect_reply=expect_reply,
manufacturer=-1,
)
self.endpoint.device.command_bus.listener_event(
TUYA_MCU_COMMAND,
cluster_data,
)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.SUCCESS)
# (go_to_lift_percentage)
elif command_id == 0x0005:
lift_value = args[0]
cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_name=TuyaMCUCluster.ep_attribute,
cluster_attr="current_position_lift_percentage",
attr_value=lift_value,
expect_reply=expect_reply,
manufacturer=-1,
)
self.endpoint.device.command_bus.listener_event(
TUYA_MCU_COMMAND,
cluster_data,
)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.SUCCESS)
# # Custom Command
# elif command_id == 0x0006: # ¿doc reference?
# tuya_payload.status = args[0]
# tuya_payload.tsn = args[1]
# tuya_payload.command_id = args[2]
# tuya_payload.function = args[3]
# tuya_payload.data = args[4]
self.warning("Unsupported command_id: %s", command_id)
return foundation.GENERAL_COMMANDS[
foundation.GeneralCommand.Default_Response
].schema(command_id=command_id, status=foundation.Status.UNSUP_CLUSTER_COMMAND)
class TuyaWindowCoverManufCluster(TuyaMCUCluster):
"""Tuya with WindowCover data points."""
attributes = TuyaMCUCluster.attributes.copy()
attributes.update(
{
0x5000: ("backlight_mode", t.enum8, True), # 0: off, 1: on
0x8001: ("indicator_status", t.enum8, True), # 0: status, 1: position, 2: off (¿backlight_mode?)
}
)
dp_to_attribute: Dict[int, DPToAttributeMapping] = {
1: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"curtain_switch",
dp_type=TuyaDPType.ENUM,
),
2: DPToAttributeMapping(
TuyaWindowCovering.ep_attribute,
"current_position_lift_percentage",
dp_type=TuyaDPType.VALUE,
),
# 3: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "accurate_calibration",
# dp_type=TuyaDPType.ENUM,
# ),
# 4: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "on_off",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 5: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "current_position_lift_percentage",
# dp_type=TuyaDPType.VALUE,
# endpoint_id=2,
# ),
# 6: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "accurate_calibration",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 7: DPToAttributeMapping(
# TuyaMCUCluster.ep_attribute,
# "backlight_mode",
# dp_type=TuyaDPType.ENUM,
# ),
# 8: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "motor_steering",
# dp_type=TuyaDPType.ENUM,
# ),
# 9: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "motor_steering",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 10: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "quick_calibration",
# dp_type=TuyaDPType.ENUM,
# ),
# 11: DPToAttributeMapping(
# TuyaWindowCovering.ep_attribute,
# "quick_calibration",
# dp_type=TuyaDPType.ENUM,
# endpoint_id=2,
# ),
# 14: DPToAttributeMapping(
# TuyaMCUCluster.ep_attribute,
# "indicator_status",
# dp_type=TuyaDPType.ENUM,
# ),
}
data_point_handlers = {
1: "_dp_2_attr_update",
2: "_dp_2_attr_update",
3: "_dp_2_attr_update",
4: "_dp_2_attr_update",
5: "_dp_2_attr_update",
6: "_dp_2_attr_update",
7: "_dp_2_attr_update",
8: "_dp_2_attr_update",
9: "_dp_2_attr_update",
10: "_dp_2_attr_update",
11: "_dp_2_attr_update",
14: "_dp_2_attr_update",
}
class TuyaCover0601_GP(TuyaWindowCover):
tuya_cover_inverted_by_default = True
"""Tuya blind controller device."""
signature = {
# "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=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752,
# maximum_outgoing_transfer_size=66, 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
# )
MODELS_INFO: [
("_TZE200_nogaemzt", "TS0601"),
],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaWindowCoverManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaWindowCoverManufCluster,
TuyaWindowCovering,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
}
} Seems like the only problem now is that controls have the right direction, but the curtains state and slider control are inverted. To open curtains via slider, I need to move a slider to the 0 so state results in Closed (and vice versa). And if the curtain state is Closed (but it's actually Open), Close button is inactive. Also, commands (buttons) don't result in updating a slider value and curtain state (maybe this doesn't relate to the invertion). Tried to add |
If you have a physical remote for that cover try to reverse the motor spin according to the manufacturer. |
Hi, @Drmatterpl ts0601_cover.py
|
Thanks! it works for me. |
The implementation needs to be fixed to handle the About the percentage behavior, does the cover have a remote? Is the control from remote fine? |
Yes i have a remote, up arrow on the remote opens, same goes for up arrow in ha. |
First of all, you were right:
Yes it should, and maybe is the reason of the behavior. Let's see if this can fix something My proposal is add and replace this definitions: class TuyaCC(t.enum8):
"""Tuya cover commands."""
OPEN = 0x00
STOP = 0x01
CLOSE = 0x02
class ZclCC(t.enum8):
"""ZCL cover commands."""
OPEN = 0x00
CLOSE = 0x01
STOP = 0x02
TUYA2ZB_COMMANDS = {
ZclCC.OPEN: TuyaCC.OPEN,
ZclCC.CLOSE: TuyaCC.CLOSE,
ZclCC.STOP: TuyaCC.STOP,
} Also, remove the cluster_data = TuyaClusterData(
endpoint_id=self.endpoint.endpoint_id,
cluster_name=self.ep_attribute,
cluster_attr="curtain_switch",
attr_value=TUYA2ZB_COMMANDS[command_id], # convert tuya2zigbee command
expect_reply=expect_reply,
manufacturer=-1,
) Save changes, remove any |
BTW, do you have any |
Your Code proposal is working as expected. Thank you.
|
It works. Let me sum up all the steps and the updated quirk file:
This will look for quirks in that folder, so create the folder custom_zha_quirks inside config (can be also done with File editor) and inside create a file named TZE200_r0jdjrvi.py and copy the content of this file: P.D for pairing mode, press the button in the base of the motor 3 times and then keep it pressed for the forth time for like two seconds. green led will start blinking |
Hello, guys. I was having problem pairing and using a similar model curtain motor. But the the quirk file that @danidask posted on his last comment worked like a charm. I just had to change TZE200 to TZE204 on line 261, because my motor model is _TZE204_r0jdjrvi. The one thing that still isn't working is the curtain state whenever i use the set_position service (either by calling it manually or by selecting the appropriate position on the scroller in the UI). If click on the half of the bar (50%) and the curtain goes to that position, the state of the curtain keeps on "Opening" forever. I have to click the Stop button for the state to change to Open. The same thing happens with the Closing state. Is there a way to fix this? |
You might need to reverse the motor direction reporting. IIRC, this is an
option in the quirk.
…On Sat, Sep 23, 2023, 4:43 PM utech-git ***@***.***> wrote:
Hello, guys.
I was having problem pairing and using a similar model curtain motor. But
the the quirk file that @danidask <https://github.com/danidask> posted on
his last comment worked like a charm. I just had to change TZE200 to TZE204
on line 261, because my motor model is _TZE204_r0jdjrvi.
The one thing that still isn't working is the curtain state whenever i use
the set_position service (either by calling it manually or by selecting the
appropriate position on the scroller in the UI).
If click on the half of the bar (50%) and the curtain goes to that
position, the state of the curtain keeps on "Opening" forever. I have to
click the Stop button for the state to change to Open. The same thing
happens with the Closing state.
Is there a way to fix this?
—
Reply to this email directly, view it on GitHub
<#1953 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACFFMXQUSUNHURUSRSGBOGDX35CY7ANCNFSM6AAAAAASHGFG3Q>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
|
_TZE200_r0jdjrvi TS0601 -- Added as described, but there is no slider? IEEE: a4:c1:38:3e:e4:4e:76:90 The motor is currently sitting on my desk (was never installed on the curtain track, was never calibrated (I've no idea how to calibrate it)). Open / Close do rotate the motor, but when Stop is pressed - Failed to call service cover/stop_cover. '>' not supported between instances of 'NoneType' and 'int': 2023-10-07 22:13:07.392 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [546958404032] '>' not supported between instances of 'NoneType' and 'int' |
In case is helps someone in the future, I was able to use the above quirk to integrate a different model number: _TZE200_xu4a5rhj I simply changed the model number in the quirk from _TZE200_r0jdjrvi to _TZE200_xu4a5rhj. Open/Stop/Close seems to work fine. I did have to set the motor reverse flag to get those going the correct direction. |
Hi @utech-git |
Well, every time after a power outage it loses the limits. But I don't do anything to get the limits back. Once the power is back, the first time I open the curtain it goes slowly to the beggining of the rail and when it reachs it automatically knows where the start limit is. The same way when I first close the curtain, it goes to the end of the rail and set the finish limit. And there it goes. It knows where the end/beggining is when it feels resistance and can't go any further (just like you said when you force the motor to stop). |
Could you please tell me how to set this "reverse flag"? I couldnt find it on the ZHA. |
In ZHA - select the device. Under device info - hit the 3 dots -> Manage zigbee device.
This is reflect in the quirk here:
|
Thank you for your response. In my situation, I have vertical curtains that do not have a physical limit when they are lowered, so I need to force them to stop. I was wondering if it is possible to save the limits in Home Assistant or somewhere else, so that they can be uploaded in case of a power outage. |
I did try to change this "True" to "False" on the quirk, at first. But nothing happened. Now I tried on the Manage Zigbee Device menu... when I click "Read Attribute" it shows "None". I try to write attribute to 0 or 1, but when I read it again it continues "None" and the motor is still not reversed. Maybe it doesn't work with this model, but thanks any way! :) |
@utech-git works fine with zigbee2mqtt ... I'm using it until zha gets enough supported devices working properly .... |
I have a similar motor, model TZE200_idkvxabg. Thanks to everyone here I was able to get it to work with the most recent quirk, however, my open/close percentage stays out of sync with the remote control. Switching the motor direction will always have the remote and HA be opposite controls. Does anyone have any idea how to invert the display percentages? |
I have the same issue, _TZE204_r0jdjrvi model doesn't appear to accept any 0, 1, True, or False for reversing the motor. I wonder if there's a way to tell Home Assistant to treat it as the opposite. Did you figure out any other way to deal with that? I do have a remote but there's nothing described on the docs as to how one might reverse it on the device itself. |
There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates. Please make sure to update to the latest version and check if that solves the issue. Let us know if that works for you by adding a comment 👍 This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions. |
Describe the bug
Curtain motor has no entities.
Expected behavior
Entities for opening and closing curtain and if possible slider to set partially closed
According to #1602 (comment) its already fixed.
The text was updated successfully, but these errors were encountered: