mirror of
https://github.com/esphome/esphome.git
synced 2024-11-26 17:05:21 +01:00
Extend nfc ndef records with Text (#2191)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
parent
0d0954d74b
commit
4c390d9f9f
8 changed files with 264 additions and 123 deletions
|
@ -10,16 +10,13 @@ NdefMessage::NdefMessage(std::vector<uint8_t> &data) {
|
|||
uint8_t index = 0;
|
||||
while (index <= data.size()) {
|
||||
uint8_t tnf_byte = data[index++];
|
||||
bool me = tnf_byte & 0x40;
|
||||
bool sr = tnf_byte & 0x10;
|
||||
bool il = tnf_byte & 0x08;
|
||||
uint8_t tnf = tnf_byte & 0x07;
|
||||
bool me = tnf_byte & 0x40; // Message End bit (is set if this is the last record of the message)
|
||||
bool sr = tnf_byte & 0x10; // Short record bit (is set if payload size is less or equal to 255 bytes)
|
||||
bool il = tnf_byte & 0x08; // ID length bit (is set if ID Length field exists)
|
||||
uint8_t tnf = tnf_byte & 0x07; // Type Name Format
|
||||
|
||||
ESP_LOGVV(TAG, "me=%s, sr=%s, il=%s, tnf=%d", YESNO(me), YESNO(sr), YESNO(il), tnf);
|
||||
|
||||
auto record = make_unique<NdefRecord>();
|
||||
record->set_tnf(tnf);
|
||||
|
||||
uint8_t type_length = data[index++];
|
||||
uint32_t payload_length = 0;
|
||||
if (sr) {
|
||||
|
@ -38,28 +35,34 @@ NdefMessage::NdefMessage(std::vector<uint8_t> &data) {
|
|||
ESP_LOGVV(TAG, "Lengths: type=%d, payload=%d, id=%d", type_length, payload_length, id_length);
|
||||
|
||||
std::string type_str(data.begin() + index, data.begin() + index + type_length);
|
||||
record->set_type(type_str);
|
||||
|
||||
index += type_length;
|
||||
|
||||
std::string id_str = "";
|
||||
if (il) {
|
||||
std::string id_str(data.begin() + index, data.begin() + index + id_length);
|
||||
record->set_id(id_str);
|
||||
id_str = std::string(data.begin() + index, data.begin() + index + id_length);
|
||||
index += id_length;
|
||||
}
|
||||
|
||||
uint8_t payload_identifier = 0x00;
|
||||
if (type_str == "U") {
|
||||
payload_identifier = data[index++];
|
||||
payload_length -= 1;
|
||||
std::vector<uint8_t> payload_data(data.begin() + index, data.begin() + index + payload_length);
|
||||
|
||||
std::unique_ptr<NdefRecord> record;
|
||||
|
||||
// Based on tnf and type, create a more specific NdefRecord object
|
||||
// constructed from the payload data
|
||||
if (tnf == TNF_WELL_KNOWN && type_str == "U") {
|
||||
record = make_unique<NdefRecordUri>(payload_data);
|
||||
} else if (tnf == TNF_WELL_KNOWN && type_str == "T") {
|
||||
record = make_unique<NdefRecordText>(payload_data);
|
||||
} else {
|
||||
// Could not recognize the record, so store as generic one.
|
||||
record = make_unique<NdefRecord>(payload_data);
|
||||
record->set_tnf(tnf);
|
||||
record->set_type(type_str);
|
||||
}
|
||||
|
||||
std::string payload_str(data.begin() + index, data.begin() + index + payload_length);
|
||||
record->set_id(id_str);
|
||||
|
||||
if (payload_identifier > 0x00 && payload_identifier <= PAYLOAD_IDENTIFIERS_COUNT) {
|
||||
payload_str.insert(0, PAYLOAD_IDENTIFIERS[payload_identifier]);
|
||||
}
|
||||
|
||||
record->set_payload(payload_str);
|
||||
index += payload_length;
|
||||
|
||||
ESP_LOGV(TAG, "Adding record type %s = %s", record->get_type().c_str(), record->get_payload().c_str());
|
||||
|
@ -82,13 +85,10 @@ bool NdefMessage::add_record(std::unique_ptr<NdefRecord> record) {
|
|||
bool NdefMessage::add_text_record(const std::string &text) { return this->add_text_record(text, "en"); };
|
||||
|
||||
bool NdefMessage::add_text_record(const std::string &text, const std::string &encoding) {
|
||||
std::string payload = to_string(text.length()) + encoding + text;
|
||||
return this->add_record(make_unique<NdefRecord>(TNF_WELL_KNOWN, "T", payload));
|
||||
return this->add_record(make_unique<NdefRecordText>(encoding, text));
|
||||
}
|
||||
|
||||
bool NdefMessage::add_uri_record(const std::string &uri) {
|
||||
return this->add_record(make_unique<NdefRecord>(TNF_WELL_KNOWN, "U", uri));
|
||||
}
|
||||
bool NdefMessage::add_uri_record(const std::string &uri) { return this->add_record(make_unique<NdefRecordUri>(uri)); }
|
||||
|
||||
std::vector<uint8_t> NdefMessage::encode() {
|
||||
std::vector<uint8_t> data;
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "ndef_record.h"
|
||||
#include "ndef_record_text.h"
|
||||
#include "ndef_record_uri.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nfc {
|
||||
|
@ -18,7 +20,7 @@ class NdefMessage {
|
|||
NdefMessage(const NdefMessage &msg) {
|
||||
records_.reserve(msg.records_.size());
|
||||
for (const auto &r : msg.records_) {
|
||||
records_.emplace_back(make_unique<NdefRecord>(*r));
|
||||
records_.emplace_back(r->clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,40 +5,22 @@ namespace nfc {
|
|||
|
||||
static const char *const TAG = "nfc.ndef_record";
|
||||
|
||||
uint32_t NdefRecord::get_encoded_size() {
|
||||
uint32_t size = 2;
|
||||
if (this->payload_.length() > 255) {
|
||||
size += 4;
|
||||
} else {
|
||||
size += 1;
|
||||
}
|
||||
if (this->id_.length()) {
|
||||
size += 1;
|
||||
}
|
||||
size += (this->type_.length() + this->payload_.length() + this->id_.length());
|
||||
return size;
|
||||
NdefRecord::NdefRecord(std::vector<uint8_t> payload_data) {
|
||||
this->payload_ = std::string(payload_data.begin(), payload_data.end());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> NdefRecord::encode(bool first, bool last) {
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
data.push_back(this->get_tnf_byte(first, last));
|
||||
// Get encoded payload, this is overriden by more specific record classes
|
||||
std::vector<uint8_t> payload_data = get_encoded_payload();
|
||||
|
||||
size_t payload_length = payload_data.size();
|
||||
|
||||
data.push_back(this->create_flag_byte(first, last, payload_length));
|
||||
|
||||
data.push_back(this->type_.length());
|
||||
|
||||
uint8_t payload_prefix = 0x00;
|
||||
uint8_t payload_prefix_length = 0x00;
|
||||
for (uint8_t i = 1; i < PAYLOAD_IDENTIFIERS_COUNT; i++) {
|
||||
std::string prefix = PAYLOAD_IDENTIFIERS[i];
|
||||
if (this->payload_.substr(0, prefix.length()).find(prefix) != std::string::npos) {
|
||||
payload_prefix = i;
|
||||
payload_prefix_length = prefix.length();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t payload_length = this->payload_.length() - payload_prefix_length + 1;
|
||||
|
||||
if (payload_length <= 255) {
|
||||
data.push_back(payload_length);
|
||||
} else {
|
||||
|
@ -58,25 +40,23 @@ std::vector<uint8_t> NdefRecord::encode(bool first, bool last) {
|
|||
data.insert(data.end(), this->id_.begin(), this->id_.end());
|
||||
}
|
||||
|
||||
data.push_back(payload_prefix);
|
||||
|
||||
data.insert(data.end(), this->payload_.begin() + payload_prefix_length, this->payload_.end());
|
||||
data.insert(data.end(), payload_data.begin(), payload_data.end());
|
||||
return data;
|
||||
}
|
||||
|
||||
uint8_t NdefRecord::get_tnf_byte(bool first, bool last) {
|
||||
uint8_t value = this->tnf_;
|
||||
uint8_t NdefRecord::create_flag_byte(bool first, bool last, size_t payload_size) {
|
||||
uint8_t value = this->tnf_ & 0b00000111;
|
||||
if (first) {
|
||||
value = value | 0x80;
|
||||
value = value | 0x80; // Set MB bit
|
||||
}
|
||||
if (last) {
|
||||
value = value | 0x40;
|
||||
value = value | 0x40; // Set ME bit
|
||||
}
|
||||
if (this->payload_.length() <= 255) {
|
||||
value = value | 0x10;
|
||||
if (payload_size <= 255) {
|
||||
value = value | 0x10; // Set SR bit
|
||||
}
|
||||
if (this->id_.length()) {
|
||||
value = value | 0x08;
|
||||
value = value | 0x08; // Set IL bit
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
|
|
@ -15,86 +15,40 @@ static const uint8_t TNF_UNKNOWN = 0x05;
|
|||
static const uint8_t TNF_UNCHANGED = 0x06;
|
||||
static const uint8_t TNF_RESERVED = 0x07;
|
||||
|
||||
static const uint8_t PAYLOAD_IDENTIFIERS_COUNT = 0x23;
|
||||
static const char *const PAYLOAD_IDENTIFIERS[] = {"",
|
||||
"http://www.",
|
||||
"https://www.",
|
||||
"http://",
|
||||
"https://",
|
||||
"tel:",
|
||||
"mailto:",
|
||||
"ftp://anonymous:anonymous@",
|
||||
"ftp://ftp.",
|
||||
"ftps://",
|
||||
"sftp://",
|
||||
"smb://",
|
||||
"nfs://",
|
||||
"ftp://",
|
||||
"dav://",
|
||||
"news:",
|
||||
"telnet://",
|
||||
"imap:",
|
||||
"rtsp://",
|
||||
"urn:",
|
||||
"pop:",
|
||||
"sip:",
|
||||
"sips:",
|
||||
"tftp:",
|
||||
"btspp://",
|
||||
"btl2cap://",
|
||||
"btgoep://",
|
||||
"tcpobex://",
|
||||
"irdaobex://",
|
||||
"file://",
|
||||
"urn:epc:id:",
|
||||
"urn:epc:tag:",
|
||||
"urn:epc:pat:",
|
||||
"urn:epc:raw:",
|
||||
"urn:epc:",
|
||||
"urn:nfc:"};
|
||||
|
||||
class NdefRecord {
|
||||
public:
|
||||
NdefRecord(){};
|
||||
NdefRecord(uint8_t tnf, const std::string &type, const std::string &payload) {
|
||||
this->tnf_ = tnf;
|
||||
this->type_ = type;
|
||||
this->set_payload(payload);
|
||||
};
|
||||
NdefRecord(uint8_t tnf, const std::string &type, const std::string &payload, const std::string &id) {
|
||||
this->tnf_ = tnf;
|
||||
this->type_ = type;
|
||||
this->set_payload(payload);
|
||||
this->id_ = id;
|
||||
};
|
||||
NdefRecord(const NdefRecord &rhs) {
|
||||
this->tnf_ = rhs.tnf_;
|
||||
this->type_ = rhs.type_;
|
||||
this->payload_ = rhs.payload_;
|
||||
this->payload_identifier_ = rhs.payload_identifier_;
|
||||
this->id_ = rhs.id_;
|
||||
};
|
||||
NdefRecord(std::vector<uint8_t> payload_data);
|
||||
void set_tnf(uint8_t tnf) { this->tnf_ = tnf; };
|
||||
void set_type(const std::string &type) { this->type_ = type; };
|
||||
void set_payload_identifier(uint8_t payload_identifier) { this->payload_identifier_ = payload_identifier; };
|
||||
void set_payload(const std::string &payload) { this->payload_ = payload; };
|
||||
void set_id(const std::string &id) { this->id_ = id; };
|
||||
NdefRecord(const NdefRecord &) = default;
|
||||
virtual ~NdefRecord() {}
|
||||
virtual std::unique_ptr<NdefRecord> clone() const { // To allow copying polymorphic classes
|
||||
return make_unique<NdefRecord>(*this);
|
||||
};
|
||||
|
||||
uint32_t get_encoded_size();
|
||||
|
||||
std::vector<uint8_t> encode(bool first, bool last);
|
||||
uint8_t get_tnf_byte(bool first, bool last);
|
||||
|
||||
uint8_t create_flag_byte(bool first, bool last, size_t payload_size);
|
||||
|
||||
const std::string &get_type() const { return this->type_; };
|
||||
const std::string &get_id() const { return this->id_; };
|
||||
const std::string &get_payload() const { return this->payload_; };
|
||||
virtual const std::string &get_payload() const { return this->payload_; };
|
||||
|
||||
virtual std::vector<uint8_t> get_encoded_payload() {
|
||||
std::vector<uint8_t> empty_payload;
|
||||
return empty_payload;
|
||||
};
|
||||
|
||||
protected:
|
||||
uint8_t tnf_;
|
||||
std::string type_;
|
||||
uint8_t payload_identifier_;
|
||||
std::string payload_;
|
||||
std::string id_;
|
||||
std::string payload_;
|
||||
};
|
||||
|
||||
} // namespace nfc
|
||||
|
|
40
esphome/components/nfc/ndef_record_text.cpp
Normal file
40
esphome/components/nfc/ndef_record_text.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "ndef_record_text.h"
|
||||
#include "ndef_record.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nfc {
|
||||
|
||||
static const char *const TAG = "nfc.ndef_record_text";
|
||||
|
||||
NdefRecordText::NdefRecordText(const std::vector<uint8_t> &payload) {
|
||||
if (payload.empty()) {
|
||||
ESP_LOGE(TAG, "Record payload too short");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t language_code_length = payload[0] & 0b00111111; // Todo, make use of encoding bit?
|
||||
|
||||
this->language_code_ = std::string(payload.begin() + 1, payload.begin() + 1 + language_code_length);
|
||||
|
||||
this->text_ = std::string(payload.begin() + 1 + language_code_length, payload.end());
|
||||
|
||||
this->tnf_ = TNF_WELL_KNOWN;
|
||||
|
||||
this->type_ = "T";
|
||||
}
|
||||
|
||||
std::vector<uint8_t> NdefRecordText::get_encoded_payload() {
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
uint8_t flag_byte = this->language_code_.length() & 0b00111111; // UTF8 assumed
|
||||
|
||||
data.push_back(flag_byte);
|
||||
|
||||
data.insert(data.end(), this->language_code_.begin(), this->language_code_.end());
|
||||
|
||||
data.insert(data.end(), this->text_.begin(), this->text_.end());
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace nfc
|
||||
} // namespace esphome
|
41
esphome/components/nfc/ndef_record_text.h
Normal file
41
esphome/components/nfc/ndef_record_text.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "ndef_record.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nfc {
|
||||
|
||||
class NdefRecordText : public NdefRecord {
|
||||
public:
|
||||
NdefRecordText(){};
|
||||
NdefRecordText(const std::vector<uint8_t> &payload);
|
||||
NdefRecordText(const std::string &language_code, const std::string &text) {
|
||||
this->tnf_ = TNF_WELL_KNOWN;
|
||||
this->type_ = "T";
|
||||
this->language_code_ = language_code;
|
||||
this->text_ = text;
|
||||
};
|
||||
NdefRecordText(const std::string &language_code, const std::string &text, const std::string &id) {
|
||||
this->tnf_ = TNF_WELL_KNOWN;
|
||||
this->type_ = "T";
|
||||
this->language_code_ = language_code;
|
||||
this->text_ = text;
|
||||
this->id_ = id;
|
||||
};
|
||||
NdefRecordText(const NdefRecordText &) = default;
|
||||
|
||||
std::unique_ptr<NdefRecord> clone() const override { return make_unique<NdefRecordText>(*this); };
|
||||
|
||||
std::vector<uint8_t> get_encoded_payload() override;
|
||||
|
||||
const std::string &get_payload() const override { return this->text_; };
|
||||
|
||||
protected:
|
||||
std::string text_;
|
||||
std::string language_code_;
|
||||
};
|
||||
|
||||
} // namespace nfc
|
||||
} // namespace esphome
|
48
esphome/components/nfc/ndef_record_uri.cpp
Normal file
48
esphome/components/nfc/ndef_record_uri.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include "ndef_record_uri.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nfc {
|
||||
|
||||
static const char *const TAG = "nfc.ndef_record_uri";
|
||||
|
||||
NdefRecordUri::NdefRecordUri(const std::vector<uint8_t> &payload) {
|
||||
if (payload.empty()) {
|
||||
ESP_LOGE(TAG, "Record payload too short");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t payload_identifier = payload[0]; // First byte of payload is prefix code
|
||||
|
||||
std::string uri(payload.begin() + 1, payload.end());
|
||||
|
||||
if (payload_identifier > 0x00 && payload_identifier <= PAYLOAD_IDENTIFIERS_COUNT) {
|
||||
uri.insert(0, PAYLOAD_IDENTIFIERS[payload_identifier]);
|
||||
}
|
||||
|
||||
this->tnf_ = TNF_WELL_KNOWN;
|
||||
this->type_ = "U";
|
||||
this->set_URI(uri);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> NdefRecordUri::get_encoded_payload() {
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
uint8_t payload_prefix = 0x00;
|
||||
uint8_t payload_prefix_length = 0x00;
|
||||
for (uint8_t i = 1; i < PAYLOAD_IDENTIFIERS_COUNT; i++) {
|
||||
std::string prefix = PAYLOAD_IDENTIFIERS[i];
|
||||
if (this->URI_.substr(0, prefix.length()).find(prefix) != std::string::npos) {
|
||||
payload_prefix = i;
|
||||
payload_prefix_length = prefix.length();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data.push_back(payload_prefix);
|
||||
|
||||
data.insert(data.end(), this->URI_.begin() + payload_prefix_length, this->URI_.end());
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace nfc
|
||||
} // namespace esphome
|
76
esphome/components/nfc/ndef_record_uri.h
Normal file
76
esphome/components/nfc/ndef_record_uri.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "ndef_record.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nfc {
|
||||
|
||||
static const uint8_t PAYLOAD_IDENTIFIERS_COUNT = 0x23;
|
||||
static const char *const PAYLOAD_IDENTIFIERS[] = {"",
|
||||
"http://www.",
|
||||
"https://www.",
|
||||
"http://",
|
||||
"https://",
|
||||
"tel:",
|
||||
"mailto:",
|
||||
"ftp://anonymous:anonymous@",
|
||||
"ftp://ftp.",
|
||||
"ftps://",
|
||||
"sftp://",
|
||||
"smb://",
|
||||
"nfs://",
|
||||
"ftp://",
|
||||
"dav://",
|
||||
"news:",
|
||||
"telnet://",
|
||||
"imap:",
|
||||
"rtsp://",
|
||||
"urn:",
|
||||
"pop:",
|
||||
"sip:",
|
||||
"sips:",
|
||||
"tftp:",
|
||||
"btspp://",
|
||||
"btl2cap://",
|
||||
"btgoep://",
|
||||
"tcpobex://",
|
||||
"irdaobex://",
|
||||
"file://",
|
||||
"urn:epc:id:",
|
||||
"urn:epc:tag:",
|
||||
"urn:epc:pat:",
|
||||
"urn:epc:raw:",
|
||||
"urn:epc:",
|
||||
"urn:nfc:"};
|
||||
|
||||
class NdefRecordUri : public NdefRecord {
|
||||
public:
|
||||
NdefRecordUri(){};
|
||||
NdefRecordUri(const std::vector<uint8_t> &payload);
|
||||
NdefRecordUri(const std::string &URI) {
|
||||
this->tnf_ = TNF_WELL_KNOWN;
|
||||
this->type_ = "U";
|
||||
this->URI_ = URI;
|
||||
};
|
||||
NdefRecordUri(const std::string &URI, const std::string &id) {
|
||||
this->tnf_ = TNF_WELL_KNOWN;
|
||||
this->type_ = "U";
|
||||
this->URI_ = URI;
|
||||
this->id_ = id;
|
||||
};
|
||||
NdefRecordUri(const NdefRecordUri &) = default;
|
||||
std::unique_ptr<NdefRecord> clone() const override { return make_unique<NdefRecordUri>(*this); };
|
||||
|
||||
void set_URI(const std::string &URI) { this->URI_ = URI; };
|
||||
|
||||
std::vector<uint8_t> get_encoded_payload() override;
|
||||
const std::string &get_payload() const override { return this->URI_; };
|
||||
|
||||
protected:
|
||||
std::string URI_;
|
||||
};
|
||||
|
||||
} // namespace nfc
|
||||
} // namespace esphome
|
Loading…
Reference in a new issue