3 * Copyright (c) 2020 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 * Copyright (c) 2020 Silicon Labs
22 * Licensed under the Apache License, Version 2.0 (the "License");
23 * you may not use this file except in compliance with the License.
24 * You may obtain a copy of the License at
26 * http://www.apache.org/licenses/LICENSE-2.0
28 * Unless required by applicable law or agreed to in writing, software
29 * distributed under the License is distributed on an "AS IS" BASIS,
30 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31 * See the License for the specific language governing permissions and
32 * limitations under the License.
34 /***************************************************************************/
37 * @brief Routines for the On-Off plugin, which
38 *implements the On-Off server cluster.
39 *******************************************************************************
40 ******************************************************************************/
45 #include "gen/att-storage.h"
46 #include "gen/attribute-id.h"
47 #include "gen/attribute-type.h"
48 #include "gen/cluster-id.h"
49 #include "gen/command-id.h"
51 #include <app/reporting/reporting.h>
53 #ifdef EMBER_AF_PLUGIN_SCENES
54 #include <app/clusters/scenes/scenes.h>
55 #endif // EMBER_AF_PLUGIN_SCENES
57 #ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
58 #include "../zll-on-off-server/zll-on-off-server.h"
61 #ifdef EMBER_AF_PLUGIN_ZLL_LEVEL_CONTROL_SERVER
62 #include "../zll-level-control-server/zll-level-control-server.h"
67 #ifdef ZCL_USING_ON_OFF_CLUSTER_START_UP_ON_OFF_ATTRIBUTE
68 static bool areStartUpOnOffServerAttributesTokenized(EndpointId endpoint);
71 EmberAfStatus emberAfOnOffClusterSetValueCallback(EndpointId endpoint, uint8_t command, bool initiatedByLevelChange)
74 bool currentValue, newValue;
76 emberAfOnOffClusterPrintln("On/Off set value: %x %x", endpoint, command);
78 // read current on/off value
79 status = emberAfReadAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
80 (uint8_t *) ¤tValue, sizeof(currentValue),
82 if (status != EMBER_ZCL_STATUS_SUCCESS)
84 emberAfOnOffClusterPrintln("ERR: reading on/off %x", status);
88 // if the value is already what we want to set it to then do nothing
89 if ((!currentValue && command == ZCL_OFF_COMMAND_ID) || (currentValue && command == ZCL_ON_COMMAND_ID))
91 emberAfOnOffClusterPrintln("On/off already set to new value");
92 return EMBER_ZCL_STATUS_SUCCESS;
95 // we either got a toggle, or an on when off, or an off when on,
96 // so we need to swap the value
97 newValue = !currentValue;
98 emberAfOnOffClusterPrintln("Toggle on/off from %x to %x", currentValue, newValue);
100 // the sequence of updating on/off attribute and kick off level change effect should
101 // be depend on whether we are turning on or off. If we are turning on the light, we
102 // should update the on/off attribute before kicking off level change, if we are
103 // turning off the light, we should do the opposite, that is kick off level change
104 // before updating the on/off attribute.
107 // write the new on/off value
108 status = emberAfWriteAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
109 (uint8_t *) &newValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
110 if (status != EMBER_ZCL_STATUS_SUCCESS)
112 emberAfOnOffClusterPrintln("ERR: writing on/off %x", status);
116 #ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL
117 // If initiatedByLevelChange is false, then we assume that the level change
118 // ZCL stuff has not happened and we do it here
119 if (!initiatedByLevelChange && emberAfContainsServer(endpoint, ZCL_LEVEL_CONTROL_CLUSTER_ID))
121 emberAfOnOffClusterLevelControlEffectCallback(endpoint, newValue);
123 #endif // EMBER_AF_PLUGIN_LEVEL_CONTROL
127 #ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL
128 // If initiatedByLevelChange is false, then we assume that the level change
129 // ZCL stuff has not happened and we do it here
130 if (!initiatedByLevelChange && emberAfContainsServer(endpoint, ZCL_LEVEL_CONTROL_CLUSTER_ID))
132 emberAfOnOffClusterLevelControlEffectCallback(endpoint, newValue);
134 #endif // EMBER_AF_PLUGIN_LEVEL_CONTROL
136 // write the new on/off value
137 status = emberAfWriteAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
138 (uint8_t *) &newValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
139 if (status != EMBER_ZCL_STATUS_SUCCESS)
141 emberAfOnOffClusterPrintln("ERR: writing on/off %x", status);
146 #ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
147 if (initiatedByLevelChange)
149 emberAfPluginZllOnOffServerLevelControlZllExtensions(endpoint);
153 #ifdef EMBER_AF_PLUGIN_SCENES
154 // the scene has been changed (the value of on/off has changed) so
155 // the current scene as descibed in the attribute table is invalid,
156 // so mark it as invalid (just writes the valid/invalid attribute)
157 if (emberAfContainsServer(endpoint, ZCL_SCENES_CLUSTER_ID))
159 emberAfScenesClusterMakeInvalidCallback(endpoint);
161 #endif // EMBER_AF_PLUGIN_SCENES
163 // The returned status is based solely on the On/Off cluster. Errors in the
164 // Level Control and/or Scenes cluster are ignored.
165 return EMBER_ZCL_STATUS_SUCCESS;
168 bool emberAfOnOffClusterOffCallback(void)
170 EmberAfStatus status = emberAfOnOffClusterSetValueCallback(emberAfCurrentEndpoint(), ZCL_OFF_COMMAND_ID, false);
171 #ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
172 if (status == EMBER_ZCL_STATUS_SUCCESS)
174 emberAfPluginZllOnOffServerOffZllExtensions(emberAfCurrentCommand());
177 emberAfSendImmediateDefaultResponse(status);
181 bool emberAfOnOffClusterOnCallback(void)
183 EmberAfStatus status = emberAfOnOffClusterSetValueCallback(emberAfCurrentEndpoint(), ZCL_ON_COMMAND_ID, false);
184 #ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
185 if (status == EMBER_ZCL_STATUS_SUCCESS)
187 emberAfPluginZllOnOffServerOnZllExtensions(emberAfCurrentCommand());
190 emberAfSendImmediateDefaultResponse(status);
194 bool emberAfOnOffClusterToggleCallback(void)
196 EmberAfStatus status = emberAfOnOffClusterSetValueCallback(emberAfCurrentEndpoint(), ZCL_TOGGLE_COMMAND_ID, false);
197 #ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
198 if (status == EMBER_ZCL_STATUS_SUCCESS)
200 emberAfPluginZllOnOffServerToggleZllExtensions(emberAfCurrentCommand());
203 emberAfSendImmediateDefaultResponse(status);
207 void emberAfOnOffClusterServerInitCallback(EndpointId endpoint)
209 #ifdef ZCL_USING_ON_OFF_CLUSTER_START_UP_ON_OFF_ATTRIBUTE
210 // StartUp behavior relies on OnOff and StartUpOnOff attributes being tokenized.
211 if (areStartUpOnOffServerAttributesTokenized(endpoint))
213 // Read the StartUpOnOff attribute and set the OnOff attribute as per
214 // following from zcl 7 14-0127-20i-zcl-ch-3-general.doc.
215 // 3.8.2.2.5 StartUpOnOff Attribute
216 // The StartUpOnOff attribute SHALL define the desired startup behavior of a
217 // lamp device when it is supplied with power and this state SHALL be
218 // reflected in the OnOff attribute. The values of the StartUpOnOff
219 // attribute are listed below.
220 // Table 3 46. Values of the StartUpOnOff Attribute
221 // Value Action on power up
222 // 0x00 Set the OnOff attribute to 0 (off).
223 // 0x01 Set the OnOff attribute to 1 (on).
224 // 0x02 If the previous value of the OnOff attribute is equal to 0,
225 // set the OnOff attribute to 1.If the previous value of the OnOff
226 // attribute is equal to 1, set the OnOff attribute to 0 (toggle).
227 // 0x03-0xfe These values are reserved. No action.
228 // 0xff Set the OnOff attribute to its previous value.
230 // Initialize startUpOnOff to No action value 0xFE
231 uint8_t startUpOnOff = 0xFE;
232 EmberAfStatus status = emberAfReadAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_START_UP_ON_OFF_ATTRIBUTE_ID,
233 CLUSTER_MASK_SERVER, (uint8_t *) &startUpOnOff, sizeof(startUpOnOff), NULL);
234 if (status == EMBER_ZCL_STATUS_SUCCESS)
236 // Initialise updated value to 0
237 bool updatedOnOff = 0;
238 status = emberAfReadAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
239 (uint8_t *) &updatedOnOff, sizeof(updatedOnOff), NULL);
240 if (status == EMBER_ZCL_STATUS_SUCCESS)
242 switch (startUpOnOff)
244 case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_OFF:
245 updatedOnOff = 0; // Off
247 case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_ON:
248 updatedOnOff = 1; // On
250 case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_TOGGLE:
251 updatedOnOff = !updatedOnOff;
253 case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_PREVIOUS:
255 // All other values 0x03- 0xFE are reserved - no action.
256 // When value is 0xFF - update with last value - that is as good as
260 status = emberAfWriteAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
261 (uint8_t *) &updatedOnOff, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
266 emberAfPluginOnOffClusterServerPostInitCallback(endpoint);
269 #ifdef ZCL_USING_ON_OFF_CLUSTER_START_UP_ON_OFF_ATTRIBUTE
270 static bool areStartUpOnOffServerAttributesTokenized(EndpointId endpoint)
272 EmberAfAttributeMetadata * metadata;
274 metadata = emberAfLocateAttributeMetadata(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
275 EMBER_AF_NULL_MANUFACTURER_CODE);
276 if (!emberAfAttributeIsTokenized(metadata))
281 metadata = emberAfLocateAttributeMetadata(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_START_UP_ON_OFF_ATTRIBUTE_ID,
282 CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE);
283 if (!emberAfAttributeIsTokenized(metadata))
292 void emberAfPluginOnOffClusterServerPostInitCallback(EndpointId endpoint) {}