/* * * 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 #include #include #include using namespace chip; using namespace chip::app; using namespace chip::app::Clusters::Thermostat; using namespace chip::app::Clusters::Thermostat::Structs; ThermostatDelegate ThermostatDelegate::sInstance; ThermostatDelegate::ThermostatDelegate() { mNumberOfPresets = kMaxNumberOfPresetsSupported; mNextFreeIndexInPresetsList = 0; mNextFreeIndexInPendingPresetsList = 0; InitializePresets(); memset(mActivePresetHandleData, 0, sizeof(mActivePresetHandleData)); mActivePresetHandleDataSize = 0; } void ThermostatDelegate::InitializePresets() { // Initialize the presets with 2 built in presets - occupied and unoccupied. PresetScenarioEnum presetScenarioEnumArray[2] = { PresetScenarioEnum::kOccupied, PresetScenarioEnum::kUnoccupied }; static_assert(ArraySize(presetScenarioEnumArray) <= ArraySize(mPresets)); uint8_t index = 0; for (PresetScenarioEnum presetScenario : presetScenarioEnumArray) { mPresets[index].SetPresetScenario(presetScenario); // Set the preset handle to the preset scenario value as a unique id. const uint8_t handle[] = { static_cast(presetScenario) }; mPresets[index].SetPresetHandle(DataModel::MakeNullable(ByteSpan(handle))); mPresets[index].SetName(NullOptional); int16_t coolingSetpointValue = static_cast(2500 + (index * 100)); mPresets[index].SetCoolingSetpoint(MakeOptional(coolingSetpointValue)); int16_t heatingSetpointValue = static_cast(2100 - (index * 100)); mPresets[index].SetHeatingSetpoint(MakeOptional(heatingSetpointValue)); mPresets[index].SetBuiltIn(DataModel::MakeNullable(true)); index++; } // Set the value of the next free index in the presets list. mNextFreeIndexInPresetsList = index; } CHIP_ERROR ThermostatDelegate::GetPresetTypeAtIndex(size_t index, PresetTypeStruct::Type & presetType) { static PresetTypeStruct::Type presetTypes[] = { { .presetScenario = PresetScenarioEnum::kOccupied, .numberOfPresets = kMaxNumberOfPresetsOfEachType, .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kAutomatic) }, { .presetScenario = PresetScenarioEnum::kUnoccupied, .numberOfPresets = kMaxNumberOfPresetsOfEachType, .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kAutomatic) }, { .presetScenario = PresetScenarioEnum::kSleep, .numberOfPresets = kMaxNumberOfPresetsOfEachType, .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, { .presetScenario = PresetScenarioEnum::kWake, .numberOfPresets = kMaxNumberOfPresetsOfEachType, .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, { .presetScenario = PresetScenarioEnum::kVacation, .numberOfPresets = kMaxNumberOfPresetsOfEachType, .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, { .presetScenario = PresetScenarioEnum::kUserDefined, .numberOfPresets = kMaxNumberOfPresetsOfEachType, .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, }; if (index < ArraySize(presetTypes)) { presetType = presetTypes[index]; return CHIP_NO_ERROR; } return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; } uint8_t ThermostatDelegate::GetNumberOfPresets() { return mNumberOfPresets; } CHIP_ERROR ThermostatDelegate::GetPresetAtIndex(size_t index, PresetStructWithOwnedMembers & preset) { if (index < mNextFreeIndexInPresetsList) { preset = mPresets[index]; return CHIP_NO_ERROR; } return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; } CHIP_ERROR ThermostatDelegate::GetActivePresetHandle(DataModel::Nullable & activePresetHandle) { if (mActivePresetHandleDataSize != 0) { ReturnErrorOnFailure( CopySpanToMutableSpan(ByteSpan(mActivePresetHandleData, mActivePresetHandleDataSize), activePresetHandle.Value())); activePresetHandle.Value().reduce_size(mActivePresetHandleDataSize); } else { activePresetHandle.SetNull(); } return CHIP_NO_ERROR; } CHIP_ERROR ThermostatDelegate::SetActivePresetHandle(const DataModel::Nullable & newActivePresetHandle) { if (!newActivePresetHandle.IsNull()) { size_t newActivePresetHandleSize = newActivePresetHandle.Value().size(); if (newActivePresetHandleSize > sizeof(mActivePresetHandleData)) { ChipLogError(NotSpecified, "Failed to set ActivePresetHandle. newActivePresetHandle size %u is larger than preset handle size %u", static_cast(newActivePresetHandleSize), static_cast(kPresetHandleSize)); return CHIP_ERROR_NO_MEMORY; } memcpy(mActivePresetHandleData, newActivePresetHandle.Value().data(), newActivePresetHandleSize); mActivePresetHandleDataSize = newActivePresetHandleSize; ChipLogDetail(NotSpecified, "Set ActivePresetHandle to "); ChipLogByteSpan(NotSpecified, newActivePresetHandle.Value()); } else { memset(mActivePresetHandleData, 0, sizeof(mActivePresetHandleData)); mActivePresetHandleDataSize = 0; ChipLogDetail(NotSpecified, "Clear ActivePresetHandle"); } return CHIP_NO_ERROR; } std::optional ThermostatDelegate::GetMaxAtomicWriteTimeout(chip::AttributeId attributeId) { switch (attributeId) { case Attributes::Presets::Id: // If the client expects to edit the presets, then we'll give it 3 seconds to do so return std::chrono::milliseconds(3000); case Attributes::Schedules::Id: // If the client expects to edit the schedules, then we'll give it 9 seconds to do so return std::chrono::milliseconds(9000); default: return std::nullopt; } } void ThermostatDelegate::InitializePendingPresets() { mNextFreeIndexInPendingPresetsList = 0; for (uint8_t indexInPresets = 0; indexInPresets < mNextFreeIndexInPresetsList; indexInPresets++) { mPendingPresets[mNextFreeIndexInPendingPresetsList] = mPresets[indexInPresets]; mNextFreeIndexInPendingPresetsList++; } } CHIP_ERROR ThermostatDelegate::AppendToPendingPresetList(const PresetStructWithOwnedMembers & preset) { if (mNextFreeIndexInPendingPresetsList < ArraySize(mPendingPresets)) { mPendingPresets[mNextFreeIndexInPendingPresetsList] = preset; if (preset.GetPresetHandle().IsNull()) { // TODO: #34556 Since we support only one preset of each type, using the octet string containing the preset scenario // suffices as the unique preset handle. Need to fix this to actually provide unique handles once multiple presets of // each type are supported. const uint8_t handle[] = { static_cast(preset.GetPresetScenario()) }; mPendingPresets[mNextFreeIndexInPendingPresetsList].SetPresetHandle(DataModel::MakeNullable(ByteSpan(handle))); } mNextFreeIndexInPendingPresetsList++; return CHIP_NO_ERROR; } return CHIP_ERROR_WRITE_FAILED; } CHIP_ERROR ThermostatDelegate::GetPendingPresetAtIndex(size_t index, PresetStructWithOwnedMembers & preset) { if (index < mNextFreeIndexInPendingPresetsList) { preset = mPendingPresets[index]; return CHIP_NO_ERROR; } return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; } CHIP_ERROR ThermostatDelegate::CommitPendingPresets() { mNextFreeIndexInPresetsList = 0; for (uint8_t indexInPendingPresets = 0; indexInPendingPresets < mNextFreeIndexInPendingPresetsList; indexInPendingPresets++) { const PresetStructWithOwnedMembers & pendingPreset = mPendingPresets[indexInPendingPresets]; mPresets[mNextFreeIndexInPresetsList] = pendingPreset; mNextFreeIndexInPresetsList++; } return CHIP_NO_ERROR; } void ThermostatDelegate::ClearPendingPresetList() { mNextFreeIndexInPendingPresetsList = 0; }