- Add SocketIO /chat client to dm.js for real-time DM and ACK updates
- Listen for new_message (dm), ack, device_status events
- Remove 5x cascading refresh after send (replaced by SocketIO ACK)
- Reduce polling interval from 10s to 60s (fallback only)
- Add data-ack attribute to status icons for real-time ACK updates
- Enrich ACK emission with snr/rssi/route_type (device_manager.py)
- Include socket.io.min.js in dm.html template
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Sync last_advert from device contacts as Unix timestamp (was missing)
- Convert _on_advertisement to store Unix timestamp (was ISO string)
- Add _parse_last_advert() to handle both ISO and Unix formats in API
- Truncate full pubkey to short prefix in DM placeholder and dropdown
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DM handler: don't overwrite contact name with prefix when name unknown
- Migration: upsert contact with sender name from v1 PRIV entries
- Fixes conversations showing "4e45565e" instead of "demo mc-webui"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Incoming DM events only contain a short pubkey_prefix. Now resolves it
to the full public_key via mc.get_contact_by_key_prefix() so incoming
and outgoing messages end up in the same conversation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Convert bytes to hex string for expected_ack and pkt_payload via _to_str()
- Support pubkey prefix matching in get_dm_messages() (LIKE for short keys)
- Fixes "Object of type bytes is not JSON serializable" error on DM view
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Channel messages from meshcore arrive as "SenderName: message text".
The library doesn't provide sender name separately. Now parsing it
from the text (split on first colon), matching v1 parser behavior.
Also:
- Look up DM sender names from mc.contacts instead of event payload
- Fix SNR field name (uppercase 'SNR' from meshcore library)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The meshcore Event class has 'payload' not 'data'. All event handlers
were silently getting empty dicts, causing:
- Channel messages showing 'Unknown' sender
- Channel info not returning name/secret
- Sent message event data being lost
Also normalizes channel_name/channel_secret keys from CHANNEL_INFO
events and converts secret bytes to hex string.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MeshCore library exposes command methods (get_channel, send_msg,
send_advert, etc.) on mc.commands, not directly on the MeshCore
instance. Updated all DeviceManager calls accordingly.
Fixes: channels not loading, message sending, advert, battery, etc.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add _connect_with_retry() with exponential backoff (10 attempts)
- Guard against self_info being None after meshcore library disconnects
due to unresponsive device
- Prevents crash when device is busy (e.g. held by orphan container)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add _detect_serial_port() to DeviceManager — resolves 'auto' to
actual device via /dev/serial/by-id with common path fallbacks
- Make channel_idx optional in get_channel_messages() so status and
channel-updates endpoints can query across all channels
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add pkt_payload column to direct_messages table for stable packet
hash generation and Analyzer URL linking
- Update insert_direct_message() and DeviceManager to store pkt_payload
- Add test for DM pkt_payload storage (43 tests pass)
- Update watchdog to monitor only mc-webui (meshcore-bridge removed)
- USB reset trigger now fires for mc-webui container failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Background thread runs meshcore async event loop. Supports both
serial and TCP transports. Flask routes bridge sync→async via
execute() method. Event subscriptions marked as TODO for Phase 1.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>