blob: b2b2367f9b826fef72178f6d858bfff5c7e5f6f1 [file] [log] [blame]
/*
*
* 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() {}