Skip to content

Commit d1a3856

Browse files
authored
Merge pull request #603 from BrentIO/fix/602
Call mqttClient.loop() during OTA download to prevent MQTT offline, and broadcast update percentage over MQTT
2 parents bc84e15 + 539ee66 commit d1a3856

2 files changed

Lines changed: 119 additions & 25 deletions

File tree

Controller/Controller.ino

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ bool _otaCheckFailed = false; /* Set by onError during checkForUpdate(
116116
int _otaCheckErrorCode = 0; /* Error code captured by onError during checkForUpdate() */
117117
JsonDocument _otaPendingDoc;
118118
static char _otaCurrentPartition[8] = "";
119+
static char _otaLatestVersion[32] = "";
120+
static char _otaReleaseUrl[256] = "";
121+
static int _otaLastPublishedPercentage = -1;
119122
uint64_t _otaLastCheckedTime = 0;
120123

121124
fs::LittleFSFS uiFS;
@@ -3584,6 +3587,25 @@ void setup_OtaFirmware(){
35843587
eventLog.createEvent(msg, EventLog::LOG_LEVEL_INFO);
35853588
}
35863589
oled.setProgressBar((float)written / (float)total);
3590+
int pct = (int)((float)written / (float)total * 100);
3591+
if(pct != _otaLastPublishedPercentage && deviceIdentity.enabled && mqttClient.connected()){
3592+
_otaLastPublishedPercentage = pct;
3593+
JsonDocument mqttDoc;
3594+
mqttDoc["installed_version"] = VERSION;
3595+
mqttDoc["latest_version"] = _otaLatestVersion;
3596+
if(strlen(_otaReleaseUrl) > 0){ mqttDoc["release_url"] = _otaReleaseUrl; }
3597+
mqttDoc["in_progress"] = true;
3598+
mqttDoc["update_percentage"] = pct;
3599+
char title[32];
3600+
snprintf(title, sizeof(title), "Updating %s", _otaCurrentPartition);
3601+
mqttDoc["title"] = title;
3602+
char topic[MQTT_TOPIC_UPDATE_STATE_PATTERN_LENGTH+1];
3603+
snprintf(topic, sizeof(topic), MQTT_TOPIC_UPDATE_STATE_PATTERN, deviceIdentity.data.uuid);
3604+
char buffer[384];
3605+
serializeJson(mqttDoc, buffer, sizeof(buffer));
3606+
mqttClient.publish(topic, buffer);
3607+
}
3608+
mqttClient.loop();
35873609
});
35883610

35893611
otaFirmware.onPartitionComplete([](const char* partition, bool success){
@@ -3614,6 +3636,9 @@ void setup_OtaFirmware(){
36143636
});
36153637

36163638
otaFirmware.onAvailable([](const char* version, const char* app, const char* releaseUrl){
3639+
strlcpy(_otaLatestVersion, version, sizeof(_otaLatestVersion));
3640+
strlcpy(_otaReleaseUrl, releaseUrl ? releaseUrl : "", sizeof(_otaReleaseUrl));
3641+
_otaLastPublishedPercentage = -1;
36173642
if(deviceIdentity.enabled == false){ return; }
36183643
if(!mqttClient.connected()){ return; }
36193644
eventLog.createEvent("OTA update available");
@@ -3690,6 +3715,25 @@ void otaFirmware_checkPending(){
36903715
eventLog.createEvent(msg, EventLog::LOG_LEVEL_INFO);
36913716
}
36923717
oled.setProgressBar((float)written / (float)total);
3718+
int pct = (int)((float)written / (float)total * 100);
3719+
if(pct != _otaLastPublishedPercentage && deviceIdentity.enabled && mqttClient.connected()){
3720+
_otaLastPublishedPercentage = pct;
3721+
JsonDocument mqttDoc;
3722+
mqttDoc["installed_version"] = VERSION;
3723+
mqttDoc["latest_version"] = _otaLatestVersion;
3724+
if(strlen(_otaReleaseUrl) > 0){ mqttDoc["release_url"] = _otaReleaseUrl; }
3725+
mqttDoc["in_progress"] = true;
3726+
mqttDoc["update_percentage"] = pct;
3727+
char title[32];
3728+
snprintf(title, sizeof(title), "Updating %s", _otaCurrentPartition);
3729+
mqttDoc["title"] = title;
3730+
char topic[MQTT_TOPIC_UPDATE_STATE_PATTERN_LENGTH+1];
3731+
snprintf(topic, sizeof(topic), MQTT_TOPIC_UPDATE_STATE_PATTERN, deviceIdentity.data.uuid);
3732+
char buffer[384];
3733+
serializeJson(mqttDoc, buffer, sizeof(buffer));
3734+
mqttClient.publish(topic, buffer);
3735+
}
3736+
mqttClient.loop();
36933737
});
36943738

36953739
otaFirmware.onPartitionComplete([](const char* partition, bool success){
@@ -3720,16 +3764,20 @@ void otaFirmware_checkPending(){
37203764
});
37213765

37223766
eventLog.createEvent("OTA update available");
3767+
const char* targetVersion = _otaPendingDoc["version"] | VERSION;
3768+
const char* releaseUrl = _otaPendingDoc["release_url"] | "";
3769+
strlcpy(_otaLatestVersion, targetVersion, sizeof(_otaLatestVersion));
3770+
strncat(_otaLatestVersion, " (Forced)", sizeof(_otaLatestVersion) - strlen(_otaLatestVersion) - 1);
3771+
strlcpy(_otaReleaseUrl, releaseUrl, sizeof(_otaReleaseUrl));
3772+
_otaLastPublishedPercentage = -1;
37233773
if(deviceIdentity.enabled && mqttClient.connected()){
37243774
JsonDocument mqttDoc;
37253775
mqttDoc["installed_version"] = VERSION;
3726-
const char* targetVersion = _otaPendingDoc["version"] | VERSION;
37273776
mqttDoc["latest_version"] = targetVersion;
3728-
const char* releaseUrl = _otaPendingDoc["release_url"] | "";
37293777
if(releaseUrl && strlen(releaseUrl) > 0){
37303778
mqttDoc["release_url"] = releaseUrl;
37313779
}
3732-
mqttDoc["in_progress"] = false;
3780+
mqttDoc["in_progress"] = true;
37333781
char topic[MQTT_TOPIC_UPDATE_STATE_PATTERN_LENGTH+1];
37343782
snprintf(topic, sizeof(topic), MQTT_TOPIC_UPDATE_STATE_PATTERN, deviceIdentity.data.uuid);
37353783
mqttClient.beginPublish(topic, measureJson(mqttDoc), true);

Controller/asyncapi.yaml

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,17 +1130,17 @@ components:
11301130
$ref: "#/components/schemas/updateAvailablePayload"
11311131
examples:
11321132
- name: update-available
1133+
summary: A newer firmware version is available
11331134
payload:
1134-
installed_version: "2025.04.01"
1135-
latest_version: "2025.05.01"
1136-
title: "FireFly Controller 2025.05.01"
1137-
release_url: "https://docs.fireflylx.com/releases/2025.05.01"
1135+
installed_version: "2026.04.01"
1136+
latest_version: "2026.06.21"
1137+
release_url: "https://github.com/BrentIO/FireFly-Controller/releases/tag/2026.06.21"
11381138
in_progress: false
1139-
update_percentage: null
1140-
- name: no-update
1139+
- name: up-to-date
1140+
summary: Device is running the latest firmware
11411141
payload:
1142-
installed_version: "2025.05.01"
1143-
latest_version: "2025.05.01"
1142+
installed_version: "2026.06.21"
1143+
latest_version: "2026.06.21"
11441144
in_progress: false
11451145

11461146
updateInProgress:
@@ -1151,15 +1151,34 @@ components:
11511151
payload:
11521152
$ref: "#/components/schemas/updateInProgressPayload"
11531153
examples:
1154-
- name: started
1154+
- name: flashing-ui
1155+
summary: UI partition download underway
11551156
payload:
1157+
installed_version: "2026.04.01"
1158+
latest_version: "2026.06.21"
1159+
release_url: "https://github.com/BrentIO/FireFly-Controller/releases/tag/2026.06.21"
1160+
title: "Updating ui"
11561161
in_progress: true
1157-
update_percentage: 0
1158-
- name: mid-way
1162+
update_percentage: 23
1163+
- name: flashing-firmware
1164+
summary: Firmware partition download underway
11591165
payload:
1166+
installed_version: "2026.04.01"
1167+
latest_version: "2026.06.21"
1168+
release_url: "https://github.com/BrentIO/FireFly-Controller/releases/tag/2026.06.21"
1169+
title: "Updating app"
11601170
in_progress: true
1161-
update_percentage: 4200
1171+
update_percentage: 67
1172+
- name: forced-in-progress
1173+
summary: Forced OTA install — same installed and target version
1174+
payload:
1175+
installed_version: "2026.06.21"
1176+
latest_version: "2026.06.21 (Forced)"
1177+
title: "Updating app"
1178+
in_progress: true
1179+
update_percentage: 42
11621180
- name: failed
1181+
summary: OTA install failed
11631182
payload:
11641183
in_progress: false
11651184

@@ -1350,14 +1369,14 @@ components:
13501369
- "2025.04.01"
13511370
latest_version:
13521371
type: string
1353-
description: Latest available firmware version
1372+
description: >
1373+
Latest available firmware version. For forced OTA installs the
1374+
suffix " (Forced)" is appended (e.g., "2026.06.21 (Forced)") so
1375+
that Home Assistant detects a version difference even when the
1376+
installed and target versions are identical.
13541377
examples:
13551378
- "2025.05.01"
1356-
title:
1357-
type: string
1358-
description: Human-readable release title (optional)
1359-
examples:
1360-
- "FireFly Controller 2025.05.01"
1379+
- "2025.05.01 (Forced)"
13611380
release_url:
13621381
type: string
13631382
description: URL to the full release notes (optional)
@@ -1369,8 +1388,9 @@ components:
13691388
- integer
13701389
- "null"
13711390
description: >
1372-
Install progress as an integer where the value is percentage * 100
1373-
(e.g., 4200 = 42%). Null when no install is in progress.
1391+
Install progress as an integer from 0 to 100 representing the
1392+
percentage of the current partition downloaded
1393+
(e.g., 42 = 42%). Null when no install is in progress.
13741394
required:
13751395
- installed_version
13761396
- latest_version
@@ -1380,14 +1400,40 @@ components:
13801400
type: object
13811401
description: Firmware OTA install progress or failure notification
13821402
properties:
1403+
installed_version:
1404+
type: string
1405+
description: Currently installed firmware version
1406+
examples:
1407+
- "2025.04.01"
1408+
latest_version:
1409+
type: string
1410+
description: >
1411+
Target firmware version. For forced OTA installs the suffix
1412+
" (Forced)" is appended (e.g., "2026.06.21 (Forced)").
1413+
examples:
1414+
- "2025.05.01"
1415+
- "2025.05.01 (Forced)"
1416+
release_url:
1417+
type: string
1418+
description: URL to the full release notes (optional)
1419+
title:
1420+
type: string
1421+
description: >
1422+
Label indicating which partition is currently being flashed,
1423+
e.g. "Updating app" or "Updating ui". Present only when
1424+
in_progress is true.
1425+
examples:
1426+
- "Updating app"
1427+
- "Updating ui"
13831428
in_progress:
13841429
type: boolean
13851430
description: Whether the OTA update is still in progress. False indicates failure or completion.
13861431
update_percentage:
13871432
type: integer
13881433
description: >
1389-
Install progress as an integer where the value is percentage * 100
1390-
(e.g., 4200 = 42%). Present only when in_progress is true.
1434+
Install progress as an integer from 0 to 100 representing the
1435+
percentage of the current partition downloaded
1436+
(e.g., 42 = 42%). Present only when in_progress is true.
13911437
required:
13921438
- in_progress
13931439

0 commit comments

Comments
 (0)