feat: optional one-time Telegram notification on first boot - #177
feat: optional one-time Telegram notification on first boot#177joaosouz4dev wants to merge 1 commit into
Conversation
Add a build-time toggle and an idempotent NVS-guarded notifier that sends
a single "MimiClaw booted" message to a configured Telegram admin chat
on the first boot after flashing. Useful for confirming remotely that a
device came online after a power-cycle, OTA, or relocating it onto a
USB charger / power bank.
Config:
- mimi_config.h: MIMI_TG_SEND_FIRST_BOOT (1 to enable, 0 to disable)
- mimi_secrets.h: MIMI_SECRET_TG_ADMIN_CHAT_ID (target chat ID)
- NVS flag: MIMI_NVS_KEY_FIRST_BOOT_DONE in MIMI_NVS_TG namespace
Behaviour:
- No-op when the toggle is 0 or the chat ID is empty.
- Sends once per device lifetime (persisted in NVS). On send failure
the flag is left unset, so the next boot retries.
- Never blocks app_main: the call is intentionally not wrapped in
ESP_ERROR_CHECK and always returns ESP_OK to callers.
A reset_first_boot CLI command clears the NVS flag so the next boot
re-sends the notice without needing a full NVS erase.
README updated with the new secret and CLI command.
📝 WalkthroughWalkthroughThis PR adds an optional first-boot Telegram notification feature to the device. When enabled via configuration, the device sends a one-time "booted" message containing its IP address and timestamp to a Telegram admin chat, with persistence tracked via NVS. A new CLI command allows re-arming the notification for testing. ChangesFirst-boot Telegram notification feature
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@main/channels/telegram/telegram_bot.c`:
- Around line 611-616: The first-boot functions mishandle NVS persistence: in
telegram_send_first_boot_notice() and telegram_reset_first_boot_flag() check and
handle return values from nvs_set_u8 and nvs_commit instead of ignoring
them—verify nvs_set_u8(...) result, call nvs_commit(...) and check its esp_err_t
return, log failures with ESP_LOGI/ESP_LOGE including the error, and propagate
the persistence error as the function's return value (do not override err =
ESP_OK). Make these changes around the symbols telegram_send_first_boot_notice,
telegram_reset_first_boot_flag, nvs_set_u8, nvs_commit, telegram_send_message
and the key MIMI_NVS_KEY_FIRST_BOOT_DONE so that send/ reset only report success
when the NVS commit actually succeeds.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 847b1608-fa2d-478c-bfbd-e80d4d857bb8
📒 Files selected for processing (7)
README.mdmain/channels/telegram/telegram_bot.cmain/channels/telegram/telegram_bot.hmain/cli/serial_cli.cmain/mimi.cmain/mimi_config.hmain/mimi_secrets.h.example
| esp_err_t send_err = telegram_send_message(chat_id, msg); | ||
| if (send_err == ESP_OK) { | ||
| if (nvs_set_u8(nvs, MIMI_NVS_KEY_FIRST_BOOT_DONE, 1) == ESP_OK) { | ||
| nvs_commit(nvs); | ||
| } | ||
| ESP_LOGI(TAG, "First-boot notice sent to %s", chat_id); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="main/channels/telegram/telegram_bot.c"
# Show relevant regions around the referenced lines
sed -n '560,700p' "$FILE" | nl -ba | sed -n '1,200p'
# Extract the full functions if possible (find by signature)
rg -n "telegram_send_first_boot_notice|telegram_reset_first_boot_flag" "$FILE" -n
# Print from each function start to just after the function end (best-effort via awk on braces)
python3 - <<'PY'
import re,sys
path="main/channels/telegram/telegram_bot.c"
with open(path,'r',encoding='utf-8',errors='ignore') as f:
s=f.read()
def extract(fn):
# naive extraction: find signature line then take until matching braces
m=re.search(r'\b%s\s*\(' % re.escape(fn), s)
if not m:
print(f"NOT FOUND: {fn}")
return
start=s.rfind('\n',0,m.start())+1
# find first '{' after signature
i=s.find('{', m.start())
if i==-1:
print(f"NO BRACE FOR: {fn}")
return
depth=0
for j in range(i, len(s)):
if s[j]=='{': depth+=1
elif s[j]=='}':
depth-=1
if depth==0:
end=j+1
print(f"\n===== {fn} =====\n")
print(s[start:end])
return
print(f"UNTERMINATED: {fn}")
extract("telegram_send_first_boot_notice")
extract("telegram_reset_first_boot_flag")
PYRepository: memovai/mimiclaw
Length of output: 103
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="main/channels/telegram/telegram_bot.c"
echo "== Snippet 580-680 =="
sed -n '580,680p' "$FILE" | cat -n
echo
echo "== Snippet around 600-660 =="
sed -n '600,660p' "$FILE" | cat -n
echo
echo "== Functions (best-effort) via python brace matching =="
python3 - <<'PY'
import re
path="main/channels/telegram/telegram_bot.c"
with open(path,'r',encoding='utf-8',errors='ignore') as f:
s=f.read()
def line_of_index(idx):
return s[:idx].count('\n') + 1
def extract(fn):
# match function definition line with return type(s) + name + '('
m=re.search(r'\b' + re.escape(fn) + r'\s*\(', s)
if not m:
print(f"NOT FOUND: {fn}")
return
# backtrack to start of line
start = s.rfind('\n', 0, m.start())
start = 0 if start==-1 else start+1
# find first '{' after match
i=s.find('{', m.start())
if i==-1:
print(f"NO BRACE FOR: {fn}")
return
depth=0
for j in range(i, len(s)):
if s[j]=='{': depth+=1
elif s[j]=='}':
depth-=1
if depth==0:
end=j+1
print(f"\n===== {fn} (lines {line_of_index(start)}-{line_of_index(end-1)}) =====\n")
print(s[start:end])
return
print(f"UNTERMINATED: {fn}")
extract("telegram_send_first_boot_notice")
extract("telegram_reset_first_boot_flag")
PY
echo
echo "== Direct search for nvs_commit/nvs_set_u8/nvs_erase_key in this file =="
rg -n "nvs_commit\\(|nvs_set_u8\\(|nvs_erase_key\\(" "$FILE"Repository: memovai/mimiclaw
Length of output: 6671
Handle NVS set/commit failures for first-boot flag before reporting success.
telegram_send_first_boot_notice()(around lines 611-616) commits NVS without checkingnvs_commit()’s return value, logs"First-boot notice sent...", and returnsESP_OKeven if persistence fails.telegram_reset_first_boot_flag()(around lines 633-640) commits NVS without checkingnvs_commit()’s return value and then forceserr = ESP_OK, so it can log/return success when the reset wasn’t actually persisted.
Suggested fix
@@
esp_err_t send_err = telegram_send_message(chat_id, msg);
if (send_err == ESP_OK) {
- if (nvs_set_u8(nvs, MIMI_NVS_KEY_FIRST_BOOT_DONE, 1) == ESP_OK) {
- nvs_commit(nvs);
- }
- ESP_LOGI(TAG, "First-boot notice sent to %s", chat_id);
+ esp_err_t persist_err = nvs_set_u8(nvs, MIMI_NVS_KEY_FIRST_BOOT_DONE, 1);
+ if (persist_err == ESP_OK) {
+ persist_err = nvs_commit(nvs);
+ }
+ if (persist_err == ESP_OK) {
+ ESP_LOGI(TAG, "First-boot notice sent to %s", chat_id);
+ } else {
+ ESP_LOGW(TAG, "First-boot notice sent but flag persist failed: %s",
+ esp_err_to_name(persist_err));
+ }
@@
err = nvs_erase_key(nvs, MIMI_NVS_KEY_FIRST_BOOT_DONE);
if (err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND) {
- nvs_commit(nvs);
- err = ESP_OK;
+ esp_err_t commit_err = nvs_commit(nvs);
+ err = (commit_err == ESP_OK) ? ESP_OK : commit_err;
}
nvs_close(nvs);
- ESP_LOGI(TAG, "First-boot flag cleared; next boot will re-send notice");
+ if (err == ESP_OK) {
+ ESP_LOGI(TAG, "First-boot flag cleared; next boot will re-send notice");
+ } else {
+ ESP_LOGW(TAG, "Failed to persist first-boot flag reset: %s", esp_err_to_name(err));
+ }
return err;
}Also propagate the persistence error as the return value from telegram_send_first_boot_notice() (it currently always returns ESP_OK).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@main/channels/telegram/telegram_bot.c` around lines 611 - 616, The first-boot
functions mishandle NVS persistence: in telegram_send_first_boot_notice() and
telegram_reset_first_boot_flag() check and handle return values from nvs_set_u8
and nvs_commit instead of ignoring them—verify nvs_set_u8(...) result, call
nvs_commit(...) and check its esp_err_t return, log failures with
ESP_LOGI/ESP_LOGE including the error, and propagate the persistence error as
the function's return value (do not override err = ESP_OK). Make these changes
around the symbols telegram_send_first_boot_notice,
telegram_reset_first_boot_flag, nvs_set_u8, nvs_commit, telegram_send_message
and the key MIMI_NVS_KEY_FIRST_BOOT_DONE so that send/ reset only report success
when the NVS commit actually succeeds.
Summary
Adds a build-time toggle and an idempotent, NVS-guarded notifier that sends a single "MimiClaw booted" message to a configured Telegram admin chat the first time the device comes online after flashing.
Why this is useful:
How it works
MIMI_TG_SEND_FIRST_BOOTinmain/mimi_config.his the master toggle (defaults to1).MIMI_SECRET_TG_ADMIN_CHAT_IDinmimi_secrets.hholds the destination chat ID (defaults to empty — feature inert).MIMI_NVS_KEY_FIRST_BOOT_DONElives in the existingMIMI_NVS_TGnamespace and is set to1after a successful send, making subsequent boots silent.app_mainis intentionally not wrapped inESP_ERROR_CHECKand the function always returnsESP_OK, so no failure mode here can block boot.What's in the diff
main/mimi_config.h— newMIMI_TG_SEND_FIRST_BOOTflag, new NVS key, new optional secret fallback.main/mimi_secrets.h.example— newMIMI_SECRET_TG_ADMIN_CHAT_IDslot with inline guidance pointing to@userinfobot.main/channels/telegram/telegram_bot.{h,c}—telegram_send_first_boot_notice()(the guarded sender) andtelegram_reset_first_boot_flag()(test helper).main/mimi.c— single call aftertelegram_bot_start()in the boot sequence.main/cli/serial_cli.c—reset_first_bootCLI command to re-arm the notifier without erasing NVS.README.md— documents the new secret, the toggle, and the CLI command.No existing call sites or behaviours change. The notifier uses the public
telegram_send_message()API and the standardMIMI_NVS_TGnamespace pattern already used for the polling offset.Test plan
idf.py fullclean && idf.py buildidf.py -p <PORT> erase-flash flash monitorMIMI_SECRET_TG_ADMIN_CHAT_IDset andMIMI_TG_SEND_FIRST_BOOT=1: confirm a single Telegram message arrives once WiFi is upmimi> restart— no additional message (idempotent)mimi> reset_first_bootthenmimi> restart— message is sent againMIMI_TG_SEND_FIRST_BOOT 0, rebuild and erase-flash — no message ever sentMIMI_SECRET_TG_ADMIN_CHAT_IDempty with the flag enabled — boot continues normally with a single warn log, no crashSummary by CodeRabbit
New Features
Documentation