Architecture Overview
This document provides a high-level architecture overview of the ALPHA HWR protocol implementation.
System Architecture
graph TD
A[Application/CLI] --> B[Client Facade]
B --> C[Services Layer]
C --> D[Protocol Layer]
D --> E[Transport Layer]
E --> F[Core Layer]
F --> G[BLE Stack]
G --> H[Pump Hardware]
Layer Responsibilities
1. Core Layer
Purpose: Foundation for BLE communication and session management
Components:
- authentication.py - Authentication handshake
- session.py - Connection state machine
- transport.py - BLE send/receive abstraction
Responsibilities: - Manage BLE connection lifecycle - Execute authentication sequence - Handle packet transmission/reception - Maintain session state
2. Protocol Layer
Purpose: GENI protocol encoding/decoding
Components:
- codec.py - Primitive encoding/decoding (floats, ints)
- frame_builder.py - Construct GENI frames
- frame_parser.py - Parse GENI frames
- telemetry_decoder.py - Decode telemetry payloads
Responsibilities: - Build valid GENI protocol packets - Parse received packets - Calculate CRC checksums - Extract data from payloads
3. Services Layer
Purpose: Business logic for pump operations
Components:
- telemetry.py - Read measurements
- control.py - Pump control operations
- device_info.py - Device information
- schedule.py - Schedule management
- configuration.py - Backup/restore
Responsibilities: - Expose high-level operations - Handle complex multi-step workflows - Validate inputs - Manage service state
A fifth service worth copying: a write layer. The pump acknowledges
frames it is about to clamp — ask for 600 RPM and it stores 1650 — so an
ack-as-success design reports a state the pump is not in. Route every write
through one serialized path that reads the value back and reports what was
actually stored. In this implementation that is write_operation.py; the
C++ port has the same shape.
Services may depend on other services. A verified write has to read state back through the service that owns it, so the write layer takes the control service, and the control service takes the schedule service. Inject at construction; forbid cycles, not composition.
4. Client Facade
Purpose: Simple unified API
File: client.py
Responsibilities: - Single entry point for applications - Manage service lifecycles - Handle connection/disconnection - Provide context manager interface
Sequence Diagrams
Authentication Sequence
sequenceDiagram
participant App
participant Auth
participant Transport
participant Pump
App->>Auth: authenticate()
Auth->>Transport: send(LEGACY_MAGIC) x3
Transport->>Pump: BLE write
Auth->>Transport: send(CLASS10_UNLOCK) x5
Transport->>Pump: BLE write
Auth->>Transport: send(EXTEND_1)
Auth->>Transport: send(EXTEND_2)
Transport->>Pump: BLE write
Note right of Pump: No ack is sent
Auth-->>App: Sequence completed
Telemetry Reading
sequenceDiagram
participant App
participant Telemetry
participant Protocol
participant Transport
participant Pump
App->>Telemetry: read_once()
Telemetry->>Protocol: build_class10_read(0x570045)
Protocol->>Transport: send(packet)
Transport->>Pump: BLE write
Pump-->>Transport: Response
Transport-->>Protocol: raw_data
Protocol->>Protocol: parse_frame(raw_data)
Protocol->>Protocol: decode_telemetry(frame)
Protocol-->>Telemetry: TelemetryData
Telemetry-->>App: TelemetryData
Control Mode Setting
sequenceDiagram
participant App
participant Control
participant Protocol
participant Transport
participant Pump
App->>Control: set_setpoint(CONSTANT_PRESSURE, 1.5)
Control->>Protocol: encode_float_be(14710.0)
Protocol->>Protocol: build_set_command(0x5600, 0x0601, payload)
Protocol->>Transport: send(packet, 20-byte chunks)
Transport->>Pump: BLE write
Pump-->>Transport: ACK
Note right of Pump: The ack is not the verdict
Control->>Protocol: read Object 86 Sub 7
Protocol-->>Control: stored setpoint
Control-->>App: WriteResult(CLAMPED, value=...)
Data Flow
Request Flow
graph TD
App[Application] --> Client[Client Facade]
Client --> Services[Service Layer]
Services --> Protocol[Protocol Layer]
Protocol --> Transport[Transport Layer]
Transport --> Pump[Pump Hardware]
Response Flow
graph TD
Pump[Pump Hardware] --> Transport[Transport Layer]
Transport --> Protocol[Protocol Layer]
Protocol --> Services[Service Layer]
Services --> Client[Client Facade]
Client --> App[Application]
State Management
Session States
stateDiagram-v2
[*] --> DISCONNECTED
DISCONNECTED --> CONNECTED: connect()
CONNECTED --> AUTHENTICATING: authenticate()
AUTHENTICATING --> AUTHENTICATED: success
AUTHENTICATING --> ERROR: failure
AUTHENTICATED --> CONNECTED: timeout
CONNECTED --> DISCONNECTED: disconnect()
ERROR --> DISCONNECTED: reset
Transport States
- IDLE: No active operation
- SENDING: Packet transmission in progress
- WAITING: Awaiting response
- RECEIVING: Processing notification
- ERROR: Communication failure
Design Patterns
1. Layered Architecture
Each layer has clear boundaries and responsibilities. Upper layers depend only on adjacent lower layers.
2. Async/Await
All I/O operations are asynchronous to prevent blocking.
3. Context Managers
Resources (connections, sessions) use context managers for automatic cleanup.
4. Service Locator
Client facade provides access to all services through a single interface.
5. Strategy Pattern
Different control modes, schedule types, etc. use strategy pattern.
Error Handling
Error Propagation
graph TD
Services[Service Layer] -- raises --> Error[AlphaHWRError]
Error -- catches --> Client[Client Facade]
Client -- re-raises --> App[Application]
App -- handles --> User[User]
Error Types
- ConnectionError: BLE connection issues
- AuthenticationError: Authentication failure
- ProtocolError: Invalid packet or CRC
- TimeoutError: Operation timeout
- ValidationError: Invalid input
Thread Safety
Concurrent Access
- Transport: Uses asyncio locks for sequential command execution
- Session: Thread-safe state machine
- Services: Stateless operations (safe for concurrent use)
Lock Hierarchy
Always acquire locks from highest to lowest to prevent deadlocks.
Performance Considerations
Latency
- Authentication: ~500ms
- Single telemetry read: ~100-200ms
- Control command: ~100-150ms
Throughput
- BLE MTU: 20 bytes (default)
- Max packet size: ~250 bytes
- Notifications: Up to 1Hz from pump
Resource Usage
- Memory: Minimal (< 1MB for client)
- CPU: Low (BLE and parsing only)
- Battery: Dependent on connection interval
Security
Authentication
- Custom challenge-response using magic packets
- No encryption at application layer
- Relies on BLE pairing for security
Data Privacy
- Telemetry data sent in clear text over BLE
- Recommend BLE pairing for sensitive deployments
Extensibility
Adding New Services
- Create service class in
services/ - Add to
client.pyfacade - Document in guides
- Add tests
Adding New Commands
- Define in
protocol/frame_builder.py - Parse in
protocol/frame_parser.py - Expose in relevant service
- Add test vectors
Supporting New Pumps
- Verify GENI protocol compatibility
- Update constants if needed
- Add pump-specific tests
- Document differences
Testing Strategy
Unit Tests
- Each layer tested independently
- Mock dependencies
- Test vectors for validation
Integration Tests
- End-to-end with mock pump
- Real hardware tests
- Cross-platform validation
Reference Tests
- Portable test suite
- Byte-level validation
- Other languages can use same tests
Deployment
Packaging
- Python: PyPI package
- JavaScript: npm package
- Rust: crates.io
- Others: Language-specific registries
Dependencies
- BLE library (Bleak, noble, btleplug, etc.)
- Async runtime (asyncio, tokio, etc.)
- Optional: CLI framework, GUI toolkit
Next Steps
- Review Layer-by-Layer Guide for implementation details
- Use Checklist to track progress
- Validate with Test Vectors
- Check Common Pitfalls for known issues