blob: a21a046f9cf782d9e7272ef069ce37040c342848 [file] [log] [blame]
/**
*
* Copyright (c) 2020 Project CHIP Authors
*
* 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.
*/
/**
*
* Copyright (c) 2020 Silicon Labs
*
* 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
* @brief Routines for the On-Off plugin, which implements the On-Off server
* cluster.
*******************************************************************************
******************************************************************************/
#include "af.h"
#ifdef EMBER_AF_PLUGIN_REPORTING
#include "app/framework/plugin/reporting/reporting.h"
#endif
#ifdef EMBER_AF_PLUGIN_SCENES
#include "../scenes/scenes.h"
#endif //EMBER_AF_PLUGIN_SCENES
#ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
#include "../zll-on-off-server/zll-on-off-server.h"
#endif
#ifdef EMBER_AF_PLUGIN_ZLL_LEVEL_CONTROL_SERVER
#include "../zll-level-control-server/zll-level-control-server.h"
#endif
#ifdef ZCL_USING_ON_OFF_CLUSTER_START_UP_ON_OFF_ATTRIBUTE
static bool areStartUpOnOffServerAttributesTokenized(uint8_t endpoint);
#endif
EmberAfStatus emberAfOnOffClusterSetValueCallback(uint8_t endpoint,
uint8_t command,
bool initiatedByLevelChange)
{
EmberAfStatus status;
bool currentValue, newValue;
emberAfOnOffClusterPrintln("On/Off set value: %x %x", endpoint, command);
// read current on/off value
status = emberAfReadAttribute(endpoint,
ZCL_ON_OFF_CLUSTER_ID,
ZCL_ON_OFF_ATTRIBUTE_ID,
CLUSTER_MASK_SERVER,
(uint8_t *)&currentValue,
sizeof(currentValue),
NULL); // data type
if (status != EMBER_ZCL_STATUS_SUCCESS) {
emberAfOnOffClusterPrintln("ERR: reading on/off %x", status);
return status;
}
// if the value is already what we want to set it to then do nothing
if ((!currentValue && command == ZCL_OFF_COMMAND_ID)
|| (currentValue && command == ZCL_ON_COMMAND_ID)) {
emberAfOnOffClusterPrintln("On/off already set to new value");
return EMBER_ZCL_STATUS_SUCCESS;
}
// we either got a toggle, or an on when off, or an off when on,
// so we need to swap the value
newValue = !currentValue;
emberAfOnOffClusterPrintln("Toggle on/off from %x to %x", currentValue, newValue);
// the sequence of updating on/off attribute and kick off level change effect should
// be depend on whether we are turning on or off. If we are turning on the light, we
// should update the on/off attribute before kicking off level change, if we are
// turning off the light, we should do the opposite, that is kick off level change
// before updating the on/off attribute.
if (newValue) {
// write the new on/off value
status = emberAfWriteAttribute(endpoint,
ZCL_ON_OFF_CLUSTER_ID,
ZCL_ON_OFF_ATTRIBUTE_ID,
CLUSTER_MASK_SERVER,
(uint8_t *)&newValue,
ZCL_BOOLEAN_ATTRIBUTE_TYPE);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
emberAfOnOffClusterPrintln("ERR: writing on/off %x", status);
return status;
}
// If initiatedByLevelChange is false, then we assume that the level change
// ZCL stuff has not happened and we do it here
if (!initiatedByLevelChange
&& emberAfContainsServer(endpoint, ZCL_LEVEL_CONTROL_CLUSTER_ID)) {
emberAfOnOffClusterLevelControlEffectCallback(endpoint,
newValue);
}
} else {
// If initiatedByLevelChange is false, then we assume that the level change
// ZCL stuff has not happened and we do it here
if (!initiatedByLevelChange
&& emberAfContainsServer(endpoint, ZCL_LEVEL_CONTROL_CLUSTER_ID)) {
emberAfOnOffClusterLevelControlEffectCallback(endpoint,
newValue);
}
// write the new on/off value
status = emberAfWriteAttribute(endpoint,
ZCL_ON_OFF_CLUSTER_ID,
ZCL_ON_OFF_ATTRIBUTE_ID,
CLUSTER_MASK_SERVER,
(uint8_t *)&newValue,
ZCL_BOOLEAN_ATTRIBUTE_TYPE);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
emberAfOnOffClusterPrintln("ERR: writing on/off %x", status);
return status;
}
}
#ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
if (initiatedByLevelChange) {
emberAfPluginZllOnOffServerLevelControlZllExtensions(endpoint);
}
#endif
// the scene has been changed (the value of on/off has changed) so
// the current scene as descibed in the attribute table is invalid,
// so mark it as invalid (just writes the valid/invalid attribute)
if (emberAfContainsServer(endpoint, ZCL_SCENES_CLUSTER_ID)) {
emberAfScenesClusterMakeInvalidCallback(endpoint);
}
// The returned status is based solely on the On/Off cluster. Errors in the
// Level Control and/or Scenes cluster are ignored.
return EMBER_ZCL_STATUS_SUCCESS;
}
bool emberAfOnOffClusterOffCallback(void)
{
EmberAfStatus status = emberAfOnOffClusterSetValueCallback(emberAfCurrentEndpoint(),
ZCL_OFF_COMMAND_ID,
false);
#ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
if (status == EMBER_ZCL_STATUS_SUCCESS) {
emberAfPluginZllOnOffServerOffZllExtensions(emberAfCurrentCommand());
}
#endif
emberAfSendImmediateDefaultResponse(status);
return true;
}
bool emberAfOnOffClusterOnCallback(void)
{
EmberAfStatus status = emberAfOnOffClusterSetValueCallback(emberAfCurrentEndpoint(),
ZCL_ON_COMMAND_ID,
false);
#ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
if (status == EMBER_ZCL_STATUS_SUCCESS) {
emberAfPluginZllOnOffServerOnZllExtensions(emberAfCurrentCommand());
}
#endif
emberAfSendImmediateDefaultResponse(status);
return true;
}
bool emberAfOnOffClusterToggleCallback(void)
{
EmberAfStatus status = emberAfOnOffClusterSetValueCallback(emberAfCurrentEndpoint(),
ZCL_TOGGLE_COMMAND_ID,
false);
#ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
if (status == EMBER_ZCL_STATUS_SUCCESS) {
emberAfPluginZllOnOffServerToggleZllExtensions(emberAfCurrentCommand());
}
#endif
emberAfSendImmediateDefaultResponse(status);
return true;
}
void emberAfOnOffClusterServerInitCallback(uint8_t endpoint)
{
#ifdef ZCL_USING_ON_OFF_CLUSTER_START_UP_ON_OFF_ATTRIBUTE
// StartUp behavior relies on OnOff and StartUpOnOff attributes being tokenized.
if (areStartUpOnOffServerAttributesTokenized(endpoint)) {
// Read the StartUpOnOff attribute and set the OnOff attribute as per
// following from zcl 7 14-0127-20i-zcl-ch-3-general.doc.
// 3.8.2.2.5 StartUpOnOff Attribute
// The StartUpOnOff attribute SHALL define the desired startup behavior of a
// lamp device when it is supplied with power and this state SHALL be
// reflected in the OnOff attribute. The values of the StartUpOnOff
// attribute are listed below.
// Table 3 46. Values of the StartUpOnOff Attribute
// Value Action on power up
// 0x00 Set the OnOff attribute to 0 (off).
// 0x01 Set the OnOff attribute to 1 (on).
// 0x02 If the previous value of the OnOff attribute is equal to 0,
// set the OnOff attribute to 1.If the previous value of the OnOff
// attribute is equal to 1, set the OnOff attribute to 0 (toggle).
// 0x03-0xfe These values are reserved. No action.
// 0xff Set the OnOff attribute to its previous value.
// Initialize startUpOnOff to No action value 0xFE
uint8_t startUpOnOff = 0xFE;
EmberAfStatus status = emberAfReadAttribute(endpoint,
ZCL_ON_OFF_CLUSTER_ID,
ZCL_START_UP_ON_OFF_ATTRIBUTE_ID,
CLUSTER_MASK_SERVER,
(uint8_t *)&startUpOnOff,
sizeof(startUpOnOff),
NULL);
if (status == EMBER_ZCL_STATUS_SUCCESS) {
// Initialise updated value to 0
bool updatedOnOff = 0;
status = emberAfReadAttribute(endpoint,
ZCL_ON_OFF_CLUSTER_ID,
ZCL_ON_OFF_ATTRIBUTE_ID,
CLUSTER_MASK_SERVER,
(uint8_t *)&updatedOnOff,
sizeof(updatedOnOff),
NULL);
if (status == EMBER_ZCL_STATUS_SUCCESS) {
switch (startUpOnOff) {
case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_OFF:
updatedOnOff = 0; // Off
break;
case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_ON:
updatedOnOff = 1; //On
break;
case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_TOGGLE:
updatedOnOff = !updatedOnOff;
break;
case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_PREVIOUS:
default:
// All other values 0x03- 0xFE are reserved - no action.
// When value is 0xFF - update with last value - that is as good as
// no action.
break;
}
status = emberAfWriteAttribute(endpoint,
ZCL_ON_OFF_CLUSTER_ID,
ZCL_ON_OFF_ATTRIBUTE_ID,
CLUSTER_MASK_SERVER,
(uint8_t *)&updatedOnOff,
ZCL_BOOLEAN_ATTRIBUTE_TYPE);
}
}
}
#endif
emberAfPluginOnOffClusterServerPostInitCallback(endpoint);
}
#ifdef ZCL_USING_ON_OFF_CLUSTER_START_UP_ON_OFF_ATTRIBUTE
static bool areStartUpOnOffServerAttributesTokenized(uint8_t endpoint)
{
EmberAfAttributeMetadata *metadata;
metadata = emberAfLocateAttributeMetadata(endpoint,
ZCL_ON_OFF_CLUSTER_ID,
ZCL_ON_OFF_ATTRIBUTE_ID,
CLUSTER_MASK_SERVER,
EMBER_AF_NULL_MANUFACTURER_CODE);
if (!emberAfAttributeIsTokenized(metadata)) {
return false;
}
metadata = emberAfLocateAttributeMetadata(endpoint,
ZCL_ON_OFF_CLUSTER_ID,
ZCL_START_UP_ON_OFF_ATTRIBUTE_ID,
CLUSTER_MASK_SERVER,
EMBER_AF_NULL_MANUFACTURER_CODE);
if (!emberAfAttributeIsTokenized(metadata)) {
return false;
}
return true;
}
#endif