# MAX! (eQ-3) Thermostat Integration ## Overview This document describes the integration of MAX! (eQ-3) thermostats via Homegear into the home automation system. ## Protocol Characteristics MAX! thermostats use a **simple integer-based protocol** (not JSON): - **SET messages**: Plain integer temperature value (e.g., `22`) - **STATE messages**: Plain integer temperature value (e.g., `22`) - **Topics**: Homegear MQTT format ### MQTT Topics **SET Command:** ``` homegear/instance1/set///SET_TEMPERATURE Payload: "22" (plain integer as string) ``` **STATE Update:** ``` homegear/instance1/plain///SET_TEMPERATURE Payload: "22" (plain integer as string) ``` ## Transformation Layer The abstraction layer provides automatic transformation between the abstract home protocol and MAX! format. ### Abstract → MAX! (SET) **Input (Abstract):** ```json { "mode": "heat", "target": 22.5 } ``` **Output (MAX!):** ``` 22 ``` **Transformation Rules:** - Extract `target` temperature - Convert float → integer (round to nearest) - Return as plain string (no JSON) - Ignore `mode` field (MAX! always in heating mode) ### MAX! → Abstract (STATE) **Input (MAX!):** ``` 22 ``` **Output (Abstract):** ```json { "target": 22.0, "mode": "heat" } ``` **Transformation Rules:** - Parse plain string/integer value - Convert to float - Add default `mode: "heat"` (MAX! always heating) - Wrap in abstract payload structure ## Device Configuration ### Example devices.yaml Entry ```yaml - device_id: "thermostat_wolfgang" type: "thermostat" cap_version: "thermostat@1.0.0" technology: "max" features: mode: true target: true current: false # SET_TEMPERATURE doesn't report current temp topics: set: "homegear/instance1/set/39/1/SET_TEMPERATURE" state: "homegear/instance1/plain/39/1/SET_TEMPERATURE" metadata: friendly_name: "Thermostat Wolfgang" location: "Arbeitszimmer Wolfgang" vendor: "eQ-3" model: "MAX! Thermostat" peer_id: "39" channel: "1" ``` ### Configuration Notes 1. **technology**: Must be set to `"max"` to activate MAX! transformations 2. **topics.set**: Use Homegear's `/set/` path with `/SET_TEMPERATURE` parameter 3. **topics.state**: Use Homegear's `/plain/` path with `/SET_TEMPERATURE` parameter 4. **features.current**: Set to `false` - SET_TEMPERATURE topic doesn't provide current temperature 5. **metadata**: Include `peer_id` and `channel` for reference ## Temperature Rounding MAX! only supports **integer temperatures**. The system uses standard rounding: | Abstract Input | MAX! Output | |----------------|-------------| | 20.4°C | 20 | | 20.5°C | 20 | | 20.6°C | 21 | | 21.5°C | 22 | | 22.5°C | 22 | Python's `round()` function uses "banker's rounding" (round half to even). ## Limitations 1. **No current temperature**: SET_TEMPERATURE topic only reports target, not actual temperature 2. **No mode control**: MAX! thermostats are always in heating mode 3. **Integer only**: Temperature precision limited to 1°C steps 4. **No battery status**: Not available via SET_TEMPERATURE topic 5. **No window detection**: Not available via SET_TEMPERATURE topic ## Testing Test the transformation functions: ```bash poetry run python /tmp/test_max_transform.py ``` Expected output: ``` ✅ PASS: Float 22.5 -> Integer string ✅ PASS: Integer string -> Abstract dict ✅ PASS: Integer -> Abstract dict ✅ PASS: Rounding works correctly 🎉 All MAX! transformation tests passed! ``` ## Implementation Details ### Files Modified 1. **apps/abstraction/transformation.py** - Added `_transform_thermostat_max_to_vendor()` - converts abstract → plain integer - Added `_transform_thermostat_max_to_abstract()` - converts plain integer → abstract - Registered handlers in `TRANSFORM_HANDLERS` registry 2. **apps/abstraction/main.py** - Modified `handle_abstract_set()` to send plain string for MAX! devices (not JSON) - Modified message processing to handle plain text payloads from MAX! STATE topics ### Transformation Functions ```python def _transform_thermostat_max_to_vendor(payload: dict[str, Any]) -> str: """Convert {"target": 22.5} → "22" """ target_temp = payload.get("target", 21.0) return str(int(round(target_temp))) def _transform_thermostat_max_to_abstract(payload: str | int | float) -> dict[str, Any]: """Convert "22" → {"target": 22.0, "mode": "heat"} """ target_temp = float(payload) return {"target": target_temp, "mode": "heat"} ``` ## Usage Example ### Setting Temperature via API ```bash curl -X POST http://localhost:8001/devices/thermostat_wolfgang/set \ -H "Content-Type: application/json" \ -d '{ "type": "thermostat", "payload": { "mode": "heat", "target": 22.5 } }' ``` **Flow:** 1. API receives abstract payload: `{"mode": "heat", "target": 22.5}` 2. Abstraction transforms to MAX!: `"22"` 3. Publishes to: `homegear/instance1/set/39/1/SET_TEMPERATURE` with payload `22` ### Receiving State Updates **Homegear publishes:** ``` Topic: homegear/instance1/plain/39/1/SET_TEMPERATURE Payload: 22 ``` **Flow:** 1. Abstraction receives plain text: `"22"` 2. Transforms to abstract: `{"target": 22.0, "mode": "heat"}` 3. Publishes to: `home/thermostat/thermostat_wolfgang/state` 4. Publishes to Redis: `ui:updates` channel for real-time UI updates ## Future Enhancements Potential improvements for better MAX! integration: 1. **Current Temperature**: Subscribe to separate Homegear topic for actual temperature 2. **Battery Status**: Subscribe to LOWBAT or battery level topics 3. **Valve Position**: Monitor actual valve opening percentage 4. **Window Detection**: Subscribe to window open detection status 5. **Mode Control**: Support comfort/eco temperature presets ## Related Documentation - [Homegear MAX! Documentation](https://doc.homegear.eu/data/homegear-max/) - [Abstract Protocol Specification](docs/PROTOCOL.md) - [Transformation Layer Design](apps/abstraction/README.md)