Metadata-Version: 2.4
Name: salus-it600-client
Version: 0.6.2
Summary: Asynchronous Python client for Salus iT600 local gateways
Author: Julius Vitkauskas, Jordi Albareda Fortuny, Leonard Pitzu
License-Expression: MIT OR Apache-2.0
Project-URL: Homepage, https://github.com/Jordi-14/salus-it600-client
Project-URL: Issues, https://github.com/Jordi-14/salus-it600-client/issues
Project-URL: Source, https://github.com/Jordi-14/salus-it600-client
Project-URL: Original, https://github.com/epoplavskis/pyit600
Keywords: salus,it600,api,async,client,home-assistant
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Home Automation
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: LICENSE-MIT
License-File: LICENSE-APACHE
License-File: NOTICE
Requires-Dist: aiohttp>=3.9
Requires-Dist: cryptography>=42.0.0
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: coverage[toml]; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# salus-it600-client

An asynchronous Python client for **local** control and monitoring of [Salus iT600](https://salus-controls.com/) smart home devices through UGE600 or UG800 gateways — thermostats, TRVs, smart plugs, roller shutters, sensors, and more, all without cloud dependency.

## Features

### Climate (thermostats & TRVs)

| Device family | Capabilities |
|---|---|
| **Standard heat-only iT600 / TRV devices** | Heat/off/auto control model, active target temperature, battery level, 0.1 °C precision |
| **SQ610 / SQ610RF Quantum** | Off/heat modes, cool mode when the thermostat reports cooling support, Away / Permanent Hold / Follow Schedule presets, reported Schedule Override state, normalized humidity, floor temperature (external probe), battery level, 0.1 °C precision, read-only decoded Advanced Settings |
| **FC600 fan-coil** | Off/heat/cool modes, Follow Schedule / Permanent Hold / Eco presets when supported, reported Schedule Override state, fan modes (auto/high/medium/low/off), separate heating/cooling setpoints |
| **TRV3RF** | Heat/off/auto modes, valve opening %, battery (voltage curve), open-window detection, error code diagnostics |

All thermostat families expose the same normalized climate model where their
protocols overlap: hold/system/running state, active target temperature, active
range, heat/cool setpoints when present, online status, and whitelisted support
fields for diagnostics.

SQ610-family thermostats additionally expose the vendor app's per-thermostat
"Advanced Settings" screen (valve protection, comfort warm floor, optimum
start/stop, control algorithm, S1/S2 input, floor-temperature limits, relay
configuration, minimum off times, display and language options) as a typed,
read-only `advanced_settings` model, decoded from the packed
`Status_2_d`/`Schedule` blob. See
[docs/device-protocol.md](docs/device-protocol.md) for the byte layout and
what is deliberately not decoded.

### Sensors

| Sensor | Source |
|---|---|
| Temperature | Standalone sensors (TS600, PS600) and thermostat readings |
| Humidity | SQ610 (`SunnySetpoint_x100`) and standalone multi-sensors |
| Floor temperature | SQ610 with external floor probe (`OUTSensorProbe`) |
| Battery | Status_d (SQ610RF) and voltage curves (TRV, window/door sensors) |
| Power (W) | Smart plug (`sMeteringS`) and energy meter ECM600 (`sMeterS`) |
| Energy (kWh) | Smart plug (`sMeteringS`) and energy meter ECM600 (`sMeterS`) |
| Signal strength (dBm) | Thermostats, TRVs, and it600WC (`sIT600I.LastMessageRSSI_d`) |
| Link quality | Thermostats, TRVs, and it600WC (`sIT600I.LastMessageLQI_d`, 0-255) |

Signal strength and link quality are diagnostic children of the device they
belong to. The gateway only fills those fields in for devices the coordinator
heard from directly on a given poll, so the last known reading is kept for as
long as the parent device is reported; both follow the parent's availability
and disappear with it.

### Binary sensors

| Sensor | Source |
|---|---|
| Window / Door | SW600, OS600 (`sIASZS`) |
| Water leak | WLS600 |
| Smoke | SmokeSensor-EM |
| TRV relay (heat) | it600MINITRV (`sIT600I.RelayStatus`) |
| Receiver relay | it600Receiver |
| Low battery | Diagnostic child on battery-powered sensors and TRVs |
| Thermostat problem | Aggregated error flags (Error01–Error32) with descriptions |
| Battery problem | Battery-specific error flags (battery models only) |
| Open window | TRV open-window detection (`sComm.OpenWindowStatus`) |
| Wiring centre connectivity | it600WC online status |
| Wiring centre problem | it600WC fault registers (Error10-20, Error26-29, `ErrorCodeWC_d`) |

### Covers

Roller shutters and blinds (RS600): open, close, set position (0-100 %).

### Switches

Smart plugs and relays (SP600, SPE600, SR600, and RS600 relay endpoints): on/off control. Dual-endpoint devices exposed as separate entities.

### RS600 notes

RS600 is a multifunction physical device. Depending on gateway payloads and installation mode, it can expose a cover surface through `sLevelS` and separate relay switch endpoints through `sOnOffS`. The client keeps these capabilities separate so Home Assistant can decide how to present them.

### it600WC notes

The it600WC wiring centre has no valve/relay state of its own in its gateway
payload -- it is a fault-register bank paired with SQ610-family thermostats on
a hydronic system, and the single point of failure for every zone it serves.
It is exposed as connectivity and problem binary sensors, plus the same
signal-quality sensors thermostats get, rather than a controllable entity.

## Installation

```bash
pip install salus-it600-client
```

## Usage

```python
from salus_it600.gateway import IT600Gateway

async with IT600Gateway(host="192.168.1.100", euid="001E5E0D32906128") as gw:
    await gw.connect()
    await gw.poll_status()

    for device_id, device in gw.get_climate_devices().items():
        print(f"{device.name}: {device.current_temperature}°C → {device.target_temperature}°C")

    # Set temperature
    await gw.set_climate_device_temperature("device_001", 21.5)
```

## Encryption & protocol support

Salus gateways encrypt all local API traffic. The library **auto-detects** the correct protocol by trying each one in order during connection.

| | Legacy AES-CBC | AES-CCM (newer firmware) |
|---|---|---|
| **Gateways** | UGE600, older UG800 firmware | UG800 with newer firmware |
| **Cipher** | AES-256-CBC (fallback: AES-128-CBC) | AES-256-CCM (authenticated encryption) |
| **Key derivation** | `MD5("Salus-{euid}")` — static, derived from the gateway EUID | EUID bytes + hardcoded suffix — 32-byte key |
| **IV / nonce** | Fixed 16-byte IV | 8-byte random nonce (3 random + 2-byte counter + 3-byte timestamp) |
| **Authentication** | None | 8-byte MAC tag (CBC-MAC) |
| **Padding** | PKCS7 | None (CCM handles arbitrary lengths) |
| **Wire format** | Block-aligned encrypted HTTP body | `[ciphertext + 8-byte MAC][8-byte nonce]` |

Protocol auto-detection order:
1. **AES-256-CBC** — legacy iT600 / UGE600 gateways
2. **AES-128-CBC** — intermediate firmware variant
3. **AES-CCM** — newer UG800 firmware

Rejected attempts are identified by a characteristic 33-byte reject frame (trailer byte `0xAE`).

## Supported devices

SQ610RF, SQ610RF(WB), SQ610RFNH, SQ610RFNH(WB), FC600, TRV3RF, it600MINITRV, it600Receiver, it600WC, SP600, SPE600, SR600, RS600, SW600, OS600, WLS600, SmokeSensor-EM, SD600, TS600, RE600, RE10B, PS600, ECM600.

### Unsupported

Buttons (SB600, CSB600) — actions only work through the Salus Smart Home app.

## Troubleshooting

- If you can't connect using the EUID on your gateway, try `0000000000000000` as EUID.
- Make sure **Local WiFi Mode** is enabled:
  1. Open the Salus Smart Home app and sign in.
  2. Double-tap your gateway to open the info screen.
  3. Press the gear icon to enter configuration.
  4. Check that **Disable Local WiFi Mode** is set to **No**.
  5. Save and power-cycle the gateway.

## Development

```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
python -m pytest
python -m ruff check .
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for architecture details and contribution guidelines.

## Migration from `pyit600`

This package replaces the unmaintained `pyit600`. Update imports:

```python
# Old
from pyit600.gateway import IT600Gateway

# New
from salus_it600.gateway import IT600Gateway
```

## License

Licensed under either the Apache License, Version 2.0
([LICENSE-APACHE](LICENSE-APACHE)) or the MIT License
([LICENSE-MIT](LICENSE-MIT)), at your option. See [LICENSE](LICENSE) and
[NOTICE](NOTICE) for the license expression and attribution details.

## Project origin

This project is a maintained fork of [epoplavskis/pyit600](https://github.com/epoplavskis/pyit600). It incorporates protocol, device-support, and parser work from [leonardpitzu/homeassistant_salus](https://github.com/leonardpitzu/homeassistant_salus). Leonard Pitzu has since joined development here, and his fork has been retired.
