Skip to content

Commit

Permalink
[EFR32] Adding add binding commands to switch (#16030)
Browse files Browse the repository at this point in the history
* Adding binding commands to matter switch shell

* restyle

* update wordlist

* remove debug define

* PR comments - add save binding function

* Restyled by whitespace

* Restyled by clang-format

* PR comments

* add missing entry value

* update comment

Co-authored-by: Boris Zbarsky <[email protected]>

Co-authored-by: Restyled.io <[email protected]>
Co-authored-by: Boris Zbarsky <[email protected]>
  • Loading branch information
3 people authored and pull[bot] committed Apr 18, 2022
1 parent 39df8ab commit 1152144
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .github/.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1401,3 +1401,8 @@ kManage
kOperate
kView
xFFFFFFFD
NAMESERVER
UTF
localedef
nameserver
nmcli
14 changes: 13 additions & 1 deletion examples/light-switch-app/efr32/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ combination with JLinkRTTClient as follows:
- 'switch groups onoff off' : Sends On group command to bound group
- 'switch groups onoff toggle' : Sends On group command to bound group

**_Binding Cluster_**

- 'switch binding unicast <fabric index> <node id> <endpoint>' : Creates a unicast binding
- 'switch binding group <fabric index> <group id>' : Creates a group binding

* You can provision and control the Chip device using the python controller,
[CHIPTool](https://github.com/project-chip/connectedhomeip/blob/master/examples/chip-tool/README.md)
standalone, Android or iOS app
Expand All @@ -280,7 +285,7 @@ combination with JLinkRTTClient as follows:
```
chip-tool pairing ble-thread 1 hex:<operationalDataset> 20202021 3840
chip-tool accesscontrol write acl '[{"fabricIndex": 1, "privilege": 3, "authMode": 2, "subjects": [1], "targets": null }]' <lighting-node-id> 0
chip-tool accesscontrol write acl '[{"fabricIndex": 1, "privilege": 5, "authMode": 2, "subjects": [<chip-tool-node-id>], "targets": null }{"fabricIndex": 1, "privilege": 3, "authMode": 2, "subjects": [1], "targets": null }]' <lighting-node-id> 0
chip-tool binding write binding '[{"fabricIndex": 1, "node": <lighting-node-id>, "endpoint": 1, "cluster":6}]' 1 1
```
Expand All @@ -304,6 +309,13 @@ combination with JLinkRTTClient as follows:
chip-tool binding write binding '[{"fabricIndex": 1, "group": 257},{"fabricIndex": 1, "node": <lighting-node-id>, "endpoint": 1, "cluster":6} ]' 1 1
```
To acquire the chip-tool node id, read the acl table right after
commissioning
```
./connectedhomeip/out/chip-tool/chip-tool accesscontrol read acl <nodeid> 0
```
### Notes
- Depending on your network settings your router might not provide native ipv6
Expand Down
1 change: 1 addition & 0 deletions examples/light-switch-app/efr32/include/binding-handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

CHIP_ERROR InitBindingHandler();
void SwitchWorkerFunction(intptr_t context);
void BindingWorkerFunction(intptr_t context);

struct BindingCommandData
{
Expand Down
79 changes: 75 additions & 4 deletions examples/light-switch-app/efr32/src/binding-handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "app/server/Server.h"
#include "controller/InvokeInteraction.h"
#include "platform/CHIPDeviceLayer.h"
#include <app/clusters/bindings/bindings.h>
#include <lib/support/CodeUtils.h>

#if defined(ENABLE_CHIP_SHELL)
Expand All @@ -41,9 +42,11 @@ using Shell::streamer_printf;

Engine sShellSwitchSubCommands;
Engine sShellSwitchOnOffSubCommands;
Engine sShellSwitchGroupsSubCommands;

Engine sShellSwitchGroupsSubCommands;
Engine sShellSwitchGroupsOnOffSubCommands;

Engine sShellSwitchBindingSubCommands;
#endif // defined(ENABLE_CHIP_SHELL)

namespace {
Expand Down Expand Up @@ -202,6 +205,57 @@ CHIP_ERROR ToggleSwitchCommandHandler(int argc, char ** argv)
return CHIP_NO_ERROR;
}

/********************************************************
* bind switch shell functions
*********************************************************/

CHIP_ERROR BindingHelpHandler(int argc, char ** argv)
{
sShellSwitchBindingSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr);
return CHIP_NO_ERROR;
}

CHIP_ERROR BindingSwitchCommandHandler(int argc, char ** argv)
{
if (argc == 0)
{
return BindingHelpHandler(argc, argv);
}

return sShellSwitchBindingSubCommands.ExecCommand(argc, argv);
}

CHIP_ERROR BindingGroupBindCommandHandler(int argc, char ** argv)
{
VerifyOrReturnError(argc == 2, CHIP_ERROR_INVALID_ARGUMENT);

EmberBindingTableEntry * entry = Platform::New<EmberBindingTableEntry>();
entry->type = EMBER_MULTICAST_BINDING;
entry->fabricIndex = atoi(argv[0]);
entry->groupId = atoi(argv[1]);
entry->local = 1; // Hardcoded to endpoint 1 for now
entry->clusterId.SetValue(6); // Hardcoded to OnOff cluster for now

DeviceLayer::PlatformMgr().ScheduleWork(BindingWorkerFunction, reinterpret_cast<intptr_t>(entry));
return CHIP_NO_ERROR;
}

CHIP_ERROR BindingUnicastBindCommandHandler(int argc, char ** argv)
{
VerifyOrReturnError(argc == 3, CHIP_ERROR_INVALID_ARGUMENT);

EmberBindingTableEntry * entry = Platform::New<EmberBindingTableEntry>();
entry->type = EMBER_UNICAST_BINDING;
entry->fabricIndex = atoi(argv[0]);
entry->nodeId = atoi(argv[1]);
entry->local = 1; // Hardcoded to endpoint 1 for now
entry->remote = atoi(argv[2]);
entry->clusterId.SetValue(6)); // Hardcode to OnOff cluster for now

DeviceLayer::PlatformMgr().ScheduleWork(BindingWorkerFunction, reinterpret_cast<intptr_t>(entry));
return CHIP_NO_ERROR;
}

/********************************************************
* Groups switch shell functions
*********************************************************/
Expand Down Expand Up @@ -285,7 +339,7 @@ static void RegisterSwitchCommands()
{ &SwitchHelpHandler, "help", "Usage: switch <subcommand>" },
{ &OnOffSwitchCommandHandler, "onoff", " Usage: switch onoff <subcommand>" },
{ &GroupsSwitchCommandHandler, "groups", "Usage: switch groups <subcommand>" },

{ &BindingSwitchCommandHandler, "binding", "Usage: switch binding <subcommand>" }
};

static const shell_command_t sSwitchOnOffSubCommands[] = {
Expand All @@ -299,19 +353,26 @@ static void RegisterSwitchCommands()
{ &GroupsOnOffSwitchCommandHandler, "onoff",
"Usage: switch groups onoff <subcommand>" } };

static const shell_command_t sSwichGroupsOnOffSubCommands[] = {
static const shell_command_t sSwitchGroupsOnOffSubCommands[] = {
{ &GroupsOnOffHelpHandler, "help", "Usage: switch groups onoff <subcommand>" },
{ &GroupOnSwitchCommandHandler, "on", "Sends on command to bound group" },
{ &GroupOffSwitchCommandHandler, "off", "Sends off command to bound group" },
{ &GroupToggleSwitchCommandHandler, "toggle", "Sends toggle command to group" }
};

static const shell_command_t sSwitchBindingSubCommands[] = {
{ &BindingHelpHandler, "help", "Usage: switch binding <subcommand>" },
{ &BindingGroupBindCommandHandler, "group", "Usage: switch binding group <fabric index> <group id>" },
{ &BindingUnicastBindCommandHandler, "unicast", "Usage: switch binding group <fabric index> <node id> <endpoint>" }
};

static const shell_command_t sSwitchCommand = { &SwitchCommandHandler, "switch",
"Light-switch commands. Usage: switch <subcommand>" };

sShellSwitchGroupsOnOffSubCommands.RegisterCommands(sSwichGroupsOnOffSubCommands, ArraySize(sSwichGroupsOnOffSubCommands));
sShellSwitchGroupsOnOffSubCommands.RegisterCommands(sSwitchGroupsOnOffSubCommands, ArraySize(sSwitchGroupsOnOffSubCommands));
sShellSwitchOnOffSubCommands.RegisterCommands(sSwitchOnOffSubCommands, ArraySize(sSwitchOnOffSubCommands));
sShellSwitchGroupsSubCommands.RegisterCommands(sSwitchGroupsSubCommands, ArraySize(sSwitchGroupsSubCommands));
sShellSwitchBindingSubCommands.RegisterCommands(sSwitchBindingSubCommands, ArraySize(sSwitchBindingSubCommands));
sShellSwitchSubCommands.RegisterCommands(sSwitchSubCommands, ArraySize(sSwitchSubCommands));

Engine::Root().RegisterCommands(&sSwitchCommand, 1);
Expand Down Expand Up @@ -342,6 +403,16 @@ void SwitchWorkerFunction(intptr_t context)
Platform::Delete(data);
}

void BindingWorkerFunction(intptr_t context)
{
VerifyOrReturn(context != 0, ChipLogError(NotSpecified, "BindingWorkerFunction - Invalid work data"));

EmberBindingTableEntry * entry = reinterpret_cast<EmberBindingTableEntry *>(context);
AddBindingEntry(*entry);

Platform::Delete(entry);
}

CHIP_ERROR InitBindingHandler()
{
// The initialization of binding manager will try establishing connection with unicast peers
Expand Down
39 changes: 23 additions & 16 deletions src/app/clusters/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@
#include <app/AttributeAccessInterface.h>
#include <app/CommandHandler.h>
#include <app/ConcreteAttributePath.h>
#include <app/clusters/bindings/BindingManager.h>
#include <app/clusters/bindings/bindings.h>
#include <app/util/attribute-storage.h>
#include <app/util/binding-table.h>
#include <lib/support/logging/CHIPLogging.h>

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
Expand Down Expand Up @@ -86,7 +84,7 @@ CHIP_ERROR CheckValidBindingList(const DecodableBindingListType & bindingList, F
return CHIP_NO_ERROR;
}

void AddBindingEntry(const TargetStructType & entry, EndpointId localEndpoint)
void CreateBindingEntry(const TargetStructType & entry, EndpointId localEndpoint)
{
EmberBindingTableEntry bindingEntry;

Expand All @@ -98,18 +96,9 @@ void AddBindingEntry(const TargetStructType & entry, EndpointId localEndpoint)
{
bindingEntry = EmberBindingTableEntry::ForNode(entry.fabricIndex, entry.node.Value(), localEndpoint, entry.endpoint.Value(),
entry.cluster);
CHIP_ERROR err = BindingManager::GetInstance().UnicastBindingCreated(entry.fabricIndex, entry.node.Value());
if (err != CHIP_NO_ERROR)
{
// Unicast connection failure can happen if peer is offline. We'll retry connection on-demand.
ChipLogProgress(
Zcl, "Binding: Failed to create session for unicast binding to device " ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT,
ChipLogValueX64(entry.node.Value()), err.Format());
}
}
BindingTable::GetInstance().Add(bindingEntry);

BindingManager::GetInstance().NotifyBindingAdded(bindingEntry);
AddBindingEntry(bindingEntry);
}

CHIP_ERROR BindingTableAccess::Read(const ConcreteReadAttributePath & path, AttributeValueEncoder & encoder)
Expand Down Expand Up @@ -200,7 +189,7 @@ CHIP_ERROR BindingTableAccess::WriteBindingTable(const ConcreteDataAttributePath
auto iter = newBindingList.begin();
while (iter.Next())
{
AddBindingEntry(iter.GetValue(), path.mEndpointId);
CreateBindingEntry(iter.GetValue(), path.mEndpointId);
}
return CHIP_NO_ERROR;
}
Expand All @@ -212,7 +201,7 @@ CHIP_ERROR BindingTableAccess::WriteBindingTable(const ConcreteDataAttributePath
{
return CHIP_IM_GLOBAL_STATUS(ConstraintError);
}
AddBindingEntry(target, path.mEndpointId);
CreateBindingEntry(target, path.mEndpointId);
return CHIP_NO_ERROR;
}
return CHIP_IM_GLOBAL_STATUS(UnsupportedWrite);
Expand All @@ -223,3 +212,21 @@ void MatterBindingPluginServerInitCallback()
{
registerAttributeAccessOverride(&gAttrAccess);
}

void AddBindingEntry(const EmberBindingTableEntry & entry)
{
if (entry.type == EMBER_UNICAST_BINDING)
{
CHIP_ERROR err = BindingManager::GetInstance().UnicastBindingCreated(entry.fabricIndex, entry.nodeId);
if (err != CHIP_NO_ERROR)
{
// Unicast connection failure can happen if peer is offline. We'll retry connection on-demand.
ChipLogProgress(
Zcl, "Binding: Failed to create session for unicast binding to device " ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT,
ChipLogValueX64(entry.nodeId), err.Format());
}
}

BindingTable::GetInstance().Add(entry);
BindingManager::GetInstance().NotifyBindingAdded(entry);
}
31 changes: 31 additions & 0 deletions src/app/clusters/bindings/bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <app/clusters/bindings/BindingManager.h>
#include <app/util/binding-table.h>

/**
* @brief appends a binding to the list of bindings
* This function is to be used when a device wants to add a binding to its own table
* If entry is a unicast binding, BindingManager will be notified and will establish a case session with the peer device
* Entry will be added to the binding table and persisted into storage
* BindingManager will be notified and the binding added callback will be called if it has been set
*
* @param entry binding to add
*/
void AddBindingEntry(const EmberBindingTableEntry & entry);

0 comments on commit 1152144

Please sign in to comment.