mirror of
https://github.com/home-assistant/core.git
synced 2026-05-05 04:14:32 +02:00
Fixed a couple issues as well as added a test file
This commit is contained in:
@@ -466,6 +466,7 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/flume/ @ChrisMandich @bdraco @jeeftor
|
||||
/tests/components/flume/ @ChrisMandich @bdraco @jeeftor
|
||||
/homeassistant/components/fluss/ @fluss
|
||||
/tests/components/fluss/ @fluss
|
||||
/homeassistant/components/flux_led/ @icemanch
|
||||
/tests/components/flux_led/ @icemanch
|
||||
/homeassistant/components/forecast_solar/ @klaasnicolaas @frenck
|
||||
|
||||
@@ -8,23 +8,33 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .api import FlussApiClient
|
||||
from .const import DOMAIN
|
||||
from .api import (
|
||||
FlussApiClient,
|
||||
FlussApiClientAuthenticationError, # noqa: F401
|
||||
FlussApiClientCommunicationError, # noqa: F401
|
||||
FlussApiClientError, # noqa: F401
|
||||
)
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
# For your initial PR, limit it to 1 platform.
|
||||
PLATFORMS: list[Platform] = [Platform.BUTTON]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Fluss+ from a config entry."""
|
||||
try:
|
||||
api = FlussApiClient(entry.data[CONF_API_KEY], hass)
|
||||
except FlussApiClientAuthenticationError as e:
|
||||
LOGGER.error("Authentication error initializing FlussApiClient: %s", e)
|
||||
return False
|
||||
except FlussApiClientCommunicationError as e:
|
||||
LOGGER.error("Communication error initializing FlussApiClient: %s", e)
|
||||
return False
|
||||
except FlussApiClientError as e:
|
||||
LOGGER.error("General error initializing FlussApiClient: %s", e)
|
||||
return False
|
||||
|
||||
api = FlussApiClient(entry.data[CONF_API_KEY], hass)
|
||||
response = await api.async_get_devices()
|
||||
LOGGER.warning("D %s", response)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = api
|
||||
entry.runtime_data = {"api": api}
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ class FlussApiClientError(Exception):
|
||||
"""Exception to indicate a general API error."""
|
||||
|
||||
|
||||
class FlussDeviceError(Exception):
|
||||
"""Exception to indicate that an error occurred when retriveing devices."""
|
||||
|
||||
|
||||
class FlussApiClientCommunicationError(FlussApiClientError):
|
||||
"""Exception to indicate a communication error."""
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .api import FlussApiClient
|
||||
from .const import DOMAIN
|
||||
from .api import FlussApiClient, FlussDeviceError
|
||||
from .device import FlussButton
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
@@ -18,16 +17,26 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up the Fluss Devices."""
|
||||
|
||||
api: FlussApiClient = hass.data[DOMAIN][entry.entry_id]
|
||||
entry_data = entry.runtime_data
|
||||
api: FlussApiClient = entry_data["api"]
|
||||
|
||||
devices = await api.async_get_devices()
|
||||
if isinstance(devices, dict) and "devices" in devices:
|
||||
devices = devices["devices"]
|
||||
|
||||
if not isinstance(devices, list):
|
||||
try:
|
||||
devices_data = await api.async_get_devices()
|
||||
devices = devices_data["devices"]
|
||||
except FlussDeviceError as e:
|
||||
LOGGER.error("Error fetching devices: %s", e)
|
||||
return
|
||||
|
||||
device_info_list = []
|
||||
for device in devices:
|
||||
device_info = {
|
||||
"deviceId": device.get("deviceId"),
|
||||
"deviceName": device.get("deviceName"),
|
||||
"userType": device.get("userPermissions", {}).get("userType"),
|
||||
}
|
||||
device_info_list.append(device_info)
|
||||
|
||||
buttons = [
|
||||
FlussButton(api, device) for device in devices if isinstance(device, dict)
|
||||
]
|
||||
async_add_entities(buttons, update_before_add=True)
|
||||
async_add_entities(buttons)
|
||||
|
||||
@@ -20,8 +20,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||
STEP_USER_DATA_SCHEMA = vol.Schema({vol.Required(CONF_API_KEY): cv.string})
|
||||
|
||||
|
||||
class PlaceholderHub:
|
||||
"""Placeholder class to store APIs."""
|
||||
class ApiKeyStorageHub:
|
||||
"""ApiKeyStorageHub class to store APIs."""
|
||||
|
||||
def __init__(self, apikey: str) -> None:
|
||||
"""Initialize."""
|
||||
@@ -38,10 +38,10 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str,
|
||||
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
|
||||
"""
|
||||
|
||||
hub = PlaceholderHub(data[CONF_API_KEY])
|
||||
hub = ApiKeyStorageHub(data[CONF_API_KEY])
|
||||
|
||||
if not await hub.authenticate():
|
||||
raise InvalidAuth
|
||||
raise InvalidAuth("Invalid authentication provided.")
|
||||
|
||||
return {"title": "Fluss+"}
|
||||
|
||||
@@ -56,9 +56,13 @@ class FlussConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle the initial step."""
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
if user_input is not None:
|
||||
try:
|
||||
info = await validate_input(self.hass, user_input)
|
||||
return self.async_create_entry(
|
||||
title=info.get("title", "Fluss Device"), data=user_input
|
||||
)
|
||||
except CannotConnect:
|
||||
errors["base"] = "cannot_connect"
|
||||
except InvalidAuth:
|
||||
@@ -66,11 +70,24 @@ class FlussConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
except Exception:
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
else:
|
||||
return self.async_create_entry(title=info["title"], data=user_input)
|
||||
|
||||
if errors:
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): str,
|
||||
}
|
||||
),
|
||||
errors=errors,
|
||||
)
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): str,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Base entities for the Motionblinds Bluetooth integration."""
|
||||
"""Base entities for the Fluss+ integration."""
|
||||
|
||||
import logging
|
||||
|
||||
@@ -15,26 +15,33 @@ class FlussEntity(Entity):
|
||||
"""Base class for Fluss entities."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_should_poll = False
|
||||
|
||||
device: FlussButton
|
||||
entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
def __init__( # noqa: D107
|
||||
self,
|
||||
device: FlussButton,
|
||||
entry: ConfigEntry,
|
||||
entity_description: EntityDescription,
|
||||
unique_id_suffix: str | None = None,
|
||||
) -> None:
|
||||
self.device = device
|
||||
self.entry = entry
|
||||
self.entity_description = entity_description
|
||||
|
||||
"""Initialize the entity."""
|
||||
if unique_id_suffix is None:
|
||||
self._attr_unique_id = entry.data[CONF_ADDRESS]
|
||||
else:
|
||||
self._attr_unique_id = f"{entry.data[CONF_ADDRESS]}_{unique_id_suffix}"
|
||||
self.device = device
|
||||
self.entry = entry
|
||||
self.entity_description = entity_description
|
||||
if (
|
||||
CONF_ADDRESS not in entry.data
|
||||
or entry.data[CONF_ADDRESS] != self._attr_unique_id
|
||||
):
|
||||
data = dict(entry.data)
|
||||
data[CONF_ADDRESS] = self._attr_unique_id
|
||||
self.hass.config_entries.async_update_entry(entry, data=data)
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update state, called by HA if there is a poll interval and by the service homeassistant.update_entity."""
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
"name": "Fluss +",
|
||||
"codeowners": ["@fluss"],
|
||||
"config_flow": true,
|
||||
"dependencies": [],
|
||||
"documentation": "https://www.home-assistant.io/integrations/fluss",
|
||||
"homekit": {},
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": [],
|
||||
"ssdp": [],
|
||||
"zeroconf": []
|
||||
"requirements": []
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Tests for the fluss integration."""
|
||||
@@ -0,0 +1,50 @@
|
||||
from unittest.mock import MagicMock, patch # noqa: D100
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.fluss import config_flow
|
||||
from homeassistant.components.fluss.config_flow import CannotConnect, InvalidAuth
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
# Mock data for testing
|
||||
MOCK_CONFIG = {config_flow.CONF_API_KEY: "mock_api_key"}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_hass():
|
||||
"""Fixture for creating a mock Home Assistant instance."""
|
||||
return MagicMock()
|
||||
|
||||
|
||||
async def setup_fluss_config_flow(hass: HomeAssistant, config: dict) -> dict: # noqa: D417
|
||||
"""Set up the configuration flow for Fluss.
|
||||
|
||||
Args:
|
||||
- hass (HomeAssistant): The Home Assistant instance.
|
||||
- config (dict): The configuration data for setting up Fluss.
|
||||
|
||||
Returns:
|
||||
- dict: Result of the configuration flow.
|
||||
|
||||
"""
|
||||
with patch.object(
|
||||
config_flow, "validate_input", return_value={"title": "Mock Title"}
|
||||
):
|
||||
return await config_flow.FlussConfigFlow().async_step_user(config)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_form_invalid_auth(mock_hass) -> None:
|
||||
"""Test handling of invalid authentication."""
|
||||
with patch.object(config_flow, "validate_input", side_effect=InvalidAuth):
|
||||
result = await setup_fluss_config_flow(mock_hass, MOCK_CONFIG)
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_form_cannot_connect(mock_hass) -> None:
|
||||
"""Test handling of connection errors."""
|
||||
with patch.object(config_flow, "validate_input", side_effect=CannotConnect):
|
||||
result = await setup_fluss_config_flow(mock_hass, MOCK_CONFIG)
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
Reference in New Issue
Block a user