Koenkk / zigbee2mqtt

Zigbee 🐝 to MQTT bridge 🌉, get rid of your proprietary Zigbee bridges 🔨
https://www.zigbee2mqtt.io
GNU General Public License v3.0
11.74k stars 1.64k forks source link

Develco smart plug SPLZB-131 unsupported devices help #2666

Closed ChumKiu closed 4 years ago

ChumKiu commented 4 years ago

Bug Report

Help for setup a new device, actually I've just did the first step concerning the devices.js file like into the guide "how to add new devices":

... // Develco { zigbeeModel: ['SPLZB-131'], model: 'SPLZB-131', vendor: 'Develco', description: 'Zigbee smart plug with power meter', supports: 'on/off, power measurement', fromZigbee: [], toZigbee: [], }, ...

I kindly ask some guidance how to implement this specific type of devices. To gather some details on the device I took a look at the content of the database.db file, here below the section related this device:

{ "id":7, "type":"Router", "ieeeAddr":"0x00xxxxxxxxxxxxxx", "nwkAddr":12898, "manufId":4117, "manufName":"Develco Products A/S", "powerSource":"Mains (single phase)", "modelId":"SPLZB-131", "epList":[2,1], "endpoints": { "1": { "profId":49353, "epId":1, "devId":1, "inClusterList":[5,6], "outClusterList":[], "clusters":{}, "binds":[] }, "2": { "profId":260, "epId":2, "devId":81, "inClusterList":[0,1794,3,9,2820,6,4,5,2], "outClusterList":[0,25,10,3,1030], "clusters": { "genBasic": { "attributes": { "modelId":"SPLZB-131", "manufacturerName":"Develco Products A/S", "powerSource":1, "zclVersion":1, "dateCode":"2018-08-08 19:44" } } }, "binds":[] } }, "dateCode":"2018-08-08 19:44", "zclVersion":1, "interviewCompleted":true, "meta":{} }

The interesting endpoint is the number 2, clusters are detailed inside the techinical datasheet and also inside theZigBee Cluster Library Specification.

What happened

Pairing ok

What did you expect to happen

Integration inside zigbee2mqtt and able to read and drive data of this device.

How to reproduce it (minimal and precise)

None at the moment.

Debug Info

zigbee2mqtt version: v1.8.0 CC253X firmware version: (znp_CC26X2R1_LAUNCHXL_tirtos_ccs.hex (compiled inside CCS v3.40.00.02)

splzb-13x-technical-manual-smart-plug-mini.pdf

sjorge commented 4 years ago

You can try with

        fromZigbee: [fz.on_off],
        toZigbee: [tz.on_off],

And see if you can toggle it on and off, if that works, try with the generic_power converter for power measurements:

        fromZigbee: [fz.on_off, fz.generic_power],
        toZigbee: [tz.on_off],

That does not cover the occupancy detection stuff for auto on/off (I'm not sure anything specials needs to be done, it might just work by binding a motion sensor?), it also doesn't deal with the alarms mentioned. Not sure on those either.

ChumKiu commented 4 years ago

@sjorge Thank for the hint, I'll test it next week now I'm away from my devices. Also I'll try to implement custom management code in order to maximize the smart plug usage through the library, now I've to read a little about how to code my function (adding converter) by looking some example... Thank if you hve some other hint/manual/reference about the customization process other than the official documentation.

sjorge commented 4 years ago

Not sure for the custom functions, you could look at other existing fromZigbee and toZigbee converters, there may already be some that are close to what you need.

ChumKiu commented 4 years ago

@sjorge I've did the test and worked! Thank! So by transmitting the message (publish to the mosquitto broker from my python source code):

zigbee2mqtt/0xxxxxxxxxxxxxxxxx/set/state/ON

I was able to turn on the internal relay and then power on the load connected to the smart plug, in the same way by means of:

zigbee2mqtt/0xxxxxxxxxxxxxxxxx/set/state/OFF

I was able to switch off the load. Now I would like to go more deep inside the library in order to to be able to build all the details around this device, hence I kindly ask if there is some guide or hint to follow in order to better know what the single keywoards mean like 'meta', 'configure', 'extend' keyword and how to manage it inside a custom device definition. Do you know if there is a sort of walk through about this task. Actually I find very difficult found a way to better understand all the path to do in order to fix the single parts. Inside the "How to support new devices" page (link) there is some information, but from what I can see into the devices.js file there is a lot of other concepts that should be more clear in order to start working in a systematically way in order to write a more complete device support integration. Hope some can let me know some way or guidance... Thank for your time!!!

sjorge commented 4 years ago

Maybe @Koenkk has some more docs somewhere, but I just learned most of what I do by reading and trial an error, zigbee2mqtt and herdsman is a rather complex beast.

ChumKiu commented 4 years ago

@sjorge Hope @Koenkk can give some in deep info concerning this subject, it is very interesting in order to expand the supported devices... Thank! Best regards.

Koenkk commented 4 years ago

Can you try with the following config:

{
    zigbeeModel: ['SPLZB-131'],
    model: 'SPLZB-131',
    vendor: 'Develco',
    description: 'Zigbee smart plug with power meter',
    supports: 'on/off, power measurement',
    fromZigbee: [fz.on_off, fz.electrical_measurement],
    toZigbee: [tz.on_off],
    meta: {configureKey: 1},
    configure: async (device, coordinatorEndpoint) => {
        const endpoint = device.getEndpoint(1);
        await bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement']);
        await endpoint.read('haElectricalMeasurement', [
            'acVoltageMultiplier', 'acVoltageDivisor', 'acCurrentMultiplier',
            'acCurrentDivisor', 'acPowerMultiplier', 'acPowerDivisor',
        ]);
        await configureReporting.onOff(endpoint);
        await configureReporting.rmsVoltage(endpoint);
        await configureReporting.rmsCurrent(endpoint);
        await configureReporting.activePower(endpoint);
    },
},
ChumKiu commented 4 years ago

@Koenkk thank for your time, it work, just changed the endpoint from 1 to 2.

Here below some rows taken from the log with the smart plug in ON state with an IKEA lamp bulb on it: ... zigbee2mqtt:info 2020-01-09 10:18:05: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"ON","linkquality":84,"voltage":218.48,"current":0.06,"power":12}' zigbee2mqtt:info 2020-01-09 10:18:06: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"ON","linkquality":90,"voltage":218.48,"current":0.06,"power":13}' zigbee2mqtt:info 2020-01-09 10:18:07: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"ON","linkquality":84,"voltage":218.62,"current":0.06,"power":13}' zigbee2mqtt:info 2020-01-09 10:18:08: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"ON","linkquality":111,"voltage":218.55,"current":0.06,"power":12}' zigbee2mqtt:info 2020-01-09 10:18:09: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"ON","linkquality":108,"voltage":218.55,"current":0.06,"power":12}' ...

from the log the report message come in approx every second.

There is a way to slow down the reporting time? I think this is a parameter that have to send from zigbee2mqtt to the end device...

Also there is a way to configure the device in order to report back info only when the internal relay state change or when power or other electrical reportable entity change of some units in order to avoid the continuously transmission? It seems to me that from ZB spec this should be possible, but I need some guidance in order to get deep inside the z2m and the zigbee-herdsman-converters module.

Another thigs can be read also the frequency, I've look at the devices.js (not sure if this is the right place, I'm still in learning...), but seems to me not available the ACFrequency that I can call in a similar way you did so by means of:

await configureReporting.ACFrequency(endpoint);

As first try I've used this code:

// Develco
{
    zigbeeModel: ['SPLZB-131'],
    model: 'SPLZB-131',
    vendor: 'Develco',
    description: 'Zigbee smart plug with power meter',
    supports: 'on/off, power measurement',
    fromZigbee: [fz.on_off, fz.generic_power],
    toZigbee: [tz.on_off],
    meta: {configureKey: 1},
    configure: async (device, coordinatorEndpoint) => {
        const endpoint = device.getEndpoint(2);
        await bind(endpoint, coordinatorEndpoint, ['genOnOff']);
        await configureReporting.instantaneousDemand(endpoint);
        await endpoint.read('seMetering', ['multiplier', 'divisor']);
     },
},

with this I've no info about the electrical parameters but I can see that the device report back the state of the relay (ON or OFF) only when this one is changed by pressing the button that is on the module plug. So a useful thing to optimize the data transmission may be get back info only when the state change (relay state or some power parameter) and configure the reporting with a bigger time interval, may be 1 time every 60". Make sense? Thank to help me in a better understanding, these are amazing things!

Thank!

ChumKiu commented 4 years ago

@Koenkk Also a strange things now is that I'm still not able to drive the smart plug on and off through publishing messages like this one in Python:

msg="zigbee2mqtt/0xXXXXXXXXXXXXXXXX/set" payload={"state": "ON"} fsmZigBee.publishMessage(msg, json.dumps(payload)) # Translate to JSON object

with payload ON or OFF... everty time I got an error:

zigbee2mqtt:error 2020-01-09 12:56:56: Publish 'set' 'state' to '0xXXXXXXXXXXXXXXXX' failed: 'Error: Timeout - 1461 - 1 - 11 - 6 - 11 after 15000ms' zigbee2mqtt:info 2020-01-09 12:56:56: MQTT publish: topic 'zigbee2mqtt/bridge/log', payload '{"type":"zigbee_publish_error","message":"Publish 'set' 'state' to '0xXXXXXXXXXXXXXXXX' failed: 'Error: Timeout - 1461 - 1 - 11 - 6 - 11 after 15000ms'","meta":{"friendly_name":"0xXXXXXXXXXXXXXXXX"}}'

So I've reverted the code inside the devices.js to this one:

{
    zigbeeModel: ['SPLZB-131'],
    model: 'SPLZB-131',
    vendor: 'Develco',
    description: 'Zigbee smart plug with power meter',
    supports: 'on/off, power measurement',
    fromZigbee: [fz.on_off, fz.electrical_measurement],
    toZigbee: [tz.on_off],
    meta: {configureKey: 1},
    configure: async (device, coordinatorEndpoint) => {
        const endpoint = device.getEndpoint(2);
        await bind(endpoint, coordinatorEndpoint, ['genOnOff']);
    },
},

also I've restarted npm removed the device from the network and perform again pairing. Now from my Python source:

msg="zigbee2mqtt/0xXXXXXXXXXXXXXXXX/set"
payload={"state": "ON"}
fsmZigBee.publishMessage(msg, json.dumps(payload))
time.sleep(5)
msg="zigbee2mqtt/0xXXXXXXXXXXXXXXXX/set"
payload={"state": "OFF"}
fsmZigBee.publishMessage(msg, json.dumps(payload))
time.sleep(5)

I'm able to switch ON and OFF the load and also i got back the report of voltage, power that was deleted from the device.js file... could someone please help me to better understand the reason of this strange behaviour? Why the reporting configuration is still active if I removed all the definition, have to do with the meta (configureKey:1) as you wrote inside this post ?

Thank!

ChumKiu commented 4 years ago

Hi all, here some update on the subject, it is not clear because some time (after restarting the server) I'm not able to properly drive the internal relay of the smart-plug by publishing the message ON or OFF. Publishing is made from a simple Python code to mosquitto as broker.

msg="zigbee2mqtt/"+friendly_name+"/set" payload={"state": "ON"} fsmZigBee.publishMessage(msg, json.dumps(payload))

and similar to switch the load OFF.

If I restart the server I'm again unable to get the command properly working.

So I'll try to explain how to reproduce this behavior exactly step by step. First here below the definition that I've actually inside the devices.js file:

{
    zigbeeModel: ['SPLZB-131'],
    model: 'SPLZB-131',
    vendor: 'Develco',
    description: 'Zigbee smart plug with power meter',
    supports: 'on/off, power measurement',
    fromZigbee: [fz.on_off, fz.electrical_measurement],
    toZigbee: [tz.on_off],
    meta: {configureKey: 1},
    configure: async (device, coordinatorEndpoint) => {
        const endpoint = device.getEndpoint(2);
        await bind(endpoint, coordinatorEndpoint, ['genOnOff']);
        await configureReporting.onOff(endpoint);
    },
},

Now I'm starting with a situation where the command "set" get in reply a timeout error, I've fired Wireshark to sniff the packet, here the log about the sending command:

Frame 17416: 122 bytes on wire (976 bits), 122 bytes captured (976 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
User Datagram Protocol, Src Port: 17754, Dst Port: 17754
ZigBee Encapsulation Protocol, Channel: 11, Length: 48
IEEE 802.15.4 Data, Dst: 0x405b, Src: 0x0000
    Frame Control Field: 0x8861, Frame Type: Data, Acknowledge Request, PAN ID Compression, Destination Addressing Mode: Short/16-bit, Frame Version: IEEE Std 802.15.4-2003, Source Addressing Mode: Short/16-bit
    Sequence Number: 159
    Destination PAN: 0x1a62
    Destination: 0x405b
    Source: 0x0000
    [Extended Source: TexasIns_00:1c:a1:4e:59 (00:12:4b:00:1c:a1:4e:59)]
    [Origin: 187]
    TI CC24xx-format metadata: FCS OK
        FCS Valid: True
        RSSI: 0dB
        LQI Correlation Value: 0
ZigBee Network Layer Data, Dst: 0x405b, Src: 0x0000
    Frame Control Field: 0x0248, Frame Type: Data, Discover Route: Enable, Security Data
    Destination: 0x405b
    Source: 0x0000
    Radius: 30
    Sequence Number: 30
    [Extended Source: TexasIns_00:1c:a1:4e:59 (00:12:4b:00:1c:a1:4e:59)]
    [Origin: 187]
    ZigBee Security Header
        Security Control Field: 0x28, Key Id: Network Key, Extended Nonce
        Frame Counter: 139398
        Extended Source: TexasIns_00:1c:a1:4e:59 (00:12:4b:00:1c:a1:4e:59)
        Key Sequence Number: 0
        Message Integrity Code: 1f3b4eea
        [Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
        [Key Label: My Default zigbee2mqtt]
ZigBee Application Support Layer Data, Dst Endpt: 1, Src Endpt: 1
    Frame Control Field: Data (0x00)
    Destination Endpoint: 1
    Cluster: On/Off (0x0006)
    Profile: Home Automation (0x0104)
    Source Endpoint: 1
    Counter: 119
ZigBee Cluster Library Frame
    Frame Control Field: Cluster-specific (0x01)
        .... ..01 = Frame Type: Cluster-specific (0x1)
        .... .0.. = Manufacturer Specific: False
        .... 0... = Direction: Client to Server
        ...0 .... = Disable Default Response: False
    Sequence Number: 5
    Command: On (0x01)

Looking at the "ZigBee Application Support Layer Data" frame section I can see that the listed destination EndPoint was 1 instead of the right 2 as correctly specified inside the devices.js file!

So at this point my first things about the timeout error is: "may be this is originated because the command was sent to the wrong endpoint?"

Now I try a factory reset of the smart-plug, accordling to the manual I've to keep pushed the button for some time until the led will blink continously then release it. After this operation the device will Leave the network and the commissioning stage take to start. After some time the smart-plug are again inside the network (the fiendly name used to communicate with zigbee2mqtt is unaltered, what is changing is the source address set by the ZC to the ZED, this is clearly show inside Wireshark). Now I send again the set command to switch ON the load, from Wireshark I've:

39575 7353.851707 0x0000 0x5435 ZigBee HA 122 ZCL OnOff: On, Seq: 10

with the following details of the packet:

Frame 39575: 122 bytes on wire (976 bits), 122 bytes captured (976 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
User Datagram Protocol, Src Port: 17754, Dst Port: 17754
ZigBee Encapsulation Protocol, Channel: 11, Length: 48
    Protocol ID String: EX
    Protocol Version: 2
    Type: Data (1)
    Channel ID: 11
    Device ID: 1
    LQI/CRC Mode: LQI
    Timestamp: Jan 10, 2020 12:39:47.686999999 ora solare Europa occidentale
    Sequence Number: 3500
    .011 0000 = Length: 48 bytes
IEEE 802.15.4 Data, Dst: 0x5435, Src: 0x0000
    Frame Control Field: 0x8861, Frame Type: Data, Acknowledge Request, PAN ID Compression, Destination Addressing Mode: Short/16-bit, Frame Version: IEEE Std 802.15.4-2003, Source Addressing Mode: Short/16-bit
        .... .... .... .001 = Frame Type: Data (0x1)
        .... .... .... 0... = Security Enabled: False
        .... .... ...0 .... = Frame Pending: False
        .... .... ..1. .... = Acknowledge Request: True
        .... .... .1.. .... = PAN ID Compression: True
        .... .... 0... .... = Reserved: False
        .... ...0 .... .... = Sequence Number Suppression: False
        .... ..0. .... .... = Information Elements Present: False
        .... 10.. .... .... = Destination Addressing Mode: Short/16-bit (0x2)
        ..00 .... .... .... = Frame Version: IEEE Std 802.15.4-2003 (0)
        10.. .... .... .... = Source Addressing Mode: Short/16-bit (0x2)
    Sequence Number: 37
    Destination PAN: 0x1a62
    Destination: 0x5435
    Source: 0x0000
    [Extended Source: TexasIns_00:1c:a1:4e:59 (00:12:4b:00:1c:a1:4e:59)]
    [Origin: 187]
    TI CC24xx-format metadata: FCS OK
        FCS Valid: True
        RSSI: 0dB
        LQI Correlation Value: 0
ZigBee Network Layer Data, Dst: 0x5435, Src: 0x0000
    Frame Control Field: 0x0248, Frame Type: Data, Discover Route: Enable, Security Data
    Destination: 0x5435
    Source: 0x0000
    Radius: 30
    Sequence Number: 122
    [Extended Source: TexasIns_00:1c:a1:4e:59 (00:12:4b:00:1c:a1:4e:59)]
    [Origin: 187]
    ZigBee Security Header
        Security Control Field: 0x28, Key Id: Network Key, Extended Nonce
            ...0 1... = Key Id: Network Key (0x1)
            ..1. .... = Extended Nonce: True
        Frame Counter: 139786
        Extended Source: TexasIns_00:1c:a1:4e:59 (00:12:4b:00:1c:a1:4e:59)
        Key Sequence Number: 0
        Message Integrity Code: 06de3804
        [Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
        [Key Origin: 38727]
ZigBee Application Support Layer Data, Dst Endpt: 2, Src Endpt: 1
    Frame Control Field: Data (0x00)
        .... ..00 = Frame Type: Data (0x0)
        .... 00.. = Delivery Mode: Unicast (0x0)
        ..0. .... = Security: False
        .0.. .... = Acknowledgement Request: False
        0... .... = Extended Header: False
    Destination Endpoint: 2 <-- RIGHT DESTINTATION ENDPOINT!!
    Cluster: On/Off (0x0006)
    Profile: Home Automation (0x0104)
    Source Endpoint: 1
    Counter: 224
ZigBee Cluster Library Frame
    Frame Control Field: Cluster-specific (0x01)
        .... ..01 = Frame Type: Cluster-specific (0x1)
        .... .0.. = Manufacturer Specific: False
        .... 0... = Direction: Client to Server
        ...0 .... = Disable Default Response: False
    Sequence Number: 10
    Command: On (0x01)

Now the load that is connected to the smart-plug was correctly switched ON and also from the ZigBee packet I can see the right server endpoint set to 2, hence the behavior is correct.

Pushing the button on the smart-plug body for less than 5" will drive manually the relay contact and trigger a Report Attributes command from Server (the smart-plug or ZED) and the Client (the coordinator or ZC) to notify the state of the internal switch. So until this point all seems to me correct.

Now the next test step is to stop the zigbee2mqtt server and then restart it again in order to see if all is working properly, hence press CTRL+C and then again npm start.

Now I've sent the set command in order to switch on the load and now I've got the timeout error message! From Wireshark I've:

41555 8105.290893 0x0000 0x5435 ZigBee HA 122 ZCL OnOff: On, Seq: 2

this time I'll omit the entirely packet structure, I'll report only the important thing, the direction is Client (ZC) to Server (ZED) so is correct, but the destination endpoint is 1 instead 2!!!!

ZigBee Application Support Layer Data, Dst Endpt: 1, Src Endpt: 1
    Frame Control Field: Data (0x00)
        .... ..00 = Frame Type: Data (0x0)
        .... 00.. = Delivery Mode: Unicast (0x0)
        ..0. .... = Security: False
        .0.. .... = Acknowledgement Request: False
        0... .... = Extended Header: False
    Destination Endpoint: 1 <--- WRONG DESTINATION ENDPOINT!!!!
    Cluster: On/Off (0x0006)
    Profile: Home Automation (0x0104)
    Source Endpoint: 1
    Counter: 255
ZigBee Cluster Library Frame
    Frame Control Field: Cluster-specific (0x01)
        .... ..01 = Frame Type: Cluster-specific (0x1)
        .... .0.. = Manufacturer Specific: False
        .... 0... = Direction: Client to Server
        ...0 .... = Disable Default Response: False
    Sequence Number: 2
    Command: On (0x01)

so this may be the proof of the reason because I've got the timeout error when the server is restarted...

I know that is a rather long analysis but I hope that can help in order to have some guidance in order to solve this issue...

Thank!!

ccorderod commented 4 years ago

@ChumKiu you are using in your device.js definition the "standard" ZB On-Off command (tz.on_off); if I am not wrong, it address endpoint 1 (as defined in the ZB documentation). For whatever reason, your Develco device does not use the standard ZCL on_off definition (to endpoint 1), hence it does not work.

IMHO, you should write a specific tozigbee on_off definition for your device, using endpoint 2 (it is probably as simple as copying the actual definition and changing the endpoint).

ChumKiu commented 4 years ago

@ccorderod Hi, thank very much for your explanation, now make sense for me the timeout error! Well, from the toZigbee.js file, inside the converters array I see:

on_off: {
        key: ['state'],
        convertSet: async (entity, key, value, meta) => {
            await entity.command('genOnOff', value.toLowerCase(), {}, getOptions(meta));
            if (value.toLowerCase() === 'toggle') {
                if (!meta.state.hasOwnProperty('state')) {
                    return {};
                } else {
                    return {state: {state: meta.state.state === 'OFF' ? 'ON' : 'OFF'}};
                }
            } else {
                return {state: {state: value.toUpperCase()}};
            }
        },
        convertGet: async (entity, key, meta) => {
            await entity.read('genOnOff', ['onOff']);
        },
    },

so I think that this should be the code to be copy and modified (kindly be patient I'm starting to study zigbee2mqtt and some question may be simple... :)). I suppose that inside the following code:

await entity.command('genOnOff', value.toLowerCase(), {}, getOptions(meta));

the genOnOff have to do somethings with the endpoint question because I can't see any other stuff that may be related to the endpoint question but I've to dig into the code in order to better know the logic... do you have some hint about?

Thank

ChumKiu commented 4 years ago

@ccorderod

if I am not wrong, it address endpoint 1 (as defined in the ZB documentation).

About this point I've digged some time into the code until I've found this file:

zigbee2mqtt\node_modules\zigbee-herdsman\dist\zcl\definition\cluster.js

where is defined the genOnOff cluster (0x06), this definition agree with the ZCL Cluster specification for the cluster, but I'm unable to know where is possible to specify the endpoint for this specific cluster.

This seems to me a library file following the ZCL standard. Reading about the ZigBee specification, seems to me that the endpoint is not related to some specific cluster, so this seems to me not the right place where look about endpoint, the endpoint concept (a part the ZDO endpoint 0) is more device oriented. Let me explain better my thinking, a designer is free to implement many cluster (so may be I can them as "functionality") in different endpoint hence, from my perspective, there is no special guidance that constraint to put some specific cluster inside the endpoint 1 rather than into the endpoint 2. What is mandatory instead seems to me that every node shall have the endpoin 0 (called ZDO) because this one is used to perform the starting operation of a node when this have to join a network.

So, as long as I understand the specs correctly, in order to implement ZCL features, each Zigbee device must have at least one endpoint declared besides the built-in one that is the endpoint 0.

I'm forgotting somethings?

Then if my thinking is correct I think that the endpoint definition should not reside in this part of the code which seems instead refers to the standard ZCL specification but in another part.

Any help will be appreciated!!

EDIT I've found this post that may be useful, I think that the referred file is the entityPublish.js (row 141) at the zigbee2mqtt\lib\extension\ path... Some short guide with a logic flow and how the blocks are related each other would be a very great things, is a very big work (thank Koenkk for it)...

ChumKiu commented 4 years ago

SOLVED

Well after further reading the code I've got the switch working, this post give me the right way.

Initially I've used the ep: statement but I got error, then throuhg some search for the string (device) => inside the file devices.js I realized that the correct stament to use is endpoint: and not simply ep: !!

Then by using the other information from this post about the message structure I've defined the MQTT message to use in order to switch on the load:

message ="zigbee2mqtt//ep2/set" payload={"state": "ON"}

and the load was switched ON.

@Koenkk could you please add these information on the "MQTT topics and message structure" help page?

The only thing now is that I've also a bunch of messages on the zigbee2mqtt log, here below the ones related the ON switch command:

(node:26948) UnhandledPromiseRejectionWarning: TypeError: Cannot read property '0' of undefined
    at Zigbee.resolveEntity (/opt/zigbee2mqtt/lib/zigbee.js:191:109)
    at Controller.publishEntityState (/opt/zigbee2mqtt/lib/controller.js:241:36)
    at EntityPublish.onMQTTMessage (/opt/zigbee2mqtt/lib/extension/entityPublish.js:180:30)
    at async Controller.callExtensionMethod (/opt/zigbee2mqtt/lib/controller.js:322:21)
(node:26948) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 6)
(node:26948) UnhandledPromiseRejectionWarning: TypeError: Cannot read property '0' of undefined
    at Zigbee.resolveEntity (/opt/zigbee2mqtt/lib/zigbee.js:191:109)
    at Controller.publishEntityState (/opt/zigbee2mqtt/lib/controller.js:241:36)
    at publish (/opt/zigbee2mqtt/lib/extension/deviceReceive.js:150:22)
    at DeviceReceive.onZigbeeEvent (/opt/zigbee2mqtt/lib/extension/deviceReceive.js:179:13)
    at Controller.callExtensionMethod (/opt/zigbee2mqtt/lib/controller.js:322:44)
    at Controller.onZigbeeEvent (/opt/zigbee2mqtt/lib/controller.js:226:14)
    at Zigbee.emit (events.js:304:20)
    at Controller.<anonymous> (/opt/zigbee2mqtt/lib/zigbee.js:57:52)
    at Controller.emit (events.js:304:20)
    at Controller.<anonymous> (/opt/zigbee2mqtt/node_modules/zigbee-herdsman/dist/controller/controller.js:446:22)
(node:26948) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 7)

any hint? Thank!

Koenkk commented 4 years ago

@ChumKiu what did you add in devices.js now?

Adding ep2 shouldn't be necessary, you can specify the default endpoint by using https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/devices.js#L531.

ChumKiu commented 4 years ago

@Koenkk thank for the hint, I've not noticed also this possibility. Actually I've changed my ZED definition inside the devices.js file as per your suggestion so:

{
    zigbeeModel: ['SPLZB-131'],
    model: 'SPLZB-131',
    vendor: 'Develco',
    description: 'Zigbee smart plug with power meter',
    supports: 'on/off, power measurement',
    fromZigbee: [fz.on_off],
    toZigbee: [tz.on_off],
    endpoint: (device) => {
        return {'system': 1, 'default': 2};
    },
    meta: {configureKey: 1},
    configure: async (device, coordinatorEndpoint) => {
        const endpoint = device.getEndpoint(2);
        await bind(endpoint, coordinatorEndpoint, ['genOnOff']);
        await configureReporting.onOff(endpoint);
    },
},

and now with this MQTT command:

message: zigbee2mqtt/set payload: {"state": "ON"}

I'm able to switch ON the smart-plug relay, so no need to use the direct specification of the ep2 as in my previous post, thank to pointed me about this stuff.

Now I'm try to perform the following task:

Definitively I'm try to better understand your code in order to be able to write many custom device definition and adapter to support other devices inside zigbee2mqtt, is a great project thank! Just need some guidance how to use the right stuff...

Thank again. Best regard!

ChumKiu commented 4 years ago

@Koenkk I'm study your zigbee2mqtt code about the configure Reporting, now I think is more clear, let me explain in order to see if I got the point.

In order to enable the periodic reporting attribute feature we have to define a related method inside the Object Array configureReporting located inside the device.js file; in order to access these method the dot notation is used. So to change somethings we have to work inside this array and then referring to the right function. As example for the standard onOff the code is:

onOff: async (endpoint) => {
    const payload = [{
        attribute: 'onOff',
        minimumReportInterval: 0,
        maximumReportInterval: repInterval.HOUR,
        reportableChange: 0,
    }];
    await endpoint.configureReporting('genOnOff', payload);
},

then to change the time interval parameter (Periodic Reporting) we have to change (or better setup a new custom method??) the minimumReportInterval and the maximumReportInterval accordling to the ZCL specification parag. 2.5.11.2.1 (ZigBee Document – 075123 R6).

From the spec if we have to disable this feature we have two possibility:

1. If the Maximum Reporting Interval is set to 0x0000, there is no periodic reporting, but change based reporting is still operational.

2. If the Maximum Reporting Interval is set to 0xffff, no reports SHALL be generated, whatever other conditions are satisfied.

Into parag 2.5.7.1.6 about the "Maximum Reporting Interval Field" is also written:

If this value is set to 0xffff, then the device SHALL not issue reports for the specified attribute, and the configuration information for that attribute need not be maintained. (Note: in an implementation using dynamic memory allocation, the memory space for that information may then be reclaimed). If this value is set to 0x0000 and the minimum reporting interval field equals 0xffff, then the device SHALL revert back to its default reporting configuration. The reportable change field, if present, SHALL be set to zero.

Then if I have to restore the report attribute to the default reporting configuration I think that I've to use minimumReportInterval=0xFFFF and maximumReportInterval = 0x0000 before calling the API "await endpoint.configureReporting(, payload);"

I'm right?

So if I need to reset the attribute report configuration for my smart-plug, as exercise, I've to setup a new function able to manage for the cluster 0x0B04 Electrical Measurement these attribute ID (for the 'haElectricalMeasurement' see related definitions inside the _ZigBee2MQTT\opt_zigbee2mqtt\nodemodules\zigbee-herdsman\dist\zcl\definition\cluster.js file):

0x505 (1285) rmsVoltage
0x508 (1288) rmsCurrent
0x50B (1291) activePower

so if I correctly understand a function example to reset the reporting attributes may be:

develcoResetReportAttributes: async (endpoint) => {
    const payload1 = [{
        attribute: 'rmsVoltage',
        minimumReportInterval: 0xFFFF,
        maximumReportInterval: 0,
        reportableChange: 0,
    }];
    await endpoint.configureReporting('haElectricalMeasurement', payload1);

    const payload2 = [{
        attribute: 'rmsCurrent',
        minimumReportInterval: 0xFFFF,
        maximumReportInterval: 0,
        reportableChange: 0,
    }];
    await endpoint.configureReporting('haElectricalMeasurement', payload2);

    const payload3 = [{
        attribute: 'activePower',
        minimumReportInterval: 0xFFFF,
        maximumReportInterval: 0,
        reportableChange: 0,
    }];
    await endpoint.configureReporting('haElectricalMeasurement', payload3);
},

but I've to invoke it only one time, so my doubt is: if I put it inside the device definition into the configuration section as below:

{
    zigbeeModel: ['SPLZB-131'],
    model: 'SPLZB-131',
    vendor: 'Develco',
    description: 'Zigbee smart plug with power meter',
    supports: 'on/off, power measurement',
    fromZigbee: [fz.on_off],
    toZigbee: [tz.on_off],
    endpoint: (device) => {
        return {'system': 1, 'default': 2};
    },
    meta: {configureKey: 1},
    configure: async (device, coordinatorEndpoint) => {
        const endpoint = device.getEndpoint(2);
        await bind(endpoint, coordinatorEndpoint, ['genOnOff']);
        await configureReporting.onOff(endpoint);
        await configureReporting.develcoResetReportAttributes(endpoint);
    },
},

and then restart the zigbee2mqtt server, after that I think also to have forcing the configuration through the message:

zigbee2mqtt/bridge/configure with payload equal to the friendly name of the smart-plug.

If all the above is correct, suppose now that in the future I would have the need to re-enable the automatic reporting feature, here came a question:

I've to change again the device.js configuration file by removing the develcoResetReportAttributes function and then issue a new zigbee2mqtt/bridge/configure command or there is another way that which does not imply to have changing the device.js file?

Thank a lot, I'm still learning...

ChumKiu commented 4 years ago

Ok I've did a little step, as a first try I've added the attribute reporting also for the reactive power, from the device datasheet this attribute has index 0x050e (cluster reference 0x0B04 Electrical Measurement). Looking at the cluster.js file the reactive power index is 1294 that is exactly 0x050e so this is the right attribute. In order to have all the thing functioning I've figured that I've to do at least this three steps:

  1. change the device definition inside the devices.js file because I've to add the reference to the configure reporting function for the attribute
  2. update the converter code inside the fromZigbee.js file. A converter is a piece of code responsible to manage the message sent from your ZED (ZigBee End Device) to your ZC (ZigBee Coordinator)
  3. write the configure reporting function for the desired attribute. This function must be written inside the file devices.js on the configureReporting Object Array and is called at the configuration of the ZED when zigbee2mqtt start (if the configureKey parameter is greater than the old used the last time) or when a "zigbee2mqtt/bridge/configure" mqtt message with payload equal to the friendly name of the device is issued to force the zigbee2mqtt to configure the device.

Step 1: change the device definition inside the devices.js file:

{
    zigbeeModel: ['SPLZB-131'],
    model: 'SPLZB-131',
    vendor: 'Develco',
    description: 'Zigbee smart plug with power meter',
    supports: 'on/off, power measurement',
    fromZigbee: [fz.on_off, fz.electrical_measurement],
    toZigbee: [tz.on_off],
    endpoint: (device) => {
        return {'system': 1, 'default': 2};
    },
    meta: {configureKey: 1},
    configure: async (device, coordinatorEndpoint) => {
        const endpoint = device.getEndpoint(2);
        await bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement']);
        await endpoint.read('haElectricalMeasurement', [
            'acVoltageMultiplier', 'acVoltageDivisor', 'acCurrentMultiplier',
            'acCurrentDivisor', 'acPowerMultiplier', 'acPowerDivisor',
        ]);
        await configureReporting.onOff(endpoint);
        await configureReporting.rmsVoltage(endpoint);
        await configureReporting.rmsCurrent(endpoint);
        await configureReporting.activePower(endpoint);
        await configureReporting.reactivePower(endpoint);
    },
},

Step 2: change the converter code inside the fromZigbee.js file

electrical_measurement: {
    /**
     * When using this converter also add the following to the configure method of the device:
     * await endpoint.read('haElectricalMeasurement', [
     *   'acVoltageMultiplier', 'acVoltageDivisor', 'acCurrentMultiplier',
     *   'acCurrentDivisor', 'acPowerMultiplier', 'acPowerDivisor',
     * ]);
     */
    cluster: 'haElectricalMeasurement',
    type: ['attributeReport', 'readResponse'],
    convert: (model, msg, publish, options) => {
        const payload = {};
        if (msg.data.hasOwnProperty('activePower')) {
            const multiplier = msg.endpoint.getClusterAttributeValue(
                'haElectricalMeasurement', 'acPowerMultiplier'
            );
            const divisor = msg.endpoint.getClusterAttributeValue(
                'haElectricalMeasurement', 'acPowerDivisor'
            );
            const factor = multiplier && divisor ? multiplier / divisor : 1;
            payload.power = precisionRound(msg.data['activePower'] * factor, 2);
        }
        if (msg.data.hasOwnProperty('rmsCurrent')) {
            const multiplier = msg.endpoint.getClusterAttributeValue(
                'haElectricalMeasurement', 'acCurrentMultiplier'
            );
            const divisor = msg.endpoint.getClusterAttributeValue('haElectricalMeasurement', 'acCurrentDivisor');
            const factor = multiplier && divisor ? multiplier / divisor : 1;
            payload.current = precisionRound(msg.data['rmsCurrent'] * factor, 2);
        }
        if (msg.data.hasOwnProperty('rmsVoltage')) {
            const multiplier = msg.endpoint.getClusterAttributeValue(
                'haElectricalMeasurement', 'acVoltageMultiplier'
            );
            const divisor = msg.endpoint.getClusterAttributeValue('haElectricalMeasurement', 'acVoltageDivisor');
            const factor = multiplier && divisor ? multiplier / divisor : 1;
            payload.voltage = precisionRound(msg.data['rmsVoltage'] * factor, 2);
        }
        // Update: handling reactive power
        if (msg.data.hasOwnProperty('reactivePower')) {
            const multiplier = msg.endpoint.getClusterAttributeValue(
                'haElectricalMeasurement', 'acPowerMultiplier'
            );
            const divisor = msg.endpoint.getClusterAttributeValue(
                'haElectricalMeasurement', 'acPowerDivisor'
            );
            const factor = multiplier && divisor ? multiplier / divisor : 1;
            payload.reactivepower = precisionRound(msg.data['reactivePower'] * factor, 2);
        }
        return payload;
    },
},},

Step 3: set a new configureReporting function to configure this report attribute

reactivePower: async (endpoint) => {    
    const payload = [{
        attribute: 'reactivePower',
        minimumReportInterval: 1,
        maximumReportInterval: repInterval.MINUTES_5,
        reportableChange: 1,
    }];
    await endpoint.configureReporting('haElectricalMeasurement', payload);
},

Then restart zigbee2mqtt, here below a sample of the log when the load is switched ON and OFF with a load (a IKEA led lamp of 6 W nominal power):

zigbee2mqtt:info  2020-01-14 15:12:03: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"ON","linkquality":105,"voltage":218.83,"current":0.03,"power":5,"reactivepower":-3}'
zigbee2mqtt:info  2020-01-14 15:12:04: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"ON","linkquality":108,"voltage":218.8,"current":0.03,"power":5,"reactivepower":-3}'
zigbee2mqtt:info  2020-01-14 15:12:05: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"ON","linkquality":105,"voltage":218.87,"current":0.03,"power":5,"reactivepower":-3}'
zigbee2mqtt:info  2020-01-14 15:12:06: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"OFF","linkquality":105,"voltage":218.87,"current":0.03,"power":5,"reactivepower":-3}'
zigbee2mqtt:info  2020-01-14 15:12:06: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"OFF","linkquality":108,"voltage":218.87,"current":0.03,"power":5,"reactivepower":-3}'
zigbee2mqtt:info  2020-01-14 15:12:06: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"OFF","linkquality":105,"voltage":218.9,"current":0.03,"power":5,"reactivepower":-3}'
zigbee2mqtt:info  2020-01-14 15:12:07: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"OFF","linkquality":105,"voltage":218.97,"current":0.01,"power":2,"reactivepower":-1}'
zigbee2mqtt:info  2020-01-14 15:12:08: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"OFF","linkquality":105,"voltage":218.76,"current":0,"power":0,"reactivepower":0}'
zigbee2mqtt:info  2020-01-14 15:12:09: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"OFF","linkquality":105,"voltage":218.8,"current":0,"power":0,"reactivepower":0}'
zigbee2mqtt:info  2020-01-14 15:12:10: MQTT publish: topic 'zigbee2mqtt/0xXXXXXXXXXXXXXXXX', payload '{"state":"OFF","linkquality":105,"voltage":218.94,"current":0,"power":0,"reactivepower":0}'

So from the log, when the smart-plug is in ON state the led lamp is powered and the power is about 5 W, reactive power is -3, I've to check about this value. When the smart-plug is in OFF state the load is de-energized and the power is equal to 0.

I kindly request if my steps and reasoning are correct.

Now the next step is try to manage command to the smart-plug, so I think I've to work on the toZigbee.js file side.

Thank!

ChumKiu commented 4 years ago

Just a question about the reportableChange key (excuse me if this may be the right technical definition from the Javascript programming language point of view, feel free to correct me!) inside the configureReporting Object Array, from ZCL spec at 2.5.7.1.7 Reportable Change Field is written:

For attributes of 'discrete' data type (see Table 2-10) this field is omitted.

if we see the onOff related configuration function we have:

onOff: async (endpoint) => {
    const payload = [{
        attribute: 'onOff',
        minimumReportInterval: 0,
        maximumReportInterval: repInterval.HOUR,
        reportableChange: 0,
    }];
    await endpoint.configureReporting('genOnOff', payload);
},

so the value reportableChange: 0, in this context has the meaning that this field is omitted because we are talking about discrete quantity, I'm right? Just to better understanding whole things and the logic behind every concept.

Thank!

Koenkk commented 4 years ago

@ChumKiu yes you are right, for discrete values these are indeed omitted, so the reportableChange that you see there does nothing.

ChumKiu commented 4 years ago

@Koenkk thank for your clarification, do you have also a chance to take a look at the post above? Just to know if I've did all the necessary step or I have to add somethings to be more complete... Thank!

ChumKiu commented 4 years ago

command / read and write API's question

I'm looking how to communicate with the ZED in order to write and read attributes, about this point I kindly request the main difference between the command statements and the read , write API's.

In other words I can see inside the toZigbee.js file (used to send at one ZED a specific message to write/read some attribute) that inside the convertSet section are used mainly:

entity.command(clusterKey: number | string, commandKey: number | string, payload: KeyValue, options?: Options): Promise‹void | KeyValue›
entity.write(  clusterKey: number | string, attributes: KeyValue, options?: Options): Promise‹void›

and into the convertGet section (which I think is used to send messages able to request the reading of some data from a ZED cluster):

entity.read(   clusterKey: number | string, attributes: string[] | number[], options?: Options): Promise‹KeyValue›

So I kindly request if someone can let me know when I've to use the command API instead of the write API because these seems similar to me... be patient I'm studying...

Thank

Koenkk commented 4 years ago
ChumKiu commented 4 years ago
* Could you make a PR for the changes (won't merge it for now but it's much easier to review as we can annotate the code there, just add the open questions there so I can easily answer them).

* Command is cluster specific (e.g. `toggle` command for the `genOnOff` cluster), others are not cluster specific (every cluster supports read, write, configureReporting, etc..)

@Koenkk thank for your help, about the first question you means simply open a new issue and then paste my whole code?

Koenkk commented 4 years ago

No I mean forking the repository and editing the files, e.g. click the pencil/edit icon here: https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/devices.js

ChumKiu commented 4 years ago

@Koenkk, ok understand thank.

zelekinator commented 4 years ago

Hello excuse me for small off topic but this conversation is closest to my small issue. I owe some of Develco devices and wanted to add them to my HA using zigbee2mqtt and mosquito broker addons i have set up to use some Aqara sensors togheter with CC2531. Devices are: Window sensor - WISZB -120 Smart plug - SPLZB -132

Till now i had them paired with Develco Hub (Squid Link) working with some app provided by my energy supplier.

I want to have them under HA. I have followed guidline how to pair not supported devices per zigbee2mqtt documentation (factory reset for both devices to allow pairing etc,)

For both of these devices i have received below error massage from zigbe2mqtt:

zigbee2mqtt:info 2020-01-23 17:58:51: Device ‘0x0015bc001e004dbd’ joined zigbee2mqtt:info 2020-01-23 17:58:51: MQTT publish: topic ‘zigbee2mqtt/bridge/log’, payload ‘{“type”:“device_connected”,“message”:{“friendly_name”:“0x0015bc001e004dbd”}}’ zigbee2mqtt:info 2020-01-23 17:58:51: Starting interview of ‘0x0015bc001e004dbd’ zigbee2mqtt:info 2020-01-23 17:58:51: MQTT publish: topic ‘zigbee2mqtt/bridge/log’, payload ‘{“type”:“pairing”,“message”:“interview_started”,“meta”:{“friendly_name”:“0x0015bc001e004dbd”}}’ zigbee2mqtt:error 2020-01-23 17:59:03: Failed to interview ‘0x0015bc001e004dbd’, device has not successfully been paired zigbee2mqtt:info 2020-01-23 17:59:03: MQTT publish: topic ‘zigbee2mqtt/bridge/log’, payload ‘{“type”:“pairing”,“message”:“interview_failed”,“meta”:{“friendly_name”:“0x0015bc001e004dbd”}}’

Have i missed some step? I would be very grateful for any hints how to pair them.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

kailampi commented 4 years ago

Thank you all very much about this topic. I want to add one thing, if someone else wants also add Develco plug to Home Assistant. You will need add to /opt/zigbee2mqtt/lib/extension/homeassistant.js the following line.

'SPLZB-131': [cfg.switch, cfg.sensor_power, cfg.sensor_voltage],

After all three steps above and this one, the Develco plug SPLZB-131 will appear in the Home Assistant MQTT device list.