mirror of
https://github.com/home-assistant/core.git
synced 2025-08-06 12:08:19 +00:00
Code refactoring
This commit is contained in:
parent
baeb70af8a
commit
7b2641d136
@ -1,7 +1,12 @@
|
|||||||
"""Constants for the Swing2Sleep Smarla integration."""
|
"""Constants for the Swing2Sleep Smarla integration."""
|
||||||
|
|
||||||
|
from homeassistant.const import Platform
|
||||||
|
|
||||||
DOMAIN = "smarla"
|
DOMAIN = "smarla"
|
||||||
|
|
||||||
HOST = "https://devices.swing2sleep.de"
|
HOST = "https://devices.swing2sleep.de"
|
||||||
|
|
||||||
PLATFORMS = ["switch"]
|
PLATFORMS = [Platform.SWITCH]
|
||||||
|
|
||||||
|
DEVICE_MODEL_NAME = "Smarla"
|
||||||
|
MANUFACTURER_NAME = "Swing2Sleep"
|
||||||
|
@ -5,12 +5,14 @@ from pysmarlaapi import Federwiege
|
|||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DEVICE_MODEL_NAME, DOMAIN, MANUFACTURER_NAME
|
||||||
|
|
||||||
|
|
||||||
class SmarlaBaseEntity(Entity):
|
class SmarlaBaseEntity(Entity):
|
||||||
"""Common Base Entity class for defining Smarla device."""
|
"""Common Base Entity class for defining Smarla device."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
federwiege: Federwiege,
|
federwiege: Federwiege,
|
||||||
@ -18,12 +20,10 @@ class SmarlaBaseEntity(Entity):
|
|||||||
"""Initialise the entity."""
|
"""Initialise the entity."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self._attr_has_entity_name = True
|
|
||||||
|
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, federwiege.serial_number)},
|
identifiers={(DOMAIN, federwiege.serial_number)},
|
||||||
name="Smarla",
|
name=DEVICE_MODEL_NAME,
|
||||||
model="Smarla",
|
model=DEVICE_MODEL_NAME,
|
||||||
manufacturer="Swing2Sleep",
|
manufacturer=MANUFACTURER_NAME,
|
||||||
serial_number=federwiege.serial_number,
|
serial_number=federwiege.serial_number,
|
||||||
)
|
)
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
"name": "Swing2Sleep Smarla",
|
"name": "Swing2Sleep Smarla",
|
||||||
"codeowners": ["@explicatis", "@rlint-explicatis"],
|
"codeowners": ["@explicatis", "@rlint-explicatis"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"dependencies": [],
|
|
||||||
"documentation": "https://www.home-assistant.io/integrations/smarla",
|
"documentation": "https://www.home-assistant.io/integrations/smarla",
|
||||||
"integration_type": "device",
|
"integration_type": "device",
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"access_token": "[%key:common::config_flow::data::access_token%]"
|
"access_token": "[%key:common::config_flow::data::access_token%]"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"access_token": "The access token generated from the App."
|
"access_token": "The access token generated by the Swing2Sleep app."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,7 @@ from dataclasses import dataclass
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pysmarlaapi import Federwiege
|
from pysmarlaapi import Federwiege
|
||||||
|
from pysmarlaapi.federwiege.classes import Property
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -15,13 +16,13 @@ from .entity import SmarlaBaseEntity
|
|||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
class SmarlaSwitchEntityDescription(SwitchEntityDescription):
|
class SmarlaSwitchEntityDescription(SwitchEntityDescription):
|
||||||
"""Class describing Swing2Sleep Smarla switch entities."""
|
"""Class describing Swing2Sleep Smarla switch entity."""
|
||||||
|
|
||||||
service: str
|
service: str
|
||||||
property: str
|
property: str
|
||||||
|
|
||||||
|
|
||||||
NUMBER_TYPES: list[SmarlaSwitchEntityDescription] = [
|
SWITCHES: list[SmarlaSwitchEntityDescription] = [
|
||||||
SmarlaSwitchEntityDescription(
|
SmarlaSwitchEntityDescription(
|
||||||
key="cradle",
|
key="cradle",
|
||||||
name=None,
|
name=None,
|
||||||
@ -43,20 +44,17 @@ async def async_setup_entry(
|
|||||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Smarla switches from config entry."""
|
"""Set up the Smarla switches from config entry."""
|
||||||
federwiege = config_entry.runtime_data
|
federwiege: Federwiege = config_entry.runtime_data
|
||||||
|
async_add_entities(SmarlaSwitch(federwiege, desc) for desc in SWITCHES)
|
||||||
entities: list[SwitchEntity] = []
|
|
||||||
|
|
||||||
for desc in NUMBER_TYPES:
|
|
||||||
entity = SmarlaSwitch(federwiege, desc)
|
|
||||||
entities.append(entity)
|
|
||||||
|
|
||||||
async_add_entities(entities)
|
|
||||||
|
|
||||||
|
|
||||||
class SmarlaSwitch(SmarlaBaseEntity, SwitchEntity):
|
class SmarlaSwitch(SmarlaBaseEntity, SwitchEntity):
|
||||||
"""Representation of Smarla switch."""
|
"""Representation of Smarla switch."""
|
||||||
|
|
||||||
|
entity_description: SmarlaSwitchEntityDescription
|
||||||
|
_property: Property
|
||||||
|
_attr_should_poll = False
|
||||||
|
|
||||||
async def on_change(self, value: Any):
|
async def on_change(self, value: Any):
|
||||||
"""Notify ha when state changes."""
|
"""Notify ha when state changes."""
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
@ -68,30 +66,29 @@ class SmarlaSwitch(SmarlaBaseEntity, SwitchEntity):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a Smarla switch."""
|
"""Initialize a Smarla switch."""
|
||||||
super().__init__(federwiege)
|
super().__init__(federwiege)
|
||||||
self.property = federwiege.get_service(description.service).get_property(
|
self._property = federwiege.get_service(description.service).get_property(
|
||||||
description.property
|
description.property
|
||||||
)
|
)
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_should_poll = False
|
|
||||||
self._attr_unique_id = f"{federwiege.serial_number}-{description.key}"
|
self._attr_unique_id = f"{federwiege.serial_number}-{description.key}"
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Run when this Entity has been added to HA."""
|
"""Run when this Entity has been added to HA."""
|
||||||
await self.property.add_listener(self.on_change)
|
await self._property.add_listener(self.on_change)
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
"""Entity being removed from hass."""
|
"""Entity being removed from hass."""
|
||||||
await self.property.remove_listener(self.on_change)
|
await self._property.remove_listener(self.on_change)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return the entity value to represent the entity state."""
|
"""Return the entity value to represent the entity state."""
|
||||||
return self.property.get()
|
return self._property.get()
|
||||||
|
|
||||||
def turn_on(self, **kwargs: Any) -> None:
|
def turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch on."""
|
"""Turn the switch on."""
|
||||||
self.property.set(True)
|
self._property.set(True)
|
||||||
|
|
||||||
def turn_off(self, **kwargs: Any) -> None:
|
def turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch off."""
|
"""Turn the switch off."""
|
||||||
self.property.set(False)
|
self._property.set(False)
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
"""Test config flow for Swing2Sleep Smarla integration."""
|
"""Test config flow for Swing2Sleep Smarla integration."""
|
||||||
|
|
||||||
|
import json
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.smarla.config_flow import Connection
|
from homeassistant.components.smarla.config_flow import Connection
|
||||||
from homeassistant.components.smarla.const import DOMAIN
|
from homeassistant.components.smarla.const import DOMAIN
|
||||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
@ -10,9 +12,12 @@ from homeassistant.data_entry_flow import FlowResultType
|
|||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
MOCK_ACCESS_TOKEN = "eyJyZWZyZXNoVG9rZW4iOiJ0ZXN0IiwidG9rZW4iOiJ0ZXN0IiwiZGF0ZUNyZWF0ZWQiOiIyMDI1LTAxLTAxVDIzOjU5OjU5Ljk5OTk5OVoiLCJhcHBJZGVudGlmaWVyIjoiSEEtaG9tZWFzc2lzdGFudHRlc3QiLCJzZXJpYWxOdW1iZXIiOiJBQkNELUFCQ0QiLCJhcHBWZXJzaW9uIjoidW5rbm93biIsImFwcEN1bHR1cmUiOiJkZSJ9"
|
MOCK_ACCESS_TOKEN = "eyJyZWZyZXNoVG9rZW4iOiJ0ZXN0IiwiYXBwSWRlbnRpZmllciI6IkhBLXRlc3QiLCJzZXJpYWxOdW1iZXIiOiJBQkNEIn0="
|
||||||
MOCK_ACCESS_TOKEN_JSON = '{"refreshToken": "test", "token": "test", "dateCreated": "2025-01-01T23:59:59.999999Z", "appIdentifier": "HA-homeassistanttest", "serialNumber": "ABCD-ABCD", "appVersion": "unknown", "appCulture": "de"}'
|
MOCK_ACCESS_TOKEN_JSON = {
|
||||||
MOCK_SERIAL = "ABCD-ABCD"
|
"refreshToken": "test",
|
||||||
|
"appIdentifier": "HA-test",
|
||||||
|
"serialNumber": "ABCD",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_show_form(hass: HomeAssistant) -> None:
|
async def test_show_form(hass: HomeAssistant) -> None:
|
||||||
@ -30,13 +35,13 @@ async def test_create_entry(hass: HomeAssistant) -> None:
|
|||||||
with patch.object(Connection, "get_token", new=AsyncMock(return_value=True)):
|
with patch.object(Connection, "get_token", new=AsyncMock(return_value=True)):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": "user"},
|
context={"source": config_entries.SOURCE_USER},
|
||||||
data={CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN},
|
data={CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||||
assert result["title"] == MOCK_SERIAL
|
assert result["title"] == MOCK_ACCESS_TOKEN_JSON["serialNumber"]
|
||||||
assert result["data"] == {CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN_JSON}
|
assert result["data"] == {CONF_ACCESS_TOKEN: json.dumps(MOCK_ACCESS_TOKEN_JSON)}
|
||||||
|
|
||||||
|
|
||||||
async def test_invalid_auth(hass: HomeAssistant) -> None:
|
async def test_invalid_auth(hass: HomeAssistant) -> None:
|
||||||
@ -44,7 +49,7 @@ async def test_invalid_auth(hass: HomeAssistant) -> None:
|
|||||||
with patch.object(Connection, "get_token", new=AsyncMock(return_value=None)):
|
with patch.object(Connection, "get_token", new=AsyncMock(return_value=None)):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": "user"},
|
context={"source": config_entries.SOURCE_USER},
|
||||||
data={CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN},
|
data={CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -56,7 +61,7 @@ async def test_invalid_token(hass: HomeAssistant) -> None:
|
|||||||
"""Test we handle invalid/malformed tokens."""
|
"""Test we handle invalid/malformed tokens."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": "user"},
|
context={"source": config_entries.SOURCE_USER},
|
||||||
data={CONF_ACCESS_TOKEN: "invalid_token"},
|
data={CONF_ACCESS_TOKEN: "invalid_token"},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -68,8 +73,8 @@ async def test_device_exists_abort(hass: HomeAssistant) -> None:
|
|||||||
"""Test we abort config flow if Smarla device already configured."""
|
"""Test we abort config flow if Smarla device already configured."""
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id=MOCK_SERIAL,
|
unique_id=MOCK_ACCESS_TOKEN_JSON["serialNumber"],
|
||||||
source="user",
|
source=config_entries.SOURCE_USER,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
@ -77,7 +82,7 @@ async def test_device_exists_abort(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": "user"},
|
context={"source": config_entries.SOURCE_USER},
|
||||||
data={CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN},
|
data={CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user