@@ -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+ // /////////////////////////////////////////////////////////////////////////////
56112static sl_status_t zwave_command_class_notification_update_state_event (
57113 const attribute ¬ification_type_node,
58114 uint8_t state,
@@ -113,16 +169,36 @@ static sl_status_t zwave_command_class_notification_update_state_event(
113169
114170static 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
0 commit comments