Merge PR #33: Add support for TI z-Stack based adapters

pull/45/head
Jenkins nymea 2022-03-10 12:33:38 +01:00
commit ea2e1cbf0a
16 changed files with 3006 additions and 7 deletions

View File

@ -3,19 +3,37 @@
This repository contains the nymea-zigbee library and tools.
# Supported hardware
nymea-zigbee is a general purpose ZigBee coordinator library to build ZigBee coordinators/gateways.
The provided zigbee-cli is a minimal ZigBee coordinator implementation which allows to host a ZigBee
network for devices to join and interact with each other but without interacting with the devices.
Depending on your available hardware following gateway modules are supported
For a full fetaured ZigBee coordinator/gateway implementation based on this library, please see
https://github.com/nymea/nymea.
# Supported ZigBee adapters
## TI z-Stack
All USB and serial port adapters based on the Texas Instruments CC1352/CC2652
chipset are supported, provided they are flashed with the z-Stack coordinator firmware.
Pre-built binaries of the firmware are provided by Koenkk:
https://github.com/Koenkk/Z-Stack-firmware/tree/master/coordinator
## NXP
> Note: the firmware erquires an entire rework and implement the APS layer
The following NXP chip based adapters are supported, provided they are flashed with
the nymea coordinator firmware found in this repository.
* JN5168 (SoM)
* JN5169 (USB Stick)
## deCONZ
All deCONZ based adapters are supported, with the standard firmware preinstalled.
It is recommended to update to the latest firmware.
* ConBee
* RaspBee
* ConBee II

3
debian/control vendored
View File

@ -13,7 +13,8 @@ Build-Depends: debhelper (>= 9.0.0),
qtbase5-dev-tools,
libqt5sql5-sqlite,
libqt5serialport5-dev,
libudev-dev
libudev-dev,
libqca-qt5-2-dev,
Package: libnymea-zigbee1
Section: libs

Binary file not shown.

View File

@ -0,0 +1,584 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef TI_H
#define TI_H
#include <QObject>
#define MT_RPC_DATA_MAX 250
class Ti
{
Q_GADGET
public:
enum ZnpVersion {
zStack12 = 0x00,
zStack3x0 = 0x01,
zStack30x = 0x02
};
Q_ENUM(ZnpVersion)
enum ResetType {
ResetTypeHard = 0x00,
ResetTypeSoft = 0x01
};
Q_ENUM(ResetType)
enum ResetReason {
ResetReasonPowerUp = 0x00,
ResetReasonExternal = 0x01,
ResetReasonWatchDog = 0x02
};
Q_ENUM(ResetReason)
enum StartupMode {
StartupModeNormal = 0x00,
StartupModeClean = 0x03
};
Q_ENUM(StartupMode)
enum DeviceLogicalType {
DeviceLogicalTypeCoordinator = 0x00,
DeviceLogicalTypeRouter = 0x01,
DeviceLogicalTypeEndDevice = 0x02,
DeviceLogicalTypeComplexDescriptorAvailable = 0x04,
DeviceLogicalTypeUserDescriptorAvailable = 0x08,
DeviceLogicalTypeReserved1 = 0x10,
DeviceLogicalTypeReserved2 = 0x20,
DeviceLogicalTypeReserved3 = 0x40,
DeviceLogicalTypeReserved4 = 0x80,
};
Q_ENUM(DeviceLogicalType)
enum ControllerCapability {
ControllerCapabilityNone = 0x0000,
ControllerCapabilitySys = 0x0001,
ControllerCapabilityMAC = 0x0002,
ControllerCapabilityNWK = 0x0004,
ControllerCapabilityAF = 0x0008,
ControllerCapabilityZDO = 0x0010,
ControllerCapabilitySAPI = 0x0020,
ControllerCapabilityUtil = 0x0040,
ControllerCapabilityDebug = 0x0080,
ControllerCapabilityApp = 0x0100,
ControllerCapabilityZOAD = 0x1000
};
Q_DECLARE_FLAGS(ControllerCapabilities, ControllerCapability)
Q_FLAG(ControllerCapabilities)
enum StatusCode {
StatusCodeSuccess = 0x00,
StatusCodeFailure = 0x01,
StatusCodeBusy = 0x02,
StatusCodeTimeout = 0x03,
StatusCodeUnsupported = 0x04,
StatusCodeError = 0x05,
StatusCodeNoNetwork = 0x06,
StatusCodeInvalidValue = 0x07
};
Q_ENUM(StatusCode)
enum CommandType {
CommandTypePoll = 0x00,
CommandTypeSReq = 0x20,
CommandTypeAReq = 0x40,
CommandTypeSRsp = 0x60,
};
Q_ENUM(CommandType)
enum SubSystem {
SubSystemReserved = 0x00,
SubSystemSys = 0x01,
SubSystemMAC = 0x02,
SubSystemNwk = 0x03,
SubSystemAF = 0x04,
SubSystemZDO = 0x05,
SubSystemSAPI = 0x06,
SubSystemUtil = 0x07,
SubSystemDebug = 0x08,
SubSystemApp = 0x09,
SubSystemAppCnf = 0x0F,
SubSystemGreenPower = 0x15,
};
Q_ENUM(SubSystem)
enum SYSCommand {
SYSCommandResetReq = 0x00,
SYSCommandPing = 0x01,
SYSCommandVersion = 0x02,
SYSCommandSetExtAddress = 0x03,
SYSCommandGetExtAddress = 0x04,
SYSCommandRamRead = 0x05,
SYSCommandRamWrite = 0x06,
SYSCommandOsalNvItemInit = 0x07,
SYSCommandOsalNvRead = 0x08,
SYSCommandOsaNvWrite = 0x09,
SYSCommandOsalStartTimer = 0x0A,
SYSCommandOsalStopTimer = 0x0B,
SYSCommandOsalRandom = 0x0C,
SYSCommandAdcRead = 0x0D,
SYSCommandGpio = 0x0E,
SYSCommandStackTune = 0x0F,
SYSCommandSetTime = 0x10,
SYSCommandGetTime = 0x11,
SYSCommandOsalNvDelete = 0x12,
SYSCommandOsalNvLength = 0x13,
SYSCommandSetTxPower = 0x14,
SYSCommandJammerParameters = 0x15,
SYSCommandSnifferParameters = 016,
SYSCommandZdiagsInitStats = 0x17,
SYSCommandZdiagsClearStats = 0x18,
SYSCommandZdiagsGetStats = 0x19,
SYSCommandZdiagsRestoreStatsNv = 0x1A,
SYSCommandZdiagsSaveStatsToNv = 0x1B,
SYSCommandOsalNvReadExt = 0x1C,
SYSCommandOsalNvWriteExt = 0x01D,
SYSCommandNvCreate = 0x30,
SYSCommandNvDelete = 0x31,
SYSCommandNvLength = 0x32,
SYSCommandNvRead = 0x33,
SYSCommandNvWrite = 0x34,
SYSCommandNvUpdate = 0x35,
SYSCommandNvCompact = 0x36,
SYSCommandResetInd = 0x80,
SYSCommandOsalTimerExpired = 0x81,
SYSCommandJammerInd = 0x82
};
Q_ENUM(SYSCommand)
enum MACCommand {
MACCommandResetReq = 0x01,
MACCommandInit = 0x02,
MACCommandStartReq = 0x03,
MACCommandSyncReq = 0x04,
MACCommandDataReq = 0x05,
MACCommandAssociateReq = 0x06,
MACCommandDisassociateReq = 0x07,
MACCommandGetReq = 0x08,
MACCommandSetReq = 0x09,
MACCommandScanReq = 0x0C,
MACCommandPollReq = 0x0D,
MACCommandPurgeReq = 0x0E,
MACCommandSetRxGainReq = 0x0F,
MACCommandSecurityGetReq = 0x30,
MACCommandSecuritySetReq = 0x31,
MACCommandAssociateRsp = 0x50,
MACCommandOrphanRsp = 0x51,
MACCommandSyncLossInd = 0x80,
MACCommandAssociateInd = 0x81,
MACCommandAssociateCnf = 0x82,
MACCommandBeaconNotifyInd = 0x83,
MACCommandDataCnf = 0x84,
MACCommandDataInd = 0x85,
MACCommandDisassociateInd = 0x86,
MACCommandDisassociateCnf = 0x87,
MACCommandOrphanInd = 0x8A,
MACCommandPollCnf = 0x8B,
MACCommandScanCnf = 0x8C,
MACCommandCommStatusInd = 0x8D,
MACCommandStartCnf = 0x8E,
MACCommandRxEnableCnf = 0x8F,
MACCommandPurgeCnf = 0x90
};
Q_ENUM(MACCommand)
enum AFCommand {
AFCommandRegister = 0x00,
AFCommandDataRequest = 0x01,
AFCommandDataRequestExt = 0x02,
AFCommandDataRequestSrcRtg = 0x03,
AFCommandDelete = 0x04,
AFCommandInterPanCtl = 0x10,
AFCommandDataStore = 0x11,
AFCommandDataRetrieve = 0x12,
AFCommandApsfConfigSet = 0x13,
AFCommandApsfConfigGet = 0x14,
AFCommandDataConfirm = 0x80,
AFCommandIncomingMsg = 0x81,
AFCommandIncomingMsgExt = 0x82,
AFCommandReflectError = 0x83,
};
Q_ENUM(AFCommand)
enum ZDOCommand {
ZDOCommandNwwAddrReq = 0x00,
ZDOCommandIeeeAddrReq = 0x01,
ZDOCommandNodeDescReq = 0x02,
ZDOCommandPowerDescReq = 0x03,
ZDOCommandSimpleDescReq = 0x04,
ZDOCommandActiveEpReq = 0x05,
ZDOCommandMatchDescReq = 0x06,
ZDOCommandComplexDescReq = 0x07,
ZDOCommandUserDescReq = 0x08,
ZDOCommandEndDeviceAnnce = 0x0A,
ZDOCommandUserDescSet = 0x0B,
ZDOCommandServerDiscReq = 0x0C,
ZDOCommandEndDeviceTimeoutReq = 0x0D,
ZDOCommandEndDeviceBindReq = 0x20,
ZDOCommandBindReq = 0x21,
ZDOCommandUnbindReq = 0x22,
ZDOCommandSetLinkKey = 0x23,
ZDOCommandRemoveLinkKey = 0x24,
ZDOCommandGetLinkKey = 0x25,
ZDOCommandNwkDiscoveryReq = 0x26,
ZDOCommandJoinReq = 0x27,
ZDOCommandSendData = 0x28,
ZDOCommandNwkAddrOfInterestReq = 0x26,
ZDOCommandMgmtNwkDiscReq = 0x30,
ZDOCommandMgmtLqiReq = 0x31,
ZDOCommandMgmtRtgReq = 0x32,
ZDOCommandMgmtBindReq = 0x33,
ZDOCommandMgmtLeaveReq = 0x34,
ZDOCommandMgmtDirectJoinReq = 0x35,
ZDOCommandMgmtPermitJoinReq = 0x36,
ZDOCommandMgmtNwkUpdateReq = 0x37,
ZDOCommandMsgCbRegister = 0x3E,
ZDOCommandMsgCbRemove = 0x3F,
ZDOCommandStartupFromApp = 0x40,
ZDOCommandAutoFindDestination = 0x41,
ZDOCommandSecAddLinkKey = 0x42,
ZDOCommandSecEntryLookupExt = 0x43,
ZDOCommandSecDeviceRemove = 0x044,
ZDOCommandExtRouteDisc = 0x45,
ZDOCommandExtRouteCheck = 0x46,
ZDOCommandExtRemoveGroup = 0x47,
ZDOCommandExtRemoveAllGroup = 0x48,
ZDOCommandExtFindAllGroupsEndpoint = 0x49,
ZDOCommandExtFindGroup = 0x4A,
ZDOCommandExtAddGroup = 0x4B,
ZDOCommandExtCountAllGroups = 0xAC,
ZDOCommandExtRxIdle = 0xAD,
ZDOCommandExtUpdateNwkKey = 0xAE,
ZDOCommandExtSwitchNwkKey = 0xAF,
ZDOCommandExtNwkInfo = 0x50,
ZDOCommandExtSecApsRemoveReq = 0x51,
ZDOCommandForceContentratorChange = 0x52,
ZDOCommandExtSetParams = 0x53,
ZDOCommandNwkAddrRsp = 0x80,
ZDOCommandNwkIeeeAddrRsp = 0x81,
ZDOCommandNodeDescRsp = 0x82,
ZDOCommandPowerDescRsp = 0x83,
ZDOCommandSimpleDescRsp = 0x84,
ZDOCommandActiveEpRsp = 0x85,
ZDOCommandMatchDescRsp = 0x86,
ZDOCommandComplexDescRsp = 0x87,
ZDOCommandUserDescRsp = 0x88,
ZDOCommandUserDescConf = 0x89,
ZDOCommandServerDiscRsp = 0x8A,
ZDOCommandEndDeviceBindRsp = 0xA0,
ZDOCommandBindRsp = 0xA1,
ZDOCommandUnbindRsp = 0xA2,
ZDOCommandMgmtNwkDiscRsp = 0xB0,
ZDOCommandMgmtLqiRsp = 0xB1,
ZDOCommandMgmtRtgRsp = 0xB2,
ZDOCommandMgmtBindRsp = 0xB3,
ZDOCommandMgmtLeaveRsp = 0xB4,
ZDOCommandMgmtDirectJoinRsp = 0xB5,
ZDOCommandMgmtPermitJoinRsp = 0xB6,
ZDOCommandMgmtNwkUpdateNotify = 0xB8,
ZDOCommandStateChangeInd = 0xC0,
ZDOCommandEndDeviceAnnceInd = 0xC1,
ZDOCommandMatchNodeDscRsp = 0xC2,
ZDOCommandStatusErrorRsp = 0xC3,
ZDOCommandSrcRtgInd = 0xC4,
ZDOCommandBeaconNotifyInd = 0xC5,
ZDOCommandJoinCnf = 0xC6,
ZDOCommandNwkDiscoveryCnf = 0xC7,
ZDOCommandConcentratorIndCb = 0xC8,
ZDOCommandLeaveInd = 0xC9,
ZDOCommandTcDeviceInd = 0xCA,
ZDOCommandPermitJoinInd = 0xCB,
ZDOCommandSetRejoinParametersReq = 0xCC,
ZDOCommandMsgCbIncoming = 0xFF
};
Q_ENUM(ZDOCommand)
enum SAPICommand {
SAPICommandStartRequest = 0x00,
SAPICommandSystemReset = 0x09,
SAPICommandBindDevice = 0x01,
SAPICommandAllowBind = 0x02,
SAPICommandSendDataRequest = 0x03,
SAPICommandReadConfiguration = 0x04,
SAPICommandWriteConfiguration = 0x05,
SAPICommandGetDeviceInfo = 0x06,
SAPICommandFindDeviceRequest = 0x07,
SAPICommandPermitJoiningRequest = 0x08,
SAPICommandStartConfirm = 0x80,
SAPICommandBindConfirm = 0x81,
SAPICommandAllowBindConfirm = 0x82,
SAPICommandSendDataConfirm = 0x83,
SAPICommandFindDeviceConfirm = 0x84,
SAPICommandReceiveDataIndication = 0x87,
};
Q_ENUM(SAPICommand)
enum UtilCommand {
UtilCommandGetDeviceInfo = 0x00,
UtilCommandGetNvInfo = 0x01,
UtilCommandSetPanId = 0x02,
UtilCommandSetChannels = 0x03,
UtilCommandSetSecLevel = 0x04,
UtilCommandSetPreCfgKey = 0x05,
UtilCommandCallbackSubCmd = 0x06,
UtilCommandKeyEvent = 0x07,
UtilCommandTimeAlive = 0x09,
UtilCommandLedControl = 0x0A,
UtilCommandTestLoopback = 0x10,
UtilCommandDataReq = 0x11,
UtilCommandGpioSetDirection = 0x14,
UtilCommandGpioRead = 0x15,
UtilCommandGpioWrite = 0x16,
UtilCommandSrcMatchEnable = 0x20,
UtilCommandSrcMatchAddEntry = 0x21,
UtilCommandSrcMatchDelEntry = 0x22,
UtilCommandSrcMatchCheckSrcAddr = 0x23,
UtilCommandSrcMatchAckAllPending = 0x24,
UtilCommandSrcMatchCheckAllPending = 0x25,
UtilCommandAddrMgrExtAddrLookup = 0x40,
UtilCommandAddrMgrNwkAddrLookup = 0x41,
UtilCommandApsmeLinkKeyDataGet = 0x44,
UtilCommandApsmeLinkKeyNvIdGet = 0x45,
UtilCommandAssocCount = 0x48,
UtilCommandAssocFindDevice = 0x49,
UtilCommandAssocGetWithAddress = 0x4A,
UtilCommandApsmeRequestKeyCmd = 0x4B,
UtilCommandSrngGen = 0x4C,
UtilCommandBindAddKey = 0x4D,
UtilCommandAssocRemove = 0x63,
UtilCommandAssocAdd = 0x64,
UtilCommandZclKeyEstInitEst = 0x80,
UtilCommandZclKeyEstSign = 0x81,
UtilCommandSyncReq = 0xE0,
UtilCommandZclKeyEstablishInd = 0xE1,
};
Q_ENUM(UtilCommand)
enum DebugCommand {
DebugCommandSetThreshold = 0x00,
DebugCommandMsg = 0x80,
};
Q_ENUM(DebugCommand)
enum AppCommand {
AppCommandMsg = 0x00,
AppCommandUserTest = 0x01,
AppCommandTllTlInd = 0x81,
};
Q_ENUM(AppCommand)
enum AppCnfCommand {
AppCnfCommandBdbStartCommissioning = 0x05,
AppCnfCommandBdbSetChannel = 0x08,
AppCnfCommandBdbSetTcRequireKeyExchange = 0x09,
AppCnfCommandBdbCommissioningNotification = 0x80,
AppCnfCommandSetNwkFrameCounter = 0xFF
};
Q_ENUM(AppCnfCommand)
enum GreenPowerCommand {
GreenPowerCommandSecReq = 0x03,
};
Q_ENUM(GreenPowerCommand)
enum NvItemId {
NvItemIdExtAddr = 0x01,
NvItemIdBootCounter = 0x02,
NvItemIdStartupOption = 0x03,
NvItemIdStartDelay = 0x04,
NvItemIdNIB = 0x21,
NvItemIdDeviceList = 0x22,
NvItemIdAddrMgr = 0x23,
NvItemIdPollRate = 0x24,
NvItemIdQueuedPollRate = 0x25,
NvItemIdResponsePollRate = 0x26,
NvItemIdRejoinPollRate = 0x27,
NvItemIdDataRetries = 0x28,
NvItemIdPollFailureRetries = 0x29,
NvItemIdStackProfile = 0x2A,
NvItemIdIndirectMsgTimeout = 0x2B,
NvItemIdRouteExpiryTime = 0x2C,
NvItemIdExtendedPanId = 0x2D,
NvItemIdBcastRetries = 0x2E,
NvItemIdPassiveAckTimeout = 0x2F,
NvItemIdBcastDeliveryTime = 0x30,
NvItemIdNwkMode = 0x31,
NvItemIdConcentratorEnable = 0x32,
NvItemIdConcentratorDiscovery = 0x33,
NvItemIdConcentratorRadius = 0x34,
NvItemIdConcentratorRC = 0x36,
NvItemIdMwkMgrMode = 0x37,
NvItemIdSrcRtgExpiryTime = 0x38,
NvItemIdRouteDiscoveryTime = 0x39,
NvItemIdNwkActiveKeyInfo = 0x3A,
NvItemIdNwkAlternKeyInfo = 0x3B,
NvItemIdRouterOffAssocCleanup = 0x3C,
NvItemIdNwkLeaveReqAllowed = 0x3D,
NvItemIdNwkChildAgeEnable = 0x3E,
NvItemIdDeviceListKaTimeout = 0x3F,
NvItemIdBindingTable = 0x41,
NvItemIdGroupTable = 0x42,
NvItemIdApsFrameRetries = 0x43,
NvItemIdApsAckWaitDuration = 0x44,
NvItemIdApsAckWaitMultiplier = 0x45,
NvItemIdBindingTime = 0x46,
NvItemIdApsUseExtPanId = 0x47,
NvItemIdApsUseInsecureJoin = 0x48,
NvItemIdCommisionedNwkAddr = 0x49,
NvItemIdApsNonMemberRadious = 0x4B,
NvItemIdApsLinkKeyTable = 0x4C,
NvItemIdApsDuprejTimeoutInc = 0x4D,
NvItemIdApsDuprejTimeoutCount = 0x4E,
NvItemIdApsDuprejTableSize = 0x4F,
NvItemIdDiagnosticStats = 0x50,
NvItemIdBdbNodeIsOnANetwork = 0x55,
NvItemIdSecurityLevel = 0x61,
NvItemIdPreCfgKey = 0x62,
NvItemIdPreCfgKeysEnable = 0x63,
NvItemIdSecurityMode = 0x64,
NvItemIdSecurePermitJoin = 0x65,
NvItemIdApsLinkKeyType = 0x66,
NvItemIdApsAllowR19Security = 0x67,
NvItemIdImplicitCertificate = 0x69,
NvItemIdDevicePrivateKey = 0x6A,
NvItemIdCaPublicKey = 0x6B,
NvItemIdKeMaxDevices = 0x6C,
NvItemIdUseDefaultTclk = 0x6D,
NvItemIdRngCounter = 0x6F,
NvItemIdRandomSeed = 0x70,
NvItemIdTrustcenterAddr = 0x71,
NvItemIdLegacyNwkSecMaterialTableStart = 0x74, // Valid for <= Z-Stack 3.0.x
NvItemIdExNwkSecMaterialTable = 0x07, // Valid for >= Z-Stack 3.x.0
NvItemIdUserDesc = 0x81,
NvItemIdNwkKey = 0x82,
NvItemIdPanId = 0x83,
NvItemIdChanList = 0x84,
NvItemIdLeaveCtrl = 0x85,
NvItemIdScanDuration = 0x86,
NvItemIdLogicalType = 0x87,
NvItemIdNwkMgrMinTx = 0x88,
NvItemIdNwkMgrAddr = 0x89,
NvItemIdZdoDirectCb = 0x8F,
NvItemIdSceneTable = 0x91,
NvItemIdMinFreeNwkAddr = 0x92,
NvItemIdMaxFreeNwkAddr = 0x93,
NvItemIdMinFreeGrpId = 0x94,
NvItemIdMaxFreeGrpId = 0x95,
NvItemIdMinGrpIds = 0x96,
NvItemIdMaxGrpIds = 0x97,
NvItemIdOtaBlockReqDelay = 0x98,
NvItemIdSAPIEndpoint = 0xA1,
NvItemIdSASShortAddr = 0xB1,
NvItemIdSASExtPanId = 0xB2,
NvItemIdSASPanId = 0xB3,
NvItemIdSASChannelMask = 0xB4,
NvItemIdSASProtocolVer = 0xB5,
NvItemIdSASStackProfile = 0xB6,
NvItemIdSASStartupCtrl = 0xB7,
NvItemIdSASTcAddr = 0xC1,
NvItemIdSASTcMasterKey = 0xC2,
NvItemIdSASNwkKey = 0xC3,
NvItemIdSASUseInsecJoin = 0xC4,
NvItemIdSASPreCfgLinkKey = 0xC5,
NvItemIdSASNwkKeySeqNum = 0xC6,
NvItemIdSASNwkKeyType = 0xC7,
NvItemIdSASNwkMgrAddr = 0xC8,
NvItemIdSASCurrTcMasterKey = 0xD1,
NvItemIdSASCurrNwkKey = 0xD2,
NvItemIdSASCurrPreCfgLinkKey = 0xD3,
NvItemIdTclkSeed = 0x101,
NvItemIdLegacyTclkTableStart_12 = 0x101, // Keep it for Legacy 1.2 stack
NvItemIdLegacyTclkTableStart = 0x111, // Valid for <= Z-Stack 3.0.x
NvItemIdExTclkTable = 0x04, // Valid for >= Z-Stack 3.0.x
NvItemIdApsLinkKeyDataStart = 0x201,
NvItemIdApsLinkKeyDataEnd = 0x2FF,
NvItemIdDuplicateBindingTable = 0x300,
NvItemIdDuplicateDeviceList = 0x301,
NvItemIdDuplicateDeviceListKaTimeout = 0x302,
NvItemIdZnpHasConfiguredStack1 = 0xF00,
NvItemIdZnpHasConfiguredStack3 = 0x60,
NvItemIdZcdNvExApsKeyDataTable = 0x06,
NvItemIdZcdNvExAddrMgr = 0x01
};
Q_ENUM(NvItemId)
enum TxOption {
TxOptionNone = 0x00,
TxOptionsWildcardProfileId = 0x02,
TxOptionPreprocess = 0x04,
TxOptionLimitConcentrator = 0x08,
TxOptionApsAck = 0x10,
TxOptionSuppressRouteDiscovery = 0x20,
TxOptionApsSecurity = 0x40,
TxOptionSkipRouting = 0x80,
TxOptionBroadCastEndpoint = 0xFF
};
Q_DECLARE_FLAGS(TxOptions, TxOption)
Q_FLAG(TxOption)
};
#endif // TI_H

View File

@ -0,0 +1,267 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeeinterfaceti.h"
#include "zigbee.h"
#include "zigbeeutils.h"
#include "loggingcategory.h"
#include <QDataStream>
ZigbeeInterfaceTi::ZigbeeInterfaceTi(QObject *parent) : QObject(parent)
{
m_reconnectTimer = new QTimer(this);
m_reconnectTimer->setSingleShot(true);
m_reconnectTimer->setInterval(5000);
connect(m_reconnectTimer, &QTimer::timeout, this, &ZigbeeInterfaceTi::onReconnectTimeout);
}
ZigbeeInterfaceTi::~ZigbeeInterfaceTi()
{
}
bool ZigbeeInterfaceTi::available() const
{
return m_available;
}
QString ZigbeeInterfaceTi::serialPort() const
{
return m_serialPort->portName();
}
void ZigbeeInterfaceTi::sendMagicByte()
{
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream << static_cast<quint8>(0xef);
m_serialPort->write(message);
}
void ZigbeeInterfaceTi::setDTR(bool dtr)
{
m_serialPort->setDataTerminalReady(dtr);
}
void ZigbeeInterfaceTi::setRTS(bool rts)
{
m_serialPort->setRequestToSend(rts);
}
quint8 ZigbeeInterfaceTi::calculateChecksum(const QByteArray &data)
{
quint8 checksum = 0;
for (int i = 0; i < data.length(); i++) {
checksum ^= static_cast<quint8>(data.at(i));
}
return checksum;
}
void ZigbeeInterfaceTi::setAvailable(bool available)
{
if (m_available == available)
return;
// Clear the data buffer in any case
if (m_available) {
m_dataBuffer.clear();
}
m_available = available;
emit availableChanged(m_available);
}
void ZigbeeInterfaceTi::onReconnectTimeout()
{
if (m_serialPort && !m_serialPort->isOpen()) {
if (!m_serialPort->open(QSerialPort::ReadWrite)) {
setAvailable(false);
qCDebug(dcZigbeeInterface()) << "Interface reconnected failed" << m_serialPort->portName() << m_serialPort->baudRate();
m_reconnectTimer->start();
} else {
qCDebug(dcZigbeeInterface()) << "Interface reconnected successfully on" << m_serialPort->portName() << m_serialPort->baudRate();
m_serialPort->clear();
setAvailable(true);
}
}
}
void ZigbeeInterfaceTi::onReadyRead()
{
m_dataBuffer.append(m_serialPort->readAll());
processBuffer();
}
void ZigbeeInterfaceTi::processBuffer()
{
if (m_dataBuffer.isEmpty()) {
return;
}
qCDebug(dcZigbeeInterfaceTraffic()) << "<--" << m_dataBuffer.toHex();
// StartOfFrame
if (static_cast<quint8>(m_dataBuffer.at(0)) != SOF) {
qCWarning(dcZigbeeInterface()) << "Data doesn't start with StartOfFrame byte 0xfe. Discarding data...";
m_dataBuffer.remove(0, 1);
processBuffer();
return;
}
// payload length
quint8 payloadLength = static_cast<quint8>(m_dataBuffer[1]);
// Packet must be SOF + payload length field + CMD0 + CMD1 + payload length + Checksum
if (m_dataBuffer.length() < payloadLength + 5) {
qCDebug(dcZigbeeInterface()) << "Not enough data in buffer....";
return;
}
QByteArray packet = m_dataBuffer.left(5 + payloadLength);
m_dataBuffer.remove(0, 5 + payloadLength);
quint8 cmd0 = static_cast<quint8>(packet[2]);
quint8 cmd1 = static_cast<quint8>(packet[3]);
QByteArray payload = packet.mid(4, payloadLength);
quint8 checksum = packet.at(4 + payloadLength);
if (calculateChecksum(packet.mid(1, 3 + payloadLength)) != checksum) {
qCWarning(dcZigbeeInterface()) << "Checksum mismatch!";
processBuffer();
return;
}
// qCDebug(dcZigbeeInterface()) << "packet received:" << payloadLength << cmd0 << command << payload.toHex(' ') << checksum;
Ti::SubSystem subSystem = static_cast<Ti::SubSystem>(cmd0 & 0x1F);
Ti::CommandType type = static_cast<Ti::CommandType>(cmd0 & 0xE0);
emit packetReceived(subSystem, type, cmd1, payload);
// In case there's more...
processBuffer();
}
void ZigbeeInterfaceTi::onError(const QSerialPort::SerialPortError &error)
{
if (error != QSerialPort::NoError && m_serialPort->isOpen()) {
qCWarning(dcZigbeeInterface()) << "Serial port error:" << error << m_serialPort->errorString();
m_reconnectTimer->start();
m_serialPort->close();
setAvailable(false);
}
}
void ZigbeeInterfaceTi::sendPacket(Ti::CommandType type, Ti::SubSystem subSystem, quint8 command, const QByteArray &payload)
{
if (!m_available) {
qCWarning(dcZigbeeInterface()) << "Can not send data. The interface is not available";
return;
}
quint8 cmd0 = type | subSystem;
// Build transport data
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << static_cast<quint8>(SOF);
stream << static_cast<quint8>(payload.length());
stream << cmd0;
stream << static_cast<quint8>(command);
for (int i = 0; i < payload.length(); i++) {
stream << static_cast<quint8>(payload.at(i));
}
stream << calculateChecksum(data.right(data.length() - 1));
// Send the data
qCDebug(dcZigbeeInterfaceTraffic()) << "-->" << data.toHex();
if (m_serialPort->write(data) < 0) {
qCWarning(dcZigbeeInterface()) << "Could not stream byte" << ZigbeeUtils::convertByteArrayToHexString(data);
}
//m_serialPort->flush();
}
bool ZigbeeInterfaceTi::enable(const QString &serialPort, qint32 baudrate)
{
qCDebug(dcZigbeeInterface()) << "Start UART interface " << serialPort << baudrate;
if (m_serialPort) {
delete m_serialPort;
m_serialPort = nullptr;
}
m_serialPort = new QSerialPort(serialPort, this);
m_serialPort->setBaudRate(baudrate);
m_serialPort->setDataBits(QSerialPort::Data8);
m_serialPort->setStopBits(QSerialPort::OneStop);
m_serialPort->setParity(QSerialPort::NoParity);
m_serialPort->setFlowControl(QSerialPort::NoFlowControl);
connect(m_serialPort, &QSerialPort::readyRead, this, &ZigbeeInterfaceTi::onReadyRead);
typedef void (QSerialPort::* errorSignal)(QSerialPort::SerialPortError);
connect(m_serialPort, static_cast<errorSignal>(&QSerialPort::error), this, &ZigbeeInterfaceTi::onError);
if (!m_serialPort->open(QSerialPort::ReadWrite)) {
qCWarning(dcZigbeeInterface()) << "Could not open serial port" << serialPort << baudrate << m_serialPort->errorString();
m_reconnectTimer->start();
return false;
}
qCDebug(dcZigbeeInterface()) << "Interface enabled successfully on" << serialPort << baudrate;
m_serialPort->clear();
setAvailable(true);
return true;
}
void ZigbeeInterfaceTi::reconnectController()
{
if (!m_serialPort)
return;
if (m_serialPort->isOpen())
m_serialPort->close();
delete m_serialPort;
m_serialPort = nullptr;
setAvailable(false);
m_reconnectTimer->start();
}
void ZigbeeInterfaceTi::disable()
{
if (!m_serialPort)
return;
if (m_serialPort->isOpen())
m_serialPort->close();
delete m_serialPort;
m_serialPort = nullptr;
setAvailable(false);
qCDebug(dcZigbeeInterface()) << "Interface disabled";
}

View File

@ -0,0 +1,80 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef ZIGBEEINTERFACETI_H
#define ZIGBEEINTERFACETI_H
#include <QObject>
#include <QTimer>
#include <QSerialPort>
#include "zigbeeinterfacetireply.h"
#define SOF 0xFE
class ZigbeeInterfaceTi : public QObject
{
Q_OBJECT
public:
explicit ZigbeeInterfaceTi(QObject *parent = nullptr);
~ZigbeeInterfaceTi();
bool available() const;
QString serialPort() const;
void sendMagicByte();
void setDTR(bool dtr);
void setRTS(bool rts);
void sendPacket(Ti::CommandType type, Ti::SubSystem subSystem, quint8 command, const QByteArray &payload);
public slots:
bool enable(const QString &serialPort = "/dev/ttyS0", qint32 baudrate = 38400);
void reconnectController();
void disable();
signals:
void availableChanged(bool available);
void packetReceived(Ti::SubSystem subSystem, Ti::CommandType type, quint8 command, const QByteArray &payload);
private slots:
void onReconnectTimeout();
void onReadyRead();
void onError(const QSerialPort::SerialPortError &error);
void processBuffer();
private:
QTimer *m_reconnectTimer = nullptr;
QSerialPort *m_serialPort = nullptr;
bool m_available = false;
QByteArray m_dataBuffer;
quint8 calculateChecksum(const QByteArray &data);
void setAvailable(bool available);
};
#endif // ZIGBEEINTERFACETI_H

View File

@ -0,0 +1,103 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeeinterfacetireply.h"
quint8 ZigbeeInterfaceTiReply::command() const
{
return m_command;
}
Ti::SubSystem ZigbeeInterfaceTiReply::subSystem() const
{
return m_subSystem;
}
QByteArray ZigbeeInterfaceTiReply::requestPayload() const
{
return m_requestPayload;
}
QByteArray ZigbeeInterfaceTiReply::responsePayload() const
{
return m_responsePayload;
}
Ti::StatusCode ZigbeeInterfaceTiReply::statusCode() const
{
return m_statusCode;
}
bool ZigbeeInterfaceTiReply::timendOut() const
{
return m_timeout;
}
bool ZigbeeInterfaceTiReply::aborted() const
{
return m_aborted;
}
void ZigbeeInterfaceTiReply::abort()
{
m_timer->stop();
m_aborted = true;
emit finished();
}
ZigbeeInterfaceTiReply::ZigbeeInterfaceTiReply(QObject *parent, int timeout):
QObject(parent),
m_timer(new QTimer(this))
{
m_timer->setInterval(timeout);
m_timer->setSingleShot(true);
connect(m_timer, &QTimer::timeout, this, &ZigbeeInterfaceTiReply::onTimeout);
// We'll auto-delete ourselves.
connect(this, &ZigbeeInterfaceTiReply::finished, this, &QObject::deleteLater, Qt::QueuedConnection);
}
ZigbeeInterfaceTiReply::ZigbeeInterfaceTiReply(Ti::SubSystem subSystem, quint8 command, QObject *parent, const QByteArray &requestPayload, int timeout) :
ZigbeeInterfaceTiReply(parent, timeout)
{
m_subSystem = subSystem;
m_command = command;
m_requestPayload = requestPayload;
}
void ZigbeeInterfaceTiReply::finish(Ti::StatusCode statusCode)
{
m_statusCode = statusCode;
emit finished();
}
void ZigbeeInterfaceTiReply::onTimeout()
{
m_timeout = true;
emit timeout();
emit finished();
}

View File

@ -0,0 +1,88 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef ZIGBEEINTERFACETIREPLY_H
#define ZIGBEEINTERFACETIREPLY_H
#include <QObject>
#include <QTimer>
#include "ti.h"
#include "zigbeenetworkrequest.h"
class ZigbeeInterfaceTiReply: public QObject
{
Q_OBJECT
friend class ZigbeeBridgeControllerTi;
public:
explicit ZigbeeInterfaceTiReply(QObject *parent = nullptr, int timeout = 5000);
explicit ZigbeeInterfaceTiReply(Ti::SubSystem subSystem, quint8 command, QObject *parent = nullptr, const QByteArray &requestPayload = QByteArray(), int timeout = 5000);
// Request content
Ti::SubSystem subSystem() const;
quint8 command() const;
QByteArray requestPayload() const;
QByteArray responsePayload() const;
// Response content
Ti::StatusCode statusCode() const;
bool timendOut() const;
bool aborted() const;
void abort();
signals:
void timeout();
void finished();
private slots:
void onTimeout();
void finish(Ti::StatusCode statusCode = Ti::StatusCodeSuccess);
private:
QTimer *m_timer = nullptr;
bool m_timeout = false;
bool m_aborted = false;
void setSequenceNumber(quint8 sequenceNumber);
// Request content
Ti::SubSystem m_subSystem = Ti::SubSystemSys;
quint8 m_command = 0;
QByteArray m_requestPayload;
// Response content
Ti::StatusCode m_statusCode = Ti::StatusCodeError;
QByteArray m_responsePayload;
};
#endif // ZIGBEEINTERFACETIREPLY_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,154 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef ZIGBEEBRIDGECONTROLLERTI_H
#define ZIGBEEBRIDGECONTROLLERTI_H
#include <QHash>
#include <QTimer>
#include <QQueue>
#include <QObject>
#include "zigbee.h"
#include "zigbeenetwork.h"
#include "zigbeeaddress.h"
#include "zigbeenetworkkey.h"
#include "zigbeenetworkrequest.h"
#include "zigbeebridgecontroller.h"
#include "interface/ti.h"
#include "interface/zigbeeinterfaceti.h"
#include "interface/zigbeeinterfacetireply.h"
typedef struct TiNetworkConfiguration {
ZigbeeAddress ieeeAddress; // R
quint16 panId = 0; // R
quint16 shortAddress = 0; // R
quint64 extendedPanId = 0; // R
ZigbeeChannelMask channelMask = 0; // RW
ZigbeeNetworkKey networkKey; // RW
quint8 currentChannel = 0; // R
Ti::ZnpVersion znpVersion = Ti::zStack12;
} TiNetworkConfiguration;
class ZigbeeBridgeControllerTi : public ZigbeeBridgeController
{
Q_OBJECT
friend class ZigbeeNetworkTi;
public:
enum ControllerState {
ControllerStateDown,
ControllerStateInitialized,
ControllerStateRunning,
};
Q_ENUM(ControllerState)
explicit ZigbeeBridgeControllerTi(QObject *parent = nullptr);
~ZigbeeBridgeControllerTi() override;
ControllerState state() const;
ZigbeeInterfaceTiReply *init();
ZigbeeInterfaceTiReply *commission(Ti::DeviceLogicalType deviceType, quint16 panId, const ZigbeeChannelMask &channelMask);
ZigbeeInterfaceTiReply *start();
ZigbeeInterfaceTiReply *reset();
ZigbeeInterfaceTiReply *factoryReset();
// Network config will be available after initialisation.
TiNetworkConfiguration networkConfiguration() const;
// Anything else is available once running
ZigbeeInterfaceTiReply *setLed(bool on);
ZigbeeInterfaceTiReply *requestPermitJoin(quint8 seconds, const quint16 &networkAddress);
ZigbeeInterfaceTiReply *registerEndpoint(quint8 endpointId, Zigbee::ZigbeeProfile profile, quint16 deviceId, quint8 deviceVersion);
ZigbeeInterfaceTiReply *addEndpointToGroup(quint8 endpointId, quint16 groupId);
// Send APS request data
ZigbeeInterfaceTiReply *requestSendRequest(const ZigbeeNetworkRequest &request);
public slots:
bool enable(const QString &serialPort, qint32 baudrate);
void disable();
signals:
void controllerStateChanged(ControllerState state);
void permitJoinStateChanged(qint8 duration);
void deviceIndication(quint16 shortAddress, const ZigbeeAddress &ieeeAddress, quint8 macCapabilities);
void nodeLeft(const ZigbeeAddress &ieeeAddress, bool request, bool remove, bool rejoin);
void deviceProfileIndicationReceived(const Zigbee::ApsdeDataIndication &indication);
void clusterLibraryIndicationReceived(const Zigbee::ApsdeDataIndication &indication);
private slots:
void onInterfaceAvailableChanged(bool available);
void onInterfacePacketReceived(Ti::SubSystem subSystem, Ti::CommandType commandType, quint8 command, const QByteArray &payload);
void sendNextRequest();
void initPhase2(ZigbeeInterfaceTiReply* initReply, int attempt);
void postStartup();
private:
ZigbeeInterfaceTiReply *sendCommand(Ti::SubSystem subSystem, quint8 command, const QByteArray &payload = QByteArray(), int timeout = 5000);
ZigbeeInterfaceTiReply *readNvItem(Ti::NvItemId itemId, quint16 offset = 0);
ZigbeeInterfaceTiReply *writeNvItem(Ti::NvItemId itemId, const QByteArray &data, quint16 offset = 0);
ZigbeeInterfaceTiReply *deleteNvItem(Ti::NvItemId itemId);
void retrieveHugeMessage(const Zigbee::ApsdeDataIndication &pendingIndication, quint32 timestamp, quint16 dataLength);
void waitFor(ZigbeeInterfaceTiReply *reply, Ti::SubSystem subSystem, quint8 command);
void waitFor(ZigbeeInterfaceTiReply *reply, Ti::SubSystem subSystem, quint8 command, const QByteArray &payload);
struct WaitData {
Ti::SubSystem subSystem;
quint8 command;
QByteArray payload;
bool comparePayload = false;
};
QHash<ZigbeeInterfaceTiReply*, WaitData> m_waitFors;
ZigbeeInterfaceTi *m_interface = nullptr;
TiNetworkConfiguration m_networkConfiguration;
ControllerState m_controllerState = ControllerStateDown;
ZigbeeInterfaceTiReply *m_currentReply = nullptr;
QQueue<ZigbeeInterfaceTiReply *> m_replyQueue;
QTimer m_permitJoinTimer;
QList<int> m_registeredEndpointIds;
void finishRequest(Ti::StatusCode statusCode = Ti::StatusCodeSuccess);
};
QDebug operator<<(QDebug debug, const TiNetworkConfiguration &configuration);
#endif // ZIGBEEBRIDGECONTROLLERTI_H

View File

@ -0,0 +1,541 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zdo/zigbeedeviceprofile.h"
#include "zigbeenetworkti.h"
#include "loggingcategory.h"
#include "zigbeeutils.h"
#include "zigbeenetworkdatabase.h"
#include <QDataStream>
#include <QtCrypto>
ZigbeeNetworkTi::ZigbeeNetworkTi(const QUuid &networkUuid, QObject *parent) :
ZigbeeNetwork(networkUuid, parent)
{
m_controller = new ZigbeeBridgeControllerTi(this);
connect(m_controller, &ZigbeeBridgeControllerTi::availableChanged, this, &ZigbeeNetworkTi::onControllerAvailableChanged);
connect(m_controller, &ZigbeeBridgeControllerTi::controllerStateChanged, this, &ZigbeeNetworkTi::onControllerStateChanged);
connect(m_controller, &ZigbeeBridgeControllerTi::firmwareVersionChanged, this, &ZigbeeNetworkTi::firmwareVersionChanged);
connect(m_controller, &ZigbeeBridgeControllerTi::permitJoinStateChanged, this, &ZigbeeNetworkTi::onPermitJoinStateChanged);
connect(m_controller, &ZigbeeBridgeControllerTi::deviceIndication, this, &ZigbeeNetworkTi::onDeviceIndication);
connect(m_controller, &ZigbeeBridgeControllerTi::apsDataIndicationReceived, this, &ZigbeeNetworkTi::onApsDataIndicationReceived);
connect(m_controller, &ZigbeeBridgeControllerTi::apsDataConfirmReceived, this, &ZigbeeNetworkTi::onApsDataConfirmReceived);
connect(m_controller, &ZigbeeBridgeControllerTi::nodeLeft, this, &ZigbeeNetworkTi::onNodeLeaveIndication);
}
ZigbeeBridgeController *ZigbeeNetworkTi::bridgeController() const
{
if (!m_controller)
return nullptr;
return m_controller;
}
Zigbee::ZigbeeBackendType ZigbeeNetworkTi::backendType() const
{
return Zigbee::ZigbeeBackendTypeTi;
}
ZigbeeNetworkReply *ZigbeeNetworkTi::sendRequest(const ZigbeeNetworkRequest &request)
{
ZigbeeNetworkReply *reply = createNetworkReply(request);
// Finish the reply right away if the network is offline
if (!m_controller->available() || state() == ZigbeeNetwork::StateOffline || state() == ZigbeeNetwork::StateStopping) {
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorNetworkOffline);
return reply;
}
if (state() == ZigbeeNetwork::StateStarting) {
m_requestQueue.append(reply);
return reply;
}
ZigbeeInterfaceTiReply *interfaceReply = m_controller->requestSendRequest(request);
connect(interfaceReply, &ZigbeeInterfaceTiReply::finished, reply, [this, reply, interfaceReply](){
if (interfaceReply->statusCode() != Ti::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could send request to controller." << interfaceReply->statusCode();
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorInterfaceError);
return;
}
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorNoError);
});
return reply;
}
void ZigbeeNetworkTi::setPermitJoining(quint8 duration, quint16 address)
{
if (duration > 0) {
qCDebug(dcZigbeeNetwork()) << "Set permit join for" << duration << "s on" << ZigbeeUtils::convertUint16ToHexString(address);
} else {
qCDebug(dcZigbeeNetwork()) << "Disable permit join on"<< ZigbeeUtils::convertUint16ToHexString(address);
}
setPermitJoiningDuration(duration);
ZigbeeInterfaceTiReply *requestPermitJoinReply = m_controller->requestPermitJoin(duration, address);
connect(requestPermitJoinReply, &ZigbeeInterfaceTiReply::finished, this, [=](){
if (requestPermitJoinReply->statusCode() != Ti::StatusCodeSuccess) {
qCWarning(dcZigbeeNetwork()) << "Could not set permit join to" << duration << ZigbeeUtils::convertUint16ToHexString(address) << requestPermitJoinReply->statusCode();
return;
}
qCDebug(dcZigbeeNetwork()) << "Permit join request finished successfully";
// Opening the green power network too
// Todo: This should probably be somewhere else, but not yet sure how other backeds deal with this
QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(0x0b); // options
stream << static_cast<quint16>(duration);
// Build ZCL frame control
ZigbeeClusterLibrary::FrameControl frameControl;
frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific;
frameControl.manufacturerSpecific = false;
frameControl.direction = ZigbeeClusterLibrary::DirectionServerToClient;
frameControl.disableDefaultResponse = true;
// Build ZCL header
ZigbeeClusterLibrary::Header header;
header.frameControl = frameControl;
header.command = 0x02;// TODO: ZigbeeClusterGreenPower::ClusterCommandCommissioningMode;
header.transactionSequenceNumber = generateSequenceNumber();
// Build ZCL frame
ZigbeeClusterLibrary::Frame frame;
frame.header = header;
frame.payload = payload;
ZigbeeNetworkRequest request;
request.setRequestId(generateSequenceNumber());
request.setDestinationAddressMode(Zigbee::DestinationAddressModeShortAddress);
request.setDestinationShortAddress(0xFFFD);
request.setDestinationEndpoint(242); // Green Power Endpoint
request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP
request.setClusterId(0x21);
request.setTxOptions(static_cast<Zigbee::ZigbeeTxOption>(0x00)); // FIXME: There should be TxOptionsNone I guess...
request.setSourceEndpoint(242); // Green Power Endpoint
request.setRadius(30); // FIXME: There should be a more clever way to figure out the radius
request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame));
m_controller->requestSendRequest(request);
});
}
void ZigbeeNetworkTi::initController()
{
qCDebug(dcZigbeeNetwork()) << "Initializing controller";
setState(StateStarting);
setError(ErrorNoError);
ZigbeeInterfaceTiReply *initReply = m_controller->init();
connect(initReply, &ZigbeeInterfaceTiReply::finished, this, [=](){
if (initReply->statusCode() != Ti::StatusCodeSuccess) {
qCWarning(dcZigbeeNetwork()) << "Error initializing controller";
setState(StateUninitialized);
setError(ErrorZigbeeError);
return;
}
});
}
void ZigbeeNetworkTi::commissionController()
{
qCDebug(dcZigbeeNetwork()) << "Commissioning controller";
quint16 panId = ZigbeeUtils::generateRandomPanId();
ZigbeeInterfaceTiReply *commissionReply = m_controller->commission(Ti::DeviceLogicalTypeCoordinator, panId, channelMask());
connect(commissionReply, &ZigbeeInterfaceTiReply::finished, this, [=](){
if (commissionReply->statusCode() != Ti::StatusCodeSuccess) {
qCWarning(dcZigbeeNetwork()) << "Error commissioning controller.";
setState(StateUninitialized);
setError(ErrorZigbeeError);
return;
}
qCDebug(dcZigbeeNetwork()) << "Controller commissioned";
startControllerNetwork();
setMacAddress(m_controller->networkConfiguration().ieeeAddress);
});
}
void ZigbeeNetworkTi::startControllerNetwork()
{
setState(StateStarting);
qCDebug(dcZigbeeNetwork()) << "Starting network on controller...";
ZigbeeInterfaceTiReply *startReply = m_controller->start();
connect(startReply, &ZigbeeInterfaceTiReply::finished, this, [=]() {
if (startReply->statusCode() != Ti::StatusCodeSuccess) {
qCWarning(dcZigbeeNetwork()) << "Error starting network.";
setState(StateOffline);
setError(ErrorZigbeeError);
return;
}
});
}
void ZigbeeNetworkTi::processGreenPowerFrame(const Zigbee::ApsdeDataIndication &indication)
{
ZigbeeClusterLibrary::Frame inputFrame = ZigbeeClusterLibrary::parseFrameData(indication.asdu);
QDataStream inputStream(inputFrame.payload);
inputStream.setByteOrder(QDataStream::LittleEndian);
quint8 commandId, payloadSize;
quint16 options;
quint32 srcId, frameCounter;
inputStream >> options >> srcId >> frameCounter >> commandId >> payloadSize;
QByteArray commandFrame = inputFrame.payload.right(payloadSize);
qCWarning(dcZigbeeNetwork()) << "Green Power frame:" << options << srcId << frameCounter << commandId << payloadSize << commandFrame.toHex();
if (commandId == 0xE0) {
qCWarning(dcZigbeeNetwork()) << "Green power commissioning";
QDataStream commandStream(commandFrame);
commandStream.setByteOrder(QDataStream::LittleEndian);
quint8 deviceId, inputOptions, extendedOptions;
QByteArray securityKey;
quint32 keyMic;
quint32 outgoingCounter;
commandStream >> deviceId >> inputOptions >> extendedOptions;
for (int i = 0; i < 16; i++) {
quint8 byte;
commandStream >> byte;
securityKey.append(byte);
}
commandStream >> keyMic >> outgoingCounter;
// Send commissioning reply
QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
ZigbeeDataType options = ZigbeeDataType(static_cast<quint32>(0x00e548), Zigbee::Uint24); // options
for (int i = 0; i < options.data().length(); i++) {
stream << static_cast<quint8>(options.data().at(i));
}
stream << srcId;
stream << static_cast<quint16>(0x0b84); // Green Power group as configured during startup
stream << deviceId;
stream << outgoingCounter;
payload.append(encryptSecurityKey(srcId, securityKey));
// Build ZCL frame control
ZigbeeClusterLibrary::FrameControl frameControl;
frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific;
frameControl.manufacturerSpecific = false;
frameControl.direction = ZigbeeClusterLibrary::DirectionServerToClient;
frameControl.disableDefaultResponse = true;
// Build ZCL header
ZigbeeClusterLibrary::Header header;
header.frameControl = frameControl;
header.command = 0x01; // TODO: ZigbeeClusterGreenPower::ClusterCommandPairing;
header.transactionSequenceNumber = generateSequenceNumber() - 1;
// Build ZCL frame
ZigbeeClusterLibrary::Frame frame;
frame.header = header;
frame.payload = payload;
ZigbeeNetworkRequest request;
request.setRequestId(generateSequenceNumber() + 1);
request.setDestinationAddressMode(Zigbee::DestinationAddressModeShortAddress);
request.setDestinationShortAddress(0xFFFD);
request.setDestinationEndpoint(242); // Green Power Endpoint
request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP
request.setClusterId(0x21);
request.setTxOptions(static_cast<Zigbee::ZigbeeTxOption>(0x00)); // FIXME: There should be TxOptionsNone I guess...
request.setSourceEndpoint(242); // Green Power Endpoint
request.setRadius(30); // FIXME: There should be a more clever way to figure out the radius
request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame));
m_controller->requestSendRequest(request);
QStringList l;
for (int i = 0; i < request.asdu().length(); i++) {
l.append(QString::number(static_cast<quint8>(request.asdu().at(i))));
}
// TODO: create the GreenPower Node here once GreenPower support is added properly
// emit greenPowerDeviceJoined(srcId, deviceId, indication.sourceIeeeAddress())
}
}
QByteArray ZigbeeNetworkTi::encryptSecurityKey(quint32 sourceId, const QByteArray &securityKey)
{
#if (QCA_VERSION >= QCA_VERSION_CHECK(2, 2, 0))
QByteArray sourceIdArray;
sourceIdArray.append(static_cast<quint8>(sourceId & 0x000000ff));
sourceIdArray.append(static_cast<quint8>((sourceId & 0x0000ff00) >> 8));
sourceIdArray.append(static_cast<quint8>((sourceId & 0x00ff0000) >> 16));
sourceIdArray.append(static_cast<quint8>((sourceId & 0xff000000) >> 24));
QByteArray nonce(13, Qt::Uninitialized);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
nonce[4 * i + j] = sourceIdArray.at(j);
}
}
nonce[12] = 0x05;
QByteArray zigbeeLinkKey = securityConfiguration().globalTrustCenterLinkKey().toByteArray();
QCA::Initializer init;
QCA::Cipher cipher("aes128", QCA::Cipher::CCM, QCA::Cipher::DefaultPadding, QCA::Encode, zigbeeLinkKey, nonce);
QByteArray encrypted = cipher.update(securityKey).toByteArray();
encrypted.append(cipher.final().toByteArray());
return encrypted;
#else
Q_UNUSED(sourceId)
qCWarning(dcZigbeeNetwork()) << "Greenpower encryption requires AES-128-CCM (requires qca-qt5-2 >= 2.2.0)";
return securityKey;
#endif
}
void ZigbeeNetworkTi::onControllerAvailableChanged(bool available)
{
if (!available) {
qCWarning(dcZigbeeNetwork()) << "Hardware controller is not available any more.";
setError(ErrorHardwareUnavailable);
setPermitJoiningEnabled(false);
setState(StateOffline);
setError(ErrorHardwareUnavailable);
} else {
setPermitJoiningEnabled(false);
setState(StateOffline);
setError(ErrorNoError);
qCDebug(dcZigbeeNetwork()) << "Hardware controller is now available.";
initController();
}
}
void ZigbeeNetworkTi::onControllerStateChanged(ZigbeeBridgeControllerTi::ControllerState state)
{
switch (state) {
case ZigbeeBridgeControllerTi::ControllerStateDown:
setState(StateOffline);
break;
case ZigbeeBridgeControllerTi::ControllerStateInitialized:
// The mac address of the controller will be stored locally when the coordinator node replies
// to the node descriptor request which includes the IEEE address.
// If we don't know the mac address yet, it means that we've never had the zigbee network up and running
// => start the commissioning procedure, else, directly start the network
// Note: if the user messes around with the stick by provisioning stuff with another instance/software, it
// will not be detected currently and the "wrong" network starts up.
qCDebug(dcZigbeeNetwork()) << "Controller initialized";
qCDebug(dcZigbeeNetwork()) << "Stored MAC address:" << macAddress() << "Controller MAC address:" << m_controller->networkConfiguration().ieeeAddress;
if (macAddress() != ZigbeeAddress("00:00:00:00:00:00:00:00") && m_controller->networkConfiguration().ieeeAddress != macAddress()) {
qCWarning(dcZigbeeNetwork()) << "The controller MAC address changed. Please connect the original controller or create a new network.";
setState(StateUninitialized);
setError(ErrorHardwareModuleChanged);
stopNetwork();
return;
}
if (macAddress() == ZigbeeAddress("00:00:00:00:00:00:00")) {
// Controller network is not commissioned yet
qCDebug(dcZigbeeNetwork()) << "Controller is not comissioned yet. Comissioning now...";
commissionController();
return;
}
qCDebug(dcZigbeeNetwork()) << "Controller is ready and commissioned.";
startControllerNetwork();
break;
case ZigbeeBridgeControllerTi::ControllerStateRunning: {
qCDebug(dcZigbeeNetwork()) << "Controller network running. Registering endpoints on controller..";
setPanId(m_controller->networkConfiguration().panId);
setExtendedPanId(m_controller->networkConfiguration().extendedPanId);
setChannel(m_controller->networkConfiguration().currentChannel);
// TODO: This should be public API of libnymea-zigbee so that the application layer (e.g. nymea-plugins)
// can register the endpoints it needs for the particular application/device.
// Fow now we're registering HomeAutomation, LightLink and GreenPower endpoints.
m_controller->registerEndpoint(1, Zigbee::ZigbeeProfileHomeAutomation, 5, 0);
m_controller->registerEndpoint(12, Zigbee::ZigbeeProfileLightLink, 5, 0);
// The Green Power endpoing is a bit special, it also needs to be added to a group
ZigbeeInterfaceTiReply *registerLLEndpointReply = m_controller->registerEndpoint(242, Zigbee::ZigbeeProfileGreenPower, 5, 0);
connect(registerLLEndpointReply, &ZigbeeInterfaceTiReply::finished, this, [=]() {
if (registerLLEndpointReply->statusCode() != Ti::StatusCodeSuccess) {
qCWarning(dcZigbeeNetwork()) << "Error registering GreenPower endpoint.";
setState(StateOffline);
return;
}
qCDebug(dcZigbeeNetwork()) << "Registered GreenPower endpoint on coordinator node.";
ZigbeeInterfaceTiReply *addGEndpointGroupReply = m_controller->addEndpointToGroup(242, 0x0b84);
connect(addGEndpointGroupReply, &ZigbeeInterfaceTiReply::finished, this, [=]() {
if (addGEndpointGroupReply->statusCode() != Ti::StatusCodeSuccess) {
qCWarning(dcZigbeeNetwork()) << "Error adding GreenPower endpoint to group.";
setState(StateOffline);
return;
}
// Now we're done. If this is a first start (no coordinator node loaded from configs) we'll add
// ourselves now and start the inspection.
if (!m_coordinatorNode) {
qCDebug(dcZigbeeNetwork()) << "Initializing coordinator node:" << m_controller->networkConfiguration();
ZigbeeNode *coordinatorNode = createNode(m_controller->networkConfiguration().shortAddress, m_controller->networkConfiguration().ieeeAddress, this);
m_coordinatorNode = coordinatorNode;
addUnitializedNode(coordinatorNode);
}
// Introspecing ourselves on every start. Most of the times this wouldn't be needed, but if the above
// endpoints are changed (e.g. on a future upgrade), we'll want to refresh.
m_coordinatorNode->startInitialization();
ZigbeeInterfaceTiReply *ledReply = m_controller->setLed(false);
connect(ledReply, &ZigbeeInterfaceTiReply::finished, this, [=]() {
setState(StateRunning);
while (!m_requestQueue.isEmpty()) {
ZigbeeNetworkReply *reply = m_requestQueue.takeFirst();
ZigbeeInterfaceTiReply *interfaceReply = m_controller->requestSendRequest(reply->request());
connect(interfaceReply, &ZigbeeInterfaceTiReply::finished, reply, [this, reply, interfaceReply](){
if (interfaceReply->statusCode() != Ti::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could send request to controller." << interfaceReply->statusCode();
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorInterfaceError);
return;
}
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorNoError);
});
}
});
});
});
break;
}
}
}
void ZigbeeNetworkTi::onPermitJoinStateChanged(quint8 duration)
{
setPermitJoiningRemaining(duration);
setPermitJoiningEnabled(duration > 0);
m_controller->setLed(duration > 0);
}
void ZigbeeNetworkTi::onDeviceIndication(quint16 shortAddress, const ZigbeeAddress &ieeeAddress, quint8 macCapabilities)
{
onDeviceAnnounced(shortAddress, ieeeAddress, macCapabilities);
}
void ZigbeeNetworkTi::onNodeLeaveIndication(const ZigbeeAddress &ieeeAddress, bool request, bool remove, bool rejoin)
{
qCDebug(dcZigbeeNetwork()) << "Received node leave indication" << ieeeAddress.toString() << "request:" << request << "remove:" << remove << "rejoining:" << rejoin;
if (!hasNode(ieeeAddress)) {
qCDebug(dcZigbeeNetwork()) << "Node left the network" << ieeeAddress.toString();
return;
}
ZigbeeNode *node = getZigbeeNode(ieeeAddress);
qCDebug(dcZigbeeNetwork()) << node << "left the network";
removeNode(node);
}
void ZigbeeNetworkTi::onApsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm)
{
qCDebug(dcZigbeeNetwork()) << "Data confirm received:" << confirm;
}
void ZigbeeNetworkTi::onApsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication)
{
// If it's for the green power endpoint, we'll have to do a commissioning.
// TODO: This should probably be in ZigbeeNode or ZigbeeDeviceObject, but not yet sure how other backends deal with it
if (indication.destinationEndpoint == 242) {
processGreenPowerFrame(indication);
}
// Check if this indocation is related to any pending reply
if (indication.profileId == Zigbee::ZigbeeProfileDevice) {
handleZigbeeDeviceProfileIndication(indication);
return;
}
// Else let the node handle this indication
handleZigbeeClusterLibraryIndication(indication);
}
void ZigbeeNetworkTi::startNetwork()
{
loadNetwork();
if (!m_controller->enable(serialPortName(), serialBaudrate())) {
setPermitJoiningEnabled(false);
setState(StateOffline);
setError(ErrorHardwareUnavailable);
return;
}
setPermitJoiningEnabled(false);
}
void ZigbeeNetworkTi::stopNetwork()
{
setState(StateStopping);
m_controller->disable();
setState(StateOffline);
}
void ZigbeeNetworkTi::reset()
{
qCDebug(dcZigbeeNetwork()) << "Resetting controller.";
m_controller->reset();
}
void ZigbeeNetworkTi::factoryResetNetwork()
{
qCDebug(dcZigbeeNetwork()) << "Factory resetting network and forget all information. This cannot be undone.";
ZigbeeInterfaceTiReply *reply = m_controller->factoryReset();
connect(reply, &ZigbeeInterfaceTiReply::finished, this, [=](){
qCDebug(dcZigbeeNetwork()) << "Factory reset reply finished" << reply->statusCode();
m_controller->disable();
clearSettings();
setMacAddress(ZigbeeAddress("00:00:00:00:00:00"));
setState(StateOffline);
setError(ErrorNoError);
qCDebug(dcZigbeeNetwork()) << "The factory reset is finished. Restarting with a fresh network.";
startNetwork();
});
}
void ZigbeeNetworkTi::destroyNetwork()
{
qCDebug(dcZigbeeNetwork()) << "Destroy network and delete the database";
m_controller->disable();
clearSettings();
}

View File

@ -0,0 +1,85 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef ZIGBEENETWORKTI_H
#define ZIGBEENETWORKTI_H
#include <QObject>
#include "zigbeenetwork.h"
#include "zigbeechannelmask.h"
#include "zcl/zigbeeclusterlibrary.h"
#include "zigbeebridgecontrollerti.h"
class ZigbeeNetworkTi : public ZigbeeNetwork
{
Q_OBJECT
public:
explicit ZigbeeNetworkTi(const QUuid &networkUuid, QObject *parent = nullptr);
ZigbeeBridgeController *bridgeController() const override;
Zigbee::ZigbeeBackendType backendType() const override;
// Sending an APSDE-DATA.request, will be finished on APSDE-DATA.confirm
ZigbeeNetworkReply *sendRequest(const ZigbeeNetworkRequest &request) override;
void setPermitJoining(quint8 duration, quint16 address = Zigbee::BroadcastAddressAllRouters) override;
public slots:
void startNetwork() override;
void stopNetwork() override;
void reset() override;
void factoryResetNetwork() override;
void destroyNetwork() override;
private slots:
void onControllerAvailableChanged(bool available);
void onControllerStateChanged(ZigbeeBridgeControllerTi::ControllerState state);
void onPermitJoinStateChanged(quint8 duration);
void onDeviceIndication(quint16 shortAddress, const ZigbeeAddress &ieeeAddress, quint8 macCapabilities);
void onNodeLeaveIndication(const ZigbeeAddress &ieeeAddress, bool request, bool remove, bool rejoin);
void onApsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm);
void onApsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication);
private:
void initController();
void commissionController();
void startControllerNetwork();
void processGreenPowerFrame(const Zigbee::ApsdeDataIndication &indication);
QByteArray encryptSecurityKey(quint32 sourceId, const QByteArray &securityKey);
private:
ZigbeeBridgeControllerTi *m_controller = nullptr;
QList<ZigbeeNetworkReply*> m_requestQueue;
};
#endif // ZIGBEENETWORKDECONZ_H

View File

@ -12,6 +12,8 @@ packagesExist(libudev) {
DEFINES += DISABLE_UDEV
}
PKGCONFIG += qca2-qt5
SOURCES += \
backends/deconz/interface/zigbeeinterfacedeconz.cpp \
backends/deconz/interface/zigbeeinterfacedeconzreply.cpp \
@ -22,6 +24,10 @@ SOURCES += \
backends/nxp/interface/zigbeeinterfacenxpreply.cpp \
backends/nxp/zigbeebridgecontrollernxp.cpp \
backends/nxp/zigbeenetworknxp.cpp \
backends/ti/interface/zigbeeinterfaceti.cpp \
backends/ti/interface/zigbeeinterfacetireply.cpp \
backends/ti/zigbeebridgecontrollerti.cpp \
backends/ti/zigbeenetworkti.cpp \
zcl/closures/zigbeeclusterdoorlock.cpp \
zcl/general/zigbeeclusteranaloginput.cpp \
zcl/general/zigbeeclusteranalogoutput.cpp \
@ -90,6 +96,11 @@ HEADERS += \
backends/nxp/interface/zigbeeinterfacenxpreply.h \
backends/nxp/zigbeebridgecontrollernxp.h \
backends/nxp/zigbeenetworknxp.h \
backends/ti/interface/ti.h \
backends/ti/interface/zigbeeinterfaceti.h \
backends/ti/interface/zigbeeinterfacetireply.h \
backends/ti/zigbeebridgecontrollerti.h \
backends/ti/zigbeenetworkti.h \
zcl/closures/zigbeeclusterdoorlock.h \
zcl/general/zigbeeclusteranaloginput.h \
zcl/general/zigbeeclusteranalogoutput.h \

View File

@ -42,7 +42,8 @@ class Zigbee
public:
enum ZigbeeBackendType {
ZigbeeBackendTypeDeconz,
ZigbeeBackendTypeNxp
ZigbeeBackendTypeNxp,
ZigbeeBackendTypeTi
};
Q_ENUM(ZigbeeBackendType)

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Copyright 2013 - 2021, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
@ -30,6 +30,7 @@
#include "backends/nxp/zigbeenetworknxp.h"
#include "backends/deconz/zigbeenetworkdeconz.h"
#include "backends/ti/zigbeenetworkti.h"
#include <QDateTime>
@ -48,6 +49,8 @@ ZigbeeNetwork *ZigbeeNetworkManager::createZigbeeNetwork(const QUuid &networkUui
return qobject_cast<ZigbeeNetwork *>(new ZigbeeNetworkNxp(networkUuid, parent));
case Zigbee::ZigbeeBackendTypeDeconz:
return qobject_cast<ZigbeeNetwork *>(new ZigbeeNetworkDeconz(networkUuid, parent));
case Zigbee::ZigbeeBackendTypeTi:
return qobject_cast<ZigbeeNetwork *>(new ZigbeeNetworkTi(networkUuid, parent));
}
return nullptr;

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Copyright 2013 - 2021, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
@ -237,6 +237,12 @@ void ZigbeeUartAdapterMonitor::addAdapterInternally(const QString &serialPort)
adapter.setBackendType(Zigbee::ZigbeeBackendTypeNxp);
adapter.setBaudRate(115200);
}
QStringList zStackModels = {"cc2530", "cc2531", "cc2538", "cc1352p", "cc2652p", "cc2652r", "cc2652rb", "sonoff zigbee 3.0 usb"};
if (QRegExp(".*(" + zStackModels.join("|") + ").*").exactMatch(serialPortInfo.description().toLower())) {
adapter.setHardwareRecognized(true);
adapter.setBackendType(Zigbee::ZigbeeBackendTypeTi);
adapter.setBaudRate(115200);
}
qCDebug(dcZigbeeAdapterMonitor()) << "Added" << adapter;
m_availableAdapters.insert(adapter.serialPort(), adapter);