Skip to content

Commit

Permalink
dtu: new component
Browse files Browse the repository at this point in the history
  • Loading branch information
dentra committed Aug 9, 2024
1 parent ff8db73 commit 0c4fd8b
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 0 deletions.
1 change: 1 addition & 0 deletions components/dtu/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CODEOWNERS = ["@dentra"]
6 changes: 6 additions & 0 deletions components/dtu/dtu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

#include "str.h"
#include "ver.h"
#include "sys.h"
#include "net.h"
34 changes: 34 additions & 0 deletions components/dtu/net.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "esphome/core/defines.h"
#ifdef USE_ESP_IDF
#include "esphome/core/log.h"
#include "esp_http_client.h"
#include "net.h"

namespace esphome {
namespace dtu {

static const char *const TAG = "dtu.net";

bool check_url_available(const char *url) {
esp_http_client_config_t config{};
config.url = url;
config.buffer_size_tx = 2048;
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = ESP_OK;
do {
err = esp_http_client_perform(client);
} while (err == ESP_ERR_HTTP_EAGAIN);
ESP_LOGD(TAG, "Perform result for %s: %s", url, esp_err_to_name(err));
if (err == ESP_OK) {
const auto status_code = esp_http_client_get_status_code(client);
ESP_LOGD(TAG, "Status code for %s: %d", url, status_code);
err = status_code == HttpStatus_Ok ? ESP_OK : ESP_FAIL;
}
esp_http_client_cleanup(client);
ESP_LOGD(TAG, "Checking %s available: %s", url, YESNO(err == ESP_OK));
return err == ESP_OK;
}

} // namespace dtu
} // namespace esphome
#endif
14 changes: 14 additions & 0 deletions components/dtu/net.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_ESP_IDF
#include <string>

namespace esphome {
namespace dtu {

bool check_url_available(const char *url);
inline bool check_url_available(const std::string &url) { return check_url_available(url.c_str()); }

} // namespace dtu
} // namespace esphome
#endif
22 changes: 22 additions & 0 deletions components/dtu/str.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "str.h"

namespace esphome {
namespace dtu {

template<typename T> std::vector<std::string> str_split_(const std::string &s, T delimiter) {
std::vector<std::string> res;
size_t start = 0, end;
while ((end = s.find(delimiter, start)) != std::string::npos) {
res.emplace_back(s.substr(start, end - start));
start = end + 1;
}
res.push_back(s.substr(start));
return res;
}

std::vector<std::string> str_split(const std::string &s, char delimiter) { return str_split_(s, delimiter); }

std::vector<std::string> str_split(const std::string &s, const char *delimiter) { return str_split_(s, delimiter); }

} // namespace dtu
} // namespace esphome
77 changes: 77 additions & 0 deletions components/dtu/str.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once

#include <functional>
#include <string>

namespace esphome {
namespace dtu {

/// Trim string inplace from the begin.
/// @param predicate to test trimmed characters, by default checks with isspace.
template<typename... Predicate> constexpr inline void str_ltrim_ref(std::string &s, Predicate... predicate) {
if constexpr (sizeof...(Predicate) == 1) {
s.erase(s.cbegin(), std::find_if_not(s.cbegin(), s.cend(), std::forward<Predicate>(predicate)...));
} else if constexpr (sizeof...(Predicate) == 0) {
s.erase(s.cbegin(), std::find_if_not(s.cbegin(), s.cend(), [](char c) { return std::isspace(c); }));
} else {
static_assert(sizeof...(Predicate) > 1, "invalid predicate parameter");
}
}

/// Trim string inplace from then end.
/// @param predicate to test trimmed characters, by default checks with isspace.
template<typename... Predicate> constexpr inline void str_rtrim_ref(std::string &s, Predicate... predicate) {
if constexpr (sizeof...(Predicate) == 1) {
s.erase(std::find_if_not(s.rbegin(), s.rend(), std::forward<Predicate>(predicate)...).base(), s.end());
} else if constexpr (sizeof...(Predicate) == 0) {
s.erase(std::find_if_not(s.rbegin(), s.rend(), [](char c) { return std::isspace(c); }).base(), s.end());
} else {
static_assert(sizeof...(Predicate) > 1, "invalid predicate parameter");
}
}

/// Trim string inplace from then both sides.
/// @param predicate to test trimmed characters, by default checks with isspace.
template<typename... Predicate> constexpr inline void str_trim_ref(std::string &s, Predicate... predicate) {
str_rtrim_ref(s, std::forward<Predicate>(predicate)...);
str_ltrim_ref(s, std::forward<Predicate>(predicate)...);
}

/// Trim string from the begin.
/// @param predicate to test trimmed characters, by default checks with isspace.
template<typename... Predicate> inline std::string str_ltrim(const std::string &s, Predicate... predicate) {
auto ref = s;
str_ltrim_ref(ref, std::forward<Predicate>(predicate)...);
return ref;
}

/// Trim string from then end.
/// @param predicate to test trimmed characters, by default checks with isspace.
template<typename... Predicate> inline std::string str_rtrim(const std::string &s, Predicate... predicate) {
auto ref = s;
str_rtrim_ref(ref, std::forward<Predicate>(predicate)...);
return ref;
}

/// Trim string from then both sides.
/// @param predicate to test trimmed characters, by default checks with isspace.
template<typename... Predicate> inline std::string str_trim(const std::string &s, Predicate... predicate) {
auto ref = s;
str_trim_ref(ref, std::forward<Predicate>(predicate)...);
return ref;
}

/// Split the string into parts by separator.
/// Default splits by space.
std::vector<std::string> str_split(const std::string &s, char delimiter = ' ');

/// Split the string into parts by separator.
std::vector<std::string> str_split(const std::string &s, const char *delimiter);

/// Split the string into parts by separator.
inline std::vector<std::string> str_split(const std::string &s, const std::string &delimiter) {
return str_split(s, delimiter.c_str());
}

} // namespace dtu
} // namespace esphome
21 changes: 21 additions & 0 deletions components/dtu/sys.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <cinttypes>

#include "esphome/core/helpers.h"

#include "sys.h"

namespace esphome {
namespace dtu {

std::string get_mac_suffix() {
uint8_t mac[6];
get_mac_address_raw(mac);
auto s = format_hex(mac + 3, 3);
s.insert(s.cbegin(), '-');
return s;
}

std::string fnv1_hash_str(const std::string &str) { return str_snprintf("%08" PRIx32, 9, fnv1_hash(str)); }

} // namespace dtu
} // namespace esphome
15 changes: 15 additions & 0 deletions components/dtu/sys.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <string>

namespace esphome {
namespace dtu {

/// Get the last 6 symbols of MAC-address prependend with "-".
std::string get_mac_suffix();

/// Calculate a FNV-1 hash of \p str and return it as string repr.
std::string fnv1_hash_str(const std::string &str);

} // namespace dtu
} // namespace esphome
29 changes: 29 additions & 0 deletions components/dtu/ver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "ver.h"

namespace esphome {
namespace dtu {

int parse_version(const char *version) {
char *next{};
uint32_t res = std::strtoul(version, &next, 10) * 100000;
if (next && *next != 0) {
uint32_t part = std::strtoul(++next, &next, 10);
if (part < 99) {
res += part * 1000;
} else {
res += 99000;
}
if (next && *next != 0) {
part = std::strtoul(++next, &next, 10);
if (part < 999) {
res += part;
} else {
res += 999;
}
}
}
return res;
}

} // namespace dtu
} // namespace esphome
32 changes: 32 additions & 0 deletions components/dtu/ver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include <string>

namespace esphome {
namespace dtu {

/// Parse version string like 1, 1.2, 1.2.3 into uint32_t representation.
/// Max number of part 1 is 9999.
/// Max number of part 2 is 99.
/// Max number of part 3 is 999.
/// Only 3 parts are supported.
/// If the version contains more than 3 parts, all other parts are skipped and the result will be negative.
/// Max version number is 999999999 or its negative version.
/// Part separator is any non number character.
/// Samples:
/// 1 -> 100000
/// 1.0 -> 100000
/// 1.1 -> 101000
/// 1.0.0 -> 100000
/// 1.1.0 -> 101000
/// 1.1.1 -> 101001
/// 1.1.1.1 -> -101001
/// 1.0.0-beta -> -100000
/// master -> 0
int parse_version(const char *version);

/// @see parse_version(const char*)
inline int parse_version(const std::string &version) { return parse_version(version.c_str()); }

} // namespace dtu
} // namespace esphome

0 comments on commit 0c4fd8b

Please sign in to comment.