blob: b3c9b8edbccf3406796d171869cfbb3badcf707e [file] [log] [blame]
/**
*
* Copyright (c) 2023 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.
*
*/
#include "dishwasher-alarm-server.h"
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/cluster-enums.h>
#include <app-common/zap-generated/cluster-objects.h>
#include <app/EventLogging.h>
#include <app/InteractionModelEngine.h>
#include <app/util/attribute-storage.h>
#include <app/util/config.h>
#include <lib/support/BitFlags.h>
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::DishwasherAlarm;
using namespace chip::app::Clusters::DishwasherAlarm::Attributes;
using namespace chip::DeviceLayer;
using chip::Protocols::InteractionModel::Status;
using namespace std;
static constexpr size_t kDishwasherAlarmDelegateTableSize =
MATTER_DM_DISHWASHER_ALARM_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
static_assert(kDishwasherAlarmDelegateTableSize <= kEmberInvalidEndpointIndex, "Dishwasher Alarm Delegate table size error");
namespace chip {
namespace app {
namespace Clusters {
namespace DishwasherAlarm {
Delegate * gDelegateTable[kDishwasherAlarmDelegateTableSize] = { nullptr };
Delegate * GetDelegate(EndpointId endpoint)
{
uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, DishwasherAlarm::Id,
MATTER_DM_DISHWASHER_ALARM_CLUSTER_SERVER_ENDPOINT_COUNT);
return (ep >= kDishwasherAlarmDelegateTableSize ? nullptr : gDelegateTable[ep]);
}
void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate)
{
uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, DishwasherAlarm::Id,
MATTER_DM_DISHWASHER_ALARM_CLUSTER_SERVER_ENDPOINT_COUNT);
// if endpoint is found
if (ep < kDishwasherAlarmDelegateTableSize)
{
gDelegateTable[ep] = delegate;
}
}
} // namespace DishwasherAlarm
} // namespace Clusters
} // namespace app
} // namespace chip
DishwasherAlarmServer DishwasherAlarmServer::instance;
DishwasherAlarmServer & DishwasherAlarmServer::Instance()
{
return instance;
}
Status DishwasherAlarmServer::GetMaskValue(EndpointId endpoint, BitMask<AlarmMap> * mask)
{
Status status = Attributes::Mask::Get(endpoint, mask);
if (status != Status::Success)
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: reading mask, err:0x%x", to_underlying(status));
return status;
}
return status;
}
Status DishwasherAlarmServer::GetLatchValue(EndpointId endpoint, BitMask<AlarmMap> * latch)
{
if (!HasResetFeature(endpoint))
{
ChipLogProgress(Zcl, "Dishwasher Alarm feature: Unsupport Latch attribute");
return Status::UnsupportedAttribute;
}
Status status = Attributes::Latch::Get(endpoint, latch);
if (status != Status::Success)
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: reading latch, err:0x%x", to_underlying(status));
return status;
}
return status;
}
Status DishwasherAlarmServer::GetStateValue(EndpointId endpoint, BitMask<AlarmMap> * state)
{
Status status = Attributes::State::Get(endpoint, state);
if (status != Status::Success)
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: get state, err:0x%x", to_underlying(status));
return status;
}
return status;
}
Status DishwasherAlarmServer::GetSupportedValue(EndpointId endpoint, BitMask<AlarmMap> * supported)
{
Status status = Attributes::Supported::Get(endpoint, supported);
if (status != Status::Success)
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: reading supported, err:0x%x", to_underlying(status));
}
return status;
}
Status DishwasherAlarmServer::SetSupportedValue(EndpointId endpoint, const BitMask<AlarmMap> supported)
{
Status status = Status::Success;
if ((status = Attributes::Supported::Set(endpoint, supported)) != Status::Success)
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: writing supported, err:0x%x", to_underlying(status));
return status;
}
// Whenever there is change in Supported attribute, Latch should change accordingly (if possible).
BitMask<AlarmMap> latch;
if (GetLatchValue(endpoint, &latch) == Status::Success && !supported.HasAll(latch))
{
latch = latch & supported;
status = SetLatchValue(endpoint, latch);
}
// Whenever there is change in Supported attribute, Mask, State should change accordingly.
BitMask<AlarmMap> mask;
if ((status = GetMaskValue(endpoint, &mask)) != Status::Success)
{
return status;
}
if (!supported.HasAll(mask))
{
mask = supported & mask;
status = SetMaskValue(endpoint, mask);
}
return status;
}
Status DishwasherAlarmServer::SetMaskValue(EndpointId endpoint, const BitMask<AlarmMap> mask)
{
BitMask<AlarmMap> supported;
if (Status::Success != GetSupportedValue(endpoint, &supported) || !supported.HasAll(mask))
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: Mask is not supported");
return Status::Failure;
}
Status status = Status::Success;
if ((status = Attributes::Mask::Set(endpoint, mask)) != Status::Success)
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: writing mask, err:0x%x", to_underlying(status));
return status;
}
// Whenever there is change in Mask, State should change accordingly.
BitMask<AlarmMap> state;
status = GetStateValue(endpoint, &state);
if (status != Status::Success)
{
return status;
}
if (!mask.HasAll(state))
{
state = mask & state;
status = SetStateValue(endpoint, state, true);
}
return status;
}
Status DishwasherAlarmServer::SetLatchValue(EndpointId endpoint, const BitMask<AlarmMap> latch)
{
if (!HasResetFeature(endpoint))
{
ChipLogProgress(Zcl, "Dishwasher Alarm feature: Unsupport Latch attribute");
return Status::UnsupportedAttribute;
}
BitMask<AlarmMap> supported;
if (Status::Success != GetSupportedValue(endpoint, &supported) || !supported.HasAll(latch))
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: Latch is not supported");
return Status::Failure;
}
Status status = Attributes::Latch::Set(endpoint, latch);
if (status != Status::Success)
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: writing latch, err:0x%x", to_underlying(status));
return status;
}
return status;
}
Status DishwasherAlarmServer::SetStateValue(EndpointId endpoint, const BitMask<AlarmMap> newState, bool ignoreLatchState)
{
BitMask<AlarmMap> supported;
BitMask<AlarmMap> finalNewState;
finalNewState.Set(newState);
if (Status::Success != GetSupportedValue(endpoint, &supported) || !supported.HasAll(finalNewState))
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: Alarm is not supported");
return Status::Failure;
}
BitMask<AlarmMap> mask;
if (Status::Success != GetMaskValue(endpoint, &mask) || !mask.HasAll(finalNewState))
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: Alarm is suppressed");
return Status::Failure;
}
Status status = Status::Success;
BitMask<AlarmMap> currentState;
status = Attributes::State::Get(endpoint, &currentState);
if (status != Status::Success)
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: reading state, err:0x%x", to_underlying(status));
return status;
}
BitMask<AlarmMap> latch;
if (!ignoreLatchState && (GetLatchValue(endpoint, &latch) == Status::Success))
{
// Restore bits that have their Latch bit set.
auto bitsToKeep = latch & currentState;
finalNewState.Set(bitsToKeep);
}
// Store the new value of the State attribute.
status = Attributes::State::Set(endpoint, finalNewState);
if (status != Status::Success)
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: writing state, err:0x%x", to_underlying(status));
return status;
}
// Generate Notify event.
BitMask<AlarmMap> becameActive;
becameActive.Set(finalNewState).Clear(currentState);
BitMask<AlarmMap> becameInactive;
becameInactive.Set(currentState).Clear(finalNewState);
SendNotifyEvent(endpoint, becameActive, becameInactive, finalNewState, mask);
return status;
}
Status DishwasherAlarmServer::ResetLatchedAlarms(EndpointId endpoint, const BitMask<AlarmMap> alarms)
{
BitMask<AlarmMap> supported;
if (Status::Success != GetSupportedValue(endpoint, &supported) || !supported.HasAll(alarms))
{
ChipLogProgress(Zcl, "Dishwasher Alarm: ERR: Alarm is not supported");
return Status::Failure;
}
BitMask<AlarmMap> state;
if (GetStateValue(endpoint, &state) != Status::Success)
{
return Status::Failure;
}
state.Clear(alarms);
return SetStateValue(endpoint, state, true);
}
bool DishwasherAlarmServer::HasResetFeature(EndpointId endpoint)
{
uint32_t featureMap = 0;
if (Attributes::FeatureMap::Get(endpoint, &featureMap) != Status::Success)
{
return false;
}
if (featureMap & to_underlying(Feature::kReset))
{
return true;
}
return false;
}
void DishwasherAlarmServer::SendNotifyEvent(EndpointId endpointId, BitMask<AlarmMap> becameActive, BitMask<AlarmMap> becameInactive,
BitMask<AlarmMap> newState, BitMask<AlarmMap> mask)
{
Events::Notify::Type event{ .active = becameActive, .inactive = becameInactive, .state = newState, .mask = mask };
EventNumber eventNumber;
CHIP_ERROR error = LogEvent(event, endpointId, eventNumber);
if (CHIP_NO_ERROR != error)
{
ChipLogError(Zcl, "[Notify] Unable to send notify event: %s [endpointId=%d]", error.AsString(), endpointId);
}
}
static Status ModifyEnabledHandler(const app::ConcreteCommandPath & commandPath, const BitMask<AlarmMap> mask)
{
EndpointId endpoint = commandPath.mEndpointId;
BitMask<AlarmMap> supported;
if (DishwasherAlarmServer::Instance().GetSupportedValue(endpoint, &supported) != Status::Success)
{
return Status::Failure;
}
// receives this command with a Mask that includes bits that are set for alarms which are not supported
if (!supported.HasAll(mask))
{
return Status::InvalidCommand;
}
// A server that is unable to enable a currently suppressed alarm,
// or is unable to suppress a currently enabled alarm SHALL respond
// with a status code of FAILURE
Delegate * delegate = DishwasherAlarm::GetDelegate(endpoint);
if (delegate && !(delegate->ModifyEnabledAlarmsCallback(mask)))
{
ChipLogProgress(Zcl, "Unable to modify enabled alarms");
return Status::Failure;
}
// The cluster will do this update if delegate.ModifyEnabledAlarmsCallback() returns true.
if (DishwasherAlarmServer::Instance().SetMaskValue(endpoint, mask) != Status::Success)
{
return Status::Failure;
}
return Status::Success;
}
static Status ResetHandler(const app::ConcreteCommandPath & commandPath, const BitMask<AlarmMap> alarms)
{
EndpointId endpoint = commandPath.mEndpointId;
if (!DishwasherAlarmServer::Instance().HasResetFeature(endpoint))
{
ChipLogProgress(Zcl, "Dishwasher Alarm feature: Unsupport Reset Command");
return Status::UnsupportedCommand;
}
// A server that is unable to reset alarms SHALL respond with a status code of FAILURE
Delegate * delegate = DishwasherAlarm::GetDelegate(endpoint);
if (delegate && !(delegate->ResetAlarmsCallback(alarms)))
{
ChipLogProgress(Zcl, "Unable to reset alarms");
return Status::Failure;
}
// The cluster will do this update if delegate.ResetAlarmsCallback() returns true.
if (DishwasherAlarmServer::Instance().ResetLatchedAlarms(endpoint, alarms) != Status::Success)
{
ChipLogProgress(Zcl, "reset alarms fail");
return Status::Failure;
}
return Status::Success;
}
bool emberAfDishwasherAlarmClusterResetCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath,
const Commands::Reset::DecodableType & commandData)
{
auto & alarms = commandData.alarms;
Status status = ResetHandler(commandPath, alarms);
commandObj->AddStatus(commandPath, status);
return true;
}
bool emberAfDishwasherAlarmClusterModifyEnabledAlarmsCallback(app::CommandHandler * commandObj,
const app::ConcreteCommandPath & commandPath,
const Commands::ModifyEnabledAlarms::DecodableType & commandData)
{
auto & mask = commandData.mask;
Status status = ModifyEnabledHandler(commandPath, mask);
commandObj->AddStatus(commandPath, status);
return true;
}