Skip to content

Commit ab50297

Browse files
committed
GH-43: Add callbacks support for supported events UIC-3234: Add endpoint node information on event callback
Bug-SiliconLabs: UIC-3234 Bug-GitHub: #43 Signed-off-by: Philippe Coval <philippe.coval@silabs.com>
1 parent 535dcaf commit ab50297

File tree

3 files changed

+397
-23
lines changed

3 files changed

+397
-23
lines changed

applications/zpc/components/zwave_command_classes/src/zwave_command_class_notification.cpp

Lines changed: 116 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,62 @@ using namespace attribute_store;
5353

5454
#include "zwave_command_class_notification_types.inc"
5555

56+
57+
58+
///////////////////////////////////////////////////////////////////////////////
59+
// Callbacks
60+
///////////////////////////////////////////////////////////////////////////////
61+
namespace
62+
{
63+
// For modification of a value/state (reported/desired) for a given node type
64+
std::map<attribute_store_node_t,
65+
std::set<notification_event_callback_t>>
66+
event_callbacks;
67+
} // namespace
68+
69+
void zwave_command_class_notification_register_event_callback(
70+
attribute_store_node_t endpoint_node, notification_event_callback_t callback_function)
71+
{
72+
event_callbacks[endpoint_node].insert(callback_function);
73+
}
74+
///////////////////////////////////////////////////////////////////////////////
75+
// Helpers
76+
///////////////////////////////////////////////////////////////////////////////
77+
/**
78+
* @brief Check if a given event is supported for a given notification type.
79+
*
80+
* @param notification_type_node The notification type node.
81+
* @param event The event to check.
82+
* @return true if the event is supported, false otherwise.
83+
*/
84+
bool is_event_supported(attribute_store_node_t notification_type_node,
85+
uint8_t event)
86+
{
87+
uint8_t supported_notification_states[MAX_SUPPORTED_NOTIFICATION_STATES];
88+
uint8_t number_of_supported_states;
89+
attribute_store_node_t supported_states_node
90+
= attribute_store_get_first_child_by_type(
91+
notification_type_node,
92+
ATTRIBUTE(SUPPORTED_STATES_OR_EVENTS));
93+
attribute_store_get_node_attribute_value(supported_states_node,
94+
REPORTED_ATTRIBUTE,
95+
supported_notification_states,
96+
&number_of_supported_states);
97+
if (!number_of_supported_states) {
98+
sl_log_warning(LOG_TAG, "Couldn't verify if event %d is supported.", event);
99+
return false;
100+
}
101+
102+
// Find event in event supported list
103+
uint8_t *begin = supported_notification_states;
104+
uint8_t *end = supported_notification_states + number_of_supported_states;
105+
106+
return std::find(begin, end, event) != end;
107+
}
108+
109+
///////////////////////////////////////////////////////////////////////////////
110+
// Internal
111+
///////////////////////////////////////////////////////////////////////////////
56112
static sl_status_t zwave_command_class_notification_update_state_event(
57113
const attribute &notification_type_node,
58114
uint8_t state,
@@ -113,16 +169,36 @@ static sl_status_t zwave_command_class_notification_update_state_event(
113169

114170
static sl_status_t zwave_command_class_notification_report_cmd_handler(
115171
const zwave_controller_connection_info_t *connection_info,
116-
const uint8_t *frame_data,
172+
const uint8_t *frame,
117173
uint16_t frame_length)
118174
{
119175
if (frame_length < 3) {
120176
return SL_STATUS_OK;
121177
}
122178

123-
const auto *frame
124-
= reinterpret_cast<const ZW_NOTIFICATION_REPORT_1BYTE_V3_FRAME *>(
125-
frame_data);
179+
const uint8_t v1_alarm_type = frame[2];
180+
const uint8_t v1_alarm_level = frame[3];
181+
// NOTE : frame[4] is reserved and not yet used
182+
// uint8_t notification_status = 0; // NOT USED
183+
uint8_t notification_type = 0;
184+
uint8_t event = 0;
185+
uint8_t event_parameters_length = 0;
186+
187+
const uint8_t INDEX_FIRST_PARAMETER = 9;
188+
189+
// Check if we have all the information needed (e.g. we are not using v1 or v2 notification)
190+
const bool notification_frame_complete = (frame_length >= 8);
191+
192+
// Guard in case we received a V1 or V2 frame
193+
if (notification_frame_complete) {
194+
// notification_status = frame[5];
195+
notification_type = frame[6];
196+
event = frame[7];
197+
event_parameters_length
198+
= frame[8]
199+
& NOTIFICATION_REPORT_PROPERTIES1_EVENT_PARAMETERS_LENGTH_MASK_V8;
200+
}
201+
126202

127203
// Get the unid of the sending node
128204
unid_t sending_node_unid;
@@ -140,7 +216,6 @@ static sl_status_t zwave_command_class_notification_report_cmd_handler(
140216
// ATTRIBUTE_COMMAND_CLASS_NOTIFICATION_V1_ALARM_TYPE should be added under
141217
// endpoint attribute and the level shall be under TYPE attribute:
142218
// EP --> ..V1_ALARAM_TYPE-->..V1_ALARM_LEVEL
143-
const uint8_t v1_alarm_type = frame->v1AlarmType;
144219
attribute v1_alarm_type_node;
145220
if (v1_alarm_type != 0) {
146221
v1_alarm_type_node
@@ -152,17 +227,16 @@ static sl_status_t zwave_command_class_notification_report_cmd_handler(
152227
0);
153228
if (v1_alarm_type_node.is_valid()) {
154229
v1_alarm_type_node.child_by_type(ATTRIBUTE(V1_ALARM_LEVEL))
155-
.set_reported(frame->v1AlarmLevel);
230+
.set_reported(v1_alarm_level);
156231
} else {
157232
v1_alarm_type_node = ep_node.add_node(ATTRIBUTE(V1_ALARM_TYPE));
158233
v1_alarm_type_node.set_reported(v1_alarm_type);
159234
attribute vl_alarm_level_node
160235
= v1_alarm_type_node.add_node(ATTRIBUTE(V1_ALARM_LEVEL));
161-
vl_alarm_level_node.set_reported(frame->v1AlarmLevel);
236+
vl_alarm_level_node.set_reported(v1_alarm_level);
162237
}
163238
}
164239

165-
const uint8_t notification_type = frame->notificationType;
166240
attribute notification_type_node
167241
= attribute_store_get_node_child_by_value(ep_node,
168242
ATTRIBUTE(TYPE),
@@ -175,21 +249,16 @@ static sl_status_t zwave_command_class_notification_report_cmd_handler(
175249
return SL_STATUS_OK;
176250
}
177251

178-
uint8_t event = frame->mevent;
179252
// If event is 0 it we find the event to use for state lookup in the event
180253
// parameters, as due to spec the "IDLE" shall have the event to set to idle
181254
// as paramter.
182-
if (((event == 0)
183-
&& (frame->properties1
184-
& NOTIFICATION_REPORT_PROPERTIES1_EVENT_PARAMETERS_LENGTH_MASK_V8)
185-
== 0)
186-
|| (event == 0xFE)) {
255+
if (((event == 0) && (event_parameters_length == 0)) || (event == 0xFE)) {
187256
sl_log_debug(LOG_TAG,
188257
"Notification Idle Event without any event parameters or "
189258
"Event 0xFE received. Setting all states to idle.");
190259
std::set<unsigned int> updated_states;
191260
for (auto const &elem:
192-
notification_event_state_map.at(frame->notificationType)) {
261+
notification_event_state_map.at(notification_type)) {
193262
try {
194263
if (updated_states.find(elem.second) != updated_states.end()) {
195264
// State is already set to idle
@@ -199,7 +268,7 @@ static sl_status_t zwave_command_class_notification_report_cmd_handler(
199268
zwave_command_class_notification_update_state_event(
200269
notification_type_node,
201270
elem.second,
202-
frame,
271+
reinterpret_cast<const ZW_NOTIFICATION_REPORT_1BYTE_V3_FRAME *>(frame),
203272
frame_length);
204273
} catch (const std::exception &ex) {
205274
// continue with next state in case of erorrs
@@ -215,23 +284,47 @@ static sl_status_t zwave_command_class_notification_report_cmd_handler(
215284
}
216285

217286
if (event == 0) {
218-
event = frame->eventParameter1;
287+
event = frame[INDEX_FIRST_PARAMETER];
219288
}
220289

290+
// Old way (deprecated) : check the map and update corresponding state
291+
// This will not work with "events" since only state are mapped.
221292
uint8_t state;
222293
try {
223294
// Lookup if the event is a state change
224295
state = static_cast<uint8_t>(
225-
notification_event_state_map.at(frame->notificationType).at(event));
296+
notification_event_state_map.at(notification_type).at(event));
226297
} catch (const std::exception &e) {
227298
// If no state is looked up it is an event, write to LAST_EVENT attribute
228299
state = NOTIFICATION_STATE_LAST_EVENT;
229300
}
230-
zwave_command_class_notification_update_state_event(notification_type_node,
231-
state,
232-
frame,
233-
frame_length);
234301

302+
zwave_command_class_notification_update_state_event(
303+
notification_type_node,
304+
state,
305+
reinterpret_cast<const ZW_NOTIFICATION_REPORT_1BYTE_V3_FRAME *>(frame),
306+
frame_length);
307+
308+
// Call events callbacks
309+
if (notification_frame_complete
310+
&& event_callbacks.find(ep_node) != event_callbacks.end()) {
311+
// Check if the event is supported for the notification type
312+
if (!is_event_supported(notification_type_node, event)) {
313+
sl_log_error(LOG_TAG,
314+
"Event/State %d is not supported for notification type %d",
315+
event,
316+
notification_type);
317+
return SL_STATUS_NOT_SUPPORTED;
318+
}
319+
for (auto &callback: event_callbacks.at(ep_node)) {
320+
sl_log_debug(LOG_TAG, "Sending event %d to callback for endpoint_node %d", event, ep_node);
321+
callback(ep_node,
322+
notification_type,
323+
event,
324+
&frame[INDEX_FIRST_PARAMETER],
325+
event_parameters_length);
326+
}
327+
}
235328
} catch (const std::exception &ex) {
236329
// do nothing
237330
sl_log_warning(LOG_TAG, "%s", ex.what());
@@ -504,6 +597,7 @@ static sl_status_t
504597
if (((byte >> bit_idx) & 1) == 1) {
505598
const uint8_t event = byte_idx * 8 + bit_idx;
506599
supported_events.push_back(event);
600+
507601
try {
508602
// Lookup if the event is a state change
509603
uint8_t state

applications/zpc/components/zwave_command_classes/src/zwave_command_class_notification.h

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ extern "C" {
4141
#include "zwave_rx.h"
4242
#include "attribute_store.h"
4343

44-
4544
// Following defines are created using the notification.py
4645
#define NOTIFICATION_SMOKE_ALARM (0x1)
4746
#define NOTIFICATION_CO_ALARM (0x2)
@@ -180,6 +179,43 @@ extern "C" {
180179
(0xd)
181180
#define NOTIFICATION_STATE_HOME_MONITORING_HOME_OCCUPANCY_STATUS (0x0)
182181

182+
/**
183+
* @brief Typedef for the notification event callback function.
184+
*
185+
* This typedef defines the function signature for the notification event callback.
186+
* The callback function is called when a notification event occurs.
187+
*
188+
* @param endpoint_node The endpoint node that received the notification event.
189+
* @param notification_type Notification type.
190+
* @param event_code Event code associated with the notification type.
191+
* @param event_parameters Raw event parameters data.
192+
* @param event_parameters_length The length of the event parameters data.
193+
*/
194+
typedef void (*notification_event_callback_t)(attribute_store_node_t endpoint_node,
195+
uint8_t notification_type,
196+
uint8_t event_code,
197+
const uint8_t *event_parameters,
198+
uint8_t event_parameters_length);
199+
200+
/**
201+
* @brief Register a callback function to be called when a notification event is received
202+
*
203+
* Any command class might call this function to be notified when a notification event is received.
204+
* The callback function will be called with the notification type, event code and the event parameters.
205+
* The event parameters are the raw data received in the notification event.
206+
*
207+
* The callback will only be called if the event/state is supported for given notification type.
208+
*
209+
* @note This callback also send the raw state received by a Notification Report.
210+
* @note The events are not store in the attribute store since there is no point to make them persistent.
211+
*
212+
*
213+
* @param endpoint_node The endpoint node to register the callback
214+
* @param callback The callback function to be called when a notification event is received
215+
*/
216+
void zwave_command_class_notification_register_event_callback(
217+
attribute_store_node_t endpoint_node, notification_event_callback_t callback);
218+
183219
/**
184220
* @brief Intitialize the Notification command class control APIs
185221
*

0 commit comments

Comments
 (0)