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

Chime server #35892

Merged
merged 13 commits into from
Oct 25, 2024
279 changes: 279 additions & 0 deletions src/app/clusters/chime-server/chime-server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
/*
*
* Copyright (c) 2024 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.
*/

/****************************************************************************'
* @file
* @brief Implementation for the Chime Server Cluster
***************************************************************************/

#include "chime-server.h"

#include <app/AttributeAccessInterfaceRegistry.h>
#include <app/CommandHandlerInterfaceRegistry.h>
#include <app/ConcreteAttributePath.h>
#include <app/SafeAttributePersistenceProvider.h>
#include <protocols/interaction_model/StatusCode.h>

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters::Chime;
using namespace chip::app::Clusters::Chime::Attributes;
using chip::Protocols::InteractionModel::Status;
using ChimeSoundStructType = Structs::ChimeSoundStruct::Type;

namespace chip {
namespace app {
namespace Clusters {

ChimeServer::ChimeServer(EndpointId endpointId, ChimeDelegate & delegate) :
AttributeAccessInterface(MakeOptional(endpointId), Chime::Id), CommandHandlerInterface(MakeOptional(endpointId), Chime::Id),
mDelegate(delegate), mActiveChimeID(0), mEnabled(true)
{
mDelegate.SetChimeServer(this);
}

ChimeServer::~ChimeServer()
{
// null out the ref to us on the delegate
mDelegate.SetChimeServer(nullptr);

// unregister
CommandHandlerInterfaceRegistry::Instance().UnregisterCommandHandler(this);
AttributeAccessInterfaceRegistry::Instance().Unregister(this);
}

CHIP_ERROR ChimeServer::Init()
{
LoadPersistentAttributes();

VerifyOrReturnError(AttributeAccessInterfaceRegistry::Instance().Register(this), CHIP_ERROR_INTERNAL);
ReturnErrorOnFailure(CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(this));
return CHIP_NO_ERROR;
}

void ChimeServer::LoadPersistentAttributes()
{
// Load Active Chime ID
uint8_t storedActiveChimeID;
CHIP_ERROR err = GetSafeAttributePersistenceProvider()->ReadScalarValue(
ConcreteAttributePath(GetEndpointId(), Chime::Id, ActiveChimeID::Id), storedActiveChimeID);
if (err == CHIP_NO_ERROR)
{
mActiveChimeID = storedActiveChimeID;
}
else
{
// otherwise defaults
ChipLogDetail(Zcl, "Chime: Unable to load the ActiveChimeID attribute from the KVS. Defaulting to %u", mActiveChimeID);
}

// Load Enabled
bool storedEnabled;
err = GetSafeAttributePersistenceProvider()->ReadScalarValue(ConcreteAttributePath(GetEndpointId(), Chime::Id, Enabled::Id),
storedEnabled);
if (err == CHIP_NO_ERROR)
{
mEnabled = storedEnabled;
}
else
{
// otherwise take the default
ChipLogDetail(Zcl, "Chime: Unable to load the Enabled attribute from the KVS. Defaulting to %u", mEnabled);
}
}

// AttributeAccessInterface
CHIP_ERROR ChimeServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
{
VerifyOrDie(aPath.mClusterId == Chime::Id);

switch (aPath.mAttributeId)
{
case ActiveChimeID::Id:
ReturnErrorOnFailure(aEncoder.Encode(mActiveChimeID));
break;
case Enabled::Id:
ReturnErrorOnFailure(aEncoder.Encode(mEnabled));
break;
case InstalledChimeSounds::Id:
ChimeServer * cs = this;
CHIP_ERROR err =
aEncoder.EncodeList([cs](const auto & encoder) -> CHIP_ERROR { return cs->EncodeSupportedChimeSounds(encoder); });
return err;
}

return CHIP_NO_ERROR;
}

uint8_t ChimeServer::GetActiveChimeID() const
{
return mActiveChimeID;
}

bool ChimeServer::GetEnabled() const
{
return mEnabled;
}

// helper method to get the Chime Sounds one by one and encode into a list
CHIP_ERROR ChimeServer::EncodeSupportedChimeSounds(const AttributeValueEncoder::ListEncodeHelper & encoder)
{

for (uint8_t i = 0; true; i++)
{
ChimeSoundStructType chimeSound;

// Get the chime sound
// We pass in a MutableCharSpan to avoid any ownership issues - Delegate needs to use
// CopyCharSpanToMutableCharSpan to copy data in
char buffer[kMaxChimeSoundNameSize];
MutableCharSpan name(buffer);
auto err = mDelegate.GetChimeSoundByIndex(i, chimeSound.chimeID, name);

// return if we've run off the end of the Chime Sound List on the delegate
if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
{
return CHIP_NO_ERROR;
}

ReturnErrorOnFailure(err);

// set the name on the struct
chimeSound.name = name;

// and now encode the struct
ReturnErrorOnFailure(encoder.Encode(chimeSound));
}
return CHIP_NO_ERROR;
}

// helper method to check if the chimeID param is supported by the delegate
bool ChimeServer::IsSupportedChimeID(uint8_t chimeID)
{
uint8_t supportedChimeID;
for (uint8_t i = 0; mDelegate.GetChimeIDByIndex(i, supportedChimeID) != CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; i++)
{
if (supportedChimeID == chimeID)
{
return true;
}
}

ChipLogDetail(Zcl, "Cannot find a supported ChimeID with value %u", chimeID);
return false;
}

CHIP_ERROR ChimeServer::Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder)
{
VerifyOrDie(aPath.mClusterId == Chime::Id);
Status status;

switch (aPath.mAttributeId)
{
case ActiveChimeID::Id: {
uint8_t newValue;
ReturnErrorOnFailure(aDecoder.Decode(newValue));
status = SetActiveChimeID(newValue);
return StatusIB(status).ToChipError();
}
case Enabled::Id: {
bool newValue;
ReturnErrorOnFailure(aDecoder.Decode(newValue));
status = SetEnabled(newValue);
return StatusIB(status).ToChipError();
}

default:
// Unknown attribute
return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
}
}

Status ChimeServer::SetActiveChimeID(uint8_t chimeID)
{
if (!IsSupportedChimeID(chimeID))
{
return Protocols::InteractionModel::Status::ConstraintError;
}

bool activeIDChanged = !(mActiveChimeID == chimeID);
if (activeIDChanged)
{
mActiveChimeID = chimeID;

// Write new value to persistent storage.
auto endpointId = GetEndpointId();
ConcreteAttributePath path = ConcreteAttributePath(endpointId, Chime::Id, ActiveChimeID::Id);
GetSafeAttributePersistenceProvider()->WriteScalarValue(path, mActiveChimeID);

// and mark as dirty
MatterReportingAttributeChangeCallback(path);
}
return Protocols::InteractionModel::Status::Success;
}

Status ChimeServer::SetEnabled(bool Enabled)
{
bool enableChanged = !(mEnabled == Enabled);

if (enableChanged)
{
mEnabled = Enabled;

// Write new value to persistent storage.
auto endpointId = GetEndpointId();
ConcreteAttributePath path = ConcreteAttributePath(endpointId, Chime::Id, Enabled::Id);
GetSafeAttributePersistenceProvider()->WriteScalarValue(path, mEnabled);

// and mark as dirty
MatterReportingAttributeChangeCallback(path);
}

return Protocols::InteractionModel::Status::Success;
}

void ChimeServer::InvokeCommand(HandlerContext & ctx)
{
switch (ctx.mRequestPath.mCommandId)
{
case Commands::PlayChimeSound::Id:
CommandHandlerInterface::HandleCommand<Commands::PlayChimeSound::DecodableType>(
ctx, [this](HandlerContext & ctx, const auto & req) { HandlePlayChimeSound(ctx, req); });
break;
}
}

void ChimeServer::HandlePlayChimeSound(HandlerContext & ctx, const Commands::PlayChimeSound::DecodableType & req)
{

ChipLogDetail(Zcl, "Chime: PlayChimeSound");

// call the delegate to play the chime
Status status = mDelegate.PlayChimeSound();
ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status);
}

} // namespace Clusters
} // namespace app
} // namespace chip

/** @brief Chime Cluster Server Init
*
* Server Init
*
*/
void MatterChimePluginServerInitCallback() {}
Loading
Loading