224 lines
6.0 KiB
Markdown
224 lines
6.0 KiB
Markdown
# 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/<peerId>/<channel>/SET_TEMPERATURE
|
|
Payload: "22" (plain integer as string)
|
|
```
|
|
|
|
**STATE Update:**
|
|
```
|
|
homegear/instance1/plain/<peerId>/<channel>/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)
|