Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / app / clusters / on-off-server / on-off.cpp
1 /**
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *
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
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 /**
19  *
20  *    Copyright (c) 2020 Silicon Labs
21  *
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
25  *
26  *        http://www.apache.org/licenses/LICENSE-2.0
27  *
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.
33  */
34 /***************************************************************************/
35 /**
36  * @file
37  * @brief Routines for the On-Off plugin, which
38  *implements the On-Off server cluster.
39  *******************************************************************************
40  ******************************************************************************/
41 #include "on-off.h"
42
43 #include "af.h"
44
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"
50
51 #include <app/reporting/reporting.h>
52
53 #ifdef EMBER_AF_PLUGIN_SCENES
54 #include <app/clusters/scenes/scenes.h>
55 #endif // EMBER_AF_PLUGIN_SCENES
56
57 #ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
58 #include "../zll-on-off-server/zll-on-off-server.h"
59 #endif
60
61 #ifdef EMBER_AF_PLUGIN_ZLL_LEVEL_CONTROL_SERVER
62 #include "../zll-level-control-server/zll-level-control-server.h"
63 #endif
64
65 using namespace chip;
66
67 #ifdef ZCL_USING_ON_OFF_CLUSTER_START_UP_ON_OFF_ATTRIBUTE
68 static bool areStartUpOnOffServerAttributesTokenized(EndpointId endpoint);
69 #endif
70
71 EmberAfStatus emberAfOnOffClusterSetValueCallback(EndpointId endpoint, uint8_t command, bool initiatedByLevelChange)
72 {
73     EmberAfStatus status;
74     bool currentValue, newValue;
75
76     emberAfOnOffClusterPrintln("On/Off set value: %x %x", endpoint, command);
77
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 *) &currentValue, sizeof(currentValue),
81                                   NULL); // data type
82     if (status != EMBER_ZCL_STATUS_SUCCESS)
83     {
84         emberAfOnOffClusterPrintln("ERR: reading on/off %x", status);
85         return status;
86     }
87
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))
90     {
91         emberAfOnOffClusterPrintln("On/off already set to new value");
92         return EMBER_ZCL_STATUS_SUCCESS;
93     }
94
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);
99
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.
105     if (newValue)
106     {
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)
111         {
112             emberAfOnOffClusterPrintln("ERR: writing on/off %x", status);
113             return status;
114         }
115
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))
120         {
121             emberAfOnOffClusterLevelControlEffectCallback(endpoint, newValue);
122         }
123 #endif // EMBER_AF_PLUGIN_LEVEL_CONTROL
124     }
125     else
126     {
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))
131         {
132             emberAfOnOffClusterLevelControlEffectCallback(endpoint, newValue);
133         }
134 #endif // EMBER_AF_PLUGIN_LEVEL_CONTROL
135
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)
140         {
141             emberAfOnOffClusterPrintln("ERR: writing on/off %x", status);
142             return status;
143         }
144     }
145
146 #ifdef EMBER_AF_PLUGIN_ZLL_ON_OFF_SERVER
147     if (initiatedByLevelChange)
148     {
149         emberAfPluginZllOnOffServerLevelControlZllExtensions(endpoint);
150     }
151 #endif
152
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))
158     {
159         emberAfScenesClusterMakeInvalidCallback(endpoint);
160     }
161 #endif // EMBER_AF_PLUGIN_SCENES
162
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;
166 }
167
168 bool emberAfOnOffClusterOffCallback(void)
169 {
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)
173     {
174         emberAfPluginZllOnOffServerOffZllExtensions(emberAfCurrentCommand());
175     }
176 #endif
177     emberAfSendImmediateDefaultResponse(status);
178     return true;
179 }
180
181 bool emberAfOnOffClusterOnCallback(void)
182 {
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)
186     {
187         emberAfPluginZllOnOffServerOnZllExtensions(emberAfCurrentCommand());
188     }
189 #endif
190     emberAfSendImmediateDefaultResponse(status);
191     return true;
192 }
193
194 bool emberAfOnOffClusterToggleCallback(void)
195 {
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)
199     {
200         emberAfPluginZllOnOffServerToggleZllExtensions(emberAfCurrentCommand());
201     }
202 #endif
203     emberAfSendImmediateDefaultResponse(status);
204     return true;
205 }
206
207 void emberAfOnOffClusterServerInitCallback(EndpointId endpoint)
208 {
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))
212     {
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.
229
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)
235         {
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)
241             {
242                 switch (startUpOnOff)
243                 {
244                 case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_OFF:
245                     updatedOnOff = 0; // Off
246                     break;
247                 case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_ON:
248                     updatedOnOff = 1; // On
249                     break;
250                 case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_TOGGLE:
251                     updatedOnOff = !updatedOnOff;
252                     break;
253                 case EMBER_ZCL_START_UP_ON_OFF_VALUE_SET_TO_PREVIOUS:
254                 default:
255                     // All other values 0x03- 0xFE are reserved - no action.
256                     // When value is 0xFF - update with last value - that is as good as
257                     // no action.
258                     break;
259                 }
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);
262             }
263         }
264     }
265 #endif
266     emberAfPluginOnOffClusterServerPostInitCallback(endpoint);
267 }
268
269 #ifdef ZCL_USING_ON_OFF_CLUSTER_START_UP_ON_OFF_ATTRIBUTE
270 static bool areStartUpOnOffServerAttributesTokenized(EndpointId endpoint)
271 {
272     EmberAfAttributeMetadata * metadata;
273
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))
277     {
278         return false;
279     }
280
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))
284     {
285         return false;
286     }
287
288     return true;
289 }
290 #endif
291
292 void emberAfPluginOnOffClusterServerPostInitCallback(EndpointId endpoint) {}