Metadata-Version: 2.4
Name: eac-dso-portal
Version: 0.1.0
Summary: Async Python client for the EAC (Electricity Authority of Cyprus) Distribution Web Portal
Project-URL: Homepage, https://github.com/santafox/ha-eac
Project-URL: Issues, https://github.com/santafox/ha-eac/issues
Author: santafox
License: MIT
License-File: LICENSE
Keywords: cyprus,eac,electricity,home-assistant,smart-meter
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: License :: OSI Approved :: MIT License
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
Requires-Dist: aiohttp>=3.9
Provides-Extra: dev
Requires-Dist: aioresponses>=0.7; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: python-dotenv>=1; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Description-Content-Type: text/markdown

# ha-eac

Home Assistant integration + Python library for the **EAC** (Electricity Authority of Cyprus) Distribution Web Portal — `meterreading-dso.eac.com.cy`.

## What's in this repo

| Path | What it is |
|---|---|
| `src/eac_dso_portal/` | Standalone async Python client for the (undocumented) EAC DSO REST API. Will be published to PyPI as `eac-dso-portal`. |
| `custom_components/eac_cyprus/` | Home Assistant custom integration. Will be installable via HACS. |
| `tests/` | Unit tests + optional smoke test against the real API. |

## Status

Pre-alpha. Library covers login, service points, meter configs and readings (daily totals + 30-min load profile when the meter exposes them). HA integration is being scaffolded.

## Design notes — devices, service points, and meters

### How EAC structures the data

- A **service point** (`spId`, 12-digit) is a permanent supply contract tied to an address. It does not change.
- A **meter** is the physical hardware at that point. It can be replaced (e.g. mechanical → smart), and the API exposes the full history (`installDate`, `removalDate`, serial number, model).
- A meter can have multiple **measurement channels** (`mcList`) over its lifetime — total kWh, peak/off-peak, 30-minute load profile, export, reactive, etc. Channels live inside meter *configurations*, which themselves can change over the meter's life.

### What this integration does today

**One Home Assistant device per service point.** The device's `manufacturer` / `model` / `serial_number` reflect the *currently installed* meter. Sensors are created per measurement channel that the API actually returns data for — when a channel starts being populated (e.g. `S-KWH-EXP` after a PV install, or `S-KWH-NORMAL`/`S-KWH-OFFPEAK` after switching to a two-tariff plan), new sensors appear on the next refresh without any reconfiguration.

This is a deliberate choice for **home automation**, not industrial-grade meter accounting:

- The Energy Dashboard wants a single, continuous consumption track per address. Splitting that across two devices (one for the old meter, one for the new) would force the user to template-sum or live with broken graphs at the swap boundary.
- The physical meter is metadata about *how* the data is collected; the address is what the user actually cares about ("how much electricity did this flat use?").
- A separate per-meter device model is still possible later — see "Open question" below.

### Counter resets when the meter is swapped

Cumulative kWh sensors are exposed with `state_class = total_increasing`, which is precisely Home Assistant's contract for "this is a monotonic counter that may reset to zero." When the DSO swaps the meter, the cumulative reading visibly jumps **down** (e.g. 79 304 kWh on the old ITRON → 0 kWh on the new Landis+Gyr). HA's recorder treats the drop as a reset, **not** as a negative consumption: long-term statistics keep accumulating the *delta*, so the lifetime total in the Energy Dashboard remains correct across the swap. No template, helper, or `utility_meter` workaround is required for this to be right.

What this *does not* preserve: the raw cumulative number on the old meter. It's available via the API and surfaced in entity attributes / diagnostics, but it isn't the headline state of any sensor.

### Open question

Whether `device = service point` or `device = physical meter` is the right model is **not settled**. The current shape is optimised for home automation; an installation that cares about per-meter accountability (audit, billing reconciliation, regulator reporting) would probably prefer a hierarchical model:

- Service point as the primary device,
- Each historical meter as a sub-device linked via `via_device`,
- Frozen sensors on retired meters carrying their final readings.

If you have a use case for the latter, please open an issue — happy to add it as a config-flow option without breaking the existing device layout.

## Development

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

The smoke test in `tests/test_smoke.py` talks to the real portal. It's auto-skipped unless `.env` exists with valid credentials:

```
EAC_USERNAME=you@example.com
EAC_PASSWORD=********
```

`.env` is git-ignored. Never commit credentials.

## License

MIT
