누수 감지↔복구 플래핑 SMS 폭탄 방지, 온습도 측정 주기 1시간화(부수 오탐
차단), 센서별 SMS 수신자 그룹 지정 3종을 함께 반영한다.
1) 누수 디바운스 (서버 전용, 펌웨어 무변경)
- leak_sensor_status 에 leak_confirmed / leak_pending_since 컬럼 추가.
- leak_debounce_decide(): 단일 대칭 타이머 순수 함수. raw 상태가 확인창
동안 지속돼야 확정하고 확정 엣지에서만 SMS 1회. dry 블립<복구창이면
복구 미확정 → 확정 유지 → 재감지·복구 SMS 둘 다 차단(폭탄 원천 봉쇄).
- apply_leak_debounce()/sweep_pending_leak_transitions(): 센서 네임드 락으로
직렬화, cron sweep 으로 보고 끊겨도 확정 해소. 사고(leak_incident)
open/close 도 확정 엣지로 이동(월간보고 사고건수 정합). raw 이벤트는
leak_sensor_log 에 그대로 보존(플래핑 진단).
- 컬럼 없는 구배포는 레거시 즉시 발송으로 graceful degrade.
- 확인창 기본: 감지 60s / 복구 180s (config.local.php 조정).
2) 온습도 1시간 주기 + 센서별 히트비트 타임아웃
- raspberry_pi4_sht30/config.py REPORT_INTERVAL 300→3600.
- heartbeat_timeout_for(): 명시 오버라이드 > 온습도 기본(7500s) > 전역(1200s).
cron_heartbeat / dashboard 오프라인 판정을 센서별로. 매시간 오탐 제거,
누수 오프라인은 20분 유지.
- STUCK_DETECT_SAMPLES 6→3(1시간 주기 ≈3시간), 관련 주석 정정.
3) 센서별 수신자 그룹
- SENSOR_RECIPIENTS 맵 + recipients_for_sensor() 전역 폴백.
- sms_dispatch_to_recipients 가 센서별로 수신자 해소(에스컬레이션 TIER2
명시 전달은 유지, 헬스/잔액은 전역).
대시보드: 누수 카드 '확인중(디바운스)' 배지, 시스템 탭에 확인창·센서별
타임아웃·수신자 그룹 표시.
마이그레이션: sql/migration_leak_debounce.sql (컬럼 2개), schema_sht30.sql 갱신.
테스트: leak_debounce(12) / recipients_group / heartbeat_timeout 순수 단위 추가.
설계: docs/superpowers/specs/2026-07-13-leak-debounce-sensor-config-design.md
|
||
|---|---|---|
| .. | ||
| systemd | ||
| .gitignore | ||
| config.py | ||
| config_local.example.py | ||
| install.sh | ||
| README.md | ||
| requirements.txt | ||
| sht30_driver.py | ||
| sht30_sensor.py | ||
| test_connection.py | ||
| test_sensor.py | ||
| vendor.sh | ||
| wire.py | ||
| WIRING.html | ||
Temperature/Humidity (SHT30) Sensor Module — Raspberry Pi 4 Model B only (Module 2)
A module that reads a Sensirion SHT30 (Hanjin Data P4422-3, I2C) temperature/humidity sensor over the
Raspberry Pi 4 Model B I2C bus and periodically reports to the cafe24 server (php/api/sensor_data.php).
The server stores the measurements and performs high/low temperature and high/low humidity threshold evaluation, SMS, and dashboard display.
This folder is a port of the STM32F407 firmware (
sht30_fw) behavior to Raspberry Pi 4 + Python, the "Module 2". The wire format (body/signature), CRC, and conversion formulas are byte-for-byte identical to the firmware. "Module 1" (leak + camera) is in the../raspberry_pi4/folder. For the full setup, seeSYSTEM_OVERVIEW.mdin the root.
1. Files
| File | Role |
|---|---|
sht30_sensor.py |
Main — periodic measurement → body build/sign → POST (with retry), startup→periodic |
sht30_driver.py |
SHT30 I2C measurement + CRC-8 verification + physical-value conversion (smbus2, simulation fallback) |
wire.py |
Body JSON + raw-body HMAC signature (byte-identical to firmware jsonbody.c/sig.c) |
config.py |
Non-secret default settings (sensor id/interval/range/I2C). Loads secrets from config_local |
config_local.example.py |
Operational/secret-value template → copy to config_local.py for use (not committed) |
requirements.txt |
Python dependencies (requests, smbus2, pinned) |
install.sh |
Pi 4 install (air-gapped offline + online fallback, includes enabling I2C) |
vendor.sh |
Pre-download dependencies for air-gapped use (run on the build machine) |
test_connection.py |
Server connectivity/signed-POST check |
test_sensor.py |
CRC/format self-check + one measurement (format check passes even without hardware) |
systemd/sht30-sensor.service |
Service unit for auto-start on boot |
2. Hardware (Pi 4 Model B baseline)
- Raspberry Pi 4 Model B (BCM2711) + Raspberry Pi OS Bookworm 64-bit
- SHT30 temperature/humidity sensor module (I2C, Hanjin Data P4422-3)
- I2C pull-up resistors 4.7kΩ × 2 (omit if built into the module)
- Power: USB-C 5V/3A, network: Pi 4 Gigabit Ethernet (wired) or WiFi
2.1 Pinout (I2C bus 1)
| Signal | Pi 4 pin | Note |
|---|---|---|
| SHT30 VCC | 3.3V (physical 1) | No 5V |
| SHT30 GND | GND (physical 6/9/14/…) | |
| SHT30 SDA | GPIO2 / SDA1 (physical 3) | + 4.7kΩ pull-up → 3.3V |
| SHT30 SCL | GPIO3 / SCL1 (physical 5) | + 4.7kΩ pull-up → 3.3V |
| SHT30 ADDR | GND → address 0x44 | 0x45 if tied to VDD (config.py I2C_ADDR) |
3. Server compatibility (must verify)
This module reports to the current repo's php/ server (= the cafe24 SHT30 dashboard server).
The server requires a raw-body HMAC signature.
X-Signature = lowercase_hex( HMAC-SHA256( key=API_KEY, msg=request body bytes ) )
API_KEYinconfig_local.pymust be exactly identical toAPI_KEYin the server'sconfig.local.php.- The server blocks replays via
timestampfreshness (±600 seconds), so the Pi clock must be NTP-synchronized (enabled by default on Raspberry Pi OS). - It reports with
sensor_id=2, and the server dashboard (dashboard.php) shows sensor 2 as the default chart. - Threshold alerts (high/low temperature, high/low humidity), SMS, and recovery evaluation are all performed by the server. This module only reports measurements.
For the server-side schema/deployment, see
../sql/schema_sht30.sql,../docs/INSTALL_PI_SERVER.md,../docs/SHT30_SENSOR2_SETUP.md.
4. Installation
4.1 Prepare configuration values
cp config_local.example.py config_local.py
# Open config_local.py and fill in API_URL / API_KEY / DEVICE_ID / DEVICE_LOCATION
If config_local.py is missing, execution stops immediately with a clear error (fail closed).
Never commit config_local.py or API_KEY (handled by .gitignore).
4.2 Online install (internet-capable environment)
chmod +x install.sh
./install.sh
sudo reboot # reboot once if I2C was newly enabled
4.3 Air-gapped offline install
On an internet-connected machine of the same architecture (aarch64, ideally the same Pi 4 + Bookworm 64-bit), download the dependencies first to build a bundle, then move it to the target Pi 4 and install.
# (build machine, internet YES) generate wheelhouse/ + debs/
./vendor.sh
# after copying wheelhouse/ and debs/ into this folder on the target Pi 4:
# (target Pi 4, internet NO)
./install.sh # if debs/ · wheelhouse/ exist, it installs offline automatically
smbus2is pure Python (arch-independent), so it can be downloaded on an x86 build machine. Other wheels/debs such asrequestsmust be the aarch64 builds (runningvendor.shon the target arch is recommended).
5. Run / check
# is the sensor visible (0x44)
i2cdetect -y 1
# format/CRC self-check + one measurement (simulation if no hardware)
source venv/bin/activate
python3 test_sensor.py
# server connectivity/signature check
python3 test_connection.py
# actual run
python3 sht30_sensor.py
In environments without smbus2 installed or with I2C disabled (dev PC, etc.), it automatically runs in simulation mode.
6. Auto-start on boot (systemd)
# assumes the folder is placed at /home/pi/raspberry_pi4_sht30 (otherwise edit the path in the .service)
sudo cp systemd/sht30-sensor.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable sht30-sensor
sudo systemctl start sht30-sensor
sudo systemctl status sht30-sensor
journalctl -u sht30-sensor -f
SupplementaryGroups=i2c is required for /dev/i2c-1 access.
7. Security notes
- Secrets (
API_KEY), server URL, and device info live only inconfig_local.py(not committed). Committed files contain only placeholders. - The report body is protected against tampering by an
HMAC-SHA256(API_KEY, raw_body)signature (X-Signatureheader) (immune to length-extension attacks). The legacy body-field sha256 fallback is disabled by default on the server. - The signed bytes and the transmitted bytes are always identical (
wire.pybuilds the body directly, signs those bytes, then raw POSTs). This matches firmwaresig.c/reference.py/ serververify_signature_rawbyte-for-byte. - If
config_local.pyis exposed, immediately rotateAPI_KEY(change it on the server at the same time).