mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 07:37:34 +00:00
2022.8.7 (#77324)
This commit is contained in:
commit
f2e177a5af
@ -90,6 +90,7 @@ class AladdinDevice(CoverEntity):
|
|||||||
self._number = device["door_number"]
|
self._number = device["door_number"]
|
||||||
self._name = device["name"]
|
self._name = device["name"]
|
||||||
self._serial = device["serial"]
|
self._serial = device["serial"]
|
||||||
|
self._model = device["model"]
|
||||||
self._attr_unique_id = f"{self._device_id}-{self._number}"
|
self._attr_unique_id = f"{self._device_id}-{self._number}"
|
||||||
self._attr_has_entity_name = True
|
self._attr_has_entity_name = True
|
||||||
|
|
||||||
@ -97,9 +98,10 @@ class AladdinDevice(CoverEntity):
|
|||||||
def device_info(self) -> DeviceInfo | None:
|
def device_info(self) -> DeviceInfo | None:
|
||||||
"""Device information for Aladdin Connect cover."""
|
"""Device information for Aladdin Connect cover."""
|
||||||
return DeviceInfo(
|
return DeviceInfo(
|
||||||
identifiers={(DOMAIN, self._device_id)},
|
identifiers={(DOMAIN, f"{self._device_id}-{self._number}")},
|
||||||
name=self._name,
|
name=self._name,
|
||||||
manufacturer="Overhead Door",
|
manufacturer="Overhead Door",
|
||||||
|
model=self._model,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
|
@ -12,3 +12,4 @@ class DoorDevice(TypedDict):
|
|||||||
name: str
|
name: str
|
||||||
status: str
|
status: str
|
||||||
serial: str
|
serial: str
|
||||||
|
model: str
|
||||||
|
@ -56,6 +56,15 @@ SENSORS: tuple[AccSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=AladdinConnectClient.get_rssi_status,
|
value_fn=AladdinConnectClient.get_rssi_status,
|
||||||
),
|
),
|
||||||
|
AccSensorEntityDescription(
|
||||||
|
key="ble_strength",
|
||||||
|
name="BLE Strength",
|
||||||
|
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
value_fn=AladdinConnectClient.get_ble_strength,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -89,22 +98,26 @@ class AladdinConnectSensor(SensorEntity):
|
|||||||
device: DoorDevice,
|
device: DoorDevice,
|
||||||
description: AccSensorEntityDescription,
|
description: AccSensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a sensor for an Abode device."""
|
"""Initialize a sensor for an Aladdin Connect device."""
|
||||||
self._device_id = device["device_id"]
|
self._device_id = device["device_id"]
|
||||||
self._number = device["door_number"]
|
self._number = device["door_number"]
|
||||||
self._name = device["name"]
|
self._name = device["name"]
|
||||||
|
self._model = device["model"]
|
||||||
self._acc = acc
|
self._acc = acc
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_unique_id = f"{self._device_id}-{self._number}-{description.key}"
|
self._attr_unique_id = f"{self._device_id}-{self._number}-{description.key}"
|
||||||
self._attr_has_entity_name = True
|
self._attr_has_entity_name = True
|
||||||
|
if self._model == "01" and description.key in ("battery_level", "ble_strength"):
|
||||||
|
self._attr_entity_registry_enabled_default = True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self) -> DeviceInfo | None:
|
def device_info(self) -> DeviceInfo | None:
|
||||||
"""Device information for Aladdin Connect sensors."""
|
"""Device information for Aladdin Connect sensors."""
|
||||||
return DeviceInfo(
|
return DeviceInfo(
|
||||||
identifiers={(DOMAIN, self._device_id)},
|
identifiers={(DOMAIN, f"{self._device_id}-{self._number}")},
|
||||||
name=self._name,
|
name=self._name,
|
||||||
manufacturer="Overhead Door",
|
manufacturer="Overhead Door",
|
||||||
|
model=self._model,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -48,7 +48,7 @@ def get_alarm_system_id_for_unique_id(
|
|||||||
gateway: DeconzGateway, unique_id: str
|
gateway: DeconzGateway, unique_id: str
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Retrieve alarm system ID the unique ID is registered to."""
|
"""Retrieve alarm system ID the unique ID is registered to."""
|
||||||
for alarm_system in gateway.api.alarmsystems.values():
|
for alarm_system in gateway.api.alarm_systems.values():
|
||||||
if unique_id in alarm_system.devices:
|
if unique_id in alarm_system.devices:
|
||||||
return alarm_system.resource_id
|
return alarm_system.resource_id
|
||||||
return None
|
return None
|
||||||
@ -123,27 +123,27 @@ class DeconzAlarmControlPanel(DeconzDevice, AlarmControlPanelEntity):
|
|||||||
async def async_alarm_arm_away(self, code: str | None = None) -> None:
|
async def async_alarm_arm_away(self, code: str | None = None) -> None:
|
||||||
"""Send arm away command."""
|
"""Send arm away command."""
|
||||||
if code:
|
if code:
|
||||||
await self.gateway.api.alarmsystems.arm(
|
await self.gateway.api.alarm_systems.arm(
|
||||||
self.alarm_system_id, AlarmSystemArmAction.AWAY, code
|
self.alarm_system_id, AlarmSystemArmAction.AWAY, code
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_alarm_arm_home(self, code: str | None = None) -> None:
|
async def async_alarm_arm_home(self, code: str | None = None) -> None:
|
||||||
"""Send arm home command."""
|
"""Send arm home command."""
|
||||||
if code:
|
if code:
|
||||||
await self.gateway.api.alarmsystems.arm(
|
await self.gateway.api.alarm_systems.arm(
|
||||||
self.alarm_system_id, AlarmSystemArmAction.STAY, code
|
self.alarm_system_id, AlarmSystemArmAction.STAY, code
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_alarm_arm_night(self, code: str | None = None) -> None:
|
async def async_alarm_arm_night(self, code: str | None = None) -> None:
|
||||||
"""Send arm night command."""
|
"""Send arm night command."""
|
||||||
if code:
|
if code:
|
||||||
await self.gateway.api.alarmsystems.arm(
|
await self.gateway.api.alarm_systems.arm(
|
||||||
self.alarm_system_id, AlarmSystemArmAction.NIGHT, code
|
self.alarm_system_id, AlarmSystemArmAction.NIGHT, code
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_alarm_disarm(self, code: str | None = None) -> None:
|
async def async_alarm_disarm(self, code: str | None = None) -> None:
|
||||||
"""Send disarm command."""
|
"""Send disarm command."""
|
||||||
if code:
|
if code:
|
||||||
await self.gateway.api.alarmsystems.arm(
|
await self.gateway.api.alarm_systems.arm(
|
||||||
self.alarm_system_id, AlarmSystemArmAction.DISARM, code
|
self.alarm_system_id, AlarmSystemArmAction.DISARM, code
|
||||||
)
|
)
|
||||||
|
@ -159,7 +159,7 @@ ENTITY_DESCRIPTIONS = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
BINARY_SENSOR_DESCRIPTIONS = [
|
COMMON_BINARY_SENSOR_DESCRIPTIONS = [
|
||||||
DeconzBinarySensorDescription(
|
DeconzBinarySensorDescription(
|
||||||
key="tampered",
|
key="tampered",
|
||||||
value_fn=lambda device: device.tampered,
|
value_fn=lambda device: device.tampered,
|
||||||
@ -215,7 +215,8 @@ async def async_setup_entry(
|
|||||||
sensor = gateway.api.sensors[sensor_id]
|
sensor = gateway.api.sensors[sensor_id]
|
||||||
|
|
||||||
for description in (
|
for description in (
|
||||||
ENTITY_DESCRIPTIONS.get(type(sensor), []) + BINARY_SENSOR_DESCRIPTIONS
|
ENTITY_DESCRIPTIONS.get(type(sensor), [])
|
||||||
|
+ COMMON_BINARY_SENSOR_DESCRIPTIONS
|
||||||
):
|
):
|
||||||
if (
|
if (
|
||||||
not hasattr(sensor, description.key)
|
not hasattr(sensor, description.key)
|
||||||
@ -284,8 +285,8 @@ class DeconzBinarySensor(DeconzDevice, BinarySensorEntity):
|
|||||||
if self._device.on is not None:
|
if self._device.on is not None:
|
||||||
attr[ATTR_ON] = self._device.on
|
attr[ATTR_ON] = self._device.on
|
||||||
|
|
||||||
if self._device.secondary_temperature is not None:
|
if self._device.internal_temperature is not None:
|
||||||
attr[ATTR_TEMPERATURE] = self._device.secondary_temperature
|
attr[ATTR_TEMPERATURE] = self._device.internal_temperature
|
||||||
|
|
||||||
if isinstance(self._device, Presence):
|
if isinstance(self._device, Presence):
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from pydeconz.interfaces.lights import CoverAction
|
from pydeconz.interfaces.lights import CoverAction
|
||||||
|
from pydeconz.models import ResourceType
|
||||||
from pydeconz.models.event import EventType
|
from pydeconz.models.event import EventType
|
||||||
from pydeconz.models.light.cover import Cover
|
from pydeconz.models.light.cover import Cover
|
||||||
|
|
||||||
@ -23,9 +24,9 @@ from .deconz_device import DeconzDevice
|
|||||||
from .gateway import DeconzGateway, get_gateway_from_config_entry
|
from .gateway import DeconzGateway, get_gateway_from_config_entry
|
||||||
|
|
||||||
DECONZ_TYPE_TO_DEVICE_CLASS = {
|
DECONZ_TYPE_TO_DEVICE_CLASS = {
|
||||||
"Level controllable output": CoverDeviceClass.DAMPER,
|
ResourceType.LEVEL_CONTROLLABLE_OUTPUT.value: CoverDeviceClass.DAMPER,
|
||||||
"Window covering controller": CoverDeviceClass.SHADE,
|
ResourceType.WINDOW_COVERING_CONTROLLER.value: CoverDeviceClass.SHADE,
|
||||||
"Window covering device": CoverDeviceClass.SHADE,
|
ResourceType.WINDOW_COVERING_DEVICE.value: CoverDeviceClass.SHADE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -72,6 +73,8 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
|||||||
|
|
||||||
self._attr_device_class = DECONZ_TYPE_TO_DEVICE_CLASS.get(cover.type)
|
self._attr_device_class = DECONZ_TYPE_TO_DEVICE_CLASS.get(cover.type)
|
||||||
|
|
||||||
|
self.legacy_mode = cover.type == ResourceType.LEVEL_CONTROLLABLE_OUTPUT.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_cover_position(self) -> int:
|
def current_cover_position(self) -> int:
|
||||||
"""Return the current position of the cover."""
|
"""Return the current position of the cover."""
|
||||||
@ -88,6 +91,7 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
|||||||
await self.gateway.api.lights.covers.set_state(
|
await self.gateway.api.lights.covers.set_state(
|
||||||
id=self._device.resource_id,
|
id=self._device.resource_id,
|
||||||
lift=position,
|
lift=position,
|
||||||
|
legacy_mode=self.legacy_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||||
@ -95,6 +99,7 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
|||||||
await self.gateway.api.lights.covers.set_state(
|
await self.gateway.api.lights.covers.set_state(
|
||||||
id=self._device.resource_id,
|
id=self._device.resource_id,
|
||||||
action=CoverAction.OPEN,
|
action=CoverAction.OPEN,
|
||||||
|
legacy_mode=self.legacy_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||||
@ -102,6 +107,7 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
|||||||
await self.gateway.api.lights.covers.set_state(
|
await self.gateway.api.lights.covers.set_state(
|
||||||
id=self._device.resource_id,
|
id=self._device.resource_id,
|
||||||
action=CoverAction.CLOSE,
|
action=CoverAction.CLOSE,
|
||||||
|
legacy_mode=self.legacy_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_stop_cover(self, **kwargs: Any) -> None:
|
async def async_stop_cover(self, **kwargs: Any) -> None:
|
||||||
@ -109,6 +115,7 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
|||||||
await self.gateway.api.lights.covers.set_state(
|
await self.gateway.api.lights.covers.set_state(
|
||||||
id=self._device.resource_id,
|
id=self._device.resource_id,
|
||||||
action=CoverAction.STOP,
|
action=CoverAction.STOP,
|
||||||
|
legacy_mode=self.legacy_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -124,6 +131,7 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
|||||||
await self.gateway.api.lights.covers.set_state(
|
await self.gateway.api.lights.covers.set_state(
|
||||||
id=self._device.resource_id,
|
id=self._device.resource_id,
|
||||||
tilt=position,
|
tilt=position,
|
||||||
|
legacy_mode=self.legacy_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_open_cover_tilt(self, **kwargs: Any) -> None:
|
async def async_open_cover_tilt(self, **kwargs: Any) -> None:
|
||||||
@ -131,6 +139,7 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
|||||||
await self.gateway.api.lights.covers.set_state(
|
await self.gateway.api.lights.covers.set_state(
|
||||||
id=self._device.resource_id,
|
id=self._device.resource_id,
|
||||||
tilt=0,
|
tilt=0,
|
||||||
|
legacy_mode=self.legacy_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_close_cover_tilt(self, **kwargs: Any) -> None:
|
async def async_close_cover_tilt(self, **kwargs: Any) -> None:
|
||||||
@ -138,6 +147,7 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
|||||||
await self.gateway.api.lights.covers.set_state(
|
await self.gateway.api.lights.covers.set_state(
|
||||||
id=self._device.resource_id,
|
id=self._device.resource_id,
|
||||||
tilt=100,
|
tilt=100,
|
||||||
|
legacy_mode=self.legacy_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_stop_cover_tilt(self, **kwargs: Any) -> None:
|
async def async_stop_cover_tilt(self, **kwargs: Any) -> None:
|
||||||
@ -145,4 +155,5 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
|||||||
await self.gateway.api.lights.covers.set_state(
|
await self.gateway.api.lights.covers.set_state(
|
||||||
id=self._device.resource_id,
|
id=self._device.resource_id,
|
||||||
action=CoverAction.STOP,
|
action=CoverAction.STOP,
|
||||||
|
legacy_mode=self.legacy_mode,
|
||||||
)
|
)
|
||||||
|
@ -26,7 +26,7 @@ async def async_get_config_entry_diagnostics(
|
|||||||
gateway.api.config.raw, REDACT_DECONZ_CONFIG
|
gateway.api.config.raw, REDACT_DECONZ_CONFIG
|
||||||
)
|
)
|
||||||
diag["websocket_state"] = (
|
diag["websocket_state"] = (
|
||||||
gateway.api.websocket.state if gateway.api.websocket else "Unknown"
|
gateway.api.websocket.state.value if gateway.api.websocket else "Unknown"
|
||||||
)
|
)
|
||||||
diag["deconz_ids"] = gateway.deconz_ids
|
diag["deconz_ids"] = gateway.deconz_ids
|
||||||
diag["entities"] = gateway.entities
|
diag["entities"] = gateway.entities
|
||||||
@ -37,7 +37,7 @@ async def async_get_config_entry_diagnostics(
|
|||||||
}
|
}
|
||||||
for event in gateway.events
|
for event in gateway.events
|
||||||
}
|
}
|
||||||
diag["alarm_systems"] = {k: v.raw for k, v in gateway.api.alarmsystems.items()}
|
diag["alarm_systems"] = {k: v.raw for k, v in gateway.api.alarm_systems.items()}
|
||||||
diag["groups"] = {k: v.raw for k, v in gateway.api.groups.items()}
|
diag["groups"] = {k: v.raw for k, v in gateway.api.groups.items()}
|
||||||
diag["lights"] = {k: v.raw for k, v in gateway.api.lights.items()}
|
diag["lights"] = {k: v.raw for k, v in gateway.api.lights.items()}
|
||||||
diag["scenes"] = {k: v.raw for k, v in gateway.api.scenes.items()}
|
diag["scenes"] = {k: v.raw for k, v in gateway.api.scenes.items()}
|
||||||
|
@ -169,7 +169,7 @@ class DeconzGateway:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
for device_id in deconz_device_interface:
|
for device_id in sorted(deconz_device_interface, key=int):
|
||||||
async_add_device(EventType.ADDED, device_id)
|
async_add_device(EventType.ADDED, device_id)
|
||||||
|
|
||||||
initializing = False
|
initializing = False
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "deCONZ",
|
"name": "deCONZ",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/deconz",
|
"documentation": "https://www.home-assistant.io/integrations/deconz",
|
||||||
"requirements": ["pydeconz==102"],
|
"requirements": ["pydeconz==104"],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
"manufacturer": "Royal Philips Electronics",
|
"manufacturer": "Royal Philips Electronics",
|
||||||
|
@ -6,7 +6,7 @@ from collections.abc import Callable
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from pydeconz.models.event import EventType
|
from pydeconz.models.event import EventType
|
||||||
from pydeconz.models.sensor.presence import PRESENCE_DELAY, Presence
|
from pydeconz.models.sensor.presence import Presence
|
||||||
|
|
||||||
from homeassistant.components.number import (
|
from homeassistant.components.number import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -42,7 +42,7 @@ ENTITY_DESCRIPTIONS = {
|
|||||||
key="delay",
|
key="delay",
|
||||||
value_fn=lambda device: device.delay,
|
value_fn=lambda device: device.delay,
|
||||||
suffix="Delay",
|
suffix="Delay",
|
||||||
update_key=PRESENCE_DELAY,
|
update_key="delay",
|
||||||
native_max_value=65535,
|
native_max_value=65535,
|
||||||
native_min_value=0,
|
native_min_value=0,
|
||||||
native_step=1,
|
native_step=1,
|
||||||
|
@ -209,7 +209,7 @@ ENTITY_DESCRIPTIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SENSOR_DESCRIPTIONS = [
|
COMMON_SENSOR_DESCRIPTIONS = [
|
||||||
DeconzSensorDescription(
|
DeconzSensorDescription(
|
||||||
key="battery",
|
key="battery",
|
||||||
value_fn=lambda device: device.battery,
|
value_fn=lambda device: device.battery,
|
||||||
@ -221,8 +221,8 @@ SENSOR_DESCRIPTIONS = [
|
|||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
DeconzSensorDescription(
|
DeconzSensorDescription(
|
||||||
key="secondary_temperature",
|
key="internal_temperature",
|
||||||
value_fn=lambda device: device.secondary_temperature,
|
value_fn=lambda device: device.internal_temperature,
|
||||||
suffix="Temperature",
|
suffix="Temperature",
|
||||||
update_key="temperature",
|
update_key="temperature",
|
||||||
device_class=SensorDeviceClass.TEMPERATURE,
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
@ -253,7 +253,7 @@ async def async_setup_entry(
|
|||||||
known_entities = set(gateway.entities[DOMAIN])
|
known_entities = set(gateway.entities[DOMAIN])
|
||||||
|
|
||||||
for description in (
|
for description in (
|
||||||
ENTITY_DESCRIPTIONS.get(type(sensor), []) + SENSOR_DESCRIPTIONS
|
ENTITY_DESCRIPTIONS.get(type(sensor), []) + COMMON_SENSOR_DESCRIPTIONS
|
||||||
):
|
):
|
||||||
if (
|
if (
|
||||||
not hasattr(sensor, description.key)
|
not hasattr(sensor, description.key)
|
||||||
@ -342,8 +342,8 @@ class DeconzSensor(DeconzDevice, SensorEntity):
|
|||||||
if self._device.on is not None:
|
if self._device.on is not None:
|
||||||
attr[ATTR_ON] = self._device.on
|
attr[ATTR_ON] = self._device.on
|
||||||
|
|
||||||
if self._device.secondary_temperature is not None:
|
if self._device.internal_temperature is not None:
|
||||||
attr[ATTR_TEMPERATURE] = self._device.secondary_temperature
|
attr[ATTR_TEMPERATURE] = self._device.internal_temperature
|
||||||
|
|
||||||
if isinstance(self._device, Consumption):
|
if isinstance(self._device, Consumption):
|
||||||
attr[ATTR_POWER] = self._device.power
|
attr[ATTR_POWER] = self._device.power
|
||||||
@ -384,14 +384,16 @@ class DeconzBatteryTracker:
|
|||||||
self.sensor = gateway.api.sensors[sensor_id]
|
self.sensor = gateway.api.sensors[sensor_id]
|
||||||
self.gateway = gateway
|
self.gateway = gateway
|
||||||
self.async_add_entities = async_add_entities
|
self.async_add_entities = async_add_entities
|
||||||
self.unsub = self.sensor.subscribe(self.async_update_callback)
|
self.unsubscribe = self.sensor.subscribe(self.async_update_callback)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self) -> None:
|
def async_update_callback(self) -> None:
|
||||||
"""Update the device's state."""
|
"""Update the device's state."""
|
||||||
if "battery" in self.sensor.changed_keys:
|
if "battery" in self.sensor.changed_keys:
|
||||||
self.unsub()
|
self.unsubscribe()
|
||||||
known_entities = set(self.gateway.entities[DOMAIN])
|
known_entities = set(self.gateway.entities[DOMAIN])
|
||||||
entity = DeconzSensor(self.sensor, self.gateway, SENSOR_DESCRIPTIONS[0])
|
entity = DeconzSensor(
|
||||||
|
self.sensor, self.gateway, COMMON_SENSOR_DESCRIPTIONS[0]
|
||||||
|
)
|
||||||
if entity.unique_id not in known_entities:
|
if entity.unique_id not in known_entities:
|
||||||
self.async_add_entities([entity])
|
self.async_add_entities([entity])
|
||||||
|
@ -22,6 +22,7 @@ from homeassistant.const import (
|
|||||||
ELECTRIC_POTENTIAL_VOLT,
|
ELECTRIC_POTENTIAL_VOLT,
|
||||||
ENERGY_KILO_WATT_HOUR,
|
ENERGY_KILO_WATT_HOUR,
|
||||||
ENERGY_WATT_HOUR,
|
ENERGY_WATT_HOUR,
|
||||||
|
FREQUENCY_HERTZ,
|
||||||
POWER_WATT,
|
POWER_WATT,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
@ -252,6 +253,7 @@ SENSOR_UNIT_MAPPING = {
|
|||||||
"A": ELECTRIC_CURRENT_AMPERE,
|
"A": ELECTRIC_CURRENT_AMPERE,
|
||||||
"V": ELECTRIC_POTENTIAL_VOLT,
|
"V": ELECTRIC_POTENTIAL_VOLT,
|
||||||
"°": DEGREE,
|
"°": DEGREE,
|
||||||
|
"Hz": FREQUENCY_HERTZ,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ class GoodweNumberEntityDescriptionBase:
|
|||||||
|
|
||||||
getter: Callable[[Inverter], Awaitable[int]]
|
getter: Callable[[Inverter], Awaitable[int]]
|
||||||
setter: Callable[[Inverter, int], Awaitable[None]]
|
setter: Callable[[Inverter, int], Awaitable[None]]
|
||||||
|
filter: Callable[[Inverter], bool]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -35,17 +36,33 @@ class GoodweNumberEntityDescription(
|
|||||||
|
|
||||||
|
|
||||||
NUMBERS = (
|
NUMBERS = (
|
||||||
|
# non DT inverters (limit in W)
|
||||||
GoodweNumberEntityDescription(
|
GoodweNumberEntityDescription(
|
||||||
key="grid_export_limit",
|
key="grid_export_limit",
|
||||||
name="Grid export limit",
|
name="Grid export limit",
|
||||||
icon="mdi:transmission-tower",
|
icon="mdi:transmission-tower",
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
native_unit_of_measurement=POWER_WATT,
|
native_unit_of_measurement=POWER_WATT,
|
||||||
getter=lambda inv: inv.get_grid_export_limit(),
|
|
||||||
setter=lambda inv, val: inv.set_grid_export_limit(val),
|
|
||||||
native_step=100,
|
native_step=100,
|
||||||
native_min_value=0,
|
native_min_value=0,
|
||||||
native_max_value=10000,
|
native_max_value=10000,
|
||||||
|
getter=lambda inv: inv.get_grid_export_limit(),
|
||||||
|
setter=lambda inv, val: inv.set_grid_export_limit(val),
|
||||||
|
filter=lambda inv: type(inv).__name__ != "DT",
|
||||||
|
),
|
||||||
|
# DT inverters (limit is in %)
|
||||||
|
GoodweNumberEntityDescription(
|
||||||
|
key="grid_export_limit",
|
||||||
|
name="Grid export limit",
|
||||||
|
icon="mdi:transmission-tower",
|
||||||
|
entity_category=EntityCategory.CONFIG,
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
native_step=1,
|
||||||
|
native_min_value=0,
|
||||||
|
native_max_value=100,
|
||||||
|
getter=lambda inv: inv.get_grid_export_limit(),
|
||||||
|
setter=lambda inv, val: inv.set_grid_export_limit(val),
|
||||||
|
filter=lambda inv: type(inv).__name__ == "DT",
|
||||||
),
|
),
|
||||||
GoodweNumberEntityDescription(
|
GoodweNumberEntityDescription(
|
||||||
key="battery_discharge_depth",
|
key="battery_discharge_depth",
|
||||||
@ -53,11 +70,12 @@ NUMBERS = (
|
|||||||
icon="mdi:battery-arrow-down",
|
icon="mdi:battery-arrow-down",
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
getter=lambda inv: inv.get_ongrid_battery_dod(),
|
|
||||||
setter=lambda inv, val: inv.set_ongrid_battery_dod(val),
|
|
||||||
native_step=1,
|
native_step=1,
|
||||||
native_min_value=0,
|
native_min_value=0,
|
||||||
native_max_value=99,
|
native_max_value=99,
|
||||||
|
getter=lambda inv: inv.get_ongrid_battery_dod(),
|
||||||
|
setter=lambda inv, val: inv.set_ongrid_battery_dod(val),
|
||||||
|
filter=lambda inv: True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -73,7 +91,7 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
for description in NUMBERS:
|
for description in filter(lambda dsc: dsc.filter(inverter), NUMBERS):
|
||||||
try:
|
try:
|
||||||
current_value = await description.getter(inverter)
|
current_value = await description.getter(inverter)
|
||||||
except (InverterError, ValueError):
|
except (InverterError, ValueError):
|
||||||
@ -82,7 +100,7 @@ async def async_setup_entry(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
entities.append(
|
entities.append(
|
||||||
InverterNumberEntity(device_info, description, inverter, current_value),
|
InverterNumberEntity(device_info, description, inverter, current_value)
|
||||||
)
|
)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
@ -8,11 +8,15 @@ DEFAULT_PLANT_ID = "0"
|
|||||||
DEFAULT_NAME = "Growatt"
|
DEFAULT_NAME = "Growatt"
|
||||||
|
|
||||||
SERVER_URLS = [
|
SERVER_URLS = [
|
||||||
"https://server.growatt.com/",
|
"https://server-api.growatt.com/",
|
||||||
"https://server-us.growatt.com/",
|
"https://server-us.growatt.com/",
|
||||||
"http://server.smten.com/",
|
"http://server.smten.com/",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
DEPRECATED_URLS = [
|
||||||
|
"https://server.growatt.com/",
|
||||||
|
]
|
||||||
|
|
||||||
DEFAULT_URL = SERVER_URLS[0]
|
DEFAULT_URL = SERVER_URLS[0]
|
||||||
|
|
||||||
DOMAIN = "growatt_server"
|
DOMAIN = "growatt_server"
|
||||||
|
@ -19,6 +19,7 @@ from .const import (
|
|||||||
CONF_PLANT_ID,
|
CONF_PLANT_ID,
|
||||||
DEFAULT_PLANT_ID,
|
DEFAULT_PLANT_ID,
|
||||||
DEFAULT_URL,
|
DEFAULT_URL,
|
||||||
|
DEPRECATED_URLS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
LOGIN_INVALID_AUTH_CODE,
|
LOGIN_INVALID_AUTH_CODE,
|
||||||
)
|
)
|
||||||
@ -62,12 +63,23 @@ async def async_setup_entry(
|
|||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Growatt sensor."""
|
"""Set up the Growatt sensor."""
|
||||||
config = config_entry.data
|
config = {**config_entry.data}
|
||||||
username = config[CONF_USERNAME]
|
username = config[CONF_USERNAME]
|
||||||
password = config[CONF_PASSWORD]
|
password = config[CONF_PASSWORD]
|
||||||
url = config.get(CONF_URL, DEFAULT_URL)
|
url = config.get(CONF_URL, DEFAULT_URL)
|
||||||
name = config[CONF_NAME]
|
name = config[CONF_NAME]
|
||||||
|
|
||||||
|
# If the URL has been deprecated then change to the default instead
|
||||||
|
if url in DEPRECATED_URLS:
|
||||||
|
_LOGGER.info(
|
||||||
|
"URL: %s has been deprecated, migrating to the latest default: %s",
|
||||||
|
url,
|
||||||
|
DEFAULT_URL,
|
||||||
|
)
|
||||||
|
url = DEFAULT_URL
|
||||||
|
config[CONF_URL] = url
|
||||||
|
hass.config_entries.async_update_entry(config_entry, data=config)
|
||||||
|
|
||||||
api = growattServer.GrowattApi()
|
api = growattServer.GrowattApi()
|
||||||
api.server_url = url
|
api.server_url = url
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "KNX",
|
"name": "KNX",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/knx",
|
"documentation": "https://www.home-assistant.io/integrations/knx",
|
||||||
"requirements": ["xknx==1.0.0"],
|
"requirements": ["xknx==1.0.1"],
|
||||||
"codeowners": ["@Julius2342", "@farmio", "@marvin-w"],
|
"codeowners": ["@Julius2342", "@farmio", "@marvin-w"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
|
@ -13,7 +13,7 @@ from homeassistant.const import (
|
|||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
LENGTH_MILLIMETERS,
|
LENGTH_MILLIMETERS,
|
||||||
PRESSURE_HPA,
|
PRESSURE_HPA,
|
||||||
SPEED_METERS_PER_SECOND,
|
SPEED_KILOMETERS_PER_HOUR,
|
||||||
TEMP_CELSIUS,
|
TEMP_CELSIUS,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -58,7 +58,7 @@ class MetEireannWeather(CoordinatorEntity, WeatherEntity):
|
|||||||
_attr_native_precipitation_unit = LENGTH_MILLIMETERS
|
_attr_native_precipitation_unit = LENGTH_MILLIMETERS
|
||||||
_attr_native_pressure_unit = PRESSURE_HPA
|
_attr_native_pressure_unit = PRESSURE_HPA
|
||||||
_attr_native_temperature_unit = TEMP_CELSIUS
|
_attr_native_temperature_unit = TEMP_CELSIUS
|
||||||
_attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND
|
_attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR
|
||||||
|
|
||||||
def __init__(self, coordinator, config, hourly):
|
def __init__(self, coordinator, config, hourly):
|
||||||
"""Initialise the platform with a data instance and site."""
|
"""Initialise the platform with a data instance and site."""
|
||||||
|
@ -188,7 +188,10 @@ class BlockSleepingClimate(
|
|||||||
def hvac_mode(self) -> HVACMode:
|
def hvac_mode(self) -> HVACMode:
|
||||||
"""HVAC current mode."""
|
"""HVAC current mode."""
|
||||||
if self.device_block is None:
|
if self.device_block is None:
|
||||||
return HVACMode(self.last_state.state) if self.last_state else HVACMode.OFF
|
if self.last_state and self.last_state.state in list(HVACMode):
|
||||||
|
return HVACMode(self.last_state.state)
|
||||||
|
return HVACMode.OFF
|
||||||
|
|
||||||
if self.device_block.mode is None or self._check_is_off():
|
if self.device_block.mode is None or self._check_is_off():
|
||||||
return HVACMode.OFF
|
return HVACMode.OFF
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "switchbot",
|
"domain": "switchbot",
|
||||||
"name": "SwitchBot",
|
"name": "SwitchBot",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/switchbot",
|
"documentation": "https://www.home-assistant.io/integrations/switchbot",
|
||||||
"requirements": ["PySwitchbot==0.18.10"],
|
"requirements": ["PySwitchbot==0.18.14"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"dependencies": ["bluetooth"],
|
"dependencies": ["bluetooth"],
|
||||||
"codeowners": [
|
"codeowners": [
|
||||||
|
@ -4,15 +4,15 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/zha",
|
"documentation": "https://www.home-assistant.io/integrations/zha",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"bellows==0.32.0",
|
"bellows==0.33.1",
|
||||||
"pyserial==3.5",
|
"pyserial==3.5",
|
||||||
"pyserial-asyncio==0.6",
|
"pyserial-asyncio==0.6",
|
||||||
"zha-quirks==0.0.78",
|
"zha-quirks==0.0.78",
|
||||||
"zigpy-deconz==0.18.0",
|
"zigpy-deconz==0.18.0",
|
||||||
"zigpy==0.49.1",
|
"zigpy==0.50.2",
|
||||||
"zigpy-xbee==0.15.0",
|
"zigpy-xbee==0.15.0",
|
||||||
"zigpy-zigate==0.9.1",
|
"zigpy-zigate==0.9.2",
|
||||||
"zigpy-znp==0.8.1"
|
"zigpy-znp==0.8.2"
|
||||||
],
|
],
|
||||||
"usb": [
|
"usb": [
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,7 @@ from .backports.enum import StrEnum
|
|||||||
|
|
||||||
MAJOR_VERSION: Final = 2022
|
MAJOR_VERSION: Final = 2022
|
||||||
MINOR_VERSION: Final = 8
|
MINOR_VERSION: Final = 8
|
||||||
PATCH_VERSION: Final = "6"
|
PATCH_VERSION: Final = "7"
|
||||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)
|
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "homeassistant"
|
name = "homeassistant"
|
||||||
version = "2022.8.6"
|
version = "2022.8.7"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "Open-source home automation platform running on Python 3."
|
description = "Open-source home automation platform running on Python 3."
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
|
@ -37,7 +37,7 @@ PyRMVtransport==0.3.3
|
|||||||
PySocks==1.7.1
|
PySocks==1.7.1
|
||||||
|
|
||||||
# homeassistant.components.switchbot
|
# homeassistant.components.switchbot
|
||||||
PySwitchbot==0.18.10
|
PySwitchbot==0.18.14
|
||||||
|
|
||||||
# homeassistant.components.transport_nsw
|
# homeassistant.components.transport_nsw
|
||||||
PyTransportNSW==0.1.1
|
PyTransportNSW==0.1.1
|
||||||
@ -396,7 +396,7 @@ beautifulsoup4==4.11.1
|
|||||||
# beewi_smartclim==0.0.10
|
# beewi_smartclim==0.0.10
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
bellows==0.32.0
|
bellows==0.33.1
|
||||||
|
|
||||||
# homeassistant.components.bmw_connected_drive
|
# homeassistant.components.bmw_connected_drive
|
||||||
bimmer_connected==0.10.2
|
bimmer_connected==0.10.2
|
||||||
@ -1455,7 +1455,7 @@ pydaikin==2.7.0
|
|||||||
pydanfossair==0.1.0
|
pydanfossair==0.1.0
|
||||||
|
|
||||||
# homeassistant.components.deconz
|
# homeassistant.components.deconz
|
||||||
pydeconz==102
|
pydeconz==104
|
||||||
|
|
||||||
# homeassistant.components.delijn
|
# homeassistant.components.delijn
|
||||||
pydelijn==1.0.0
|
pydelijn==1.0.0
|
||||||
@ -2473,7 +2473,7 @@ xboxapi==2.0.1
|
|||||||
xiaomi-ble==0.6.4
|
xiaomi-ble==0.6.4
|
||||||
|
|
||||||
# homeassistant.components.knx
|
# homeassistant.components.knx
|
||||||
xknx==1.0.0
|
xknx==1.0.1
|
||||||
|
|
||||||
# homeassistant.components.bluesound
|
# homeassistant.components.bluesound
|
||||||
# homeassistant.components.fritz
|
# homeassistant.components.fritz
|
||||||
@ -2529,13 +2529,13 @@ zigpy-deconz==0.18.0
|
|||||||
zigpy-xbee==0.15.0
|
zigpy-xbee==0.15.0
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zigpy-zigate==0.9.1
|
zigpy-zigate==0.9.2
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zigpy-znp==0.8.1
|
zigpy-znp==0.8.2
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zigpy==0.49.1
|
zigpy==0.50.2
|
||||||
|
|
||||||
# homeassistant.components.zoneminder
|
# homeassistant.components.zoneminder
|
||||||
zm-py==0.5.2
|
zm-py==0.5.2
|
||||||
|
@ -33,7 +33,7 @@ PyRMVtransport==0.3.3
|
|||||||
PySocks==1.7.1
|
PySocks==1.7.1
|
||||||
|
|
||||||
# homeassistant.components.switchbot
|
# homeassistant.components.switchbot
|
||||||
PySwitchbot==0.18.10
|
PySwitchbot==0.18.14
|
||||||
|
|
||||||
# homeassistant.components.transport_nsw
|
# homeassistant.components.transport_nsw
|
||||||
PyTransportNSW==0.1.1
|
PyTransportNSW==0.1.1
|
||||||
@ -320,7 +320,7 @@ base36==0.1.1
|
|||||||
beautifulsoup4==4.11.1
|
beautifulsoup4==4.11.1
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
bellows==0.32.0
|
bellows==0.33.1
|
||||||
|
|
||||||
# homeassistant.components.bmw_connected_drive
|
# homeassistant.components.bmw_connected_drive
|
||||||
bimmer_connected==0.10.2
|
bimmer_connected==0.10.2
|
||||||
@ -1001,7 +1001,7 @@ pycoolmasternet-async==0.1.2
|
|||||||
pydaikin==2.7.0
|
pydaikin==2.7.0
|
||||||
|
|
||||||
# homeassistant.components.deconz
|
# homeassistant.components.deconz
|
||||||
pydeconz==102
|
pydeconz==104
|
||||||
|
|
||||||
# homeassistant.components.dexcom
|
# homeassistant.components.dexcom
|
||||||
pydexcom==0.2.3
|
pydexcom==0.2.3
|
||||||
@ -1665,7 +1665,7 @@ xbox-webapi==2.0.11
|
|||||||
xiaomi-ble==0.6.4
|
xiaomi-ble==0.6.4
|
||||||
|
|
||||||
# homeassistant.components.knx
|
# homeassistant.components.knx
|
||||||
xknx==1.0.0
|
xknx==1.0.1
|
||||||
|
|
||||||
# homeassistant.components.bluesound
|
# homeassistant.components.bluesound
|
||||||
# homeassistant.components.fritz
|
# homeassistant.components.fritz
|
||||||
@ -1703,13 +1703,13 @@ zigpy-deconz==0.18.0
|
|||||||
zigpy-xbee==0.15.0
|
zigpy-xbee==0.15.0
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zigpy-zigate==0.9.1
|
zigpy-zigate==0.9.2
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zigpy-znp==0.8.1
|
zigpy-znp==0.8.2
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zigpy==0.49.1
|
zigpy==0.50.2
|
||||||
|
|
||||||
# homeassistant.components.zwave_js
|
# homeassistant.components.zwave_js
|
||||||
zwave-js-server-python==0.39.0
|
zwave-js-server-python==0.39.0
|
||||||
|
@ -11,6 +11,7 @@ DEVICE_CONFIG_OPEN = {
|
|||||||
"status": "open",
|
"status": "open",
|
||||||
"link_status": "Connected",
|
"link_status": "Connected",
|
||||||
"serial": "12345",
|
"serial": "12345",
|
||||||
|
"model": "02",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -31,6 +32,8 @@ def fixture_mock_aladdinconnect_api():
|
|||||||
mock_opener.get_battery_status.return_value = "99"
|
mock_opener.get_battery_status.return_value = "99"
|
||||||
mock_opener.async_get_rssi_status = AsyncMock(return_value="-55")
|
mock_opener.async_get_rssi_status = AsyncMock(return_value="-55")
|
||||||
mock_opener.get_rssi_status.return_value = "-55"
|
mock_opener.get_rssi_status.return_value = "-55"
|
||||||
|
mock_opener.async_get_ble_strength = AsyncMock(return_value="-45")
|
||||||
|
mock_opener.get_ble_strength.return_value = "-45"
|
||||||
mock_opener.get_doors = AsyncMock(return_value=[DEVICE_CONFIG_OPEN])
|
mock_opener.get_doors = AsyncMock(return_value=[DEVICE_CONFIG_OPEN])
|
||||||
|
|
||||||
mock_opener.register_callback = mock.Mock(return_value=True)
|
mock_opener.register_callback = mock.Mock(return_value=True)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Test the Aladdin Connect Sensors."""
|
"""Test the Aladdin Connect Sensors."""
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
from homeassistant.components.aladdin_connect.const import DOMAIN
|
from homeassistant.components.aladdin_connect.const import DOMAIN
|
||||||
from homeassistant.components.aladdin_connect.cover import SCAN_INTERVAL
|
from homeassistant.components.aladdin_connect.cover import SCAN_INTERVAL
|
||||||
@ -10,6 +10,17 @@ from homeassistant.util.dt import utcnow
|
|||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
|
||||||
|
DEVICE_CONFIG_MODEL_01 = {
|
||||||
|
"device_id": 533255,
|
||||||
|
"door_number": 1,
|
||||||
|
"name": "home",
|
||||||
|
"status": "closed",
|
||||||
|
"link_status": "Connected",
|
||||||
|
"serial": "12345",
|
||||||
|
"model": "01",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
CONFIG = {"username": "test-user", "password": "test-password"}
|
CONFIG = {"username": "test-user", "password": "test-password"}
|
||||||
RELOAD_AFTER_UPDATE_DELAY = timedelta(seconds=31)
|
RELOAD_AFTER_UPDATE_DELAY = timedelta(seconds=31)
|
||||||
|
|
||||||
@ -83,3 +94,71 @@ async def test_sensors(
|
|||||||
|
|
||||||
state = hass.states.get("sensor.home_wi_fi_rssi")
|
state = hass.states.get("sensor.home_wi_fi_rssi")
|
||||||
assert state
|
assert state
|
||||||
|
|
||||||
|
|
||||||
|
async def test_sensors_model_01(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_aladdinconnect_api: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test Sensors for AladdinConnect."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data=CONFIG,
|
||||||
|
unique_id="test-id",
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.aladdin_connect.AladdinConnectClient",
|
||||||
|
return_value=mock_aladdinconnect_api,
|
||||||
|
):
|
||||||
|
mock_aladdinconnect_api.get_doors = AsyncMock(
|
||||||
|
return_value=[DEVICE_CONFIG_MODEL_01]
|
||||||
|
)
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
registry = entity_registry.async_get(hass)
|
||||||
|
entry = registry.async_get("sensor.home_battery_level")
|
||||||
|
assert entry
|
||||||
|
assert entry.disabled is False
|
||||||
|
assert entry.disabled_by is None
|
||||||
|
state = hass.states.get("sensor.home_battery_level")
|
||||||
|
assert state
|
||||||
|
|
||||||
|
entry = registry.async_get("sensor.home_wi_fi_rssi")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert entry
|
||||||
|
assert entry.disabled
|
||||||
|
assert entry.disabled_by is entity_registry.RegistryEntryDisabler.INTEGRATION
|
||||||
|
update_entry = registry.async_update_entity(
|
||||||
|
entry.entity_id, **{"disabled_by": None}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert update_entry != entry
|
||||||
|
assert update_entry.disabled is False
|
||||||
|
state = hass.states.get("sensor.home_wi_fi_rssi")
|
||||||
|
assert state is None
|
||||||
|
|
||||||
|
update_entry = registry.async_update_entity(
|
||||||
|
entry.entity_id, **{"disabled_by": None}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass,
|
||||||
|
utcnow() + SCAN_INTERVAL,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.home_wi_fi_rssi")
|
||||||
|
assert state
|
||||||
|
|
||||||
|
entry = registry.async_get("sensor.home_ble_strength")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert entry
|
||||||
|
assert entry.disabled is False
|
||||||
|
assert entry.disabled_by is None
|
||||||
|
state = hass.states.get("sensor.home_ble_strength")
|
||||||
|
assert state
|
||||||
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from pydeconz.websocket import SIGNAL_CONNECTION_STATE, SIGNAL_DATA
|
from pydeconz.websocket import Signal
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.components.light.conftest import mock_light_profiles # noqa: F401
|
from tests.components.light.conftest import mock_light_profiles # noqa: F401
|
||||||
@ -20,10 +20,10 @@ def mock_deconz_websocket():
|
|||||||
|
|
||||||
if data:
|
if data:
|
||||||
mock.return_value.data = data
|
mock.return_value.data = data
|
||||||
await pydeconz_gateway_session_handler(signal=SIGNAL_DATA)
|
await pydeconz_gateway_session_handler(signal=Signal.DATA)
|
||||||
elif state:
|
elif state:
|
||||||
mock.return_value.state = state
|
mock.return_value.state = state
|
||||||
await pydeconz_gateway_session_handler(signal=SIGNAL_CONNECTION_STATE)
|
await pydeconz_gateway_session_handler(signal=Signal.CONNECTION_STATE)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@ -213,3 +213,111 @@ async def test_tilt_cover(hass, aioclient_mock):
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
assert aioclient_mock.mock_calls[4][2] == {"stop": True}
|
assert aioclient_mock.mock_calls[4][2] == {"stop": True}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_level_controllable_output_cover(hass, aioclient_mock):
|
||||||
|
"""Test that tilting a cover works."""
|
||||||
|
data = {
|
||||||
|
"lights": {
|
||||||
|
"0": {
|
||||||
|
"etag": "4cefc909134c8e99086b55273c2bde67",
|
||||||
|
"hascolor": False,
|
||||||
|
"lastannounced": "2022-08-08T12:06:18Z",
|
||||||
|
"lastseen": "2022-08-14T14:22Z",
|
||||||
|
"manufacturername": "Keen Home Inc",
|
||||||
|
"modelid": "SV01-410-MP-1.0",
|
||||||
|
"name": "Vent",
|
||||||
|
"state": {
|
||||||
|
"alert": "none",
|
||||||
|
"bri": 242,
|
||||||
|
"on": False,
|
||||||
|
"reachable": True,
|
||||||
|
"sat": 10,
|
||||||
|
},
|
||||||
|
"swversion": "0x00000012",
|
||||||
|
"type": "Level controllable output",
|
||||||
|
"uniqueid": "00:22:a3:00:00:00:00:00-01",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with patch.dict(DECONZ_WEB_REQUEST, data):
|
||||||
|
config_entry = await setup_deconz_integration(hass, aioclient_mock)
|
||||||
|
|
||||||
|
assert len(hass.states.async_all()) == 1
|
||||||
|
covering_device = hass.states.get("cover.vent")
|
||||||
|
assert covering_device.state == STATE_OPEN
|
||||||
|
assert covering_device.attributes[ATTR_CURRENT_TILT_POSITION] == 97
|
||||||
|
|
||||||
|
# Verify service calls for tilting cover
|
||||||
|
|
||||||
|
mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/0/state")
|
||||||
|
|
||||||
|
# Service open cover
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
COVER_DOMAIN,
|
||||||
|
SERVICE_OPEN_COVER,
|
||||||
|
{ATTR_ENTITY_ID: "cover.vent"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert aioclient_mock.mock_calls[1][2] == {"on": False}
|
||||||
|
|
||||||
|
# Service close cover
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
COVER_DOMAIN,
|
||||||
|
SERVICE_CLOSE_COVER,
|
||||||
|
{ATTR_ENTITY_ID: "cover.vent"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert aioclient_mock.mock_calls[2][2] == {"on": True}
|
||||||
|
|
||||||
|
# Service set cover position
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
COVER_DOMAIN,
|
||||||
|
SERVICE_SET_COVER_POSITION,
|
||||||
|
{ATTR_ENTITY_ID: "cover.vent", ATTR_POSITION: 40},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert aioclient_mock.mock_calls[3][2] == {"bri": 152}
|
||||||
|
|
||||||
|
# Service set tilt cover
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
COVER_DOMAIN,
|
||||||
|
SERVICE_SET_COVER_TILT_POSITION,
|
||||||
|
{ATTR_ENTITY_ID: "cover.vent", ATTR_TILT_POSITION: 40},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert aioclient_mock.mock_calls[4][2] == {"sat": 152}
|
||||||
|
|
||||||
|
# Service open tilt cover
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
COVER_DOMAIN,
|
||||||
|
SERVICE_OPEN_COVER_TILT,
|
||||||
|
{ATTR_ENTITY_ID: "cover.vent"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert aioclient_mock.mock_calls[5][2] == {"sat": 0}
|
||||||
|
|
||||||
|
# Service close tilt cover
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
COVER_DOMAIN,
|
||||||
|
SERVICE_CLOSE_COVER_TILT,
|
||||||
|
{ATTR_ENTITY_ID: "cover.vent"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert aioclient_mock.mock_calls[6][2] == {"sat": 254}
|
||||||
|
|
||||||
|
# Service stop cover movement
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
COVER_DOMAIN,
|
||||||
|
SERVICE_STOP_COVER_TILT,
|
||||||
|
{ATTR_ENTITY_ID: "cover.vent"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert aioclient_mock.mock_calls[7][2] == {"bri_inc": 0}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Test deCONZ diagnostics."""
|
"""Test deCONZ diagnostics."""
|
||||||
|
|
||||||
from pydeconz.websocket import STATE_RUNNING
|
from pydeconz.websocket import State
|
||||||
|
|
||||||
from homeassistant.components.deconz.const import CONF_MASTER_GATEWAY
|
from homeassistant.components.deconz.const import CONF_MASTER_GATEWAY
|
||||||
from homeassistant.components.diagnostics import REDACTED
|
from homeassistant.components.diagnostics import REDACTED
|
||||||
@ -17,7 +17,7 @@ async def test_entry_diagnostics(
|
|||||||
"""Test config entry diagnostics."""
|
"""Test config entry diagnostics."""
|
||||||
config_entry = await setup_deconz_integration(hass, aioclient_mock)
|
config_entry = await setup_deconz_integration(hass, aioclient_mock)
|
||||||
|
|
||||||
await mock_deconz_websocket(state=STATE_RUNNING)
|
await mock_deconz_websocket(state=State.RUNNING)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
|
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
|
||||||
@ -44,7 +44,7 @@ async def test_entry_diagnostics(
|
|||||||
"uuid": "1234",
|
"uuid": "1234",
|
||||||
"websocketport": 1234,
|
"websocketport": 1234,
|
||||||
},
|
},
|
||||||
"websocket_state": STATE_RUNNING,
|
"websocket_state": State.RUNNING.value,
|
||||||
"deconz_ids": {},
|
"deconz_ids": {},
|
||||||
"entities": {
|
"entities": {
|
||||||
str(Platform.ALARM_CONTROL_PANEL): [],
|
str(Platform.ALARM_CONTROL_PANEL): [],
|
||||||
|
@ -5,7 +5,7 @@ from copy import deepcopy
|
|||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pydeconz
|
import pydeconz
|
||||||
from pydeconz.websocket import STATE_RETRYING, STATE_RUNNING
|
from pydeconz.websocket import State
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import ssdp
|
from homeassistant.components import ssdp
|
||||||
@ -223,12 +223,12 @@ async def test_connection_status_signalling(
|
|||||||
|
|
||||||
assert hass.states.get("binary_sensor.presence").state == STATE_OFF
|
assert hass.states.get("binary_sensor.presence").state == STATE_OFF
|
||||||
|
|
||||||
await mock_deconz_websocket(state=STATE_RETRYING)
|
await mock_deconz_websocket(state=State.RETRYING)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get("binary_sensor.presence").state == STATE_UNAVAILABLE
|
assert hass.states.get("binary_sensor.presence").state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
await mock_deconz_websocket(state=STATE_RUNNING)
|
await mock_deconz_websocket(state=State.RUNNING)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get("binary_sensor.presence").state == STATE_OFF
|
assert hass.states.get("binary_sensor.presence").state == STATE_OFF
|
||||||
|
Loading…
x
Reference in New Issue
Block a user