Skip to main content

OpenMiwear

A Python toolkit for extracting, merging, auditing and analyzing MiWear device dumps — from the command line.

PyPI version Python License Downloads CI

OpenMiwear is a set of small, focused command-line tools for the everyday chores of working with MiWear device artifacts — unpacking .tar.gz / .zip / .gz bundles, merging rotated logs, extracting assertions, auditing resource directories, analyzing runtime log patterns, and driving serial consoles.

Each tool does one thing, takes sensible defaults, and produces human- or machine-readable output (plain text, CSV, Markdown, or interactive HTML).

Features

  • Zero runtime dependencies for the core tools — pure Python standard library.
  • Batch extraction of .tar.gz, .zip and .gz archives in a single pass.
  • Log workflow: pull logs over adb, merge rotated shards, extract assertions, filter by pattern.
  • Resource audit: duplicate detection, unused-asset scan, two-directory diff — with Markdown + interactive HTML reports.
  • Log analyzer for AppID / screen-transition patterns, exportable to CSV or interactive HTML.
  • Optional serial console (miniterm, one-shot, periodic, batch-file) via pyserial.
  • Works on Python 3.10+, Linux / macOS / Windows.

Installation

Published on PyPI as the miwear package:

# Core tools
pip install miwear

# Core + serial console helper
pip install 'miwear[serial]'

[!TIP] Every command accepts --help for the full option list and --version to print the installed version.

Quick start

# Extract and merge a device log bundle
miwear_log ~/Downloads/log.tar.gz

# Or pull straight from a connected Android device via adb
miwear_log --phone -f 123456_abc

# Audit a resources directory for duplicates and open an HTML report
miwear_check -d ./resources -e bin

Commands

Command Purpose
miwear_log Extract a MiWear log archive and merge its shards into a single log file
miwear_assert Extract assertion blocks from a log
miwear_gz Decompress and merge .gz log shards
miwear_tz Batch-extract every .tar.gz archive in a directory
miwear_uz Batch-extract every .zip archive in a directory
miwear_ez Pull files out of a ZIP archive by precise filename-stem suffix
miwear_check Resource audit — duplicates, unused assets, directory diff
miwear_loganalyzer Parse AppID / screen log patterns into CSV or interactive HTML
miwear_ota Diff two OTA packages with ddelta and explain the delta size
miwear_serial Serial console helper (requires pyserial)

Usage

miwear_log

Extract a log bundle and merge its shards into one file.

# Positional path (recommended)
miwear_log ~/Downloads/log.tar.gz

# Or with -f / --filename
miwear_log -f ~/Downloads/log.tar.gz

# Pull straight from an Android phone via adb
miwear_log --phone -f 123456_abc

miwear_assert

Extract assertion blocks from a merged log.

miwear_assert -i mi.log -o assert_log.txt

miwear_gz

Decompress and concatenate all .gz shards in a directory.

miwear_gz --path ./logs --log_file my.log --output_file merged.log

miwear_tz / miwear_uz

Batch-extract every archive in a directory.

miwear_tz --path ./logs     # *.tar.gz
miwear_uz --path ./logs     # *.zip

miwear_ez

Pull files out of a ZIP archive into an output directory, without performing a full extraction. Files are selected by one or more precise filename-stem suffixes: ap matches *ap.elf but not *app.elf. Pass several suffixes like ap cp ota to extract multiple at once, or all to extract everything. If no ZIP is given, the current directory is scanned and you pick one interactively.

# Auto-detect a ZIP, extract *ap.elf (default target is 'ap')
miwear_ez

# Extract every *.elf (legacy behavior), plus ota.zip
miwear_ez all

# A different stem suffix, e.g. *cp.elf
miwear_ez cp

# Multiple suffixes in one pass: *ap.elf, *cp.elf and *ota.elf
miwear_ez ap cp ota

# Explicit archive, multiple extensions, custom output directory
miwear_ez ap -z firmware.zip -e bin hex -o ./out

miwear_check

Four modes: dup (default), unused, both, and diff. All modes emit Markdown + interactive HTML reports with search / filter / sort.

Duplicate detection
miwear_check -d ./resources -e bin
miwear_check -d ./resources -e bin png
miwear_check -d ./resources -e bin -p theme_ config_
miwear_check -d ./resources -e bin --action delete
Unused resource scan

-d sets where resources live; -c sets where to look for references (defaults to -d).

miwear_check -m unused -d ./project -e bin
miwear_check -m unused -d ./project -c ./project/ota -e bin
miwear_check -m unused -d ./project -e bin --code-ext ".c,.h,.cpp,.java"

# Run duplicate + unused together
miwear_check -m both -d ./project -e bin
Two-directory diff

Matches files by base name (everything before the first dot) under the same relative path — so confirm.indexed_8.png and confirm.bin pair up automatically.

miwear_check -m diff --path1 ./design --path2 ./converted
miwear_check -m diff --path1 ./design --path2 ./converted -i .git,node_modules
miwear_check -m diff --path1 ./design --path2 ./converted --sort count
Report output
miwear_check -d ./resources -e bin -o my_report.md
miwear_check -d ./resources -e bin --no-output

[!NOTE] When the scan directory contains both view/ and view_XX/res* variants, miwear_check prompts you to pick one (default: the variant with the most files). Pass -i to skip the prompt.

miwear_loganalyzer

Parse MiWear runtime log patterns and export to CSV or HTML.

# Default: all analyzers → miwear.csv
miwear_loganalyzer -f 1.log

# Interactive HTML report (opens in browser)
miwear_loganalyzer -f 1.log --html --open-browser

# Run a single analyzer
miwear_loganalyzer -f 1.log -t appid
miwear_loganalyzer -f 1.log -t screen -o screens.csv

miwear_ota

Diff two OTA packages file by file, generate a real ddelta patch for every payload image, and explain where the delta size comes from. Emits an HTML report (plus a Markdown mirror), the .patch files, and a deflated bundle.

[!NOTE] The report body is in Chinese, matching the layout this tool inherited from its in-house predecessor.

[!IMPORTANT] ddelta_generate is not bundled — it is an architecture-specific binary built from the Vela source tree. Build it once:

cd <vela-root>/external/ddelta/ddelta && make clean && make

It is then picked up automatically when you run inside a Vela checkout. Otherwise pass --ddelta <path> or export MIWEAR_DDELTA_GENERATE=<path>. Without it the tool still runs, reporting byte-level comparison only.

[!IMPORTANT] --blocksize is required (unless you pass --no-ddelta). It is the same block size your OTA packaging step passes to ddelta_generate, it differs per project, and a non-zero value switches ddelta to in-place patching — which changes the resulting patch. Pass 0 to reproduce a build that does not set a block size. The value is printed in the report header and as a summary card so a report is never ambiguous.

# Reproduce a build that uses a 32M block size
miwear_ota ota_old.zip ota_new.zip --blocksize 32M

# A build that does not set a block size at all
miwear_ota ota_old.zip ota_new.zip --blocksize 0

# Explicit tool path, verify every patch by applying it back onto the old image
miwear_ota ota_old.zip ota_new.zip --blocksize 8M \
    --ddelta ~/vela/external/ddelta/ddelta/ddelta_generate --verify

# Any payload extension, or all files; compare two extracted build trees
miwear_ota old.zip new.zip --blocksize 32M -e bin img
miwear_ota ./out_old ./out_new --blocksize 32M -e all

# Byte-level comparison only, no patches, no block size needed
miwear_ota ota_old.zip ota_new.zip --no-ddelta

# Terminal summary only, no report files
miwear_ota ota_old.zip ota_new.zip --blocksize 32M --no-output
What the report explains
  • Per-file raw delta, deflated delta and delta/new ratios. ddelta writes uncompressed patches by design, so the deflated bundle is the number that maps to download cost.
  • Delta structure per DDELTA60 patch: header, control stream, diff data (derived from the old image) vs extra data (literal new bytes). Extra-dominated deltas mean little was reusable from the old image.
  • Change classification per 4 KB chunk: sparse fill (zeros → data), data cleared (data → zeros), in-place rewrite, and whether changes are localized or scattered.
  • Images that are byte-identical between packages, and files that exist in only one package.

miwear_serial

[!IMPORTANT] Requires pyserial. Install with pip install 'miwear[serial]'.

Interactive session
miwear_serial -p /dev/ttyACM0 -b 921600
# Press Ctrl+] to exit
One-shot command
miwear_serial -p /dev/ttyUSB1 -b 115200 -c "ps"
miwear_serial -p /dev/ttyUSB1 -b 115200 -c "ps" -r   # parse response
Periodic / batch
# Repeat on a timer
miwear_serial -p /dev/ttyACM1 -i 1.0 -c "ps"
miwear_serial -p /dev/ttyACM1 -i 2.0 -c "ps" --count 5

# Batch commands from a file
miwear_serial -f commands.txt
miwear_serial -f commands.txt -i 2.0 --count 5
Record to a log file
miwear_serial -p /dev/ttyACM0 -b 921600 -s             # miwear.log (default)
miwear_serial -p /dev/ttyACM0 -b 921600 -s log.txt     # custom path

Requirements

  • Python 3.10+
  • pyserial — only for miwear_serial

Development

Clone the repo and run the full lint + test + integration suite that mirrors CI:

git clone https://github.com/Junbo-Zheng/OpenMiwear.git
cd OpenMiwear

pip install flake8 black mypy pytest
./build.sh

build.sh runs flake8, black --check, mypy, pytest, and then exercises each CLI against the fixtures in tests/data/. GitHub Actions mirrors this on every push and pull request via .github/workflows/ci.yml, with the test job running against a Python 3.10 / 3.11 / 3.12 / 3.13 matrix.

Releases are automated through .github/workflows/publish.yml: publishing a GitHub Release triggers a build and uploads the distribution to PyPI via OIDC trusted publishing — no API tokens involved.

[!NOTE] The repository on GitHub is Junbo-Zheng/OpenMiwear, but the PyPI distribution and Python import/command names use the shorter miwear identifier (e.g. import miwear, miwear_log …). This is intentional — keep the project name OpenMiwear when referring to the repo, and the package name miwear when invoking the tools.

Release files for miwear 0.0.26

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for miwear 0.0.26
File Size Uploaded
miwear-0.0.26.tar.gz 78.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for miwear 0.0.26
File Interpreter ABI Platform
miwear-0.0.26-py3-none-any.whl Python 3 none any Details

Total release size: 163.1 kB

Release files / miwear-0.0.26.tar.gz

Download URL miwear-0.0.26.tar.gz
Size 78.2 kB
Tags Source
SHA-256 checksum
How to use checksums
38c8689bbba115472686ec44d77024f9e172b948f122e82e9018cdc04e3c387a
BLAKE2b-256 checksum
How to use checksums
d8c21a6c2b429be6af99adb1357083386d784282bdcfafd00524fcdc7cf52f43
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release files / miwear-0.0.26-py3-none-any.whl

Download URL miwear-0.0.26-py3-none-any.whl
Size 84.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
281d6682877f35f34e65c3e431fe83b29686f4c8670bbdad9f5abb5c96bcaf96
BLAKE2b-256 checksum
How to use checksums
b1ef63a2eeed8384b030262c99ac210e93ee8b20ddf9166ca5789c2cad1ce974
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release history Release notifications | RSS feed

This release

0.0.26 This release

2 release files

0.0.25

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page