Screen Diff Watcher is a tool for check if a region of your screen/app is changed and alert to you
Python
3
2 commits
updated Oct 1, 2026
English · Português (Brasil)
Watches a rectangular region (ROI) of a window and alerts you when it changes — sound, popup, Telegram, webhook/HTTP POST, syslog or log — so you don't have to keep an eye on the screen.
Runs on Windows and Linux, capturing pixels only (it never touches the watched application).
Wiki — usage guide · Architecture and specification · Build and release · Changelog
light (mean color), default (perceptual hash) and
advanced (OCR + text diff; requires Tesseract), including a text watch that fires only when a
text appears/disappears in the ROI (text_watch, advanced only).miniaudio), and the GUI has a
picker that previews the file and hands you the file: "..." snippet for the YAML. The default
sound is a bundled alert.mp3 shipped with the app; a relative file is resolved as
app-data/sounds/ → bundled assets/sounds/ → CWD.logs/actions.jsonl.input extra and must be armed.select overlay, or coordinates via
select-manual).poll_interval_s, the app captures the ROI. The first frame is the baseline — not a
change.config.yaml, selections/, state.json and logs/.target window ──► ROI ──► capture ──► mask ──► compare (light/default/advanced)
│ changed?
▼
alerts (sound/popup/Telegram/log + webhook/HTTP POST/syslog)
+ actions (if armed)
When a change is confirmed, the alert chain fires the channels enabled in the profile, each with
its own severity_min and cooldown_s:
Channel (type) | What it does | Details |
|---|---|---|
sound | plays a local sound (WAV/MP3/M4A/AAC/OGG/FLAC…) | wiki/Alerts.md |
popup | local notification | wiki/Alerts.md |
telegram | message + ROI image via bot | Telegram setup — step by step |
log | one JSON line per alert (logs/alerts.jsonl) | wiki/Alerts.md |
webhook | JSON POST/PUT/PATCH to a webhook URL (Teams Workflows, Slack, Discord, Mattermost) | wiki/Alerts.md |
http_post | JSON POST to a host/IP + port (or a full URL) | wiki/Alerts.md |
syslog | informational syslog message (udp/tcp) — no image | wiki/Alerts.md |
The alerts are configured per profile in config.yaml (the alerts: list), each with an optional stable
id. Secrets never go in the YAML — the Telegram token is read from an environment variable, and the new
channels accept url_env/${env:VAR}. Test a single channel with test-alert --list/--only ID or the
window's Test alert… button. The channel map is extensible by type.
advanced mode.| Platform | How to run | Status |
|---|---|---|
| Windows (x64) | .exe installer (Inno Setup) or from source | supported; the installer downloads Tesseract automatically (optional) |
| Linux Debian/Ubuntu (amd64, X11) | .deb package or from source | supported; Wayland is not supported for capture |
| macOS | from source only | not validated and no installer (outside the build scope) |
Per-platform details: wiki/Installation.md.
If you installed from the binaries, the command is screen-watch; from source, use
python -m screen_watch.
python -m screen_watch list-windows # 1. pick the window (note the handle)
python -m screen_watch select --handle 12345 --name panel # 2. draw the ROI in the overlay
python -m screen_watch test-alert --selection panel # 3. check the alert
python -m screen_watch run --selection panel # 4. monitor
python -m screen_watch gui # ...or use the GUI with tray
The shortest path is the GUI: New Target (overlay) → draw the ROI → Start. The selection is
saved in app-data (selections/panel.json) and can be reused by run.
Quick command reference (details in wiki/CLI-Usage.md):
python -m screen_watch init-config # create the v2 config.yaml in app-data
python -m screen_watch validate-config --selections
python -m screen_watch list-windows # handle/title/rect
python -m screen_watch probe-dpi # DPI matrix (mss physical × Qt logical)
python -m screen_watch select --handle 12345 --name panel # overlay: drag on screen
python -m screen_watch select-manual --handle 12345 --roi 120 340 400 80 --name panel
python -m screen_watch list-selections
python -m screen_watch migrate-config --dry-run # convert v1 YAML -> v2
python -m screen_watch test-alert --selection panel # synthetic alert
python -m screen_watch test-alert --selection panel --list # id/type/state/destination
python -m screen_watch test-alert --selection panel --only ID # single channel (text mode)
python -m screen_watch test-evidence --selection panel # sample prints
python -m screen_watch test-action --selection panel # actions rehearsal (--armed executes)
python -m screen_watch list-actions --selection panel
python -m screen_watch record-actions --selection panel --out snippet.yaml
python -m screen_watch compare-modes --selection panel --delay 5 # calibration
python -m screen_watch show-paths
python -m screen_watch run --selection panel # monitor
python -m screen_watch gui # GUI + tray
python -m screen_watch features # environment diagnostics
python -m screen_watch validate-i18n # validate the language catalogs
The global flags --language TAG (GUI language) and --verbose come before the subcommand, for
example: python -m screen_watch --language en-US gui.
To use the installers (Windows/Linux):
light/default modes.por + eng traineddata) for advanced mode — on Windows the
installer downloads it on demand; in the .deb it comes as a dependency.TELEGRAM_BOT_TOKEN environment variable (never in the
YAML) — step-by-step in wiki/Telegram-Setup.md.input extra (pynput).run uses miniaudio
(WAV/MP3/OGG/FLAC); the GUI uses Qt Multimedia (adds M4A/AAC/WMA on Windows/macOS). On Linux
the GUI falls back on the GStreamer plugins, and the legacy path uses winsound (WAV) or an
external player (paplay/aplay/ffplay).To run from source: Python 3.11+ and the extras you need:
python -m venv .venv
.\.venv\Scripts\Activate.ps1 # Linux/macOS: source .venv/bin/activate
python -m pip install -e ".[dev]" # core + tests (ruff/pytest)
python -m pip install -e ".[input]" # optional: actions/hotkeys (pynput)
python -m pip install -e ".[sound]" # optional: sound via simpleaudio
Download from GitHub Releases:
screen-diff-watcher_<version>_windows_x64_setup.exe (Inno Setup, per-machine,
requires admin). Creates Start Menu shortcuts and, optionally, a Desktop shortcut and Windows
startup. SmartScreen will warn (the .exe is unsigned): use "More info" → "Run anyway"; your
antivirus may do the same.screen-watch_<version>_amd64.deb
(sudo apt install ./screen-watch_<version>_amd64.deb). The package declares tesseract-ocr +
tesseract-ocr-por and the Qt6/X11 libs.python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"
python -m screen_watch --help
Detailed installation (automatic Tesseract, Linux autostart, uninstall, app-data, features
diagnostics): wiki/Installation.md.
The installers are built on the target OS (no cross-build) by scripts/build_release.py.
Full guide: doc/01-Build_and_Release.md.
python -m pip install -e ".[dev,build,input]"
python scripts/build_release.py --windows # on Windows (requires Inno Setup 6 / ISCC.exe)
python scripts/build_release.py --linux # on Linux (requires dpkg-deb)
The script reads the version from screen_watch.__version__ (single source; pyproject.toml is
dynamic), runs PyInstaller and writes the artifacts + build-info.json to dist/installers/. To
publish, create the tag vX.Y.Z (equal to __version__) and push: the
.github/workflows/release.yml workflow builds both installers, generates SHA256SUMS.txt and
creates the GitHub Release. workflow_dispatch generates artifacts only (no release).
Implemented: capture and anchoring (Model B), comparison modes (light/default/advanced)
with pipeline and short-circuit (advanced gated by phash, bypassed by text_watch), alerts
(sound/popup/Telegram/log + webhook/HTTP POST/syslog) with cooldown/re-arm and send test,
selectable sound (MP3/M4A/OGG/FLAC…) with a bundled alert.mp3 default, the
text_watch filter (appears/disappears),
selection name (renames the file to its slug), Highlight (ROI outline that never touches the
ROI pixels), double-click region re-edit (Enter starts/stops) and the 2×2 window layout,
evidence, pseudo-human actions (with GUI editor and recorder), scheduler, profiles, full CLI,
GUI + tray with i18n (pt-BR/en-US), packaging (Inno Setup and .deb) and tag-driven CI/release.
Manual validation pending: GUI/tray/overlay at 100/125/150% (doc §5.1, §9.5) and bundle details
on a clean machine (icon, StartupWMClass, package size, SmartScreen warning) — checklist in
doc/01 §9.
Radar: a fully CLI-driven selection flow (no overlay) is not defined yet — revisit when there is demand.
miniaudio covers WAV/MP3/OGG/FLAC — M4A/AAC needs a player like ffplay, otherwise the
alert falls back to beep. It never breaks.target_unavailable and the loop keeps trying (it never
breaks).Full list, display scaling (DPI) and robustness notes: wiki/DPI-and-Limitations.md.
ruff check .
python -m pytest -q -m "not integration"
Integration tests are opt-in (TEST_REAL_CAPTURE, TEST_REAL_TELEGRAM,
TEST_REAL_WEBHOOK_URL, TEST_REAL_HTTP_URL) — details, script ladder and CI in
wiki/Development-Tests-and-CI.md.
| Where | What it has |
|---|---|
| Wiki | usage and feature details: CLI, GUI, config, actions, alerts, evidence, languages, DPI, build |
doc/00-Architecture_and_Specification.md | architecture and specification — single source of truth for the design |
doc/01-Build_and_Release.md | installer build and release pipeline |
doc/releases/ | per-version release notes (detail file) |
CHANGELOG.md | changes per version (semantic) |
README.pt-BR.md | este guia em português |
Design note:
doc/00decides the design; the Wiki describes usage and features. Never record a design decision here without it being (or having to be) indoc/00.
Python
98.7%
Screen Diff Watcher is a tool for check if a region of your screen/app is changed and alert to you
Python
3
2 commits
updated Oct 1, 2026
English · Português (Brasil)
Watches a rectangular region (ROI) of a window and alerts you when it changes — sound, popup, Telegram, webhook/HTTP POST, syslog or log — so you don't have to keep an eye on the screen.
Runs on Windows and Linux, capturing pixels only (it never touches the watched application).
Wiki — usage guide · Architecture and specification · Build and release · Changelog
light (mean color), default (perceptual hash) and
advanced (OCR + text diff; requires Tesseract), including a text watch that fires only when a
text appears/disappears in the ROI (text_watch, advanced only).miniaudio), and the GUI has a
picker that previews the file and hands you the file: "..." snippet for the YAML. The default
sound is a bundled alert.mp3 shipped with the app; a relative file is resolved as
app-data/sounds/ → bundled assets/sounds/ → CWD.logs/actions.jsonl.input extra and must be armed.select overlay, or coordinates via
select-manual).poll_interval_s, the app captures the ROI. The first frame is the baseline — not a
change.config.yaml, selections/, state.json and logs/.target window ──► ROI ──► capture ──► mask ──► compare (light/default/advanced)
│ changed?
▼
alerts (sound/popup/Telegram/log + webhook/HTTP POST/syslog)
+ actions (if armed)
When a change is confirmed, the alert chain fires the channels enabled in the profile, each with
its own severity_min and cooldown_s:
Channel (type) | What it does | Details |
|---|---|---|
sound | plays a local sound (WAV/MP3/M4A/AAC/OGG/FLAC…) | wiki/Alerts.md |
popup | local notification | wiki/Alerts.md |
telegram | message + ROI image via bot | Telegram setup — step by step |
log | one JSON line per alert (logs/alerts.jsonl) | wiki/Alerts.md |
webhook | JSON POST/PUT/PATCH to a webhook URL (Teams Workflows, Slack, Discord, Mattermost) | wiki/Alerts.md |
http_post | JSON POST to a host/IP + port (or a full URL) | wiki/Alerts.md |
syslog | informational syslog message (udp/tcp) — no image | wiki/Alerts.md |
The alerts are configured per profile in config.yaml (the alerts: list), each with an optional stable
id. Secrets never go in the YAML — the Telegram token is read from an environment variable, and the new
channels accept url_env/${env:VAR}. Test a single channel with test-alert --list/--only ID or the
window's Test alert… button. The channel map is extensible by type.
advanced mode.| Platform | How to run | Status |
|---|---|---|
| Windows (x64) | .exe installer (Inno Setup) or from source | supported; the installer downloads Tesseract automatically (optional) |
| Linux Debian/Ubuntu (amd64, X11) | .deb package or from source | supported; Wayland is not supported for capture |
| macOS | from source only | not validated and no installer (outside the build scope) |
Per-platform details: wiki/Installation.md.
If you installed from the binaries, the command is screen-watch; from source, use
python -m screen_watch.
python -m screen_watch list-windows # 1. pick the window (note the handle)
python -m screen_watch select --handle 12345 --name panel # 2. draw the ROI in the overlay
python -m screen_watch test-alert --selection panel # 3. check the alert
python -m screen_watch run --selection panel # 4. monitor
python -m screen_watch gui # ...or use the GUI with tray
The shortest path is the GUI: New Target (overlay) → draw the ROI → Start. The selection is
saved in app-data (selections/panel.json) and can be reused by run.
Quick command reference (details in wiki/CLI-Usage.md):
python -m screen_watch init-config # create the v2 config.yaml in app-data
python -m screen_watch validate-config --selections
python -m screen_watch list-windows # handle/title/rect
python -m screen_watch probe-dpi # DPI matrix (mss physical × Qt logical)
python -m screen_watch select --handle 12345 --name panel # overlay: drag on screen
python -m screen_watch select-manual --handle 12345 --roi 120 340 400 80 --name panel
python -m screen_watch list-selections
python -m screen_watch migrate-config --dry-run # convert v1 YAML -> v2
python -m screen_watch test-alert --selection panel # synthetic alert
python -m screen_watch test-alert --selection panel --list # id/type/state/destination
python -m screen_watch test-alert --selection panel --only ID # single channel (text mode)
python -m screen_watch test-evidence --selection panel # sample prints
python -m screen_watch test-action --selection panel # actions rehearsal (--armed executes)
python -m screen_watch list-actions --selection panel
python -m screen_watch record-actions --selection panel --out snippet.yaml
python -m screen_watch compare-modes --selection panel --delay 5 # calibration
python -m screen_watch show-paths
python -m screen_watch run --selection panel # monitor
python -m screen_watch gui # GUI + tray
python -m screen_watch features # environment diagnostics
python -m screen_watch validate-i18n # validate the language catalogs
The global flags --language TAG (GUI language) and --verbose come before the subcommand, for
example: python -m screen_watch --language en-US gui.
To use the installers (Windows/Linux):
light/default modes.por + eng traineddata) for advanced mode — on Windows the
installer downloads it on demand; in the .deb it comes as a dependency.TELEGRAM_BOT_TOKEN environment variable (never in the
YAML) — step-by-step in wiki/Telegram-Setup.md.input extra (pynput).run uses miniaudio
(WAV/MP3/OGG/FLAC); the GUI uses Qt Multimedia (adds M4A/AAC/WMA on Windows/macOS). On Linux
the GUI falls back on the GStreamer plugins, and the legacy path uses winsound (WAV) or an
external player (paplay/aplay/ffplay).To run from source: Python 3.11+ and the extras you need:
python -m venv .venv
.\.venv\Scripts\Activate.ps1 # Linux/macOS: source .venv/bin/activate
python -m pip install -e ".[dev]" # core + tests (ruff/pytest)
python -m pip install -e ".[input]" # optional: actions/hotkeys (pynput)
python -m pip install -e ".[sound]" # optional: sound via simpleaudio
Download from GitHub Releases:
screen-diff-watcher_<version>_windows_x64_setup.exe (Inno Setup, per-machine,
requires admin). Creates Start Menu shortcuts and, optionally, a Desktop shortcut and Windows
startup. SmartScreen will warn (the .exe is unsigned): use "More info" → "Run anyway"; your
antivirus may do the same.screen-watch_<version>_amd64.deb
(sudo apt install ./screen-watch_<version>_amd64.deb). The package declares tesseract-ocr +
tesseract-ocr-por and the Qt6/X11 libs.python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"
python -m screen_watch --help
Detailed installation (automatic Tesseract, Linux autostart, uninstall, app-data, features
diagnostics): wiki/Installation.md.
The installers are built on the target OS (no cross-build) by scripts/build_release.py.
Full guide: doc/01-Build_and_Release.md.
python -m pip install -e ".[dev,build,input]"
python scripts/build_release.py --windows # on Windows (requires Inno Setup 6 / ISCC.exe)
python scripts/build_release.py --linux # on Linux (requires dpkg-deb)
The script reads the version from screen_watch.__version__ (single source; pyproject.toml is
dynamic), runs PyInstaller and writes the artifacts + build-info.json to dist/installers/. To
publish, create the tag vX.Y.Z (equal to __version__) and push: the
.github/workflows/release.yml workflow builds both installers, generates SHA256SUMS.txt and
creates the GitHub Release. workflow_dispatch generates artifacts only (no release).
Implemented: capture and anchoring (Model B), comparison modes (light/default/advanced)
with pipeline and short-circuit (advanced gated by phash, bypassed by text_watch), alerts
(sound/popup/Telegram/log + webhook/HTTP POST/syslog) with cooldown/re-arm and send test,
selectable sound (MP3/M4A/OGG/FLAC…) with a bundled alert.mp3 default, the
text_watch filter (appears/disappears),
selection name (renames the file to its slug), Highlight (ROI outline that never touches the
ROI pixels), double-click region re-edit (Enter starts/stops) and the 2×2 window layout,
evidence, pseudo-human actions (with GUI editor and recorder), scheduler, profiles, full CLI,
GUI + tray with i18n (pt-BR/en-US), packaging (Inno Setup and .deb) and tag-driven CI/release.
Manual validation pending: GUI/tray/overlay at 100/125/150% (doc §5.1, §9.5) and bundle details
on a clean machine (icon, StartupWMClass, package size, SmartScreen warning) — checklist in
doc/01 §9.
Radar: a fully CLI-driven selection flow (no overlay) is not defined yet — revisit when there is demand.
miniaudio covers WAV/MP3/OGG/FLAC — M4A/AAC needs a player like ffplay, otherwise the
alert falls back to beep. It never breaks.target_unavailable and the loop keeps trying (it never
breaks).Full list, display scaling (DPI) and robustness notes: wiki/DPI-and-Limitations.md.
ruff check .
python -m pytest -q -m "not integration"
Integration tests are opt-in (TEST_REAL_CAPTURE, TEST_REAL_TELEGRAM,
TEST_REAL_WEBHOOK_URL, TEST_REAL_HTTP_URL) — details, script ladder and CI in
wiki/Development-Tests-and-CI.md.
| Where | What it has |
|---|---|
| Wiki | usage and feature details: CLI, GUI, config, actions, alerts, evidence, languages, DPI, build |
doc/00-Architecture_and_Specification.md | architecture and specification — single source of truth for the design |
doc/01-Build_and_Release.md | installer build and release pipeline |
doc/releases/ | per-version release notes (detail file) |
CHANGELOG.md | changes per version (semantic) |
README.pt-BR.md | este guia em português |
Design note:
doc/00decides the design; the Wiki describes usage and features. Never record a design decision here without it being (or having to be) indoc/00.
Python
98.7%