Skip to main content

km003c_lib/
message.rs

1use crate::adc::{AdcDataRaw, AdcDataSimple};
2use crate::adcqueue::{AdcQueueData, AdcQueueRawData, GraphSampleRate};
3use crate::auth::{self, AuthCredential, StreamingAuthResult};
4use crate::constants::*;
5use crate::error::KMError;
6use crate::offline::{LogMetadata, LogMetadataResponse};
7use crate::packet::{
8    Attribute, AttributeSet, CtrlHeader, DataHeader, LogicalPacket, PacketType, RawPacket, StreamingAuthHeader,
9};
10use crate::pd::{PdEventStream, PdStatus, PdStatusRaw};
11use crate::pd_trace::PdTrace;
12use crate::settings::Settings;
13use bytes::Bytes;
14use num_enum::FromPrimitive;
15use zerocopy::{FromBytes, IntoBytes};
16
17const PD_MONITOR_ENABLED_PARAMETER: u16 = 1;
18const PD_MONITOR_DISABLED_PARAMETER: u16 = 0;
19
20/// Represents parsed payload data from logical packets
21#[derive(Debug, Clone, PartialEq)]
22#[cfg_attr(feature = "python", derive(pyo3::IntoPyObject))]
23pub enum PayloadData {
24    Adc(AdcDataSimple),
25    AdcQueue(AdcQueueData),
26    AdcQueueRaw(AdcQueueRawData),
27    PdStatus(PdStatus),
28    PdEvents(PdEventStream),
29    PdTrace(PdTrace),
30    Settings(Settings),
31    LogMetadata(LogMetadataResponse),
32    Unknown { attribute: Attribute, data: Vec<u8> },
33}
34
35#[derive(Debug, Clone, PartialEq)]
36pub enum Packet {
37    /// Data response with parsed payload data
38    DataResponse { payloads: Vec<PayloadData> },
39    /// Request data with attribute set
40    GetData { attributes: AttributeSet },
41    /// Start AdcQueue graph mode with sample rate.
42    ///
43    /// `CtrlHeader` places the logical rate index (0=2SPS, 1=10SPS, 2=50SPS,
44    /// 3=1000SPS) in the wire header, producing byte 2 values 0, 2, 4, and 6
45    /// respectively. A header carrying an index outside that range parses as
46    /// [`Packet::Generic`] rather than being coerced into a known rate.
47    StartGraph { rate: GraphSampleRate },
48    /// Stop AdcQueue graph mode
49    StopGraph,
50    /// Accept response
51    Accept { id: u8 },
52    /// Reject response
53    Reject { id: u8 },
54    /// NotReadable response - memory address not accessible
55    NotReadable { id: u8 },
56    /// Connect command
57    Connect,
58    /// Disconnect command
59    Disconnect,
60    /// Enable PD monitor/sniffer
61    EnablePdMonitor,
62    /// Disable PD monitor/sniffer
63    DisablePdMonitor,
64    /// MemoryRead command (0x44) - read device memory with encrypted payload
65    MemoryRead {
66        /// Memory address to read from
67        address: u32,
68        /// Number of bytes to read
69        size: u32,
70    },
71    /// StreamingAuth command (0x4C) - authenticate for AdcQueue streaming
72    StreamingAuth {
73        /// Device or calibration credential to authenticate with
74        credential: AuthCredential,
75    },
76    /// StreamingAuth response (0x4C) - authentication result
77    StreamingAuthResponse(StreamingAuthResult),
78    /// Generic packet for types we haven't specifically implemented yet
79    Generic(RawPacket),
80}
81
82/// Define a `Packet` accessor that returns the first payload of one variant.
83macro_rules! payload_accessor {
84    ($(#[$doc:meta])* $name:ident -> $target:ty, $variant:ident) => {
85        $(#[$doc])*
86        pub fn $name(&self) -> Option<&$target> {
87            self.find_payload(|payload| match payload {
88                PayloadData::$variant(inner) => Some(inner),
89                _ => None,
90            })
91        }
92    };
93}
94
95impl Packet {
96    /// Return the first payload for which `select` yields a value.
97    ///
98    /// Only [`Packet::DataResponse`] carries payloads; every other variant
99    /// yields `None`.
100    pub fn find_payload<'a, T: ?Sized>(&'a self, select: impl Fn(&'a PayloadData) -> Option<&'a T>) -> Option<&'a T> {
101        match self {
102            Self::DataResponse { payloads } => payloads.iter().find_map(select),
103            _ => None,
104        }
105    }
106
107    payload_accessor!(
108        /// Get ADC data from the packet, if present.
109        get_adc -> AdcDataSimple, Adc
110    );
111    payload_accessor!(
112        /// Get AdcQueue data from the packet, if present.
113        get_adc_queue -> AdcQueueData, AdcQueue
114    );
115    payload_accessor!(
116        /// Get losslessly parsed AdcQueue data whose graph rate is unknown.
117        get_adc_queue_raw -> AdcQueueRawData, AdcQueueRaw
118    );
119    payload_accessor!(
120        /// Get PD status from the packet, if present.
121        get_pd_status -> PdStatus, PdStatus
122    );
123    payload_accessor!(
124        /// Get PD events from the packet, if present.
125        get_pd_events -> PdEventStream, PdEvents
126    );
127    payload_accessor!(
128        /// Get the internal PD state-machine trace, if present.
129        get_pd_trace -> PdTrace, PdTrace
130    );
131    payload_accessor!(
132        /// Get the read-only settings payload, if present.
133        get_settings -> Settings, Settings
134    );
135    payload_accessor!(
136        /// Get the response to a LogMetadata request, if present.
137        get_log_metadata -> LogMetadataResponse, LogMetadata
138    );
139
140    /// Check if packet has a specific payload type
141    pub fn has_payload(&self, attr: Attribute) -> bool {
142        match self {
143            Self::DataResponse { payloads } => payloads.iter().any(|p| match p {
144                PayloadData::Adc(_) => attr == Attribute::Adc,
145                PayloadData::AdcQueue(_) => attr == Attribute::AdcQueue,
146                PayloadData::AdcQueueRaw(_) => attr == Attribute::AdcQueue,
147                PayloadData::PdStatus(_) | PayloadData::PdEvents(_) => attr == Attribute::PdPacket,
148                PayloadData::PdTrace(_) => attr == Attribute::PdTrace,
149                PayloadData::Settings(_) => attr == Attribute::Settings,
150                PayloadData::LogMetadata(_) => attr == Attribute::LogMetadata,
151                PayloadData::Unknown { attribute, .. } => *attribute == attr,
152            }),
153            _ => false,
154        }
155    }
156}
157
158impl TryFrom<RawPacket> for Packet {
159    type Error = KMError;
160
161    fn try_from(raw_packet: RawPacket) -> Result<Self, Self::Error> {
162        Self::from_raw(raw_packet, None)
163    }
164}
165
166impl Packet {
167    /// Parse a raw packet using the configured AdcQueue graph rate.
168    ///
169    /// Live device users should supply the rate used with `StartGraph` because
170    /// auxiliary AdcQueue voltage fields use rate-dependent units.
171    pub fn from_raw_with_graph_rate(raw_packet: RawPacket, rate: GraphSampleRate) -> Result<Self, KMError> {
172        Self::from_raw(raw_packet, Some(rate))
173    }
174
175    fn from_raw(raw_packet: RawPacket, graph_rate: Option<GraphSampleRate>) -> Result<Self, KMError> {
176        match raw_packet {
177            RawPacket::Ctrl { header, payload } => {
178                let packet_type = PacketType::from_primitive(header.packet_type());
179                let attribute_set = AttributeSet::from_raw(header.attribute());
180
181                match packet_type {
182                    PacketType::GetData => Ok(Packet::GetData {
183                        attributes: attribute_set,
184                    }),
185                    // An unknown rate index is preserved verbatim as a generic
186                    // packet rather than coerced into one of the four rates.
187                    PacketType::StartGraph => match GraphSampleRate::try_from(attribute_set.raw()) {
188                        Ok(rate) => Ok(Packet::StartGraph { rate }),
189                        Err(_) => Ok(Packet::Generic(RawPacket::Ctrl { header, payload })),
190                    },
191                    PacketType::StopGraph => Ok(Packet::StopGraph),
192                    PacketType::Accept => Ok(Packet::Accept { id: header.id() }),
193                    PacketType::Rejected => Ok(Packet::Reject { id: header.id() }),
194                    PacketType::NotReadable => Ok(Packet::NotReadable { id: header.id() }),
195                    PacketType::Connect => Ok(Packet::Connect),
196                    PacketType::Disconnect => Ok(Packet::Disconnect),
197                    PacketType::EnablePdMonitor => Ok(Packet::EnablePdMonitor),
198                    PacketType::DisablePdMonitor => Ok(Packet::DisablePdMonitor),
199                    _ => Ok(Packet::Generic(RawPacket::Ctrl { header, payload })),
200                }
201            }
202            RawPacket::SimpleData { header, payload } => {
203                let packet_type = PacketType::from_primitive(header.packet_type());
204
205                match packet_type {
206                    PacketType::MemoryRead => {
207                        if !header.reserved_flag()
208                            && header.into_bytes()[2..4] == [0x01, 0x01]
209                            && let Some((address, size)) = auth::parse_memory_read_payload(&payload)
210                        {
211                            Ok(Packet::MemoryRead { address, size })
212                        } else {
213                            Ok(Packet::Generic(RawPacket::SimpleData { header, payload }))
214                        }
215                    }
216                    PacketType::StreamingAuth => {
217                        let auth_header = StreamingAuthHeader::from_bytes(header.into_bytes());
218                        if auth_header.attribute() == 0x0200
219                            && let Some(credential) = auth::parse_streaming_auth_request_payload(&payload)
220                        {
221                            Ok(Packet::StreamingAuth { credential })
222                        } else if let Some(result) = auth::parse_streaming_auth_response_parts(auth_header, &payload) {
223                            Ok(Packet::StreamingAuthResponse(result))
224                        } else {
225                            Ok(Packet::Generic(RawPacket::SimpleData { header, payload }))
226                        }
227                    }
228                    _ => Ok(Packet::Generic(RawPacket::SimpleData { header, payload })),
229                }
230            }
231            RawPacket::Data { logical_packets, .. } => {
232                // Parse logical packets into PayloadData
233                let mut payloads = Vec::new();
234
235                for lp in logical_packets {
236                    let payload_data = match lp.attribute {
237                        Attribute::Adc => {
238                            // Parse ADC data (44 bytes)
239                            if lp.payload.len() < ADC_DATA_SIZE {
240                                return Err(KMError::InvalidPacket(format!(
241                                    "ADC payload too small: expected {}, got {}",
242                                    ADC_DATA_SIZE,
243                                    lp.payload.len()
244                                )));
245                            }
246
247                            let adc_data_raw = AdcDataRaw::ref_from_bytes(&lp.payload[..ADC_DATA_SIZE])
248                                .map_err(|_| KMError::InvalidPacket("Failed to parse ADC data".to_string()))?;
249                            let adc_data = AdcDataSimple::from(*adc_data_raw);
250                            PayloadData::Adc(adc_data)
251                        }
252                        Attribute::AdcQueue => {
253                            // Parse AdcQueue data (multiple 20-byte samples)
254                            // Note: Extended header size field (typically 20) indicates size per sample,
255                            // not total payload size. Actual payload contains N samples.
256                            if let Some(rate) = graph_rate {
257                                PayloadData::AdcQueue(AdcQueueData::from_bytes_with_rate(lp.payload.as_ref(), rate)?)
258                            } else {
259                                PayloadData::AdcQueueRaw(AdcQueueRawData::from_bytes(lp.payload.as_ref())?)
260                            }
261                        }
262                        Attribute::PdPacket => {
263                            // Determine if this is PD status or PD events
264                            if lp.payload.len() == PD_STATUS_SIZE {
265                                // PD Status (12 bytes)
266                                let pd_status_raw = PdStatusRaw::ref_from_bytes(lp.payload.as_ref())
267                                    .map_err(|_| KMError::InvalidPacket("Failed to parse PD status".to_string()))?;
268                                PayloadData::PdStatus(PdStatus::from(*pd_status_raw))
269                            } else {
270                                // PD Event Stream
271                                let pd_events = PdEventStream::from_bytes(Bytes::from(lp.payload))?;
272                                PayloadData::PdEvents(pd_events)
273                            }
274                        }
275                        Attribute::PdTrace => PayloadData::PdTrace(PdTrace::from_bytes(&lp.payload)?),
276                        Attribute::Settings => PayloadData::Settings(Settings::from_bytes(lp.payload.as_ref())?),
277                        Attribute::LogMetadata => {
278                            if lp.payload.is_empty() {
279                                PayloadData::LogMetadata(LogMetadataResponse::Empty)
280                            } else {
281                                if !lp.payload.len().is_multiple_of(crate::offline::LOG_METADATA_SIZE) {
282                                    return Err(KMError::InvalidPacket(format!(
283                                        "LogMetadata payload length must be a multiple of {}, got {}",
284                                        crate::offline::LOG_METADATA_SIZE,
285                                        lp.payload.len()
286                                    )));
287                                }
288                                let entries = lp
289                                    .payload
290                                    .as_chunks::<{ crate::offline::LOG_METADATA_SIZE }>()
291                                    .0
292                                    .iter()
293                                    .map(|chunk| LogMetadata::from_bytes(chunk))
294                                    .collect::<Result<Vec<_>, _>>()?;
295                                PayloadData::LogMetadata(LogMetadataResponse::Available(entries))
296                            }
297                        }
298                        _ => PayloadData::Unknown {
299                            attribute: lp.attribute,
300                            data: lp.payload.to_vec(),
301                        },
302                    };
303
304                    payloads.push(payload_data);
305                }
306
307                Ok(Packet::DataResponse { payloads })
308            }
309        }
310    }
311}
312
313impl Packet {
314    /// Convert a high-level packet to a raw packet with the given transaction ID
315    pub fn to_raw_packet(self, id: u8) -> Result<RawPacket, KMError> {
316        let raw_packet = match self {
317            Packet::DataResponse { payloads } => {
318                let mut logical_packets = Vec::new();
319
320                for payload in payloads {
321                    match payload {
322                        PayloadData::Adc(adc) => {
323                            let adc_raw = AdcDataRaw::from(adc);
324                            logical_packets.push(LogicalPacket {
325                                attribute: Attribute::Adc,
326                                next: false, // Will be fixed below
327                                chunk: 0,
328                                size: ADC_DATA_SIZE as u16,
329                                payload: adc_raw.as_bytes().to_vec(),
330                            });
331                        }
332                        PayloadData::PdStatus(pd_status) => {
333                            let raw = PdStatusRaw::from(pd_status);
334
335                            logical_packets.push(LogicalPacket {
336                                attribute: Attribute::PdPacket,
337                                next: false, // Will be fixed below
338                                chunk: 0,
339                                size: PD_STATUS_SIZE as u16,
340                                payload: raw.as_bytes().to_vec(),
341                            });
342                        }
343                        PayloadData::AdcQueue(_) => {
344                            return Err(KMError::UnsupportedSerialization { packet: "AdcQueue" });
345                        }
346                        PayloadData::AdcQueueRaw(queue) => {
347                            logical_packets.push(LogicalPacket {
348                                attribute: Attribute::AdcQueue,
349                                next: false,
350                                chunk: 0,
351                                size: 20,
352                                payload: queue.to_bytes(),
353                            });
354                        }
355                        PayloadData::PdEvents(_) => {
356                            return Err(KMError::UnsupportedSerialization {
357                                packet: "PdEventStream",
358                            });
359                        }
360                        PayloadData::PdTrace(trace) => {
361                            logical_packets.push(LogicalPacket {
362                                attribute: Attribute::PdTrace,
363                                next: false,
364                                chunk: 0,
365                                size: 0,
366                                payload: trace.to_bytes()?,
367                            });
368                        }
369                        PayloadData::Settings(settings) => {
370                            logical_packets.push(LogicalPacket {
371                                attribute: Attribute::Settings,
372                                next: false,
373                                chunk: 0,
374                                size: crate::settings::SETTINGS_SIZE as u16,
375                                payload: settings.to_bytes().to_vec(),
376                            });
377                        }
378                        PayloadData::LogMetadata(response) => {
379                            let payload = match response {
380                                LogMetadataResponse::Empty => Vec::new(),
381                                LogMetadataResponse::Available(metadata) => {
382                                    metadata.iter().flat_map(LogMetadata::to_bytes).collect()
383                                }
384                            };
385                            logical_packets.push(LogicalPacket {
386                                attribute: Attribute::LogMetadata,
387                                next: false,
388                                chunk: 0,
389                                size: payload.len() as u16,
390                                payload,
391                            });
392                        }
393                        PayloadData::Unknown { attribute, data } => {
394                            logical_packets.push(LogicalPacket {
395                                attribute,
396                                next: false, // Will be fixed below
397                                chunk: 0,
398                                size: data.len() as u16,
399                                payload: data,
400                            });
401                        }
402                    }
403                }
404
405                // Set `next` flag on all packets except the last
406                if let Some((last, rest)) = logical_packets.split_last_mut() {
407                    for lp in rest {
408                        lp.next = true;
409                    }
410                    last.next = false;
411                }
412
413                // Calculate the serialized size of all extended headers and payloads.
414                let total_size: usize = logical_packets.iter().map(|lp| 4 + lp.payload.len()).sum();
415
416                // Recorded PutData responses encode a word count relative to the
417                // complete packet: packet_len / 4 - 3. The field is not the raw
418                // payload length despite its historical `obj_count_words` name.
419                let obj_count_words = if logical_packets.is_empty() {
420                    0
421                } else {
422                    ((4 + total_size) / 4).saturating_sub(3) as u16
423                };
424
425                let header = DataHeader::new()
426                    .with_packet_type(PacketType::PutData.into())
427                    .with_reserved_flag(false)
428                    .with_id(id)
429                    .with_obj_count_words(obj_count_words);
430
431                RawPacket::Data {
432                    header,
433                    logical_packets,
434                }
435            }
436            Packet::GetData { attributes } => RawPacket::Ctrl {
437                header: CtrlHeader::new()
438                    .with_packet_type(PacketType::GetData.into())
439                    .with_reserved_flag(false)
440                    .with_id(id)
441                    .with_attribute(attributes.raw()),
442                payload: Vec::new(),
443            },
444            Packet::StartGraph { rate } => RawPacket::Ctrl {
445                header: CtrlHeader::new()
446                    .with_packet_type(PacketType::StartGraph.into())
447                    .with_reserved_flag(false)
448                    .with_id(id)
449                    .with_attribute(rate as u16),
450                payload: Vec::new(),
451            },
452            Packet::StopGraph => RawPacket::Ctrl {
453                header: CtrlHeader::new()
454                    .with_packet_type(PacketType::StopGraph.into())
455                    .with_reserved_flag(false)
456                    .with_id(id)
457                    .with_attribute(0),
458                payload: Vec::new(),
459            },
460            Packet::Accept { id: accept_id } => RawPacket::Ctrl {
461                header: CtrlHeader::new()
462                    .with_packet_type(PacketType::Accept.into())
463                    .with_reserved_flag(false)
464                    .with_id(accept_id)
465                    .with_attribute(0),
466                payload: Vec::new(),
467            },
468            Packet::Reject { id: reject_id } => RawPacket::Ctrl {
469                header: CtrlHeader::new()
470                    .with_packet_type(PacketType::Rejected.into())
471                    .with_reserved_flag(false)
472                    .with_id(reject_id)
473                    .with_attribute(0),
474                payload: Vec::new(),
475            },
476            Packet::NotReadable { id: nr_id } => RawPacket::Ctrl {
477                header: CtrlHeader::new()
478                    .with_packet_type(PacketType::NotReadable.into())
479                    .with_reserved_flag(false)
480                    .with_id(nr_id)
481                    .with_attribute(0),
482                payload: Vec::new(),
483            },
484            Packet::Connect => RawPacket::Ctrl {
485                header: CtrlHeader::new()
486                    .with_packet_type(PacketType::Connect.into())
487                    .with_reserved_flag(false)
488                    .with_id(id)
489                    .with_attribute(0),
490                payload: Vec::new(),
491            },
492            Packet::Disconnect => RawPacket::Ctrl {
493                header: CtrlHeader::new()
494                    .with_packet_type(PacketType::Disconnect.into())
495                    .with_reserved_flag(false)
496                    .with_id(id)
497                    .with_attribute(0),
498                payload: Vec::new(),
499            },
500            Packet::EnablePdMonitor => RawPacket::Ctrl {
501                header: CtrlHeader::new()
502                    .with_packet_type(PacketType::EnablePdMonitor.into())
503                    .with_reserved_flag(false)
504                    .with_id(id)
505                    .with_attribute(PD_MONITOR_ENABLED_PARAMETER),
506                payload: Vec::new(),
507            },
508            Packet::DisablePdMonitor => RawPacket::Ctrl {
509                header: CtrlHeader::new()
510                    .with_packet_type(PacketType::DisablePdMonitor.into())
511                    .with_reserved_flag(false)
512                    .with_id(id)
513                    .with_attribute(PD_MONITOR_DISABLED_PARAMETER),
514                payload: Vec::new(),
515            },
516            Packet::MemoryRead { address, size } => {
517                // Build encrypted MemoryRead payload
518                let encrypted_payload = auth::build_memory_read_payload(address, size);
519                RawPacket::SimpleData {
520                    header: DataHeader::from_bytes([PacketType::MemoryRead.into(), id, 0x01, 0x01]),
521                    payload: encrypted_payload.to_vec(),
522                }
523            }
524            Packet::StreamingAuth { credential } => {
525                // Build encrypted StreamingAuth payload
526                let encrypted_payload = auth::build_streaming_auth_payload(&credential);
527                RawPacket::SimpleData {
528                    header: DataHeader::from_bytes([PacketType::StreamingAuth.into(), id, 0x00, 0x02]),
529                    payload: encrypted_payload.to_vec(),
530                }
531            }
532            Packet::StreamingAuthResponse(result) => {
533                // Response packets are typically not sent by client, but support for completeness
534                let encrypted_payload = auth::encrypt_streaming_auth_response_payload(&result.decrypted_payload);
535                let auth_header = StreamingAuthHeader::new()
536                    .with_packet_type(PacketType::StreamingAuth.into())
537                    .with_reserved_flag(false)
538                    .with_id(auth::STREAMING_AUTH_RESPONSE_ID)
539                    .with_attribute(result.attribute);
540                RawPacket::Ctrl {
541                    header: CtrlHeader::from_bytes(auth_header.into_bytes()),
542                    payload: encrypted_payload.to_vec(),
543                }
544            }
545            Packet::Generic(raw_packet) => raw_packet,
546        };
547
548        Ok(raw_packet)
549    }
550}
551
552// Python support for Packet
553#[cfg(feature = "python")]
554impl<'py> pyo3::IntoPyObject<'py> for Packet {
555    type Target = pyo3::PyAny;
556    type Output = pyo3::Bound<'py, Self::Target>;
557    type Error = pyo3::PyErr;
558
559    fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
560        use pyo3::types::{PyDict, PyDictMethods};
561
562        let dict = PyDict::new(py);
563        match self {
564            Packet::DataResponse { payloads } => {
565                let inner = PyDict::new(py);
566                inner.set_item("payloads", payloads.into_pyobject(py)?)?;
567                dict.set_item("DataResponse", inner)?;
568            }
569            Packet::GetData { attributes } => {
570                let inner = PyDict::new(py);
571                inner.set_item("attribute_mask", attributes.raw())?;
572                dict.set_item("GetData", inner)?;
573            }
574            Packet::StartGraph { rate } => {
575                let inner = PyDict::new(py);
576                inner.set_item("rate_index", rate as u16)?;
577                dict.set_item("StartGraph", inner)?;
578            }
579            Packet::StopGraph => {
580                dict.set_item("StopGraph", py.None())?;
581            }
582            Packet::Accept { id } => {
583                let inner = PyDict::new(py);
584                inner.set_item("id", id)?;
585                dict.set_item("Accept", inner)?;
586            }
587            Packet::Reject { id } => {
588                let inner = PyDict::new(py);
589                inner.set_item("id", id)?;
590                dict.set_item("Reject", inner)?;
591            }
592            Packet::NotReadable { id } => {
593                let inner = PyDict::new(py);
594                inner.set_item("id", id)?;
595                dict.set_item("NotReadable", inner)?;
596            }
597            Packet::Connect => {
598                dict.set_item("Connect", py.None())?;
599            }
600            Packet::Disconnect => {
601                dict.set_item("Disconnect", py.None())?;
602            }
603            Packet::EnablePdMonitor => {
604                dict.set_item("EnablePdMonitor", py.None())?;
605            }
606            Packet::DisablePdMonitor => {
607                dict.set_item("DisablePdMonitor", py.None())?;
608            }
609            Packet::MemoryRead { address, size } => {
610                let inner = PyDict::new(py);
611                inner.set_item("address", address)?;
612                inner.set_item("size", size)?;
613                dict.set_item("MemoryRead", inner)?;
614            }
615            Packet::StreamingAuth { credential } => {
616                let inner = PyDict::new(py);
617                inner.set_item("credential", hex::encode(credential.as_bytes()))?;
618                dict.set_item("StreamingAuth", inner)?;
619            }
620            Packet::StreamingAuthResponse(result) => {
621                let inner = PyDict::new(py);
622                inner.set_item("success", result.success)?;
623                inner.set_item("auth_level", result.auth_level)?;
624                inner.set_item("attribute", result.attribute)?;
625                dict.set_item("StreamingAuthResponse", inner)?;
626            }
627            Packet::Generic(raw_packet) => {
628                dict.set_item("Generic", raw_packet.into_pyobject(py)?)?;
629            }
630        }
631        Ok(dict.into_any())
632    }
633}