1+ /* *****************************************************************************
2+ * # License
3+ * <b>Copyright 2024 Silicon Laboratories Inc. www.silabs.com</b>
4+ ******************************************************************************
5+ * The licensor of this software is Silicon Laboratories Inc. Your use of this
6+ * software is governed by the terms of Silicon Labs Master Software License
7+ * Agreement (MSLA) available at
8+ * www.silabs.com/about-us/legal/master-software-license-agreement. This
9+ * software is distributed to you in Source Code format and is governed by the
10+ * sections of the MSLA applicable to Source Code.
11+ *
12+ *****************************************************************************/
13+
14+ /* *
15+ * @defgroup zwave_frame_parser C++ Z-Wave Frame Handler Helper
16+ * @brief C++ definitions for handling Z-Wave Frames
17+ *
18+ * This group is used to handle Z-Wave frame and link their contents with the attribute store.
19+ *
20+ * @{
21+ */
22+
23+ #ifndef ZWAVE_FRAME_GENERATOR_HPP
24+ #define ZWAVE_FRAME_GENERATOR_HPP
25+
26+ #ifdef __cplusplus
27+
28+ // Unify includes
29+ #include " attribute_store.h"
30+ #include " sl_status.h"
31+
32+ // Cpp includes
33+ #include < vector>
34+
35+ /* *
36+ * @class zwave_frame_generator
37+ * @brief Generate frames for Z-Wave commands based on attribute store values
38+ *
39+ * Mainly used to generate Set or Get frame to send to Z-Wave devices
40+ *
41+ * You can either set raw bytes, or let the function get the value from the attribute store for
42+ * you. You are able to specify if you want a Desired or Reported value.
43+ *
44+ * @code{.cpp}
45+ * // Only needed to be instantiated once
46+ * static zwave_frame_generator frame_generator(COMMAND_CLASS_SWITCH_BINARY);
47+ *
48+ * // On a frame callback :
49+ * static sl_status_t zwave_command_class_set(
50+ * attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) {
51+ *
52+ * // We don't use frame_length here since we need to set it manually
53+ * constexpr uint8_t expected_frame_size = 3;
54+ * try {
55+ * frame_generator.initialize_frame(SWITCH_BINARY_SET, frame, expected_frame_size);
56+ * frame_generator.add_raw_byte(0x01);
57+ * frame_generator.add_value(my_binary_node, REPORTED_ATTRIBUTE);
58+ *
59+ * // Will take the DESIRED_ATTRIBUTE value from my_node and shift it 2 bits to the left,
60+ * // then add 0b1 shifted 7 bits to the left.
61+ * // Result value (if desired value is 0b11) : 0b10001100
62+ * std::vector<zwave_frame_generator::shifted_value> values_mix = {
63+ * {.left_shift = 2,
64+ * .node = my_node,
65+ * .node_value_state = DESIRED_ATTRIBUTE},
66+ * {.left_shift = 7, .raw_value = 0b1},
67+ * };
68+ * frame_generator.add_shifted_values(values_mix);
69+ *
70+ * frame_generator.validate_frame(frame_length);
71+ *
72+ * } catch (const std::exception &e) {
73+ * sl_log_error(LOG_TAG, "Error while generating frame : %s", e.what());
74+ * return SL_STATUS_FAIL;
75+ * }
76+ *
77+ * return SL_STATUS_OK;
78+ * }
79+ *
80+ * @endcode
81+ */
82+ class zwave_frame_generator
83+ {
84+ public:
85+ /* *
86+ * @brief Represent a value that needs to be shifted before being added to the frame.
87+ *
88+ * You can either specify the node and value state to get the value from the attribute store
89+ * or provide a raw value to be shifted.
90+ *
91+ * @note If you provide a node, it will read a uint8_t value from it.
92+ */
93+ struct shifted_value {
94+ /* *
95+ * @brief The number of bits to shift the value (left)
96+ */
97+ uint8_t left_shift = 0 ;
98+ /* *
99+ * @brief Node to get the value from (uint8_t). If ATTRIBUTE_STORE_INVALID_NODE, use raw_value
100+ */
101+ attribute_store_node_t node = ATTRIBUTE_STORE_INVALID_NODE;
102+ /* *
103+ * @brief State of the value to get from the node. Only used if node is not ATTRIBUTE_STORE_INVALID_NODE
104+ */
105+ attribute_store_node_value_state_t node_value_state = REPORTED_ATTRIBUTE;
106+ /* *
107+ * @brief Raw value to shift. Only used if node is ATTRIBUTE_STORE_INVALID_NODE
108+ */
109+ uint8_t raw_value = 0 ;
110+ };
111+
112+ /* *
113+ * @brief Constructor
114+ *
115+ * @param zwave_command_class The Z-Wave command class to use in the header of all generated commands
116+ */
117+ explicit zwave_frame_generator (uint8_t zwave_command_class);
118+ ~zwave_frame_generator () = default ;
119+
120+ /* *
121+ * @brief Initialize a new Z-Wave frame on the given data section.
122+ *
123+ * @note This will reset the internal counter to 0 and update the data section provided with other functions.
124+ *
125+ * After calling this function your frame will look like :
126+ * 0: zwave_command_class (from constructor)
127+ * 1: zwave_command_id (from this function)
128+ *
129+ * @param zwave_command_id The Z-Wave command ID to use in the header of the frame
130+ * @param raw_data The data section of the frame (must be allowed an valid from this address to data_size bytes after)
131+ * @param data_size The size of the data section (we use uint16_t to match Unify API)
132+ */
133+ void initialize_frame (uint8_t zwave_command_id,
134+ uint8_t *raw_data,
135+ uint16_t data_size);
136+
137+ /* *
138+ * @brief Add a raw byte to the Z-Wave frame
139+ *
140+ * @param byte The byte to add to the frame
141+ */
142+ void add_raw_byte (uint8_t byte);
143+
144+ /* *
145+ * @brief Add the value contained in the given node to the Z-Wave frame
146+ *
147+ * @throws std::runtime_error if the node is invalid or if the value can't be read
148+ *
149+ * @note The size of the value is automatically determined by the attribute store.
150+ * Numerical values will be stored in big-endian (MSB first LSB last).
151+ * Other formats will keep their original order.
152+ *
153+ * @param node The node to get the value from
154+ * @param node_value_state The state of the value to get from the node
155+ *
156+ */
157+ void add_value (attribute_store_node_t node,
158+ attribute_store_node_value_state_t node_value_state
159+ = REPORTED_ATTRIBUTE);
160+ /* *
161+ * @brief Add a shifted value to the Z-Wave frame
162+ *
163+ * You can either specify a raw value to be shifted, or directly pass the attribute
164+ * store node.
165+ *
166+ * @see shifted_value
167+ *
168+ * @param shifted_values The shifted value to add to the frame
169+ */
170+ void add_shifted_values (const std::vector<shifted_value> &shifted_values);
171+ /* *
172+ * @brief Add a shifted value to the Z-Wave frame (single value version)
173+ *
174+ * Convenience function to add a single shifted value to the frame.
175+ *
176+ * @see shifted_value
177+ *
178+ * @param shifted_values The shifted value to add to the frame
179+ */
180+ void add_shifted_values (const shifted_value &shifted_values);
181+
182+ /* *
183+ * @brief Validate the frame length and throw an error if it is not the expected length
184+ *
185+ * @note We don't use bool here since the all the function throw an error if anything goes wrong
186+ *
187+ * @param frame_length Will set the frame length if current frame length is equal to expected length
188+ */
189+ void validate_frame (uint16_t *frame_length) const ;
190+
191+ /* *
192+ * @brief Generate a Z-Wave frame with no arguments
193+ *
194+ * This function is used to generate a Z-Wave frame with no arguments like a simple Get command.
195+ * Since it is used for convenience, this method doesn't throw an exception and return a status instead.
196+ *
197+ * @param zwave_command_id The Z-Wave command ID to use in the header of the frame
198+ * @param raw_data The data section of the frame (must be able to write 2 byte to this address)
199+ * @param frame_length Frame length pointer (set to 2)
200+ *
201+ * @return SL_STATUS_OK if the frame was generated successfully, SL_STATUS_FAIL otherwise
202+ */
203+ sl_status_t generate_no_args_frame (uint8_t zwave_command_id,
204+ uint8_t *raw_data,
205+ uint16_t *frame_length);
206+
207+ private:
208+ /* *
209+ * @brief Helper function to get the raw data from the attribute store
210+ *
211+ * @note Number will be returned in little-endian (LSB first MSB last)
212+ *
213+ * @param node The node to get the value from
214+ * @param node_value_state The state of the value to get from the node
215+ *
216+ * @return The raw data from the attribute store
217+ */
218+ std::vector<uint8_t > helper_get_raw_data (
219+ attribute_store_node_t node,
220+ attribute_store_node_value_state_t node_value_state) const ;
221+
222+ // Current Z-Wave command class used in the header of all generated commands
223+ const uint8_t current_command_class;
224+ // Vector wrapper around raw frame data. See initialize_frame()
225+ uint8_t *current_zwave_frame;
226+ // Current Z-Wave frame size
227+ uint16_t current_zwave_frame_size = 0 ;
228+ // Current Z-Wave frame index (we use uint16_t to match Unify API)
229+ uint16_t current_zwave_frame_index = 0 ;
230+ };
231+
232+ #endif // __cplusplus
233+ #endif // ZWAVE_FRAME_GENERATOR_HPP
0 commit comments