Optimise photos for the SwitchBot 13.3" AI Art Frame (E Ink Spectra 6).
Project description
switchbot-e6-optimizer
Make photos look better on the SwitchBot 13.3" AI Art Frame and other E Ink Spectra 6 ("E6") panels.
E6 panels show only six muted inks and render dark, flat, and low-contrast to mimic paper. Uploaded straight from your phone, most photos come out dull and muddy. This tool applies the continuous-tone preparation the panel needs - boosted saturation and contrast, opened shadows, a warm nudge, and sharpening - then leaves the dithering to the SwitchBot app.
Do not pre-dither for the SwitchBot app. The app does its own colour mapping and dithering on upload. If you dither first you get double-dithering: visible noise, muddy colour.
Why the panel needs this
| Adjustment | Compensates for |
|---|---|
| Saturation (biggest lever) | Muted six-ink gamut |
| Contrast | Compressed (~26:1) dynamic range |
| Brightness + shadow lift | Panel renders dark and crushes shadows |
| Warmth | Paper-grey white, weak blues |
| Sharpen | Detail lost to dithering and sub-native resolution |
| No dithering | The app dithers; doing it twice = mud |
Requirements
- Python 3.14+
- uv (manages the environment, tests, and linting)
Install
git clone https://github.com/hamzic/switchbot-e6-optimizer
cd switchbot-e6-optimizer
uv sync # create the venv and install everything
Run it with uv run:
uv run switchbot-e6 photo.jpg
Or install the CLI as a standalone tool:
uv tool install .
switchbot-e6 photo.jpg
To uninstall the tool later:
uv tool uninstall switchbot-e6-optimizer
Usage
# one photo, default "medium" intensity -> photo_e6.png next to it
switchbot-e6 photo.jpg
# a whole folder into ./out, portrait mount
switchbot-e6 ~/Pictures/art --orientation portrait --out ./out
# punchier preset for flat, boldly-coloured art
switchbot-e6 poster.png --intensity high
# ride the high preset but pin saturation to exactly +80%
switchbot-e6 poster.png --intensity high --saturation 80
# crop + resize only, no tonal edits (A/B against the app's own processing)
switchbot-e6 photo.jpg --raw
Inputs can be files or whole folders. Running with no arguments prints the full
help. You can also run it as a module: python -m switchbot_e6_optimizer.
Supported formats
| Direction | Formats |
|---|---|
| Input | JPEG, PNG, WebP, BMP, TIFF, iPhone HEIC/HEIF |
| Output | PNG (default) or JPEG (--format jpg) |
When a folder is expanded, files already ending in the output suffix (*_e6)
are skipped with a notice so re-runs don't double-process previous output; pass
such a file explicitly to force it.
The exported file looks a little too vivid and too bright on your monitor;
that is correct for E6. After exporting, upload the *_e6 file to the
SwitchBot app as-is and fine-tune with the app's own saturation/contrast
sliders once you see it on the panel.
Intensity presets
Each preset sets every knob at once. medium is the default. Any single tuning
flag overrides just that value on top of the chosen preset.
| Preset | Saturation | Contrast | Brightness | Shadow lift | Warmth | Sharpen |
|---|---|---|---|---|---|---|
none / --raw |
- | - | - | - | - | - |
low |
+40% | +18% | +5% | 10 | 3 | 60 |
medium |
+60% | +25% | +8% | 18 | 5 | 80 |
high |
+100% | +30% | +12% | 26 | 8 | 100 |
none (or --raw) does crop + resize only, with no tonal changes.
Options
| Option | Default | Description |
|---|---|---|
-o, --output DIR (or --out) |
next to source | Output directory, created if missing |
--suffix S |
_e6 |
Output filename suffix |
--format {png,jpg} |
png |
Output format |
--quality N |
100 |
JPEG quality (1-100), used with --format jpg |
--orientation {landscape,portrait} |
landscape |
Panel mount |
--intensity {none,low,medium,high} |
medium |
Overall strength preset |
--raw |
off | Alias for --intensity none |
--saturation … --sharpen |
from preset | Override an individual knob (values must be ≥ 0) |
--no-crop |
off | Letterbox instead of crop-to-fill 4:3 |
--size WxH |
1600x1200 |
Override panel size |
The panel is 1600×1200 (4:3). By default the tool crops-to-fill (centred) so it
fills the frame; use --no-crop to contain the whole image with borders instead
(white with --raw, tinted slightly warm once a tonal preset is applied).
Development
Everything runs through uv:
uv sync # install deps + dev tools (pytest, ruff)
uv run pytest # run the test suite
uv run ruff check . # lint (PEP 8 + import sort + pyupgrade + bugbear)
uv run ruff format . # format
uv build # build sdist + wheel into dist/
Project structure
switchbot-e6-optimizer/
├── .github/
│ ├── dependabot.yml # weekly action-SHA + uv dep updates
│ ├── ruleset-main-protection.json # branch protection, applied via API
│ └── workflows/
│ ├── ci.yml # lint + tests on Linux/macOS/Windows
│ └── release.yml # gated build/publish (OIDC)
├── src/switchbot_e6_optimizer/
│ ├── __init__.py # version + public re-exports
│ ├── __main__.py # python -m switchbot_e6_optimizer
│ ├── optimizer.py # the pipeline + CLI
│ └── py.typed
├── tests/
│ └── test_optimizer.py
├── AGENTS.md # guidance for AI coding agents
├── CHANGELOG.md
├── LICENSE
├── README.md
├── pyproject.toml
└── uv.lock
Trademarks
"SwitchBot" and "E Ink" / "Spectra" are trademarks of their respective owners. This is an independent, unofficial tool - not affiliated with, endorsed by, or sponsored by SwitchBot or E Ink.
License
MIT - free to use, modify, and distribute; just keep the license notice so use traces back to this project.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file switchbot_e6_optimizer-1.0.0.tar.gz.
File metadata
- Download URL: switchbot_e6_optimizer-1.0.0.tar.gz
- Upload date:
- Size: 11.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a8b66e3517a4c9f481825e029c5776e37396886c601136665acd9c6993d1f284
|
|
| MD5 |
7772fe551d993a9595bb8055cc6b81ff
|
|
| BLAKE2b-256 |
c36d9f91f27dac4cc4055c3ded7cf6b45de72dbeab660a585e871e6183ee920b
|
File details
Details for the file switchbot_e6_optimizer-1.0.0-py3-none-any.whl.
File metadata
- Download URL: switchbot_e6_optimizer-1.0.0-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
22ad60e073c18ae5a5f30e176cc14a3eba882364ffeae32c931f4744892864e0
|
|
| MD5 |
bb598bafaf803475879179705e9cf168
|
|
| BLAKE2b-256 |
608720dc2685ebb0b9e0059caa8b906181eb07e539295ce05685a29bcfe30180
|