Add oven controls and fix Samsung-OCF write semantics
Major session of local-OCF reverse engineering against the NV7000BS oven and DV5000T dryer. Surfaces a working set of HA entities for the oven and resolves several Samsung-quirk regressions in the bridge's write path. Key behavioural fixes: - OBSERVE registrations now use single-byte tokens. Samsung RT-OCF silently drops registrations with TKL>1; same 4-byte tokens work fine for GET/POST. Symptom was that writes returned 2.04 but the appliance never pushed state changes. - Per-session random starting tokens + MID. Samsung retains observer state across DTLS reconnects from the same cert; reusing tokens on reconnect silently no-ops. - OBSERVE deregister sent on DtlsCoapSession.close(), with a stop- watcher thread in PushBridge.session_once() so SIGINT/SIGTERM actually reaches close() instead of hanging in sess.join(). - pyOpenSSL is not thread-safe — reader-loop conn.* calls now hold the same _send_lock the sender uses, dispatching decrypted packets outside the lock so the auto-ACK send doesn't deadlock. - Periodic CoAP Ping (RFC 7252 §4.4) keepalive to keep DTLS warm. - Post-write Block2 fetchback REMOVED. It was the root cause of every "setpoint/operationTime/modes revert ~3s after write" symptom — Samsung's stack treats a read on a freshly-written resource as a signal to invalidate that write. OBSERVE pushes keep HA in sync without the verification GET. HA-facing changes (oven): - New entities: Lamp (light), Sound (switch), Fast preheat, Natural steam, Setpoint (number), Cook time (number), Stop cycle (button). - Cooking mode surfaced as a read-only sensor — the oven owns the modes field once a cycle is active and rolls local writes back. - Cook time writes operationTime + remainingTime on /operational/state/vs/0 (discovered via OBSERVE capture of SmartThings mid-cycle changes — UpperTimerSet on /mode/vs/0 options is vestigial and doesn't drive the running cycle). - New cycle_active MQTT availability topic. Writes the oven only honours mid-cycle (setpoint, cook time, fast preheat, natural steam, stop) gate on it via avail_with_cycle / avail_with_remote_ and_cycle. Sound + Lamp remain always-available. - Cycle Start deliberately NOT exposed. Every byte-level approxi- mation of SmartThings's working start sequence is rejected at the firmware level. Empty discovery payloads remove the previous Start button and Cooking-mode select cleanly from HA. Diagnostics: - DEBUG_BRIDGE=1 env var enables verbose tracing (rx CON/NON/ACK/ RST per frame, full link-tree dump at seed, /oic/res directory, REP changes on /operational/state, /oven, /power, mode options). Quiet in production.
This commit is contained in:
1 parent
85580d81b7
commit
8775f7a930
6 files changed
+475
-133
No files matched your search
@@ -154,6 +154,14 @@ def main():
|
||||
b.heartbeat()
|
||||
return loop
|
||||
|
||||
def make_ping(b: PushBridge):
|
||||
def loop():
|
||||
while not b.stop.is_set():
|
||||
if b.stop.wait(shared.PING_INTERVAL_S):
|
||||
break
|
||||
b.ping_once()
|
||||
return loop
|
||||
|
||||
for b in bridges:
|
||||
tag = b.app.klass
|
||||
threads.append(threading.Thread(
|
||||
@@ -164,6 +172,10 @@ def main():
|
||||
threads.append(threading.Thread(
|
||||
target=make_heartbeat(b), daemon=True,
|
||||
name=f'{tag}-heartbeat'))
|
||||
if shared.PING_INTERVAL_S > 0:
|
||||
threads.append(threading.Thread(
|
||||
target=make_ping(b), daemon=True,
|
||||
name=f'{tag}-ping'))
|
||||
|
||||
stopping = threading.Event()
|
||||
|
||||
|
||||
Reference in new issue
Block a user