Open Axel-72 opened 11 months ago
these quirks seem to work
"""Tuya based cover and blinds.""" from typing import Dict
from zigpy.profiles import zgp, zha from zigpy.quirks import CustomDevice import zigpy.types as t from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time from zigpy.zcl.clusters.security import IasZone
from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, )
from zhaquirks.tuya import NoManufacturerCluster, TuyaLocalCluster from zhaquirks.tuya.mcu import ( DPToAttributeMapping, TuyaMCUCluster, TuyaOnOff, ) from zhaquirks.tuya.ts0601_dimmer import TuyaOnOffNM
ZONE_TYPE = 0x0001
class ContactSwitchCluster(TuyaLocalCluster, IasZone): """Tuya ContactSwitch Sensor."""
_CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Contact_Switch}
def _update_attribute(self, attrid, value):
self.debug("_update_attribute '%s': %s", attrid, value)
super()._update_attribute(attrid, value)
class TuyaGarageManufCluster(NoManufacturerCluster, TuyaMCUCluster): """Tuya garage door opener."""
attributes = TuyaMCUCluster.attributes.copy()
attributes.update(
{
# ramdom attribute IDs
0xEF02: ("dp_2", t.uint32_t, True),
0xEF04: ("dp_4", t.uint32_t, True),
0xEF05: ("dp_5", t.uint32_t, True),
0xEF0B: ("dp_11", t.Bool, True),
0xEF0C: ("dp_12", t.enum8, True),
}
)
dp_to_attribute: Dict[int, DPToAttributeMapping] = {
# garage door trigger ¿on movement, on open, on closed?
1: DPToAttributeMapping(
TuyaOnOffNM.ep_attribute,
"on_off",
),
2: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"dp_2",
),
3: DPToAttributeMapping(
ContactSwitchCluster.ep_attribute,
"zone_status",
lambda x: IasZone.ZoneStatus.Alarm_1 if x else 0,
endpoint_id=2,
),
4: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"dp_4",
),
5: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"dp_5",
),
11: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"dp_11",
),
# garage door status (open, closed, ...)
12: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"dp_12",
),
}
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",
11: "_dp_2_attr_update",
12: "_dp_2_attr_update",
}
class TuyaGarageSwitchTO(CustomDevice): """Tuya Garage switch."""
signature = {
MODELS_INFO: [
("_TZE200_nklqjk62", "TS0601"),
("_TZE200_wfxuhoea", "TS0601"),
("_TZE204_nklqjk62", "TS0601"),
],
ENDPOINTS: {
# <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
# input_clusters=[0, 4, 5, 61184]
# output_clusters=[10, 25]>
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaGarageManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
# <SimpleDescriptor endpoint=242 profile=41440 device_type=97
# input_clusters=[]
# output_clusters=[33]
242: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaGarageManufCluster,
TuyaOnOffNM,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
INPUT_CLUSTERS: [
ContactSwitchCluster
],
OUTPUT_CLUSTERS: [],
},
242: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
"""Tuya based cover and blinds."""
from typing import Dict
from zigpy.profiles import zgp, zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.security import IasZone
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import NoManufacturerCluster, TuyaLocalCluster
from zhaquirks.tuya.mcu import (
DPToAttributeMapping,
TuyaMCUCluster,
TuyaOnOff,
)
from zhaquirks.tuya.ts0601_dimmer import TuyaOnOffNM
ZONE_TYPE = 0x0001
class ContactSwitchCluster(TuyaLocalCluster, IasZone):
"""Tuya ContactSwitch Sensor."""
_CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Contact_Switch}
def _update_attribute(self, attrid, value):
self.debug("_update_attribute '%s': %s", attrid, value)
super()._update_attribute(attrid, value)
class TuyaGarageManufCluster(NoManufacturerCluster, TuyaMCUCluster):
"""Tuya garage door opener."""
attributes = TuyaMCUCluster.attributes.copy()
attributes.update(
{
# ramdom attribute IDs
0xEF02: ("dp_2", t.uint32_t, True),
0xEF04: ("dp_4", t.uint32_t, True),
0xEF05: ("dp_5", t.uint32_t, True),
0xEF0B: ("dp_11", t.Bool, True),
0xEF0C: ("dp_12", t.enum8, True),
}
)
dp_to_attribute: Dict[int, DPToAttributeMapping] = {
# garage door trigger ¿on movement, on open, on closed?
1: DPToAttributeMapping(
TuyaOnOffNM.ep_attribute,
"on_off",
),
2: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"dp_2",
),
3: DPToAttributeMapping(
ContactSwitchCluster.ep_attribute,
"zone_status",
lambda x: IasZone.ZoneStatus.Alarm_1 if x else 0,
endpoint_id=2,
),
4: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"dp_4",
),
5: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"dp_5",
),
11: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"dp_11",
),
# garage door status (open, closed, ...)
12: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"dp_12",
),
}
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",
11: "_dp_2_attr_update",
12: "_dp_2_attr_update",
}
class TuyaGarageSwitchTO(CustomDevice):
"""Tuya Garage switch."""
signature = {
MODELS_INFO: [
("_TZE200_nklqjk62", "TS0601"),
("_TZE200_wfxuhoea", "TS0601"),
("_TZE204_nklqjk62", "TS0601"),
],
ENDPOINTS: {
# <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
# input_clusters=[0, 4, 5, 61184]
# output_clusters=[10, 25]>
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaGarageManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
# <SimpleDescriptor endpoint=242 profile=41440 device_type=97
# input_clusters=[]
# output_clusters=[33]
242: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaGarageManufCluster,
TuyaOnOffNM,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
INPUT_CLUSTERS: [
ContactSwitchCluster
],
OUTPUT_CLUSTERS: [],
},
242: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
hello @evlas did you know where can we have some documentation to test this ? (i'm new too with quirks)
This works, but it's detected as a light instead of a garage door
Paired, quirk seems to be loaded but no enitities shown: Works fine when setting/reading attributes: I'm not sure what I did wrong... Any idea I should try/check?
I have the exact same issue, I woke up one morning to find the entity unavailable. Removed the working device to do a re-pair in ZHA and now the light entity is missing and the quirk appears to be loaded
Manually writing to the cluster works.
Has anyone else this or have any suggestions?
Same issue here, although I've never been able to get the Entities showing. Please help! (also, I'm assuming I don't need local tuya or similiar installed, just using ZHA directly)
I've followed the steps, added the custom ZHA py file, adn config entries, but still no entities.
I have the following file entries (but with carriage returns, not sure why they don't display):
Configuration.yaml
zha: database_path: /config/zigbee.db enable_quirks: true custom_quirks_path: /config/custom_zha_quirks
Folders created:
and ts0601_garage.py is as #above
And I can see this in the logs:
I also note, that I can set the value to Bool.False manually by managing the Zigbee device (opposite of 'tszdr's image above, and it opens my garage door).
Now, how do I get a proper entity and button that I can add to a dashboard and/or automation?
So i didn't get the entity part working but i did figure out how to call the service directly from HA.
Just as a workaround until someone can input why the entity is missing.
Use the call service command - select "Zigbee Home Automation: Set zigbee cluster attribute"
set the conditions as follows (i'll include the yaml)
service: zha.set_zigbee_cluster_attribute data: ieee: endpoint_id: 1 cluster_id: 61184 attribute: 61185 value: 0
That should allow you to atleast write an automation assuming you can call the attribute directly.
Thanks @intra-aud , this works perfectly. Obviously not the ideal solution, but this achieves everything I needed it to on my dashboard. I'm calling the set attribute command from a Script, so that I can just add a 'Run' command to my Dashboard under the existing 'Garage' entity card. I guess we just wait and see if the original tuya entity starts getting fully detected in the future. :)
I had the same problem of no entities being detected.
As I was continously updating the file I missed deleting the cache, basically
__pycache__
folder in your custom_quirks_path
folder is important (see comment here)database_path
to my zha config. Before everything was ignored
zha:
database_path: /config/zigbee.db
enable_quirks: true
custom_quirks_path: /config/zhaquirks/
I used the quirks that create a light and binary sensor, it works for me but even if light was a swtich, I was disapointed to not have a real garage door or gate entity.
Downsides:
So, to compensates those inconvenience, I have set up multiple configurations.
In my case, it was for a gate.
I use a simple automation because I dit not spend much time on this, seems a dirty solution but was a quick fix. I just turn off light/switch just after it has been turned during 1 second.
alias: "Reset state of Garage door / Gate"
description: ""
trigger:
- platform: state
entity_id:
- light.gate # Or switch.garage_door depending of quirks used
to: "on"
for:
hours: 0
minutes: 0
seconds: 1
condition: []
action:
- service: light.turn_off
target:
entity_id: light.gate
data: {}
mode: single
There's certainly a more elegant way to fix this.
I created some helpers in HA interface:
input_boolean.gate_opening
: Switch that when turning on, indicate that gate is in a opening state.input_boolean.gate_closing
: Switch that when turning on, indicate that gate is in a closing state.input_number.duration_opening_gate
: Number of seconds the gate make to open. input_number.duration_closing_gate
: Number of seconds the gate make to close. It can be different of the opening duration.Then a simple automation to switch on/off the boolean input depending of the state and duration:
alias: "Update gate status"
description: ""
trigger:
- platform: state
entity_id:
- light.gate
to: "on"
condition: []
action:
- if:
- condition: state
entity_id: binary_sensor.gate_sensor
state: "off" # Mean that gate is open in my configuration
then:
- service: input_boolean.turn_on
metadata: {}
data: {}
target:
entity_id: input_boolean.gate_closing
- delay:
hours: 0
minutes: 0
seconds: "{{ states('input_number.duration_closing_gate') | int }}"
milliseconds: 0
- service: input_boolean.turn_off
target:
entity_id: input_boolean.gate_closing
data: {}
else:
- service: input_boolean.turn_on
metadata: {}
data: {}
target:
entity_id: input_boolean.gate_opening
- delay:
hours: 0
minutes: 0
seconds: "{{ states('input_number.duration_opening_gate') | int }}"
milliseconds: 0
- service: input_boolean.turn_off
metadata: {}
data: {}
target:
entity_id: input_boolean.gate_opening
mode: single
With this automation, I have 2 switchs, one will be on when gate is opening the other one when gate is closing.
Obviously, it's not the more reliable you can have. Multiple calls could mess up those switch states, wind will speed up or slow down closing/opening action, gate security could occurs etc.
Finally, I can create a custom entity that I could add to my dashboard.
I simply add to my configuration.yaml
file a new custom cover entity following HA documentation:
cover:
- platform: template
covers:
gate:
device_class: gate # can be 'garage' as well
friendly_name: "Gate (custom)"
icon_template: >-
{% if is_state('binary_sensor.gate_sensor', 'on') %}
mdi:gate
{% else %}
mdi:gate-open
{% endif %}
value_template: >-
{% if is_state('input_boolean.gate_opening', 'on') %}
opening
{% elif is_state('input_boolean.gate_closing', 'on') %}
closing
{% elif is_state('binary_sensor.gate_sensor', 'on') %}
closed
{% elif is_state('binary_sensor.gate_sensor', 'off') %}
open
{% endif %}
availability_template: "{{ is_state('binary_sensor.gate_sensor', 'on') or is_state('binary_sensor.gate_sensor', 'off') }}"
open_cover:
service: light.turn_on
data: {}
target:
entity_id: light.gate
close_cover:
service: light.turn_on
data: {}
target:
entity_id: light.gate
Now I can have a well working card of my gate on my dashboard without any specific card configuration or script calls.
When using this entity in a dashboard, when gate is opening, we will only see a message saying "opening", not really how much time it remain to be fully opened. Like a classic cover, we miss the percentage progression of opening.
I haven't tested it really but it should be pretty easy with the use of attribut position_template
instead of value_template
in custom entity.
It should looks like something like this:
position_template: >-
{% if is_state('input_boolean.gate_opening', 'on') %}
{{ ((as_timestamp(now()) - as_timestamp(states.input_boolean.gate_opening.last_changed))/states('input_number.duration_opening_gate')|int)|int }}
{% elif is_state('input_boolean.gate_closing', 'on') %}
{{ ((as_timestamp(now()) - as_timestamp(states.input_boolean.gate_closing.last_changed))/states('input_number.duration_closing_gate')|int)|int }}
{% elif is_state('binary_sensor.gate_sensor', 'on') %}
0
{% elif is_state('binary_sensor.gate_sensor', 'off') %}
100
{% endif %}
Hope it can help some people while we wait a fully integrated module in ZHA 🙂
Thanks for the quirk, it's working fine. Turning on the switch activate the physical connection for a second, then it is automaticaly deactivated. This is the expected behaviour for a garage door but not for the device I use. I need a normal on/off switch.
Is this behaviour built in the hardware or is it programmed in the quirk? Do you know if there is a software way to modify it?? Thanks a lot!
the built-in quirk is definitely not working and I can't override it:
the built-in quirk is definitely not working and I can't override it:
I'm having the exact same issue.
If I go to 'manage zigbee device', select cluster 'TuyaGarage...', attribute 'contact_sensor (id:0xef03' and click read - it correctly reports false/true depending on if the reed switch has a magnet near it or not.
Problem description
I'm new with ZHA, and my first difficulties were this product, it seems to be unsupported at this moment, and i don"t know how to make hard code python for that zigbee device. I don't really know how to do this.
Solution description
I need a python hard code probably
Screenshots/Video
Screenshots/Video
[Paste/upload your media here]Device signature
Device signature
```json [Paste the device signature here] "node_descriptor": "NodeDescriptor(logical_type=Diagnostic information
Diagnostic information
```json [Paste the diagnostic information here] ```Logs
Logs
```python [Paste the logs here] ```Custom quirk
Custom quirk
```python [Paste your custom quirk here] ```Additional information
No response