Real-time monitoring of Dahua DVRs via Easy4IPCloud, with a FastAPI backend and a static web UI. The app reads an Excel workbook of devices, periodically checks their online status through Dahua's cloud, exposes a REST API, and serves a simple dashboard.
DVR.mp4
- Excel-driven inventory: Loads devices from
P2P1.xlsxwith columnsP2P NUMBER,SITE,STORE NAME. - Background status scanner: Periodically probes devices via Easy4IPCloud and caches results in-memory.
- REST API: Query aggregate stats, list devices by status, search by site, export CSV, refresh scans, and update P2P numbers.
- Static web UI: If
web/index.htmlexists, it is served at/with static assets under/static.
server.py: FastAPI app, background scanner, REST endpoints, static file servingcheck_online.py: CLI for single-device or bulk (Excel) status checkshelpers.py: Low-level UDP/WSSE protocol helpers for Easy4IPCloudP2P1.xlsx: Input Excel file of devices (not versioned typically)web/: Frontend assets (served if present)requirements.txt: Python dependencies
- On startup,
server.pyloadsP2P1.xlsx. Column names are normalized to uppercase, and onlyP2P NUMBER,SITE,STORE NAMEare retained. - P2P serial values are normalized to strings; numeric cells like
123456.0are converted to123456.
- A daemon thread runs a loop that refreshes device statuses on a cadence (default ~10 seconds in current code). Results are cached in-memory, keyed by
P2P NUMBER. - Status lookups are executed concurrently using a thread pool to minimize total latency.
- For each serial, the app resolves the target P2P service endpoint via
GET /online/p2psrv/{serial}onwww.easy4ipcloud.com:8800. - It then probes and fetches device info via UDP-based requests; a device is considered online when a valid info response is returned.
- FastAPI serves JSON endpoints under
/api/*and optionally serves the static UI fromweb/.
- Python 3.10+
- Network egress to
www.easy4ipcloud.com:8800 - An Excel workbook
P2P1.xlsxplaced in the project root with required columns
python -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -r requirements.txtpython -m uvicorn server:app --host 0.0.0.0 --port 8000 --reload- Open the UI at
http://localhost:8000/ifweb/index.htmlexists; otherwise the API root returns a basic JSON message.
Check a single serial:
python check_online.py <SERIAL>Check from an Excel file and print offline entries:
python check_online.py --excel P2P1.xlsx- The workbook must contain a sheet with the following columns (case-insensitive; they are normalized on load):
P2P NUMBER: Dahua device serial (string or number)SITE: Unique site identifierSTORE NAME: Human-friendly store name
- Only these three columns are used by the backend; any extra columns are ignored when loading.
Base URL: http://{host}:{port} (default http://localhost:8000)
Returns aggregate counts and last update epoch.
{
"total": 123,
"online": 100,
"offline": 23,
"lastUpdated": 1712345678.123
}Returns device rows filtered by status.
{ "items": [ {"P2P NUMBER":"...","SITE":"...","STORE NAME":"..."} ] }Finds a single row by exact SITE and includes computed status field.
{ "P2P NUMBER":"...","SITE":"...","STORE NAME":"...","status":"online" }Update the P2P NUMBER for a given SITE in both memory and Excel; invalidates cached status.
{ "site": "SITE-001", "p2pNumber": "NEW_SERIAL" }Responses: { "ok": true } or 404 if site not found.
Triggers an immediate rescan and returns updated stats.
{ "ok": true, "total": 123, "online": 101, "offline": 22, "lastUpdated": 1712345800.456 }Streams a CSV attachment of filtered rows with header: P2P NUMBER,SITE,STORE NAME.
curl -s http://localhost:8000/api/stats | jq
curl -s "http://localhost:8000/api/dvrs?status=offline" | jq
curl -s "http://localhost:8000/api/search?site=SITE-001" | jq
curl -s -X POST http://localhost:8000/api/update-p2p \
-H "Content-Type: application/json" \
-d '{"site":"SITE-001","p2pNumber":"ABC123456"}' | jq
curl -s -X POST http://localhost:8000/api/refresh | jq
curl -OJ "http://localhost:8000/api/download.csv?status=offline"- Scanner concurrency: controlled internally by a thread pool (default up to 20 workers). Adjust in code if needed (
scan_statuses(max_workers=...)). - Scan interval: controlled in
server.pyscanner loop (default sleep ~10s between scans). Adjust as desired. - CORS: permissive by default to simplify hosting UI separately; tweak in
server.pymiddleware setup.
- FastAPI, Uvicorn, pandas, openpyxl, cryptography, xmltodict, aiofiles
- See
requirements.txtfor the exact list.
- The low-level integration uses credentials/constants in
helpers.py(e.g., username key material). Treat these as secrets and rotate/change handling if distributing publicly. - The app does not persist PII beyond the contents of
P2P1.xlsx. Handle the Excel file in accordance with your data policies.
- "Excel file not found": ensure
P2P1.xlsxexists in the repository root. - "Missing required columns": verify the sheet has
P2P NUMBER,SITE,STORE NAME(case-insensitive). - All devices show offline: check network/firewall egress to
www.easy4ipcloud.com:8800. - Slow scans or timeouts: reduce
max_workers, increase socket timeout inhelpers.py, or increase scan interval. - Status doesn’t change immediately after updating a P2P number: invoke
POST /api/refreshor wait for the next scheduled scan.
- Linting/formatting/testing are not included by default; add your preferred tooling.
- The UI in
web/is served at/when present, and static assets are available at/static.