Appendix E: Manufacturer-specific attributes and commands

This appendix describes how a manufacturer can add their own custom attributes and commands to a cluster. Attributes and commands are covered in separate sections.

Appendix E.1: Adding Manufacturer-specific Attributes

To add a manufacturer-specific attribute to a cluster:

  1. Specify your manufacturer ID code in the Node descriptor. Do this in the ZPS Configuration Editor by clicking on Node Descriptor for the relevant node and editing the Manufacturer Code field on the Properties tab.

  2. In the zcl_options.h file:

a) Define your manufacturer ID code - for example, for a code of 0x1234, add the line:

#define ZCL_MANUFACTURER_CODE 0x1234

b) Define the macro that enables the use of manufacturer-specific attributes for the cluster - this macro is cluster-specific but for the Electrical Measurement cluster, the relevant line is:

#define CLD_ELECTMEAS_ATTR_MAN_SPEC

c) Define an Attribute ID for the new attribute (you must not use a value already used by another attribute) - for example, to add an attribute with an ID of 0x0B00 to the Electrical Measurement cluster, the relevant line is:

#define E_CLD_ELECTMEAS_ATTR_ID_MAN_SPEC 0x0B00

  1. Add the new attribute to the cluster structure in the cluster’s header file - for example, the code below shows the attribute i16ManufacturerSpecific added to the Electrical Measurement cluster (in ElectricalMeasurement.h):

typedef struct
{
    zbmap32             u32MeasurementType;
#ifdef CLD_ELECTMEAS_ATTR_AC_FREQUENCY
    zuint16             u16ACFrequency;
#endif
#ifdef CLD_ELECTMEAS_ATTR_RMS_VOLTAGE
    zuint16             u16RMSVoltage;
#endif
#ifdef CLD_ELECTMEAS_ATTR_RMS_CURRENT
    zuint16             u16RMSCurrent;
#endif
#ifdef CLD_ELECTMEAS_ATTR_ACTIVE_POWER
    zint16              i16ActivePower;
#endif
#ifdef CLD_ELECTMEAS_ATTR_REACTIVE_POWER
    zint16             i16ReactivePower;
#endif
#ifdef CLD_ELECTMEAS_ATTR_APPARENT_POWER
    zuint16            u16ApparentPower;
#endif
#ifdef CLD_ELECTMEAS_ATTR_POWER_FACTOR
    zint8             i8PowerFactor;
#endif
**\#ifdef CLD\_ELECTMEAS\_ATTR\_MAN\_SPEC**
**    zint16            i16ManufacturerSpecific;**
**\#endif**
} tsCLD_ElectricalMeasurement;

  1. Add the new attribute to the source (.c) file for the cluster, being careful to add the attribute in the correct sequential position - for example, the following code fragment shows the attribute i16ManufacturerSpecific added to the Electrical Measurement cluster (in ElectricalMeasurement.c):

const tsZCL_AttributeDefinition
asCLD_ElectricalMeasurementClusterAttributeDefinitions[] = {
/* ZigBee Cluster Library Version */
                :

#ifdef CLD_ELECTMEAS_ATTR_POWER_FACTOR
{E_CLD_ELECTMEAS_ATTR_ID_POWER_FACTOR,
E_ZCL_AF_RD,
E_ZCL_INT8,
(uint16)(&((tsCLD_ElectricalMeasurement*)(0))->i8PowerFactor),
0}, /* Optional */
#endif

/* Manufacturer-specific Read-only Attribute */
#ifdef CLD_ELECTMEAS_ATTR_MAN_SPEC
{E_CLD_ELECTMEAS_ATTR_ID_MAN_SPEC,
(E_ZCL_AF_RD|E_ZCL_AF_MS),
E_ZCL_INT16,
(uint16)(&((tsCLD_ElectricalMeasurement*)(0))->i16ManufacturerSpecific),
0}, /* Optional */
#endif

/* Manufacturer-specific Read/Write Attribute */
#ifdef CLD_ELECTMEAS_ATTR_MAN_SPEC
{E_CLD_ELECTMEAS_ATTR_ID_MAN_SPEC,
(E_ZCL_AF_RD|E_ZCL_AF_WR|E_ZCL_AF_MS),
E_ZCL_INT16,
(uint16)(&((tsCLD_ElectricalMeasurement*)(0))->i16ManufacturerSpecific),
0}, /* Optional */
#endif

Within your application code, you can remotely read the value of the new attribute using the following function call:

eStatus = eZCL_SendReadAttributesRequest(1, 1, <Add cluster here>, FALSE, 
&sSendAddress, &u8SequenceNumber, u8NumAtts, TRUE, HA_MANUFACTURER_CODE,
<Add your attribute to the list here>);

Parent topic:Appendix E: Manufacturer-specific attributes and commands

Appendix E.2: Adding Manufacturer-specific Commands

To add a manufacturer-specific command to a cluster:

  1. Ensure that a manufacturer ID code has been specified, as described in Appendix E.1.

  2. In the zcl_options.h file, define a Command ID for the new command (you must not use a value already used by another command) - for example, to add a command with an ID of 0x20 to the Basic cluster, the relevant line is:

    #define E_CLD_BASIC_CMD_MANU_SPEC 0x20
    
    
  3. In your application code, introduce a handling routine for the new command into the command handler function that is registered when the cluster instance is created - for example, in the case of the Basic cluster, this handler function is eCLD_BasicCommandHandler(), which is registered when the function eCLD_BasicCreateBasic() is called. Do this as follows:

    a) Define a handler routine specifically for the new command - for example, in the case of the Basic cluster, this function may be eCLD_BasicHandleManuSpecCommand() and has the prototype:

    
    

ZPS_tsAfEvent            *pZPSevent,     tsZCL_EndPointDefinition *psEndPointDefinition,     tsZCL_ClusterInstance    *psClusterInstance); ```

b\) Add the Command ID and the above command-specific handler function into the registered command handler function - for example, in the case of the Basic cluster, the code for **eCLD\_BasicCommandHandler\(\)** would be modified as shown in the fragment below:

    PUBLIC teZCL_Status eCLD_BasicCommandHandler(
    ZPS_tsAfEvent *pZPSevent,
    tsZCL_EndPointDefinition *psEndPointDefinition,
    tsZCL_ClusterInstance *psClusterInstance)
{...
    // SERVER
switch(u8CommandIdentifier)
{
case(E_CLD_BASIC_CMD_RESET_TO_FACTORY_DEFAULTS):
{
eCLD_BasicHandleResetToFactoryDefaultsCommand(pZPSevent, psEndPointDefinition, psClusterInstance);
break;
}
case(E_CLD_BASIC_CMD_MANU_SPEC):
eCLD_BasicHandleManuSpecCommand(pZPSevent, psEndPointDefinition, psClusterInstance);
break;
default:
{
// unlock
eZCL_ReleaseMutex(psEndPointDefinition);
return(E_ZCL_FAIL);
break;
}
}
...

}

   

4. Add a command payload structure for the new command into the source (**.c**) file for the cluster - for example:

typedef struct {     uint8  u8PayloadField1;         uint16 u16PayloadField2;     uint16 u16PayloadField3;     uint32 u32PayloadField4;     }tsMS_ManuSpecCommand;


5. Add a function for sending the command to a remote node into the header (**.h**) and source (**.c**) files for the cluster - for example, in the case of the Basic cluster, the function might be:

PUBLIC teZCL_Status eCLD_BasicCommandManuSpecSend(                     uint8 u8SourceEndpoint,                     uint8 u8DestinationEndpoint,                     tsZCL_Address *psDestinationAddress,                     tsMS_ManuSpecCommand *psManuSpecPayload,               uint8 *pu8TransactionSequenceNumber) {         teZCL_Status eZCL_Status;       tsZCL_TxPayloadItem asPayloadDefinition[] =        {           {1, E_ZCL_UINT8,  &psManuSpecPayload->u8PayloadField1},         {1, E_ZCL_UINT16, &psManuSpecPayload->u16PayloadField2},         {1, E_ZCL_UINT16, &psManuSpecPayload->u16PayloadField3},         {1, E_ZCL_UINT32, &psManuSpecPayload->u32PayloadField4}       };       eZCL_Status = eZCL_CustomCommandSend(u8SourceEndpoint,                   u8DestinationEndpoint,                    psDestinationAddress,                    GENERAL_CLUSTER_ID_BASIC,                                  TRUE,                    E_CLD_BASIC_CMD_MANU_SPEC,                    pu8TransactionSequenceNumber,                   asPayloadDefinition,                    TRUE,                    HA_MANUFACTURER_CODE,                   sizeof(asPayloadDefinition) / sizeof(tsZCL_TxPayloadItem));     return eZCL_Status; }


**Parent topic:**[Appendix E: Manufacturer-specific attributes and commands](../../appendix/topics/manufacturer-specific_attributes_and_commands.md)