blob: 03aa8389ae062d620a806b49f3acd0d14a266c98 [file] [log] [blame]
/*
* Copyright (c) 2019, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements a simple CLI for the Commissioner role.
*/
#include "cli_commissioner.hpp"
#include "cli/cli.hpp"
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
namespace ot {
namespace Cli {
/**
* @cli commissioner announce
* @code
* commissioner announce 0x00050000 2 32 fdde:ad00:beef:0:0:ff:fe00:c00
* Done
* @endcode
* @cparam commissioner announce @ca{mask} @ca{count} @ca{period} @ca{destination}
* * `mask`: Bitmask that identifies channels for sending MLE `Announce` messages.
* * `count`: Number of MLE `Announce` transmissions per channel.
* * `period`: Number of milliseconds between successive MLE `Announce` transmissions.
* * `destination`: Destination IPv6 address for the message. The message may be multicast.
* @par
* Sends an Announce Begin message.
* @note Use this command only after successfully starting the %Commissioner role
* with the `commissioner start` command.
* @csa{commissioner start}
* @sa otCommissionerAnnounceBegin
*/
template <> otError Commissioner::Process<Cmd("announce")>(Arg aArgs[])
{
otError error;
uint32_t mask;
uint8_t count;
uint16_t period;
otIp6Address address;
SuccessOrExit(error = aArgs[0].ParseAsUint32(mask));
SuccessOrExit(error = aArgs[1].ParseAsUint8(count));
SuccessOrExit(error = aArgs[2].ParseAsUint16(period));
SuccessOrExit(error = aArgs[3].ParseAsIp6Address(address));
error = otCommissionerAnnounceBegin(GetInstancePtr(), mask, count, period, &address);
exit:
return error;
}
/**
* @cli commissioner energy
* @code
* commissioner energy 0x00050000 2 32 1000 fdde:ad00:beef:0:0:ff:fe00:c00
* Done
* Energy: 00050000 0 0 0 0
* @endcode
* @cparam commissioner energy @ca{mask} @ca{count} @ca{period} @ca{scanDuration} @ca{destination}
* * `mask`: Bitmask that identifies channels for performing IEEE 802.15.4 energy scans.
* * `count`: Number of IEEE 802.15.4 energy scans per channel.
* * `period`: Number of milliseconds between successive IEEE 802.15.4 energy scans.
* * `scanDuration`: Scan duration in milliseconds to use when
* performing an IEEE 802.15.4 energy scan.
* * `destination`: Destination IPv6 address for the message. The message may be multicast.
* @par
* Sends an Energy Scan Query message. Command output is printed as it is received.
* @note Use this command only after successfully starting the %Commissioner role
* with the `commissioner start` command.
* @csa{commissioner start}
* @sa otCommissionerEnergyScan
*/
template <> otError Commissioner::Process<Cmd("energy")>(Arg aArgs[])
{
otError error;
uint32_t mask;
uint8_t count;
uint16_t period;
uint16_t scanDuration;
otIp6Address address;
SuccessOrExit(error = aArgs[0].ParseAsUint32(mask));
SuccessOrExit(error = aArgs[1].ParseAsUint8(count));
SuccessOrExit(error = aArgs[2].ParseAsUint16(period));
SuccessOrExit(error = aArgs[3].ParseAsUint16(scanDuration));
SuccessOrExit(error = aArgs[4].ParseAsIp6Address(address));
error = otCommissionerEnergyScan(GetInstancePtr(), mask, count, period, scanDuration, &address,
&Commissioner::HandleEnergyReport, this);
exit:
return error;
}
template <> otError Commissioner::Process<Cmd("joiner")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
otExtAddress addr;
const otExtAddress *addrPtr = nullptr;
otJoinerDiscerner discerner;
/**
* @cli commissioner joiner table
* @code
* commissioner joiner table
* | ID | PSKd | Expiration |
* +-----------------------+----------------------------------+------------+
* | * | J01NME | 81015 |
* | d45e64fa83f81cf7 | J01NME | 101204 |
* | 0x0000000000000abc/12 | J01NME | 114360 |
* Done
* @endcode
* @par
* Lists all %Joiner entries in table format.
*/
if (aArgs[0] == "table")
{
uint16_t iter = 0;
otJoinerInfo joinerInfo;
static const char *const kJoinerTableTitles[] = {"ID", "PSKd", "Expiration"};
static const uint8_t kJoinerTableColumnWidths[] = {
23,
34,
12,
};
OutputTableHeader(kJoinerTableTitles, kJoinerTableColumnWidths);
while (otCommissionerGetNextJoinerInfo(GetInstancePtr(), &iter, &joinerInfo) == OT_ERROR_NONE)
{
switch (joinerInfo.mType)
{
case OT_JOINER_INFO_TYPE_ANY:
OutputFormat("| %21s", "*");
break;
case OT_JOINER_INFO_TYPE_EUI64:
OutputFormat("| ");
OutputExtAddress(joinerInfo.mSharedId.mEui64);
break;
case OT_JOINER_INFO_TYPE_DISCERNER:
OutputFormat("| 0x%08lx%08lx/%2u",
static_cast<unsigned long>(joinerInfo.mSharedId.mDiscerner.mValue >> 32),
static_cast<unsigned long>(joinerInfo.mSharedId.mDiscerner.mValue & 0xffffffff),
joinerInfo.mSharedId.mDiscerner.mLength);
break;
}
OutputFormat(" | %32s | %10lu |", joinerInfo.mPskd.m8, ToUlong(joinerInfo.mExpirationTime));
OutputNewLine();
}
ExitNow(error = OT_ERROR_NONE);
}
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
ClearAllBytes(discerner);
if (aArgs[1] == "*")
{
// Intentionally empty
}
else
{
error = Interpreter::ParseJoinerDiscerner(aArgs[1], discerner);
if (error == OT_ERROR_NOT_FOUND)
{
error = aArgs[1].ParseAsHexString(addr.m8);
addrPtr = &addr;
}
SuccessOrExit(error);
}
/**
* @cli commissioner joiner add
* @code
* commissioner joiner add d45e64fa83f81cf7 J01NME
* Done
* @endcode
* @code
* commissioner joiner add 0xabc/12 J01NME
* Done
* @endcode
* @cparam commissioner joiner add @ca{eui64}|@ca{discerner pksd} [@ca{timeout}]
* * `eui64`: IEEE EUI-64 of the %Joiner. To match any joiner, use `*`.
* * `discerner`: The %Joiner discerner in the format `number/length`.
* * `pksd`: Pre-Shared Key for the joiner.
* * `timeout`: The %Joiner timeout in seconds.
* @par
* Adds a joiner entry.
* @note Use this command only after successfully starting the %Commissioner role
* with the `commissioner start` command.
* @csa{commissioner start}
* @sa otCommissionerAddJoiner
* @sa otCommissionerAddJoinerWithDiscerner
*/
if (aArgs[0] == "add")
{
uint32_t timeout = kDefaultJoinerTimeout;
VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
if (!aArgs[3].IsEmpty())
{
SuccessOrExit(error = aArgs[3].ParseAsUint32(timeout));
}
if (discerner.mLength)
{
error = otCommissionerAddJoinerWithDiscerner(GetInstancePtr(), &discerner, aArgs[2].GetCString(), timeout);
}
else
{
error = otCommissionerAddJoiner(GetInstancePtr(), addrPtr, aArgs[2].GetCString(), timeout);
}
/**
* @cli commissioner joiner remove
* @code
* commissioner joiner remove d45e64fa83f81cf7
* Done
* @endcode
* @code
* commissioner joiner remove 0xabc/12
* Done
* @endcode
* @cparam commissioner joiner remove @ca{eui64}|@ca{discerner}
* * `eui64`: IEEE EUI-64 of the joiner. To match any joiner, use `*`.
* * `discerner`: The joiner discerner in the format `number/length`.
* @par
* Removes a %Joiner entry.
* @note Use this command only after successfully starting the %Commissioner role
* with the `commissioner start` command.
* @csa{commissioner start}
* @sa otCommissionerRemoveJoiner
* @sa otCommissionerRemoveJoinerWithDiscerner
*/
}
else if (aArgs[0] == "remove")
{
if (discerner.mLength)
{
error = otCommissionerRemoveJoinerWithDiscerner(GetInstancePtr(), &discerner);
}
else
{
error = otCommissionerRemoveJoiner(GetInstancePtr(), addrPtr);
}
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
exit:
return error;
}
/**
* @cli commissioner mgmtget
* @code
* commissioner mgmtget locator sessionid
* Done
* @endcode
* @cparam commissioner mgmtget [locator] [sessionid] <!--
* --> [steeringdata] [joinerudpport] <!--
* --> [-x @ca{TLVs}]
* * `locator`: Border Router RLOC16.
* * `sessionid`: Session ID of the %Commissioner.
* * `steeringdata`: Steering data.
* * `joinerudpport`: %Joiner UDP port.
* * `TLVs`: The set of TLVs to be retrieved.
* @par
* Sends a `MGMT_GET` (Management Get) message to the Leader.
* Variable values that have been set using the `commissioner mgmtset` command are returned.
* @sa otCommissionerSendMgmtGet
*/
template <> otError Commissioner::Process<Cmd("mgmtget")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
uint8_t tlvs[32];
uint8_t length = 0;
for (; !aArgs->IsEmpty(); aArgs++)
{
VerifyOrExit(static_cast<size_t>(length) < sizeof(tlvs), error = OT_ERROR_NO_BUFS);
if (*aArgs == "locator")
{
tlvs[length++] = OT_MESHCOP_TLV_BORDER_AGENT_RLOC;
}
else if (*aArgs == "sessionid")
{
tlvs[length++] = OT_MESHCOP_TLV_COMM_SESSION_ID;
}
else if (*aArgs == "steeringdata")
{
tlvs[length++] = OT_MESHCOP_TLV_STEERING_DATA;
}
else if (*aArgs == "joinerudpport")
{
tlvs[length++] = OT_MESHCOP_TLV_JOINER_UDP_PORT;
}
else if (*aArgs == "-x")
{
uint16_t readLength;
aArgs++;
readLength = static_cast<uint16_t>(sizeof(tlvs) - length);
SuccessOrExit(error = aArgs->ParseAsHexString(readLength, tlvs + length));
length += static_cast<uint8_t>(readLength);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
error = otCommissionerSendMgmtGet(GetInstancePtr(), tlvs, static_cast<uint8_t>(length));
exit:
return error;
}
/**
* @cli commissioner mgmtset
* @code
* commissioner mgmtset joinerudpport 9988
* Done
* @endcode
* @cparam commissioner mgmtset [locator @ca{locator}] [sessionid @ca{sessionid}] <!--
* --> [steeringdata @ca{steeringdata}] [joinerudpport @ca{joinerudpport}] <!--
* --> [-x @ca{TLVs}]
* * `locator`: Border Router RLOC16.
* * `sessionid`: Session ID of the %Commissioner.
* * `steeringdata`: Steering data.
* * `joinerudpport`: %Joiner UDP port.
* * `TLVs`: The set of TLVs to be retrieved.
* @par
* Sends a `MGMT_SET` (Management Set) message to the Leader, and sets the
* variables to the values specified.
* @sa otCommissionerSendMgmtSet
*/
template <> otError Commissioner::Process<Cmd("mgmtset")>(Arg aArgs[])
{
otError error;
otCommissioningDataset dataset;
uint8_t tlvs[32];
uint8_t tlvsLength = 0;
VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
ClearAllBytes(dataset);
for (; !aArgs->IsEmpty(); aArgs++)
{
if (*aArgs == "locator")
{
aArgs++;
dataset.mIsLocatorSet = true;
SuccessOrExit(error = aArgs->ParseAsUint16(dataset.mLocator));
}
else if (*aArgs == "sessionid")
{
aArgs++;
dataset.mIsSessionIdSet = true;
SuccessOrExit(error = aArgs->ParseAsUint16(dataset.mSessionId));
}
else if (*aArgs == "steeringdata")
{
uint16_t length;
aArgs++;
dataset.mIsSteeringDataSet = true;
length = sizeof(dataset.mSteeringData.m8);
SuccessOrExit(error = aArgs->ParseAsHexString(length, dataset.mSteeringData.m8));
dataset.mSteeringData.mLength = static_cast<uint8_t>(length);
}
else if (*aArgs == "joinerudpport")
{
aArgs++;
dataset.mIsJoinerUdpPortSet = true;
SuccessOrExit(error = aArgs->ParseAsUint16(dataset.mJoinerUdpPort));
}
else if (*aArgs == "-x")
{
uint16_t length;
aArgs++;
length = sizeof(tlvs);
SuccessOrExit(error = aArgs->ParseAsHexString(length, tlvs));
tlvsLength = static_cast<uint8_t>(length);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
error = otCommissionerSendMgmtSet(GetInstancePtr(), &dataset, tlvs, tlvsLength);
exit:
return error;
}
/**
* @cli commissioner panid
* @code
* commissioner panid 0xdead 0x7fff800 fdde:ad00:beef:0:0:ff:fe00:c00
* Done
* Conflict: dead, 00000800
* @endcode
* @cparam commissioner panid @ca{panid} @ca{mask} @ca{destination}
* * `paind`: PAN ID to use to check for conflicts.
* * `mask`; Bitmask that identifies channels to perform IEEE 802.15.4
* Active Scans.
* * `destination`: IPv6 destination address for the message. The message may be multicast.
* @par
* Sends a PAN ID query. Command output is returned as it is received.
* @note Use this command only after successfully starting the %Commissioner role
* with the `commissioner start` command.
* @csa{commissioner start}
* @sa otCommissionerPanIdQuery
*/
template <> otError Commissioner::Process<Cmd("panid")>(Arg aArgs[])
{
otError error;
uint16_t panId;
uint32_t mask;
otIp6Address address;
SuccessOrExit(error = aArgs[0].ParseAsUint16(panId));
SuccessOrExit(error = aArgs[1].ParseAsUint32(mask));
SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address));
error = otCommissionerPanIdQuery(GetInstancePtr(), panId, mask, &address, &Commissioner::HandlePanIdConflict, this);
exit:
return error;
}
/**
* @cli commissioner provisioningurl
* @code
* commissioner provisioningurl http://github.com/openthread/openthread
* Done
* @endcode
* @cparam commissioner provisioningurl @ca{provisioningurl}
* @par
* Sets the %Commissioner provisioning URL.
* @sa otCommissionerSetProvisioningUrl
*/
template <> otError Commissioner::Process<Cmd("provisioningurl")>(Arg aArgs[])
{
// If aArgs[0] is empty, `GetCString() will return `nullptr`
/// which will correctly clear the provisioning URL.
return otCommissionerSetProvisioningUrl(GetInstancePtr(), aArgs[0].GetCString());
}
/**
* @cli commissioner sessionid
* @code
* commissioner sessionid
* 0
* Done
* @endcode
* @par
* Gets the current %Commissioner session ID.
* @sa otCommissionerGetSessionId
*/
template <> otError Commissioner::Process<Cmd("sessionid")>(Arg aArgs[])
{
OT_UNUSED_VARIABLE(aArgs);
OutputLine("%d", otCommissionerGetSessionId(GetInstancePtr()));
return OT_ERROR_NONE;
}
/**
* @cli commissioner id (get,set)
* @code
* commissioner id OpenThread Commissioner
* Done
* @endcode
* @code
* commissioner id
* OpenThread Commissioner
* Done
* @endcode
* @cparam commissioner id @ca{name}
* @par
* Gets or sets the OpenThread %Commissioner ID name.
* @sa otCommissionerSetId
*/
template <> otError Commissioner::Process<Cmd("id")>(Arg aArgs[])
{
otError error;
if (aArgs[0].IsEmpty())
{
OutputLine("%s", otCommissionerGetId(GetInstancePtr()));
error = OT_ERROR_NONE;
}
else
{
error = otCommissionerSetId(GetInstancePtr(), aArgs[0].GetCString());
}
return error;
}
/**
* @cli commissioner start
* @code
* commissioner start
* Commissioner: petitioning
* Done
* Commissioner: active
* @endcode
* @par
* Starts the Thread %Commissioner role.
* @note The `commissioner` commands are available only when
* `OPENTHREAD_CONFIG_COMMISSIONER_ENABLE` and `OPENTHREAD_FTD` are set.
* @sa otCommissionerStart
*/
template <> otError Commissioner::Process<Cmd("start")>(Arg aArgs[])
{
OT_UNUSED_VARIABLE(aArgs);
return otCommissionerStart(GetInstancePtr(), &Commissioner::HandleStateChanged, &Commissioner::HandleJoinerEvent,
this);
}
void Commissioner::HandleStateChanged(otCommissionerState aState, void *aContext)
{
static_cast<Commissioner *>(aContext)->HandleStateChanged(aState);
}
void Commissioner::HandleStateChanged(otCommissionerState aState)
{
OutputLine("Commissioner: %s", StateToString(aState));
}
const char *Commissioner::StateToString(otCommissionerState aState)
{
static const char *const kStateString[] = {
"disabled", // (0) OT_COMMISSIONER_STATE_DISABLED
"petitioning", // (1) OT_COMMISSIONER_STATE_PETITION
"active", // (2) OT_COMMISSIONER_STATE_ACTIVE
};
static_assert(0 == OT_COMMISSIONER_STATE_DISABLED, "OT_COMMISSIONER_STATE_DISABLED value is incorrect");
static_assert(1 == OT_COMMISSIONER_STATE_PETITION, "OT_COMMISSIONER_STATE_PETITION value is incorrect");
static_assert(2 == OT_COMMISSIONER_STATE_ACTIVE, "OT_COMMISSIONER_STATE_ACTIVE value is incorrect");
return Stringify(aState, kStateString);
}
void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent,
const otJoinerInfo *aJoinerInfo,
const otExtAddress *aJoinerId,
void *aContext)
{
static_cast<Commissioner *>(aContext)->HandleJoinerEvent(aEvent, aJoinerInfo, aJoinerId);
}
void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent,
const otJoinerInfo *aJoinerInfo,
const otExtAddress *aJoinerId)
{
static const char *const kEventStrings[] = {
"start", // (0) OT_COMMISSIONER_JOINER_START
"connect", // (1) OT_COMMISSIONER_JOINER_CONNECTED
"finalize", // (2) OT_COMMISSIONER_JOINER_FINALIZE
"end", // (3) OT_COMMISSIONER_JOINER_END
"remove", // (4) OT_COMMISSIONER_JOINER_REMOVED
};
static_assert(0 == OT_COMMISSIONER_JOINER_START, "OT_COMMISSIONER_JOINER_START value is incorrect");
static_assert(1 == OT_COMMISSIONER_JOINER_CONNECTED, "OT_COMMISSIONER_JOINER_CONNECTED value is incorrect");
static_assert(2 == OT_COMMISSIONER_JOINER_FINALIZE, "OT_COMMISSIONER_JOINER_FINALIZE value is incorrect");
static_assert(3 == OT_COMMISSIONER_JOINER_END, "OT_COMMISSIONER_JOINER_END value is incorrect");
static_assert(4 == OT_COMMISSIONER_JOINER_REMOVED, "OT_COMMISSIONER_JOINER_REMOVED value is incorrect");
OT_UNUSED_VARIABLE(aJoinerInfo);
OutputFormat("Commissioner: Joiner %s ", Stringify(aEvent, kEventStrings));
if (aJoinerId != nullptr)
{
OutputExtAddress(*aJoinerId);
}
OutputNewLine();
}
/**
* @cli commissioner stop
* @code
* commissioner stop
* Done
* @endcode
* @par
* Stops the Thread %Commissioner role.
* @sa otCommissionerStop
*/
template <> otError Commissioner::Process<Cmd("stop")>(Arg aArgs[])
{
OT_UNUSED_VARIABLE(aArgs);
return otCommissionerStop(GetInstancePtr());
}
/**
* @cli commissioner state
* @code
* commissioner state
* active
* Done
* @endcode
* @par
* Returns the current state of the %Commissioner. Possible values are
* `active`, `disabled`, or `petition` (petitioning to become %Commissioner).
* @sa otCommissionerState
*/
template <> otError Commissioner::Process<Cmd("state")>(Arg aArgs[])
{
OT_UNUSED_VARIABLE(aArgs);
OutputLine("%s", StateToString(otCommissionerGetState(GetInstancePtr())));
return OT_ERROR_NONE;
}
otError Commissioner::Process(Arg aArgs[])
{
#define CmdEntry(aCommandString) \
{ \
aCommandString, &Commissioner::Process<Cmd(aCommandString)> \
}
static constexpr Command kCommands[] = {
CmdEntry("announce"), CmdEntry("energy"), CmdEntry("id"), CmdEntry("joiner"),
CmdEntry("mgmtget"), CmdEntry("mgmtset"), CmdEntry("panid"), CmdEntry("provisioningurl"),
CmdEntry("sessionid"), CmdEntry("start"), CmdEntry("state"), CmdEntry("stop"),
};
#undef CmdEntry
static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
otError error = OT_ERROR_INVALID_COMMAND;
const Command *command;
if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
{
OutputCommandTable(kCommands);
ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
}
command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
VerifyOrExit(command != nullptr);
error = (this->*command->mHandler)(aArgs + 1);
exit:
return error;
}
void Commissioner::HandleEnergyReport(uint32_t aChannelMask,
const uint8_t *aEnergyList,
uint8_t aEnergyListLength,
void *aContext)
{
static_cast<Commissioner *>(aContext)->HandleEnergyReport(aChannelMask, aEnergyList, aEnergyListLength);
}
void Commissioner::HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength)
{
OutputFormat("Energy: %08lx ", ToUlong(aChannelMask));
for (uint8_t i = 0; i < aEnergyListLength; i++)
{
OutputFormat("%d ", static_cast<int8_t>(aEnergyList[i]));
}
OutputNewLine();
}
void Commissioner::HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext)
{
static_cast<Commissioner *>(aContext)->HandlePanIdConflict(aPanId, aChannelMask);
}
void Commissioner::HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask)
{
OutputLine("Conflict: %04x, %08lx", aPanId, ToUlong(aChannelMask));
}
} // namespace Cli
} // namespace ot
#endif // OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD