{
  "id": "mclite",
  "name": "MCLite",
  "type": "fork",
  "maintainer": "laserir",
  "description": "A lightweight off-grid communicator firmware for the LilyGo T-Deck Plus and T-Watch Ultra. Purpose-built for emergency and outdoor communication — no internet, no cell towers needed. SD card configuration, web flasher, and room server support. Compatible with MeshCore apps.\n",
  "repository": "https://github.com/laserir/MCLite",
  "license": "MIT",
  "status": "active",
  "lifecycle": "active",
  "maturity": "beta",
  "distribution": "community",
  "lineage": {
    "kind": "fork",
    "upstreamFirmwareId": "meshcore-official",
    "upstreamRepository": "https://github.com/meshcore-dev/MeshCore"
  },
  "runtime": {
    "framework": "arduino",
    "language": "cpp"
  },
  "roles": [
    "companion"
  ],
  "features": [
    "Web flasher (Chrome/Edge)",
    "SD card configuration (one person sets up, copies to everyone)",
    "Room server support (up to 8)",
    "On-screen keyboard",
    "GPS support",
    "No accounts, no pairing, no per-device setup"
  ],
  "capabilities": {
    "protocol": {
      "meshcoreCompatible": true
    },
    "transports": {
      "ble": true,
      "usbSerial": true,
      "nativeTcp": false,
      "wifiAp": false
    },
    "operations": {
      "ota": false,
      "webFlasher": true
    },
    "networking": {
      "repeater": false,
      "roomServer": false,
      "observer": false,
      "kissModem": false
    },
    "hardware": {
      "gps": true,
      "display": true,
      "sensors": false,
      "lowPowerRx": false
    }
  },
  "devices": [
    {
      "id": "lilygo-t-deck",
      "status": "supported",
      "notes": "T-Deck Plus — primary target with QWERTY keyboard, trackball, GPS."
    },
    {
      "id": "lilygo-twatch-s3",
      "status": "supported",
      "notes": "T-Watch Ultra — wrist-worn with AMOLED touchscreen."
    }
  ],
  "popularity": {
    "githubStars": 17,
    "githubForks": 2,
    "githubWatchers": 3,
    "githubOpenIssues": 2,
    "githubContributors": 3,
    "releaseDownloads": 179,
    "latestReleaseDownloads": 6,
    "lastChecked": "2026-06-21"
  },
  "verification": {
    "sourceAvailable": true,
    "releasesAvailable": true,
    "ciBuilds": true,
    "lastChecked": "2026-06-21"
  },
  "source": {
    "path": "data/firmwares/mclite/firmware.yaml",
    "updatedAt": "2026-06-22T21:31:07+02:00"
  },
  "latest_version": "0.3.9",
  "released": "2026-06-21",
  "releases": [
    {
      "version": "v0.3.9",
      "name": "0.3.9",
      "datetime": "2026-06-21T08:09:35Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.3.9",
      "prerelease": false,
      "notes": "### Added\n- **Step-wise admin permissions.** Beyond the existing `security.admin_enabled` (global on/off for the Admin\n  screen), a new `permissions` config block scopes what's reachable *inside* Admin: `permissions.settings`\n  (`full` / `restricted` / `none`) — **restricted** keeps only the basics editable (brightness, auto-dim, dim\n  brightness, keyboard brightness, theme) and shows everything else read-only (no chevron); **none** makes all\n  settings read-only. `permissions.companion` (default on) hides the Companion group (WiFi/USB/Bluetooth) when\n  off — configured services still run. `permissions.conversation_management` (default off) is reserved for a\n  future release (on-device add/edit/remove of contacts/channels/rooms; they stay read-only views for now). All\n  three are provisionable from the config tool. Defaults are fully permissive, so existing configs are unchanged.\n- **Settings reorganised into per-section screens + Admin is now a pure hub.** The on-device Admin screen no\n  longer mixes settings, diagnostics and shortcuts — it's three labelled groups of links: **Companion** (WiFi /\n  USB / Bluetooth), **Conversations** (Contacts / Channels / Rooms, read-only views), and **Settings** (Device,\n  Radio, Display, Messaging, Sound, GPS, Battery, Security). Each section is its own screen mirroring the config\n  tool, with all of its editable settings *and* its read-only diagnostics in one place (no more duplicated rows\n  across Admin and Device Settings). Newly editable on-device: **Radio** (region preset picker — EU/UK/CH vs\n  US/Canada — plus a TX-power slider and an advert-interval picker; frequency/SF/BW/CR/scope/path-hash stay read-only),\n  **Messaging** (history, max-per-chat, location format, retries, telemetry request/badges/auto-refresh, canned\n  messages, allow-mute), and **GPS** (enable, location-advert precision, timezone, clock offset, last-known max\n  age). Offgrid mode and the live Heard-Adverts count now live at the top of the Radio screen. Each hub link\n  carries an icon (gear for settings; `@`/`#`/`R` for contacts/channels/rooms; Wi-Fi/USB/Bluetooth for\n  companion), and the 3rd-party licenses moved to an *About* block at the bottom of the hub. Radio/GPS changes\n  reboot once on exit (same batched-save model as theme/language). The old single \"Device Settings\" screen is\n  superseded by this layout.\n- **Selectable UI themes.** Choose a color palette — **Dark** (default), **Light**, **Amber** (a \"military\"\n  night mode that preserves night vision), or **High contrast** — on-device (Admin → Theme, reboots to apply)\n  or via `display.theme` in config. Custom palettes can be defined under `display.themes` (start from a built-in\n  `base`, override any color with `#RRGGBB`). Default appearance is unchanged. On/off switches now use the\n  theme accent colour too. Thanks [@jason-s13r](https://github.com/jason-s13r) (#24).\n- **Per-row Info + Map buttons on the Heard Adverts screen.** Each heard node now has an explicit info (eye)\n  button that opens its detail dialog, and — when the advert carries a location — a map button that opens the\n  map centered on that node. Back now returns to the Admin screen. Thanks [@jason-s13r](https://github.com/jason-s13r) (#15).\n- **Map screen pan buttons + windowed chrome.** The map gains an on-screen D-pad (up/left/centre/right/down)\n  alongside the existing drag-to-pan. On the T-Deck the map now keeps the **status bar visible** and uses the\n  standard `lv_win` header with a back button (the T-Watch stays full-screen). Thanks [@jason-s13r](https://github.com/jason-s13r) (#22, supersedes #20/#21).\n- **Uptime + last-charged in the Admin Battery section.** Shows when the device booted (wall-clock + relative)\n  and when charging last stopped (with the level at the time). Thanks [@jason-s13r](https://github.com/jason-s13r) (#23).\n- **On-device Device Settings.** A new editable settings screen (Admin → Device Settings, behind the existing\n  `admin.enabled` gate) for changing device n\n…",
      "notesHtml": "<h3>Added</h3>\n<ul>\n<li><strong>Step-wise admin permissions.</strong> Beyond the existing <code>security.admin_enabled</code> (global on/off for the Admin\nscreen), a new <code>permissions</code> config block scopes what's reachable <em>inside</em> Admin: <code>permissions.settings</code>\n(<code>full</code> / <code>restricted</code> / <code>none</code>) — <strong>restricted</strong> keeps only the basics editable (brightness, auto-dim, dim\nbrightness, keyboard brightness, theme) and shows everything else read-only (no chevron); <strong>none</strong> makes all\nsettings read-only. <code>permissions.companion</code> (default on) hides the Companion group (WiFi/USB/Bluetooth) when\noff — configured services still run. <code>permissions.conversation_management</code> (default off) is reserved for a\nfuture release (on-device add/edit/remove of contacts/channels/rooms; they stay read-only views for now). All\nthree are provisionable from the config tool. Defaults are fully permissive, so existing configs are unchanged.</li>\n<li><strong>Settings reorganised into per-section screens + Admin is now a pure hub.</strong> The on-device Admin screen no\nlonger mixes settings, diagnostics and shortcuts — it's three labelled groups of links: <strong>Companion</strong> (WiFi /\nUSB / Bluetooth), <strong>Conversations</strong> (Contacts / Channels / Rooms, read-only views), and <strong>Settings</strong> (Device,\nRadio, Display, Messaging, Sound, GPS, Battery, Security). Each section is its own screen mirroring the config\ntool, with all of its editable settings <em>and</em> its read-only diagnostics in one place (no more duplicated rows\nacross Admin and Device Settings). Newly editable on-device: <strong>Radio</strong> (region preset picker — EU/UK/CH vs\nUS/Canada — plus a TX-power slider and an advert-interval picker; frequency/SF/BW/CR/scope/path-hash stay read-only),\n<strong>Messaging</strong> (history, max-per-chat, location format, retries, telemetry request/badges/auto-refresh, canned\nmessages, allow-mute), and <strong>GPS</strong> (enable, location-advert precision, timezone, clock offset, last-known max\nage). Offgrid mode and the live Heard-Adverts count now live at the top of the Radio screen. Each hub link\ncarries an icon (gear for settings; <code>@</code>/<code>#</code>/<code>R</code> for contacts/channels/rooms; Wi-Fi/USB/Bluetooth for\ncompanion), and the 3rd-party licenses moved to an <em>About</em> block at the bottom of the hub. Radio/GPS changes\nreboot once on exit (same batched-save model as theme/language). The old single \"Device Settings\" screen is\nsuperseded by this layout.</li>\n<li><strong>Selectable UI themes.</strong> Choose a color palette — <strong>Dark</strong> (default), <strong>Light</strong>, <strong>Amber</strong> (a \"military\"\nnight mode that preserves night vision), or <strong>High contrast</strong> — on-device (Admin → Theme, reboots to apply)\nor via <code>display.theme</code> in config. Custom palettes can be defined under <code>display.themes</code> (start from a built-in\n<code>base</code>, override any color with <code>#RRGGBB</code>). Default appearance is unchanged. On/off switches now use the\ntheme accent colour too. Thanks <a href=\"https://github.com/jason-s13r\" target=\"_blank\" rel=\"noopener noreferrer\">@jason-s13r</a> (#24).</li>\n<li><strong>Per-row Info + Map buttons on the Heard Adverts screen.</strong> Each heard node now has an explicit info (eye)\nbutton that opens its detail dialog, and — when the advert carries a location — a map button that opens the\nmap centered on that node. Back now returns to the Admin screen. Thanks <a href=\"https://github.com/jason-s13r\" target=\"_blank\" rel=\"noopener noreferrer\">@jason-s13r</a> (#15).</li>\n<li><strong>Map screen pan buttons + windowed chrome.</strong> The map gains an on-screen D-pad (up/left/centre/right/down)\nalongside the existing drag-to-pan. On the T-Deck the map now keeps the <strong>status bar visible</strong> and uses the\nstandard <code>lv_win</code> header with a back button (the T-Watch stays full-screen). Thanks <a href=\"https://github.com/jason-s13r\" target=\"_blank\" rel=\"noopener noreferrer\">@jason-s13r</a> (#22, supersedes #20/#21).</li>\n<li><strong>Uptime + last-charged in the Admin Battery section.</strong> Shows when the device booted (wall-clock + relative)\nand when charging last stopped (with the level at the time). Thanks <a href=\"https://github.com/jason-s13r\" target=\"_blank\" rel=\"noopener noreferrer\">@jason-s13r</a> (#23).</li>\n<li><strong>On-device Device Settings.</strong> A new editable settings screen (Admin → Device Settings, behind the existing\n<code>admin.enabled</code> gate) for changing device n\n…</li>\n</ul>\n"
    },
    {
      "version": "v0.3.8",
      "name": "MCLite 0.3.8",
      "datetime": "2026-06-16T09:19:15Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.3.8",
      "prerelease": false,
      "notes": "## Highlights\n\n**Less advert spam, more location privacy.**\n\n### Added\n- **Location-advert privacy precision.** The location-advert setting (now `gps.location_precision`) can coarsen the position you broadcast: **Off · Exact · ~100 m · ~750 m · ~3 km · ~12 km · ~50 km** (Meshtastic-style grid snapping, centred in the cell). Only the broadcast advert is coarsened — **telemetry replies to authorized contacts and the in-chat GPS insert always use your exact position**. Default off; old `location_advert: true/false` configs are read automatically. Scheme adopted from @jason-s13r.\n- **Zero-hop \"Local\" advert button** on the Heard Adverts screen — announce yourself to immediate neighbours without flooding the whole mesh.\n\n### Changed\n- **No more periodic flood adverts by default** (#13). MCLite previously flooded a mesh-wide advert every ~9 min; on a 110-repeater mesh one device generated ~half of all adverts. Now it sends a single flood advert **on boot** and otherwise advertises **on demand** — matching stock MeshCore clients. Thanks @stucamp and @jason-s13r.\n- **Opt-in periodic advert** — new `radio.advert_interval_min` field (config tool → Radio) re-enables periodic adverts for ad-hoc / SAR / private meshes. Default 0 = off; if set, enforced to ≥60 min (720 / 12 h recommended).\n- **GPS button inserts your location into the message** instead of a \"Send Location?\" confirm — append `@ <coords>` to the input, add context, send normally (byte-guarded against the 160-byte limit).\n\n---\nFlash from the web flasher or grab the merged `.bin` below. T-Deck Plus: `mclite-v0.3.8.bin` · T-Watch Ultra: `mclite-watch-v0.3.8.bin`.",
      "notesHtml": "<h2>Highlights</h2>\n<p><strong>Less advert spam, more location privacy.</strong></p>\n<h3>Added</h3>\n<ul>\n<li><strong>Location-advert privacy precision.</strong> The location-advert setting (now <code>gps.location_precision</code>) can coarsen the position you broadcast: <strong>Off · Exact · ~100 m · ~750 m · ~3 km · ~12 km · ~50 km</strong> (Meshtastic-style grid snapping, centred in the cell). Only the broadcast advert is coarsened — <strong>telemetry replies to authorized contacts and the in-chat GPS insert always use your exact position</strong>. Default off; old <code>location_advert: true/false</code> configs are read automatically. Scheme adopted from @jason-s13r.</li>\n<li><strong>Zero-hop \"Local\" advert button</strong> on the Heard Adverts screen — announce yourself to immediate neighbours without flooding the whole mesh.</li>\n</ul>\n<h3>Changed</h3>\n<ul>\n<li><strong>No more periodic flood adverts by default</strong> (#13). MCLite previously flooded a mesh-wide advert every ~9 min; on a 110-repeater mesh one device generated ~half of all adverts. Now it sends a single flood advert <strong>on boot</strong> and otherwise advertises <strong>on demand</strong> — matching stock MeshCore clients. Thanks @stucamp and @jason-s13r.</li>\n<li><strong>Opt-in periodic advert</strong> — new <code>radio.advert_interval_min</code> field (config tool → Radio) re-enables periodic adverts for ad-hoc / SAR / private meshes. Default 0 = off; if set, enforced to ≥60 min (720 / 12 h recommended).</li>\n<li><strong>GPS button inserts your location into the message</strong> instead of a \"Send Location?\" confirm — append <code>@ &lt;coords&gt;</code> to the input, add context, send normally (byte-guarded against the 160-byte limit).</li>\n</ul>\n<hr />\n<p>Flash from the web flasher or grab the merged <code>.bin</code> below. T-Deck Plus: <code>mclite-v0.3.8.bin</code> · T-Watch Ultra: <code>mclite-watch-v0.3.8.bin</code>.</p>\n"
    },
    {
      "version": "v0.3.7",
      "name": "MCLite 0.3.7",
      "datetime": "2026-06-15T09:34:57Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.3.7",
      "prerelease": false,
      "notes": "MCLite 0.3.7 — tappable map coordinates, lv_win screen chrome, and on-device screenshots.\n\n## Added\n- **Tap a shared location to open it on the map.** A received/sent message containing a GPS position — decimal `lat, lon` or MGRS/UTMREF — shows an underlined \"Open in map\" link under the bubble; tapping it opens the map centered there. Touch-only, shown only when map tiles are present on SD. Adds a reverse MGRS→lat/lon parser, unit-tested.\n- **Screenshot to SD** (debug aid, off by default). Enable `debug.screenshots` (config tool → Display) and capture the current screen to `/screenshots/*.bmp` (24-bit BMP, opens on any PC) — T-Deck: **Shift+$**; T-Watch: **double-press the side (PEK) button**. (Top-layer overlays like toasts/PIN/SOS aren't captured.)\n\n## Changed\n- **Heard Adverts** screen now uses the standard `lv_win` chrome (back arrow + title + action buttons) — the first step of rolling consistent screen headers across the app (community PR #8, @jason-s13r).\n\n## Removed\n- The non-functional Alt+L \"insert location\" keyboard shortcut (the chat GPS button already does this).\n\n## Notes\n- Config tool: \"Save screenshots\" toggle (Display); Security section relayout (Lock + Auto Lock on one line, Admin Screen separate).\n- Both boards (T-Deck Plus, T-Watch Ultra) build; 246 native tests pass.",
      "notesHtml": "<p>MCLite 0.3.7 — tappable map coordinates, lv_win screen chrome, and on-device screenshots.</p>\n<h2>Added</h2>\n<ul>\n<li><strong>Tap a shared location to open it on the map.</strong> A received/sent message containing a GPS position — decimal <code>lat, lon</code> or MGRS/UTMREF — shows an underlined \"Open in map\" link under the bubble; tapping it opens the map centered there. Touch-only, shown only when map tiles are present on SD. Adds a reverse MGRS→lat/lon parser, unit-tested.</li>\n<li><strong>Screenshot to SD</strong> (debug aid, off by default). Enable <code>debug.screenshots</code> (config tool → Display) and capture the current screen to <code>/screenshots/*.bmp</code> (24-bit BMP, opens on any PC) — T-Deck: <strong>Shift+$</strong>; T-Watch: <strong>double-press the side (PEK) button</strong>. (Top-layer overlays like toasts/PIN/SOS aren't captured.)</li>\n</ul>\n<h2>Changed</h2>\n<ul>\n<li><strong>Heard Adverts</strong> screen now uses the standard <code>lv_win</code> chrome (back arrow + title + action buttons) — the first step of rolling consistent screen headers across the app (community PR #8, @jason-s13r).</li>\n</ul>\n<h2>Removed</h2>\n<ul>\n<li>The non-functional Alt+L \"insert location\" keyboard shortcut (the chat GPS button already does this).</li>\n</ul>\n<h2>Notes</h2>\n<ul>\n<li>Config tool: \"Save screenshots\" toggle (Display); Security section relayout (Lock + Auto Lock on one line, Admin Screen separate).</li>\n<li>Both boards (T-Deck Plus, T-Watch Ultra) build; 246 native tests pass.</li>\n</ul>\n"
    },
    {
      "version": "v0.3.6",
      "name": "MCLite 0.3.6",
      "datetime": "2026-06-12T09:45:06Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.3.6",
      "prerelease": false,
      "notes": "MCLite 0.3.6 — emoji, three-step volume, and reliability polish.\n\n## Added\n- **Emoji in chat** — received emoji render inline (monochrome OpenMoji font with a Montserrat fallback, so plain text is unchanged and unknown glyphs degrade gracefully). An on-device emoji picker (`display.emoji`, default on, can be disabled) adds a smiley button to compose from a curated set, capped to the 160-byte message budget. Incoming/outgoing text is sanitized (strips emoji variation selectors, normalizes smart quotes to ASCII). Adopted from the @jason-s13r fork; OpenMoji is CC-BY-SA 4.0.\n- **Three-step volume** — the status-bar bell now cycles max → mute → mid → max; the built-in chime and custom WAV notifications scale to the level. Default is max (loudness unchanged); SOS stays fixed and loud (@jason-s13r, #11).\n\n## Changed\n- **`sound.enabled` is now a true master switch.** `false` means fully silent — no notifications, no chime, and no SOS sound — and the status-bar bell is hidden. Set `true` (the default) for the 3-step volume bell.\n\n## Fixed\n- A last-known position restored after reboot no longer reports \"~0s ago\" before the clock has synced — it shows \"Last known position\" until GPS/NTP re-locks, then the real age.\n- A failed send now shows a \"Send failed - try again\" toast instead of a silently FAILED bubble (still tap-to-retry).\n- Map tile loader hardening: oversized/corrupt PNGs and out-of-range tile coordinates are rejected and grey-filled cleanly.\n\nBoth boards (T-Deck Plus, T-Watch Ultra) build; 219 native tests pass.",
      "notesHtml": "<p>MCLite 0.3.6 — emoji, three-step volume, and reliability polish.</p>\n<h2>Added</h2>\n<ul>\n<li><strong>Emoji in chat</strong> — received emoji render inline (monochrome OpenMoji font with a Montserrat fallback, so plain text is unchanged and unknown glyphs degrade gracefully). An on-device emoji picker (<code>display.emoji</code>, default on, can be disabled) adds a smiley button to compose from a curated set, capped to the 160-byte message budget. Incoming/outgoing text is sanitized (strips emoji variation selectors, normalizes smart quotes to ASCII). Adopted from the @jason-s13r fork; OpenMoji is CC-BY-SA 4.0.</li>\n<li><strong>Three-step volume</strong> — the status-bar bell now cycles max → mute → mid → max; the built-in chime and custom WAV notifications scale to the level. Default is max (loudness unchanged); SOS stays fixed and loud (@jason-s13r, #11).</li>\n</ul>\n<h2>Changed</h2>\n<ul>\n<li><strong><code>sound.enabled</code> is now a true master switch.</strong> <code>false</code> means fully silent — no notifications, no chime, and no SOS sound — and the status-bar bell is hidden. Set <code>true</code> (the default) for the 3-step volume bell.</li>\n</ul>\n<h2>Fixed</h2>\n<ul>\n<li>A last-known position restored after reboot no longer reports \"~0s ago\" before the clock has synced — it shows \"Last known position\" until GPS/NTP re-locks, then the real age.</li>\n<li>A failed send now shows a \"Send failed - try again\" toast instead of a silently FAILED bubble (still tap-to-retry).</li>\n<li>Map tile loader hardening: oversized/corrupt PNGs and out-of-range tile coordinates are rejected and grey-filled cleanly.</li>\n</ul>\n<p>Both boards (T-Deck Plus, T-Watch Ultra) build; 219 native tests pass.</p>\n"
    },
    {
      "version": "v0.3.5",
      "name": "v0.3.5 — i18n cap + config-tool WiFi-wipe fixes",
      "datetime": "2026-06-11T10:32:26Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.3.5",
      "prerelease": false,
      "notes": "### Fixed\n- **Translations past ~128 keys reverted to English.** The i18n loader capped SD-loaded strings at 128, but the\n  language files now hold ~197 keys — so on German/French/Italian every key past the cap silently fell back to\n  English (e.g. `canned_5`–`8`, plus the offgrid / firmware-update / WiFi / USB / BLE / map / heard-adverts /\n  toast screens). Raised the cap to 256 and added a boot-time warning if a language file ever exceeds it.\n- **Config tool wiped stored WiFi on edit.** The tool's file-import never loaded the `wifi` section, so\n  importing a device's `config.json`, editing it, and re-exporting produced an empty `wifi` block — clearing\n  the device's stored SSID/password on the next copy to SD. WiFi (and the persisted BLE pairing PIN) now\n  round-trip correctly, including through the share-link and start-fresh paths.",
      "notesHtml": "<h3>Fixed</h3>\n<ul>\n<li><strong>Translations past ~128 keys reverted to English.</strong> The i18n loader capped SD-loaded strings at 128, but the\nlanguage files now hold ~197 keys — so on German/French/Italian every key past the cap silently fell back to\nEnglish (e.g. <code>canned_5</code>–<code>8</code>, plus the offgrid / firmware-update / WiFi / USB / BLE / map / heard-adverts /\ntoast screens). Raised the cap to 256 and added a boot-time warning if a language file ever exceeds it.</li>\n<li><strong>Config tool wiped stored WiFi on edit.</strong> The tool's file-import never loaded the <code>wifi</code> section, so\nimporting a device's <code>config.json</code>, editing it, and re-exporting produced an empty <code>wifi</code> block — clearing\nthe device's stored SSID/password on the next copy to SD. WiFi (and the persisted BLE pairing PIN) now\nround-trip correctly, including through the share-link and start-fresh paths.</li>\n</ul>\n"
    },
    {
      "version": "v0.3.4",
      "name": "v0.3.4 — Quick replies, auto-GPS refresh, companion advert + fixes",
      "datetime": "2026-06-11T10:03:31Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.3.4",
      "prerelease": false,
      "notes": "### Added\n- **Auto-refresh contact GPS** — keeps the map markers / convo-list badges of contacts who *don't* broadcast\n  their own location fresh, by quietly re-requesting telemetry GPS before the cached fix goes stale. Throttled\n  (one request per scan, respects the EU duty cycle, yields to manual requests) and self-limiting (stops asking\n  a contact that doesn't answer). New setting `messaging.auto_telemetry`, **default on**, can be disabled.\n- **Per-conversation quick replies** — any contact, channel, or room can carry its own `canned` list (max 8)\n  that overrides the global quick-reply list *for that chat only*; leave it empty to fall back to the global\n  list. Editable per card in the config tool. Turns a conversation into a command menu — e.g. a Home Assistant\n  / automation bridge (\"Open gate\", \"Lights on\", \"Status?\").\n- **Last-known location persists across reboots** — the most recent GPS fix is saved to SD\n  (`/mclite/last_location.json`, throttled) and restored on boot, so the map opens to your last position\n  without waiting for a fresh fix ([@jason-s13r](https://github.com/jason-s13r), #10).\n- **Advertise from the companion app** — the MeshCore phone/desktop app's **Advertise** button now works while\n  connected (BLE/WiFi/USB); previously it was rejected as an unsupported command. Honours the app's flood vs\n  local (zero-hop) option. The on-device advert button and the automatic periodic advert are unchanged.\n\n### Changed\n- The device-info / admin screen is now fully localized — every row label routes through the translation\n  table (de/fr/it) ([@jason-s13r](https://github.com/jason-s13r), #9).\n- Conversation history now loads only the most recent `max_history_per_chat` messages per chat at boot\n  (previously the whole file was loaded into RAM). Bounds memory if a history file is larger than the cap —\n  e.g. after lowering the setting. No visible change; the runtime cap was already in place.\n\n### Fixed\n- Telemetry retry is no longer cancelled by the contact-info pop-up's own timeout when the mesh's outbound\n  queue is busy (the two timers now stay in lockstep) — the retry fires under congestion as intended.\n- Closing the contact-info pop-up now cancels its in-flight telemetry retry, so no stray flood request goes\n  out for a pop-up you already closed.\n- Muting a chat is now absolute: it silences notifications even for a contact flagged `always_sound`\n  (`always_sound` still overrides *global* mute, unchanged).\n- Messages that exceed the 160-**byte** limit (e.g. emoji or accented/non-Latin text — which can be ≤160\n  *characters* but more bytes) are now refused with a \"Message too long\" toast that keeps your text, instead\n  of silently failing to send while still drawing a (failed) bubble.\n- A timed-out telemetry request now releases the radio's single telemetry slot when the exchange ends\n  (previously the slot stayed held after a no-response request). Without this, **auto-refresh contact GPS**\n  would stall for the rest of the session after the first contact that didn't answer, and slow/multi-hop\n  contacts could be backed off too eagerly; both are resolved.\n- Auto-refresh no longer lets a single un-sendable contact block the rest of the round-robin, and\n  `max_history_per_chat: 0` now consistently means \"unlimited\" on both load and prune (previously prune at 0\n  would wipe the conversation).",
      "notesHtml": "<h3>Added</h3>\n<ul>\n<li><strong>Auto-refresh contact GPS</strong> — keeps the map markers / convo-list badges of contacts who <em>don't</em> broadcast\ntheir own location fresh, by quietly re-requesting telemetry GPS before the cached fix goes stale. Throttled\n(one request per scan, respects the EU duty cycle, yields to manual requests) and self-limiting (stops asking\na contact that doesn't answer). New setting <code>messaging.auto_telemetry</code>, <strong>default on</strong>, can be disabled.</li>\n<li><strong>Per-conversation quick replies</strong> — any contact, channel, or room can carry its own <code>canned</code> list (max 8)\nthat overrides the global quick-reply list <em>for that chat only</em>; leave it empty to fall back to the global\nlist. Editable per card in the config tool. Turns a conversation into a command menu — e.g. a Home Assistant\n/ automation bridge (\"Open gate\", \"Lights on\", \"Status?\").</li>\n<li><strong>Last-known location persists across reboots</strong> — the most recent GPS fix is saved to SD\n(<code>/mclite/last_location.json</code>, throttled) and restored on boot, so the map opens to your last position\nwithout waiting for a fresh fix (<a href=\"https://github.com/jason-s13r\" target=\"_blank\" rel=\"noopener noreferrer\">@jason-s13r</a>, #10).</li>\n<li><strong>Advertise from the companion app</strong> — the MeshCore phone/desktop app's <strong>Advertise</strong> button now works while\nconnected (BLE/WiFi/USB); previously it was rejected as an unsupported command. Honours the app's flood vs\nlocal (zero-hop) option. The on-device advert button and the automatic periodic advert are unchanged.</li>\n</ul>\n<h3>Changed</h3>\n<ul>\n<li>The device-info / admin screen is now fully localized — every row label routes through the translation\ntable (de/fr/it) (<a href=\"https://github.com/jason-s13r\" target=\"_blank\" rel=\"noopener noreferrer\">@jason-s13r</a>, #9).</li>\n<li>Conversation history now loads only the most recent <code>max_history_per_chat</code> messages per chat at boot\n(previously the whole file was loaded into RAM). Bounds memory if a history file is larger than the cap —\ne.g. after lowering the setting. No visible change; the runtime cap was already in place.</li>\n</ul>\n<h3>Fixed</h3>\n<ul>\n<li>Telemetry retry is no longer cancelled by the contact-info pop-up's own timeout when the mesh's outbound\nqueue is busy (the two timers now stay in lockstep) — the retry fires under congestion as intended.</li>\n<li>Closing the contact-info pop-up now cancels its in-flight telemetry retry, so no stray flood request goes\nout for a pop-up you already closed.</li>\n<li>Muting a chat is now absolute: it silences notifications even for a contact flagged <code>always_sound</code>\n(<code>always_sound</code> still overrides <em>global</em> mute, unchanged).</li>\n<li>Messages that exceed the 160-<strong>byte</strong> limit (e.g. emoji or accented/non-Latin text — which can be ≤160\n<em>characters</em> but more bytes) are now refused with a \"Message too long\" toast that keeps your text, instead\nof silently failing to send while still drawing a (failed) bubble.</li>\n<li>A timed-out telemetry request now releases the radio's single telemetry slot when the exchange ends\n(previously the slot stayed held after a no-response request). Without this, <strong>auto-refresh contact GPS</strong>\nwould stall for the rest of the session after the first contact that didn't answer, and slow/multi-hop\ncontacts could be backed off too eagerly; both are resolved.</li>\n<li>Auto-refresh no longer lets a single un-sendable contact block the rest of the round-robin, and\n<code>max_history_per_chat: 0</code> now consistently means \"unlimited\" on both load and prune (previously prune at 0\nwould wipe the conversation).</li>\n</ul>\n"
    },
    {
      "version": "v0.3.3",
      "name": "v0.3.3 — Unified contact location + reliability batch",
      "datetime": "2026-06-10T16:51:48Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.3.3",
      "prerelease": false,
      "notes": "Contact-location and delivery-reliability improvements, plus a batch of community contributions from [@jason-s13r](https://github.com/jason-s13r).\n\n## Added\n- **Unified contact location** — one source of truth for where a contact is: fresh telemetry (accurate) → their advert GPS → a heard advert. The conversation-list **GPS badge** now shows for *any* known position (not just telemetry); the **contact info pop-up** shows that position even without a telemetry reply (advert-sourced coordinates are marked approximate with `~`), offers the **Map** button whenever any position is known, and on a request timeout shows the known position instead of a bare \"No response\". Telemetry stays primary; the 30-minute freshness window is unchanged.\n- **Flood-routing retries** for better delivery when a direct path degrades — on **DM** retries and on **telemetry-request** retries (with a \"Retrying…\" state).\n- **Per-chat mute** — opt-in (`messaging.allow_mute`, default off). Long-press a conversation to mute; muted chats don't beep or wake the screen (SOS always does), with an indicator in the list and chat header.\n- **Vendor row** on the device-info screen showing the firmware's source repo (`owner/repo`) — handy with fork flashing.\n\n## Fixed\n- Chat scroll-to-bottom is more robust on an empty chat area / on open.\n\n## Thanks\n- @jason-s13r for PRs #3–#7.\n\nInstall/update via the [web flasher](https://laserir.github.io/MCLite/tools/web-flasher/), or on-device (SD card / WiFi). Binaries: `mclite-v0.3.3.bin` (T-Deck Plus), `mclite-watch-v0.3.3.bin` (T-Watch Ultra).\n\n**Full Changelog**: https://github.com/laserir/MCLite/compare/v0.3.2...v0.3.3",
      "notesHtml": "<p>Contact-location and delivery-reliability improvements, plus a batch of community contributions from <a href=\"https://github.com/jason-s13r\" target=\"_blank\" rel=\"noopener noreferrer\">@jason-s13r</a>.</p>\n<h2>Added</h2>\n<ul>\n<li><strong>Unified contact location</strong> — one source of truth for where a contact is: fresh telemetry (accurate) → their advert GPS → a heard advert. The conversation-list <strong>GPS badge</strong> now shows for <em>any</em> known position (not just telemetry); the <strong>contact info pop-up</strong> shows that position even without a telemetry reply (advert-sourced coordinates are marked approximate with <code>~</code>), offers the <strong>Map</strong> button whenever any position is known, and on a request timeout shows the known position instead of a bare \"No response\". Telemetry stays primary; the 30-minute freshness window is unchanged.</li>\n<li><strong>Flood-routing retries</strong> for better delivery when a direct path degrades — on <strong>DM</strong> retries and on <strong>telemetry-request</strong> retries (with a \"Retrying…\" state).</li>\n<li><strong>Per-chat mute</strong> — opt-in (<code>messaging.allow_mute</code>, default off). Long-press a conversation to mute; muted chats don't beep or wake the screen (SOS always does), with an indicator in the list and chat header.</li>\n<li><strong>Vendor row</strong> on the device-info screen showing the firmware's source repo (<code>owner/repo</code>) — handy with fork flashing.</li>\n</ul>\n<h2>Fixed</h2>\n<ul>\n<li>Chat scroll-to-bottom is more robust on an empty chat area / on open.</li>\n</ul>\n<h2>Thanks</h2>\n<ul>\n<li>@jason-s13r for PRs #3–#7.</li>\n</ul>\n<p>Install/update via the <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">web flasher</a>, or on-device (SD card / WiFi). Binaries: <code>mclite-v0.3.3.bin</code> (T-Deck Plus), <code>mclite-watch-v0.3.3.bin</code> (T-Watch Ultra).</p>\n<p><strong>Full Changelog</strong>: <a href=\"https://github.com/laserir/MCLite/compare/v0.3.2...v0.3.3\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/laserir/MCLite/compare/v0.3.2...v0.3.3</a></p>\n"
    },
    {
      "version": "v0.3.2",
      "name": "v0.3.2 — Unified map + review fixes",
      "datetime": "2026-06-09T11:03:17Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.3.2",
      "prerelease": false,
      "notes": "Map unification plus fixes from a careful review of 0.3.0/0.3.1.\n\n## Changed\n- The contact **Map** button and the status-bar **GPS icon** now open the *same* map (one screen, one set of controls). Opening from a contact just centers on that contact and pre-selects it — drawn slightly larger with a highlight ring and its name in the bottom bar — while still showing every other contact / telemetry / heard-node location and your own position (a distinct green/amber dot).\n- **Reload** rebuilds all markers and re-checks your own position; **Center** always jumps to your own location once a fix is available (even when opened from a contact), falling back to the location the map opened on when there's no fix.\n\n## Fixed\n- Center now uses your own location on a contact-opened map (previously it only ever recentered on the contact).\n- The status-bar GPS icon stays visible (dimmed) and tappable when GPS is disabled, so the map remains reachable.\n- @mention no longer truncates a near-full message draft.\n- Companion: the contacts `since` field is read safely (removed an unaligned read).\n\nInstall/update via the [web flasher](https://laserir.github.io/MCLite/tools/web-flasher/), or on-device (SD card / WiFi). Binaries: `mclite-v0.3.2.bin` (T-Deck Plus), `mclite-watch-v0.3.2.bin` (T-Watch Ultra).\n\n**Full Changelog**: https://github.com/laserir/MCLite/compare/v0.3.1...v0.3.2",
      "notesHtml": "<p>Map unification plus fixes from a careful review of 0.3.0/0.3.1.</p>\n<h2>Changed</h2>\n<ul>\n<li>The contact <strong>Map</strong> button and the status-bar <strong>GPS icon</strong> now open the <em>same</em> map (one screen, one set of controls). Opening from a contact just centers on that contact and pre-selects it — drawn slightly larger with a highlight ring and its name in the bottom bar — while still showing every other contact / telemetry / heard-node location and your own position (a distinct green/amber dot).</li>\n<li><strong>Reload</strong> rebuilds all markers and re-checks your own position; <strong>Center</strong> always jumps to your own location once a fix is available (even when opened from a contact), falling back to the location the map opened on when there's no fix.</li>\n</ul>\n<h2>Fixed</h2>\n<ul>\n<li>Center now uses your own location on a contact-opened map (previously it only ever recentered on the contact).</li>\n<li>The status-bar GPS icon stays visible (dimmed) and tappable when GPS is disabled, so the map remains reachable.</li>\n<li>@mention no longer truncates a near-full message draft.</li>\n<li>Companion: the contacts <code>since</code> field is read safely (removed an unaligned read).</li>\n</ul>\n<p>Install/update via the <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">web flasher</a>, or on-device (SD card / WiFi). Binaries: <code>mclite-v0.3.2.bin</code> (T-Deck Plus), <code>mclite-watch-v0.3.2.bin</code> (T-Watch Ultra).</p>\n<p><strong>Full Changelog</strong>: <a href=\"https://github.com/laserir/MCLite/compare/v0.3.1...v0.3.2\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/laserir/MCLite/compare/v0.3.1...v0.3.2</a></p>\n"
    },
    {
      "version": "v0.3.1",
      "name": "v0.3.1 — GPS in adverts + map",
      "datetime": "2026-06-09T09:01:00Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.3.1",
      "prerelease": false,
      "notes": "Fork-adoption batch (features adopted from the jason-s13r/MCLite fork) plus map polish.\n\n## Added\n- **GPS location in adverts** (opt-in, `gps.location_advert`, default off) — broadcast your position so contacts see you on their map. Uses MeshCore's native advert location; sends a live fix or a still-valid last-known one. Unencrypted broadcast — hence opt-in. Read-only status on the admin GPS screen; toggled via the config tool / SD.\n- **General map** — tap the status-bar GPS icon for a map of your own location plus every heard node / contact that carries GPS (same chat / repeater / room / sensor symbols as the heard-adverts list). Tap a marker for its name; **Reload** button re-scans heard nodes.\n- **NTP time sync** — set the clock from an NTP server over WiFi when GPS hasn't locked (GPS still overrides once it does).\n- **@mention** — tap a sender's name in a channel/room to insert `@name `.\n- **Fork-aware OTA** — build-time overridable update repo so forks can self-update.\n- **Web flasher repo/fork picker** — choose which repo's published releases to flash.\n\n## Changed\n- Map markers render as filled colored dots (type color + contrasting symbol) so they read against any tile; selection ring sits just outside the dot.\n\n## Fixed\n- Map markers are reliably tappable (tap-slop dead-zone; wider hit tolerance).\n- Map markers no longer blink out near the viewport edge across zoom levels.\n\nInstall/update via the [web flasher](https://laserir.github.io/MCLite/tools/web-flasher/), or on-device (SD card / WiFi). Binaries: `mclite-v0.3.1.bin` (T-Deck Plus), `mclite-watch-v0.3.1.bin` (T-Watch Ultra).\n\n**Full Changelog**: https://github.com/laserir/MCLite/compare/v0.3.0...v0.3.1",
      "notesHtml": "<p>Fork-adoption batch (features adopted from the jason-s13r/MCLite fork) plus map polish.</p>\n<h2>Added</h2>\n<ul>\n<li><strong>GPS location in adverts</strong> (opt-in, <code>gps.location_advert</code>, default off) — broadcast your position so contacts see you on their map. Uses MeshCore's native advert location; sends a live fix or a still-valid last-known one. Unencrypted broadcast — hence opt-in. Read-only status on the admin GPS screen; toggled via the config tool / SD.</li>\n<li><strong>General map</strong> — tap the status-bar GPS icon for a map of your own location plus every heard node / contact that carries GPS (same chat / repeater / room / sensor symbols as the heard-adverts list). Tap a marker for its name; <strong>Reload</strong> button re-scans heard nodes.</li>\n<li><strong>NTP time sync</strong> — set the clock from an NTP server over WiFi when GPS hasn't locked (GPS still overrides once it does).</li>\n<li><strong>@mention</strong> — tap a sender's name in a channel/room to insert <code>@name </code>.</li>\n<li><strong>Fork-aware OTA</strong> — build-time overridable update repo so forks can self-update.</li>\n<li><strong>Web flasher repo/fork picker</strong> — choose which repo's published releases to flash.</li>\n</ul>\n<h2>Changed</h2>\n<ul>\n<li>Map markers render as filled colored dots (type color + contrasting symbol) so they read against any tile; selection ring sits just outside the dot.</li>\n</ul>\n<h2>Fixed</h2>\n<ul>\n<li>Map markers are reliably tappable (tap-slop dead-zone; wider hit tolerance).</li>\n<li>Map markers no longer blink out near the viewport edge across zoom levels.</li>\n</ul>\n<p>Install/update via the <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">web flasher</a>, or on-device (SD card / WiFi). Binaries: <code>mclite-v0.3.1.bin</code> (T-Deck Plus), <code>mclite-watch-v0.3.1.bin</code> (T-Watch Ultra).</p>\n<p><strong>Full Changelog</strong>: <a href=\"https://github.com/laserir/MCLite/compare/v0.3.0...v0.3.1\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/laserir/MCLite/compare/v0.3.0...v0.3.1</a></p>\n"
    },
    {
      "version": "v0.3.0",
      "name": "v0.3.0 — Companion mode (WiFi / USB / BLE)",
      "datetime": "2026-06-08T12:18:56Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.3.0",
      "prerelease": false,
      "notes": "## Highlights\n\n**Companion mode** — bridge the radio to a phone, desktop, or CLI using the standard MeshCore companion protocol, in parallel with normal on-device use (messages appear in both):\n- **Bluetooth** — pairs with the official MeshCore iOS/Android apps (6-digit passkey + bonding).\n- **WiFi** — reach it from `meshcore-cli`/`meshcore.js`/`meshcore_py` on your LAN.\n- **USB** — wired serial companion.\n\nOne transport at a time; messaging is read-only for config. WiFi and Bluetooth can't run together (shared radio/RAM) — the device handles the switch and offers a reboot when needed.\n\nAlso includes the on-device firmware-update fixes and a refreshed README (Quick Start + balanced companion docs).\n\nInstall/update via the [web flasher](https://laserir.github.io/MCLite/tools/web-flasher/), or on-device (SD card / WiFi). Binaries: `mclite-v0.3.0.bin` (T-Deck Plus), `mclite-watch-v0.3.0.bin` (T-Watch Ultra).\n\n**Full Changelog**: https://github.com/laserir/MCLite/compare/v0.2.2...v0.3.0",
      "notesHtml": "<h2>Highlights</h2>\n<p><strong>Companion mode</strong> — bridge the radio to a phone, desktop, or CLI using the standard MeshCore companion protocol, in parallel with normal on-device use (messages appear in both):</p>\n<ul>\n<li><strong>Bluetooth</strong> — pairs with the official MeshCore iOS/Android apps (6-digit passkey + bonding).</li>\n<li><strong>WiFi</strong> — reach it from <code>meshcore-cli</code>/<code>meshcore.js</code>/<code>meshcore_py</code> on your LAN.</li>\n<li><strong>USB</strong> — wired serial companion.</li>\n</ul>\n<p>One transport at a time; messaging is read-only for config. WiFi and Bluetooth can't run together (shared radio/RAM) — the device handles the switch and offers a reboot when needed.</p>\n<p>Also includes the on-device firmware-update fixes and a refreshed README (Quick Start + balanced companion docs).</p>\n<p>Install/update via the <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">web flasher</a>, or on-device (SD card / WiFi). Binaries: <code>mclite-v0.3.0.bin</code> (T-Deck Plus), <code>mclite-watch-v0.3.0.bin</code> (T-Watch Ultra).</p>\n<p><strong>Full Changelog</strong>: <a href=\"https://github.com/laserir/MCLite/compare/v0.2.2...v0.3.0\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/laserir/MCLite/compare/v0.2.2...v0.3.0</a></p>\n"
    },
    {
      "version": "v0.2.2",
      "name": "v0.2.2 — Fix WiFi OTA crash",
      "datetime": "2026-06-07T15:20:45Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.2.2",
      "prerelease": false,
      "notes": "Fixes a stack overflow that crashed the device when installing a firmware update over WiFi (the TLS handshake + on-stack buffer exceeded the loop-task stack). WiFi over-the-air updates now work end-to-end. SD-card install and USB flashing were unaffected.\n\nNote: devices on 0.2.1 should update via SD card or USB (the 0.2.1 WiFi installer has the crash); 0.2.2 onward updates over WiFi cleanly.",
      "notesHtml": "<p>Fixes a stack overflow that crashed the device when installing a firmware update over WiFi (the TLS handshake + on-stack buffer exceeded the loop-task stack). WiFi over-the-air updates now work end-to-end. SD-card install and USB flashing were unaffected.</p>\n<p>Note: devices on 0.2.1 should update via SD card or USB (the 0.2.1 WiFi installer has the crash); 0.2.2 onward updates over WiFi cleanly.</p>\n"
    },
    {
      "version": "v0.2.0",
      "name": "v0.2.0 — T-Watch Ultra support",
      "datetime": "2026-06-01T14:43:59Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.2.0",
      "prerelease": false,
      "notes": "**Full Changelog**: https://github.com/laserir/MCLite/compare/v0.1.8...v0.2.0",
      "notesHtml": "<p><strong>Full Changelog</strong>: <a href=\"https://github.com/laserir/MCLite/compare/v0.1.8...v0.2.0\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/laserir/MCLite/compare/v0.1.8...v0.2.0</a></p>\n"
    },
    {
      "version": "v0.1.8",
      "name": "v0.1.8 — Heard Adverts",
      "datetime": "2026-05-05T12:01:26Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.1.8",
      "prerelease": false,
      "notes": "## Highlights\n\n**Heard Adverts** — every advert MeshCore decodes goes into a 64-entry LRU buffer (RAM, evaporates on reboot). New screen reachable from the admin menu lists them with type icons, last-heard age, alias-when-known, and a queued badge for entries you've saved this session. Detail modal shows fingerprint-style pubkey + per-hop path direction.\n\n- **Live updates** while the screen is visible: 1 Hz rate-limited rebuild, focus preserved by pubkey, skipped while a modal is open.\n- **Save discovered chat contacts** — Save button on the detail modal appends a `from_discovery: true` entry to `config.json` with conservative defaults (all permissions off, no SOS in/out). Activates on next boot via the existing ContactStore + MCLiteMesh registration path. CHAT-only — repeaters/rooms/sensors stay infrastructure. After save: `[Reboot now, OK]` confirmation.\n- **Manual advert** button in the header — sends a local advert immediately and re-anchors the periodic 9-min schedule. Toast confirms.\n- **Clear button** wipes the in-memory buffer.\n\n## Config persistence hardening\n\n- `ConfigManager::save()` now uses **atomic writes**: stage to `.tmp`, retain previous file as `.bak` during the rename, drop `.bak` on successful completion. Steady state is just `config.json`.\n- **Boot fallback** to `config.json.bak` if the live file is missing or corrupt (mid-save power loss). Successful recovery promotes the loaded content back to main and removes `.bak`.\n- Required because `config.json` holds the device identity keys — a torn truncate-write would brick the unit.\n\n## Other changes\n\n- **Boot screen** — MCLite mark glyph above the title, drawn programmatically (no raster asset).\n- **Default `dim_brightness` 20 → 0** — auto-dim now goes fully dark, prevents long-term burn-in on the always-on status bar.\n- **Long-standing log fix**: `[MCLiteMesh] Discovered contact` was printing the raw packed `path_len` byte (low 6 bits = hops, upper 2 = hash size). Values like `70` actually meant 6 hops with 2-byte hashes. Now masked correctly.\n- **Config tool**: amber 'Added via device — review me' badge on `from_discovery` contacts, with an inline ✕ to mark reviewed. Round-trip preserves the flag on both load paths and the export builder.\n- **i18n**: 19 new keys covering type names, field labels, hops format, save/reboot/queue/error states, advert-sent toast — across en/de/fr/it and the embedded LANG_FILES.\n\n## Tests\n\n- 185 native test cases (was 172). New coverage in `test_heard_advert_cache` (14) and `test_config` (atomic writes, `.bak` fallback, recovery cleanup, `from_discovery` round-trip, append-discovered-contact success/duplicate/cap).\n\n## Compatibility\n\n- Backward-compatible config: missing `from_discovery` parses as `false`. Old firmware ignores the field.\n- ConfigManager now writes through the atomic path even for the existing offgrid-toggle save — no caller change needed.\n- To force first-boot regen, delete **both** `config.json` and `config.json.bak`.\n\n## Install\n\n- **Web flasher:** [laserir.github.io/MCLite/tools/web-flasher](https://laserir.github.io/MCLite/tools/web-flasher/) — pick v0.1.8\n- **Manual:** \\`esptool.py write_flash 0x0 mclite-v0.1.8.bin\\`\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)",
      "notesHtml": "<h2>Highlights</h2>\n<p><strong>Heard Adverts</strong> — every advert MeshCore decodes goes into a 64-entry LRU buffer (RAM, evaporates on reboot). New screen reachable from the admin menu lists them with type icons, last-heard age, alias-when-known, and a queued badge for entries you've saved this session. Detail modal shows fingerprint-style pubkey + per-hop path direction.</p>\n<ul>\n<li><strong>Live updates</strong> while the screen is visible: 1 Hz rate-limited rebuild, focus preserved by pubkey, skipped while a modal is open.</li>\n<li><strong>Save discovered chat contacts</strong> — Save button on the detail modal appends a <code>from_discovery: true</code> entry to <code>config.json</code> with conservative defaults (all permissions off, no SOS in/out). Activates on next boot via the existing ContactStore + MCLiteMesh registration path. CHAT-only — repeaters/rooms/sensors stay infrastructure. After save: <code>[Reboot now, OK]</code> confirmation.</li>\n<li><strong>Manual advert</strong> button in the header — sends a local advert immediately and re-anchors the periodic 9-min schedule. Toast confirms.</li>\n<li><strong>Clear button</strong> wipes the in-memory buffer.</li>\n</ul>\n<h2>Config persistence hardening</h2>\n<ul>\n<li><code>ConfigManager::save()</code> now uses <strong>atomic writes</strong>: stage to <code>.tmp</code>, retain previous file as <code>.bak</code> during the rename, drop <code>.bak</code> on successful completion. Steady state is just <code>config.json</code>.</li>\n<li><strong>Boot fallback</strong> to <code>config.json.bak</code> if the live file is missing or corrupt (mid-save power loss). Successful recovery promotes the loaded content back to main and removes <code>.bak</code>.</li>\n<li>Required because <code>config.json</code> holds the device identity keys — a torn truncate-write would brick the unit.</li>\n</ul>\n<h2>Other changes</h2>\n<ul>\n<li><strong>Boot screen</strong> — MCLite mark glyph above the title, drawn programmatically (no raster asset).</li>\n<li><strong>Default <code>dim_brightness</code> 20 → 0</strong> — auto-dim now goes fully dark, prevents long-term burn-in on the always-on status bar.</li>\n<li><strong>Long-standing log fix</strong>: <code>[MCLiteMesh] Discovered contact</code> was printing the raw packed <code>path_len</code> byte (low 6 bits = hops, upper 2 = hash size). Values like <code>70</code> actually meant 6 hops with 2-byte hashes. Now masked correctly.</li>\n<li><strong>Config tool</strong>: amber 'Added via device — review me' badge on <code>from_discovery</code> contacts, with an inline ✕ to mark reviewed. Round-trip preserves the flag on both load paths and the export builder.</li>\n<li><strong>i18n</strong>: 19 new keys covering type names, field labels, hops format, save/reboot/queue/error states, advert-sent toast — across en/de/fr/it and the embedded LANG_FILES.</li>\n</ul>\n<h2>Tests</h2>\n<ul>\n<li>185 native test cases (was 172). New coverage in <code>test_heard_advert_cache</code> (14) and <code>test_config</code> (atomic writes, <code>.bak</code> fallback, recovery cleanup, <code>from_discovery</code> round-trip, append-discovered-contact success/duplicate/cap).</li>\n</ul>\n<h2>Compatibility</h2>\n<ul>\n<li>Backward-compatible config: missing <code>from_discovery</code> parses as <code>false</code>. Old firmware ignores the field.</li>\n<li>ConfigManager now writes through the atomic path even for the existing offgrid-toggle save — no caller change needed.</li>\n<li>To force first-boot regen, delete <strong>both</strong> <code>config.json</code> and <code>config.json.bak</code>.</li>\n</ul>\n<h2>Install</h2>\n<ul>\n<li><strong>Web flasher:</strong> <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">laserir.github.io/MCLite/tools/web-flasher</a> — pick v0.1.8</li>\n<li><strong>Manual:</strong> `esptool.py write_flash 0x0 mclite-v0.1.8.bin`</li>\n</ul>\n<p>🤖 Generated with <a href=\"https://claude.com/claude-code\" target=\"_blank\" rel=\"noopener noreferrer\">Claude Code</a></p>\n"
    },
    {
      "version": "v0.1.7",
      "name": "v0.1.7 — Room Server Client",
      "datetime": "2026-04-29T10:39:57Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.1.7",
      "prerelease": false,
      "notes": "## Highlights\n\n**Room server client** — join community message boards run by MeshCore room servers. Posts appear in the conversation list with an `R` icon (purple), ordered alongside DMs and channels by last activity. Up to 8 rooms.\n\n- **Auto-login on boot** with exponential backoff (1 → 2 → 4 → cap 30 min).\n- **Disconnect recovery** — re-login on chat-open (rate-limited 30s) and after 10 minutes of silence while a room chat is foreground. Both wake the server's 3-strike push-freeze that trips after ~36s of radio dropout, so passive readers don't miss posts after brief out-of-range moments.\n- **Per-room flags** mirroring channels: `read_only`, `allow_sos` (default true), `send_sos` (default false), `scope` override.\n- **Sender resolution** — 4-byte `sender_prefix` looked up against ContactStore: alias if known, else 8-hex-char fallback.\n- **`sync_since` persisted** per-room across reboots; only newer posts replay on next login.\n- **AdminScreen Rooms section** under Channels: name, online/offline indicator, last-sync timestamp.\n- **Config tool** Rooms card with all four toggles and the new `info-icon` collapsible-hint pattern.\n\n## Other changes\n\n- `MAX_CONTACTS=32 → 40` (32 chat + 8 room) with cap warnings on overflow.\n- `MAX_CONVERSATIONS=48 → 56`.\n- Translations: `sec_rooms` added in de/fr/it.\n- `data[5]` keep-alive byte runtime watch — warns if a future MeshCore server re-enables the legacy interval.\n\n## Compatibility\n\n- Backward-compatible config: missing `room_servers` block parses as empty list. Old firmware ignores the field.\n- DM and channel history files stay byte-identical (no `syncSince` written when zero).\n- Telemetry, SOS, key/PIN lock, offgrid mode all unaffected.\n\n## Install\n\n- **Web flasher:** [laserir.github.io/MCLite/tools/web-flasher](https://laserir.github.io/MCLite/tools/web-flasher/) — pick v0.1.7\n- **Manual:** `esptool.py write_flash 0x0 mclite-v0.1.7.bin`\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)",
      "notesHtml": "<h2>Highlights</h2>\n<p><strong>Room server client</strong> — join community message boards run by MeshCore room servers. Posts appear in the conversation list with an <code>R</code> icon (purple), ordered alongside DMs and channels by last activity. Up to 8 rooms.</p>\n<ul>\n<li><strong>Auto-login on boot</strong> with exponential backoff (1 → 2 → 4 → cap 30 min).</li>\n<li><strong>Disconnect recovery</strong> — re-login on chat-open (rate-limited 30s) and after 10 minutes of silence while a room chat is foreground. Both wake the server's 3-strike push-freeze that trips after ~36s of radio dropout, so passive readers don't miss posts after brief out-of-range moments.</li>\n<li><strong>Per-room flags</strong> mirroring channels: <code>read_only</code>, <code>allow_sos</code> (default true), <code>send_sos</code> (default false), <code>scope</code> override.</li>\n<li><strong>Sender resolution</strong> — 4-byte <code>sender_prefix</code> looked up against ContactStore: alias if known, else 8-hex-char fallback.</li>\n<li><strong><code>sync_since</code> persisted</strong> per-room across reboots; only newer posts replay on next login.</li>\n<li><strong>AdminScreen Rooms section</strong> under Channels: name, online/offline indicator, last-sync timestamp.</li>\n<li><strong>Config tool</strong> Rooms card with all four toggles and the new <code>info-icon</code> collapsible-hint pattern.</li>\n</ul>\n<h2>Other changes</h2>\n<ul>\n<li><code>MAX_CONTACTS=32 → 40</code> (32 chat + 8 room) with cap warnings on overflow.</li>\n<li><code>MAX_CONVERSATIONS=48 → 56</code>.</li>\n<li>Translations: <code>sec_rooms</code> added in de/fr/it.</li>\n<li><code>data[5]</code> keep-alive byte runtime watch — warns if a future MeshCore server re-enables the legacy interval.</li>\n</ul>\n<h2>Compatibility</h2>\n<ul>\n<li>Backward-compatible config: missing <code>room_servers</code> block parses as empty list. Old firmware ignores the field.</li>\n<li>DM and channel history files stay byte-identical (no <code>syncSince</code> written when zero).</li>\n<li>Telemetry, SOS, key/PIN lock, offgrid mode all unaffected.</li>\n</ul>\n<h2>Install</h2>\n<ul>\n<li><strong>Web flasher:</strong> <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">laserir.github.io/MCLite/tools/web-flasher</a> — pick v0.1.7</li>\n<li><strong>Manual:</strong> <code>esptool.py write_flash 0x0 mclite-v0.1.7.bin</code></li>\n</ul>\n<p>🤖 Generated with <a href=\"https://claude.com/claude-code\" target=\"_blank\" rel=\"noopener noreferrer\">Claude Code</a></p>\n"
    },
    {
      "version": "v0.1.5",
      "name": "v0.1.5",
      "datetime": "2026-04-21T09:31:28Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.1.5",
      "prerelease": false,
      "notes": "## Highlights\n\n- **MeshCore upgraded to v1.15.0** (`companion-v1.15.0`) — catches up with upstream bug fixes, new token-bucket duty-cycle dispatcher, and default-scope aligned wire format. Binary-compatible with all MCLite v0.1.x peers.\n- **SOS burst hardened** — 50ms inter-send pacing and dispatcher yield between each recipient so the packet pool can drain. Fixes intermittent cases where SOS delivered to only some contacts/channels in meshes with many targets.\n- **SOS received → key lock auto-disengages** — when an incoming SOS alert appears, the device automatically unlocks its keys so the user can respond immediately. **PIN lock stays engaged** — no security compromise.\n- **Configurable path hash mode** — new `radio.path_hash_mode` setting (0/1/2 → 1/2/3 bytes per repeater fingerprint in the packet path). Larger sizes reduce path collisions in dense meshes at the cost of a few extra bytes per hop. Defaults to 0 for wire-identical compatibility with pre-v1.15 peers.\n- **Admin screen shows active path hash size** plus per-channel scope overrides rendered as `[scope:#name]`, distinct from the `[SOS]` / `[read-only]` capability flags.\n\n## Changes since v0.1.4\n\n- `08ca1f7` Bump MeshCore to v1.15.0, harden SOS burst delivery\n- `a90ad6e` Add configurable path hash mode and admin screen scope display\n- `0f53de1` Bump version to 0.1.5\n- `aba8ceb` Publish firmware v0.1.5 to web flasher\n- `debad94` Document scope and path_hash_mode in example config\n\n## Install\n\nFlash `mclite-v0.1.5.bin` via the [web flasher](https://laserir.github.io/MCLite/tools/web-flasher/) or manually with esptool. Use `mclite_config_tool.html` (offline, single file) to edit `config.json`.",
      "notesHtml": "<h2>Highlights</h2>\n<ul>\n<li><strong>MeshCore upgraded to v1.15.0</strong> (<code>companion-v1.15.0</code>) — catches up with upstream bug fixes, new token-bucket duty-cycle dispatcher, and default-scope aligned wire format. Binary-compatible with all MCLite v0.1.x peers.</li>\n<li><strong>SOS burst hardened</strong> — 50ms inter-send pacing and dispatcher yield between each recipient so the packet pool can drain. Fixes intermittent cases where SOS delivered to only some contacts/channels in meshes with many targets.</li>\n<li><strong>SOS received → key lock auto-disengages</strong> — when an incoming SOS alert appears, the device automatically unlocks its keys so the user can respond immediately. <strong>PIN lock stays engaged</strong> — no security compromise.</li>\n<li><strong>Configurable path hash mode</strong> — new <code>radio.path_hash_mode</code> setting (0/1/2 → 1/2/3 bytes per repeater fingerprint in the packet path). Larger sizes reduce path collisions in dense meshes at the cost of a few extra bytes per hop. Defaults to 0 for wire-identical compatibility with pre-v1.15 peers.</li>\n<li><strong>Admin screen shows active path hash size</strong> plus per-channel scope overrides rendered as <code>[scope:#name]</code>, distinct from the <code>[SOS]</code> / <code>[read-only]</code> capability flags.</li>\n</ul>\n<h2>Changes since v0.1.4</h2>\n<ul>\n<li><code>08ca1f7</code> Bump MeshCore to v1.15.0, harden SOS burst delivery</li>\n<li><code>a90ad6e</code> Add configurable path hash mode and admin screen scope display</li>\n<li><code>0f53de1</code> Bump version to 0.1.5</li>\n<li><code>aba8ceb</code> Publish firmware v0.1.5 to web flasher</li>\n<li><code>debad94</code> Document scope and path_hash_mode in example config</li>\n</ul>\n<h2>Install</h2>\n<p>Flash <code>mclite-v0.1.5.bin</code> via the <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">web flasher</a> or manually with esptool. Use <code>mclite_config_tool.html</code> (offline, single file) to edit <code>config.json</code>.</p>\n"
    },
    {
      "version": "v0.1.4",
      "name": "v0.1.4",
      "datetime": "2026-04-20T18:19:03Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.1.4",
      "prerelease": false,
      "notes": "## Highlights\n\n- **Map view on telemetry modal** — tap a contact's name in the chat header → telemetry modal → **Map** button (visible when the contact has fresh GPS and SD tiles are available). Full-screen slippy-tile map centred on the contact, own-device marker, `+`/`-` zoom, scale bar. Tile pack layout `/tiles/{z}/{x}/{y}.png` is compatible with upstream MeshCore T-Deck firmware — existing tile packs work unchanged. See the updated README for how to prepare a `/tiles` folder with [map-tiles-downloader](https://github.com/tekk/map-tiles-downloader).\n- **Screen lock & auto-lock unified** — single `lock` / `auto_lock` config with deferred click; backwards-compatible with older configs missing `auto_key_lock`.\n- **Region scope transport codes** — optional per-radio / per-channel scope for repeater filtering.\n- **MeshCore dependency pinned** to commit `792f2999` for reproducible builds.\n\n## Changes since v0.1.3\n\n- `444065d` Add map view on telemetry modal\n- `89c3825` Add Map view to features list, drop OSM attribution note\n- `b4598bb` Correct map-tiles-downloader description (TUI, not browser) and document GeoNames region codes\n- `6f378e0` Pin MeshCore dep to commit 792f2999 for reproducible builds\n\n## Install\n\nFlash `mclite-v0.1.4.bin` via the [web flasher](https://laserir.github.io/MCLite/tools/web-flasher/) or manually with esptool. Use `mclite_config_tool.html` (offline, single file) to edit `config.json`.",
      "notesHtml": "<h2>Highlights</h2>\n<ul>\n<li><strong>Map view on telemetry modal</strong> — tap a contact's name in the chat header → telemetry modal → <strong>Map</strong> button (visible when the contact has fresh GPS and SD tiles are available). Full-screen slippy-tile map centred on the contact, own-device marker, <code>+</code>/<code>-</code> zoom, scale bar. Tile pack layout <code>/tiles/{z}/{x}/{y}.png</code> is compatible with upstream MeshCore T-Deck firmware — existing tile packs work unchanged. See the updated README for how to prepare a <code>/tiles</code> folder with <a href=\"https://github.com/tekk/map-tiles-downloader\" target=\"_blank\" rel=\"noopener noreferrer\">map-tiles-downloader</a>.</li>\n<li><strong>Screen lock &amp; auto-lock unified</strong> — single <code>lock</code> / <code>auto_lock</code> config with deferred click; backwards-compatible with older configs missing <code>auto_key_lock</code>.</li>\n<li><strong>Region scope transport codes</strong> — optional per-radio / per-channel scope for repeater filtering.</li>\n<li><strong>MeshCore dependency pinned</strong> to commit <code>792f2999</code> for reproducible builds.</li>\n</ul>\n<h2>Changes since v0.1.3</h2>\n<ul>\n<li><code>444065d</code> Add map view on telemetry modal</li>\n<li><code>89c3825</code> Add Map view to features list, drop OSM attribution note</li>\n<li><code>b4598bb</code> Correct map-tiles-downloader description (TUI, not browser) and document GeoNames region codes</li>\n<li><code>6f378e0</code> Pin MeshCore dep to commit 792f2999 for reproducible builds</li>\n</ul>\n<h2>Install</h2>\n<p>Flash <code>mclite-v0.1.4.bin</code> via the <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">web flasher</a> or manually with esptool. Use <code>mclite_config_tool.html</code> (offline, single file) to edit <code>config.json</code>.</p>\n"
    },
    {
      "version": "v0.1.3",
      "name": "v0.1.3",
      "datetime": "2026-04-13T08:44:54Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.1.3",
      "prerelease": false,
      "notes": "## What's New\n\n- **Region scope** — tag outgoing packets with MeshCore transport codes so repeaters can filter by region. Set a global scope or override per channel. Compatible with 1-byte and 2-byte repeaters\n- **Automatic DST** — POSIX timezone strings for automatic daylight saving time transitions\n- **SOS fix** — SOS acknowledgement no longer triggers an SOS alert on the sender's device\n- **Telemetry fix** — CayenneLPP parsing off-by-one corrected\n- **Config tool** — region scope settings for radio and per-channel override\n\n## Install\n\nFlash via the [Web Flasher](https://laserir.github.io/MCLite/tools/web-flasher/) or download the binary below and flash with esptool:\n```\nesptool.py write_flash 0x0 mclite-v0.1.3.bin\n```",
      "notesHtml": "<h2>What's New</h2>\n<ul>\n<li><strong>Region scope</strong> — tag outgoing packets with MeshCore transport codes so repeaters can filter by region. Set a global scope or override per channel. Compatible with 1-byte and 2-byte repeaters</li>\n<li><strong>Automatic DST</strong> — POSIX timezone strings for automatic daylight saving time transitions</li>\n<li><strong>SOS fix</strong> — SOS acknowledgement no longer triggers an SOS alert on the sender's device</li>\n<li><strong>Telemetry fix</strong> — CayenneLPP parsing off-by-one corrected</li>\n<li><strong>Config tool</strong> — region scope settings for radio and per-channel override</li>\n</ul>\n<h2>Install</h2>\n<p>Flash via the <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">Web Flasher</a> or download the binary below and flash with esptool:</p>\n<pre><code>esptool.py write_flash 0x0 mclite-v0.1.3.bin\n</code></pre>\n"
    },
    {
      "version": "v0.1.2",
      "name": "v0.1.2",
      "datetime": "2026-03-30T12:01:18Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.1.2",
      "prerelease": false,
      "notes": "## What's New\n\n- **Quick replies** — optional canned message picker in chat (OK, Copy, Where are you?, Need help, etc.). Disabled by default, enable via config tool or `\"canned_messages\": true` in config.json. Supports custom messages via JSON array.\n- **Translations** — quick reply messages translated for German, French, and Italian\n- **Config tool** — Quick Replies toggle in Messaging section; auto-detects base64 identity keys from firmware-generated configs\n\n## Install\n\nFlash via the [Web Flasher](https://laserir.github.io/MCLite/tools/web-flasher/) or download the binary below and flash with esptool:\n```\nesptool.py write_flash 0x0 mclite-v0.1.2.bin\n```",
      "notesHtml": "<h2>What's New</h2>\n<ul>\n<li><strong>Quick replies</strong> — optional canned message picker in chat (OK, Copy, Where are you?, Need help, etc.). Disabled by default, enable via config tool or <code>\"canned_messages\": true</code> in config.json. Supports custom messages via JSON array.</li>\n<li><strong>Translations</strong> — quick reply messages translated for German, French, and Italian</li>\n<li><strong>Config tool</strong> — Quick Replies toggle in Messaging section; auto-detects base64 identity keys from firmware-generated configs</li>\n</ul>\n<h2>Install</h2>\n<p>Flash via the <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">Web Flasher</a> or download the binary below and flash with esptool:</p>\n<pre><code>esptool.py write_flash 0x0 mclite-v0.1.2.bin\n</code></pre>\n"
    },
    {
      "version": "v0.1.1",
      "name": "v0.1.1",
      "datetime": "2026-03-28T18:32:12Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.1.1",
      "prerelease": false,
      "notes": "## What's New\n\n- **Keyboard backlight support** — turns on at boot, off on auto-dim, restores on wake. Configurable via `kbd_backlight` (on/off) and `kbd_brightness` (1–255, default 127).\n- **Configurable dim brightness** — new `dim_brightness` setting (0–255, default 20). Set to 0 to turn the screen off completely when inactive.\n- **Config tool: Basic/Advanced mode** — new toggle to show only Device, Identity, Contacts, and Channels by default. Switch to Advanced for Radio, Display, Messaging, Sound, GPS, Battery, and Security settings.\n- Updated config tool, example config, admin screen, and README.\n\n## Flashing\n\nUse the [web flasher](https://laserir.github.io/MCLite/tools/web-flasher/) or download `mclite-v0.1.1.bin` below and flash with esptool:\n```\nesptool.py write_flash 0x0 mclite-v0.1.1.bin\n```",
      "notesHtml": "<h2>What's New</h2>\n<ul>\n<li><strong>Keyboard backlight support</strong> — turns on at boot, off on auto-dim, restores on wake. Configurable via <code>kbd_backlight</code> (on/off) and <code>kbd_brightness</code> (1–255, default 127).</li>\n<li><strong>Configurable dim brightness</strong> — new <code>dim_brightness</code> setting (0–255, default 20). Set to 0 to turn the screen off completely when inactive.</li>\n<li><strong>Config tool: Basic/Advanced mode</strong> — new toggle to show only Device, Identity, Contacts, and Channels by default. Switch to Advanced for Radio, Display, Messaging, Sound, GPS, Battery, and Security settings.</li>\n<li>Updated config tool, example config, admin screen, and README.</li>\n</ul>\n<h2>Flashing</h2>\n<p>Use the <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">web flasher</a> or download <code>mclite-v0.1.1.bin</code> below and flash with esptool:</p>\n<pre><code>esptool.py write_flash 0x0 mclite-v0.1.1.bin\n</code></pre>\n"
    },
    {
      "version": "v0.1.0",
      "name": "MCLite v0.1.0",
      "datetime": "2026-03-27T06:34:31Z",
      "url": "https://github.com/laserir/MCLite/releases/tag/v0.1.0",
      "prerelease": false,
      "notes": "## MCLite v0.1.0\n\nPurpose-built MeshCore companion firmware for LilyGo T-Deck Plus.\n\n### Highlights\n- MeshCore mesh messaging (DM + channels)\n- SOS alert system with long-press trigger\n- GPS location sharing (decimal, MGRS, or both)\n- On-demand telemetry (battery, location, environment)\n- Multi-language support (EN/DE/FR/IT)\n- SD card message history with ACK persistence\n- Offline config tool (HTML)\n\n### Install\nFlash `mclite-v0.1.0.bin` via the [web flasher](https://laserir.github.io/MCLite/tools/web-flasher/) or esptool.",
      "notesHtml": "<h2>MCLite v0.1.0</h2>\n<p>Purpose-built MeshCore companion firmware for LilyGo T-Deck Plus.</p>\n<h3>Highlights</h3>\n<ul>\n<li>MeshCore mesh messaging (DM + channels)</li>\n<li>SOS alert system with long-press trigger</li>\n<li>GPS location sharing (decimal, MGRS, or both)</li>\n<li>On-demand telemetry (battery, location, environment)</li>\n<li>Multi-language support (EN/DE/FR/IT)</li>\n<li>SD card message history with ACK persistence</li>\n<li>Offline config tool (HTML)</li>\n</ul>\n<h3>Install</h3>\n<p>Flash <code>mclite-v0.1.0.bin</code> via the <a href=\"https://laserir.github.io/MCLite/tools/web-flasher/\" target=\"_blank\" rel=\"noopener noreferrer\">web flasher</a> or esptool.</p>\n"
    }
  ],
  "changelogSource": "github",
  "changelogUpdatedAt": "2026-06-21T09:55:30.520Z"
}
