헤드리스 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 본문 서명 무영향
98 lines
3.9 KiB
Python
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()
|