/******************************************************************************************************** * @file zcl_levelCb.c * * @brief This is the source file for zcl_levelCb * * @author Zigbee Group * @date 2021 * * @par Copyright (c) 2021, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK") * * 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. *******************************************************************************************************/ #if (__PROJECT_TL_DIMMABLE_LIGHT__) /********************************************************************** * INCLUDES */ #include "tl_common.h" #include "zb_api.h" #include "zcl_include.h" #include "sampleLight.h" #include "sampleLightCtrl.h" #ifdef ZCL_LEVEL_CTRL /********************************************************************** * TYPEDEFS */ typedef struct { s32 stepLevel256; u16 currentLevel256; u8 withOnOff; } zcl_levelInfo_t; /********************************************************************** * LOCAL VARIABLES */ static zcl_levelInfo_t levelInfo = { .stepLevel256 = 0, .currentLevel256 = 0, .withOnOff = 0, }; static ev_timer_event_t *levelTimerEvt = NULL; /********************************************************************* * @fn sampleLight_levelTimerEvtCb * * @brief timer event to process the level command * * @param arg * * @return 0: timer continue on; -1: timer will be canceled */ static s32 sampleLight_levelTimerEvtCb(void *arg) { zcl_levelAttr_t *pLevel = zcl_levelAttrGet(); if (pLevel->remainingTime) { light_applyUpdate(&pLevel->curLevel, &levelInfo.currentLevel256, &levelInfo.stepLevel256, &pLevel->remainingTime, ZCL_LEVEL_ATTR_MIN_LEVEL, ZCL_LEVEL_ATTR_MAX_LEVEL, FALSE); } if (levelInfo.withOnOff) { if (pLevel->curLevel == ZCL_LEVEL_ATTR_MIN_LEVEL) { sampleLight_onoff(ZCL_CMD_ONOFF_OFF); } } if (pLevel->remainingTime) { return 0; } else { levelTimerEvt = NULL; return -1; } } /********************************************************************* * @fn sampleLight_LevelTimerStop * * @brief force to stop the level timer * * @param * * @return */ static void sampleLight_LevelTimerStop(void) { if (levelTimerEvt) { TL_ZB_TIMER_CANCEL(&levelTimerEvt); } } /********************************************************************* * @fn sampleLight_moveToLevelProcess * * @brief * * @param cmdId * @param cmd * * @return None */ static void sampleLight_moveToLevelProcess(u8 cmdId, moveToLvl_t *cmd) { zcl_levelAttr_t *pLevel = zcl_levelAttrGet(); pLevel->remainingTime = ((cmd->transitionTime == 0) || (cmd->transitionTime == 0xFFFF)) ? 1 : INTERP_STEPS_FROM_ONE_TENTH(cmd->transitionTime, ZCL_LEVEL_CHANGE_INTERVAL); levelInfo.withOnOff = (cmdId == ZCL_CMD_LEVEL_MOVE_TO_LEVEL_WITH_ON_OFF) ? TRUE : FALSE; levelInfo.currentLevel256 = (u16)(pLevel->curLevel) << 8; levelInfo.stepLevel256 = ((s32)(cmd->level - pLevel->curLevel)) << 8; levelInfo.stepLevel256 /= (s32)pLevel->remainingTime; light_applyUpdate(&pLevel->curLevel, &levelInfo.currentLevel256, &levelInfo.stepLevel256, &pLevel->remainingTime, ZCL_LEVEL_ATTR_MIN_LEVEL, ZCL_LEVEL_ATTR_MAX_LEVEL, FALSE); if (levelInfo.withOnOff) { if (levelInfo.stepLevel256 > 0) { sampleLight_onoff(ZCL_CMD_ONOFF_ON); } else if (pLevel->curLevel == ZCL_LEVEL_ATTR_MIN_LEVEL) { sampleLight_onoff(ZCL_CMD_ONOFF_OFF); } } sampleLight_LevelTimerStop(); if (pLevel->remainingTime) { levelTimerEvt = TL_ZB_TIMER_SCHEDULE(sampleLight_levelTimerEvtCb, NULL, ZCL_LEVEL_CHANGE_INTERVAL); } } /********************************************************************* * @fn sampleLight_moveProcess * * @brief * * @param cmdId * @param cmd * * @return None */ static void sampleLight_moveProcess(u8 cmdId, move_t *cmd) { zcl_levelAttr_t *pLevel = zcl_levelAttrGet(); levelInfo.withOnOff = (cmdId == ZCL_CMD_LEVEL_MOVE_WITH_ON_OFF) ? TRUE : FALSE; levelInfo.currentLevel256 = (u16)(pLevel->curLevel) << 8; u32 rate = (u32)cmd->rate * 100; u8 newLevel; u8 deltaLevel; if (cmd->moveMode == LEVEL_MOVE_UP) { newLevel = ZCL_LEVEL_ATTR_MAX_LEVEL; deltaLevel = ZCL_LEVEL_ATTR_MAX_LEVEL - pLevel->curLevel; } else { newLevel = ZCL_LEVEL_ATTR_MIN_LEVEL; deltaLevel = pLevel->curLevel - ZCL_LEVEL_ATTR_MIN_LEVEL; } pLevel->remainingTime = ((u32)deltaLevel * 1000) / rate; if (pLevel->remainingTime == 0) { pLevel->remainingTime = 1; } levelInfo.stepLevel256 = ((s32)(newLevel - pLevel->curLevel)) << 8; levelInfo.stepLevel256 /= (s32)pLevel->remainingTime; if (cmd->moveMode == LEVEL_MOVE_UP) { if (levelInfo.withOnOff) { sampleLight_onoff(ZCL_CMD_ONOFF_ON); } } light_applyUpdate(&pLevel->curLevel, &levelInfo.currentLevel256, &levelInfo.stepLevel256, &pLevel->remainingTime, ZCL_LEVEL_ATTR_MIN_LEVEL, ZCL_LEVEL_ATTR_MAX_LEVEL, FALSE); if (levelInfo.withOnOff) { if (pLevel->curLevel == ZCL_LEVEL_ATTR_MIN_LEVEL) { sampleLight_onoff(ZCL_CMD_ONOFF_OFF); } } sampleLight_LevelTimerStop(); if (pLevel->remainingTime) { levelTimerEvt = TL_ZB_TIMER_SCHEDULE(sampleLight_levelTimerEvtCb, NULL, ZCL_LEVEL_CHANGE_INTERVAL); } } /********************************************************************* * @fn sampleLight_stepProcess * * @brief * * @param cmdId * @param cmd * * @return None */ static void sampleLight_stepProcess(u8 cmdId, step_t *cmd) { zcl_levelAttr_t *pLevel = zcl_levelAttrGet(); pLevel->remainingTime = ((cmd->transitionTime == 0) || (cmd->transitionTime == 0xFFFF)) ? 1 : INTERP_STEPS_FROM_ONE_TENTH(cmd->transitionTime, ZCL_LEVEL_CHANGE_INTERVAL); levelInfo.withOnOff = (cmdId == ZCL_CMD_LEVEL_STEP_WITH_ON_OFF) ? TRUE : FALSE; levelInfo.currentLevel256 = (u16)(pLevel->curLevel) << 8; levelInfo.stepLevel256 = (((s32)cmd->stepSize) << 8) / pLevel->remainingTime; if (cmd->stepMode == LEVEL_STEP_UP) { if (levelInfo.withOnOff) { sampleLight_onoff(ZCL_CMD_ONOFF_ON); } } else { levelInfo.stepLevel256 = -levelInfo.stepLevel256; } light_applyUpdate(&pLevel->curLevel, &levelInfo.currentLevel256, &levelInfo.stepLevel256, &pLevel->remainingTime, ZCL_LEVEL_ATTR_MIN_LEVEL, ZCL_LEVEL_ATTR_MAX_LEVEL, FALSE); if (levelInfo.withOnOff) { if (pLevel->curLevel == ZCL_LEVEL_ATTR_MIN_LEVEL) { sampleLight_onoff(ZCL_CMD_ONOFF_OFF); } } sampleLight_LevelTimerStop(); if (pLevel->remainingTime) { levelTimerEvt = TL_ZB_TIMER_SCHEDULE(sampleLight_levelTimerEvtCb, NULL, ZCL_LEVEL_CHANGE_INTERVAL); } } /********************************************************************* * @fn sampleLight_stopProcess * * @brief * * @param cmdId * @param cmd * * @return None */ static void sampleLight_stopProcess(u8 cmdId, stop_t *cmd) { zcl_levelAttr_t *pLevel = zcl_levelAttrGet(); sampleLight_LevelTimerStop(); pLevel->remainingTime = 0; levelInfo.currentLevel256 = ((s32)pLevel->curLevel) << 8; } /********************************************************************* * @fn sampleLight_levelCb * * @brief Handler for ZCL LEVEL command. This function will set LEVEL attribute first. * * @param pAddrInfo * @param cmd - level cluster command id * @param cmdPayload * * @return status_t */ status_t sampleLight_levelCb(zclIncomingAddrInfo_t *pAddrInfo, u8 cmdId, void *cmdPayload) { if (pAddrInfo->dstEp == SAMPLE_LIGHT_ENDPOINT) { switch (cmdId) { case ZCL_CMD_LEVEL_MOVE_TO_LEVEL: case ZCL_CMD_LEVEL_MOVE_TO_LEVEL_WITH_ON_OFF: sampleLight_moveToLevelProcess(cmdId, (moveToLvl_t *)cmdPayload); break; case ZCL_CMD_LEVEL_MOVE: case ZCL_CMD_LEVEL_MOVE_WITH_ON_OFF: sampleLight_moveProcess(cmdId, (move_t *)cmdPayload); break; case ZCL_CMD_LEVEL_STEP: case ZCL_CMD_LEVEL_STEP_WITH_ON_OFF: sampleLight_stepProcess(cmdId, (step_t *)cmdPayload); break; case ZCL_CMD_LEVEL_STOP: case ZCL_CMD_LEVEL_STOP_WITH_ON_OFF: sampleLight_stopProcess(cmdId, (stop_t *)cmdPayload); break; default: break; } } return ZCL_STA_SUCCESS; } #endif /* ZCL_LEVEL_CTRL */ #endif /* __PROJECT_TL_DIMMABLE_LIGHT__ */