fix(app): stop an unreadable firmware version throwing the update check
BtDevice.firmwareRevision returns the string Unknown when the GATT read fails, and DeviceUtils.shouldUpdateFirmware only rejects the empty string before passing it to Version.parse, which throws FormatException: Not a properly formatted version string. A response missing min_version throws type 'Null' is not a subtype of type 'String', and if (latestFirmwareDetails['draft']) throws on a missing key rather than reading as not-draft.
DeviceProvider.shouldUpdateFirmware is called inside a try/catch so it retries, but pages/home/firmware_update.dart calls this from an addPostFrameCallback with no catch. The throw skips setState(() => isLoading = false), so a device whose firmware revision could not be read leaves the Firmware Update page on its spinner with no way forward.
The parses are guarded at the source rather than at the call site, since the function's contract is to return a message tuple. An unreadable current version reports Unable to determine current firmware version, and an unusable latest/minimum reports Latest Version Not Available, both of which the callers already render.
Three of the six tests pin the existing behaviour so the guards cannot quietly change an outcome for a valid response: a newer published firmware still offers the update, a device below the minimum still gets the 0 sentinel, and an up to date device is still told so.
Verified by running, from app/:
$ flutter test test/unit/firmware_version_parse_test.dart
00:00 +6: All tests passed!
$ bash test.sh
02:57 +1884 ~5: All tests passed!
$ bash scripts/analyze_ratchet.sh
Analyzer ratchet passed.
$ make preflight (repo root)
PR preflight passed: 14 checks
Failure-Class: none