mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
parent
5f67623214
commit
276e2e8f59
@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, cast
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from aiohttp import ClientError
|
||||
from pysmartthings import (
|
||||
@ -22,6 +22,12 @@ from pysmartthings import (
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_CONNECTIONS,
|
||||
ATTR_HW_VERSION,
|
||||
ATTR_MANUFACTURER,
|
||||
ATTR_MODEL,
|
||||
ATTR_SW_VERSION,
|
||||
ATTR_VIA_DEVICE,
|
||||
CONF_ACCESS_TOKEN,
|
||||
CONF_TOKEN,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
@ -172,25 +178,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: SmartThingsConfigEntry)
|
||||
raise ConfigEntryAuthFailed from err
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
for dev in device_status.values():
|
||||
for component in dev.device.components:
|
||||
if component.id == MAIN and Capability.BRIDGE in component.capabilities:
|
||||
assert dev.device.hub
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, dev.device.device_id)},
|
||||
connections=(
|
||||
{(dr.CONNECTION_NETWORK_MAC, dev.device.hub.mac_address)}
|
||||
if dev.device.hub.mac_address
|
||||
else set()
|
||||
),
|
||||
name=dev.device.label,
|
||||
sw_version=dev.device.hub.firmware_version,
|
||||
model=dev.device.hub.hardware_type,
|
||||
suggested_area=(
|
||||
rooms.get(dev.device.room_id) if dev.device.room_id else None
|
||||
),
|
||||
)
|
||||
create_devices(device_registry, device_status, entry, rooms)
|
||||
|
||||
scenes = {
|
||||
scene.scene_id: scene
|
||||
for scene in await client.get_scenes(location_id=entry.data[CONF_LOCATION_ID])
|
||||
@ -278,6 +267,58 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def create_devices(
|
||||
device_registry: dr.DeviceRegistry,
|
||||
devices: dict[str, FullDevice],
|
||||
entry: SmartThingsConfigEntry,
|
||||
rooms: dict[str, str],
|
||||
) -> None:
|
||||
"""Create devices in the device registry."""
|
||||
for device in devices.values():
|
||||
kwargs: dict[str, Any] = {}
|
||||
if device.device.hub is not None:
|
||||
kwargs = {
|
||||
ATTR_SW_VERSION: device.device.hub.firmware_version,
|
||||
ATTR_MODEL: device.device.hub.hardware_type,
|
||||
}
|
||||
if device.device.hub.mac_address:
|
||||
kwargs[ATTR_CONNECTIONS] = {
|
||||
(dr.CONNECTION_NETWORK_MAC, device.device.hub.mac_address)
|
||||
}
|
||||
if device.device.parent_device_id:
|
||||
kwargs[ATTR_VIA_DEVICE] = (DOMAIN, device.device.parent_device_id)
|
||||
if (ocf := device.device.ocf) is not None:
|
||||
kwargs.update(
|
||||
{
|
||||
ATTR_MANUFACTURER: ocf.manufacturer_name,
|
||||
ATTR_MODEL: (
|
||||
(ocf.model_number.split("|")[0]) if ocf.model_number else None
|
||||
),
|
||||
ATTR_HW_VERSION: ocf.hardware_version,
|
||||
ATTR_SW_VERSION: ocf.firmware_version,
|
||||
}
|
||||
)
|
||||
if (viper := device.device.viper) is not None:
|
||||
kwargs.update(
|
||||
{
|
||||
ATTR_MANUFACTURER: viper.manufacturer_name,
|
||||
ATTR_MODEL: viper.model_name,
|
||||
ATTR_HW_VERSION: viper.hardware_version,
|
||||
ATTR_SW_VERSION: viper.software_version,
|
||||
}
|
||||
)
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, device.device.device_id)},
|
||||
configuration_url="https://account.smartthings.com",
|
||||
name=device.device.label,
|
||||
suggested_area=(
|
||||
rooms.get(device.device.room_id) if device.device.room_id else None
|
||||
),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
KEEP_CAPABILITY_QUIRK: dict[
|
||||
Capability | str, Callable[[dict[Attribute | str, Status]], bool]
|
||||
] = {
|
||||
|
@ -134,7 +134,6 @@ async def async_setup_entry(
|
||||
entry_data.client,
|
||||
device,
|
||||
description,
|
||||
entry_data.rooms,
|
||||
capability,
|
||||
attribute,
|
||||
)
|
||||
@ -155,12 +154,11 @@ class SmartThingsBinarySensor(SmartThingsEntity, BinarySensorEntity):
|
||||
client: SmartThings,
|
||||
device: FullDevice,
|
||||
entity_description: SmartThingsBinarySensorEntityDescription,
|
||||
rooms: dict[str, str],
|
||||
capability: Capability,
|
||||
attribute: Attribute,
|
||||
) -> None:
|
||||
"""Init the class."""
|
||||
super().__init__(client, device, rooms, {capability})
|
||||
super().__init__(client, device, {capability})
|
||||
self._attribute = attribute
|
||||
self.capability = capability
|
||||
self.entity_description = entity_description
|
||||
|
@ -118,12 +118,12 @@ async def async_setup_entry(
|
||||
"""Add climate entities for a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
entities: list[ClimateEntity] = [
|
||||
SmartThingsAirConditioner(entry_data.client, entry_data.rooms, device)
|
||||
SmartThingsAirConditioner(entry_data.client, device)
|
||||
for device in entry_data.devices.values()
|
||||
if all(capability in device.status[MAIN] for capability in AC_CAPABILITIES)
|
||||
]
|
||||
entities.extend(
|
||||
SmartThingsThermostat(entry_data.client, entry_data.rooms, device)
|
||||
SmartThingsThermostat(entry_data.client, device)
|
||||
for device in entry_data.devices.values()
|
||||
if all(
|
||||
capability in device.status[MAIN] for capability in THERMOSTAT_CAPABILITIES
|
||||
@ -137,14 +137,11 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateEntity):
|
||||
|
||||
_attr_name = None
|
||||
|
||||
def __init__(
|
||||
self, client: SmartThings, rooms: dict[str, str], device: FullDevice
|
||||
) -> None:
|
||||
def __init__(self, client: SmartThings, device: FullDevice) -> None:
|
||||
"""Init the class."""
|
||||
super().__init__(
|
||||
client,
|
||||
device,
|
||||
rooms,
|
||||
{
|
||||
Capability.THERMOSTAT_FAN_MODE,
|
||||
Capability.THERMOSTAT_MODE,
|
||||
@ -338,14 +335,11 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
|
||||
_attr_name = None
|
||||
_attr_preset_mode = None
|
||||
|
||||
def __init__(
|
||||
self, client: SmartThings, rooms: dict[str, str], device: FullDevice
|
||||
) -> None:
|
||||
def __init__(self, client: SmartThings, device: FullDevice) -> None:
|
||||
"""Init the class."""
|
||||
super().__init__(
|
||||
client,
|
||||
device,
|
||||
rooms,
|
||||
{
|
||||
Capability.AIR_CONDITIONER_MODE,
|
||||
Capability.SWITCH,
|
||||
|
@ -41,9 +41,7 @@ async def async_setup_entry(
|
||||
"""Add covers for a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SmartThingsCover(
|
||||
entry_data.client, device, entry_data.rooms, Capability(capability)
|
||||
)
|
||||
SmartThingsCover(entry_data.client, device, Capability(capability))
|
||||
for device in entry_data.devices.values()
|
||||
for capability in device.status[MAIN]
|
||||
if capability in CAPABILITIES
|
||||
@ -60,14 +58,12 @@ class SmartThingsCover(SmartThingsEntity, CoverEntity):
|
||||
self,
|
||||
client: SmartThings,
|
||||
device: FullDevice,
|
||||
rooms: dict[str, str],
|
||||
capability: Capability,
|
||||
) -> None:
|
||||
"""Initialize the cover class."""
|
||||
super().__init__(
|
||||
client,
|
||||
device,
|
||||
rooms,
|
||||
{
|
||||
capability,
|
||||
Capability.BATTERY,
|
||||
|
@ -30,7 +30,6 @@ class SmartThingsEntity(Entity):
|
||||
self,
|
||||
client: SmartThings,
|
||||
device: FullDevice,
|
||||
rooms: dict[str, str],
|
||||
capabilities: set[Capability],
|
||||
*,
|
||||
component: str = MAIN,
|
||||
@ -47,38 +46,8 @@ class SmartThingsEntity(Entity):
|
||||
self.device = device
|
||||
self._attr_unique_id = device.device.device_id
|
||||
self._attr_device_info = DeviceInfo(
|
||||
configuration_url="https://account.smartthings.com",
|
||||
identifiers={(DOMAIN, device.device.device_id)},
|
||||
name=device.device.label,
|
||||
suggested_area=(
|
||||
rooms.get(device.device.room_id) if device.device.room_id else None
|
||||
),
|
||||
)
|
||||
if device.device.parent_device_id:
|
||||
self._attr_device_info["via_device"] = (
|
||||
DOMAIN,
|
||||
device.device.parent_device_id,
|
||||
)
|
||||
if (ocf := device.device.ocf) is not None:
|
||||
self._attr_device_info.update(
|
||||
{
|
||||
"manufacturer": ocf.manufacturer_name,
|
||||
"model": (
|
||||
(ocf.model_number.split("|")[0]) if ocf.model_number else None
|
||||
),
|
||||
"hw_version": ocf.hardware_version,
|
||||
"sw_version": ocf.firmware_version,
|
||||
}
|
||||
)
|
||||
if (viper := device.device.viper) is not None:
|
||||
self._attr_device_info.update(
|
||||
{
|
||||
"manufacturer": viper.manufacturer_name,
|
||||
"model": viper.model_name,
|
||||
"hw_version": viper.hardware_version,
|
||||
"sw_version": viper.software_version,
|
||||
}
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to updates."""
|
||||
|
@ -22,7 +22,7 @@ async def async_setup_entry(
|
||||
"""Add events for a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SmartThingsButtonEvent(entry_data.client, entry_data.rooms, device, component)
|
||||
SmartThingsButtonEvent(entry_data.client, device, component)
|
||||
for device in entry_data.devices.values()
|
||||
for component in device.device.components
|
||||
if Capability.BUTTON in component.capabilities
|
||||
@ -38,14 +38,11 @@ class SmartThingsButtonEvent(SmartThingsEntity, EventEntity):
|
||||
def __init__(
|
||||
self,
|
||||
client: SmartThings,
|
||||
rooms: dict[str, str],
|
||||
device: FullDevice,
|
||||
component: Component,
|
||||
) -> None:
|
||||
"""Init the class."""
|
||||
super().__init__(
|
||||
client, device, rooms, {Capability.BUTTON}, component=component.id
|
||||
)
|
||||
super().__init__(client, device, {Capability.BUTTON}, component=component.id)
|
||||
self._attr_name = component.label
|
||||
self._attr_unique_id = (
|
||||
f"{device.device.device_id}_{component.id}_{Capability.BUTTON}"
|
||||
|
@ -31,7 +31,7 @@ async def async_setup_entry(
|
||||
"""Add fans for a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SmartThingsFan(entry_data.client, entry_data.rooms, device)
|
||||
SmartThingsFan(entry_data.client, device)
|
||||
for device in entry_data.devices.values()
|
||||
if Capability.SWITCH in device.status[MAIN]
|
||||
and any(
|
||||
@ -51,14 +51,11 @@ class SmartThingsFan(SmartThingsEntity, FanEntity):
|
||||
_attr_name = None
|
||||
_attr_speed_count = int_states_in_range(SPEED_RANGE)
|
||||
|
||||
def __init__(
|
||||
self, client: SmartThings, rooms: dict[str, str], device: FullDevice
|
||||
) -> None:
|
||||
def __init__(self, client: SmartThings, device: FullDevice) -> None:
|
||||
"""Init the class."""
|
||||
super().__init__(
|
||||
client,
|
||||
device,
|
||||
rooms,
|
||||
{
|
||||
Capability.SWITCH,
|
||||
Capability.FAN_SPEED,
|
||||
|
@ -41,7 +41,7 @@ async def async_setup_entry(
|
||||
"""Add lights for a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SmartThingsLight(entry_data.client, entry_data.rooms, device)
|
||||
SmartThingsLight(entry_data.client, device)
|
||||
for device in entry_data.devices.values()
|
||||
if Capability.SWITCH in device.status[MAIN]
|
||||
and any(capability in device.status[MAIN] for capability in CAPABILITIES)
|
||||
@ -71,14 +71,11 @@ class SmartThingsLight(SmartThingsEntity, LightEntity, RestoreEntity):
|
||||
# highest kelvin found supported across 20+ handlers.
|
||||
_attr_max_color_temp_kelvin = 9000 # 111 mireds
|
||||
|
||||
def __init__(
|
||||
self, client: SmartThings, rooms: dict[str, str], device: FullDevice
|
||||
) -> None:
|
||||
def __init__(self, client: SmartThings, device: FullDevice) -> None:
|
||||
"""Initialize a SmartThingsLight."""
|
||||
super().__init__(
|
||||
client,
|
||||
device,
|
||||
rooms,
|
||||
{
|
||||
Capability.COLOR_CONTROL,
|
||||
Capability.COLOR_TEMPERATURE,
|
||||
|
@ -33,7 +33,7 @@ async def async_setup_entry(
|
||||
"""Add locks for a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SmartThingsLock(entry_data.client, device, entry_data.rooms, {Capability.LOCK})
|
||||
SmartThingsLock(entry_data.client, device, {Capability.LOCK})
|
||||
for device in entry_data.devices.values()
|
||||
if Capability.LOCK in device.status[MAIN]
|
||||
)
|
||||
|
@ -997,7 +997,6 @@ async def async_setup_entry(
|
||||
entry_data.client,
|
||||
device,
|
||||
description,
|
||||
entry_data.rooms,
|
||||
capability,
|
||||
attribute,
|
||||
)
|
||||
@ -1030,7 +1029,6 @@ class SmartThingsSensor(SmartThingsEntity, SensorEntity):
|
||||
client: SmartThings,
|
||||
device: FullDevice,
|
||||
entity_description: SmartThingsSensorEntityDescription,
|
||||
rooms: dict[str, str],
|
||||
capability: Capability,
|
||||
attribute: Attribute,
|
||||
) -> None:
|
||||
@ -1038,7 +1036,7 @@ class SmartThingsSensor(SmartThingsEntity, SensorEntity):
|
||||
capabilities_to_subscribe = {capability}
|
||||
if entity_description.use_temperature_unit:
|
||||
capabilities_to_subscribe.add(Capability.TEMPERATURE_MEASUREMENT)
|
||||
super().__init__(client, device, rooms, capabilities_to_subscribe)
|
||||
super().__init__(client, device, capabilities_to_subscribe)
|
||||
self._attr_unique_id = f"{device.device.device_id}{entity_description.unique_id_separator}{entity_description.key}"
|
||||
self._attribute = attribute
|
||||
self.capability = capability
|
||||
|
@ -37,9 +37,7 @@ async def async_setup_entry(
|
||||
"""Add switches for a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SmartThingsSwitch(
|
||||
entry_data.client, device, entry_data.rooms, {Capability.SWITCH}
|
||||
)
|
||||
SmartThingsSwitch(entry_data.client, device, {Capability.SWITCH})
|
||||
for device in entry_data.devices.values()
|
||||
if Capability.SWITCH in device.status[MAIN]
|
||||
and not any(capability in device.status[MAIN] for capability in CAPABILITIES)
|
||||
|
@ -28,9 +28,7 @@ async def async_setup_entry(
|
||||
"""Add update entities for a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SmartThingsUpdateEntity(
|
||||
entry_data.client, device, entry_data.rooms, {Capability.FIRMWARE_UPDATE}
|
||||
)
|
||||
SmartThingsUpdateEntity(entry_data.client, device, {Capability.FIRMWARE_UPDATE})
|
||||
for device in entry_data.devices.values()
|
||||
if Capability.FIRMWARE_UPDATE in device.status[MAIN]
|
||||
)
|
||||
|
@ -30,7 +30,7 @@ async def async_setup_entry(
|
||||
"""Add valves for a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SmartThingsValve(entry_data.client, entry_data.rooms, device)
|
||||
SmartThingsValve(entry_data.client, device)
|
||||
for device in entry_data.devices.values()
|
||||
if Capability.VALVE in device.status[MAIN]
|
||||
)
|
||||
@ -43,11 +43,9 @@ class SmartThingsValve(SmartThingsEntity, ValveEntity):
|
||||
_attr_reports_position = False
|
||||
_attr_name = None
|
||||
|
||||
def __init__(
|
||||
self, client: SmartThings, rooms: dict[str, str], device: FullDevice
|
||||
) -> None:
|
||||
def __init__(self, client: SmartThings, device: FullDevice) -> None:
|
||||
"""Init the class."""
|
||||
super().__init__(client, device, rooms, {Capability.VALVE})
|
||||
super().__init__(client, device, {Capability.VALVE})
|
||||
self._attr_device_class = DEVICE_CLASS_MAP.get(
|
||||
device.device.components[0].user_category
|
||||
or device.device.components[0].manufacturer_category
|
||||
|
@ -1624,7 +1624,7 @@
|
||||
'area_id': 'theater',
|
||||
'config_entries': <ANY>,
|
||||
'config_entries_subentries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'configuration_url': 'https://account.smartthings.com',
|
||||
'connections': set({
|
||||
tuple(
|
||||
'mac',
|
||||
|
Loading…
x
Reference in New Issue
Block a user