|
1 | 1 | """The linkytic integration.""" |
| 2 | + |
2 | 3 | from __future__ import annotations |
3 | 4 |
|
| 5 | +import asyncio |
4 | 6 | import logging |
5 | 7 |
|
| 8 | +from homeassistant.components import usb |
6 | 9 | from homeassistant.config_entries import ConfigEntry |
7 | 10 | from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform |
8 | 11 | from homeassistant.core import HomeAssistant |
| 12 | +from homeassistant.exceptions import ConfigEntryNotReady |
9 | 13 |
|
10 | 14 | from .const import ( |
11 | 15 | DOMAIN, |
| 16 | + LINKY_IO_ERRORS, |
12 | 17 | OPTIONS_REALTIME, |
| 18 | + SETUP_PRODUCER, |
13 | 19 | SETUP_SERIAL, |
14 | 20 | SETUP_THREEPHASE, |
15 | | - TICMODE_HISTORIC, |
| 21 | + SETUP_TICMODE, |
| 22 | + TICMODE_STANDARD, |
16 | 23 | ) |
17 | 24 | from .serial_reader import LinkyTICReader |
18 | 25 |
|
|
24 | 31 | async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: |
25 | 32 | """Set up linkytic from a config entry.""" |
26 | 33 | # Create the serial reader thread and start it |
27 | | - serial_reader = LinkyTICReader( |
28 | | - title=entry.title, |
29 | | - port=entry.data.get(SETUP_SERIAL), |
30 | | - std_mode=entry.data.get(TICMODE_HISTORIC), |
31 | | - three_phase=entry.data.get(SETUP_THREEPHASE), |
32 | | - real_time=entry.options.get(OPTIONS_REALTIME), |
33 | | - ) |
34 | | - serial_reader.start() |
| 34 | + port = entry.data.get(SETUP_SERIAL) |
| 35 | + try: |
| 36 | + serial_reader = LinkyTICReader( |
| 37 | + title=entry.title, |
| 38 | + port=port, |
| 39 | + std_mode=entry.data.get(SETUP_TICMODE) == TICMODE_STANDARD, |
| 40 | + producer_mode=entry.data.get(SETUP_PRODUCER), |
| 41 | + three_phase=entry.data.get(SETUP_THREEPHASE), |
| 42 | + real_time=entry.options.get(OPTIONS_REALTIME), |
| 43 | + ) |
| 44 | + serial_reader.start() |
| 45 | + |
| 46 | + async def read_serial_number(serial: LinkyTICReader): |
| 47 | + while serial.serial_number is None: |
| 48 | + await asyncio.sleep(1) |
| 49 | + # Check for any serial error that occurred in the serial thread context |
| 50 | + if serial.setup_error: |
| 51 | + raise serial.setup_error |
| 52 | + return serial.serial_number |
| 53 | + |
| 54 | + s_n = await asyncio.wait_for(read_serial_number(serial_reader), timeout=5) |
| 55 | + # TODO: check if S/N is the one saved in config entry, if not this is a different meter! |
| 56 | + |
| 57 | + # Error when opening serial port. |
| 58 | + except LINKY_IO_ERRORS as e: |
| 59 | + raise ConfigEntryNotReady(f"Couldn't open serial port {port}: {e}") from e |
| 60 | + |
| 61 | + # Timeout waiting for S/N to be read. |
| 62 | + except TimeoutError as e: |
| 63 | + serial_reader.signalstop("linkytic_timeout") |
| 64 | + raise ConfigEntryNotReady( |
| 65 | + "Connected to serial port but coulnd't read serial number before timeout: check if TIC is connected and active." |
| 66 | + ) from e |
| 67 | + |
| 68 | + _LOGGER.info(f"Device connected with serial number: {s_n}") |
| 69 | + |
35 | 70 | hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, serial_reader.signalstop) |
36 | 71 | # Add options callback |
37 | 72 | entry.async_on_unload(entry.add_update_listener(update_listener)) |
@@ -68,3 +103,38 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry): |
68 | 103 | return |
69 | 104 | # Update its options |
70 | 105 | serial_reader.update_options(entry.options.get(OPTIONS_REALTIME)) |
| 106 | + |
| 107 | + |
| 108 | +async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry): |
| 109 | + """Migrate old entry.""" |
| 110 | + _LOGGER.info( |
| 111 | + "Migrating from version %d.%d", config_entry.version, config_entry.minor_version |
| 112 | + ) |
| 113 | + |
| 114 | + if config_entry.version == 1: |
| 115 | + new = {**config_entry.data} |
| 116 | + |
| 117 | + if config_entry.minor_version < 2: |
| 118 | + # Migrate to serial by-id. |
| 119 | + serial_by_id = await hass.async_add_executor_job( |
| 120 | + usb.get_serial_by_id, new[SETUP_SERIAL] |
| 121 | + ) |
| 122 | + if serial_by_id == new[SETUP_SERIAL]: |
| 123 | + _LOGGER.warning( |
| 124 | + f"Couldn't find a persistent /dev/serial/by-id alias for {serial_by_id}. " |
| 125 | + "Problems might occur at startup if device names are not persistent." |
| 126 | + ) |
| 127 | + else: |
| 128 | + new[SETUP_SERIAL] = serial_by_id |
| 129 | + |
| 130 | + # config_entry.minor_version = 2 |
| 131 | + hass.config_entries.async_update_entry( |
| 132 | + config_entry, data=new, minor_version=2, version=1 |
| 133 | + ) # type: ignore |
| 134 | + |
| 135 | + _LOGGER.info( |
| 136 | + "Migration to version %d.%d successful", |
| 137 | + config_entry.version, |
| 138 | + config_entry.minor_version, |
| 139 | + ) |
| 140 | + return True |
0 commit comments