blob: c5f109fac63a318dfd60016b2ea10235f9cf9b38 [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.
*/
/**
* @file
* Routines for the Smoke CO Alarm Server plugin.
*
*/
#include "smoke-co-alarm-server.h"
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/callback.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/EventLogging.h>
#include <app/server/Server.h>
#include <app/util/af.h>
#include <app/util/error-mapping.h>
#include <cinttypes>
#include <app/CommandHandler.h>
#include <app/ConcreteAttributePath.h>
#include <app/ConcreteCommandPath.h>
#include <lib/support/CodeUtils.h>
using namespace chip;
using namespace chip::app;
using namespace chip::app::DataModel;
using namespace chip::app::Clusters::SmokeCoAlarm;
using chip::Protocols::InteractionModel::Status;
SmokeCoAlarmServer SmokeCoAlarmServer::sInstance;
/**********************************************************
* SmokeCoAlarmServer public methods
*********************************************************/
SmokeCoAlarmServer & SmokeCoAlarmServer::Instance()
{
return sInstance;
}
bool SmokeCoAlarmServer::SetExpressedState(EndpointId endpointId, ExpressedStateEnum newExpressedState)
{
ExpressedStateEnum expressedState;
bool success = GetAttribute(endpointId, Attributes::ExpressedState::Id, Attributes::ExpressedState::Get, expressedState);
if (success && (expressedState != newExpressedState))
{
success = SetAttribute(endpointId, Attributes::ExpressedState::Id, Attributes::ExpressedState::Set, newExpressedState);
if (success && (newExpressedState == ExpressedStateEnum::kNormal))
{
Events::AllClear::Type event{};
SendEvent(endpointId, event);
}
}
return success;
}
bool SmokeCoAlarmServer::SetSmokeState(EndpointId endpointId, AlarmStateEnum newSmokeState)
{
AlarmStateEnum smokeState;
bool success = GetAttribute(endpointId, Attributes::SmokeState::Id, Attributes::SmokeState::Get, smokeState);
if (success && (smokeState != newSmokeState))
{
success = SetAttribute(endpointId, Attributes::SmokeState::Id, Attributes::SmokeState::Set, newSmokeState);
if (success && (newSmokeState == AlarmStateEnum::kWarning || newSmokeState == AlarmStateEnum::kCritical))
{
Events::SmokeAlarm::Type event{ newSmokeState };
SendEvent(endpointId, event);
}
if (success && (newSmokeState == AlarmStateEnum::kCritical))
{
SetDeviceMuted(endpointId, MuteStateEnum::kNotMuted);
}
}
return success;
}
bool SmokeCoAlarmServer::SetCOState(EndpointId endpointId, AlarmStateEnum newCOState)
{
AlarmStateEnum coState;
bool success = GetAttribute(endpointId, Attributes::COState::Id, Attributes::COState::Get, coState);
if (success && (coState != newCOState))
{
success = SetAttribute(endpointId, Attributes::COState::Id, Attributes::COState::Set, newCOState);
if (success && (newCOState == AlarmStateEnum::kWarning || newCOState == AlarmStateEnum::kCritical))
{
Events::COAlarm::Type event{ newCOState };
SendEvent(endpointId, event);
}
if (success && (newCOState == AlarmStateEnum::kCritical))
{
SetDeviceMuted(endpointId, MuteStateEnum::kNotMuted);
}
}
return success;
}
bool SmokeCoAlarmServer::SetBatteryAlert(EndpointId endpointId, AlarmStateEnum newBatteryAlert)
{
AlarmStateEnum batteryAlert;
bool success = GetAttribute(endpointId, Attributes::BatteryAlert::Id, Attributes::BatteryAlert::Get, batteryAlert);
if (success && (batteryAlert != newBatteryAlert))
{
success = SetAttribute(endpointId, Attributes::BatteryAlert::Id, Attributes::BatteryAlert::Set, newBatteryAlert);
if (success && (newBatteryAlert == AlarmStateEnum::kWarning || newBatteryAlert == AlarmStateEnum::kCritical))
{
Events::LowBattery::Type event{ newBatteryAlert };
SendEvent(endpointId, event);
}
if (success && (newBatteryAlert == AlarmStateEnum::kCritical))
{
SetDeviceMuted(endpointId, MuteStateEnum::kNotMuted);
}
}
return success;
}
bool SmokeCoAlarmServer::SetDeviceMuted(EndpointId endpointId, MuteStateEnum newDeviceMuted)
{
MuteStateEnum deviceMuted;
bool success = GetAttribute(endpointId, Attributes::DeviceMuted::Id, Attributes::DeviceMuted::Get, deviceMuted);
if (success && (deviceMuted != newDeviceMuted))
{
if (newDeviceMuted == MuteStateEnum::kMuted)
{
AlarmStateEnum alarmState;
success = GetAttribute(endpointId, Attributes::SmokeState::Id, Attributes::SmokeState::Get, alarmState);
if (success && (alarmState == AlarmStateEnum::kCritical))
{
success = false;
}
if (success)
{
success = GetAttribute(endpointId, Attributes::COState::Id, Attributes::COState::Get, alarmState);
if (success && (alarmState == AlarmStateEnum::kCritical))
{
success = false;
}
}
if (success)
{
success = GetAttribute(endpointId, Attributes::BatteryAlert::Id, Attributes::BatteryAlert::Get, alarmState);
if (success && (alarmState == AlarmStateEnum::kCritical))
{
success = false;
}
}
if (success)
{
success = GetAttribute(endpointId, Attributes::InterconnectSmokeAlarm::Id, Attributes::InterconnectSmokeAlarm::Get,
alarmState);
if (success && (alarmState == AlarmStateEnum::kCritical))
{
success = false;
}
}
if (success)
{
success =
GetAttribute(endpointId, Attributes::InterconnectCOAlarm::Id, Attributes::InterconnectCOAlarm::Get, alarmState);
if (success && (alarmState == AlarmStateEnum::kCritical))
{
success = false;
}
}
}
if (success)
{
success = SetAttribute(endpointId, Attributes::DeviceMuted::Id, Attributes::DeviceMuted::Set, newDeviceMuted);
}
if (success)
{
if (newDeviceMuted == MuteStateEnum::kMuted)
{
Events::AlarmMuted::Type event{};
SendEvent(endpointId, event);
}
else if (newDeviceMuted == MuteStateEnum::kNotMuted)
{
Events::MuteEnded::Type event{};
SendEvent(endpointId, event);
}
}
}
return success;
}
bool SmokeCoAlarmServer::SetTestInProgress(EndpointId endpointId, bool newTestInProgress)
{
bool testInProgress;
bool success = GetAttribute(endpointId, Attributes::TestInProgress::Id, Attributes::TestInProgress::Get, testInProgress);
if (success && (testInProgress != newTestInProgress))
{
success = SetAttribute(endpointId, Attributes::TestInProgress::Id, Attributes::TestInProgress::Set, newTestInProgress);
if (success && !newTestInProgress)
{
Events::SelfTestComplete::Type event{};
SendEvent(endpointId, event);
}
}
return success;
}
bool SmokeCoAlarmServer::SetHardwareFaultAlert(EndpointId endpointId, bool newHardwareFaultAlert)
{
bool hardwareFaultAlert;
bool success =
GetAttribute(endpointId, Attributes::HardwareFaultAlert::Id, Attributes::HardwareFaultAlert::Get, hardwareFaultAlert);
if (success && (hardwareFaultAlert != newHardwareFaultAlert))
{
success = SetAttribute(endpointId, Attributes::HardwareFaultAlert::Id, Attributes::HardwareFaultAlert::Set,
newHardwareFaultAlert);
if (success && newHardwareFaultAlert)
{
Events::HardwareFault::Type event{};
SendEvent(endpointId, event);
}
}
return success;
}
bool SmokeCoAlarmServer::SetEndOfServiceAlert(EndpointId endpointId, EndOfServiceEnum newEndOfServiceAlert)
{
EndOfServiceEnum endOfServiceAlert;
bool success =
GetAttribute(endpointId, Attributes::EndOfServiceAlert::Id, Attributes::EndOfServiceAlert::Get, endOfServiceAlert);
if (success && (endOfServiceAlert != newEndOfServiceAlert))
{
success =
SetAttribute(endpointId, Attributes::EndOfServiceAlert::Id, Attributes::EndOfServiceAlert::Set, newEndOfServiceAlert);
if (success && (newEndOfServiceAlert == EndOfServiceEnum::kExpired))
{
Events::EndOfService::Type event{};
SendEvent(endpointId, event);
}
}
return success;
}
bool SmokeCoAlarmServer::SetInterconnectSmokeAlarm(EndpointId endpointId, AlarmStateEnum newInterconnectSmokeAlarm)
{
AlarmStateEnum interconnectSmokeAlarm;
bool success = GetAttribute(endpointId, Attributes::InterconnectSmokeAlarm::Id, Attributes::InterconnectSmokeAlarm::Get,
interconnectSmokeAlarm);
if (success && (interconnectSmokeAlarm != newInterconnectSmokeAlarm))
{
success = SetAttribute(endpointId, Attributes::InterconnectSmokeAlarm::Id, Attributes::InterconnectSmokeAlarm::Set,
newInterconnectSmokeAlarm);
if (success &&
(newInterconnectSmokeAlarm == AlarmStateEnum::kWarning || newInterconnectSmokeAlarm == AlarmStateEnum::kCritical))
{
Events::InterconnectSmokeAlarm::Type event{ newInterconnectSmokeAlarm };
SendEvent(endpointId, event);
}
if (success && (newInterconnectSmokeAlarm == AlarmStateEnum::kCritical))
{
SetDeviceMuted(endpointId, MuteStateEnum::kNotMuted);
}
}
return success;
}
bool SmokeCoAlarmServer::SetInterconnectCOAlarm(EndpointId endpointId, AlarmStateEnum newInterconnectCOAlarm)
{
AlarmStateEnum interconnectCOAlarm;
bool success =
GetAttribute(endpointId, Attributes::InterconnectCOAlarm::Id, Attributes::InterconnectCOAlarm::Get, interconnectCOAlarm);
if (success && (interconnectCOAlarm != newInterconnectCOAlarm))
{
success = SetAttribute(endpointId, Attributes::InterconnectCOAlarm::Id, Attributes::InterconnectCOAlarm::Set,
newInterconnectCOAlarm);
if (success && (newInterconnectCOAlarm == AlarmStateEnum::kWarning || newInterconnectCOAlarm == AlarmStateEnum::kCritical))
{
Events::InterconnectCOAlarm::Type event{ newInterconnectCOAlarm };
SendEvent(endpointId, event);
}
if (success && (newInterconnectCOAlarm == AlarmStateEnum::kCritical))
{
SetDeviceMuted(endpointId, MuteStateEnum::kNotMuted);
}
}
return success;
}
bool SmokeCoAlarmServer::SetContaminationState(EndpointId endpointId, ContaminationStateEnum newContaminationState)
{
ContaminationStateEnum contaminationState;
bool success =
GetAttribute(endpointId, Attributes::ContaminationState::Id, Attributes::ContaminationState::Get, contaminationState);
if (success && (contaminationState != newContaminationState))
{
success = SetAttribute(endpointId, Attributes::ContaminationState::Id, Attributes::ContaminationState::Set,
newContaminationState);
}
return success;
}
bool SmokeCoAlarmServer::SetSmokeSensitivityLevel(EndpointId endpointId, SensitivityEnum newSmokeSensitivityLevel)
{
SensitivityEnum smokeSensitivityLevel;
bool success = GetAttribute(endpointId, Attributes::SmokeSensitivityLevel::Id, Attributes::SmokeSensitivityLevel::Get,
smokeSensitivityLevel);
if (success && (smokeSensitivityLevel != newSmokeSensitivityLevel))
{
success = SetAttribute(endpointId, Attributes::SmokeSensitivityLevel::Id, Attributes::SmokeSensitivityLevel::Set,
newSmokeSensitivityLevel);
}
return success;
}
bool SmokeCoAlarmServer::GetExpressedState(chip ::EndpointId endpointId, ExpressedStateEnum & expressedState)
{
return GetAttribute(endpointId, Attributes::ExpressedState::Id, Attributes::ExpressedState::Get, expressedState);
}
bool SmokeCoAlarmServer::GetSmokeState(EndpointId endpointId, AlarmStateEnum & smokeState)
{
return GetAttribute(endpointId, Attributes::SmokeState::Id, Attributes::SmokeState::Get, smokeState);
}
bool SmokeCoAlarmServer::GetCOState(EndpointId endpointId, AlarmStateEnum & coState)
{
return GetAttribute(endpointId, Attributes::COState::Id, Attributes::COState::Get, coState);
}
bool SmokeCoAlarmServer::GetBatteryAlert(EndpointId endpointId, AlarmStateEnum & batteryAlert)
{
return GetAttribute(endpointId, Attributes::BatteryAlert::Id, Attributes::BatteryAlert::Get, batteryAlert);
}
bool SmokeCoAlarmServer::GetDeviceMuted(EndpointId endpointId, MuteStateEnum & deviceMuted)
{
return GetAttribute(endpointId, Attributes::DeviceMuted::Id, Attributes::DeviceMuted::Get, deviceMuted);
}
bool SmokeCoAlarmServer::GetTestInProgress(EndpointId endpointId, bool & testInProgress)
{
return GetAttribute(endpointId, Attributes::TestInProgress::Id, Attributes::TestInProgress::Get, testInProgress);
}
bool SmokeCoAlarmServer::GetHardwareFaultAlert(EndpointId endpointId, bool & hardwareFaultAlert)
{
return GetAttribute(endpointId, Attributes::HardwareFaultAlert::Id, Attributes::HardwareFaultAlert::Get, hardwareFaultAlert);
}
bool SmokeCoAlarmServer::GetEndOfServiceAlert(EndpointId endpointId, EndOfServiceEnum & endOfServiceAlert)
{
return GetAttribute(endpointId, Attributes::EndOfServiceAlert::Id, Attributes::EndOfServiceAlert::Get, endOfServiceAlert);
}
bool SmokeCoAlarmServer::GetInterconnectSmokeAlarm(EndpointId endpointId, AlarmStateEnum & interconnectSmokeAlarm)
{
return GetAttribute(endpointId, Attributes::InterconnectSmokeAlarm::Id, Attributes::InterconnectSmokeAlarm::Get,
interconnectSmokeAlarm);
}
bool SmokeCoAlarmServer::GetInterconnectCOAlarm(EndpointId endpointId, AlarmStateEnum & interconnectCOAlarm)
{
return GetAttribute(endpointId, Attributes::InterconnectCOAlarm::Id, Attributes::InterconnectCOAlarm::Get, interconnectCOAlarm);
}
bool SmokeCoAlarmServer::GetContaminationState(EndpointId endpointId, ContaminationStateEnum & contaminationState)
{
return GetAttribute(endpointId, Attributes::ContaminationState::Id, Attributes::ContaminationState::Get, contaminationState);
}
bool SmokeCoAlarmServer::GetSmokeSensitivityLevel(EndpointId endpointId, SensitivityEnum & smokeSensitivityLevel)
{
return GetAttribute(endpointId, Attributes::SmokeSensitivityLevel::Id, Attributes::SmokeSensitivityLevel::Get,
smokeSensitivityLevel);
}
bool SmokeCoAlarmServer::GetExpiryDate(EndpointId endpointId, uint32_t & expiryDate)
{
return GetAttribute(endpointId, Attributes::ExpiryDate::Id, Attributes::ExpiryDate::Get, expiryDate);
}
chip::BitFlags<Feature> SmokeCoAlarmServer::GetFeatures(EndpointId endpointId)
{
chip::BitFlags<Feature> featureMap;
if (!GetAttribute(endpointId, Attributes::FeatureMap::Id, Attributes::FeatureMap::Get, *featureMap.RawStorage()))
{
ChipLogError(Zcl, "Unable to get the Smoke CO Alarm feature map: attribute read error");
featureMap.ClearAll();
}
return featureMap;
}
/**********************************************************
* SmokeCoAlarmServer private methods
*********************************************************/
bool SmokeCoAlarmServer::HandleRemoteSelfTestRequest(CommandHandler * commandObj, const ConcreteCommandPath & commandPath)
{
EndpointId endpointId = commandPath.mEndpointId;
Status status = Status::Success;
ExpressedStateEnum expressedState;
bool success = GetExpressedState(endpointId, expressedState);
if (success)
{
// If the value is busy then return busy
if (expressedState == ExpressedStateEnum::kSmokeAlarm || expressedState == ExpressedStateEnum::kCOAlarm ||
expressedState == ExpressedStateEnum::kTesting || expressedState == ExpressedStateEnum::kInterconnectSmoke ||
expressedState == ExpressedStateEnum::kInterconnectCO)
{
status = Status::Busy;
}
else
{
success = SetExpressedState(endpointId, ExpressedStateEnum::kTesting);
if (success)
{
success = emberAfPluginSmokeCoAlarmSelfTestRequestCommand(endpointId);
}
}
}
if (!success)
{
status = Status::Failure;
}
commandObj->AddStatus(commandPath, status);
return true;
}
template <typename T>
void SmokeCoAlarmServer::SendEvent(EndpointId endpointId, T & event)
{
EventNumber eventNumber;
auto err = LogEvent(event, endpointId, eventNumber);
if (CHIP_NO_ERROR != err)
{
ChipLogError(Zcl, "Failed to log event: err=%" CHIP_ERROR_FORMAT ", event_id=" ChipLogFormatMEI, err.Format(),
ChipLogValueMEI(event.GetEventId()));
}
}
template <typename T>
bool SmokeCoAlarmServer::GetAttribute(EndpointId endpointId, AttributeId attributeId,
EmberAfStatus (*getFn)(EndpointId endpointId, T * value), T & value) const
{
EmberAfStatus status = getFn(endpointId, &value);
bool success = (EMBER_ZCL_STATUS_SUCCESS == status);
if (!success)
{
ChipLogError(Zcl, "Failed to read SmokeCOAlarm attribute: attribute=" ChipLogFormatMEI ", status=0x%x",
ChipLogValueMEI(attributeId), to_underlying(status));
}
return success;
}
template <typename T>
bool SmokeCoAlarmServer::SetAttribute(EndpointId endpointId, AttributeId attributeId,
EmberAfStatus (*setFn)(EndpointId endpointId, T value), T value)
{
EmberAfStatus status = setFn(endpointId, value);
bool success = (EMBER_ZCL_STATUS_SUCCESS == status);
if (!success)
{
ChipLogError(Zcl, "Failed to write SmokeCOAlarm attribute: attribute=" ChipLogFormatMEI ", status=0x%x",
ChipLogValueMEI(attributeId), to_underlying(status));
}
return success;
}
// =============================================================================
// Cluster commands callbacks
// =============================================================================
bool emberAfSmokeCoAlarmClusterSelfTestRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
const Commands::SelfTestRequest::DecodableType & commandData)
{
return SmokeCoAlarmServer::Instance().HandleRemoteSelfTestRequest(commandObj, commandPath);
}
void MatterSmokeCoAlarmPluginServerInitCallback() {}