first commit

This commit is contained in:
Flavien Haas 2025-04-25 20:01:50 +02:00
commit f85beb29e1
9 changed files with 160 additions and 0 deletions

16
__init__.py Normal file
View File

@ -0,0 +1,16 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN
async def async_setup(hass: HomeAssistant, config: ConfigType):
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "sensor")
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
return await hass.config_entries.async_forward_entry_unload(entry, "sensor")

21
config_flow.py Normal file
View File

@ -0,0 +1,21 @@
from homeassistant import config_entries
import voluptuous as vol
from .const import DOMAIN, CONF_IEEE, CONF_INTERVAL, DEFAULT_INTERVAL
class LinkyTarifConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, user_input=None):
errors = {}
if user_input is not None:
return self.async_create_entry(title="Linky Tarif", data={
CONF_IEEE: user_input[CONF_IEEE],
}, options={
CONF_INTERVAL: user_input.get(CONF_INTERVAL, DEFAULT_INTERVAL)
})
schema = vol.Schema({
vol.Required(CONF_IEEE): str,
vol.Optional(CONF_INTERVAL, default=DEFAULT_INTERVAL): int,
})
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)

4
const.py Normal file
View File

@ -0,0 +1,4 @@
DOMAIN = "linky_tarif"
CONF_IEEE = "ieee"
CONF_INTERVAL = "interval"
DEFAULT_INTERVAL = 300

46
coordinator.py Normal file
View File

@ -0,0 +1,46 @@
from datetime import timedelta
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from zigpy import types
import logging
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class LinkyTarifCoordinator(DataUpdateCoordinator):
def __init__(self, hass: HomeAssistant, ieee: str, interval: int):
super().__init__(
hass,
_LOGGER,
name="Linky Tarif Coordinator",
update_interval=timedelta(seconds=interval),
)
self._ieee = ieee
async def _async_update_data(self):
zha_storage = self.hass.data["zha"]
app_ctrl = zha_storage.gateway.application
device = app_ctrl.get_device(types.EUI64.convert(self._ieee))
if not device:
raise UpdateFailed(f"Device with IEEE {self._ieee} not found")
endpoint = device.endpoints.get(1)
if not endpoint:
raise UpdateFailed("Endpoint 1 not found")
cluster = endpoint[0xFF66]
if not cluster:
raise UpdateFailed("Cluster 0xFF66 not found")
try:
res = await cluster.read_attributes([0x0010])
value = res.get(0x0010)
return {
"tariff": value,
"last_update": self.hass.helpers.event.dt_util.utcnow().isoformat(),
}
except Exception as e:
raise UpdateFailed(f"Error reading tariff: {e}")

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

10
manifest.json Normal file
View File

@ -0,0 +1,10 @@
{
"domain": "linky_tarif",
"name": "Linky Tarif Sensor",
"version": "1.0.0",
"config_flow": true,
"documentation": "https://github.com/yourusername/linky_tarif",
"requirements": [],
"dependencies": ["zha"],
"codeowners": ["@yourusername"]
}

36
sensor.py Normal file
View File

@ -0,0 +1,36 @@
from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.entity import DeviceInfo
from .coordinator import LinkyTarifCoordinator
from .const import DOMAIN, CONF_IEEE, CONF_INTERVAL
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback):
coordinator = LinkyTarifCoordinator(
hass,
ieee=entry.data[CONF_IEEE],
interval=entry.options.get(CONF_INTERVAL, 300),
)
await coordinator.async_config_entry_first_refresh()
async_add_entities([LinkyTarifSensor(coordinator)])
class LinkyTarifSensor(SensorEntity):
def __init__(self, coordinator: LinkyTarifCoordinator):
self.coordinator = coordinator
self._attr_unique_id = f"linky_tarif_{coordinator._ieee}"
self._attr_name = "Linky Tarif Period"
self._attr_icon = "mdi:flash"
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, coordinator._ieee)})
@property
def native_value(self):
return self.coordinator.data.get("tariff") if self.coordinator.data else None
@property
def extra_state_attributes(self):
return {"last_update": self.coordinator.data.get("last_update")} if self.coordinator.data else {}
async def async_update(self):
await self.coordinator.async_request_refresh()

13
strings.json Normal file
View File

@ -0,0 +1,13 @@
{
"domain": "linky_tarif",
"name": "Linky Tarif",
"config_flow": {
"title": "Linky Tarif",
"description": "Monitors Linky energy meter tariff periods using ZHA.",
"step": {
"user": {
"title": "Configure Linky Tarif"
}
}
}
}

14
translations/en.json Normal file
View File

@ -0,0 +1,14 @@
{
"config": {
"step": {
"user": {
"title": "Configure Linky Tarif",
"description": "Enter the IEEE address of your ZLinky_TIC device.",
"data": {
"ieee": "Device IEEE address",
"interval": "Polling interval (seconds)"
}
}
}
}
}