/* * * Copyright (c) 2023-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 "AppTask.h" #include "Device.h" #include "PWMManager.h" #include #include #include #include LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); namespace { bool sTurnedOn; uint8_t sLevel; } // namespace AppTask AppTask::sAppTask; #include int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, const Span & deviceTypeList, const Span & dataVersionStorage, chip::EndpointId parentEndpointId); CHIP_ERROR RemoveDeviceEndpoint(Device * dev); void HandleDeviceTempSensorStatusChanged(DeviceTempSensor * dev, DeviceTempSensor::Changed_t itemChangedMask); Protocols::InteractionModel::Status HandleReadTempMeasurementAttribute(DeviceTempSensor * dev, chip::AttributeId attributeId, uint8_t * buffer, uint16_t maxReadLength); static const int kNodeLabelSize = 32; // Current ZCL implementation of Struct uses a max-size array of 254 bytes static const int kDescriptorAttributeArraySize = 254; static EndpointId gCurrentEndpointId; static EndpointId gFirstDynamicEndpointId; static Device * gDevices[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT]; // number of dynamic endpoints count const int16_t minMeasuredValue = -27315; const int16_t maxMeasuredValue = 32766; const int16_t initialMeasuredValue = 100; // 5 Bridged devices static Device gLight1("Light 1", "Office"); static Device gLight2("Light 2", "Office"); static Device gLight3("Light 3", "Kitchen"); static Device gLight4("Light 4", "Kitchen"); static DeviceTempSensor TempSensor1("TempSensor 1", "Office", minMeasuredValue, maxMeasuredValue, initialMeasuredValue); // (taken from src/app/zap-templates/zcl/data-model/chip/matter-devices.xml) #define DEVICE_TYPE_BRIDGED_NODE 0x0013 // (taken from lo-devices.xml) #define DEVICE_TYPE_LO_ON_OFF_LIGHT 0x0100 #define DEVICE_TYPE_ROOT_NODE 0x0016 #define DEVICE_TYPE_BRIDGE 0x000e #define DEVICE_TYPE_TEMP_SENSOR 0x0302 // Device Version for dynamic endpoints: #define DEVICE_VERSION_DEFAULT 1 /* REVISION definitions: */ #define ZCL_DESCRIPTOR_CLUSTER_REVISION (1u) #define ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_REVISION (2u) #define ZCL_FIXED_LABEL_CLUSTER_REVISION (1u) #define ZCL_ON_OFF_CLUSTER_REVISION (4u) #define ZCL_TEMPERATURE_SENSOR_CLUSTER_REVISION (4u) #define ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_FEATURE_MAP (0u) #define ZCL_TEMPERATURE_SENSOR_FEATURE_MAP (0u) /* BRIDGED DEVICE ENDPOINT: contains the following clusters: - On/Off - Descriptor - Bridged Device Basic Information */ // Declare On/Off cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(onOffAttrs) DECLARE_DYNAMIC_ATTRIBUTE(Clusters::OnOff::Attributes::OnOff::Id, BOOLEAN, 1, 0), /* on/off */ DECLARE_DYNAMIC_ATTRIBUTE(Clusters::OnOff::Attributes::ClusterRevision::Id, INT16U, ZCL_ON_OFF_CLUSTER_REVISION, 0), DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Descriptor cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(descriptorAttrs) DECLARE_DYNAMIC_ATTRIBUTE(Clusters::Descriptor::Attributes::DeviceTypeList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* device list */ DECLARE_DYNAMIC_ATTRIBUTE(Clusters::Descriptor::Attributes::ServerList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* server list */ DECLARE_DYNAMIC_ATTRIBUTE(Clusters::Descriptor::Attributes::ClientList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* client list */ DECLARE_DYNAMIC_ATTRIBUTE(Clusters::Descriptor::Attributes::PartsList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* parts list */ DECLARE_DYNAMIC_ATTRIBUTE(Clusters::Descriptor::Attributes::ClusterRevision::Id, INT16U, ZCL_DESCRIPTOR_CLUSTER_REVISION, 0), /* cluster revision */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Bridged Device Basic Information cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(bridgedDeviceBasicAttrs) DECLARE_DYNAMIC_ATTRIBUTE(chip::app::Clusters::BridgedDeviceBasicInformation::Attributes::NodeLabel::Id, CHAR_STRING, kNodeLabelSize, 0), /* NodeLabel */ DECLARE_DYNAMIC_ATTRIBUTE(chip::app::Clusters::BridgedDeviceBasicInformation::Attributes::Reachable::Id, BOOLEAN, 1, 0), /* Reachable */ DECLARE_DYNAMIC_ATTRIBUTE(chip::app::Clusters::BridgedDeviceBasicInformation::Attributes::ClusterRevision::Id, INT16U, ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_REVISION, 0), /* cluster revision */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Cluster List for Bridged Light endpoint // TODO: It's not clear whether it would be better to get the command lists from // the ZAP config on our last fixed endpoint instead. constexpr CommandId onOffIncomingCommands[] = { app::Clusters::OnOff::Commands::Off::Id, app::Clusters::OnOff::Commands::On::Id, app::Clusters::OnOff::Commands::Toggle::Id, app::Clusters::OnOff::Commands::OffWithEffect::Id, app::Clusters::OnOff::Commands::OnWithRecallGlobalScene::Id, app::Clusters::OnOff::Commands::OnWithTimedOff::Id, kInvalidCommandId, }; DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(bridgedLightClusters) DECLARE_DYNAMIC_CLUSTER(Clusters::OnOff::Id, onOffAttrs, ZAP_CLUSTER_MASK(SERVER), onOffIncomingCommands, nullptr), DECLARE_DYNAMIC_CLUSTER(Clusters::Descriptor::Id, descriptorAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), DECLARE_DYNAMIC_CLUSTER(chip::app::Clusters::BridgedDeviceBasicInformation::Id, bridgedDeviceBasicAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr) DECLARE_DYNAMIC_CLUSTER_LIST_END; // ----------------------------Temperature sensor----------------------------------------------- DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(tempSensorAttrs) DECLARE_DYNAMIC_ATTRIBUTE(Clusters::TemperatureMeasurement::Attributes::MeasuredValue::Id, INT16S, 2, 0), /* Measured Value */ DECLARE_DYNAMIC_ATTRIBUTE(Clusters::TemperatureMeasurement::Attributes::MinMeasuredValue::Id, INT16S, 2, 0), /* Min Measured Value */ DECLARE_DYNAMIC_ATTRIBUTE(Clusters::TemperatureMeasurement::Attributes::MaxMeasuredValue::Id, INT16S, 2, 0), /* Max Measured Value */ DECLARE_DYNAMIC_ATTRIBUTE(Clusters::TemperatureMeasurement::Attributes::FeatureMap::Id, BITMAP32, 4, 0), /* FeatureMap */ DECLARE_DYNAMIC_ATTRIBUTE(Clusters::TemperatureMeasurement::Attributes::ClusterRevision::Id, INT16U, ZCL_TEMPERATURE_SENSOR_CLUSTER_REVISION, 0), /* cluster revision */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // TEMPERATURE SENSOR ENDPOINT: contains the following clusters: // - Temperature measurement // - Descriptor // - Bridged Device Basic Information DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(bridgedTempSensorClusters) DECLARE_DYNAMIC_CLUSTER(Clusters::TemperatureMeasurement::Id, tempSensorAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), DECLARE_DYNAMIC_CLUSTER(Clusters::Descriptor::Id, descriptorAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), DECLARE_DYNAMIC_CLUSTER(Clusters::BridgedDeviceBasicInformation::Id, bridgedDeviceBasicAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), DECLARE_DYNAMIC_CLUSTER_LIST_END; // Declare Bridged Light endpoint DECLARE_DYNAMIC_ENDPOINT(bridgedTempSensorEndpoint, bridgedTempSensorClusters); DataVersion gTempSensor1DataVersions[ArraySize(bridgedTempSensorClusters)]; // Declare Bridged Light endpoint DECLARE_DYNAMIC_ENDPOINT(bridgedLightEndpoint, bridgedLightClusters); DataVersion gLight1DataVersions[ArraySize(bridgedLightClusters)]; DataVersion gLight2DataVersions[ArraySize(bridgedLightClusters)]; DataVersion gLight3DataVersions[ArraySize(bridgedLightClusters)]; DataVersion gLight4DataVersions[ArraySize(bridgedLightClusters)]; // DataVersion gThermostatDataVersions[ArraySize(thermostatAttrs)]; const EmberAfDeviceType gRootDeviceTypes[] = { { DEVICE_TYPE_ROOT_NODE, DEVICE_VERSION_DEFAULT } }; const EmberAfDeviceType gAggregateNodeDeviceTypes[] = { { DEVICE_TYPE_BRIDGE, DEVICE_VERSION_DEFAULT } }; const EmberAfDeviceType gBridgedOnOffDeviceTypes[] = { { DEVICE_TYPE_LO_ON_OFF_LIGHT, DEVICE_VERSION_DEFAULT }, { DEVICE_TYPE_BRIDGED_NODE, DEVICE_VERSION_DEFAULT } }; const EmberAfDeviceType gBridgedTempSensorDeviceTypes[] = { { DEVICE_TYPE_TEMP_SENSOR, DEVICE_VERSION_DEFAULT }, { DEVICE_TYPE_BRIDGED_NODE, DEVICE_VERSION_DEFAULT } }; int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, const Span & deviceTypeList, const Span & dataVersionStorage, chip::EndpointId parentEndpointId) { uint8_t index = 0; while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) { if (NULL == gDevices[index]) { gDevices[index] = dev; CHIP_ERROR err; while (true) { dev->SetEndpointId(gCurrentEndpointId); err = emberAfSetDynamicEndpoint(index, gCurrentEndpointId, ep, dataVersionStorage, deviceTypeList, parentEndpointId); if (err == CHIP_NO_ERROR) { ChipLogProgress(DeviceLayer, "Added device %s to dynamic endpoint %d (index=%d)", dev->GetName(), gCurrentEndpointId, index); return index; } else if (err != CHIP_ERROR_ENDPOINT_EXISTS) { return -1; } // Handle wrap condition if (++gCurrentEndpointId < gFirstDynamicEndpointId) { gCurrentEndpointId = gFirstDynamicEndpointId; } } } index++; } ChipLogProgress(DeviceLayer, "Failed to add dynamic endpoint: No endpoints available!"); return -1; } CHIP_ERROR RemoveDeviceEndpoint(Device * dev) { for (uint8_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; index++) { if (gDevices[index] == dev) { // Silence complaints about unused ep when progress logging // disabled. [[maybe_unused]] EndpointId ep = emberAfClearDynamicEndpoint(index); gDevices[index] = NULL; ChipLogProgress(DeviceLayer, "Removed device %s from dynamic endpoint %d (index=%d)", dev->GetName(), ep, index); return CHIP_NO_ERROR; } } return CHIP_ERROR_INTERNAL; } Protocols::InteractionModel::Status HandleReadBridgedDeviceBasicAttribute(Device * dev, chip::AttributeId attributeId, uint8_t * buffer, uint16_t maxReadLength) { using namespace chip::app::Clusters::BridgedDeviceBasicInformation::Attributes; ChipLogProgress(DeviceLayer, "HandleReadBridgedDeviceBasicAttribute: attrId=%" PRIu32 ", maxReadLength=%u", attributeId, maxReadLength); if ((attributeId == Reachable::Id) && (maxReadLength == 1)) { *buffer = dev->IsReachable() ? 1 : 0; } else if ((attributeId == NodeLabel::Id) && (maxReadLength == 32)) { MutableByteSpan zclNameSpan(buffer, maxReadLength); MakeZclCharString(zclNameSpan, dev->GetName()); } else if ((attributeId == FeatureMap::Id) && (maxReadLength == 4)) { uint32_t featureMap = ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_FEATURE_MAP; memcpy(buffer, &featureMap, sizeof(featureMap)); } else if ((attributeId == ClusterRevision::Id) && (maxReadLength == 4)) { uint16_t clusterRevision = ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_REVISION; memcpy(buffer, &clusterRevision, sizeof(clusterRevision)); } else { return Protocols::InteractionModel::Status::Failure; } return Protocols::InteractionModel::Status::Success; } Protocols::InteractionModel::Status HandleReadOnOffAttribute(Device * dev, chip::AttributeId attributeId, uint8_t * buffer, uint16_t maxReadLength) { ChipLogProgress(DeviceLayer, "HandleReadOnOffAttribute: attrId=%" PRIu32 ", maxReadLength=%u", attributeId, maxReadLength); if ((attributeId == Clusters::OnOff::Attributes::OnOff::Id) && (maxReadLength == 1)) { *buffer = dev->IsOn() ? 1 : 0; } else if ((attributeId == Clusters::OnOff::Attributes::ClusterRevision::Id) && (maxReadLength == 4)) { uint16_t clusterRevision = ZCL_ON_OFF_CLUSTER_REVISION; memcpy(buffer, &clusterRevision, sizeof(clusterRevision)); } else { return Protocols::InteractionModel::Status::Failure; } return Protocols::InteractionModel::Status::Success; } Protocols::InteractionModel::Status HandleWriteOnOffAttribute(Device * dev, chip::AttributeId attributeId, uint8_t * buffer) { ChipLogProgress(DeviceLayer, "HandleWriteOnOffAttribute: attrId=%" PRIu32, attributeId); VerifyOrReturnError((attributeId == Clusters::OnOff::Attributes::OnOff::Id) && dev->IsReachable(), Protocols::InteractionModel::Status::Failure); dev->SetOnOff(*buffer == 1); return Protocols::InteractionModel::Status::Success; } Protocols::InteractionModel::Status emberAfExternalAttributeReadCallback(EndpointId endpoint, ClusterId clusterId, const EmberAfAttributeMetadata * attributeMetadata, uint8_t * buffer, uint16_t maxReadLength) { using namespace Clusters; uint16_t endpointIndex = emberAfGetDynamicIndexFromEndpoint(endpoint); if ((endpointIndex < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) && (gDevices[endpointIndex] != NULL)) { Device * dev = gDevices[endpointIndex]; if (clusterId == BridgedDeviceBasicInformation::Id) { return HandleReadBridgedDeviceBasicAttribute(dev, attributeMetadata->attributeId, buffer, maxReadLength); } else if (clusterId == OnOff::Id) { return HandleReadOnOffAttribute(dev, attributeMetadata->attributeId, buffer, maxReadLength); } else if (clusterId == TemperatureMeasurement::Id) { return HandleReadTempMeasurementAttribute(static_cast(dev), attributeMetadata->attributeId, buffer, maxReadLength); } } return Protocols::InteractionModel::Status::Failure; } Protocols::InteractionModel::Status emberAfExternalAttributeWriteCallback(EndpointId endpoint, ClusterId clusterId, const EmberAfAttributeMetadata * attributeMetadata, uint8_t * buffer) { uint16_t endpointIndex = emberAfGetDynamicIndexFromEndpoint(endpoint); if (endpointIndex < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) { Device * dev = gDevices[endpointIndex]; if ((dev->IsReachable()) && (clusterId == Clusters::OnOff::Id)) { return HandleWriteOnOffAttribute(dev, attributeMetadata->attributeId, buffer); } } return Protocols::InteractionModel::Status::Failure; } namespace { void CallReportingCallback(intptr_t closure) { auto path = reinterpret_cast(closure); MatterReportingAttributeChangeCallback(*path); Platform::Delete(path); } void ScheduleReportingCallback(Device * dev, ClusterId cluster, AttributeId attribute) { auto * path = Platform::New(dev->GetEndpointId(), cluster, attribute); DeviceLayer::PlatformMgr().ScheduleWork(CallReportingCallback, reinterpret_cast(path)); } } // anonymous namespace void HandleDeviceStatusChanged(Device * dev, Device::Changed_t itemChangedMask) { using namespace chip::app::Clusters; if (itemChangedMask & Device::kChanged_Reachable) { ScheduleReportingCallback(dev, BridgedDeviceBasicInformation::Id, BridgedDeviceBasicInformation::Attributes::Reachable::Id); } if (itemChangedMask & Device::kChanged_State) { ScheduleReportingCallback(dev, OnOff::Id, OnOff::Attributes::OnOff::Id); } if (itemChangedMask & Device::kChanged_Name) { ScheduleReportingCallback(dev, BridgedDeviceBasicInformation::Id, BridgedDeviceBasicInformation::Attributes::NodeLabel::Id); } } bool emberAfActionsClusterInstantActionCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Clusters::Actions::Commands::InstantAction::DecodableType & commandData) { // No actions are implemented, just return status NotFound. commandObj->AddStatus(commandPath, Protocols::InteractionModel::Status::NotFound); return true; } CHIP_ERROR AppTask::Init(void) { SetExampleButtonCallbacks(LightingActionEventHandler); InitCommonParts(); Protocols::InteractionModel::Status status; app::DataModel::Nullable level; // Read brightness value status = Clusters::LevelControl::Attributes::CurrentLevel::Get(kExampleEndpointId, level); if (status == Protocols::InteractionModel::Status::Success && !level.IsNull()) { sLevel = level.Value(); } bool isOn; // Read storedValue on/off value status = Clusters::OnOff::Attributes::OnOff::Get(1, &isOn); if (status == Protocols::InteractionModel::Status::Success) { sTurnedOn = isOn; PwmManager::getInstance().setPwm(PwmManager::EAppPwm_Red, sTurnedOn); } for (size_t i = 0; i < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; i++) { gDevices[i] = nullptr; } gLight1.SetReachable(true); gLight2.SetReachable(true); gLight3.SetReachable(true); gLight4.SetReachable(true); TempSensor1.SetReachable(true); // Whenever bridged device changes its state gLight1.SetChangeCallback(&HandleDeviceStatusChanged); gLight2.SetChangeCallback(&HandleDeviceStatusChanged); gLight3.SetChangeCallback(&HandleDeviceStatusChanged); gLight4.SetChangeCallback(&HandleDeviceStatusChanged); TempSensor1.SetChangeCallback(&HandleDeviceTempSensorStatusChanged); PlatformMgr().ScheduleWork(InitServer, reinterpret_cast(nullptr)); return CHIP_NO_ERROR; } void AppTask::InitServer(intptr_t context) { // Set starting endpoint id where dynamic endpoints will be assigned, which // will be the next consecutive endpoint id after the last fixed endpoint. gFirstDynamicEndpointId = static_cast( static_cast(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1))) + 1); gCurrentEndpointId = gFirstDynamicEndpointId; // Disable last fixed endpoint, which is used as a placeholder for all of the // supported clusters so that ZAP will generate the requisite code. emberAfEndpointEnableDisable(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1)), false); // A bridge has root node device type on EP0 and aggregate node device type (bridge) at EP1 emberAfSetDeviceTypeList(0, Span(gRootDeviceTypes)); emberAfSetDeviceTypeList(1, Span(gAggregateNodeDeviceTypes)); // Add lights 1..3 --> will be mapped to ZCL endpoints 3, 4, 5 AddDeviceEndpoint(&gLight1, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), Span(gLight1DataVersions), 1); AddDeviceEndpoint(&gLight2, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), Span(gLight2DataVersions), 1); AddDeviceEndpoint(&gLight3, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), Span(gLight3DataVersions), 1); // Remove Light 2 -- Lights 1 & 3 will remain mapped to endpoints 3 & 5 RemoveDeviceEndpoint(&gLight2); // Add Light 4 -- > will be mapped to ZCL endpoint 6 AddDeviceEndpoint(&gLight4, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), Span(gLight4DataVersions), 1); // Re-add Light 2 -- > will be mapped to ZCL endpoint 7 AddDeviceEndpoint(&gLight2, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), Span(gLight2DataVersions), 1); // Add Temperature Sensor devices --> will be mapped to endpoint 8 AddDeviceEndpoint(&TempSensor1, &bridgedTempSensorEndpoint, Span(gBridgedTempSensorDeviceTypes), Span(gTempSensor1DataVersions), 1); } void HandleDeviceTempSensorStatusChanged(DeviceTempSensor * dev, DeviceTempSensor::Changed_t itemChangedMask) { using namespace Clusters; if (itemChangedMask & (DeviceTempSensor::kChanged_Reachable | DeviceTempSensor::kChanged_Name | DeviceTempSensor::kChanged_Location)) { HandleDeviceStatusChanged(static_cast(dev), (Device::Changed_t) itemChangedMask); } if (itemChangedMask & DeviceTempSensor::kChanged_MeasurementValue) { ScheduleReportingCallback(dev, TemperatureMeasurement::Id, TemperatureMeasurement::Attributes::MeasuredValue::Id); } } Protocols::InteractionModel::Status HandleReadTempMeasurementAttribute(DeviceTempSensor * dev, chip::AttributeId attributeId, uint8_t * buffer, uint16_t maxReadLength) { using namespace Clusters::TemperatureMeasurement::Attributes; if ((attributeId == MeasuredValue::Id) && (maxReadLength == 2)) { int16_t measuredValue = dev->GetMeasuredValue(); memcpy(buffer, &measuredValue, sizeof(measuredValue)); } else if ((attributeId == MinMeasuredValue::Id) && (maxReadLength == 2)) { int16_t minValue = dev->mMin; memcpy(buffer, &minValue, sizeof(minValue)); } else if ((attributeId == MaxMeasuredValue::Id) && (maxReadLength == 2)) { int16_t maxValue = dev->mMax; memcpy(buffer, &maxValue, sizeof(maxValue)); } else if ((attributeId == FeatureMap::Id) && (maxReadLength == 4)) { uint32_t featureMap = ZCL_TEMPERATURE_SENSOR_FEATURE_MAP; memcpy(buffer, &featureMap, sizeof(featureMap)); } else if ((attributeId == ClusterRevision::Id) && (maxReadLength == 4)) { uint16_t clusterRevision = ZCL_TEMPERATURE_SENSOR_CLUSTER_REVISION; memcpy(buffer, &clusterRevision, sizeof(clusterRevision)); } else { return Protocols::InteractionModel::Status::Failure; } return Protocols::InteractionModel::Status::Success; } void AppTask::LightingActionEventHandler(AppEvent * aEvent) { Action_t action = INVALID_ACTION; int32_t actor = 0; if (aEvent->Type == AppEvent::kEventType_DeviceAction) { action = static_cast(aEvent->DeviceEvent.Action); actor = aEvent->DeviceEvent.Actor; } else if (aEvent->Type == AppEvent::kEventType_Button) { sTurnedOn = !sTurnedOn; PwmManager::getInstance().setPwm(PwmManager::EAppPwm_Red, sTurnedOn); GetAppTask().UpdateClusterState(); } } void AppTask::UpdateClusterState(void) { bool isTurnedOn = sTurnedOn; // write the new on/off value Protocols::InteractionModel::Status status = Clusters::OnOff::Attributes::OnOff::Set(kExampleEndpointId, isTurnedOn); if (status != Protocols::InteractionModel::Status::Success) { LOG_ERR("Update OnOff fail: %x", to_underlying(status)); } uint8_t setLevel = sLevel; status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kExampleEndpointId, setLevel); if (status != Protocols::InteractionModel::Status::Success) { LOG_ERR("Update CurrentLevel fail: %x", to_underlying(status)); } }