Wiznet makers

josephsr

Published February 10, 2026 ©

104 UCC

13 WCC

13 VAR

0 Contests

0 Followers

0 Following

Original Link

PowerBot: Telegram Power Status Bot with ESP32 Heartbeats and Read-Only Status API

Docker-deployed Telegram bot infers building power status from ESP32 heartbeats, stores in SQLite, notifies users, and exposes a read-only HTTP status API.

COMPONENTS
PROJECT DESCRIPTION

Overview

PowerBot is a Telegram bot stack that tracks “is power up” per building/section using periodic heartbeat POSTs from ESP32 sensors. It stores state in SQLite, notifies subscribers in Telegram, and also exposes HTTP endpoints (health, sensor status, public read-only status) behind a reverse proxy.

Main Content

Image generated by gpt

Inputs

  • ESP32 sensors send POST /api/v1/heartbeat with {api_key, building_id, section_id, sensor_uuid, ...}.
  • Telegram users interact with the bot via polling (commands, callbacks, menus).
  • Optional external data sources appear wired in via configuration (weather coordinates, alert APIs, planned outage schedules).

Processing (end-to-end flow)

  • Heartbeat validation: the API server checks the sensor API key and basic payload types.
  • Canonical mapping: sensor_uuid can override a reported building_id so firmware-side IDs cannot silently drift the backend’s “source of truth.”
  • Section normalization: missing/invalid section_id can be defaulted, with stricter handling depending on whether the sensor is in the canonical map.
  • Persistence: the heartbeat is upserted into SQLite (sensor record + last heartbeat timestamp).
  • Status inference: a sensor is considered online if last_heartbeat is recent; building/section “power up” becomes true if any sensor (or virtual alias) is effectively online.
  • Monitoring loop: the bot runtime starts background tasks for sensor monitoring, alert monitoring, optional outage schedule monitoring, and an admin job worker.

Outputs

  • Telegram UI updates: users see current power status (and other menu items), and subscribers can receive notifications.
  • Public read-only status API: GET /api/v1/public/sensors/status and per-sensor status provide external consumption without revealing the write key.
  • Admin/business sidecars (optional): separate bot entrypoints can be started via Docker Compose profiles.

System Context

Role

  • Resident-facing channel: a Telegram bot that answers “is power up” (and related context) for a building/section.
  • Data-plane ingestion: an HTTP API that receives sensor heartbeats and updates state.
  • External integration surface: a read-only API for third parties to query sensor status.

Boundary

  • Assumes sensors can reach the HTTP endpoint and that Telegram polling is permitted from the host environment.
  • Treats “power up” as a proxy inferred from sensor presence/heartbeat recency, not direct voltage measurement semantics in the backend itself.

Interfaces

  • Sensor -> API: POST /api/v1/heartbeat with shared secret key.
  • Client -> Public API: GET /api/v1/public/sensors/status authenticated with a separate read-only key (header/bearer/query supported).
  • Operations -> Stack: Docker Compose services plus reverse-proxy labels (Traefik).
  • Storage: SQLite database mounted as a shared volume for multiple services (main bot, business bot, admin bot, migrations).

Architecture / Design Considerations

  • Multi-process, single-DB choice: several services share one SQLite file, with an explicit volume mount to keep WAL/SHM alongside the DB for consistency across containers. This is simple to deploy but increases sensitivity to write contention and file-lock behavior.
  • “Canonical mapping” as risk control: binding sensor_uuid -> building_id inside backend config reduces the blast radius of misconfigured firmware IDs and prevents a single rogue payload from reassigning a sensor to the wrong building.
  • Aliasing as a pragmatic bridge: “virtual sensor” aliasing lets one physical signal represent multiple sections/buildings (shared lines). It improves user experience but can make history/statistics ambiguous unless the mapping is one-to-one for a given target.
  • Feature-flag separation: business functionality is gated by environment flags and can run as a separate bot runtime; this reduces accidental impact on resident UX during rollout but adds operational surface area (more tokens, more bots, more states).

Possible Implications

  • Operational: if SQLite locking appears under load (broadcasts, frequent heartbeats, admin jobs), notifications and status freshness can degrade. The presence of broadcast throttling knobs hints that tuning is expected in real deployments.
  • Integration: third parties can query status without write credentials, but key management becomes a real responsibility (separate write vs read-only keys).
  • Verification: correctness depends heavily on heartbeat cadence, timeout settings, and mapping tables; testing needs to cover missing/invalid sections and “moved sensor” scenarios.

Conclusion

PowerBot’s core is straightforward: accept authenticated ESP32 heartbeats, infer “up/down” by recency, and present it through Telegram plus a read-only HTTP API. The first things worth validating are (1) sensor-to-building/section mapping behavior under misreports and (2) SQLite contention behavior when broadcasts, heartbeats, and admin jobs overlap.


전체 개요

본 저장소는 ESP32 센서의 heartbeat 수신을 기반으로 건물 및 섹션 단위의 “전기 공급 상태”를 추정하고, 이를 Telegram 봇 UI와 HTTP 읽기 전용 API로 제공하는 시스템으로 보입니다. 배포는 Docker Compose를 중심으로 구성되어 있으며, 역프록시(Traefik) 라우팅과 SQLite 단일 DB 공유를 전제로 하는 형태입니다.

배경과 목적

이 시스템의 목적은 “주민이 체감하는 유틸리티 상태(특히 전기)”를 빠르게 공유하는 데 있는 것으로 해석될 여지가 있습니다. 직접 계측값을 정교하게 처리하기보다는, 센서가 일정 주기로 살아있음을 알리는 heartbeat의 최신성을 기준으로 상태를 단순화하여 전달하는 접근을 택한 것으로 보입니다.

기술 흐름 설명 (신호/데이터/동작 순서 중심)

Image generated by gpt
  1. ESP32 센서가 HTTP로 POST /api/v1/heartbeat 요청을 전송합니다. payload에는 api_key, building_id, section_id, sensor_uuid 등이 포함됩니다.
  2. 서버는 api_key를 비교하여 인증을 수행하고, building_id/sensor_uuid 타입을 검증합니다.
  3. sensor_uuid 기반의 “정규화 매핑”이 적용될 수 있으며, 이 경우 센서가 보고한 building_id가 서버 기준 값으로 덮어써질 수 있습니다. 이는 센서 측 설정 실수로 인한 상태 오염을 줄이려는 의도로 보입니다.
  4. section_id가 누락되거나, 건물에 대해 유효하지 않으면 기본 섹션으로 보정하거나 오류 처리합니다(조건에 따라 달라질 수 있습니다).
  5. DB에 센서 정보 및 마지막 heartbeat 시각이 upsert 형태로 저장됩니다.
  6. 봇 런타임은 백그라운드 모니터링 루프를 통해 heartbeat 최신성을 기준으로 센서 online/offline을 판정하고, “해당 건물/섹션에 online 센서가 하나라도 있으면 전기 공급 중” 같은 방식으로 상태를 추정하는 것으로 보입니다.
  7. 사용자는 Telegram에서 메뉴/상태 화면을 통해 정보를 확인하며, 구독자에게는 알림이 발송될 수 있습니다.
  8. 외부 개발자/시스템을 위해 읽기 전용 상태 API가 제공되며, 쓰기 키를 공개하지 않도록 별도의 read-only 키를 사용합니다.

왜 이런 구조가 나왔는지에 대한 해설

  • 단일 DB(SQLite) + Docker 구성은 초기 구축과 운영 단순화를 우선한 선택으로 보입니다. 다만 여러 컨테이너가 같은 DB 파일을 공유하므로, 트래픽이 늘거나 브로드캐스트가 빈번해질수록 lock 경합이 운영 리스크가 될 수 있습니다.
  • sensor_uuid -> building_id 정규화 매핑은 “현장 펌웨어 설정이 바뀌거나 잘못 보고해도 서버 기준의 소스 오브 트루스를 유지”하려는 안전장치로 해석될 여지가 있습니다. 이는 잘못된 상태 전파의 실패 비용을 줄이는 방향입니다.
  • 섹션/건물 alias 개념은 하나의 물리 센서가 여러 섹션을 대표해야 하는 상황(전원 라인 공유 등)을 임시로 메우기 위한 장치로 보입니다. 대신 히스토리/통계가 모호해질 수 있어, 단일 출처로 귀결되는 매핑인지가 중요해 보입니다.
  • 비즈니스/어드민 봇을 분리하고 feature flag로 격리한 점은, 주민용 흐름에 영향을 최소화하면서 기능을 확장하려는 운영 전략으로 볼 수 있습니다.

생소한 개념에 대한 풀어쓴 설명

  • Heartbeat 기반 상태 추정: 센서가 “주기적으로 살아있다”는 신호를 보내면 online으로 간주하고, 그 최신성이 일정 시간(타임아웃) 이상 오래되면 offline으로 간주하는 방식입니다.
  • Read-only API 키 분리: 센서가 쓰는 키(쓰기 권한)와 외부 조회용 키(읽기 전용)를 분리하여, 조회 기능을 공개하더라도 센서 상태를 위조하는 위험을 낮춥니다.
  • Canonical mapping: 센서가 보고한 값보다 서버 설정의 매핑을 우선하여, 센서 측 값이 흔들려도 데이터가 한 곳으로 모이게 만드는 정규화 장치입니다.

시스템 구성 및 선택지 해석

  • 역할과 경계
    • 시스템이 책임지는 것: 센서 heartbeat 수신, 상태 추정 로직, DB 저장, Telegram 알림/표시, 읽기 전용 조회 API 제공입니다.
    • 시스템이 외부에 위임하는 것: 센서 네트워크 연결성, heartbeat 주기 준수, 역프록시/도메인 라우팅, Telegram 플랫폼 자체의 메시징 채널입니다.
  • 입력/출력/연계
    • 입력: HTTP heartbeat, Telegram 사용자 입력, 환경변수 기반 설정, (구성상) 외부 API 기반 부가 정보(날씨/알림/정전 일정 등)일 수 있습니다.
    • 출력: Telegram 화면/알림, 공용 상태 조회 API 응답입니다.
  • 선택지(대안 가능성)
    • 저장소 관점에서는 SQLite를 유지하되 브로드캐스트/heartbeat 빈도를 조절하는 쪽으로 운영 튜닝이 가능해 보입니다.
    • 규모가 커질 경우에는 DB를 분리하거나(예: 별도 DBMS) 쓰기 경합을 줄이는 구조가 필요할 가능성이 있습니다. 이는 코드상으로는 확정하기 어렵고, 운영 조건에 따라 달라질 수 있습니다.

내부 관점에서의 시사점

  • 실패 비용이 큰 판단 지점은 “센서 매핑(건물/섹션)과 키 관리”로 보입니다. 매핑이 어긋나면 잘못된 건물에 상태가 표시될 수 있고, 키가 유출되면 heartbeat 위조가 가능해집니다.
  • 다중 컨테이너가 단일 SQLite 파일을 공유하므로, 이벤트 폭주나 대량 알림 시점에 “database is locked” 류의 문제가 운영 리스크가 될 수 있습니다. 브로드캐스트 관련 rate/concurrency 설정이 존재하는 점은 이를 완화하려는 의도가 있었을 가능성이 있습니다.
  • 문서와 코드의 정합성 측면에서는, README가 배포/운영 중심이고 기능 정의는 코드/스키마에 더 많이 담겨 있는 형태로 보입니다. 운영자가 기능 확장 시 문서 보강을 하지 않으면 인수인계 비용이 커질 수 있습니다.

FAQ

  1. 이 시스템에서 “전기가 들어온다”는 판단은 무엇을 의미합니까?
    센서의 last_heartbeat가 타임아웃 이내이면 online으로 간주하고, 건물/섹션에 online 센서가 하나라도 있으면 전기 공급 중으로 추정하는 방식으로 보입니다. 물리 전압을 직접 표현하기보다는 “센서가 살아있다”를 대리 지표로 쓰는 형태입니다.
  2. sensor_uuid 기반의 건물 매핑이 필요한가요?
    센서가 보고하는 building_id가 현장에서 잘못 설정되거나 바뀌는 경우, 서버가 이를 그대로 수용하면 상태가 다른 건물로 섞일 수 있습니다. sensor_uuid -> building_id 정규화는 이런 실패 비용을 낮추려는 안전장치로 해석될 여지가 있습니다.
  3. 기존 접근 대비 차별 지점은 무엇인가요?
    읽기 전용 상태 API 키를 별도로 두고, 센서 쓰기 키를 노출하지 않으면서 외부 조회를 허용하는 구조가 눈에 띕니다. 또한 비즈니스/어드민 봇을 분리 런타임으로 둘 수 있어, 주민용 흐름을 보호한 채 기능을 확장하려는 방향이 보입니다.
  4. 구조/아키텍처 선택에서 가장 큰 트레이드오프는 무엇입니까?
    여러 서비스가 하나의 SQLite DB를 공유하는 구성은 배포가 단순하지만, 동시 쓰기 부하가 커질수록 lock 경합 리스크가 커질 수 있습니다. 반대로 별도 DBMS로 옮기면 운영 복잡도는 증가할 수 있습니다.
  5. 시스템 연계 또는 통신 역할은 어떻게 나뉘나요?
    센서 연계는 HTTP heartbeat 엔드포인트로, 외부 개발자 연계는 읽기 전용 status API로 분리되어 있습니다. 사용자 채널은 Telegram 봇 폴링 기반이며, 외부 트래픽 라우팅은 역프록시 라벨 구성에 의존하는 형태로 보입니다.
  6. 실패 비용이 가장 큰 판단 지점은 어디로 보이나요?
    키 유출(센서 쓰기 키 또는 공용 조회 키의 오남용)과 센서 매핑 오류가 가장 큰 리스크로 보입니다. 이 두 영역은 “상태 위조” 또는 “잘못된 건물에 상태 표시”로 직접 이어질 수 있습니다.

저자 정보

  • Samuel Morgan
    공개된 정보가 제한적임
Documents
Comments Write