blob: d70f9c078e3027c771896372abd39f7b87dee881 [file] [log] [blame]
/*
*
* Copyright (c) 2023 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 <app-common/zap-generated/attributes/Accessors.h>
#include <app/reporting/reporting.h>
#include <app/util/endpoint-config-api.h>
#include <lib/support/ZclString.h>
LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL);
namespace {
const struct pwm_dt_spec sPwmRgbSpecBlueLed = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0));
} // namespace
AppTask AppTask::sAppTask;
#include <app/InteractionModelEngine.h>
int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, const Span<const EmberAfDeviceType> & deviceTypeList,
const Span<DataVersion> & 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<const EmberAfDeviceType> & deviceTypeList,
const Span<DataVersion> & 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);
ReturnErrorCodeIf((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<DeviceTempSensor *>(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<app::ConcreteAttributePath *>(closure);
MatterReportingAttributeChangeCallback(*path);
Platform::Delete(path);
}
void ScheduleReportingCallback(Device * dev, ClusterId cluster, AttributeId attribute)
{
auto * path = Platform::New<app::ConcreteAttributePath>(dev->GetEndpointId(), cluster, attribute);
DeviceLayer::PlatformMgr().ScheduleWork(CallReportingCallback, reinterpret_cast<intptr_t>(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)
{
// Init lighting manager
uint8_t minLightLevel = kDefaultMinLevel;
Clusters::LevelControl::Attributes::MinLevel::Get(kExampleEndpointId, &minLightLevel);
uint8_t maxLightLevel = kDefaultMaxLevel;
Clusters::LevelControl::Attributes::MaxLevel::Get(kExampleEndpointId, &maxLightLevel);
// Initialize PWM LED
CHIP_ERROR err = sAppTask.mPwmRgbBlueLed.Init(&sPwmRgbSpecBlueLed, minLightLevel, maxLightLevel, maxLightLevel);
if (err != CHIP_NO_ERROR)
{
LOG_ERR("Blue RGB PWM Device Init fail");
return err;
}
sAppTask.mPwmRgbBlueLed.SetCallbacks(ActionInitiated, ActionCompleted, nullptr);
#if APP_USE_EXAMPLE_START_BUTTON
SetExampleButtonCallbacks(LightingActionEventHandler);
#endif
InitCommonParts();
memset(gDevices, 0, sizeof(gDevices));
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<intptr_t>(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<chip::EndpointId>(
static_cast<int>(emberAfEndpointFromIndex(static_cast<uint16_t>(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<uint16_t>(emberAfFixedEndpointCount() - 1)), false);
// A bridge has root node device type on EP0 and aggregate node device type (bridge) at EP1
emberAfSetDeviceTypeList(0, Span<const EmberAfDeviceType>(gRootDeviceTypes));
emberAfSetDeviceTypeList(1, Span<const EmberAfDeviceType>(gAggregateNodeDeviceTypes));
// Add lights 1..3 --> will be mapped to ZCL endpoints 3, 4, 5
AddDeviceEndpoint(&gLight1, &bridgedLightEndpoint, Span<const EmberAfDeviceType>(gBridgedOnOffDeviceTypes),
Span<DataVersion>(gLight1DataVersions), 1);
AddDeviceEndpoint(&gLight2, &bridgedLightEndpoint, Span<const EmberAfDeviceType>(gBridgedOnOffDeviceTypes),
Span<DataVersion>(gLight2DataVersions), 1);
AddDeviceEndpoint(&gLight3, &bridgedLightEndpoint, Span<const EmberAfDeviceType>(gBridgedOnOffDeviceTypes),
Span<DataVersion>(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<const EmberAfDeviceType>(gBridgedOnOffDeviceTypes),
Span<DataVersion>(gLight4DataVersions), 1);
// Re-add Light 2 -- > will be mapped to ZCL endpoint 7
AddDeviceEndpoint(&gLight2, &bridgedLightEndpoint, Span<const EmberAfDeviceType>(gBridgedOnOffDeviceTypes),
Span<DataVersion>(gLight2DataVersions), 1);
// Add Temperature Sensor devices --> will be mapped to endpoint 8
AddDeviceEndpoint(&TempSensor1, &bridgedTempSensorEndpoint, Span<const EmberAfDeviceType>(gBridgedTempSensorDeviceTypes),
Span<DataVersion>(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<Device *>(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)
{
PWMDevice::Action_t action = PWMDevice::INVALID_ACTION;
int32_t actor = 0;
if (aEvent->Type == AppEvent::kEventType_Lighting)
{
action = static_cast<PWMDevice::Action_t>(aEvent->LightingEvent.Action);
actor = aEvent->LightingEvent.Actor;
}
else if (aEvent->Type == AppEvent::kEventType_Button)
{
action = sAppTask.mPwmRgbBlueLed.IsTurnedOn() ? PWMDevice::OFF_ACTION : PWMDevice::ON_ACTION;
actor = AppEvent::kEventType_Button;
}
if (action != PWMDevice::INVALID_ACTION && (!sAppTask.mPwmRgbBlueLed.InitiateAction(action, actor, NULL)))
{
LOG_INF("Action is in progress or active");
}
}
void AppTask::ActionInitiated(PWMDevice::Action_t aAction, int32_t aActor)
{
if (aAction == PWMDevice::ON_ACTION)
{
LOG_DBG("ON_ACTION initiated");
}
else if (aAction == PWMDevice::OFF_ACTION)
{
LOG_DBG("OFF_ACTION initiated");
}
else if (aAction == PWMDevice::LEVEL_ACTION)
{
LOG_DBG("LEVEL_ACTION initiated");
}
}
void AppTask::ActionCompleted(PWMDevice::Action_t aAction, int32_t aActor)
{
if (aAction == PWMDevice::ON_ACTION)
{
LOG_DBG("ON_ACTION completed");
}
else if (aAction == PWMDevice::OFF_ACTION)
{
LOG_DBG("OFF_ACTION completed");
}
else if (aAction == PWMDevice::LEVEL_ACTION)
{
LOG_DBG("LEVEL_ACTION completed");
}
if (aActor == AppEvent::kEventType_Button)
{
sAppTask.UpdateClusterState();
}
}
void AppTask::UpdateClusterState(void)
{
bool isTurnedOn = sAppTask.mPwmRgbBlueLed.IsTurnedOn();
// 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 = sAppTask.mPwmRgbBlueLed.GetLevel();
status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kExampleEndpointId, setLevel);
if (status != Protocols::InteractionModel::Status::Success)
{
LOG_ERR("Update CurrentLevel fail: %x", to_underlying(status));
}
}