blob: 234b0139e9b24e6336aeedc151925fcab049c721 [file] [log] [blame]
/**
*
* Copyright (c) 2020 Project CHIP Authors
* Copyright (c) 2018 Silicon Laboratories Inc. www.silabs.com
*
* 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
* This file provides an implementation of the CHIP ZCL Application
* layer's level control server
*
*/
#include "level-control-server.h"
#include <stdlib.h> /* for abs() */
// User options for plugin Level Control Server
#define CHIP_AF_PLUGIN_LEVEL_CONTROL_SERVER_MAXIMUM_LEVEL 255
#define CHIP_AF_PLUGIN_LEVEL_CONTROL_SERVER_MINIMUM_LEVEL 0
#define MIN_LEVEL CHIP_AF_PLUGIN_LEVEL_CONTROL_SERVER_MINIMUM_LEVEL
#define MAX_LEVEL CHIP_AF_PLUGIN_LEVEL_CONTROL_SERVER_MAXIMUM_LEVEL
#define BOUND_MIN(value) ((value) < MIN_LEVEL ? MIN_LEVEL : (value))
#define BOUND_MAX(value) ((value) < MAX_LEVEL ? (value) : MAX_LEVEL)
#define BOUND_MIN_MAX(value) BOUND_MIN(BOUND_MAX(value))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN_DELAY_MS 1
#define TRANSITION_TIME_USE_ATTRIBUTE 0xFFFF
#define MOVE_RATE_USE_ATTRIBUTE 0xFF
#ifdef CHIP_AF_API_ZCL_SCENES_SERVER
#ifdef DEFINETOKENS
// Token based storage.
#define retrieveSceneSubTableEntry(entry, i) halCommonGetIndexedToken(&entry, TOKEN_ZCL_CORE_LEVEL_CONTROL_SCENE_SUBTABLE, i)
#define saveSceneSubTableEntry(entry, i) halCommonSetIndexedToken(TOKEN_ZCL_CORE_LEVEL_CONTROL_SCENE_SUBTABLE, i, &entry)
#else
// RAM based storage.
ChipZclLevelControlSceneSubTableEntry_t zapPluginLevelControlServerSceneSubTable[CHIP_AF_PLUGIN_SCENES_SERVER_TABLE_SIZE] = {
{ 0 }
};
#define retrieveSceneSubTableEntry(entry, i) (entry = zapPluginLevelControlServerSceneSubTable[i])
#define saveSceneSubTableEntry(entry, i) (zapPluginLevelControlServerSceneSubTable[i] = entry)
#endif
#endif
static void moveToLevelHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandMoveToLevelRequest_t * request, bool withOnOff);
static void moveHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandMoveRequest_t * request, bool withOnOff);
static void stepHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandStepRequest_t * request, bool withOnOff);
static void stopHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandStopRequest_t * request, bool withOnOff);
static bool getOnOff(ChipZclEndpointId_t endpointId);
static void setOnOff(ChipZclEndpointId_t endpointId, bool onOff);
static uint8_t getCurrentLevel(ChipZclEndpointId_t endpointId);
static uint16_t getOnOffTransitionTime(ChipZclEndpointId_t endpointId);
static uint8_t getDefaultMoveRate(ChipZclEndpointId_t endpointId);
static bool shouldExecuteIfOff(uint8_t endpoint, uint8_t optionMask, uint8_t optionOverride);
static void setCurrentLevel(ChipZclEndpointId_t endpointId, uint8_t currentLevel);
static uint8_t getOnLevelOrCurrentLevel(ChipZclEndpointId_t endpointId);
typedef struct
{
uint32_t delayMs;
ChipZclEndpointId_t endpointId;
uint8_t targetLevel;
uint8_t postTransitionLevel;
bool increasing;
bool withOnOff;
bool immediate; // If true level will be set in next tick
} State;
typedef struct
{
Event event;
State state;
} LevelControlEvent;
extern EventQueue emAppEventQueue;
static void eventHandler(LevelControlEvent * event);
static void eventMarker(LevelControlEvent * event);
static EventActions actions = { &emAppEventQueue, (void (*)(struct Event_s *)) eventHandler,
(void (*)(struct Event_s *)) eventMarker, "level control server" };
static LevelControlEvent * cancel(State * state);
static ChipZclStatus_t schedule(State * state);
void chipZclLevelControlServerSetOnOff(ChipZclEndpointId_t endpointId, bool value)
{
State state = {
.endpointId = endpointId,
.increasing = value,
.withOnOff = true,
.immediate = false,
};
if (value)
{
// Light is being turned on
state.targetLevel = getOnLevelOrCurrentLevel(endpointId);
state.postTransitionLevel = state.targetLevel;
if (!getOnOff(endpointId))
{
// Only smoothly transition from off to on if the light is off
setCurrentLevel(endpointId, MIN_LEVEL);
setOnOff(endpointId, true);
}
}
else
{
// Light is being turned off
setOnOff(endpointId, false);
state.targetLevel = MIN_LEVEL;
state.postTransitionLevel = getOnLevelOrCurrentLevel(endpointId);
}
uint8_t currentLevel = getCurrentLevel(endpointId);
if (currentLevel == state.targetLevel)
{
cancel(&state);
}
else
{
uint8_t stepSize = abs(currentLevel - state.targetLevel);
uint16_t onOffTransitionTime = getOnOffTransitionTime(endpointId);
uint32_t delayMs =
(onOffTransitionTime == 0x0 ? MIN_DELAY_MS : (onOffTransitionTime * MILLISECOND_TICKS_PER_DECISECOND / stepSize));
state.delayMs = MAX(delayMs, MIN_DELAY_MS); // Make sure we didn't round down to zero
schedule(&state);
}
}
void chipZclClusterLevelControlServerCommandMoveToLevelRequestHandler(
const ChipZclCommandContext_t * context, const ChipZclClusterLevelControlServerCommandMoveToLevelRequest_t * request)
{
moveToLevelHandler(context, request, false); // without on/off
}
void chipZclClusterLevelControlServerCommandMoveRequestHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandMoveRequest_t * request)
{
moveHandler(context, request, false); // without on/off
}
void chipZclClusterLevelControlServerCommandStepRequestHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandStepRequest_t * request)
{
stepHandler(context, request, false); // without on/off
}
void chipZclClusterLevelControlServerCommandStopRequestHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandStopRequest_t * request)
{
stopHandler(context, request, false); // without on/off
}
void chipZclClusterLevelControlServerCommandMoveToLevelWithOnOffRequestHandler(
const ChipZclCommandContext_t * context, const ChipZclClusterLevelControlServerCommandMoveToLevelWithOnOffRequest_t * request)
{
moveToLevelHandler(context, (const ChipZclClusterLevelControlServerCommandMoveToLevelRequest_t *) request,
true); // with on/off
}
void chipZclClusterLevelControlServerCommandMoveWithOnOffRequestHandler(
const ChipZclCommandContext_t * context, const ChipZclClusterLevelControlServerCommandMoveWithOnOffRequest_t * request)
{
moveHandler(context, (const ChipZclClusterLevelControlServerCommandMoveRequest_t *) request,
true); // with on/off
}
void chipZclClusterLevelControlServerCommandStepWithOnOffRequestHandler(
const ChipZclCommandContext_t * context, const ChipZclClusterLevelControlServerCommandStepWithOnOffRequest_t * request)
{
stepHandler(context, (const ChipZclClusterLevelControlServerCommandStepRequest_t *) request,
true); // with on/off
}
void chipZclClusterLevelControlServerCommandStopWithOnOffRequestHandler(
const ChipZclCommandContext_t * context, const ChipZclClusterLevelControlServerCommandStopWithOnOffRequest_t * request)
{
stopHandler(context, (const ChipZclClusterLevelControlServerCommandStopRequest_t *) request,
true); // with on/off
}
static void moveToLevelHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandMoveToLevelRequest_t * request, bool withOnOff)
{
chipZclCorePrintln("RX: MoveToLevel%s", (withOnOff ? "WithOnOff" : ""));
// Use OptionsMask and OptionsOverride when implemented
if (!withOnOff && (shouldExecuteIfOff(context->endpointId, 0, 0) != true))
{
chipZclSendDefaultResponse(context, CHIP_ZCL_STATUS_SUCCESS);
return;
}
uint8_t currentLevel = getCurrentLevel(context->endpointId);
uint8_t level = BOUND_MIN_MAX(request->level);
State state = {
.endpointId = context->endpointId,
.targetLevel = level,
.postTransitionLevel = level,
.increasing = (currentLevel <= level),
.withOnOff = withOnOff,
.immediate = false,
};
if (state.increasing && state.withOnOff)
{
setOnOff(context->endpointId, (state.targetLevel != MIN_LEVEL));
}
ChipZclStatus_t status;
if (currentLevel == state.targetLevel)
{
cancel(&state);
status = CHIP_ZCL_STATUS_SUCCESS;
}
else
{
uint8_t stepSize = abs(currentLevel - request->level);
uint16_t onOffTransitionTime = getOnOffTransitionTime(context->endpointId);
uint32_t delayMs =
((request->transitionTime == TRANSITION_TIME_USE_ATTRIBUTE)
? (onOffTransitionTime == 0x0 ? MIN_DELAY_MS : (onOffTransitionTime * MILLISECOND_TICKS_PER_DECISECOND / stepSize))
: (request->transitionTime * MILLISECOND_TICKS_PER_DECISECOND / stepSize));
// If requested transition time is zero set level in next tick
if (delayMs == 0)
{
state.immediate = true;
}
state.delayMs = MAX(delayMs, MIN_DELAY_MS); // Make sure we didn't round down to zero
status = schedule(&state);
}
chipZclSendDefaultResponse(context, status);
}
static void moveHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandMoveRequest_t * request, bool withOnOff)
{
chipZclCorePrintln("RX: Move%s", (withOnOff ? "WithOnOff" : ""));
uint8_t level;
switch (request->moveMode)
{
case MOVE_MODE_UP:
level = MAX_LEVEL;
break;
case MOVE_MODE_DOWN:
level = MIN_LEVEL;
break;
default:
chipZclSendDefaultResponse(context, CHIP_ZCL_STATUS_INVALID_FIELD);
return;
}
// Use OptionsMask and OptionsOverride when implemented
if (!withOnOff && (shouldExecuteIfOff(context->endpointId, 0, 0) != true))
{
chipZclSendDefaultResponse(context, CHIP_ZCL_STATUS_SUCCESS);
return;
}
// Moving at zero rate means not moving at all
if (request->rate == 0)
{
chipZclSendDefaultResponse(context, CHIP_ZCL_STATUS_SUCCESS);
return;
}
uint8_t currentLevel = getCurrentLevel(context->endpointId);
State state = {
.endpointId = context->endpointId,
.targetLevel = level,
.postTransitionLevel = level,
.increasing = (currentLevel <= level),
.withOnOff = withOnOff,
.immediate = false,
};
if (state.increasing && state.withOnOff)
{
setOnOff(context->endpointId, (state.targetLevel != MIN_LEVEL));
}
ChipZclStatus_t status;
if (currentLevel == state.targetLevel)
{
cancel(&state);
status = CHIP_ZCL_STATUS_SUCCESS;
}
else
{
uint8_t defaultMoveRate = getDefaultMoveRate(context->endpointId);
uint32_t delayMs = (request->rate == MOVE_RATE_USE_ATTRIBUTE
? (defaultMoveRate == 0 ? MIN_DELAY_MS : MILLISECOND_TICKS_PER_SECOND / defaultMoveRate)
: MILLISECOND_TICKS_PER_SECOND / request->rate);
state.delayMs = MAX(delayMs, MIN_DELAY_MS); // Make sure we didn't round down to zero
status = schedule(&state);
}
chipZclSendDefaultResponse(context, status);
}
static void stepHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandStepRequest_t * request, bool withOnOff)
{
chipZclCorePrintln("RX: Step%s", (withOnOff ? "WithOnOff" : ""));
uint8_t currentLevel = getCurrentLevel(context->endpointId);
uint8_t level;
switch (request->stepMode)
{
case MOVE_MODE_UP:
level = BOUND_MAX(currentLevel + request->stepSize);
break;
case MOVE_MODE_DOWN:
level = BOUND_MIN(currentLevel - request->stepSize);
break;
default:
chipZclSendDefaultResponse(context, CHIP_ZCL_STATUS_INVALID_FIELD);
return;
}
// Use OptionsMask and OptionsOverride when implemented
if (!withOnOff && (shouldExecuteIfOff(context->endpointId, 0, 0) != true))
{
chipZclSendDefaultResponse(context, CHIP_ZCL_STATUS_SUCCESS);
return;
}
State state = {
.endpointId = context->endpointId,
.targetLevel = level,
.postTransitionLevel = level,
.increasing = (currentLevel <= level),
.withOnOff = withOnOff,
.immediate = false,
};
if (state.increasing && state.withOnOff)
{
setOnOff(context->endpointId, (state.targetLevel != MIN_LEVEL));
}
ChipZclStatus_t status;
if (currentLevel == state.targetLevel)
{
cancel(&state);
status = CHIP_ZCL_STATUS_SUCCESS;
}
else
{
state.delayMs =
(request->transitionTime == 0xFFFF ? MIN_DELAY_MS
: (request->transitionTime * MILLISECOND_TICKS_PER_DECISECOND / request->stepSize));
status = schedule(&state);
}
chipZclSendDefaultResponse(context, status);
}
static void stopHandler(const ChipZclCommandContext_t * context,
const ChipZclClusterLevelControlServerCommandStopRequest_t * request, bool withOnOff)
{
chipZclCorePrintln("RX: Stop%s", (withOnOff ? "WithOnOff" : ""));
// Use OptionsMask and OptionsOverride when implemented
if (!withOnOff && (shouldExecuteIfOff(context->endpointId, 0, 0) != true))
{
chipZclSendDefaultResponse(context, CHIP_ZCL_STATUS_SUCCESS);
return;
}
State state = {
.endpointId = context->endpointId,
.immediate = false,
};
cancel(&state);
//}
chipZclSendDefaultResponse(context, CHIP_ZCL_STATUS_SUCCESS);
}
static bool getOnOff(ChipZclEndpointId_t endpointId)
{
bool onOff;
if (chipZclReadAttribute(endpointId, &chipZclClusterOnOffServerSpec, CHIP_ZCL_CLUSTER_ON_OFF_SERVER_ATTRIBUTE_ON_OFF, &onOff,
sizeof(onOff)) == CHIP_ZCL_STATUS_SUCCESS)
{
// Returns true if the light is on, false if the light is off
return onOff;
}
else
{
// If chipZclReadAttribute fails to return a success status,
// false is returned as a default to represent the off state
return false;
}
}
static void setOnOff(ChipZclEndpointId_t endpointId, bool onOff)
{
chipZclWriteAttribute(endpointId, &chipZclClusterOnOffServerSpec, CHIP_ZCL_CLUSTER_ON_OFF_SERVER_ATTRIBUTE_ON_OFF, &onOff,
sizeof(onOff));
}
static uint8_t getCurrentLevel(ChipZclEndpointId_t endpointId)
{
uint8_t currentLevel;
if (chipZclReadAttribute(endpointId, &chipZclClusterLevelControlServerSpec,
CHIP_ZCL_CLUSTER_LEVEL_CONTROL_SERVER_ATTRIBUTE_CURRENT_LEVEL, &currentLevel,
sizeof(currentLevel)) == CHIP_ZCL_STATUS_SUCCESS)
{
return currentLevel;
}
else
{
return 0;
}
}
static uint16_t getOnOffTransitionTime(ChipZclEndpointId_t endpointId)
{
uint16_t onOffTransitionTime;
if (chipZclReadAttribute(endpointId, &chipZclClusterLevelControlServerSpec,
CHIP_ZCL_CLUSTER_LEVEL_CONTROL_SERVER_ATTRIBUTE_ON_OFF_TRANSITION_TIME, &onOffTransitionTime,
sizeof(onOffTransitionTime)) == CHIP_ZCL_STATUS_SUCCESS)
{
return onOffTransitionTime;
}
else
{
return 0; // Also default value for this attr
}
}
// If attribute is not supported return 0
static uint8_t getDefaultMoveRate(ChipZclEndpointId_t endpointId)
{
uint8_t defaultMoveRate = 0;
if (chipZclReadAttribute(endpointId, &chipZclClusterLevelControlServerSpec,
CHIP_ZCL_CLUSTER_LEVEL_CONTROL_SERVER_ATTRIBUTE_DEFAULT_MOVE_RATE, &defaultMoveRate,
sizeof(defaultMoveRate)) == CHIP_ZCL_STATUS_SUCCESS)
{
return defaultMoveRate;
}
else
{
// There's no default for this attribute. Caller should treat 0 as "unsupported"
return 0x0;
}
}
// Determine whether the command should be executed based on the Options attribute and passed Mask & Override parameters
// Based on the corresponding function from Zigbee
static bool shouldExecuteIfOff(uint8_t endpoint, uint8_t optionMask, uint8_t optionOverride)
{
// From 3.10.2.2.8.1 of ZCL7 document 14-0127-20j-zcl-ch-3-general.docx:
// "Command execution SHALL NOT continue beyond the Options processing if
// all of these criteria are true:
// - The command is one of the ‘without On/Off’ commands: Move, Move to
// Level, Stop, or Step.
// - The On/Off cluster exists on the same endpoint as this cluster.
// - The OnOff attribute of the On/Off cluster, on this endpoint, is 0x00
// (FALSE).
// - The value of the ExecuteIfOff bit is 0."
uint8_t options;
if (chipZclReadAttribute(endpoint, &chipZclClusterLevelControlServerSpec,
CHIP_ZCL_CLUSTER_LEVEL_CONTROL_SERVER_ATTRIBUTE_OPTIONS, &options,
sizeof(options)) != CHIP_ZCL_STATUS_SUCCESS)
{
options = 0; // Default
}
// The device is on - hence ExecuteIfOff does not matter
if (getOnOff(endpoint))
{
return true;
}
// The OptionsMask & OptionsOverride fields SHALL both be present or both
// omitted in the command. A temporary Options bitmap SHALL be created from
// the Options attribute, using the OptionsMask & OptionsOverride fields, if
// present. Each bit of the temporary Options bitmap SHALL be determined as
// follows:
// Each bit in the Options attribute SHALL determine the corresponding bit in
// the temporary Options bitmap, unless the OptionsMask field is present and
// has the corresponding bit set to 1, in which case the corresponding bit in
// the OptionsOverride field SHALL determine the corresponding bit in the
// temporary Options bitmap.
// The resulting temporary Options bitmap SHALL then be processed as defined
// in section 3.10.2.2.3 (typo, should be 3.10.2.2.8.1).
// ---------- The following order is important in decission making -------
// -----------more readable ----------
//
if (optionMask == 0xFF && optionOverride == 0xFF)
{
// 0xFF are the default values passed to the command handler when
// the payload is not present - in that case there is use of option
// attribute to decide execution of the command
return READBITS(options, CHIP_LEVEL_CONTROL_OPTIONS_EXECUTE_IF_OFF);
}
// ---------- The above is to distinguish if the payload is present or not
if (READBITS(optionMask, CHIP_LEVEL_CONTROL_OPTIONS_EXECUTE_IF_OFF))
{
// Mask is present and set in the command payload, this indicates
// use the over ride as temporary option
return READBITS(optionOverride, CHIP_LEVEL_CONTROL_OPTIONS_EXECUTE_IF_OFF);
}
// if we are here - use the option bits
return (READBITS(options, CHIP_LEVEL_CONTROL_OPTIONS_EXECUTE_IF_OFF));
}
static void setCurrentLevel(ChipZclEndpointId_t endpointId, uint8_t currentLevel)
{
chipZclWriteAttribute(endpointId, &chipZclClusterLevelControlServerSpec,
CHIP_ZCL_CLUSTER_LEVEL_CONTROL_SERVER_ATTRIBUTE_CURRENT_LEVEL, &currentLevel, sizeof(currentLevel));
}
static uint8_t getOnLevelOrCurrentLevel(ChipZclEndpointId_t endpointId)
{
uint8_t onLevel;
if ((chipZclReadAttribute(endpointId, &chipZclClusterLevelControlServerSpec,
CHIP_ZCL_CLUSTER_LEVEL_CONTROL_SERVER_ATTRIBUTE_ON_LEVEL, &onLevel,
sizeof(onLevel)) == CHIP_ZCL_STATUS_SUCCESS) &&
onLevel != 0xFF)
{
return onLevel;
}
else
{
return getCurrentLevel(endpointId);
}
}
static void eventHandler(LevelControlEvent * event)
{
uint8_t currentLevel;
if (event->state.immediate)
{
currentLevel = event->state.targetLevel;
event->state.immediate = false;
}
else
{
currentLevel = (getCurrentLevel(event->state.endpointId) + (event->state.increasing ? +1 : -1));
}
setCurrentLevel(event->state.endpointId, currentLevel);
if (currentLevel == event->state.targetLevel)
{
if (event->state.withOnOff)
{
setOnOff(event->state.endpointId, (currentLevel != MIN_LEVEL));
}
if (currentLevel != event->state.postTransitionLevel)
{
setCurrentLevel(event->state.endpointId, event->state.postTransitionLevel);
}
}
else
{
chipZclEventSetDelayMs((Event *) event, event->state.delayMs);
}
}
static void eventMarker(LevelControlEvent * event) {}
static bool predicate(LevelControlEvent * event, ChipZclEndpointId_t * endpointId)
{
return (*endpointId == event->state.endpointId);
}
static LevelControlEvent * cancel(State * state)
{
return (LevelControlEvent *) chipZclFindEvent(actions.queue, &actions, (EventPredicate) predicate, &state->endpointId);
}
static ChipZclStatus_t schedule(State * state)
{
LevelControlEvent * event = cancel(state);
if (event == NULL)
{
ChipZclBuffer_t * buffer = chipZclBufferAlloc(sizeof(LevelControlEvent));
if (buffer == NULL)
{
return CHIP_ZCL_STATUS_FAILURE;
}
event = (LevelControlEvent *) chipZclBufferPointer(buffer);
}
event->event.actions = &actions;
event->event.next = NULL;
event->state = *state;
chipZclEventSetDelayMs((Event *) event, event->state.delayMs);
return CHIP_ZCL_STATUS_SUCCESS;
}
#ifdef CHIP_AF_API_ZCL_SCENES_SERVER
// Scenes callback handlers...
void chipZclLevelControlServerEraseSceneHandler(uint8_t tableIdx)
{
ChipZclLevelControlSceneSubTableEntry_t entry;
entry.hasCurrentLevelValue = false;
saveSceneSubTableEntry(entry, tableIdx);
}
bool chipZclLevelControlServerAddSceneHandler(ChipZclClusterId_t clusterId, uint8_t tableIdx, const uint8_t * sceneData,
uint8_t length)
{
if (clusterId == CHIP_ZCL_CLUSTER_LEVEL_CONTROL)
{
if (length < 1)
{
return false; // ext field format error (CurrentLevelValue byte must be present).
}
// Extract bytes from input data block and update scene subtable fields.
ChipZclLevelControlSceneSubTableEntry_t entry = { 0 };
uint8_t * pData = (uint8_t *) sceneData;
// We only know of one extension for the Level Control cluster and it is just
// one byte, which means we can skip some logic for this cluster.
// If other extensions are added in this cluster, more logic will be needed here.
entry.hasCurrentLevelValue = true;
entry.currentLevelValue = zapPluginScenesServerGetUint8FromBuffer(&pData);
saveSceneSubTableEntry(entry, tableIdx);
return true;
}
return false;
}
void chipZclLevelControlServerRecallSceneHandler(ChipZclEndpointId_t endpointId, uint8_t tableIdx, uint32_t transitionTime100mS)
{
// Handles the recallScene command for the level control cluster.
// Note- this handler presently just updates (writes) the relevant cluster
// attribute(s), in a production system this could be replaced by a call
// to the relevant level control command handler to actually change the
// hw state at the rate specified by the transition time.
ChipZclLevelControlSceneSubTableEntry_t entry;
retrieveSceneSubTableEntry(entry, tableIdx);
if (entry.hasCurrentLevelValue)
{
chipZclWriteAttribute(endpointId, &chipZclClusterLevelControlServerSpec,
CHIP_ZCL_CLUSTER_LEVEL_CONTROL_SERVER_ATTRIBUTE_CURRENT_LEVEL, (uint8_t *) &entry.currentLevelValue,
sizeof(entry.currentLevelValue));
}
}
void chipZclLevelControlServerStoreSceneHandler(ChipZclEndpointId_t endpointId, uint8_t tableIdx)
{
ChipZclLevelControlSceneSubTableEntry_t entry;
entry.hasCurrentLevelValue =
(chipZclReadAttribute(endpointId, &chipZclClusterLevelControlServerSpec,
CHIP_ZCL_CLUSTER_LEVEL_CONTROL_SERVER_ATTRIBUTE_CURRENT_LEVEL, (uint8_t *) &entry.currentLevelValue,
sizeof(entry.currentLevelValue)) == CHIP_ZCL_STATUS_SUCCESS);
saveSceneSubTableEntry(entry, tableIdx);
}
void chipZclLevelControlServerCopySceneHandler(uint8_t srcTableIdx, uint8_t dstTableIdx)
{
ChipZclLevelControlSceneSubTableEntry_t entry;
retrieveSceneSubTableEntry(entry, srcTableIdx);
saveSceneSubTableEntry(entry, dstTableIdx);
}
void chipZclLevelControlServerViewSceneHandler(uint8_t tableIdx, uint8_t ** ppExtFldData)
{
ChipZclLevelControlSceneSubTableEntry_t entry;
retrieveSceneSubTableEntry(entry, tableIdx);
if (entry.hasCurrentLevelValue)
{
zapPluginScenesServerPutUint16InBuffer(ppExtFldData, CHIP_ZCL_CLUSTER_LEVEL_CONTROL);
zapPluginScenesServerPutUint8InBuffer(ppExtFldData, 1); // length=1
zapPluginScenesServerPutUint8InBuffer(ppExtFldData, entry.currentLevelValue);
}
}
void chipZclLevelControlServerPrintInfoSceneHandler(uint8_t tableIdx)
{
ChipZclLevelControlSceneSubTableEntry_t entry;
retrieveSceneSubTableEntry(entry, tableIdx);
zapCorePrint(" lvl:%x", entry.currentLevelValue);
}
#endif