<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged ota</title>
<link>https://coderlegion.com/tag/ota</link>
<description>Powered by Question2Answer</description>
<item>
<title>Updating Radar Firmware Without a Cloud: Self-Hosted OTA over MQTT</title>
<link>https://coderlegion.com/32750/updating-radar-firmware-without-a-cloud-self-hosted-ota-over-mqtt</link>
<description>&lt;p&gt;Updating Radar Firmware Without a Cloud: Self-Hosted OTA over MQTT&lt;/p&gt;
&lt;p&gt;Most connected devices want you to push firmware through somebody's cloud. These do not ask for that. The sleep and fall radars fetch an image from a URL you hand them, and the command arrives on the same MQTT connection the telemetry already uses. Nothing has to leave your network, and there is no vendor account in the path.&lt;/p&gt;
&lt;p&gt;This is the practical shape of that: which units support it, the three commands involved, how to confirm an update actually landed, and the operational precautions the protocol document leaves to you.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Everything below comes from the Wi-Fi protocol documents for the sleep and fall units. Where they do not specify something, this says so rather than filling the gap.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;scope-first&quot;&gt;Scope first&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Unit &lt;/th&gt;&lt;th&gt;OTA update via this protocol&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Sleep radar (60 GHz, Wi-Fi) &lt;/td&gt;&lt;td&gt;Yes — topic family &lt;code&gt;/Radar60SP/{DeviceID}/sys/property/{post,set}&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Fall radar (60 GHz, Wi-Fi) &lt;/td&gt;&lt;td&gt;Yes — topic family &lt;code&gt;/Radar60FL/{DeviceID}/sys/property/{post,set}&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Presence radar (24 GHz, Wi-Fi) &lt;/td&gt;&lt;td&gt;No such command appears in its protocol document&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That last row matters. The presence unit's document specifies &lt;code&gt;online&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt;, &lt;code&gt;active&lt;/code&gt; and &lt;code&gt;passive&lt;/code&gt; report topics plus a &lt;code&gt;property/set&lt;/code&gt; command topic — and no update command at all. If you need to update those, that is a question for the hardware, not for this protocol.&lt;/p&gt;
&lt;p&gt;Commands go to the unit's &lt;code&gt;.../sys/property/set&lt;/code&gt; topic; everything it reports arrives on &lt;code&gt;.../sys/property/post&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;three-commands&quot;&gt;Three commands&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. Select the target module&lt;/strong&gt; — required before every update.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;version&quot;: &quot;1.0&quot;, &quot;method&quot;: &quot;ota_set&quot;, &quot;params&quot;: {&quot;module&quot;: &quot;0&quot;}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;0&lt;/code&gt; is the Wi-Fi module, &lt;code&gt;1&lt;/code&gt; is the radar module; the default is &lt;code&gt;0&lt;/code&gt;. The documentation is explicit that this control command has to be sent before each update — it is not a one-time setting you configure and forget.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Hand over the image location.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;version&quot;: &quot;1.0&quot;, &quot;method&quot;: &quot;ota_set&quot;,
 &quot;params&quot;: {&quot;otaInform&quot;: {&quot;firmwareUrl&quot;: &quot;http://192.168.31.88/app.rbl&quot;, &quot;port&quot;: &quot;8090&quot;}}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The device fetches the firmware from that URL and port and starts updating. A failure comes back as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;version&quot;: &quot;1.0&quot;, &quot;opt&quot;: &quot;otaInform&quot;, &quot;res&quot;: &quot;fail&quot;, &quot;msg&quot;: &quot;ota failed!&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. Observe progress.&lt;/strong&gt; Described as an active report during update, sent with method &lt;code&gt;ota_post&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;version&quot;: &quot;1.0&quot;, &quot;method&quot;: &quot;ota_post&quot;, &quot;params&quot;: {&quot;downloadProgress&quot;: &quot;0&quot;}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Values run &lt;code&gt;0&lt;/code&gt;–&lt;code&gt;100&lt;/code&gt;. Subscribe to the unit's report topic before you start; if your unit reports it somewhere else, record that during commissioning rather than assuming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;After a successful update&lt;/strong&gt;, the module restarts and then reports its version number. Treat that report — not the arrival of &lt;code&gt;100&lt;/code&gt; — as the completion signal.&lt;/p&gt;
&lt;h3 id=&quot;confirming-the-result&quot;&gt;Confirming the result&lt;/h3&gt;
&lt;p&gt;&quot;Transfer finished&quot; and &quot;the unit now runs the new code&quot; are two different statements.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sleep and fall units&lt;/strong&gt; carry &lt;code&gt;firmwareVersion&lt;/code&gt; and &lt;code&gt;firmwareVersionWiFi&lt;/code&gt; in their property list, and the protocol states that the post-restart report carries the version number after updating. Compare against the value you recorded before starting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Presence units&lt;/strong&gt; publish a version snapshot on &lt;code&gt;/RadarHP/{DeviceID}/sys/version/post&lt;/code&gt; containing &lt;code&gt;radarID&lt;/code&gt;, &lt;code&gt;softwareVersion&lt;/code&gt;, &lt;code&gt;firmwareVersion&lt;/code&gt; and &lt;code&gt;protocolVersion&lt;/code&gt;. These are read-only — they cannot be written, only read. There is also a request command to solicit it on demand: publish &lt;code&gt;{&quot;version&quot;:&quot;1.0&quot;,&quot;method&quot;:&quot;get_version&quot;,&quot;params&quot;:{}}&lt;/code&gt; to &lt;code&gt;/RadarHP/{DeviceID}/sys/property/set&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep the before-and-after version pair in the same commissioning record you keep for everything else. It is the only evidence you have later.&lt;/p&gt;
&lt;h3 id=&quot;running-an-update&quot;&gt;Running an update&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import json
import paho.mqtt.client as mqtt

BROKER, PORT = &quot;192.168.1.2&quot;, 1883
DEV = &quot;&amp;lt;DeviceID&amp;gt;&quot;                        # the MAC, printed on the label
SET_TOPIC  = f&quot;/Radar60SP/{DEV}/sys/property/set&quot;
POST_TOPIC = f&quot;/Radar60SP/{DEV}/sys/property/post&quot;

IMAGE_URL, IMAGE_PORT = &quot;http://192.168.1.50:8090/app.rbl&quot;, &quot;8090&quot;
TARGET = &quot;0&quot;                              # &quot;0&quot; Wi-Fi module, &quot;1&quot; radar module


def command(client, method, params):
    client.publish(SET_TOPIC,
                   json.dumps({&quot;version&quot;: &quot;1.0&quot;, &quot;method&quot;: method, &quot;params&quot;: params}),
                   qos=1)


def on_message(client, userdata, msg):
    payload = json.loads(msg.payload.decode())
    params = payload.get(&quot;params&quot;, {})
    if &quot;downloadProgress&quot; in params:
        print(&quot;transfer:&quot;, params[&quot;downloadProgress&quot;], &quot;%&quot;)
    if payload.get(&quot;opt&quot;) == &quot;otaInform&quot; and payload.get(&quot;res&quot;) == &quot;fail&quot;:
        print(&quot;update failed:&quot;, payload.get(&quot;msg&quot;))
    if &quot;firmwareVersion&quot; in params:
        print(&quot;reported version after restart:&quot;, params[&quot;firmwareVersion&quot;])


client = mqtt.Client()
client.username_pw_set(&quot;backend&quot;, &quot;...&quot;)
client.on_message = on_message
client.connect(BROKER, PORT, keepalive=60)
client.subscribe(POST_TOPIC, qos=1)
client.loop_start()

command(client, &quot;ota_set&quot;, {&quot;module&quot;: TARGET})          # select the target first
command(client, &quot;ota_set&quot;, {&quot;otaInform&quot;: {&quot;firmwareUrl&quot;: IMAGE_URL, &quot;port&quot;: IMAGE_PORT}})
client.loop_forever()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is the whole mechanism — two publishes and a subscription. The rest is procedure.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Declare a maintenance window.&lt;/strong&gt; The unit is unreachable while it rewrites and restarts. Monitoring will read that as a failure unless you suppress it — see &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/27/online-heartbeat-radar-health.html&quot;&gt;Online Says It's Connected, HeartBeat Says It Works&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Select the target deliberately.&lt;/strong&gt; &lt;code&gt;module&lt;/code&gt; defaults to the Wi-Fi module. Sending a radar image to it is a mistake the command will not catch for you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make sure the URL is reachable from the device.&lt;/strong&gt; Same subnet or routable, firewall permits that port, and never &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt; — the address is resolved by the radar, not by the machine issuing the command.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stage it.&lt;/strong&gt; One unit first. Watch &lt;code&gt;downloadProgress&lt;/code&gt; reach &lt;code&gt;100&lt;/code&gt;, wait for the restart, confirm the version changed, then proceed with the rest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep the previous image served.&lt;/strong&gt; Given what is not documented below, this is your only fallback.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do not batch-update overnight in a care deployment.&lt;/strong&gt; A unit that comes back with different behaviour at 03:00 is discovered by nobody until morning.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;a-minimal-image-host&quot;&gt;A minimal image host&lt;/h3&gt;
&lt;p&gt;Nothing more than a static file server on the management network:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python -m http.server 8090 --directory ./firmware
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which makes &lt;code&gt;http://{host}:8090/app.rbl&lt;/code&gt; available to the units. Use it on a segregated network or behind whatever reverse proxy you already trust; it is plain HTTP with directory listing enabled, and it is a build artifact service, not a public endpoint.&lt;/p&gt;
&lt;h3 id=&quot;what-the-documents-do-not-say&quot;&gt;What the documents do not say&lt;/h3&gt;
&lt;p&gt;Stated plainly, because guessing here costs hardware:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Transport.&lt;/strong&gt; Every example uses &lt;code&gt;http://&lt;/code&gt;. HTTPS support is not stated. Plan for plain HTTP on a network you control.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rollback.&lt;/strong&gt; No rollback command appears. Assume you cannot command a unit back to the previous revision — which is why keeping the old image available and piloting one unit first are not optional.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duration and interruption.&lt;/strong&gt; No update duration is given, and the documents describe failure only as the &lt;code&gt;ota failed!&lt;/code&gt; response. Behaviour after power loss mid-update is undocumented.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The consequence is procedural rather than technical: update when someone is present to observe, one unit first, with the previous image still served.&lt;/p&gt;
&lt;h3 id=&quot;checklist&quot;&gt;Checklist&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Unit confirmed to be one of the two that document this command&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;module&lt;/code&gt; selected explicitly before every update — Wi-Fi module vs radar module&lt;/li&gt;
&lt;li&gt;[ ] Image URL and port reachable from the device's network, not from yours only&lt;/li&gt;
&lt;li&gt;[ ] Subscribed to the report topic &lt;em&gt;before&lt;/em&gt; publishing either command&lt;/li&gt;
&lt;li&gt;[ ] Monitoring suppression window opened, and closed only after the version report arrives&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;downloadProgress&lt;/code&gt; observed to &lt;code&gt;100&lt;/code&gt;, restart observed, version compared against the pre-update record&lt;/li&gt;
&lt;li&gt;[ ] Previous firmware image still available at a known URL&lt;/li&gt;
&lt;li&gt;[ ] Staged: one unit, verified, then the cohort&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;related-reading&quot;&gt;Related reading&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Reading the stream these commands travel on: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/25/reading-radar-data-your-own-app.html&quot;&gt;Reading Radar Data in Your Own Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Managing many units: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/24/running-several-radars-one-home.html&quot;&gt;Running Several Radars in One Home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The broker underneath it: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/23/apache-artemis-mqtt-broker-radar.html&quot;&gt;Apache Artemis setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Full documentation: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/&quot;&gt;https://249263431.github.io/60GHz-Radar-SDK/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Modules and finished units: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.tindie.com/stores/fuxinsuo/&quot;&gt;https://www.tindie.com/stores/fuxinsuo/&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32750/updating-radar-firmware-without-a-cloud-self-hosted-ota-over-mqtt</guid>
<pubDate>Tue, 27 Oct 2026 23:05:42 +0000</pubDate>
</item>
<item>
<title>How to Push Over-the-Air Flutter Updates with Shorebird — Complete 2026 Guide</title>
<link>https://coderlegion.com/12374/how-to-push-over-the-air-flutter-updates-with-shorebird-complete-2026-guide</link>
<description>&lt;p&gt;One of the biggest frustrations for mobile devs is still waiting days or weeks for App Store or Play Store approvals to ship a small bug fix, UI tweak, or critical security patch. What if you could push those changes instantly, over-the-air, directly to your users' devices? No resubmission, no waiting, no lost users. That's exactly what @shorebird  Code Push delivers: true over-the-air (OTA) updates for Dart code in Flutter apps.&lt;/p&gt;
</description>
<category>Videos</category>
<guid isPermaLink="true">https://coderlegion.com/12374/how-to-push-over-the-air-flutter-updates-with-shorebird-complete-2026-guide</guid>
<pubDate>Wed, 04 Mar 2026 20:40:21 +0000</pubDate>
</item>
</channel>
</rss>