Where to start writing Custom driver for TS130F smart curtain switch (tuya clone)?

Gents,

yet another set of newbie questions, as I’d like to get Loratap blinds switch (yet another clone of Tuya TS130F ) to work with ST.
Is there a “Edge for dummies” guide for how to do simple/sample drivers ?
Where would you start to get all device specific attributes etc ? There is for instance ZHA driver here zha-device-handlers/ts130f.py at 470ccf9faa7f76e3718a619c8114305f42acacaa · zigpy/zha-device-handlers · GitHub, shall I use that one ?

Best regards,
Alex

Hi, @NonAlex.

There are different samples depending on the protocol used by the device to get you started:

Also, you can check the following documents:

The manufacturer should provide the technical information so you know how you can interact with the device.
The file you shared is written in Python and the Edge drivers are based on Lua so, I think you should check if it works and then “translate” it to Edge drivers.

I don’t know if someone else in the Community has worked with this device, @JDRoberts who has an incredible experience around here might remember seeing something about that. Could you help us, JD, please?

1 Like

Thanks for the shout. I’ve heard of people getting this device to work with MQTT to ZIgbee ZHA, but I haven’t seen anyone in this forum mention it. Of course it can be hard to tell because the Tuya devices are available under so many different company names.

It looks like at one point zemismart had a version that worked with smartthings classic app, but they don’t appear to have ever updated the DTH for the current version of the app, let alone think about an edge driver. And they no longer sell it as far as I can tell.

So I’m sorry, I can’t help. You should be able to query any Zigbee device for the clusters it supports, and people used to get that from the hub logging, but again I don’t know how to do that with edge drivers.

Here’s a thread showing how it used to be done. You could use that right now while the groovy option is still available to get the clusters if that’s of any help. I would think there would be a way to do something similar with edge drivers, but I will have to leave that to others to discuss.

I will check this with the engineering team, that would be very helpful in case we don’t have the technical details of the device.

Thank you for your help, @JDRoberts! :grinning_face_with_smiling_eyes:

2 Likes

I would start with Zigbee Blinds implementation, as far the curtain supports Windows covering cluster you will be half way there.

I have implemented the version for both Ikea and Aqara motors , can help in case you stuck with something

1 Like

Thanks Andrew @veonua ,

Can you point me to a sample blinds/curtains DTH to use as a start point ?

Apart from standard 0,4,5,6,10 clusters it implements 0x102, Window Covering.
Question: is there a way to send Zigbee commands to a device/poll attributes through ST command line?

“ids”:
{
“modelId”: “TS130F”,
“manufacturerName”:
“_TZ3000_8kzqqzu4”
},
“endpoints”:
{
“endpointDescriptors”: [
{
“endpointId”: 1,
“applicationProfileId”: 260,
“applicationDeviceId”: 514,
“applicationDeviceVersion”: 0,
“_reserved1”: 1,
“inputClusters”: [ 0, 10, 4, 5, 6, 258 ],
“outputClusters”: [ 25 ]
} ],
“endpoints”:
{ “1”:
{ “clusters”:
{ “basic”:
{ “attributes”: [
{ “acl”: [ “readable”, “reportable” ],
“id”: 0,
“name”: “zclVersion”,
“value”: 3
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 1,
“name”: “appVersion”,
“value”: 68
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 2,
“name”: “stackVersion”,
“value”: 0
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 3,
“name”: “hwVersion”,
“value”: 1
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 4,
“name”: “manufacturerName”,
“value”: “_TZ3000_8kzqqzu4”
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 5,
“name”: “modelId”,
“value”: “TS130F”
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 6,
“name”: “dateCode”,
“value”: “”
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 7,
“name”: “powerSource”,
“value”: “mains”
},
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 65502
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 65533,
“name”: “clusterRevision”,
“value”: 1
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 65534,
“name”: “attributeReportingStatus”,
“value”: “PENDING”
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 65504
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 65505
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 65506
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 65507
} ],
“commandsGenerated”: “UNSUP_GENERAL_COMMAND”,
“commandsReceived”: “UNSUP_GENERAL_COMMAND”
},
“time”:
{ “attributes”: [
{ “acl”: [ “readable”, “reportable” ],
“id”: 7
} ],
“commandsGenerated”: “UNSUP_GENERAL_COMMAND”,
“commandsReceived”: “UNSUP_GENERAL_COMMAND”
},
“groups”:
{ “attributes”: [
{ “acl”: [ “readable”, “reportable” ],
“id”: 0,
“name”: “nameSupport”,
“value”:
{ “type”: “Buffer”,
“data”: [ 0 ]
}
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 65533,
“name”: “clusterRevision”,
“value”: 1
} ],
“commandsGenerated”: “UNSUP_GENERAL_COMMAND”,
“commandsReceived”: “UNSUP_GENERAL_COMMAND”
},
“scenes”:
{ “attributes”: [
{ “acl”: [ “readable”, “reportable” ],
“id”: 0
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 1
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 2
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 3
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 4
},
{ “acl”: [ “readable”, “reportable” ],
“id”: 65533,
“name”: “clusterRevision”, “value”: 1
} ],
“commandsGenerated”: “UNSUP_GENERAL_COMMAND”,
“commandsReceived”: “UNSUP_GENERAL_COMMAND”
},
“onOff”:
{ “attributes”: [
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 20480
},
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 32769
},
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 16385,
“name”: “onTime”,
“value”: 0
},
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 16386,
“name”: “offWaitTime”,
“value”: 0
} ],
“commandsGenerated”: “UNSUP_GENERAL_COMMAND”,
“commandsReceived”: “UNSUP_GENERAL_COMMAND”
},
“windowCovering”:
{ “attributes”: [
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 8,
“name”: “currentPositionLiftPercentage”,
“value”: 3
},
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 61440
},
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 61441
},
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 61442
},
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 61443
},
{ “acl”: [ “readable”, “writable”, “reportable” ],
“id”: 32768
} ],
“commandsGenerated”: “UNSUP_GENERAL_COMMAND”,
“commandsReceived”: “UNSUP_GENERAL_COMMAND”
}
},
“bindings”:
{ “ota”:
{ “attributes”: ,
“commandsGenerated”: “UNSUP_GENERAL_COMMAND”,
“commandsReceived”: “UNSUP_GENERAL_COMMAND”
}
}
}
}
}

Tried to use this groovy DTH as a sample Window Covering - Unable to parse response - #3 by cookierookie44

and freaking thing doesn’t even react…

Nice! IMO is a great fingerprint source = ) and in my experience, with the proper manufacturer and model definition you can begin to integrate the device using the tutorials Nayely shared

1 Like

OK, some progress here… got it working with commands to close and set covering level. Open command is consistently ignored for sone reason, same as On and Off ones…
I.e.
zigbee.command(CLUSTER_WINDOW_COVERING, 0x01)
works fine, but
zigbee.command(CLUSTER_WINDOW_COVERING, 0x00)
doesn’t

ST UI says “network error”… what does it mean ? Is there a way to see Hub logs to figure out why command wasn’t sent or ignored ?

That’s using a DTH, right?
When you send the command from the mobile app and the Zigbee device receives the command correctly, it should send a report with the new value if the update was successful.
When the report is received in the parse() method, the value is sent to the capability.
In this case, if we see the network error, it means the command wasn’t successful because no event was received by the parse method.
Make sure you’re sending the correct parameters:

zigbee.command(clusterID, commandID, [optional payload])

For DTH, we cannot see the message exchange in the event log, but I’ve seen other Community members use a Zigbee sniffer for this purpose, so you could get one.

For Edge drivers, you can use the CLI logcat command to see the RX and TX messages between the Hub and the device.

After switching to Edge and using sample driver from @veonua as a template, it finally works

I’d like to iron it out a little bit adding calibration dialog in GUI, but that’s TBD. For now one needs to turn calibration mode manually, do it, and then turn it off…

Documentation for Edge libraries is terrible… some elements are not documented as all, ones that are documented - have no examples of use, types are a mess… so without working example it’s nearly impossible to create a driver :slight_smile:

2 Likes

Thank you for the feedback regarding the Edge documentation. Just to have a better reference, can you provide more details, please?

  • Which functions did you notice missing?
  • What do you consider wrong about the data types?

In addition to the Edge reference documentation, did you check the ST Lua Libraries included in this section?
In the last one, you can see the code of the default handlers, and other functions that would help you understand how they work.

Nayely,

here are couple cases which I stumbled upon:

  1. local cluster_base = require “st.zigbee.cluster_base”. That “cluster_base” is not documented at all. Do search in your Edge reference - nothing returns
  2. device:send_to_component(command.component, zcl_clusters.WindowCovering.server.commands.Stop(device)) . Where, where on earth is it documented in WindowCovering ? in Edge references, it’s described as “class st.zigbee.zcl.clusters.WindowCovering.Stop”. So that “.server.commands.” complements part I can only learn/pick from examples, not from the documentation
  3. device:send(cluster_base.write_attribute(device, data_types.ClusterId(zcl_clusters.WindowCovering.ID), attr, data_msg)) Again, why zcl_clusters.WindowCovering.ID property, which is Cluster ID, returns value with wrong data type ?! So I need to explicitly convert it to ClusterID type before I can use it ?

I guess it’s a lack of experience with Lua. Nayely’s link to the ST Lua Libraries is very useful.

local cluster_base = require “st.zigbee.cluster_base”

basically, you just load the external library made by ST team.

So I need to explicitly convert it to ClusterID type before I can use it?

this is how Lua works, it’s possible to handle other types there, but it will take much more effort. The Edge API is in Beta and it’s impossible to make everything perfect in such a short period of time.

2 Likes

Andrew,

my feedback is about documentation mostly. I understand what “require” statement does, however I
Unpacked API docs from that link - not a word about “cluster_base”. EVERY sample piece of code there loads st.zigbee.cluster_base, but where is documentation what this library does/implements ?

And yes, LUA is a strict types language, however I still find it strange that cluster number always needs to be used as ClusterID type in library functions, why same library class property returns it as uint16 ? Now, it’s just a little quirk, and perhaps too much grunt from my side, but just as an observation.

Yes, it’s beta so I’m not complaining, but rather provide feedback. Sooner or later it will be out of beta right ?
BTW, for how long has it been in beta ?

The open Beta was released in August 2021.

But there has been continuous development during that time, as you can tell from the release dates on the individual stock (created by Samsung) Edge Drivers.

August 2021

2 Likes

Corrected, thanks! :sunglasses:

1 Like