esp-vfd-clock/firmware/main/wifi_provisioner.hpp
2024-01-23 14:42:48 +02:00

137 lines
3.7 KiB
C++

//
// Created by erki on 07/01/24.
//
#pragma once
#include "nvs_handle.hpp"
#include <esp_http_server.h>
#include <etl/vector.h>
#include <etl/string.h>
#include <etl/delegate.h>
#include <cstdint>
#include <expected>
#include <variant>
#include <memory>
#include <cJSON.h>
struct CJsonStringDeleter
{
void operator()(char* json_str) const
{
if (json_str)
cJSON_free(json_str);
}
};
using CJsonStrPtr = std::unique_ptr<char, CJsonStringDeleter>;
struct CJsonDeleter
{
void operator()(cJSON* element) const
{
if (element)
cJSON_Delete(element);
}
};
using CJsonPtr = std::unique_ptr<cJSON, CJsonDeleter>;
class WifiProvisioner
{
public:
struct Parameter
{
etl::string<100> name;
etl::string<15> nvs_name;
enum class Type
{
INT,
FLOAT,
STRING
};
Type type;
using Value = std::variant<std::monostate, std::int32_t, float, etl::string<100>>;
Value data;
Parameter() = default;
Parameter(const char* name, const char* nvs_name, const Parameter::Type type)
: name(name)
, nvs_name(nvs_name)
, type(type)
, data(std::monostate())
{ }
std::expected<Value, esp_err_t> tryReadAndAssignValue(nvs::NVSHandle* file_handle);
std::expected<Value, const char*> tryValidateAndAssign(cJSON* object);
template<typename T>
std::expected<T, std::error_code> getValue() const
{
static_assert(std::is_same_v<T, std::int32_t> || std::is_same_v<T, float> || std::is_same_v<T, etl::string<100>>,
"T must be in the Value variant.");
if (holds_alternative<std::monostate>(data))
return std::unexpected(std::make_error_code(std::errc::io_error));
if constexpr (std::is_same_v<T, std::int32_t>)
{
if (type != Type::INT)
return std::unexpected(std::make_error_code(std::errc::invalid_argument));
return std::get<std::int32_t>(data);
}
else if constexpr (std::is_same_v<T, float>)
{
if (type != Type::FLOAT)
return std::unexpected(std::make_error_code(std::errc::invalid_argument));
return std::get<float>(data);
}
else
{
if (type != Type::STRING)
return std::unexpected(std::make_error_code(std::errc::invalid_argument));
return std::get<etl::string<100>>(data);
}
}
};
explicit WifiProvisioner(const char* settings_namespace, etl::delegate<void (const etl::vector<Parameter, 10>&)> success_cb);
~WifiProvisioner();
WifiProvisioner() = delete;
WifiProvisioner(const WifiProvisioner&) = delete;
WifiProvisioner(WifiProvisioner&&) = delete;
WifiProvisioner& operator=(const WifiProvisioner&) = delete;
WifiProvisioner& operator=(WifiProvisioner&&) = delete;
bool parametersAreConfigured();
void clearSettings();
std::expected<void, esp_err_t> startProvisioning();
const etl::vector<Parameter, 10>& getParameters();
std::expected<Parameter, std::error_code> getParameter(const etl::string<15>& name);
std::expected<void, std::error_code> addParameter(const char* name, const char* nvs_name, const Parameter::Type type);
CJsonPtr getParameterObjects() const;
std::expected<void, const char*> tryConfigureParameters(CJsonPtr json_root);
private:
std::unique_ptr<nvs::NVSHandle> file_handle_;
bool settings_initialized_ = false;
void* esp_netif_ = nullptr;
httpd_handle_t http_server_ = nullptr;
etl::vector<Parameter, 10> params_;
etl::delegate<void (const etl::vector<Parameter, 10>& params)> success_cb_;
esp_err_t initializeNvsNamespace_();
std::expected<void, esp_err_t> initializeWifiAp_();
std::expected<httpd_handle_t, esp_err_t> initializeCaptivePortal_();
std::expected<void, esp_err_t> writeAndCommitParameters_();
};