Replace fiblary3 with pyfibaro library (#83500)

* Replace fiblary3 with pyfibaro library

* Fix some missing replacements for pyfibaro library

* Remove debug code which was committed accidentially

* Use fibaro_parent_id in another place

* Fix some bugs

* Move more code to the library

* Move has_unit check to correct place
This commit is contained in:
rappenze 2023-02-11 22:13:12 +01:00 committed by GitHub
parent 86a93e9fce
commit e1340e6ed4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 234 additions and 292 deletions

View File

@ -6,12 +6,10 @@ from collections.abc import Mapping
import logging import logging
from typing import Any from typing import Any
from fiblary3.client.v4.client import ( from pyfibaro.fibaro_client import FibaroClient
Client as FibaroClientV4, from pyfibaro.fibaro_device import DeviceModel
StateHandler as StateHandlerV4, from pyfibaro.fibaro_scene import SceneModel
) from requests.exceptions import HTTPError
from fiblary3.client.v5.client import StateHandler as StateHandlerV5
from fiblary3.common.exceptions import HTTPException
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
@ -134,24 +132,11 @@ class FibaroController:
def __init__( def __init__(
self, config: Mapping[str, Any], serial_number: str | None = None self, config: Mapping[str, Any], serial_number: str | None = None
) -> None: ) -> None:
"""Initialize the Fibaro controller. """Initialize the Fibaro controller."""
Version 4 is used for home center 2 (SN starts with HC2) and # The FibaroClient uses the correct API version automatically
home center lite (SN starts with HCL). self._client = FibaroClient(config[CONF_URL])
self._client.set_authentication(config[CONF_USERNAME], config[CONF_PASSWORD])
Version 5 is used for home center 3 (SN starts with HC3),
home center 3 lite (SN starts with HC3L) and yubii home (SN starts with YH).
Here the serial number is optional and we choose then the V4 client. You
should do that only when you use the FibaroController for login test as only
the login and info API's are equal throughout the different versions.
"""
# Only use V4 API as it works better even for HC3, after the library is fixed, we should
# add here support for the newer library version V5 again.
self._client = FibaroClientV4(
config[CONF_URL], config[CONF_USERNAME], config[CONF_PASSWORD]
)
self._scene_map = None self._scene_map = None
# Whether to import devices from plugins # Whether to import devices from plugins
@ -162,7 +147,6 @@ class FibaroController:
list list
) # List of devices by entity platform ) # List of devices by entity platform
self._callbacks: dict[Any, Any] = {} # Update value callbacks by deviceId self._callbacks: dict[Any, Any] = {} # Update value callbacks by deviceId
self._state_handler = None # Fiblary's StateHandler object
self.hub_serial: str # Unique serial number of the hub self.hub_serial: str # Unique serial number of the hub
self.hub_name: str # The friendly name of the hub self.hub_name: str # The friendly name of the hub
self.hub_software_version: str self.hub_software_version: str
@ -173,21 +157,21 @@ class FibaroController:
def connect(self): def connect(self):
"""Start the communication with the Fibaro controller.""" """Start the communication with the Fibaro controller."""
try: try:
login = self._client.login.get() connected = self._client.connect()
info = self._client.info.get() info = self._client.read_info()
self.hub_serial = info.serialNumber self.hub_serial = info.serial_number
self.hub_name = info.hcName self.hub_name = info.hc_name
self.hub_software_version = info.softVersion self.hub_software_version = info.current_version
except AssertionError: except AssertionError:
_LOGGER.error("Can't connect to Fibaro HC. Please check URL") _LOGGER.error("Can't connect to Fibaro HC. Please check URL")
return False return False
if login is None or login.status is False: if connected is False:
_LOGGER.error( _LOGGER.error(
"Invalid login for Fibaro HC. Please check username and password" "Invalid login for Fibaro HC. Please check username and password"
) )
return False return False
self._room_map = {room.id: room for room in self._client.rooms.list()} self._room_map = {room.fibaro_id: room for room in self._client.read_rooms()}
self._read_devices() self._read_devices()
self._read_scenes() self._read_scenes()
return True return True
@ -201,8 +185,8 @@ class FibaroController:
connected = self.connect() connected = self.connect()
if not connected: if not connected:
raise FibaroConnectFailed("Connect status is false") raise FibaroConnectFailed("Connect status is false")
except HTTPException as http_ex: except HTTPError as http_ex:
if http_ex.details == "Forbidden": if http_ex.response.status_code == 403:
raise FibaroAuthFailed from http_ex raise FibaroAuthFailed from http_ex
raise FibaroConnectFailed from http_ex raise FibaroConnectFailed from http_ex
@ -211,15 +195,11 @@ class FibaroController:
def enable_state_handler(self): def enable_state_handler(self):
"""Start StateHandler thread for monitoring updates.""" """Start StateHandler thread for monitoring updates."""
if isinstance(self._client, FibaroClientV4): self._client.register_update_handler(self._on_state_change)
self._state_handler = StateHandlerV4(self._client, self._on_state_change)
else:
self._state_handler = StateHandlerV5(self._client, self._on_state_change)
def disable_state_handler(self): def disable_state_handler(self):
"""Stop StateHandler thread used for monitoring updates.""" """Stop StateHandler thread used for monitoring updates."""
self._state_handler.stop() self._client.unregister_update_handler()
self._state_handler = None
def _on_state_change(self, state): def _on_state_change(self, state):
"""Handle change report received from the HomeCenter.""" """Handle change report received from the HomeCenter."""
@ -262,7 +242,7 @@ class FibaroController:
return [ return [
device device
for device in self._device_map.values() for device in self._device_map.values()
if device.parentId == device_id if device.parent_fibaro_id == device_id
] ]
def get_children2(self, device_id, endpoint_id): def get_children2(self, device_id, endpoint_id):
@ -270,31 +250,28 @@ class FibaroController:
return [ return [
device device
for device in self._device_map.values() for device in self._device_map.values()
if device.parentId == device_id if device.parent_fibaro_id == device_id
and ( and (not device.has_endpoint_id or device.endpoint_id == endpoint_id)
"endPointId" not in device.properties
or device.properties.endPointId == endpoint_id
)
] ]
def get_siblings(self, device): def get_siblings(self, device):
"""Get the siblings of a device.""" """Get the siblings of a device."""
if "endPointId" in device.properties: if device.has_endpoint_id:
return self.get_children2( return self.get_children2(
self._device_map[device.id].parentId, self._device_map[device.fibaro_id].parent_fibaro_id,
self._device_map[device.id].properties.endPointId, self._device_map[device.fibaro_id].endpoint_id,
) )
return self.get_children(self._device_map[device.id].parentId) return self.get_children(self._device_map[device.fibaro_id].parent_fibaro_id)
@staticmethod @staticmethod
def _map_device_to_platform(device: Any) -> Platform | None: def _map_device_to_platform(device: Any) -> Platform | None:
"""Map device to HA device type.""" """Map device to HA device type."""
# Use our lookup table to identify device type # Use our lookup table to identify device type
platform: Platform | None = None platform: Platform | None = None
if "type" in device: if device.type:
platform = FIBARO_TYPEMAP.get(device.type) platform = FIBARO_TYPEMAP.get(device.type)
if platform is None and "baseType" in device: if platform is None and device.base_type:
platform = FIBARO_TYPEMAP.get(device.baseType) platform = FIBARO_TYPEMAP.get(device.base_type)
# We can also identify device type by its capabilities # We can also identify device type by its capabilities
if platform is None: if platform is None:
@ -306,8 +283,8 @@ class FibaroController:
platform = Platform.COVER platform = Platform.COVER
elif "secure" in device.actions: elif "secure" in device.actions:
platform = Platform.LOCK platform = Platform.LOCK
elif "value" in device.properties: elif device.value.has_value:
if device.properties.value in ("true", "false"): if device.value.is_bool_value:
platform = Platform.BINARY_SENSOR platform = Platform.BINARY_SENSOR
else: else:
platform = Platform.SENSOR platform = Platform.SENSOR
@ -317,31 +294,33 @@ class FibaroController:
platform = Platform.LIGHT platform = Platform.LIGHT
return platform return platform
def _create_device_info(self, device: Any, devices: list) -> None: def _create_device_info(
self, device: DeviceModel, devices: list[DeviceModel]
) -> None:
"""Create the device info. Unrooted entities are directly shown below the home center.""" """Create the device info. Unrooted entities are directly shown below the home center."""
# The home center is always id 1 (z-wave primary controller) # The home center is always id 1 (z-wave primary controller)
if "parentId" not in device or device.parentId <= 1: if device.parent_fibaro_id <= 1:
return return
master_entity: Any | None = None master_entity: Any | None = None
if device.parentId == 1: if device.parent_fibaro_id == 1:
master_entity = device master_entity = device
else: else:
for parent in devices: for parent in devices:
if "id" in parent and parent.id == device.parentId: if parent.fibaro_id == device.parent_fibaro_id:
master_entity = parent master_entity = parent
if master_entity is None: if master_entity is None:
_LOGGER.error("Parent with id %s not found", device.parentId) _LOGGER.error("Parent with id %s not found", device.parent_fibaro_id)
return return
if "zwaveCompany" in master_entity.properties: if "zwaveCompany" in master_entity.properties:
manufacturer = master_entity.properties.zwaveCompany manufacturer = master_entity.properties.get("zwaveCompany")
else: else:
manufacturer = "Unknown" manufacturer = "Unknown"
self._device_infos[master_entity.id] = DeviceInfo( self._device_infos[master_entity.fibaro_id] = DeviceInfo(
identifiers={(DOMAIN, master_entity.id)}, identifiers={(DOMAIN, master_entity.fibaro_id)},
manufacturer=manufacturer, manufacturer=manufacturer,
name=master_entity.name, name=master_entity.name,
via_device=(DOMAIN, self.hub_serial), via_device=(DOMAIN, self.hub_serial),
@ -349,70 +328,65 @@ class FibaroController:
def get_device_info(self, device: Any) -> DeviceInfo: def get_device_info(self, device: Any) -> DeviceInfo:
"""Get the device info by fibaro device id.""" """Get the device info by fibaro device id."""
if device.id in self._device_infos: if device.fibaro_id in self._device_infos:
return self._device_infos[device.id] return self._device_infos[device.fibaro_id]
if "parentId" in device and device.parentId in self._device_infos: if device.parent_fibaro_id in self._device_infos:
return self._device_infos[device.parentId] return self._device_infos[device.parent_fibaro_id]
return DeviceInfo(identifiers={(DOMAIN, self.hub_serial)}) return DeviceInfo(identifiers={(DOMAIN, self.hub_serial)})
def _read_scenes(self): def _read_scenes(self):
scenes = self._client.scenes.list() scenes = self._client.read_scenes()
self._scene_map = {} self._scene_map = {}
for device in scenes: for device in scenes:
if "name" not in device or "id" not in device:
continue
device.fibaro_controller = self device.fibaro_controller = self
if "roomID" not in device or device.roomID == 0: if device.room_id == 0:
room_name = "Unknown" room_name = "Unknown"
else: else:
room_name = self._room_map[device.roomID].name room_name = self._room_map[device.room_id].name
device.room_name = room_name device.room_name = room_name
device.friendly_name = f"{room_name} {device.name}" device.friendly_name = f"{room_name} {device.name}"
device.ha_id = ( device.ha_id = (
f"scene_{slugify(room_name)}_{slugify(device.name)}_{device.id}" f"scene_{slugify(room_name)}_{slugify(device.name)}_{device.fibaro_id}"
) )
device.unique_id_str = f"{slugify(self.hub_serial)}.scene.{device.id}" device.unique_id_str = (
self._scene_map[device.id] = device f"{slugify(self.hub_serial)}.scene.{device.fibaro_id}"
)
self._scene_map[device.fibaro_id] = device
self.fibaro_devices[Platform.SCENE].append(device) self.fibaro_devices[Platform.SCENE].append(device)
_LOGGER.debug("%s scene -> %s", device.ha_id, device) _LOGGER.debug("%s scene -> %s", device.ha_id, device)
def _read_devices(self): def _read_devices(self):
"""Read and process the device list.""" """Read and process the device list."""
devices = list(self._client.devices.list()) devices = self._client.read_devices()
self._device_map = {} self._device_map = {}
last_climate_parent = None last_climate_parent = None
last_endpoint = None last_endpoint = None
for device in devices: for device in devices:
try: try:
if "name" not in device or "id" not in device:
continue
device.fibaro_controller = self device.fibaro_controller = self
if "roomID" not in device or device.roomID == 0: if device.room_id == 0:
room_name = "Unknown" room_name = "Unknown"
else: else:
room_name = self._room_map[device.roomID].name room_name = self._room_map[device.room_id].name
device.room_name = room_name device.room_name = room_name
device.friendly_name = f"{room_name} {device.name}" device.friendly_name = f"{room_name} {device.name}"
device.ha_id = ( device.ha_id = (
f"{slugify(room_name)}_{slugify(device.name)}_{device.id}" f"{slugify(room_name)}_{slugify(device.name)}_{device.fibaro_id}"
) )
if device.enabled and ( if device.enabled and (not device.is_plugin or self._import_plugins):
"isPlugin" not in device
or (not device.isPlugin or self._import_plugins)
):
device.mapped_platform = self._map_device_to_platform(device) device.mapped_platform = self._map_device_to_platform(device)
else: else:
device.mapped_platform = None device.mapped_platform = None
if (platform := device.mapped_platform) is None: if (platform := device.mapped_platform) is None:
continue continue
device.unique_id_str = f"{slugify(self.hub_serial)}.{device.id}" device.unique_id_str = f"{slugify(self.hub_serial)}.{device.fibaro_id}"
self._create_device_info(device, devices) self._create_device_info(device, devices)
self._device_map[device.id] = device self._device_map[device.fibaro_id] = device
_LOGGER.debug( _LOGGER.debug(
"%s (%s, %s) -> %s %s", "%s (%s, %s) -> %s %s",
device.ha_id, device.ha_id,
device.type, device.type,
device.baseType, device.base_type,
platform, platform,
str(device), str(device),
) )
@ -421,11 +395,11 @@ class FibaroController:
continue continue
# We group climate devices into groups with the same # We group climate devices into groups with the same
# endPointID belonging to the same parent device. # endPointID belonging to the same parent device.
if "endPointId" in device.properties: if device.has_endpoint_id:
_LOGGER.debug( _LOGGER.debug(
"climate device: %s, endPointId: %s", "climate device: %s, endPointId: %s",
device.ha_id, device.ha_id,
device.properties.endPointId, device.endpoint_id,
) )
else: else:
_LOGGER.debug("climate device: %s, no endPointId", device.ha_id) _LOGGER.debug("climate device: %s, no endPointId", device.ha_id)
@ -433,17 +407,13 @@ class FibaroController:
# otherwise add the first visible device in the group # otherwise add the first visible device in the group
# which is a hack, but solves a problem with FGT having # which is a hack, but solves a problem with FGT having
# hidden compatibility devices before the real device # hidden compatibility devices before the real device
if last_climate_parent != device.parentId or ( if last_climate_parent != device.parent_fibaro_id or (
"endPointId" in device.properties device.has_endpoint_id and last_endpoint != device.endpoint_id
and last_endpoint != device.properties.endPointId
): ):
_LOGGER.debug("Handle separately") _LOGGER.debug("Handle separately")
self.fibaro_devices[platform].append(device) self.fibaro_devices[platform].append(device)
last_climate_parent = device.parentId last_climate_parent = device.parent_fibaro_id
if "endPointId" in device.properties: last_endpoint = device.endpoint_id
last_endpoint = device.properties.endPointId
else:
last_endpoint = 0
else: else:
_LOGGER.debug("not handling separately") _LOGGER.debug("not handling separately")
except (KeyError, ValueError): except (KeyError, ValueError):
@ -548,21 +518,23 @@ class FibaroDevice(Entity):
_attr_should_poll = False _attr_should_poll = False
def __init__(self, fibaro_device): def __init__(self, fibaro_device: DeviceModel | SceneModel) -> None:
"""Initialize the device.""" """Initialize the device."""
self.fibaro_device = fibaro_device self.fibaro_device = fibaro_device
self.controller = fibaro_device.fibaro_controller self.controller = fibaro_device.fibaro_controller
self.ha_id = fibaro_device.ha_id self.ha_id = fibaro_device.ha_id
self._attr_name = fibaro_device.friendly_name self._attr_name = fibaro_device.friendly_name
self._attr_unique_id = fibaro_device.unique_id_str self._attr_unique_id = fibaro_device.unique_id_str
if isinstance(fibaro_device, DeviceModel):
self._attr_device_info = self.controller.get_device_info(fibaro_device) self._attr_device_info = self.controller.get_device_info(fibaro_device)
# propagate hidden attribute set in fibaro home center to HA # propagate hidden attribute set in fibaro home center to HA
if "visible" in fibaro_device and fibaro_device.visible is False: if not fibaro_device.visible:
self._attr_entity_registry_visible_default = False self._attr_entity_registry_visible_default = False
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Call when entity is added to hass.""" """Call when entity is added to hass."""
self.controller.register(self.fibaro_device.id, self._update_callback) self.controller.register(self.fibaro_device.fibaro_id, self._update_callback)
def _update_callback(self): def _update_callback(self):
"""Update the state.""" """Update the state."""
@ -571,15 +543,15 @@ class FibaroDevice(Entity):
@property @property
def level(self): def level(self):
"""Get the level of Fibaro device.""" """Get the level of Fibaro device."""
if "value" in self.fibaro_device.properties: if self.fibaro_device.value.has_value:
return self.fibaro_device.properties.value return self.fibaro_device.value.int_value()
return None return None
@property @property
def level2(self): def level2(self):
"""Get the tilt level of Fibaro device.""" """Get the tilt level of Fibaro device."""
if "value2" in self.fibaro_device.properties: if self.fibaro_device.value_2.has_value:
return self.fibaro_device.properties.value2 return self.fibaro_device.value_2.int_value()
return None return None
def dont_know_message(self, action): def dont_know_message(self, action):
@ -593,16 +565,16 @@ class FibaroDevice(Entity):
def set_level(self, level): def set_level(self, level):
"""Set the level of Fibaro device.""" """Set the level of Fibaro device."""
self.action("setValue", level) self.action("setValue", level)
if "value" in self.fibaro_device.properties: if self.fibaro_device.value.has_value:
self.fibaro_device.properties.value = level self.fibaro_device.properties["value"] = level
if "brightness" in self.fibaro_device.properties: if self.fibaro_device.has_brightness:
self.fibaro_device.properties.brightness = level self.fibaro_device.properties["brightness"] = level
def set_level2(self, level): def set_level2(self, level):
"""Set the level2 of Fibaro device.""" """Set the level2 of Fibaro device."""
self.action("setValue2", level) self.action("setValue2", level)
if "value2" in self.fibaro_device.properties: if self.fibaro_device.value_2.has_value:
self.fibaro_device.properties.value2 = level self.fibaro_device.properties["value2"] = level
def call_turn_on(self): def call_turn_on(self):
"""Turn on the Fibaro device.""" """Turn on the Fibaro device."""
@ -619,13 +591,13 @@ class FibaroDevice(Entity):
blue = int(max(0, min(255, blue))) blue = int(max(0, min(255, blue)))
white = int(max(0, min(255, white))) white = int(max(0, min(255, white)))
color_str = f"{red},{green},{blue},{white}" color_str = f"{red},{green},{blue},{white}"
self.fibaro_device.properties.color = color_str self.fibaro_device.properties["color"] = color_str
self.action("setColor", str(red), str(green), str(blue), str(white)) self.action("setColor", str(red), str(green), str(blue), str(white))
def action(self, cmd, *args): def action(self, cmd, *args):
"""Perform an action on the Fibaro HC.""" """Perform an action on the Fibaro HC."""
if cmd in self.fibaro_device.actions: if cmd in self.fibaro_device.actions:
getattr(self.fibaro_device, cmd)(*args) self.fibaro_device.execute_action(cmd, args)
_LOGGER.debug("-> %s.%s%s called", str(self.ha_id), str(cmd), str(args)) _LOGGER.debug("-> %s.%s%s called", str(self.ha_id), str(cmd), str(args))
else: else:
self.dont_know_message(cmd) self.dont_know_message(cmd)
@ -633,35 +605,18 @@ class FibaroDevice(Entity):
@property @property
def current_binary_state(self): def current_binary_state(self):
"""Return the current binary state.""" """Return the current binary state."""
if self.fibaro_device.properties.value == "false": return self.fibaro_device.value.bool_value(False)
return False
if (
self.fibaro_device.properties.value == "true"
or int(self.fibaro_device.properties.value) > 0
):
return True
return False
@property @property
def extra_state_attributes(self): def extra_state_attributes(self):
"""Return the state attributes of the device.""" """Return the state attributes of the device."""
attr = {"fibaro_id": self.fibaro_device.id} attr = {"fibaro_id": self.fibaro_device.fibaro_id}
try: if isinstance(self.fibaro_device, DeviceModel):
if "battery" in self.fibaro_device.interfaces: if self.fibaro_device.has_battery_level:
attr[ATTR_BATTERY_LEVEL] = int( attr[ATTR_BATTERY_LEVEL] = self.fibaro_device.battery_level
self.fibaro_device.properties.batteryLevel if self.fibaro_device.has_armed:
) attr[ATTR_ARMED] = self.fibaro_device.armed
if "armed" in self.fibaro_device.properties:
armed = self.fibaro_device.properties.armed
if isinstance(armed, bool):
attr[ATTR_ARMED] = armed
elif isinstance(armed, str) and armed.lower() in ("true", "false"):
attr[ATTR_ARMED] = armed.lower() == "true"
else:
attr[ATTR_ARMED] = None
except (ValueError, KeyError):
pass
return attr return attr

View File

@ -2,9 +2,10 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping from collections.abc import Mapping
import json
from typing import Any, cast from typing import Any, cast
from pyfibaro.fibaro_device import DeviceModel
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
ENTITY_ID_FORMAT, ENTITY_ID_FORMAT,
BinarySensorDeviceClass, BinarySensorDeviceClass,
@ -58,7 +59,7 @@ async def async_setup_entry(
class FibaroBinarySensor(FibaroDevice, BinarySensorEntity): class FibaroBinarySensor(FibaroDevice, BinarySensorEntity):
"""Representation of a Fibaro Binary Sensor.""" """Representation of a Fibaro Binary Sensor."""
def __init__(self, fibaro_device: Any) -> None: def __init__(self, fibaro_device: DeviceModel) -> None:
"""Initialize the binary_sensor.""" """Initialize the binary_sensor."""
super().__init__(fibaro_device) super().__init__(fibaro_device)
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
@ -66,8 +67,8 @@ class FibaroBinarySensor(FibaroDevice, BinarySensorEntity):
self._fibaro_sensor_type = None self._fibaro_sensor_type = None
if fibaro_device.type in SENSOR_TYPES: if fibaro_device.type in SENSOR_TYPES:
self._fibaro_sensor_type = fibaro_device.type self._fibaro_sensor_type = fibaro_device.type
elif fibaro_device.baseType in SENSOR_TYPES: elif fibaro_device.base_type in SENSOR_TYPES:
self._fibaro_sensor_type = fibaro_device.baseType self._fibaro_sensor_type = fibaro_device.base_type
if self._fibaro_sensor_type: if self._fibaro_sensor_type:
self._attr_device_class = cast( self._attr_device_class = cast(
BinarySensorDeviceClass, SENSOR_TYPES[self._fibaro_sensor_type][2] BinarySensorDeviceClass, SENSOR_TYPES[self._fibaro_sensor_type][2]
@ -105,9 +106,4 @@ class FibaroBinarySensor(FibaroDevice, BinarySensorEntity):
def _get_moving_values(self) -> Mapping[str, Any]: def _get_moving_values(self) -> Mapping[str, Any]:
"""Get the moving values of the accelerator sensor in a dict.""" """Get the moving values of the accelerator sensor in a dict."""
value = self.fibaro_device.properties.value return self.fibaro_device.value.dict_value()
if isinstance(value, str):
# HC2 returns dict as str
return json.loads(value)
# HC3 returns a real dict
return value

View File

@ -5,6 +5,8 @@ from contextlib import suppress
import logging import logging
from typing import Any from typing import Any
from pyfibaro.fibaro_device import DeviceModel
from homeassistant.components.climate import ( from homeassistant.components.climate import (
ENTITY_ID_FORMAT, ENTITY_ID_FORMAT,
PRESET_AWAY, PRESET_AWAY,
@ -124,7 +126,7 @@ async def async_setup_entry(
class FibaroThermostat(FibaroDevice, ClimateEntity): class FibaroThermostat(FibaroDevice, ClimateEntity):
"""Representation of a Fibaro Thermostat.""" """Representation of a Fibaro Thermostat."""
def __init__(self, fibaro_device): def __init__(self, fibaro_device: DeviceModel) -> None:
"""Initialize the Fibaro device.""" """Initialize the Fibaro device."""
super().__init__(fibaro_device) super().__init__(fibaro_device)
self._temp_sensor_device: FibaroDevice | None = None self._temp_sensor_device: FibaroDevice | None = None
@ -141,26 +143,23 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
# doing so, so we prefer the hard evidence, if there is such. # doing so, so we prefer the hard evidence, if there is such.
if device.type == "com.fibaro.temperatureSensor": if device.type == "com.fibaro.temperatureSensor":
self._temp_sensor_device = FibaroDevice(device) self._temp_sensor_device = FibaroDevice(device)
tempunit = device.properties.unit tempunit = device.unit
elif ( elif (
self._temp_sensor_device is None self._temp_sensor_device is None
and "unit" in device.properties and device.has_unit
and ( and (device.value.has_value or device.has_heating_thermostat_setpoint)
"value" in device.properties and device.unit in ("C", "F")
or "heatingThermostatSetpoint" in device.properties
)
and device.properties.unit in ("C", "F")
): ):
self._temp_sensor_device = FibaroDevice(device) self._temp_sensor_device = FibaroDevice(device)
tempunit = device.properties.unit tempunit = device.unit
if any( if any(
action for action in TARGET_TEMP_ACTIONS if action in device.actions action for action in TARGET_TEMP_ACTIONS if action in device.actions
): ):
self._target_temp_device = FibaroDevice(device) self._target_temp_device = FibaroDevice(device)
self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE
if "unit" in device.properties: if device.has_unit:
tempunit = device.properties.unit tempunit = device.unit
if any(action for action in OP_MODE_ACTIONS if action in device.actions): if any(action for action in OP_MODE_ACTIONS if action in device.actions):
self._op_mode_device = FibaroDevice(device) self._op_mode_device = FibaroDevice(device)
@ -176,12 +175,9 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
self._attr_temperature_unit = UnitOfTemperature.CELSIUS self._attr_temperature_unit = UnitOfTemperature.CELSIUS
if self._fan_mode_device: if self._fan_mode_device:
fan_modes = ( fan_modes = self._fan_mode_device.fibaro_device.supported_modes
self._fan_mode_device.fibaro_device.properties.supportedModes.split(",")
)
self._attr_fan_modes = [] self._attr_fan_modes = []
for mode in fan_modes: for mode in fan_modes:
mode = int(mode)
if mode not in FANMODES: if mode not in FANMODES:
_LOGGER.warning("%d unknown fan mode", mode) _LOGGER.warning("%d unknown fan mode", mode)
continue continue
@ -190,21 +186,20 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
self._attr_hvac_modes = [HVACMode.AUTO] # default self._attr_hvac_modes = [HVACMode.AUTO] # default
if self._op_mode_device: if self._op_mode_device:
self._attr_preset_modes = [] self._attr_preset_modes = []
self._attr_hvac_modes = [] self._attr_hvac_modes: list[HVACMode] = []
prop = self._op_mode_device.fibaro_device.properties device = self._op_mode_device.fibaro_device
if "supportedThermostatModes" in prop: if device.has_supported_thermostat_modes:
for mode in prop.supportedThermostatModes: for mode in device.supported_thermostat_modes:
try: try:
self._attr_hvac_modes.append(HVACMode(mode.lower())) self._attr_hvac_modes.append(HVACMode(mode.lower()))
except ValueError: except ValueError:
self._attr_preset_modes.append(mode) self._attr_preset_modes.append(mode)
else: else:
if "supportedOperatingModes" in prop: if device.has_supported_operating_modes:
op_modes = prop.supportedOperatingModes.split(",") op_modes = device.supported_operating_modes
else: else:
op_modes = prop.supportedModes.split(",") op_modes = device.supported_modes
for mode in op_modes: for mode in op_modes:
mode = int(mode)
if ( if (
mode in OPMODES_HVAC mode in OPMODES_HVAC
and (mode_ha := OPMODES_HVAC.get(mode)) and (mode_ha := OPMODES_HVAC.get(mode))
@ -236,14 +231,14 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
siblings = self.fibaro_device.fibaro_controller.get_siblings(self.fibaro_device) siblings = self.fibaro_device.fibaro_controller.get_siblings(self.fibaro_device)
for device in siblings: for device in siblings:
if device != self.fibaro_device: if device != self.fibaro_device:
self.controller.register(device.id, self._update_callback) self.controller.register(device.fibaro_id, self._update_callback)
@property @property
def fan_mode(self) -> str | None: def fan_mode(self) -> str | None:
"""Return the fan setting.""" """Return the fan setting."""
if not self._fan_mode_device: if not self._fan_mode_device:
return None return None
mode = int(self._fan_mode_device.fibaro_device.properties.mode) mode = self._fan_mode_device.fibaro_device.mode
return FANMODES[mode] return FANMODES[mode]
def set_fan_mode(self, fan_mode: str) -> None: def set_fan_mode(self, fan_mode: str) -> None:
@ -258,14 +253,13 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
if not self._op_mode_device: if not self._op_mode_device:
return HA_OPMODES_HVAC[HVACMode.AUTO] return HA_OPMODES_HVAC[HVACMode.AUTO]
prop = self._op_mode_device.fibaro_device.properties device = self._op_mode_device.fibaro_device
if "operatingMode" in prop: if device.has_operating_mode:
return int(prop.operatingMode) return device.operating_mode
if "thermostatMode" in prop: if device.has_thermostat_mode:
return prop.thermostatMode return device.thermostat_mode
return device.mode
return int(prop.mode)
@property @property
def hvac_mode(self) -> HVACMode | str | None: def hvac_mode(self) -> HVACMode | str | None:
@ -288,9 +282,9 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
if "setOperatingMode" in self._op_mode_device.fibaro_device.actions: if "setOperatingMode" in self._op_mode_device.fibaro_device.actions:
self._op_mode_device.action("setOperatingMode", HA_OPMODES_HVAC[hvac_mode]) self._op_mode_device.action("setOperatingMode", HA_OPMODES_HVAC[hvac_mode])
elif "setThermostatMode" in self._op_mode_device.fibaro_device.actions: elif "setThermostatMode" in self._op_mode_device.fibaro_device.actions:
prop = self._op_mode_device.fibaro_device.properties device = self._op_mode_device.fibaro_device
if "supportedThermostatModes" in prop: if device.has_supported_thermostat_modes:
for mode in prop.supportedThermostatModes: for mode in device.supported_thermostat_modes:
if mode.lower() == hvac_mode: if mode.lower() == hvac_mode:
self._op_mode_device.action("setThermostatMode", mode) self._op_mode_device.action("setThermostatMode", mode)
break break
@ -303,10 +297,10 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
if not self._op_mode_device: if not self._op_mode_device:
return None return None
prop = self._op_mode_device.fibaro_device.properties device = self._op_mode_device.fibaro_device
if "thermostatOperatingState" in prop: if device.has_thermostat_operating_state:
with suppress(ValueError): with suppress(ValueError):
return HVACAction(prop.thermostatOperatingState.lower()) return HVACAction(device.thermostat_operating_state.lower())
return None return None
@ -319,15 +313,15 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
if not self._op_mode_device: if not self._op_mode_device:
return None return None
if "thermostatMode" in self._op_mode_device.fibaro_device.properties: if self._op_mode_device.fibaro_device.has_thermostat_mode:
mode = self._op_mode_device.fibaro_device.properties.thermostatMode mode = self._op_mode_device.fibaro_device.thermostat_mode
if self.preset_modes is not None and mode in self.preset_modes: if self.preset_modes is not None and mode in self.preset_modes:
return mode return mode
return None return None
if "operatingMode" in self._op_mode_device.fibaro_device.properties: if self._op_mode_device.fibaro_device.has_operating_mode:
mode = int(self._op_mode_device.fibaro_device.properties.operatingMode) mode = self._op_mode_device.fibaro_device.operating_mode
else: else:
mode = int(self._op_mode_device.fibaro_device.properties.mode) mode = self._op_mode_device.fibaro_device.mode
if mode not in OPMODES_PRESET: if mode not in OPMODES_PRESET:
return None return None
@ -352,9 +346,9 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
"""Return the current temperature.""" """Return the current temperature."""
if self._temp_sensor_device: if self._temp_sensor_device:
device = self._temp_sensor_device.fibaro_device device = self._temp_sensor_device.fibaro_device
if "heatingThermostatSetpoint" in device.properties: if device.has_heating_thermostat_setpoint:
return float(device.properties.heatingThermostatSetpoint) return device.heating_thermostat_setpoint
return float(device.properties.value) return device.value.float_value()
return None return None
@property @property
@ -362,9 +356,9 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
"""Return the temperature we try to reach.""" """Return the temperature we try to reach."""
if self._target_temp_device: if self._target_temp_device:
device = self._target_temp_device.fibaro_device device = self._target_temp_device.fibaro_device
if "heatingThermostatSetpointFuture" in device.properties: if device.has_heating_thermostat_setpoint_future:
return float(device.properties.heatingThermostatSetpointFuture) return device.heating_thermostat_setpoint_future
return float(device.properties.targetLevel) return device.target_level
return None return None
def set_temperature(self, **kwargs: Any) -> None: def set_temperature(self, **kwargs: Any) -> None:

View File

@ -3,6 +3,8 @@ from __future__ import annotations
from typing import Any from typing import Any
from pyfibaro.fibaro_device import DeviceModel
from homeassistant.components.cover import ( from homeassistant.components.cover import (
ATTR_POSITION, ATTR_POSITION,
ATTR_TILT_POSITION, ATTR_TILT_POSITION,
@ -39,7 +41,7 @@ async def async_setup_entry(
class FibaroCover(FibaroDevice, CoverEntity): class FibaroCover(FibaroDevice, CoverEntity):
"""Representation a Fibaro Cover.""" """Representation a Fibaro Cover."""
def __init__(self, fibaro_device): def __init__(self, fibaro_device: DeviceModel) -> None:
"""Initialize the Vera device.""" """Initialize the Vera device."""
super().__init__(fibaro_device) super().__init__(fibaro_device)
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
@ -67,9 +69,7 @@ class FibaroCover(FibaroDevice, CoverEntity):
"""Return if only open / close is supported.""" """Return if only open / close is supported."""
# Normally positionable devices report the position over value, # Normally positionable devices report the position over value,
# so if it is missing we have a device which supports open / close only # so if it is missing we have a device which supports open / close only
if "value" not in self.fibaro_device.properties: return not self.fibaro_device.value.has_value
return True
return False
@property @property
def current_cover_position(self) -> int | None: def current_cover_position(self) -> int | None:
@ -93,12 +93,10 @@ class FibaroCover(FibaroDevice, CoverEntity):
def is_closed(self) -> bool | None: def is_closed(self) -> bool | None:
"""Return if the cover is closed.""" """Return if the cover is closed."""
if self._is_open_close_only(): if self._is_open_close_only():
if ( state = self.fibaro_device.state
"state" not in self.fibaro_device.properties if not state.has_value or state.str_value.lower() == "unknown":
or self.fibaro_device.properties.state.lower() == "unknown"
):
return None return None
return self.fibaro_device.properties.state.lower() == "closed" return state.str_value.lower() == "closed"
if self.current_cover_position is None: if self.current_cover_position is None:
return None return None

View File

@ -6,6 +6,8 @@ from contextlib import suppress
from functools import partial from functools import partial
from typing import Any from typing import Any
from pyfibaro.fibaro_device import DeviceModel
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
ATTR_RGB_COLOR, ATTR_RGB_COLOR,
@ -68,7 +70,7 @@ async def async_setup_entry(
class FibaroLight(FibaroDevice, LightEntity): class FibaroLight(FibaroDevice, LightEntity):
"""Representation of a Fibaro Light, including dimmable.""" """Representation of a Fibaro Light, including dimmable."""
def __init__(self, fibaro_device): def __init__(self, fibaro_device: DeviceModel) -> None:
"""Initialize the light.""" """Initialize the light."""
self._update_lock = asyncio.Lock() self._update_lock = asyncio.Lock()
@ -77,7 +79,7 @@ class FibaroLight(FibaroDevice, LightEntity):
or "colorComponents" in fibaro_device.properties or "colorComponents" in fibaro_device.properties
or "RGB" in fibaro_device.type or "RGB" in fibaro_device.type
or "rgb" in fibaro_device.type or "rgb" in fibaro_device.type
or "color" in fibaro_device.baseType or "color" in fibaro_device.base_type
) and ( ) and (
"setColor" in fibaro_device.actions "setColor" in fibaro_device.actions
or "setColorComponents" in fibaro_device.actions or "setColorComponents" in fibaro_device.actions
@ -88,7 +90,7 @@ class FibaroLight(FibaroDevice, LightEntity):
or "rgbw" in fibaro_device.type or "rgbw" in fibaro_device.type
) )
supports_dimming = ( supports_dimming = (
"levelChange" in fibaro_device.interfaces fibaro_device.has_interface("levelChange")
and "setValue" in fibaro_device.actions and "setValue" in fibaro_device.actions
) )
@ -153,17 +155,16 @@ class FibaroLight(FibaroDevice, LightEntity):
JSON for HC2 uses always string, HC3 uses int for integers. JSON for HC2 uses always string, HC3 uses int for integers.
""" """
props = self.fibaro_device.properties
if self.current_binary_state: if self.current_binary_state:
return True return True
with suppress(ValueError, TypeError): with suppress(TypeError):
if "brightness" in props and int(props.brightness) != 0: if self.fibaro_device.brightness != 0:
return True return True
with suppress(ValueError, TypeError): with suppress(TypeError):
if "currentProgram" in props and int(props.currentProgram) != 0: if self.fibaro_device.current_program != 0:
return True return True
with suppress(ValueError, TypeError): with suppress(TypeError):
if "currentProgramID" in props and int(props.currentProgramID) != 0: if self.fibaro_device.current_program_id != 0:
return True return True
return False return False
@ -177,21 +178,19 @@ class FibaroLight(FibaroDevice, LightEntity):
"""Really update the state.""" """Really update the state."""
# Brightness handling # Brightness handling
if brightness_supported(self.supported_color_modes): if brightness_supported(self.supported_color_modes):
self._attr_brightness = scaleto255(int(self.fibaro_device.properties.value)) self._attr_brightness = scaleto255(self.fibaro_device.value.int_value())
# Color handling # Color handling
if ( if (
color_supported(self.supported_color_modes) color_supported(self.supported_color_modes)
and "color" in self.fibaro_device.properties and self.fibaro_device.color.has_color
and "," in self.fibaro_device.properties.color
): ):
# Fibaro communicates the color as an 'R, G, B, W' string # Fibaro communicates the color as an 'R, G, B, W' string
rgbw_s = self.fibaro_device.properties.color rgbw = self.fibaro_device.color.rgbw_color
if rgbw_s == "0,0,0,0" and "lastColorSet" in self.fibaro_device.properties: if rgbw == (0, 0, 0, 0) and self.fibaro_device.last_color_set.has_color:
rgbw_s = self.fibaro_device.properties.lastColorSet rgbw = self.fibaro_device.last_color_set.rgbw_color
rgbw_list = [int(i) for i in rgbw_s.split(",")][:4]
if self._attr_color_mode == ColorMode.RGB: if self._attr_color_mode == ColorMode.RGB:
self._attr_rgb_color = tuple(rgbw_list[:3]) self._attr_rgb_color = rgbw[:3]
else: else:
self._attr_rgbw_color = tuple(rgbw_list) self._attr_rgbw_color = rgbw

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Any from typing import Any
from fiblary3.client.v4.models import DeviceModel, SceneModel from pyfibaro.fibaro_device import DeviceModel
from homeassistant.components.lock import ENTITY_ID_FORMAT, LockEntity from homeassistant.components.lock import ENTITY_ID_FORMAT, LockEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -35,7 +35,7 @@ async def async_setup_entry(
class FibaroLock(FibaroDevice, LockEntity): class FibaroLock(FibaroDevice, LockEntity):
"""Representation of a Fibaro Lock.""" """Representation of a Fibaro Lock."""
def __init__(self, fibaro_device: DeviceModel | SceneModel) -> None: def __init__(self, fibaro_device: DeviceModel) -> None:
"""Initialize the Fibaro device.""" """Initialize the Fibaro device."""
super().__init__(fibaro_device) super().__init__(fibaro_device)
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)

View File

@ -6,6 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/fibaro", "documentation": "https://www.home-assistant.io/integrations/fibaro",
"integration_type": "hub", "integration_type": "hub",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["fiblary3"], "loggers": ["pyfibaro"],
"requirements": ["fiblary3==0.1.8"] "requirements": ["pyfibaro==0.6.6"]
} }

View File

@ -3,6 +3,8 @@ from __future__ import annotations
from typing import Any from typing import Any
from pyfibaro.fibaro_scene import SceneModel
from homeassistant.components.scene import Scene from homeassistant.components.scene import Scene
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
@ -34,7 +36,7 @@ async def async_setup_entry(
class FibaroScene(FibaroDevice, Scene): class FibaroScene(FibaroDevice, Scene):
"""Representation of a Fibaro scene entity.""" """Representation of a Fibaro scene entity."""
def __init__(self, fibaro_device: Any) -> None: def __init__(self, fibaro_device: SceneModel) -> None:
"""Initialize the Fibaro scene.""" """Initialize the Fibaro scene."""
super().__init__(fibaro_device) super().__init__(fibaro_device)

View File

@ -2,7 +2,8 @@
from __future__ import annotations from __future__ import annotations
from contextlib import suppress from contextlib import suppress
from typing import Any
from pyfibaro.fibaro_device import DeviceModel
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
ENTITY_ID_FORMAT, ENTITY_ID_FORMAT,
@ -125,7 +126,9 @@ class FibaroSensor(FibaroDevice, SensorEntity):
"""Representation of a Fibaro Sensor.""" """Representation of a Fibaro Sensor."""
def __init__( def __init__(
self, fibaro_device: Any, entity_description: SensorEntityDescription | None self,
fibaro_device: DeviceModel,
entity_description: SensorEntityDescription | None,
) -> None: ) -> None:
"""Initialize the sensor.""" """Initialize the sensor."""
super().__init__(fibaro_device) super().__init__(fibaro_device)
@ -138,20 +141,20 @@ class FibaroSensor(FibaroDevice, SensorEntity):
with suppress(KeyError, ValueError): with suppress(KeyError, ValueError):
if not self.native_unit_of_measurement: if not self.native_unit_of_measurement:
self._attr_native_unit_of_measurement = FIBARO_TO_HASS_UNIT.get( self._attr_native_unit_of_measurement = FIBARO_TO_HASS_UNIT.get(
fibaro_device.properties.unit, fibaro_device.properties.unit fibaro_device.unit, fibaro_device.unit
) )
def update(self) -> None: def update(self) -> None:
"""Update the state.""" """Update the state."""
with suppress(KeyError, ValueError): with suppress(TypeError):
self._attr_native_value = float(self.fibaro_device.properties.value) self._attr_native_value = self.fibaro_device.value.float_value()
class FibaroAdditionalSensor(FibaroDevice, SensorEntity): class FibaroAdditionalSensor(FibaroDevice, SensorEntity):
"""Representation of a Fibaro Additional Sensor.""" """Representation of a Fibaro Additional Sensor."""
def __init__( def __init__(
self, fibaro_device: Any, entity_description: SensorEntityDescription self, fibaro_device: DeviceModel, entity_description: SensorEntityDescription
) -> None: ) -> None:
"""Initialize the sensor.""" """Initialize the sensor."""
super().__init__(fibaro_device) super().__init__(fibaro_device)

View File

@ -3,6 +3,8 @@ from __future__ import annotations
from typing import Any from typing import Any
from pyfibaro.fibaro_device import DeviceModel
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
from homeassistant.const import Platform from homeassistant.const import Platform
@ -33,7 +35,7 @@ async def async_setup_entry(
class FibaroSwitch(FibaroDevice, SwitchEntity): class FibaroSwitch(FibaroDevice, SwitchEntity):
"""Representation of a Fibaro Switch.""" """Representation of a Fibaro Switch."""
def __init__(self, fibaro_device: Any) -> None: def __init__(self, fibaro_device: DeviceModel) -> None:
"""Initialize the Fibaro device.""" """Initialize the Fibaro device."""
super().__init__(fibaro_device) super().__init__(fibaro_device)
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)

View File

@ -703,9 +703,6 @@ fastdotcom==0.0.3
# homeassistant.components.feedreader # homeassistant.components.feedreader
feedparser==6.0.10 feedparser==6.0.10
# homeassistant.components.fibaro
fiblary3==0.1.8
# homeassistant.components.file # homeassistant.components.file
file-read-backwards==2.0.0 file-read-backwards==2.0.0
@ -1620,6 +1617,9 @@ pyevilgenius==2.0.0
# homeassistant.components.ezviz # homeassistant.components.ezviz
pyezviz==0.2.0.9 pyezviz==0.2.0.9
# homeassistant.components.fibaro
pyfibaro==0.6.6
# homeassistant.components.fido # homeassistant.components.fido
pyfido==2.1.1 pyfido==2.1.1

View File

@ -537,9 +537,6 @@ faadelays==0.0.7
# homeassistant.components.feedreader # homeassistant.components.feedreader
feedparser==6.0.10 feedparser==6.0.10
# homeassistant.components.fibaro
fiblary3==0.1.8
# homeassistant.components.file # homeassistant.components.file
file-read-backwards==2.0.0 file-read-backwards==2.0.0
@ -1160,6 +1157,9 @@ pyevilgenius==2.0.0
# homeassistant.components.ezviz # homeassistant.components.ezviz
pyezviz==0.2.0.9 pyezviz==0.2.0.9
# homeassistant.components.fibaro
pyfibaro==0.6.6
# homeassistant.components.fido # homeassistant.components.fido
pyfido==2.1.1 pyfido==2.1.1

View File

@ -1,8 +1,8 @@
"""Test the Fibaro config flow.""" """Test the Fibaro config flow."""
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from fiblary3.common.exceptions import HTTPException
import pytest import pytest
from requests.exceptions import HTTPError
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.fibaro import DOMAIN from homeassistant.components.fibaro import DOMAIN
@ -25,37 +25,31 @@ TEST_VERSION = "4.360"
def fibaro_client_fixture(): def fibaro_client_fixture():
"""Mock common methods and attributes of fibaro client.""" """Mock common methods and attributes of fibaro client."""
info_mock = Mock() info_mock = Mock()
info_mock.get.return_value = Mock( info_mock.return_value.serial_number = TEST_SERIALNUMBER
serialNumber=TEST_SERIALNUMBER, hcName=TEST_NAME, softVersion=TEST_VERSION info_mock.return_value.hc_name = TEST_NAME
) info_mock.return_value.current_version = TEST_VERSION
array_mock = Mock()
array_mock.list.return_value = []
client_mock = Mock() client_mock = Mock()
client_mock.base_url.return_value = TEST_URL client_mock.base_url.return_value = TEST_URL
with patch( with patch(
"homeassistant.components.fibaro.FibaroClientV4.__init__", "homeassistant.components.fibaro.FibaroClient.__init__",
return_value=None, return_value=None,
), patch( ), patch(
"homeassistant.components.fibaro.FibaroClientV4.info", "homeassistant.components.fibaro.FibaroClient.read_info",
info_mock, info_mock,
create=True, create=True,
), patch( ), patch(
"homeassistant.components.fibaro.FibaroClientV4.rooms", "homeassistant.components.fibaro.FibaroClient.read_rooms",
array_mock, return_value=[],
create=True,
), patch( ), patch(
"homeassistant.components.fibaro.FibaroClientV4.devices", "homeassistant.components.fibaro.FibaroClient.read_devices",
array_mock, return_value=[],
create=True,
), patch( ), patch(
"homeassistant.components.fibaro.FibaroClientV4.scenes", "homeassistant.components.fibaro.FibaroClient.read_scenes",
array_mock, return_value=[],
create=True,
), patch( ), patch(
"homeassistant.components.fibaro.FibaroClientV4.client", "homeassistant.components.fibaro.FibaroClient._rest_client",
client_mock, client_mock,
create=True, create=True,
): ):
@ -72,10 +66,9 @@ async def test_config_flow_user_initiated_success(hass: HomeAssistant) -> None:
assert result["step_id"] == "user" assert result["step_id"] == "user"
assert result["errors"] == {} assert result["errors"] == {}
login_mock = Mock()
login_mock.get.return_value = Mock(status=True)
with patch( with patch(
"homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True "homeassistant.components.fibaro.FibaroClient.connect",
return_value=True,
), patch( ), patch(
"homeassistant.components.fibaro.async_setup_entry", "homeassistant.components.fibaro.async_setup_entry",
return_value=True, return_value=True,
@ -109,10 +102,9 @@ async def test_config_flow_user_initiated_connect_failure(hass: HomeAssistant) -
assert result["step_id"] == "user" assert result["step_id"] == "user"
assert result["errors"] == {} assert result["errors"] == {}
login_mock = Mock()
login_mock.get.return_value = Mock(status=False)
with patch( with patch(
"homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True "homeassistant.components.fibaro.FibaroClient.connect",
return_value=False,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
@ -139,9 +131,9 @@ async def test_config_flow_user_initiated_auth_failure(hass: HomeAssistant) -> N
assert result["errors"] == {} assert result["errors"] == {}
login_mock = Mock() login_mock = Mock()
login_mock.get.side_effect = HTTPException(details="Forbidden") login_mock.side_effect = HTTPError(response=Mock(status_code=403))
with patch( with patch(
"homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True "homeassistant.components.fibaro.FibaroClient.connect", login_mock, create=True
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
@ -170,9 +162,9 @@ async def test_config_flow_user_initiated_unknown_failure_1(
assert result["errors"] == {} assert result["errors"] == {}
login_mock = Mock() login_mock = Mock()
login_mock.get.side_effect = HTTPException(details="Any") login_mock.side_effect = HTTPError(response=Mock(status_code=500))
with patch( with patch(
"homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True "homeassistant.components.fibaro.FibaroClient.connect", login_mock, create=True
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
@ -200,6 +192,11 @@ async def test_config_flow_user_initiated_unknown_failure_2(
assert result["step_id"] == "user" assert result["step_id"] == "user"
assert result["errors"] == {} assert result["errors"] == {}
login_mock = Mock()
login_mock.side_effect = Exception()
with patch(
"homeassistant.components.fibaro.FibaroClient.connect", login_mock, create=True
):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{ {
@ -216,10 +213,8 @@ async def test_config_flow_user_initiated_unknown_failure_2(
async def test_config_flow_import(hass: HomeAssistant) -> None: async def test_config_flow_import(hass: HomeAssistant) -> None:
"""Test for importing config from configuration.yaml.""" """Test for importing config from configuration.yaml."""
login_mock = Mock()
login_mock.get.return_value = Mock(status=True)
with patch( with patch(
"homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True "homeassistant.components.fibaro.FibaroClient.connect", return_value=True
), patch( ), patch(
"homeassistant.components.fibaro.async_setup_entry", "homeassistant.components.fibaro.async_setup_entry",
return_value=True, return_value=True,
@ -271,10 +266,8 @@ async def test_reauth_success(hass: HomeAssistant) -> None:
assert result["step_id"] == "reauth_confirm" assert result["step_id"] == "reauth_confirm"
assert result["errors"] == {} assert result["errors"] == {}
login_mock = Mock()
login_mock.get.return_value = Mock(status=True)
with patch( with patch(
"homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True "homeassistant.components.fibaro.FibaroClient.connect", return_value=True
), patch( ), patch(
"homeassistant.components.fibaro.async_setup_entry", "homeassistant.components.fibaro.async_setup_entry",
return_value=True, return_value=True,
@ -315,9 +308,9 @@ async def test_reauth_connect_failure(hass: HomeAssistant) -> None:
assert result["errors"] == {} assert result["errors"] == {}
login_mock = Mock() login_mock = Mock()
login_mock.get.return_value = Mock(status=False) login_mock.side_effect = Exception()
with patch( with patch(
"homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True "homeassistant.components.fibaro.FibaroClient.connect", login_mock, create=True
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
@ -356,9 +349,9 @@ async def test_reauth_auth_failure(hass: HomeAssistant) -> None:
assert result["errors"] == {} assert result["errors"] == {}
login_mock = Mock() login_mock = Mock()
login_mock.get.side_effect = HTTPException(details="Forbidden") login_mock.side_effect = HTTPError(response=Mock(status_code=403))
with patch( with patch(
"homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True "homeassistant.components.fibaro.FibaroClient.connect", login_mock, create=True
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],