Update nibe library to 2.0.0 (#88769)

This commit is contained in:
Joakim Plate 2023-02-26 11:05:31 +01:00 committed by GitHub
parent 7c23de469e
commit e00ff54869
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 66 additions and 67 deletions

View File

@ -8,11 +8,11 @@ from datetime import timedelta
from functools import cached_property from functools import cached_property
from typing import Any, Generic, TypeVar from typing import Any, Generic, TypeVar
from nibe.coil import Coil from nibe.coil import Coil, CoilData
from nibe.connection import Connection from nibe.connection import Connection
from nibe.connection.modbus import Modbus from nibe.connection.modbus import Modbus
from nibe.connection.nibegw import NibeGW, ProductInfo from nibe.connection.nibegw import NibeGW, ProductInfo
from nibe.exceptions import CoilNotFoundException, CoilReadException from nibe.exceptions import CoilNotFoundException, ReadException
from nibe.heatpump import HeatPump, Model, Series from nibe.heatpump import HeatPump, Model, Series
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -182,7 +182,7 @@ class ContextCoordinator(
return release_update return release_update
class Coordinator(ContextCoordinator[dict[int, Coil], int]): class Coordinator(ContextCoordinator[dict[int, CoilData], int]):
"""Update coordinator for nibe heat pumps.""" """Update coordinator for nibe heat pumps."""
config_entry: ConfigEntry config_entry: ConfigEntry
@ -199,17 +199,18 @@ class Coordinator(ContextCoordinator[dict[int, Coil], int]):
) )
self.data = {} self.data = {}
self.seed: dict[int, Coil] = {} self.seed: dict[int, CoilData] = {}
self.connection = connection self.connection = connection
self.heatpump = heatpump self.heatpump = heatpump
self.task: asyncio.Task | None = None self.task: asyncio.Task | None = None
heatpump.subscribe(heatpump.COIL_UPDATE_EVENT, self._on_coil_update) heatpump.subscribe(heatpump.COIL_UPDATE_EVENT, self._on_coil_update)
def _on_coil_update(self, coil: Coil): def _on_coil_update(self, data: CoilData):
"""Handle callback on coil updates.""" """Handle callback on coil updates."""
self.data[coil.address] = coil coil = data.coil
self.seed[coil.address] = coil self.data[coil.address] = data
self.seed[coil.address] = data
self.async_update_context_listeners([coil.address]) self.async_update_context_listeners([coil.address])
@property @property
@ -246,26 +247,26 @@ class Coordinator(ContextCoordinator[dict[int, Coil], int]):
async def async_write_coil(self, coil: Coil, value: int | float | str) -> None: async def async_write_coil(self, coil: Coil, value: int | float | str) -> None:
"""Write coil and update state.""" """Write coil and update state."""
coil.value = value data = CoilData(coil, value)
coil = await self.connection.write_coil(coil) await self.connection.write_coil(data)
self.data[coil.address] = coil self.data[coil.address] = data
self.async_update_context_listeners([coil.address]) self.async_update_context_listeners([coil.address])
async def async_read_coil(self, coil: Coil) -> Coil: async def async_read_coil(self, coil: Coil) -> CoilData:
"""Read coil and update state using callbacks.""" """Read coil and update state using callbacks."""
return await self.connection.read_coil(coil) return await self.connection.read_coil(coil)
async def _async_update_data(self) -> dict[int, Coil]: async def _async_update_data(self) -> dict[int, CoilData]:
self.task = asyncio.current_task() self.task = asyncio.current_task()
try: try:
return await self._async_update_data_internal() return await self._async_update_data_internal()
finally: finally:
self.task = None self.task = None
async def _async_update_data_internal(self) -> dict[int, Coil]: async def _async_update_data_internal(self) -> dict[int, CoilData]:
result: dict[int, Coil] = {} result: dict[int, CoilData] = {}
def _get_coils() -> Iterable[Coil]: def _get_coils() -> Iterable[Coil]:
for address in sorted(self.context_callbacks.keys()): for address in sorted(self.context_callbacks.keys()):
@ -282,10 +283,10 @@ class Coordinator(ContextCoordinator[dict[int, Coil], int]):
yield coil yield coil
try: try:
async for coil in self.connection.read_coils(_get_coils()): async for data in self.connection.read_coils(_get_coils()):
result[coil.address] = coil result[data.coil.address] = data
self.seed.pop(coil.address, None) self.seed.pop(data.coil.address, None)
except CoilReadException as exception: except ReadException as exception:
if not result: if not result:
raise UpdateFailed(f"Failed to update: {exception}") from exception raise UpdateFailed(f"Failed to update: {exception}") from exception
self.logger.debug( self.logger.debug(
@ -329,7 +330,7 @@ class CoilEntity(CoordinatorEntity[Coordinator]):
self.coordinator.data or {} self.coordinator.data or {}
) )
def _async_read_coil(self, coil: Coil): def _async_read_coil(self, data: CoilData):
"""Update state of entity based on coil data.""" """Update state of entity based on coil data."""
async def _async_write_coil(self, value: int | float | str): async def _async_write_coil(self, value: int | float | str):
@ -337,10 +338,9 @@ class CoilEntity(CoordinatorEntity[Coordinator]):
await self.coordinator.async_write_coil(self._coil, value) await self.coordinator.async_write_coil(self._coil, value)
def _handle_coordinator_update(self) -> None: def _handle_coordinator_update(self) -> None:
coil = self.coordinator.data.get(self._coil.address) data = self.coordinator.data.get(self._coil.address)
if coil is None: if data is None:
return return
self._coil = coil self._async_read_coil(data)
self._async_read_coil(coil)
self.async_write_ha_state() self.async_write_ha_state()

View File

@ -1,7 +1,7 @@
"""The Nibe Heat Pump binary sensors.""" """The Nibe Heat Pump binary sensors."""
from __future__ import annotations from __future__ import annotations
from nibe.coil import Coil from nibe.coil import Coil, CoilData
from homeassistant.components.binary_sensor import ENTITY_ID_FORMAT, BinarySensorEntity from homeassistant.components.binary_sensor import ENTITY_ID_FORMAT, BinarySensorEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -37,5 +37,5 @@ class BinarySensor(CoilEntity, BinarySensorEntity):
"""Initialize entity.""" """Initialize entity."""
super().__init__(coordinator, coil, ENTITY_ID_FORMAT) super().__init__(coordinator, coil, ENTITY_ID_FORMAT)
def _async_read_coil(self, coil: Coil) -> None: def _async_read_coil(self, data: CoilData) -> None:
self._attr_is_on = coil.value == "ON" self._attr_is_on = data.value == "ON"

View File

@ -8,10 +8,10 @@ from nibe.connection.nibegw import NibeGW
from nibe.exceptions import ( from nibe.exceptions import (
AddressInUseException, AddressInUseException,
CoilNotFoundException, CoilNotFoundException,
CoilReadException,
CoilReadSendException,
CoilWriteException,
CoilWriteSendException, CoilWriteSendException,
ReadException,
ReadSendException,
WriteException,
) )
from nibe.heatpump import HeatPump, Model from nibe.heatpump import HeatPump, Model
import voluptuous as vol import voluptuous as vol
@ -108,13 +108,13 @@ async def validate_nibegw_input(
try: try:
await connection.verify_connectivity() await connection.verify_connectivity()
except (CoilReadSendException, CoilWriteSendException) as exception: except (ReadSendException, CoilWriteSendException) as exception:
raise FieldError(str(exception), CONF_IP_ADDRESS, "address") from exception raise FieldError(str(exception), CONF_IP_ADDRESS, "address") from exception
except CoilNotFoundException as exception: except CoilNotFoundException as exception:
raise FieldError("Coils not found", "base", "model") from exception raise FieldError("Coils not found", "base", "model") from exception
except CoilReadException as exception: except ReadException as exception:
raise FieldError("Timeout on read from pump", "base", "read") from exception raise FieldError("Timeout on read from pump", "base", "read") from exception
except CoilWriteException as exception: except WriteException as exception:
raise FieldError("Timeout on writing to pump", "base", "write") from exception raise FieldError("Timeout on writing to pump", "base", "write") from exception
finally: finally:
await connection.stop() await connection.stop()
@ -147,13 +147,13 @@ async def validate_modbus_input(
try: try:
await connection.verify_connectivity() await connection.verify_connectivity()
except (CoilReadSendException, CoilWriteSendException) as exception: except (ReadSendException, CoilWriteSendException) as exception:
raise FieldError(str(exception), CONF_MODBUS_URL, "address") from exception raise FieldError(str(exception), CONF_MODBUS_URL, "address") from exception
except CoilNotFoundException as exception: except CoilNotFoundException as exception:
raise FieldError("Coils not found", "base", "model") from exception raise FieldError("Coils not found", "base", "model") from exception
except CoilReadException as exception: except ReadException as exception:
raise FieldError("Timeout on read from pump", "base", "read") from exception raise FieldError("Timeout on read from pump", "base", "read") from exception
except CoilWriteException as exception: except WriteException as exception:
raise FieldError("Timeout on writing to pump", "base", "write") from exception raise FieldError("Timeout on writing to pump", "base", "write") from exception
finally: finally:
await connection.stop() await connection.stop()

View File

@ -5,5 +5,5 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/nibe_heatpump", "documentation": "https://www.home-assistant.io/integrations/nibe_heatpump",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["nibe==1.6.0"] "requirements": ["nibe==2.0.0"]
} }

View File

@ -1,7 +1,7 @@
"""The Nibe Heat Pump numbers.""" """The Nibe Heat Pump numbers."""
from __future__ import annotations from __future__ import annotations
from nibe.coil import Coil from nibe.coil import Coil, CoilData
from homeassistant.components.number import ENTITY_ID_FORMAT, NumberEntity from homeassistant.components.number import ENTITY_ID_FORMAT, NumberEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -58,13 +58,13 @@ class Number(CoilEntity, NumberEntity):
self._attr_native_unit_of_measurement = coil.unit self._attr_native_unit_of_measurement = coil.unit
self._attr_native_value = None self._attr_native_value = None
def _async_read_coil(self, coil: Coil) -> None: def _async_read_coil(self, data: CoilData) -> None:
if coil.value is None: if data.value is None:
self._attr_native_value = None self._attr_native_value = None
return return
try: try:
self._attr_native_value = float(coil.value) self._attr_native_value = float(data.value)
except ValueError: except ValueError:
self._attr_native_value = None self._attr_native_value = None

View File

@ -1,7 +1,7 @@
"""The Nibe Heat Pump select.""" """The Nibe Heat Pump select."""
from __future__ import annotations from __future__ import annotations
from nibe.coil import Coil from nibe.coil import Coil, CoilData
from homeassistant.components.select import ENTITY_ID_FORMAT, SelectEntity from homeassistant.components.select import ENTITY_ID_FORMAT, SelectEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -40,12 +40,12 @@ class Select(CoilEntity, SelectEntity):
self._attr_options = list(coil.mappings.values()) self._attr_options = list(coil.mappings.values())
self._attr_current_option = None self._attr_current_option = None
def _async_read_coil(self, coil: Coil) -> None: def _async_read_coil(self, data: CoilData) -> None:
if not isinstance(coil.value, str): if not isinstance(data.value, str):
self._attr_current_option = None self._attr_current_option = None
return return
self._attr_current_option = coil.value self._attr_current_option = data.value
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
"""Support writing value.""" """Support writing value."""

View File

@ -1,7 +1,7 @@
"""The Nibe Heat Pump sensors.""" """The Nibe Heat Pump sensors."""
from __future__ import annotations from __future__ import annotations
from nibe.coil import Coil from nibe.coil import Coil, CoilData
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
ENTITY_ID_FORMAT, ENTITY_ID_FORMAT,
@ -146,5 +146,5 @@ class Sensor(CoilEntity, SensorEntity):
self._attr_native_unit_of_measurement = coil.unit self._attr_native_unit_of_measurement = coil.unit
self._attr_entity_category = EntityCategory.DIAGNOSTIC self._attr_entity_category = EntityCategory.DIAGNOSTIC
def _async_read_coil(self, coil: Coil): def _async_read_coil(self, data: CoilData):
self._attr_native_value = coil.value self._attr_native_value = data.value

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Any from typing import Any
from nibe.coil import Coil from nibe.coil import Coil, CoilData
from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchEntity from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -40,8 +40,8 @@ class Switch(CoilEntity, SwitchEntity):
super().__init__(coordinator, coil, ENTITY_ID_FORMAT) super().__init__(coordinator, coil, ENTITY_ID_FORMAT)
self._attr_is_on = None self._attr_is_on = None
def _async_read_coil(self, coil: Coil) -> None: def _async_read_coil(self, data: CoilData) -> None:
self._attr_is_on = coil.value == "ON" self._attr_is_on = data.value == "ON"
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on.""" """Turn the entity on."""

View File

@ -1201,7 +1201,7 @@ nextcord==2.0.0a8
nextdns==1.3.0 nextdns==1.3.0
# homeassistant.components.nibe_heatpump # homeassistant.components.nibe_heatpump
nibe==1.6.0 nibe==2.0.0
# homeassistant.components.niko_home_control # homeassistant.components.niko_home_control
niko-home-control==0.2.1 niko-home-control==0.2.1

View File

@ -891,7 +891,7 @@ nextcord==2.0.0a8
nextdns==1.3.0 nextdns==1.3.0
# homeassistant.components.nibe_heatpump # homeassistant.components.nibe_heatpump
nibe==1.6.0 nibe==2.0.0
# homeassistant.components.nfandroidtv # homeassistant.components.nfandroidtv
notifications-android-tv==0.1.5 notifications-android-tv==0.1.5

View File

@ -4,9 +4,9 @@ from contextlib import ExitStack
from typing import Any from typing import Any
from unittest.mock import AsyncMock, Mock, patch from unittest.mock import AsyncMock, Mock, patch
from nibe.coil import Coil from nibe.coil import Coil, CoilData
from nibe.connection import Connection from nibe.connection import Connection
from nibe.exceptions import CoilReadException from nibe.exceptions import ReadException
import pytest import pytest
@ -39,12 +39,11 @@ async def fixture_coils(mock_connection):
"""Return a dict with coil data.""" """Return a dict with coil data."""
coils: dict[int, Any] = {} coils: dict[int, Any] = {}
async def read_coil(coil: Coil, timeout: float = 0) -> Coil: async def read_coil(coil: Coil, timeout: float = 0) -> CoilData:
nonlocal coils nonlocal coils
if (data := coils.get(coil.address, None)) is None: if (data := coils.get(coil.address, None)) is None:
raise CoilReadException() raise ReadException()
coil.value = data return CoilData(coil, data)
return coil
async def read_coils( async def read_coils(
coils: Iterable[Coil], timeout: float = 0 coils: Iterable[Coil], timeout: float = 0

View File

@ -3,7 +3,7 @@ from typing import Any
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
from nibe.coil import Coil from nibe.coil import CoilData
from nibe.coil_groups import UNIT_COILGROUPS from nibe.coil_groups import UNIT_COILGROUPS
from nibe.heatpump import Model from nibe.heatpump import Model
import pytest import pytest
@ -91,6 +91,6 @@ async def test_reset_button(
# Verify reset was written # Verify reset was written
args = mock_connection.write_coil.call_args args = mock_connection.write_coil.call_args
assert args assert args
coil: Coil = args.args[0] coil: CoilData = args.args[0]
assert coil.address == unit.alarm_reset assert coil.coil.address == unit.alarm_reset
assert coil.value == 1 assert coil.value == 1

View File

@ -5,9 +5,9 @@ from nibe.coil import Coil
from nibe.exceptions import ( from nibe.exceptions import (
AddressInUseException, AddressInUseException,
CoilNotFoundException, CoilNotFoundException,
CoilReadException, ReadException,
CoilReadSendException, ReadSendException,
CoilWriteException, WriteException,
) )
import pytest import pytest
@ -169,7 +169,7 @@ async def test_read_timeout(
"""Test we handle cannot connect error.""" """Test we handle cannot connect error."""
result = await _get_connection_form(hass, connection_type) result = await _get_connection_form(hass, connection_type)
mock_connection.verify_connectivity.side_effect = CoilReadException() mock_connection.verify_connectivity.side_effect = ReadException()
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data) result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data)
@ -190,7 +190,7 @@ async def test_write_timeout(
"""Test we handle cannot connect error.""" """Test we handle cannot connect error."""
result = await _get_connection_form(hass, connection_type) result = await _get_connection_form(hass, connection_type)
mock_connection.verify_connectivity.side_effect = CoilWriteException() mock_connection.verify_connectivity.side_effect = WriteException()
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data) result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data)
@ -232,7 +232,7 @@ async def test_nibegw_invalid_host(
"""Test we handle cannot connect error.""" """Test we handle cannot connect error."""
result = await _get_connection_form(hass, connection_type) result = await _get_connection_form(hass, connection_type)
mock_connection.verify_connectivity.side_effect = CoilReadSendException() mock_connection.verify_connectivity.side_effect = ReadSendException()
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data) result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data)