Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/buildprocess.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ Changing factory time zone setting is a common requirement. This requires a litt

Various settings support placeholder substitution, indicated by comments in [factory_settings.ini](https://github.com/theelims/ESP32-sveltekit/blob/main/factory_settings.ini). This can be particularly useful where settings need to be unique, such as the Access Point SSID or MQTT client id. The following placeholders are supported:

| Placeholder | Substituted value |
| ------------ | ---------------------------------------------------------------------------- |
| #{platform} | The microcontroller platform, e.g. "esp32" or "esp32c3" |
| #{unique_id} | A unique identifier derived from the MAC address, e.g. "~~0b0a859d~~6816" 🌙 |
| #{random} | A random number encoded as a hex string, e.g. "55722f94" |
| Placeholder | Substituted value |
| ------------ | ----------------------------------------------------------------------------- |
| #{platform} | The platform identifier "ml-" (MoonLight), used in hostnames and MQTT topics🌙|
| #{unique_id} | A unique identifier derived from the MAC address, e.g. "~~0b0a859d~~6816" 🌙 |
| #{random} | A random number encoded as a hex string, e.g. "55722f94" |

## Other Build Flags

Expand Down
2 changes: 0 additions & 2 deletions docs/develop/sveltekit.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ This is a checklist, More info on most of the items can be found in the ESP32-Sv
* add {custom}-logo.png (used in mkdocs.yml)
* replace favicon.png
* factory_settings.ini
* FACTORY_AP_SSID=\"{custom}-#{unique_id}\"
* FACTORY_AP_PASSWORD=\"\" (recommendation)
* FACTORY_NTP_TIME_ZONE_LABEL=\"Europe/Berlin\"
* package.json
Expand All @@ -97,7 +96,6 @@ This is a checklist, More info on most of the items can be found in the ESP32-Sv
* interface/static/favicon.png
* replace favicon.png
* lib/framework/APSettingsService.h
* FACTORY_AP_SSID "{custom}-#{unique_id}"
* FACTORY_AP_PASSWORD ""
* mkdocs.yml
* site_name: {custom}
Expand Down
2 changes: 1 addition & 1 deletion docs/gettingstarted/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Keep this page visible until installation complete.

!!! tip "Hostname"

The hostname will be used to access your device. E.g. if the hostname is ML-home you can access it using [http://ml-home.local](http://ml-home.local)
The hostname will be used to access your device. E.g. if the hostname is ml-home you can access it using [http://ml-home.local](http://ml-home.local)

!!! tip "Track analytics"

Expand Down
20 changes: 20 additions & 0 deletions docs/network/mqtt.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# MQTT

<img width="320" src="https://github.com/user-attachments/assets/51e336fe-4935-4135-a9f8-bf469c67de8e" />

## Home Assistant setup

In Home Assistant

* create non admin user in home assistant e.g mqttuser
* install the Mosquitto MQTT Broker via Settings / Devices & Services, it will auto-discover the MoonLight devices
* Optionally enable TLS (Transport Layer Security) for secure connections in MQTT / Configure

In MoonLight / Network / MQTT:

* Enable MQTT
* URI: mqtt://homeassistant.local:1883 (non secure)
* replace homeassistant.local by it appropriate mdns name or IP address
* secure HA: use mqtts://homeassistant.local:8883
* to test outsde HA e.g. use mqtts://broker.hivemq.com:8883\
* Username / password: mqttuser credentials
* client id: ml-xxxx.
* Apply Settings

16 changes: 8 additions & 8 deletions docs/statefulservice.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,19 +403,19 @@ In case of a websocket connection the JWT token is supplied as a search paramete

Various settings support placeholder substitution, indicated by comments in [factory_settings.ini](https://github.com/theelims/ESP32-sveltekit/blob/main/factory_settings.ini). This can be particularly useful where settings need to be unique, such as the Access Point SSID or MQTT client id. Strings must be properly escaped in the ini-file. The following placeholders are supported:

| Placeholder | Substituted value |
| ------------ | ---------------------------------------------------------------------------- |
| #{platform} | The microcontroller platform, e.g. "esp32" or "esp32c3" |
| #{unique_id} | A unique identifier derived from the MAC address, e.g. "~~0b0a859d~~6816" 🌙 |
| #{random} | A random number encoded as a hex string, e.g. "55722f94" |
| Placeholder | Substituted value |
| ------------ | ----------------------------------------------------------------------------- |
| #{platform} | The platform identifier "ml-" (MoonLight), used in hostnames and MQTT topics🌙|
| #{unique_id} | A unique identifier derived from the MAC address, e.g. "~~0b0a859d~~6816" 🌙 |
| #{random} | A random number encoded as a hex string, e.g. "55722f94" |

You may use SettingValue::format in your own code if you require the use of these placeholders. This is demonstrated in the demo project:

```cpp
static StateUpdateResult update(JsonObject& root, LightMqttSettings& settings) {
settings.mqttPath = root["mqtt_path"] | SettingValue::format("homeassistant/light/#{unique_id}");
settings.name = root["name"] | SettingValue::format("light-#{unique_id}");
settings.uniqueId = root["unique_id"] | SettingValue::format("light-#{unique_id}");
settings.mqttPath = root["mqtt_path"] | SettingValue::format("homeassistant/#{platform}/#{unique_id}");
settings.name = root["name"] | SettingValue::format("#{platform}-#{unique_id}");
settings.uniqueId = root["unique_id"] | SettingValue::format("#{platform}-#{unique_id}");
return StateUpdateResult::CHANGED;
}
```
Expand Down
10 changes: 5 additions & 5 deletions factory_settings.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
; The indicated settings support placeholder substitution as follows:
;
; #{platform} - The microcontroller platform, e.g. "esp32" or "esp32c3"
; #{platform} - The platform identifier "ml-" (MoonLight), used in hostnames and MQTT topics
; #{unique_id} - A unique identifier derived from the MAC address, e.g. "0b0a859d6816"
; #{random} - A random number encoded as a hex string, e.g. "55722f94"

Expand All @@ -9,12 +9,12 @@ build_flags =
; WiFi settings
-D FACTORY_WIFI_SSID=\"\"
-D FACTORY_WIFI_PASSWORD=\"\"
-D FACTORY_WIFI_HOSTNAME=\"ML-#{unique_id}\" ; supports placeholders
-D FACTORY_WIFI_HOSTNAME=\"#{platform}-#{unique_id}\" ; supports placeholders
-D FACTORY_WIFI_RSSI_THRESHOLD=-80 ; dBm, -80 is a good value for most applications

; Access point settings
-D FACTORY_AP_PROVISION_MODE=AP_MODE_DISCONNECTED
-D FACTORY_AP_SSID=\"ML-#{unique_id}\" ; 1-64 characters, supports placeholders
-D FACTORY_AP_SSID=\"#{platform}-#{unique_id}\" ; 1-64 characters, supports placeholders
-D FACTORY_AP_PASSWORD=\"\" ; 8-64 characters ; 🌙 empty no password needed
-D FACTORY_AP_CHANNEL=6 ; // 🌙: See https://github.com/wled/WLED/commit/fc7993f4a7241739d1878df25fd4600100abc506
-D FACTORY_AP_SSID_HIDDEN=false
Expand All @@ -37,13 +37,13 @@ build_flags =

; MQTT settings
-D FACTORY_MQTT_ENABLED=false
-D FACTORY_MQTT_URI=\"mqtts://broker.hivemq.com:8883\"
-D FACTORY_MQTT_URI=\"mqtt://homeassistant.local:1883\"
-D FACTORY_MQTT_USERNAME=\"\" ; supports placeholders
-D FACTORY_MQTT_PASSWORD=\"\"
-D FACTORY_MQTT_CLIENT_ID=\"#{platform}-#{unique_id}\" ; supports placeholders
-D FACTORY_MQTT_KEEP_ALIVE=120
-D FACTORY_MQTT_CLEAN_SESSION=true
-D FACTORY_MQTT_STATUS_TOPIC=\"esp32sveltekit/#{unique_id}/status\" ; supports placeholders
-D FACTORY_MQTT_STATUS_TOPIC=\"light/#{unique_id}/status\" ; supports placeholders
-D FACTORY_MQTT_MIN_MESSAGE_INTERVAL_MS=0

; JWT Secret
Expand Down
2 changes: 1 addition & 1 deletion lib/framework/EthernetSettingsService.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#include <vector>

#ifndef FACTORY_ETHERNET_HOSTNAME
#define FACTORY_ETHERNET_HOSTNAME "ML-#{unique_id}" // 🌙 use ML
#define FACTORY_ETHERNET_HOSTNAME "#{platform}-#{unique_id}"
#endif

#define ETHERNET_EVENT_DELAY 500
Expand Down
2 changes: 1 addition & 1 deletion lib/framework/MqttEndpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ class MqttEndpoint : public MqttCommitHandler
{
if (_subTopic.length() > 0)
{
_mqttClient->subscribe(_subTopic.c_str(), 2);
_mqttClient->subscribe(_subTopic.c_str(), 2); // 🌙 Home Assistant / Mosquitto does not benefit from QoS 2, 1 might be better, add as UI variable?
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion lib/framework/MqttSettingsService.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
#endif

#ifndef FACTORY_MQTT_STATUS_TOPIC
#define FACTORY_MQTT_STATUS_TOPIC "#{platform}/#{unique_id}/status"
#define FACTORY_MQTT_STATUS_TOPIC "light/#{unique_id}/status"
#endif

#ifndef FACTORY_MQTT_KEEP_ALIVE
Expand Down
2 changes: 1 addition & 1 deletion lib/framework/SettingValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

namespace SettingValue
{
const String PLATFORM = "esp32";
const String PLATFORM = "ml"; // 🌙

/**
* Returns a new string after replacing each instance of the pattern with a value generated by calling the provided
Expand Down
10 changes: 5 additions & 5 deletions lib/framework/WiFiSettingsService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,15 @@ void WiFiSettingsService::reconfigureWiFiConnection()
//logging:
// [0;32mI (5079) 🐼: Reconfiguring WiFi connection to: STRENGTH
// I (5079) 🐼: Reconfiguring WiFi TxPower to: 34
// [0;32mI (5079) 🐼: Hostname: ML-you35
// [0;32mI (5079) 🐼: Hostname: ml-you35
// ...
// [0;32mI (16438) 🐼: 20 networks found.
// [0;32mI (16439) 🐼: Connecting to strongest network: ewtr, BSSID: 92:5a:9e:0e:cc:e4
// D (16440) 🐼: Connecting to SSID: ewtr, Channel: 11, BSSID: 92:5a:9e:0e:cc:e4, Hostname: ML-you35
// D (16440) 🐼: Connecting to SSID: ewtr, Channel: 11, BSSID: 92:5a:9e:0e:cc:e4, Hostname: ml-you35
// [0;32mI (16451) 🐼: WiFi setTxPower to: 34
// [0;32mI (16612) 🐼: WiFi Connected.
// ...
// [0;32mI (18124) 🐼: WiFi Got IP. localIP=http://192.168.1.105, hostName=http://ML-you35.local
// [0;32mI (18124) 🐼: WiFi Got IP. localIP=http://192.168.1.105, hostName=http://ml-you35.local

#else
//by @troyhacks, needed to make P4 networking work (AP and STA)
Expand All @@ -144,13 +144,13 @@ void WiFiSettingsService::reconfigureWiFiConnection()
// E (7253) system_api: 0 mac type is incorrect (not found)
// mI (7254) 🐼: Reconfiguring WiFi connection to: STRENGTH
// [0;32mI (7254) 🐼: Reconfiguring WiFi TxPower to: 0
// 32mI (7259) 🐼: Hostname: ML-P4
// 32mI (7259) 🐼: Hostname: ml-P4
// ...
// [0;32mI (19737) 🐼: WiFi Connected.
// [0;32mI (20761) 🐼: WiFi Got IP. localIP=http://192.168.1.188, hostName=http://esp32p4-E1E3E7.local

// findings:
// mac type is incorrect ??? No 20 networks found. ???, wrong hostname displayed (but the hostname ML-P4.local works!)
// mac type is incorrect ??? No 20 networks found. ???, wrong hostname displayed (but the hostname ml-P4.local works!)
// deleting the Saved network: it still connects (still stored on the C5 chip???)
// erase flash: Wifi still connects!!

Expand Down
2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ build_flags =
-D BUILD_TARGET=\"$PIOENV\"
-D APP_NAME=\"MoonLight\" ; 🌙 Must only contain characters from [a-zA-Z0-9-_] as this is converted into a filename
-D APP_VERSION=\"0.7.1\" ; semver compatible version string
-D APP_DATE=\"20251229\" ; 🌙
-D APP_DATE=\"20251230\" ; 🌙

-D PLATFORM_VERSION=\"pioarduino-55.03.35\" ; 🌙 make sure it matches with above plaftform

Expand Down
76 changes: 74 additions & 2 deletions src/MoonLight/Modules/ModuleLightsControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,35 @@
#include "MoonBase/Modules/FileManager.h"
#include "MoonBase/Utilities.h" //for isInPSRAM

// Convert ModuleLightsControl state -> Home Assistant JSON
void readMQTT(ModuleState& state, JsonObject& root) {
root["state"] = state.data["lightsOn"].as<bool>() ? "ON" : "OFF";
root["brightness"] = state.data["brightness"].as<uint8_t>();
String js;
serializeJson(root, js);
EXT_LOGD(ML_TAG, "Read HA %s", js.c_str());
}

// Convert Home Assistant JSON -> ModuleLightsControl state updates
StateUpdateResult updateMQTT(JsonObject& root, ModuleState& state, const String& originId) {
String js;
serializeJson(root, js);
EXT_LOGD(ML_TAG, "Update HA %s", js.c_str());
JsonDocument doc;
JsonObject newState = doc.to<JsonObject>();

if (!root["state"].isNull()) newState["lightsOn"] = (root["state"] == "ON");
if (!root["brightness"].isNull()) newState["brightness"] = root["brightness"].as<uint8_t>();

return ModuleState::update(newState, state, originId);
}

class ModuleLightsControl : public Module {
private:
MqttEndpoint<ModuleState> _mqttEndpoint;
PsychicMqttClient* _mqttClient;
MqttSettingsService* _mqttSettingsService;

public:
PsychicHttpServer* _server;
FileManager* _fileManager;
Expand All @@ -29,7 +57,11 @@ class ModuleLightsControl : public Module {
uint8_t pinPushButtonLightsOn = UINT8_MAX;
uint8_t pinToggleButtonLightsOn = UINT8_MAX;

ModuleLightsControl(PsychicHttpServer* server, ESP32SvelteKit* sveltekit, FileManager* fileManager, ModuleIO* moduleIO) : Module("lightscontrol", server, sveltekit) {
ModuleLightsControl(PsychicHttpServer* server, ESP32SvelteKit* sveltekit, FileManager* fileManager, ModuleIO* moduleIO)
: Module("lightscontrol", server, sveltekit), //
_mqttEndpoint(readMQTT, updateMQTT, this, sveltekit->getMqttClient()),
_mqttClient(sveltekit->getMqttClient()),
_mqttSettingsService(sveltekit->getMqttSettingsService()) {
EXT_LOGV(ML_TAG, "constructor");
_server = server;
_fileManager = fileManager;
Expand Down Expand Up @@ -68,6 +100,46 @@ class ModuleLightsControl : public Module {
});
moduleIO.addUpdateHandler([this](const String& originId) { readPins(); }, false);
readPins(); // initially

// configure MQTT callback
_mqttClient->onConnect(std::bind(&ModuleLightsControl::registerConfig, this));
}

void registerConfig() {
if (!_mqttClient->connected()) {
return;
}
String configTopic;
String subTopic;
String pubTopic;

String settingsMqttPath = SettingValue::format("homeassistant/light/#{platform}-#{unique_id}"); // currently configured as a homeassistent light type
String settingsName = SettingValue::format("#{platform}-#{unique_id}");
String settingsUniqueId = SettingValue::format("#{platform}-#{unique_id}");
String settingsStateTopic = SettingValue::format(FACTORY_MQTT_STATUS_TOPIC);

JsonDocument doc;
configTopic = settingsMqttPath + "/config";
subTopic = settingsMqttPath + "/set";
pubTopic = settingsMqttPath + "/state";
doc["~"] = settingsMqttPath;
doc["name"] = settingsName;
doc["unique_id"] = settingsUniqueId;

doc["cmd_t"] = "~/set";
doc["stat_t"] = "~/state";
doc["schema"] = "json";
doc["brightness"] = true;

_mqttSettingsService->setStatusTopic(settingsStateTopic);

String payload;
serializeJson(doc, payload);
_mqttClient->publish(configTopic.c_str(), 0, false, payload.c_str());

_mqttEndpoint.configureTopics(pubTopic, subTopic);

EXT_LOGI(ML_TAG, "Published HA discovery to %s", configTopic.c_str());
}

void readPins() {
Expand Down Expand Up @@ -373,7 +445,7 @@ class ModuleLightsControl : public Module {
});
} else if (isPositions == 0 && layerP.lights.header.nrOfLights) { // send to UI
static unsigned long monitorMillis = 0;
if (millis() - monitorMillis >= MAX(20, layerP.lights.header.nrOfLights / 300)) { // 12K lights -> 40ms
if (millis() - monitorMillis >= MAX(20, layerP.lights.header.nrOfLights / 300)) { // 12K lights -> 40ms
monitorMillis = millis();

read([&](ModuleState& _state) {
Expand Down