feat(gateway): enhance GatewayNetworkService with HTTP and UDP support, add status LED control
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -12,9 +12,9 @@ This folder hosts the native ESP-IDF C++ rewrite of the Lua DALI gateway.
|
||||
- `dali_domain/`: native DALI domain facade over `dali_cpp`.
|
||||
- `gateway_ble/`: NimBLE GATT bridge for BLE transport parity on `FFF1`/`FFF2`/`FFF3`.
|
||||
- `gateway_controller/`: Lua-compatible gateway command dispatcher, internal scene/group state, and notification fan-out.
|
||||
- `gateway_network/`: initial HTTP `/info` and `/dali/cmd` plus UDP port `2020` control-plane ingress for the native gateway.
|
||||
- `gateway_network/`: HTTP `/info`, `/dali/cmd`, `/led/1`, `/led/0`, `/jq.js`, UDP port `2020` command/notify routing, Wi-Fi STA lifecycle, and setup AP mode for the native gateway.
|
||||
- `gateway_runtime/`: persistent runtime state, command queueing, and device info services.
|
||||
|
||||
## Current status
|
||||
|
||||
The native rewrite now wires a shared `gateway_core` bootstrap component, a multi-channel `dali_domain` wrapper over `dali_cpp`, a local vendored `dali` hardware backend from the LuatOS ESP-IDF port, an initial `gateway_runtime` service that provides persistent settings, device info, Lua-compatible command framing helpers, and Lua-style query command deduplication, plus a `gateway_controller` service that starts the gateway command task, dispatches core Lua gateway opcodes, and owns internal scene/group state. The gateway app now also includes an initial `gateway_ble` NimBLE bridge that advertises a Lua-compatible GATT service and forwards `FFF3` framed notifications plus incoming `FFF1`/`FFF2`/`FFF3` writes into the native controller and DALI domain, and an initial `gateway_network` service that starts the native HTTP `/info` and `POST /dali/cmd` surfaces plus the UDP control-plane router on port `2020`. The gateway app exposes per-channel PHY selection through `main/Kconfig.projbuild`; each channel can be disabled, bound to the native DALI GPIO HAL, or bound to a UART1/UART2 serial PHY.
|
||||
The native rewrite now wires a shared `gateway_core` bootstrap component, a multi-channel `dali_domain` wrapper over `dali_cpp`, a local vendored `dali` hardware backend from the LuatOS ESP-IDF port, an initial `gateway_runtime` service that provides persistent settings, device info, Lua-compatible command framing helpers, and Lua-style query command deduplication, plus a `gateway_controller` service that starts the gateway command task, dispatches core Lua gateway opcodes, and owns internal scene/group state. The gateway app also includes a `gateway_ble` NimBLE bridge that advertises a Lua-compatible GATT service and forwards `FFF3` framed notifications plus incoming `FFF1`/`FFF2`/`FFF3` writes into the native controller and DALI domain, and a `gateway_network` service that starts the native HTTP `/info`, `GET`/`POST /dali/cmd`, `/led/1`, `/led/0`, `/jq.js`, UDP control-plane router on port `2020`, Wi-Fi STA startup from persisted credentials, and the Lua-style `LAMMIN_Gateway` setup AP on `192.168.3.1`. The gateway app exposes per-channel PHY selection through `main/Kconfig.projbuild`; each channel can be disabled, bound to the native DALI GPIO HAL, or bound to a UART1/UART2 serial PHY. The checked-in `sdkconfig` is aligned with the app's custom 16 MB partition table so the Wi-Fi/BLE/network-enabled image fits the OTA app slots.
|
||||
@@ -258,4 +258,44 @@ config GATEWAY_DALI_BAUDRATE
|
||||
help
|
||||
Runtime baudrate used when initializing the local DALI bus.
|
||||
|
||||
menu "Gateway Network Services"
|
||||
|
||||
config GATEWAY_NETWORK_HTTP_ENABLED
|
||||
bool "Enable HTTP gateway API"
|
||||
default y
|
||||
help
|
||||
Enables Lua-compatible HTTP gateway routes such as /info and /dali/cmd.
|
||||
|
||||
config GATEWAY_NETWORK_HTTP_PORT
|
||||
int "HTTP API port"
|
||||
depends on GATEWAY_NETWORK_HTTP_ENABLED
|
||||
range 1 65535
|
||||
default 80
|
||||
|
||||
config GATEWAY_NETWORK_UDP_ROUTER_ENABLED
|
||||
bool "Enable UDP IP router"
|
||||
default y
|
||||
help
|
||||
Enables raw gateway command ingress and notification replies on UDP port 2020 by default.
|
||||
|
||||
config GATEWAY_NETWORK_UDP_PORT
|
||||
int "UDP IP router port"
|
||||
depends on GATEWAY_NETWORK_UDP_ROUTER_ENABLED
|
||||
range 1 65535
|
||||
default 2020
|
||||
|
||||
config GATEWAY_STATUS_LED_GPIO
|
||||
int "Status LED GPIO"
|
||||
range -1 48
|
||||
default -1
|
||||
help
|
||||
GPIO used by the HTTP /led/1 and /led/0 routes. Set to -1 to disable GPIO control.
|
||||
|
||||
config GATEWAY_STATUS_LED_ACTIVE_HIGH
|
||||
bool "Status LED is active high"
|
||||
depends on GATEWAY_STATUS_LED_GPIO >= 0
|
||||
default y
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
@@ -16,6 +16,18 @@
|
||||
#define CONFIG_GATEWAY_CHANNEL_COUNT 2
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_NETWORK_HTTP_PORT
|
||||
#define CONFIG_GATEWAY_NETWORK_HTTP_PORT 80
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_NETWORK_UDP_PORT
|
||||
#define CONFIG_GATEWAY_NETWORK_UDP_PORT 2020
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_STATUS_LED_GPIO
|
||||
#define CONFIG_GATEWAY_STATUS_LED_GPIO -1
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
constexpr const char* kProjectName = "DALI_485_Gateway";
|
||||
constexpr const char* kProjectVersion = "0.1.0";
|
||||
@@ -266,8 +278,25 @@ extern "C" void app_main(void) {
|
||||
|
||||
if (profile.enable_wifi || profile.enable_eth) {
|
||||
gateway::GatewayNetworkServiceConfig network_config;
|
||||
network_config.wifi_enabled = profile.enable_wifi;
|
||||
#ifdef CONFIG_GATEWAY_NETWORK_HTTP_ENABLED
|
||||
network_config.http_enabled = true;
|
||||
#else
|
||||
network_config.http_enabled = false;
|
||||
#endif
|
||||
#ifdef CONFIG_GATEWAY_NETWORK_UDP_ROUTER_ENABLED
|
||||
network_config.udp_enabled = true;
|
||||
#else
|
||||
network_config.udp_enabled = false;
|
||||
#endif
|
||||
network_config.http_port = static_cast<uint16_t>(CONFIG_GATEWAY_NETWORK_HTTP_PORT);
|
||||
network_config.udp_port = static_cast<uint16_t>(CONFIG_GATEWAY_NETWORK_UDP_PORT);
|
||||
network_config.status_led_gpio = CONFIG_GATEWAY_STATUS_LED_GPIO;
|
||||
#ifdef CONFIG_GATEWAY_STATUS_LED_ACTIVE_HIGH
|
||||
network_config.status_led_active_high = true;
|
||||
#else
|
||||
network_config.status_led_active_high = false;
|
||||
#endif
|
||||
s_network = std::make_unique<gateway::GatewayNetworkService>(*s_controller, *s_runtime,
|
||||
network_config);
|
||||
ESP_ERROR_CHECK(s_network->start());
|
||||
|
||||
+16
-6
@@ -562,13 +562,13 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ="80m"
|
||||
# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set
|
||||
# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set
|
||||
# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set
|
||||
# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||
# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set
|
||||
# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set
|
||||
# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="16MB"
|
||||
# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set
|
||||
CONFIG_ESPTOOLPY_BEFORE_RESET=y
|
||||
# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set
|
||||
@@ -584,11 +584,11 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
|
||||
#
|
||||
# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
|
||||
# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set
|
||||
CONFIG_PARTITION_TABLE_TWO_OTA=y
|
||||
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
|
||||
# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set
|
||||
# CONFIG_PARTITION_TABLE_CUSTOM is not set
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_two_ota.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
||||
CONFIG_PARTITION_TABLE_MD5=y
|
||||
# end of Partition Table
|
||||
@@ -619,6 +619,16 @@ CONFIG_GATEWAY_CHANNEL2_PHY_DISABLED=y
|
||||
# end of Gateway Channel 2
|
||||
|
||||
# CONFIG_GATEWAY_ENABLE_DALI_BUS is not set
|
||||
|
||||
#
|
||||
# Gateway Network Services
|
||||
#
|
||||
CONFIG_GATEWAY_NETWORK_HTTP_ENABLED=y
|
||||
CONFIG_GATEWAY_NETWORK_HTTP_PORT=80
|
||||
CONFIG_GATEWAY_NETWORK_UDP_ROUTER_ENABLED=y
|
||||
CONFIG_GATEWAY_NETWORK_UDP_PORT=2020
|
||||
CONFIG_GATEWAY_STATUS_LED_GPIO=-1
|
||||
# end of Gateway Network Services
|
||||
# end of Gateway App
|
||||
|
||||
#
|
||||
|
||||
@@ -31,10 +31,34 @@ struct GatewayControllerConfig {
|
||||
bool internal_group_supported{true};
|
||||
};
|
||||
|
||||
struct GatewayChannelSnapshot {
|
||||
uint8_t channel_index{0};
|
||||
uint8_t gateway_id{0};
|
||||
std::string name;
|
||||
std::string phy;
|
||||
uint8_t scene_mask_low{0};
|
||||
uint8_t scene_mask_high{0};
|
||||
uint8_t group_mask_low{0};
|
||||
uint8_t group_mask_high{0};
|
||||
bool allocating{false};
|
||||
int last_alloc_addr{0};
|
||||
};
|
||||
|
||||
struct GatewayControllerSnapshot {
|
||||
bool setup_mode{false};
|
||||
bool ble_enabled{false};
|
||||
bool wifi_enabled{false};
|
||||
bool ip_router_enabled{false};
|
||||
bool internal_scene_supported{false};
|
||||
bool internal_group_supported{false};
|
||||
std::vector<GatewayChannelSnapshot> channels;
|
||||
};
|
||||
|
||||
class GatewayController {
|
||||
public:
|
||||
using NotificationSink = std::function<void(const std::vector<uint8_t>& frame)>;
|
||||
using BleStateSink = std::function<void(bool enabled)>;
|
||||
using WifiStateSink = std::function<void(uint8_t mode)>;
|
||||
using GatewayNameSink = std::function<void(uint8_t gateway_id)>;
|
||||
|
||||
GatewayController(GatewayRuntime& runtime, DaliDomainService& dali_domain,
|
||||
@@ -45,12 +69,14 @@ class GatewayController {
|
||||
bool enqueueCommandFrame(const std::vector<uint8_t>& frame);
|
||||
void addNotificationSink(NotificationSink sink);
|
||||
void addBleStateSink(BleStateSink sink);
|
||||
void addWifiStateSink(WifiStateSink sink);
|
||||
void addGatewayNameSink(GatewayNameSink sink);
|
||||
|
||||
bool setupMode() const;
|
||||
bool bleEnabled() const;
|
||||
bool wifiEnabled() const;
|
||||
bool ipRouterEnabled() const;
|
||||
GatewayControllerSnapshot snapshot();
|
||||
|
||||
private:
|
||||
struct InternalScene {
|
||||
@@ -143,6 +169,7 @@ class GatewayController {
|
||||
nvs_handle_t storage_{0};
|
||||
std::vector<NotificationSink> notification_sinks_;
|
||||
std::vector<BleStateSink> ble_state_sinks_;
|
||||
std::vector<WifiStateSink> wifi_state_sinks_;
|
||||
std::vector<GatewayNameSink> gateway_name_sinks_;
|
||||
std::map<uint8_t, SceneStore> scenes_;
|
||||
std::map<uint8_t, GroupStore> groups_;
|
||||
|
||||
@@ -80,6 +80,18 @@ void AppendPaddedName(std::vector<uint8_t>& out, std::string_view name) {
|
||||
}
|
||||
}
|
||||
|
||||
const char* PhyKindToString(DaliPhyKind phy_kind) {
|
||||
switch (phy_kind) {
|
||||
case DaliPhyKind::kNativeHardware:
|
||||
return "native";
|
||||
case DaliPhyKind::kSerialUart:
|
||||
return "serial";
|
||||
case DaliPhyKind::kCustom:
|
||||
default:
|
||||
return "custom";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GatewayController::GatewayController(GatewayRuntime& runtime, DaliDomainService& dali_domain,
|
||||
@@ -157,6 +169,12 @@ void GatewayController::addBleStateSink(BleStateSink sink) {
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayController::addWifiStateSink(WifiStateSink sink) {
|
||||
if (sink) {
|
||||
wifi_state_sinks_.push_back(std::move(sink));
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayController::addGatewayNameSink(GatewayNameSink sink) {
|
||||
if (sink) {
|
||||
gateway_name_sinks_.push_back(std::move(sink));
|
||||
@@ -179,6 +197,36 @@ bool GatewayController::ipRouterEnabled() const {
|
||||
return ip_router_enabled_;
|
||||
}
|
||||
|
||||
GatewayControllerSnapshot GatewayController::snapshot() {
|
||||
GatewayControllerSnapshot out;
|
||||
out.setup_mode = setup_mode_;
|
||||
out.ble_enabled = ble_enabled_;
|
||||
out.wifi_enabled = wifi_enabled_;
|
||||
out.ip_router_enabled = ip_router_enabled_;
|
||||
out.internal_scene_supported = config_.internal_scene_supported;
|
||||
out.internal_group_supported = config_.internal_group_supported;
|
||||
|
||||
const auto channels = dali_domain_.channelInfo();
|
||||
out.channels.reserve(channels.size());
|
||||
for (const auto& channel : channels) {
|
||||
const auto [scene_low, scene_high] = sceneMask(channel.gateway_id);
|
||||
const auto [group_low, group_high] = groupMask(channel.gateway_id);
|
||||
GatewayChannelSnapshot channel_snapshot;
|
||||
channel_snapshot.channel_index = channel.channel_index;
|
||||
channel_snapshot.gateway_id = channel.gateway_id;
|
||||
channel_snapshot.name = channel.name;
|
||||
channel_snapshot.phy = PhyKindToString(channel.phy_kind);
|
||||
channel_snapshot.scene_mask_low = scene_low;
|
||||
channel_snapshot.scene_mask_high = scene_high;
|
||||
channel_snapshot.group_mask_low = group_low;
|
||||
channel_snapshot.group_mask_high = group_high;
|
||||
channel_snapshot.allocating = dali_domain_.isAllocAddr(channel.gateway_id);
|
||||
channel_snapshot.last_alloc_addr = dali_domain_.lastAllocAddr(channel.gateway_id);
|
||||
out.channels.push_back(std::move(channel_snapshot));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void GatewayController::TaskEntry(void* arg) {
|
||||
static_cast<GatewayController*>(arg)->taskLoop();
|
||||
}
|
||||
@@ -257,6 +305,9 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
|
||||
} else if (addr == 1 || addr == 101) {
|
||||
wifi_enabled_ = true;
|
||||
}
|
||||
for (const auto& sink : wifi_state_sinks_) {
|
||||
sink(addr);
|
||||
}
|
||||
break;
|
||||
case 0x05:
|
||||
handleGatewayNameCommand(gateway_id, command);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
idf_component_register(
|
||||
SRCS "src/gateway_network.cpp"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp_event esp_http_server esp_netif freertos gateway_controller gateway_runtime log lwip espressif__cjson
|
||||
REQUIRES esp_event esp_http_server esp_netif esp_wifi freertos gateway_controller gateway_runtime log lwip espressif__cjson
|
||||
)
|
||||
|
||||
set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17)
|
||||
@@ -5,7 +5,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "esp_netif.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
@@ -17,10 +19,13 @@ class GatewayController;
|
||||
class GatewayRuntime;
|
||||
|
||||
struct GatewayNetworkServiceConfig {
|
||||
bool wifi_enabled{true};
|
||||
bool http_enabled{true};
|
||||
bool udp_enabled{true};
|
||||
uint16_t http_port{80};
|
||||
uint16_t udp_port{2020};
|
||||
int status_led_gpio{-1};
|
||||
bool status_led_active_high{true};
|
||||
uint32_t udp_task_stack_size{4096};
|
||||
UBaseType_t udp_task_priority{4};
|
||||
};
|
||||
@@ -35,21 +40,38 @@ class GatewayNetworkService {
|
||||
private:
|
||||
static void UdpTaskEntry(void* arg);
|
||||
static esp_err_t HandleInfoGet(httpd_req_t* req);
|
||||
static esp_err_t HandleCommandGet(httpd_req_t* req);
|
||||
static esp_err_t HandleCommandPost(httpd_req_t* req);
|
||||
static esp_err_t HandleLedOnGet(httpd_req_t* req);
|
||||
static esp_err_t HandleLedOffGet(httpd_req_t* req);
|
||||
static esp_err_t HandleJqJsGet(httpd_req_t* req);
|
||||
static void HandleWifiEvent(void* arg, esp_event_base_t event_base, int32_t event_id,
|
||||
void* event_data);
|
||||
|
||||
esp_err_t ensureNetworkStack();
|
||||
esp_err_t startWifi();
|
||||
esp_err_t startSetupAp();
|
||||
esp_err_t configureStatusLed();
|
||||
esp_err_t startHttpServer();
|
||||
esp_err_t startUdpTask();
|
||||
void udpTaskLoop();
|
||||
void handleGatewayNotification(const std::vector<uint8_t>& frame);
|
||||
void handleWifiControl(uint8_t mode);
|
||||
void handleWifiEvent(esp_event_base_t event_base, int32_t event_id, void* event_data);
|
||||
std::string deviceInfoJson() const;
|
||||
std::string deviceInfoDoubleEncodedJson() const;
|
||||
std::string gatewaySnapshotJson();
|
||||
void setStatusLed(bool on);
|
||||
|
||||
GatewayController& controller_;
|
||||
GatewayRuntime& runtime_;
|
||||
GatewayNetworkServiceConfig config_;
|
||||
bool started_{false};
|
||||
httpd_handle_t http_server_{nullptr};
|
||||
esp_netif_t* wifi_sta_netif_{nullptr};
|
||||
esp_netif_t* wifi_ap_netif_{nullptr};
|
||||
bool wifi_started_{false};
|
||||
bool setup_ap_started_{false};
|
||||
TaskHandle_t udp_task_handle_{nullptr};
|
||||
int udp_socket_{-1};
|
||||
SemaphoreHandle_t udp_lock_{nullptr};
|
||||
|
||||
@@ -4,14 +4,19 @@
|
||||
#include "gateway_runtime.hpp"
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ip_addr.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "lwip/inet.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace gateway {
|
||||
@@ -19,6 +24,7 @@ namespace gateway {
|
||||
namespace {
|
||||
|
||||
constexpr const char* kTag = "gateway_network";
|
||||
constexpr const char* kSetupApSsid = "LAMMIN_Gateway";
|
||||
constexpr size_t kUdpBufferSize = 256;
|
||||
|
||||
class LockGuard {
|
||||
@@ -73,6 +79,13 @@ std::vector<uint8_t> DecodeHex(std::string_view hex) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::string MacToHex(const uint8_t mac[6]) {
|
||||
char out[13] = {0};
|
||||
std::snprintf(out, sizeof(out), "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3],
|
||||
mac[4], mac[5]);
|
||||
return std::string(out);
|
||||
}
|
||||
|
||||
std::string PrintJson(cJSON* node) {
|
||||
if (node == nullptr) {
|
||||
return {};
|
||||
@@ -105,6 +118,16 @@ esp_err_t ReadRequestBody(httpd_req_t* req, std::string& body) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t RegisterUri(httpd_handle_t server, const char* uri, httpd_method_t method,
|
||||
esp_err_t (*handler)(httpd_req_t*), void* user_ctx) {
|
||||
httpd_uri_t route = {};
|
||||
route.uri = uri;
|
||||
route.method = method;
|
||||
route.handler = handler;
|
||||
route.user_ctx = user_ctx;
|
||||
return httpd_register_uri_handler(server, &route);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GatewayNetworkService::GatewayNetworkService(GatewayController& controller,
|
||||
@@ -123,8 +146,21 @@ esp_err_t GatewayNetworkService::start() {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (config_.wifi_enabled) {
|
||||
err = startWifi();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = configureStatusLed();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
controller_.addNotificationSink(
|
||||
[this](const std::vector<uint8_t>& frame) { handleGatewayNotification(frame); });
|
||||
controller_.addWifiStateSink([this](uint8_t mode) { handleWifiControl(mode); });
|
||||
|
||||
if (config_.http_enabled) {
|
||||
err = startHttpServer();
|
||||
@@ -162,6 +198,162 @@ esp_err_t GatewayNetworkService::ensureNetworkStack() {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::startWifi() {
|
||||
if (wifi_started_) {
|
||||
return ESP_OK;
|
||||
}
|
||||
setup_ap_started_ = false;
|
||||
|
||||
if (wifi_sta_netif_ == nullptr) {
|
||||
wifi_sta_netif_ = esp_netif_create_default_wifi_sta();
|
||||
if (wifi_sta_netif_ == nullptr) {
|
||||
ESP_LOGE(kTag, "failed to create default Wi-Fi STA netif");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
esp_err_t err = esp_wifi_init(&wifi_init_config);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to init Wi-Fi: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
||||
&GatewayNetworkService::HandleWifiEvent, this);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to register Wi-Fi event handler: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
||||
&GatewayNetworkService::HandleWifiEvent, this);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to register IP event handler: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
||||
err = esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to set Wi-Fi mode STA: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
const auto device_info = runtime_.deviceInfo();
|
||||
if (device_info.wlan.has_value() && !device_info.wlan->ssid.empty()) {
|
||||
wifi_config_t wifi_config = {};
|
||||
std::strncpy(reinterpret_cast<char*>(wifi_config.sta.ssid), device_info.wlan->ssid.c_str(),
|
||||
sizeof(wifi_config.sta.ssid) - 1);
|
||||
std::strncpy(reinterpret_cast<char*>(wifi_config.sta.password),
|
||||
device_info.wlan->password.c_str(), sizeof(wifi_config.sta.password) - 1);
|
||||
err = esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to set Wi-Fi credentials: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = esp_wifi_start();
|
||||
if (err != ESP_OK && err != ESP_ERR_WIFI_CONN) {
|
||||
ESP_LOGE(kTag, "failed to start Wi-Fi: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t mac[6] = {0};
|
||||
if (device_info.wlan.has_value() && !device_info.wlan->ssid.empty() &&
|
||||
esp_wifi_get_mac(WIFI_IF_STA, mac) == ESP_OK) {
|
||||
WirelessInfo wireless = device_info.wlan.value_or(WirelessInfo{});
|
||||
wireless.mac = MacToHex(mac);
|
||||
runtime_.setWirelessInfo(std::move(wireless));
|
||||
}
|
||||
|
||||
wifi_started_ = true;
|
||||
ESP_LOGI(kTag, "Wi-Fi STA started has_credentials=%d",
|
||||
device_info.wlan.has_value() && !device_info.wlan->ssid.empty());
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::startSetupAp() {
|
||||
if (wifi_ap_netif_ == nullptr) {
|
||||
wifi_ap_netif_ = esp_netif_create_default_wifi_ap();
|
||||
if (wifi_ap_netif_ == nullptr) {
|
||||
ESP_LOGE(kTag, "failed to create default Wi-Fi AP netif");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_netif_ip_info_t ip_info = {};
|
||||
IP4_ADDR(&ip_info.ip, 192, 168, 3, 1);
|
||||
IP4_ADDR(&ip_info.gw, 192, 168, 3, 1);
|
||||
IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(wifi_ap_netif_));
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_set_ip_info(wifi_ap_netif_, &ip_info));
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(wifi_ap_netif_));
|
||||
}
|
||||
|
||||
wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
esp_err_t err = esp_wifi_init(&wifi_init_config);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to init Wi-Fi for setup AP: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
if (wifi_started_) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
|
||||
}
|
||||
|
||||
wifi_config_t ap_config = {};
|
||||
std::strncpy(reinterpret_cast<char*>(ap_config.ap.ssid), kSetupApSsid,
|
||||
sizeof(ap_config.ap.ssid) - 1);
|
||||
ap_config.ap.ssid_len = std::strlen(kSetupApSsid);
|
||||
ap_config.ap.channel = 1;
|
||||
ap_config.ap.authmode = WIFI_AUTH_OPEN;
|
||||
ap_config.ap.max_connection = 4;
|
||||
|
||||
err = esp_wifi_set_mode(WIFI_MODE_AP);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to set Wi-Fi AP mode: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
err = esp_wifi_set_config(WIFI_IF_AP, &ap_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to configure setup AP: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
err = esp_wifi_start();
|
||||
if (err != ESP_OK && err != ESP_ERR_WIFI_CONN) {
|
||||
ESP_LOGE(kTag, "failed to start setup AP: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
wifi_started_ = true;
|
||||
setup_ap_started_ = true;
|
||||
ESP_LOGI(kTag, "setup AP started ssid=%s ip=192.168.3.1", kSetupApSsid);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::configureStatusLed() {
|
||||
if (config_.status_led_gpio < 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
gpio_config_t io_config = {};
|
||||
io_config.pin_bit_mask = 1ULL << static_cast<uint32_t>(config_.status_led_gpio);
|
||||
io_config.mode = GPIO_MODE_OUTPUT;
|
||||
io_config.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
io_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_config.intr_type = GPIO_INTR_DISABLE;
|
||||
const esp_err_t err = gpio_config(&io_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to configure status LED GPIO%d: %s", config_.status_led_gpio,
|
||||
esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
setStatusLed(false);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::startHttpServer() {
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.server_port = config_.http_port;
|
||||
@@ -173,26 +365,27 @@ esp_err_t GatewayNetworkService::startHttpServer() {
|
||||
return err;
|
||||
}
|
||||
|
||||
httpd_uri_t info_uri = {};
|
||||
info_uri.uri = "/info";
|
||||
info_uri.method = HTTP_GET;
|
||||
info_uri.handler = &GatewayNetworkService::HandleInfoGet;
|
||||
info_uri.user_ctx = this;
|
||||
err = httpd_register_uri_handler(http_server_, &info_uri);
|
||||
struct Route {
|
||||
const char* uri;
|
||||
httpd_method_t method;
|
||||
esp_err_t (*handler)(httpd_req_t*);
|
||||
};
|
||||
|
||||
const Route routes[] = {
|
||||
{"/info", HTTP_GET, &GatewayNetworkService::HandleInfoGet},
|
||||
{"/dali/cmd", HTTP_GET, &GatewayNetworkService::HandleCommandGet},
|
||||
{"/dali/cmd", HTTP_POST, &GatewayNetworkService::HandleCommandPost},
|
||||
{"/led/1", HTTP_GET, &GatewayNetworkService::HandleLedOnGet},
|
||||
{"/led/0", HTTP_GET, &GatewayNetworkService::HandleLedOffGet},
|
||||
{"/jq.js", HTTP_GET, &GatewayNetworkService::HandleJqJsGet},
|
||||
};
|
||||
|
||||
for (const auto& route : routes) {
|
||||
err = RegisterUri(http_server_, route.uri, route.method, route.handler, this);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to register /info handler: %s", esp_err_to_name(err));
|
||||
ESP_LOGE(kTag, "failed to register %s handler: %s", route.uri, esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
httpd_uri_t command_uri = {};
|
||||
command_uri.uri = "/dali/cmd";
|
||||
command_uri.method = HTTP_POST;
|
||||
command_uri.handler = &GatewayNetworkService::HandleCommandPost;
|
||||
command_uri.user_ctx = this;
|
||||
err = httpd_register_uri_handler(http_server_, &command_uri);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to register /dali/cmd handler: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
@@ -220,6 +413,52 @@ void GatewayNetworkService::UdpTaskEntry(void* arg) {
|
||||
static_cast<GatewayNetworkService*>(arg)->udpTaskLoop();
|
||||
}
|
||||
|
||||
void GatewayNetworkService::HandleWifiEvent(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data) {
|
||||
auto* service = static_cast<GatewayNetworkService*>(arg);
|
||||
if (service != nullptr) {
|
||||
service->handleWifiEvent(event_base, event_id, event_data);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayNetworkService::handleWifiEvent(esp_event_base_t event_base, int32_t event_id,
|
||||
void* event_data) {
|
||||
if (!config_.wifi_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
const auto info = runtime_.deviceInfo();
|
||||
if (info.wlan.has_value() && !info.wlan->ssid.empty()) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_connect());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
auto info = runtime_.deviceInfo();
|
||||
if (info.wlan.has_value()) {
|
||||
const bool has_credentials = !info.wlan->ssid.empty();
|
||||
info.wlan->ip.clear();
|
||||
runtime_.setWirelessInfo(std::move(*info.wlan));
|
||||
if (has_credentials) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_connect());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP && event_data != nullptr) {
|
||||
auto* event = static_cast<ip_event_got_ip_t*>(event_data);
|
||||
char ip[16] = {0};
|
||||
esp_ip4addr_ntoa(&event->ip_info.ip, ip, sizeof(ip));
|
||||
WirelessInfo wireless = runtime_.deviceInfo().wlan.value_or(WirelessInfo{});
|
||||
wireless.ip = ip;
|
||||
runtime_.setWirelessInfo(std::move(wireless));
|
||||
ESP_LOGI(kTag, "Wi-Fi got IP %s", ip);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayNetworkService::udpTaskLoop() {
|
||||
udp_socket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (udp_socket_ < 0) {
|
||||
@@ -285,6 +524,39 @@ void GatewayNetworkService::handleGatewayNotification(const std::vector<uint8_t>
|
||||
reinterpret_cast<const sockaddr*>(&remote_addr), remote_addr_len);
|
||||
}
|
||||
|
||||
void GatewayNetworkService::handleWifiControl(uint8_t mode) {
|
||||
if (mode == 0) {
|
||||
config_.wifi_enabled = false;
|
||||
if (wifi_started_) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
|
||||
wifi_started_ = false;
|
||||
setup_ap_started_ = false;
|
||||
}
|
||||
auto info = runtime_.deviceInfo();
|
||||
if (info.wlan.has_value()) {
|
||||
info.wlan->ip.clear();
|
||||
runtime_.setWirelessInfo(std::move(*info.wlan));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == 101) {
|
||||
config_.wifi_enabled = true;
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(startSetupAp());
|
||||
return;
|
||||
}
|
||||
if (mode == 1) {
|
||||
config_.wifi_enabled = true;
|
||||
if (setup_ap_started_) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
|
||||
wifi_started_ = false;
|
||||
setup_ap_started_ = false;
|
||||
}
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(startWifi());
|
||||
}
|
||||
}
|
||||
|
||||
std::string GatewayNetworkService::deviceInfoJson() const {
|
||||
const auto info = runtime_.deviceInfo();
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
@@ -332,6 +604,61 @@ std::string GatewayNetworkService::deviceInfoDoubleEncodedJson() const {
|
||||
return rendered;
|
||||
}
|
||||
|
||||
std::string GatewayNetworkService::gatewaySnapshotJson() {
|
||||
const auto snapshot = controller_.snapshot();
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
if (root == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
cJSON_AddNumberToObject(root, "count", static_cast<double>(snapshot.channels.size()));
|
||||
|
||||
cJSON* gw_info = cJSON_CreateObject();
|
||||
if (gw_info != nullptr) {
|
||||
cJSON_AddBoolToObject(gw_info, "setupMode", snapshot.setup_mode);
|
||||
cJSON_AddBoolToObject(gw_info, "bleEnabled", snapshot.ble_enabled);
|
||||
cJSON_AddBoolToObject(gw_info, "wifiEnabled", snapshot.wifi_enabled);
|
||||
cJSON_AddBoolToObject(gw_info, "IPRouter", snapshot.ip_router_enabled);
|
||||
cJSON_AddBoolToObject(gw_info, "iSceneEnabled", snapshot.internal_scene_supported);
|
||||
cJSON_AddBoolToObject(gw_info, "iGroupEnabled", snapshot.internal_group_supported);
|
||||
cJSON_AddItemToObject(root, "gwInfo", gw_info);
|
||||
}
|
||||
|
||||
cJSON* channels = cJSON_CreateArray();
|
||||
if (channels != nullptr) {
|
||||
for (const auto& channel : snapshot.channels) {
|
||||
cJSON* item = cJSON_CreateObject();
|
||||
if (item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
cJSON_AddNumberToObject(item, "channel", channel.channel_index + 1);
|
||||
cJSON_AddNumberToObject(item, "gw", channel.gateway_id);
|
||||
cJSON_AddStringToObject(item, "name", channel.name.c_str());
|
||||
cJSON_AddStringToObject(item, "phy", channel.phy.c_str());
|
||||
cJSON_AddNumberToObject(item, "sceneMaskLow", channel.scene_mask_low);
|
||||
cJSON_AddNumberToObject(item, "sceneMaskHigh", channel.scene_mask_high);
|
||||
cJSON_AddNumberToObject(item, "groupMaskLow", channel.group_mask_low);
|
||||
cJSON_AddNumberToObject(item, "groupMaskHigh", channel.group_mask_high);
|
||||
cJSON_AddBoolToObject(item, "isAllocAddr", channel.allocating);
|
||||
cJSON_AddNumberToObject(item, "lastAllocAddr", channel.last_alloc_addr);
|
||||
cJSON_AddItemToArray(channels, item);
|
||||
}
|
||||
cJSON_AddItemToObject(root, "channels", channels);
|
||||
}
|
||||
|
||||
const std::string rendered = PrintJson(root);
|
||||
cJSON_Delete(root);
|
||||
return rendered;
|
||||
}
|
||||
|
||||
void GatewayNetworkService::setStatusLed(bool on) {
|
||||
if (config_.status_led_gpio < 0) {
|
||||
return;
|
||||
}
|
||||
const bool level = config_.status_led_active_high ? on : !on;
|
||||
gpio_set_level(static_cast<gpio_num_t>(config_.status_led_gpio), level ? 1 : 0);
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::HandleInfoGet(httpd_req_t* req) {
|
||||
auto* service = static_cast<GatewayNetworkService*>(req->user_ctx);
|
||||
if (service == nullptr) {
|
||||
@@ -343,6 +670,17 @@ esp_err_t GatewayNetworkService::HandleInfoGet(httpd_req_t* req) {
|
||||
return httpd_resp_send(req, payload.data(), payload.size());
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::HandleCommandGet(httpd_req_t* req) {
|
||||
auto* service = static_cast<GatewayNetworkService*>(req->user_ctx);
|
||||
if (service == nullptr) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
const std::string payload = service->gatewaySnapshotJson();
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
return httpd_resp_send(req, payload.data(), payload.size());
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::HandleCommandPost(httpd_req_t* req) {
|
||||
auto* service = static_cast<GatewayNetworkService*>(req->user_ctx);
|
||||
if (service == nullptr) {
|
||||
@@ -376,4 +714,46 @@ esp_err_t GatewayNetworkService::HandleCommandPost(httpd_req_t* req) {
|
||||
return httpd_resp_sendstr(req, "ok");
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::HandleLedOnGet(httpd_req_t* req) {
|
||||
auto* service = static_cast<GatewayNetworkService*>(req->user_ctx);
|
||||
if (service == nullptr) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
service->setStatusLed(true);
|
||||
httpd_resp_set_type(req, "text/plain");
|
||||
return httpd_resp_sendstr(req, "ok");
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::HandleLedOffGet(httpd_req_t* req) {
|
||||
auto* service = static_cast<GatewayNetworkService*>(req->user_ctx);
|
||||
if (service == nullptr) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
service->setStatusLed(false);
|
||||
httpd_resp_set_type(req, "text/plain");
|
||||
return httpd_resp_sendstr(req, "ok");
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::HandleJqJsGet(httpd_req_t* req) {
|
||||
FILE* file = std::fopen("/jq.js", "rb");
|
||||
if (file == nullptr) {
|
||||
return httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Not Found/jq.js");
|
||||
}
|
||||
|
||||
httpd_resp_set_type(req, "application/javascript");
|
||||
char buffer[512] = {0};
|
||||
while (true) {
|
||||
const size_t read_len = std::fread(buffer, 1, sizeof(buffer), file);
|
||||
if (read_len > 0 && httpd_resp_send_chunk(req, buffer, read_len) != ESP_OK) {
|
||||
std::fclose(file);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (read_len < sizeof(buffer)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::fclose(file);
|
||||
return httpd_resp_send_chunk(req, nullptr, 0);
|
||||
}
|
||||
|
||||
} // namespace gateway
|
||||
@@ -312,14 +312,23 @@ GatewayRuntime::CommandDropReason GatewayRuntime::lastEnqueueDropReason() const
|
||||
}
|
||||
|
||||
void GatewayRuntime::setGatewayCount(size_t gateway_count) {
|
||||
LockGuard guard(command_lock_);
|
||||
gateway_count_ = gateway_count;
|
||||
}
|
||||
|
||||
void GatewayRuntime::setWirelessInfo(WirelessInfo info) {
|
||||
if (!info.ssid.empty() || !info.password.empty()) {
|
||||
bool should_persist_credentials = false;
|
||||
{
|
||||
LockGuard guard(command_lock_);
|
||||
should_persist_credentials = (!info.ssid.empty() || !info.password.empty()) &&
|
||||
(!wireless_info_.has_value() ||
|
||||
wireless_info_->ssid != info.ssid ||
|
||||
wireless_info_->password != info.password);
|
||||
wireless_info_ = info;
|
||||
}
|
||||
if (should_persist_credentials) {
|
||||
settings_.setWifiCredentials(info.ssid, info.password);
|
||||
}
|
||||
wireless_info_ = std::move(info);
|
||||
}
|
||||
|
||||
void GatewayRuntime::setCommandAddressResolver(
|
||||
@@ -333,6 +342,7 @@ void GatewayRuntime::setCommandAddressResolver(
|
||||
}
|
||||
|
||||
GatewayDeviceInfo GatewayRuntime::deviceInfo() const {
|
||||
LockGuard guard(command_lock_);
|
||||
GatewayDeviceInfo info;
|
||||
info.serial_id = config_.serial_id;
|
||||
info.type = GatewayCore::RoleToString(profile_.role);
|
||||
|
||||
Reference in New Issue
Block a user