Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add plumbing that allows fabric-bridge endpoints to report CADMIN attr #35076

Merged
Merged
2 changes: 2 additions & 0 deletions examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ source_set("fabric-bridge-lib") {
public_configs = [ ":config" ]

sources = [
"include/BridgedAdministratorCommissioning.h",
"include/BridgedDevice.h",
"include/BridgedDeviceBasicInformationImpl.h",
"include/BridgedDeviceManager.h",
"include/CHIPProjectAppConfig.h",
"src/BridgedAdministratorCommissioning.cpp",
"src/BridgedDevice.cpp",
"src/BridgedDeviceBasicInformationImpl.cpp",
"src/BridgedDeviceManager.cpp",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2024 Project CHIP Authors
* All rights reserved.
*
* 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-common/zap-generated/cluster-objects.h>
#include <app/AttributeAccessInterfaceRegistry.h>

/**
* @brief CADMIN cluster implementation for handling attribute interactions of bridged device endpoints.
*
* The current Administrator Commissioning Cluster server's zap generated code will automatically
* register an Attribute Access Interface for the root node endpoint implementation. In order to
* properly respond to a read attribute for bridged devices we are representing, we override the
* currently registered Attribute Interface such that we are first to receive any read attribute
* request on Administrator Commissioning Cluster, and if it is not an endpoint for a device we
* are a bridge for we redirect to the default cluster server implementation of Administrator
* Commissioning Cluster.
*/
class BridgedAdministratorCommissioning : public chip::app::AttributeAccessInterface
{
public:
// Register for the AdministratorCommissioning cluster on all endpoints.
BridgedAdministratorCommissioning() :
AttributeAccessInterface(chip::NullOptional, chip::app::Clusters::AdministratorCommissioning::Id)
{}

CHIP_ERROR Init();

CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath & aPath, chip::app::AttributeValueEncoder & aEncoder) override;

// We do not allow writing to CADMIN attributes of a bridged device endpoint. We simply redirect
// write requests to the original attribute interface.
CHIP_ERROR Write(const chip::app::ConcreteDataAttributePath & aPath, chip::app::AttributeValueDecoder & aDecoder) override
{
VerifyOrDie(mOriginalAttributeInterface);
return mOriginalAttributeInterface->Write(aPath, aDecoder);
}

private:
// If mOriginalAttributeInterface is removed from here, the class description needs to be updated
// to reflect this change.
chip::app::AttributeAccessInterface * mOriginalAttributeInterface = nullptr;
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#pragma once

#include <app-common/zap-generated/cluster-objects.h>
#include <app/util/attribute-storage.h>

#include <string>
Expand All @@ -40,6 +41,14 @@ class BridgedDevice
std::string softwareVersionString;
};

struct AdminCommissioningAttributes
{
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum commissioningWindowStatus =
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen;
std::optional<chip::FabricIndex> openerFabricIndex = std::nullopt;
std::optional<chip::VendorId> openerVendorId = std::nullopt;
};

BridgedDevice(chip::NodeId nodeId);
virtual ~BridgedDevice() = default;

Expand All @@ -59,6 +68,8 @@ class BridgedDevice

[[nodiscard]] const BridgedAttributes & GetBridgedAttributes() const { return mAttributes; }
void SetBridgedAttributes(const BridgedAttributes & value) { mAttributes = value; }
// TODO(#35077): Need to allow mAdminCommissioningAttributes to be set from fabric-admin.
const AdminCommissioningAttributes & GetAdminCommissioningAttributes() const { return mAdminCommissioningAttributes; }

/// Convenience method to set just the unique id of a bridged device as it
/// is one of the few attributes that is not always bulk-set
Expand All @@ -73,4 +84,5 @@ class BridgedDevice
chip::EndpointId mParentEndpointId = 0;

BridgedAttributes mAttributes;
AdminCommissioningAttributes mAdminCommissioningAttributes;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2024 Project CHIP Authors
* All rights reserved.
*
* 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.
*/

#include "BridgedAdministratorCommissioning.h"

#include "BridgedDevice.h"
#include "BridgedDeviceManager.h"
#include <app/AttributeAccessInterfaceRegistry.h>

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::AdministratorCommissioning;

CHIP_ERROR BridgedAdministratorCommissioning::Init()
{
// We expect initialization after all embr plugin clusters initialization. This allows us to unregister
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tehampson what is "embr"?

// the existing AccessAttributeInterface for AdministratorCommissioning and register ourselves, ensuring
// we get the callback for reading attribute. If the read is not intended for a bridged device we will
// forward it to the original attribute interface that we are unregistering.
mOriginalAttributeInterface = AttributeAccessInterfaceRegistry::Instance().Get(chip::kRootEndpointId, AdministratorCommissioning::Id);
tehampson marked this conversation as resolved.
Show resolved Hide resolved
VerifyOrReturnError(mOriginalAttributeInterface, CHIP_ERROR_INTERNAL);
AttributeAccessInterfaceRegistry::Instance().Unregister(mOriginalAttributeInterface);
VerifyOrDie(AttributeAccessInterfaceRegistry::Instance().Register(this));
return CHIP_NO_ERROR;
}

CHIP_ERROR BridgedAdministratorCommissioning::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
{
VerifyOrDie(aPath.mClusterId == Clusters::AdministratorCommissioning::Id);
EndpointId endpointId = aPath.mEndpointId;
BridgedDevice * device = BridgeDeviceMgr().GetDevice(endpointId);

if (!device)
{
tehampson marked this conversation as resolved.
Show resolved Hide resolved
VerifyOrDie(mOriginalAttributeInterface);
return mOriginalAttributeInterface->Read(aPath, aEncoder);
}
auto attr = device->GetAdminCommissioningAttributes();

switch (aPath.mAttributeId)
{
case Attributes::WindowStatus::Id: {
return aEncoder.Encode(attr.commissioningWindowStatus);
}
case Attributes::AdminFabricIndex::Id: {
DataModel::Nullable<FabricIndex> encodeableFabricIndex = DataModel::NullNullable;
if (attr.openerFabricIndex.has_value())
{
encodeableFabricIndex.SetNonNull(attr.openerFabricIndex.value());
}
return aEncoder.Encode(encodeableFabricIndex);
}
case Attributes::AdminVendorId::Id: {
DataModel::Nullable<VendorId> encodeableVendorId = DataModel::NullNullable;
if (attr.openerVendorId.has_value())
{
encodeableVendorId.SetNonNull(attr.openerVendorId.value());
}
return aEncoder.Encode(encodeableVendorId);
}
default:
break;
}

return CHIP_NO_ERROR;
}
3 changes: 3 additions & 0 deletions examples/fabric-bridge-app/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <AppMain.h>

#include "BridgedAdministratorCommissioning.h"
#include "BridgedDevice.h"
#include "BridgedDeviceBasicInformationImpl.h"
#include "BridgedDeviceManager.h"
Expand Down Expand Up @@ -234,6 +235,7 @@ void BridgedDeviceInformationCommandHandler::InvokeCommand(HandlerContext & hand
handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, status);
}

BridgedAdministratorCommissioning gBridgedAdministratorCommissioning;
AdministratorCommissioningCommandHandler gAdministratorCommissioningCommandHandler;
BridgedDeviceInformationCommandHandler gBridgedDeviceInformationCommandHandler;

Expand All @@ -258,6 +260,7 @@ void ApplicationInit()
pollingThread.detach();

BridgeDeviceMgr().Init();
VerifyOrDie(gBridgedAdministratorCommissioning.Init() == CHIP_NO_ERROR);

VerifyOrDieWithMsg(CommissionerControlInit() == CHIP_NO_ERROR, NotSpecified,
"Failed to initialize Commissioner Control Server");
Expand Down
Loading