#include "bbnow_priv.hpp" #include #include const static char TAG[] = "bbnow_priv"; size_t BBNowPrivPacket::serialize_size() const { switch (this->packet_type) { case BBNowPrivPacketType::NONE: return -1; case BBNowPrivPacketType::ACK: return BBNowPrivPacket::ACK_PACKET_SIZE; case BBNowPrivPacketType::DISCOVERY_RESPONSE: return BBNowPrivPacket::DISCOVERY_RESPONSE_PACKET_SIZE; case BBNowPrivPacketType::EXPANDER_MESSAGE: { const auto& data = std::get( this->packet_data ); return sizeof(BBNowPrivPacketType) + sizeof(uint16_t) + data.data.size(); } case BBNowPrivPacketType::INFO_QUERY: return BBNowPrivPacket::INFO_QUERY_PACKET_SIZE; default: return -1; } return -1; } std::vector BBNowPrivPacket::serialize() const { auto buf = std::vector(); if (this->serialize_size() == static_cast(-1)) { return buf; } this->serialize_into(buf); return buf; } size_t BBNowPrivPacket::serialize_into( std::vector& buf ) const { size_t initial_size = buf.size(); size_t size = this->serialize_size(); if (size == static_cast(-1)) { return 0; } buf.reserve(initial_size + size); // Push packet type. static_assert( sizeof(BBNowPrivPacketType) == 2, "expect packet type to be 2 bytes" ); uint16_t packet_type = static_cast(this->packet_type); buf.push_back((packet_type >> 8) & 0xFF); buf.push_back((packet_type >> 0) & 0xFF); // Push sequence number. static_assert( sizeof(this->sequence) == 2, "expect sequence number to be 2 bytes" ); buf.push_back((this->sequence >> 8) & 0xFF); buf.push_back((this->sequence >> 0) & 0xFF); switch (this->packet_type) { case BBNowPrivPacketType::NONE: break; case BBNowPrivPacketType::ACK: break; case BBNowPrivPacketType::DISCOVERY_RESPONSE: { const auto& data = std::get( this->packet_data ); static_assert( sizeof(data.expantion_type) == 2, "expect expansion type to be 2 bytes" ); uint16_t expantion_type = static_cast(data.expantion_type); buf.push_back((expantion_type >> 8) & 0xFF); buf.push_back((expantion_type >> 0) & 0xFF); break; } case BBNowPrivPacketType::EXPANDER_MESSAGE: { const auto& data = std::get( this->packet_data ); buf.insert( buf.end(), data.data.begin(), data.data.end() ); break; } case BBNowPrivPacketType::INFO_QUERY: { const auto& data = std::get( this->packet_data ); static_assert( sizeof(data) == 1, "expect info query type to be 1 byte" ); buf.push_back(data.battery_percentage); break; } } assert(buf.size() - initial_size == size); return size; } std::optional BBNowPrivPacket::deserialize( std::span bytes ) { // Packet type + sequence number. static_assert( sizeof(BBNowPrivPacketType) + sizeof(uint16_t) == 4, "expect packet type and sequence number to be 4 bytes" ); if (bytes.size() < 4) { ESP_LOGW( TAG, "packet not long enough for priv packet" ); return std::nullopt; } // Read packet type. uint16_t packet_type = ((uint16_t)bytes[0] << 8) | ((uint16_t)bytes[1] << 0); BBNowPrivPacket packet; packet.packet_type = static_cast(packet_type); if (packet.packet_type == BBNowPrivPacketType::NONE) { ESP_LOGW( TAG, "BBNowPrivPacketType was NONE! discarding" ); return std::nullopt; } if (packet.packet_type > BBNowPrivPacketType::MAX) { ESP_LOGW( TAG, "BBNowPrivPacketType not in range! discarding" ); return std::nullopt; } // Read sequence number. packet.sequence = ((uint16_t)bytes[2] << 8) | ((uint16_t)bytes[3] << 0); switch (packet.packet_type) { case BBNowPrivPacketType::ACK: { if (bytes.size() < ACK_PACKET_SIZE) { ESP_LOGW( TAG, "ACK packet not long enough" ); return std::nullopt; } if (bytes.size() > ACK_PACKET_SIZE) { ESP_LOGW( TAG, "ACK packet longer than expected, " "decoding anyway" ); } packet.packet_data = BBNowPrivAckData{}; break; } case BBNowPrivPacketType::DISCOVERY_RESPONSE: { if (bytes.size() < DISCOVERY_RESPONSE_PACKET_SIZE) { ESP_LOGW( TAG, "discovery response packet not long enough" ); return std::nullopt; } if (bytes.size() > DISCOVERY_RESPONSE_PACKET_SIZE) { ESP_LOGW( TAG, "discovery response packet longer than expected, " "decoding anyway" ); } uint16_t expantion_type = ((uint16_t)bytes[4] << 8) | ((uint16_t)bytes[5] << 0); auto data = BBNowPrivDiscoveryResponseData{ .expantion_type = static_cast(expantion_type) }; if (data.expantion_type > BlkBoxExpantion::MAX) { ESP_LOGW( TAG, "BlkBoxExpantion not in range! discarding" ); return std::nullopt; } packet.packet_data = std::move(data); break; } case BBNowPrivPacketType::EXPANDER_MESSAGE: { // Everything after the 4-byte header is the message. auto data = BBNowPrivExpanderMessageData{ .data = std::vector( bytes.begin() + 4, bytes.end() ) }; packet.packet_data = std::move(data); break; } case BBNowPrivPacketType::INFO_QUERY: { constexpr size_t INFO_QUERY_PACKET_SIZE = sizeof(BBNowPrivPacketType) + sizeof(uint16_t) + sizeof(BBNowPrivInfoQueryData); if (bytes.size() < INFO_QUERY_PACKET_SIZE) { ESP_LOGW( TAG, "info query packet not long enough" ); return std::nullopt; } if (bytes.size() > INFO_QUERY_PACKET_SIZE) { ESP_LOGW( TAG, "info query packet longer than expected, " "decoding anyway" ); } BBNowPrivInfoQueryData data{ .battery_percentage = bytes[4] }; packet.packet_data = data; break; } case BBNowPrivPacketType::NONE: return std::nullopt; } return packet; } BBNowPrivPacket BBNowPrivPacket::new_ack( uint16_t sequence ) { BBNowPrivPacket packet; packet.packet_type = BBNowPrivPacketType::ACK; packet.sequence = sequence; packet.packet_data = BBNowPrivAckData{}; return packet; } BBNowPrivPacket BBNowPrivPacket::new_discovery_response( uint16_t sequence, BBNowPrivDiscoveryResponseData data ) { BBNowPrivPacket packet; packet.packet_type = BBNowPrivPacketType::DISCOVERY_RESPONSE; packet.sequence = sequence; packet.packet_data = std::move(data); return packet; } BBNowPrivPacket BBNowPrivPacket::new_expander_message( uint16_t sequence, BBNowPrivExpanderMessageData data ) { BBNowPrivPacket packet; packet.packet_type = BBNowPrivPacketType::EXPANDER_MESSAGE; packet.sequence = sequence; packet.packet_data = std::move(data); return packet; } BBNowPrivPacket BBNowPrivPacket::new_info_query( uint16_t sequence, BBNowPrivInfoQueryData data ) { BBNowPrivPacket packet; packet.packet_type = BBNowPrivPacketType::INFO_QUERY; packet.sequence = sequence; packet.packet_data = std::move(data); return packet; }