POSA_LEAKSMS/raspberry_pi4_sht30/wire.py
cw2007 a72ea18130 i18n(pi): Raspberry Pi 번들 소스 한글 → 영어 (콘솔 mojibake 해소)
헤드리스 Pi 콘솔/journalctl에서 한글이 깨져 보이는 문제로, Pi 번들(02/03/04)에 들어가는 모든 소스의 사람-대상 텍스트를 영어로 전환. 코드·식별자·설정키·경로·숫자·hex·서명 스킴·f-string 플레이스홀더는 불변.

- raspberry_pi4/, raspberry_pi4_sht30/: 로그·주석·문서·센서명·위치 영어화
- tools/make_deploy_bundles.py: 생성 템플릿(config_local.py/setup.sh/SETUP.md/PI_META 위치) 영어화
- tools/harden_pi.sh, tools/set_network.sh: echo/주석 영어화
- WIRING.html: 영어화 + SHT30 전압 경고 정정 반영
- 서버 번들(01, php/)은 한글 유지(브라우저 대시보드, Pi에서 안 깨짐)
- 전 파이썬/셸 문법검사 통과, JSON 본문 서명 무영향
2026-07-10 10:44:13 +09:00

98 lines
3.9 KiB
Python

# =============================================================================
# wire.py - wire format for server transmission (body JSON + raw-body HMAC signature)
#
# [Why a separate module]
# The server (php/api/sensor_data.php) verifies, against the received "body bytes as-is",
# X-Signature = lowercase_hex(HMAC-SHA256(key=API_KEY, msg=raw_body)).
# Therefore the "signed bytes" and the "actually transmitted bytes" must be 100% identical.
# We build the body directly as a string, sign those bytes, then POST those same bytes as-is.
# (Leaving serialization to requests' json= can change float/whitespace/order and break the signature.)
#
# [Byte-level compatibility targets] — output must be identical to the four below.
# - firmware/common/jsonbody.c::jb_sht30_event (STM32 body generation)
# - firmware/common/sig.c::sig_raw_body (STM32 HMAC signature)
# - firmware/test/host/reference.py (verified Python reference)
# - php/config.php::verify_signature_raw (server verification part)
#
# fixed2() <-> jb_fixed2() / reference.fixed2()
# sht30_event_body() <-> jb_sht30_event() / reference.sht30_event_body()
# sign_raw() <-> sig_raw_body() / reference.sign_raw()
# =============================================================================
from __future__ import annotations
import hashlib
import hmac
def fixed2(v: float) -> str:
"""Fixed 2-decimal format (round-half-up, locale-independent).
Byte-identical to jb_fixed2() / reference.fixed2().
24.0 -> "24.00"
48.5 -> "48.50"
-3.245 -> "-3.25"
"""
neg = v < 0
if neg:
v = -v
scaled = int(v * 100.0 + 0.5) # round half up (C: (long long)(v*100+0.5))
ip = scaled // 100
fp = scaled % 100
sign = "-" if (neg and scaled != 0) else ""
return f"{sign}{ip}.{fp:02d}"
def _esc(s: str) -> str:
r"""Escape JSON string value (identical to jb_estr / reference._esc).
Escapes only '"', '\\', and control characters (<0x20). '/' is left as-is.
Multibyte UTF-8 (e.g. Korean) is left as-is (server verifies raw bytes).
"""
out = []
for ch in s:
o = ord(ch)
if ch == '"':
out.append('\\"')
elif ch == '\\':
out.append('\\\\')
elif o < 0x20:
out.append('\\u%04x' % o)
else:
out.append(ch)
return "".join(out)
def sht30_event_body(device_id: str, device_location: str, sensor_id: int,
sensor_name: str, event_type: str, timestamp: int,
temperature_c: float, humidity_percent: float,
metric_status: str, app_version: str) -> str:
"""Generate SHT30 event body JSON string.
Field order is fixed to match firmware jb_sht30_event() (signature compatibility).
metric_type is always fixed to "sht30". Temperature/humidity serialized to 2 decimals via fixed2.
"""
return (
'{'
f'"device_id":"{_esc(device_id)}",'
f'"device_location":"{_esc(device_location)}",'
f'"sensor_id":{int(sensor_id)},'
f'"sensor_name":"{_esc(sensor_name)}",'
f'"event_type":"{_esc(event_type)}",'
f'"timestamp":{int(timestamp)},'
f'"metric_type":"sht30",'
f'"temperature_c":{fixed2(temperature_c)},'
f'"humidity_percent":{fixed2(humidity_percent)},'
f'"metric_status":"{_esc(metric_status)}",'
f'"app_version":"{_esc(app_version)}"'
'}'
)
def sign_raw(api_key: str, body: str) -> str:
"""raw-body signature: lowercase_hex(HMAC-SHA256(key=api_key, msg=body)).
Identical to sig_raw_body() / reference.sign_raw() / php verify_signature_raw.
The body is encoded as UTF-8 then signed (same as transmitted bytes).
"""
return hmac.new(api_key.encode("utf-8"), body.encode("utf-8"),
hashlib.sha256).hexdigest()