Lightweight alerting layer on top of ZoneMinderβs Event Server (ES). It listens to ES WebSocket events, enriches each event via the ZoneMinder API, and posts clean alerts to a Telegram group/topic.
- π‘ Reliable event forwarding from the ES (port 9000)
- π Enriched alerts (monitor name, cause/zone, scores, direct βView Eventβ link)
- π¬ Telegram delivery to a group topic/thread
- π©Ί Simple guard scripts to start/stop/status and auto-heal the ES + forwarder
- π One tidy folder:
/opt/iMouseGuard(code, config, env, logs, vendor)
ZoneMinder (API + ES:9000)
β (WebSocket)
βΌ
zmes_ws_to_telegram.py ββ(stdin JSON)βββΆ imouse_hook_alert.py
β β
ββββββββββββ guard scripts manage both ββ
β
βΌ
Telegram Bot
ββββββββββββββββββββββββββββββββ ZoneMinder βββββββββββββββββββββββββββββββββ
β Cameras β Events β Event Server (ES:9000 WebSocket) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ws://β¦/9000
βΌ
zmes_ws_to_telegram.py (forwarder)
- maintains WS connection
- deduplicates event IDs
- spawns hook subprocess per event
β argv: eid, mid
β stdin: {behavior, notes, monitor_name}
βΌ
imouse_hook_alert.py (hook)
- fetches ZM API details (event + monitor)
- formats alert text
- posts to Telegram (optional thread/topic)
β HTTPS
βΌ
Telegram Bot API β Your chat/topic
/opt/iMouseGuard
ββ bin/ # runnable scripts
β ββ zmes_ws_to_telegram.py
β ββ imouse_hook_alert.py
β ββ es-start es-stop
β ββ fwd-start fwd-stop
β ββ guard-start guard-stop guard-status guard-watch
ββ config/
β ββ zmeventnotification.ini
ββ env/
β ββ prod.env # exported environment vars
ββ logs/
β ββ es.log
β ββ forwarder.log
β ββ guard.log
β
ββ zmeventnotification/ # ES source (perl script + docs)
ββ venv/ # python virtual environment
- ZoneMinder running and reachable from this host/container (API and DB OK).
- ES Perl deps (installed once):
cpanm JSON JSON::XS Net::WebSocket::Server(Ifcpanmisnβt present:apt-get update && apt-get install -y cpanminus.)
# 1) Create base tree
sudo mkdir -p /opt/iMouseGuard/{bin,config,env,logs,vendor}
sudo chown -R root:root /opt/iMouseGuard
# 2) Python venv + deps
python3 -m venv /opt/iMouseGuard/venv
/opt/iMouseGuard/venv/bin/pip install --upgrade pip
/opt/iMouseGuard/venv/bin/pip install websocket-client requests
# 3) Environment file (edit values!)
cat >/opt/iMouseGuard/env/prod.env <<'EOF'
export TELEGRAM_TOKEN='YOUR_TELEGRAM_BOT_TOKEN'
export TELEGRAM_CHAT_ID='-100XXXXXXXXXX' # group ID (negative for supergroup)
export TELEGRAM_THREAD_ID='3' # topic id (optional)
export IMOUSE_API_BASE='http://127.0.0.1' # ZM API base
export IMOUSE_WEB_BASE='http://10.0.2.2' # for βView Eventβ link
export WS_URL='ws://127.0.0.1:9000' # ES WebSocket endpoint
export WS_SEND_AUTH=0 # 1 to send ES credentials
export ES_USER=''
export ES_PASSWORD=''
EOF
chmod 600 /opt/iMouseGuard/env/prod.env
. /opt/iMouseGuard/env/prod.env
# 4) Put your two Python scripts into /opt/iMouseGuard/bin (chmod +x them)
# (zmes_ws_to_telegram.py and imouse_hook_alert.py)
# 5) Place zmeventnotification (vendor) and a minimal config:
cat >/opt/iMouseGuard/config/zmeventnotification.ini <<'EOF'
[general]
port = 9000
address = ::
event_check_interval = 5
monitor_reload_interval = 300
verbose = yes
es_debug_level = 5
send_event_start_notification = yes
send_event_end_notification = no
[auth]
enable = no
[ssl]
enable = no
[hook]
enable = no
[push]
enable = fcm
EOF
# 6) Start ES and forwarder
/opt/iMouseGuard/bin/es-start
/opt/iMouseGuard/bin/fwd-start
# 7) Check status/logs
/opt/iMouseGuard/bin/guard-status
tail -n 60 /opt/iMouseGuard/logs/{es.log,forwarder.log}bin/zmes_ws_to_telegram.py
-
Connects to
WS_URL(ES port 9000) and receives alarm frames. -
For each new event, calls the hook (
imouse_hook_alert.py) with:- argv:
eid,mid - stdin JSON payload (
behavior,notes, monitor name if present)
- argv:
Start/stop:
/opt/iMouseGuard/bin/fwd-start
/opt/iMouseGuard/bin/fwd-stopbin/imouse_hook_alert.py
- Reads env from
env/prod.env(no secrets hard-coded). - Looks up event/monitor via ZM API (
/api/events/view/{eid}.json,/api/monitors/view/{mid}.json). - Builds a clean message and posts to Telegram (uses
TELEGRAM_THREAD_IDif set).
If your ZM web is not
127.0.0.1, setIMOUSE_WEB_BASEso βView Eventβ opens correctly.
es-start/es-stopβ run/stop the Perl ES with our config intologs/es.log.fwd-start/fwd-stopβ run/stop the forwarder intologs/forwarder.log.guard-statusβ quick health check (are ES & forwarder up? is port 9000 listening?)guard-watchβ tiny loop that restarts ES if port 9000 stops listening and restarts the forwarder if it dies. You can run it persistently vianohupor a simplecron @reboot.
Example cron entry:
# crontab -e
@reboot /opt/iMouseGuard/bin/guard-start
* * * * * /opt/iMouseGuard/bin/guard-watch| Name | What it does |
|---|---|
TELEGRAM_TOKEN |
Bot token (from @BotFather). |
TELEGRAM_CHAT_ID |
Target chat (group) ID. |
TELEGRAM_THREAD_ID |
Topic/thread ID inside the group (optional). |
IMOUSE_API_BASE |
Base URL for ZM API (e.g., http://127.0.0.1). |
IMOUSE_WEB_BASE |
Base URL for web links (e.g., http://10.0.2.2). |
WS_URL |
ES WebSocket URL (ws://127.0.0.1:9000). |
WS_SEND_AUTH |
1 to send ES credentials, else blank auth frame is sent. |
ES_USER/ES_PASSWORD |
If ES auth is enabled. |
Load into current shell:
. /opt/iMouseGuard/env/prod.env- ES:
/opt/iMouseGuard/logs/es.log - Forwarder:
/opt/iMouseGuard/logs/forwarder.log - Guard:
/opt/iMouseGuard/logs/guard.log
Helpful tails:
tail -n 80 /opt/iMouseGuard/logs/es.log
tail -n 80 /opt/iMouseGuard/logs/forwarder.logES not listening on 9000
-
Check
/opt/iMouseGuard/logs/es.logfirst lines for missing Perl modules.- Install:
cpanm JSON JSON::XS Net::WebSocket::Server
- Install:
-
Confirm the config path in
es-startmatchesconfig/zmeventnotification.ini.
Forwarder says βConnection refusedβ
- ES isnβt up or port isnβt open inside this container/host.
Run:
ss -ltnp | grep :9000(ornetstat -lntp) Start ES:/opt/iMouseGuard/bin/es-start
Telegram error: βURL canβt contain control charactersβ
-
Your token/chat/thread env values probably have stray quotes or spaces. Open
env/prod.env, remove quotes around numbers, ensure one value per line:export TELEGRAM_TOKEN='123:ABC...' # quotes fine for token export TELEGRAM_CHAT_ID=-1002597925763 # no quotes for pure numbers export TELEGRAM_THREAD_ID=3 # optional, numeric
Event links open to 127.0.0.1
- Set
IMOUSE_WEB_BASEto your browser-reachable address, e.g.,http://10.0.2.2.
Lots of βUse of uninitialized value β¦β lines in es.log
- These are harmless warnings from ES when some fields are blank. They donβt affect alerting.
- Keep
env/prod.envmode600(chmod 600) to protect the bot token. - If you need TLS or ES auth, enable it in
config/zmeventnotification.iniand setWS_SEND_AUTH=1.
/opt/iMouseGuard/bin/fwd-stop || true
/opt/iMouseGuard/bin/es-stop || true
rm -rf /opt/iMouseGuard- Maintains a WebSocket connection to the ES (reconnects with backoff).
- Normalizes different ES payload shapes (different field names across ES versions).
- Debounces duplicate event IDs.
- Triggers the hook for each unique event.
- Accepts
(eid, mid)on argv and JSON on stdin. - Calls ZM API to enrich the alert (event timing, max score, monitor name).
- Posts a formatted message to Telegram (optionally in a thread).
π Folder & File Permissions for iMouseGuard
- Top-level folder
Path: /opt/iMouseGuard
Owner: root:root (or a dedicated imouse user if you want to isolate)
Mode: 750 (rwx for owner, rx for group, no access for others)
chown -R root:root /opt/iMouseGuard chmod 750 /opt/iMouseGuard
- Subfolders Folder Purpose Mode bin/ scripts & executables 750 config/ INI, YAML configs 640 env/ prod.env with tokens 600 (strict) logs/ runtime logs 750 (so only owner & group can read/write) state/ last_seen.json etc. 750 var/ runtime tmp, push tokens 750
- Files File type Example Mode Notes Executables bin/imouse_hook_alert.py 755 executable by system/service Configs config/zmes_ws_only.ini 640 only owner+group can read Env vars env/prod.env 600 secrets: bot token, chat id Logs logs/es.log 640 optional: 644 if you want world-readable State files state/last_seen.json 640
- Commands (safe defaults)
chmod 755 /opt/iMouseGuard/bin/*
chmod 640 /opt/iMouseGuard/config/*
chmod 600 /opt/iMouseGuard/env/prod.env
chmod 750 /opt/iMouseGuard/logs /opt/iMouseGuard/state /opt/iMouseGuard/var chmod 640 /opt/iMouseGuard/logs/* /opt/iMouseGuard/state/* 2>/dev/null || true
- Why not 777 or 644?
777 β anyone can tamper/run maliciously.
644 on secrets β leaks tokens.
600 on prod.env ensures only root (or the service account) can read Telegram tokens & DB creds.
755 on scripts ensures theyβre executable but not writable by others.