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:
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.
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
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;
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:
Ensure that a manufacturer ID code has been specified, as described in Appendix E.1.
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
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)