mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 23:27:37 +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._name = device["name"]
|
||||
self._serial = device["serial"]
|
||||
self._model = device["model"]
|
||||
self._attr_unique_id = f"{self._device_id}-{self._number}"
|
||||
self._attr_has_entity_name = True
|
||||
|
||||
@ -97,9 +98,10 @@ class AladdinDevice(CoverEntity):
|
||||
def device_info(self) -> DeviceInfo | None:
|
||||
"""Device information for Aladdin Connect cover."""
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self._device_id)},
|
||||
identifiers={(DOMAIN, f"{self._device_id}-{self._number}")},
|
||||
name=self._name,
|
||||
manufacturer="Overhead Door",
|
||||
model=self._model,
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
|
@ -12,3 +12,4 @@ class DoorDevice(TypedDict):
|
||||
name: str
|
||||
status: str
|
||||
serial: str
|
||||
model: str
|
||||
|
@ -56,6 +56,15 @@ SENSORS: tuple[AccSensorEntityDescription, ...] = (
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
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,
|
||||
description: AccSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize a sensor for an Abode device."""
|
||||
"""Initialize a sensor for an Aladdin Connect device."""
|
||||
self._device_id = device["device_id"]
|
||||
self._number = device["door_number"]
|
||||
self._name = device["name"]
|
||||
self._model = device["model"]
|
||||
self._acc = acc
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{self._device_id}-{self._number}-{description.key}"
|
||||
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
|
||||
def device_info(self) -> DeviceInfo | None:
|
||||
"""Device information for Aladdin Connect sensors."""
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self._device_id)},
|
||||
identifiers={(DOMAIN, f"{self._device_id}-{self._number}")},
|
||||
name=self._name,
|
||||
manufacturer="Overhead Door",
|
||||
model=self._model,
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -48,7 +48,7 @@ def get_alarm_system_id_for_unique_id(
|
||||
gateway: DeconzGateway, unique_id: str
|
||||
) -> str | None:
|
||||
"""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:
|
||||
return alarm_system.resource_id
|
||||
return None
|
||||
@ -123,27 +123,27 @@ class DeconzAlarmControlPanel(DeconzDevice, AlarmControlPanelEntity):
|
||||
async def async_alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Send arm away command."""
|
||||
if code:
|
||||
await self.gateway.api.alarmsystems.arm(
|
||||
await self.gateway.api.alarm_systems.arm(
|
||||
self.alarm_system_id, AlarmSystemArmAction.AWAY, code
|
||||
)
|
||||
|
||||
async def async_alarm_arm_home(self, code: str | None = None) -> None:
|
||||
"""Send arm home command."""
|
||||
if code:
|
||||
await self.gateway.api.alarmsystems.arm(
|
||||
await self.gateway.api.alarm_systems.arm(
|
||||
self.alarm_system_id, AlarmSystemArmAction.STAY, code
|
||||
)
|
||||
|
||||
async def async_alarm_arm_night(self, code: str | None = None) -> None:
|
||||
"""Send arm night command."""
|
||||
if code:
|
||||
await self.gateway.api.alarmsystems.arm(
|
||||
await self.gateway.api.alarm_systems.arm(
|
||||
self.alarm_system_id, AlarmSystemArmAction.NIGHT, code
|
||||
)
|
||||
|
||||
async def async_alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Send disarm command."""
|
||||
if code:
|
||||
await self.gateway.api.alarmsystems.arm(
|
||||
await self.gateway.api.alarm_systems.arm(
|
||||
self.alarm_system_id, AlarmSystemArmAction.DISARM, code
|
||||
)
|
||||
|
@ -159,7 +159,7 @@ ENTITY_DESCRIPTIONS = {
|
||||
],
|
||||
}
|
||||
|
||||
BINARY_SENSOR_DESCRIPTIONS = [
|
||||
COMMON_BINARY_SENSOR_DESCRIPTIONS = [
|
||||
DeconzBinarySensorDescription(
|
||||
key="tampered",
|
||||
value_fn=lambda device: device.tampered,
|
||||
@ -215,7 +215,8 @@ async def async_setup_entry(
|
||||
sensor = gateway.api.sensors[sensor_id]
|
||||
|
||||
for description in (
|
||||
ENTITY_DESCRIPTIONS.get(type(sensor), []) + BINARY_SENSOR_DESCRIPTIONS
|
||||
ENTITY_DESCRIPTIONS.get(type(sensor), [])
|
||||
+ COMMON_BINARY_SENSOR_DESCRIPTIONS
|
||||
):
|
||||
if (
|
||||
not hasattr(sensor, description.key)
|
||||
@ -284,8 +285,8 @@ class DeconzBinarySensor(DeconzDevice, BinarySensorEntity):
|
||||
if self._device.on is not None:
|
||||
attr[ATTR_ON] = self._device.on
|
||||
|
||||
if self._device.secondary_temperature is not None:
|
||||
attr[ATTR_TEMPERATURE] = self._device.secondary_temperature
|
||||
if self._device.internal_temperature is not None:
|
||||
attr[ATTR_TEMPERATURE] = self._device.internal_temperature
|
||||
|
||||
if isinstance(self._device, Presence):
|
||||
|
||||
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
from typing import Any, cast
|
||||
|
||||
from pydeconz.interfaces.lights import CoverAction
|
||||
from pydeconz.models import ResourceType
|
||||
from pydeconz.models.event import EventType
|
||||
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
|
||||
|
||||
DECONZ_TYPE_TO_DEVICE_CLASS = {
|
||||
"Level controllable output": CoverDeviceClass.DAMPER,
|
||||
"Window covering controller": CoverDeviceClass.SHADE,
|
||||
"Window covering device": CoverDeviceClass.SHADE,
|
||||
ResourceType.LEVEL_CONTROLLABLE_OUTPUT.value: CoverDeviceClass.DAMPER,
|
||||
ResourceType.WINDOW_COVERING_CONTROLLER.value: 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.legacy_mode = cover.type == ResourceType.LEVEL_CONTROLLABLE_OUTPUT.value
|
||||
|
||||
@property
|
||||
def current_cover_position(self) -> int:
|
||||
"""Return the current position of the cover."""
|
||||
@ -88,6 +91,7 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
||||
await self.gateway.api.lights.covers.set_state(
|
||||
id=self._device.resource_id,
|
||||
lift=position,
|
||||
legacy_mode=self.legacy_mode,
|
||||
)
|
||||
|
||||
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(
|
||||
id=self._device.resource_id,
|
||||
action=CoverAction.OPEN,
|
||||
legacy_mode=self.legacy_mode,
|
||||
)
|
||||
|
||||
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(
|
||||
id=self._device.resource_id,
|
||||
action=CoverAction.CLOSE,
|
||||
legacy_mode=self.legacy_mode,
|
||||
)
|
||||
|
||||
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(
|
||||
id=self._device.resource_id,
|
||||
action=CoverAction.STOP,
|
||||
legacy_mode=self.legacy_mode,
|
||||
)
|
||||
|
||||
@property
|
||||
@ -124,6 +131,7 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
||||
await self.gateway.api.lights.covers.set_state(
|
||||
id=self._device.resource_id,
|
||||
tilt=position,
|
||||
legacy_mode=self.legacy_mode,
|
||||
)
|
||||
|
||||
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(
|
||||
id=self._device.resource_id,
|
||||
tilt=0,
|
||||
legacy_mode=self.legacy_mode,
|
||||
)
|
||||
|
||||
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(
|
||||
id=self._device.resource_id,
|
||||
tilt=100,
|
||||
legacy_mode=self.legacy_mode,
|
||||
)
|
||||
|
||||
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(
|
||||
id=self._device.resource_id,
|
||||
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
|
||||
)
|
||||
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["entities"] = gateway.entities
|
||||
@ -37,7 +37,7 @@ async def async_get_config_entry_diagnostics(
|
||||
}
|
||||
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["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()}
|
||||
|
@ -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)
|
||||
|
||||
initializing = False
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "deCONZ",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/deconz",
|
||||
"requirements": ["pydeconz==102"],
|
||||
"requirements": ["pydeconz==104"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Royal Philips Electronics",
|
||||
|
@ -6,7 +6,7 @@ from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
|
||||
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 (
|
||||
DOMAIN,
|
||||
@ -42,7 +42,7 @@ ENTITY_DESCRIPTIONS = {
|
||||
key="delay",
|
||||
value_fn=lambda device: device.delay,
|
||||
suffix="Delay",
|
||||
update_key=PRESENCE_DELAY,
|
||||
update_key="delay",
|
||||
native_max_value=65535,
|
||||
native_min_value=0,
|
||||
native_step=1,
|
||||
|
@ -209,7 +209,7 @@ ENTITY_DESCRIPTIONS = {
|
||||
}
|
||||
|
||||
|
||||
SENSOR_DESCRIPTIONS = [
|
||||
COMMON_SENSOR_DESCRIPTIONS = [
|
||||
DeconzSensorDescription(
|
||||
key="battery",
|
||||
value_fn=lambda device: device.battery,
|
||||
@ -221,8 +221,8 @@ SENSOR_DESCRIPTIONS = [
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
DeconzSensorDescription(
|
||||
key="secondary_temperature",
|
||||
value_fn=lambda device: device.secondary_temperature,
|
||||
key="internal_temperature",
|
||||
value_fn=lambda device: device.internal_temperature,
|
||||
suffix="Temperature",
|
||||
update_key="temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
@ -253,7 +253,7 @@ async def async_setup_entry(
|
||||
known_entities = set(gateway.entities[DOMAIN])
|
||||
|
||||
for description in (
|
||||
ENTITY_DESCRIPTIONS.get(type(sensor), []) + SENSOR_DESCRIPTIONS
|
||||
ENTITY_DESCRIPTIONS.get(type(sensor), []) + COMMON_SENSOR_DESCRIPTIONS
|
||||
):
|
||||
if (
|
||||
not hasattr(sensor, description.key)
|
||||
@ -342,8 +342,8 @@ class DeconzSensor(DeconzDevice, SensorEntity):
|
||||
if self._device.on is not None:
|
||||
attr[ATTR_ON] = self._device.on
|
||||
|
||||
if self._device.secondary_temperature is not None:
|
||||
attr[ATTR_TEMPERATURE] = self._device.secondary_temperature
|
||||
if self._device.internal_temperature is not None:
|
||||
attr[ATTR_TEMPERATURE] = self._device.internal_temperature
|
||||
|
||||
if isinstance(self._device, Consumption):
|
||||
attr[ATTR_POWER] = self._device.power
|
||||
@ -384,14 +384,16 @@ class DeconzBatteryTracker:
|
||||
self.sensor = gateway.api.sensors[sensor_id]
|
||||
self.gateway = gateway
|
||||
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
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the device's state."""
|
||||
if "battery" in self.sensor.changed_keys:
|
||||
self.unsub()
|
||||
self.unsubscribe()
|
||||
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:
|
||||
self.async_add_entities([entity])
|
||||
|
@ -22,6 +22,7 @@ from homeassistant.const import (
|
||||
ELECTRIC_POTENTIAL_VOLT,
|
||||
ENERGY_KILO_WATT_HOUR,
|
||||
ENERGY_WATT_HOUR,
|
||||
FREQUENCY_HERTZ,
|
||||
POWER_WATT,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
@ -252,6 +253,7 @@ SENSOR_UNIT_MAPPING = {
|
||||
"A": ELECTRIC_CURRENT_AMPERE,
|
||||
"V": ELECTRIC_POTENTIAL_VOLT,
|
||||
"°": DEGREE,
|
||||
"Hz": FREQUENCY_HERTZ,
|
||||
}
|
||||
|
||||
|
||||
|
@ -25,6 +25,7 @@ class GoodweNumberEntityDescriptionBase:
|
||||
|
||||
getter: Callable[[Inverter], Awaitable[int]]
|
||||
setter: Callable[[Inverter, int], Awaitable[None]]
|
||||
filter: Callable[[Inverter], bool]
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -35,17 +36,33 @@ class GoodweNumberEntityDescription(
|
||||
|
||||
|
||||
NUMBERS = (
|
||||
# non DT inverters (limit in W)
|
||||
GoodweNumberEntityDescription(
|
||||
key="grid_export_limit",
|
||||
name="Grid export limit",
|
||||
icon="mdi:transmission-tower",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
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_min_value=0,
|
||||
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(
|
||||
key="battery_discharge_depth",
|
||||
@ -53,11 +70,12 @@ NUMBERS = (
|
||||
icon="mdi:battery-arrow-down",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
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_min_value=0,
|
||||
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 = []
|
||||
|
||||
for description in NUMBERS:
|
||||
for description in filter(lambda dsc: dsc.filter(inverter), NUMBERS):
|
||||
try:
|
||||
current_value = await description.getter(inverter)
|
||||
except (InverterError, ValueError):
|
||||
@ -82,7 +100,7 @@ async def async_setup_entry(
|
||||
continue
|
||||
|
||||
entities.append(
|
||||
InverterNumberEntity(device_info, description, inverter, current_value),
|
||||
InverterNumberEntity(device_info, description, inverter, current_value)
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
@ -8,11 +8,15 @@ DEFAULT_PLANT_ID = "0"
|
||||
DEFAULT_NAME = "Growatt"
|
||||
|
||||
SERVER_URLS = [
|
||||
"https://server.growatt.com/",
|
||||
"https://server-api.growatt.com/",
|
||||
"https://server-us.growatt.com/",
|
||||
"http://server.smten.com/",
|
||||
]
|
||||
|
||||
DEPRECATED_URLS = [
|
||||
"https://server.growatt.com/",
|
||||
]
|
||||
|
||||
DEFAULT_URL = SERVER_URLS[0]
|
||||
|
||||
DOMAIN = "growatt_server"
|
||||
|
@ -19,6 +19,7 @@ from .const import (
|
||||
CONF_PLANT_ID,
|
||||
DEFAULT_PLANT_ID,
|
||||
DEFAULT_URL,
|
||||
DEPRECATED_URLS,
|
||||
DOMAIN,
|
||||
LOGIN_INVALID_AUTH_CODE,
|
||||
)
|
||||
@ -62,12 +63,23 @@ async def async_setup_entry(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Growatt sensor."""
|
||||
config = config_entry.data
|
||||
config = {**config_entry.data}
|
||||
username = config[CONF_USERNAME]
|
||||
password = config[CONF_PASSWORD]
|
||||
url = config.get(CONF_URL, DEFAULT_URL)
|
||||
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.server_url = url
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "KNX",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/knx",
|
||||
"requirements": ["xknx==1.0.0"],
|
||||
"requirements": ["xknx==1.0.1"],
|
||||
"codeowners": ["@Julius2342", "@farmio", "@marvin-w"],
|
||||
"quality_scale": "platinum",
|
||||
"iot_class": "local_push",
|
||||
|
@ -13,7 +13,7 @@ from homeassistant.const import (
|
||||
CONF_NAME,
|
||||
LENGTH_MILLIMETERS,
|
||||
PRESSURE_HPA,
|
||||
SPEED_METERS_PER_SECOND,
|
||||
SPEED_KILOMETERS_PER_HOUR,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -58,7 +58,7 @@ class MetEireannWeather(CoordinatorEntity, WeatherEntity):
|
||||
_attr_native_precipitation_unit = LENGTH_MILLIMETERS
|
||||
_attr_native_pressure_unit = PRESSURE_HPA
|
||||
_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):
|
||||
"""Initialise the platform with a data instance and site."""
|
||||
|
@ -188,7 +188,10 @@ class BlockSleepingClimate(
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
"""HVAC current mode."""
|
||||
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():
|
||||
return HVACMode.OFF
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
"domain": "switchbot",
|
||||
"name": "SwitchBot",
|
||||
"documentation": "https://www.home-assistant.io/integrations/switchbot",
|
||||
"requirements": ["PySwitchbot==0.18.10"],
|
||||
"requirements": ["PySwitchbot==0.18.14"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["bluetooth"],
|
||||
"codeowners": [
|
||||
|
@ -4,15 +4,15 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/zha",
|
||||
"requirements": [
|
||||
"bellows==0.32.0",
|
||||
"bellows==0.33.1",
|
||||
"pyserial==3.5",
|
||||
"pyserial-asyncio==0.6",
|
||||
"zha-quirks==0.0.78",
|
||||
"zigpy-deconz==0.18.0",
|
||||
"zigpy==0.49.1",
|
||||
"zigpy==0.50.2",
|
||||
"zigpy-xbee==0.15.0",
|
||||
"zigpy-zigate==0.9.1",
|
||||
"zigpy-znp==0.8.1"
|
||||
"zigpy-zigate==0.9.2",
|
||||
"zigpy-znp==0.8.2"
|
||||
],
|
||||
"usb": [
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ from .backports.enum import StrEnum
|
||||
|
||||
MAJOR_VERSION: Final = 2022
|
||||
MINOR_VERSION: Final = 8
|
||||
PATCH_VERSION: Final = "6"
|
||||
PATCH_VERSION: Final = "7"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)
|
||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2022.8.6"
|
||||
version = "2022.8.7"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
readme = "README.rst"
|
||||
|
@ -37,7 +37,7 @@ PyRMVtransport==0.3.3
|
||||
PySocks==1.7.1
|
||||
|
||||
# homeassistant.components.switchbot
|
||||
PySwitchbot==0.18.10
|
||||
PySwitchbot==0.18.14
|
||||
|
||||
# homeassistant.components.transport_nsw
|
||||
PyTransportNSW==0.1.1
|
||||
@ -396,7 +396,7 @@ beautifulsoup4==4.11.1
|
||||
# beewi_smartclim==0.0.10
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.32.0
|
||||
bellows==0.33.1
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer_connected==0.10.2
|
||||
@ -1455,7 +1455,7 @@ pydaikin==2.7.0
|
||||
pydanfossair==0.1.0
|
||||
|
||||
# homeassistant.components.deconz
|
||||
pydeconz==102
|
||||
pydeconz==104
|
||||
|
||||
# homeassistant.components.delijn
|
||||
pydelijn==1.0.0
|
||||
@ -2473,7 +2473,7 @@ xboxapi==2.0.1
|
||||
xiaomi-ble==0.6.4
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknx==1.0.0
|
||||
xknx==1.0.1
|
||||
|
||||
# homeassistant.components.bluesound
|
||||
# homeassistant.components.fritz
|
||||
@ -2529,13 +2529,13 @@ zigpy-deconz==0.18.0
|
||||
zigpy-xbee==0.15.0
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-zigate==0.9.1
|
||||
zigpy-zigate==0.9.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-znp==0.8.1
|
||||
zigpy-znp==0.8.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy==0.49.1
|
||||
zigpy==0.50.2
|
||||
|
||||
# homeassistant.components.zoneminder
|
||||
zm-py==0.5.2
|
||||
|
@ -33,7 +33,7 @@ PyRMVtransport==0.3.3
|
||||
PySocks==1.7.1
|
||||
|
||||
# homeassistant.components.switchbot
|
||||
PySwitchbot==0.18.10
|
||||
PySwitchbot==0.18.14
|
||||
|
||||
# homeassistant.components.transport_nsw
|
||||
PyTransportNSW==0.1.1
|
||||
@ -320,7 +320,7 @@ base36==0.1.1
|
||||
beautifulsoup4==4.11.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.32.0
|
||||
bellows==0.33.1
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer_connected==0.10.2
|
||||
@ -1001,7 +1001,7 @@ pycoolmasternet-async==0.1.2
|
||||
pydaikin==2.7.0
|
||||
|
||||
# homeassistant.components.deconz
|
||||
pydeconz==102
|
||||
pydeconz==104
|
||||
|
||||
# homeassistant.components.dexcom
|
||||
pydexcom==0.2.3
|
||||
@ -1665,7 +1665,7 @@ xbox-webapi==2.0.11
|
||||
xiaomi-ble==0.6.4
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknx==1.0.0
|
||||
xknx==1.0.1
|
||||
|
||||
# homeassistant.components.bluesound
|
||||
# homeassistant.components.fritz
|
||||
@ -1703,13 +1703,13 @@ zigpy-deconz==0.18.0
|
||||
zigpy-xbee==0.15.0
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-zigate==0.9.1
|
||||
zigpy-zigate==0.9.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-znp==0.8.1
|
||||
zigpy-znp==0.8.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy==0.49.1
|
||||
zigpy==0.50.2
|
||||
|
||||
# homeassistant.components.zwave_js
|
||||
zwave-js-server-python==0.39.0
|
||||
|
@ -11,6 +11,7 @@ DEVICE_CONFIG_OPEN = {
|
||||
"status": "open",
|
||||
"link_status": "Connected",
|
||||
"serial": "12345",
|
||||
"model": "02",
|
||||
}
|
||||
|
||||
|
||||
@ -31,6 +32,8 @@ def fixture_mock_aladdinconnect_api():
|
||||
mock_opener.get_battery_status.return_value = "99"
|
||||
mock_opener.async_get_rssi_status = AsyncMock(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.register_callback = mock.Mock(return_value=True)
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""Test the Aladdin Connect Sensors."""
|
||||
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.cover import SCAN_INTERVAL
|
||||
@ -10,6 +10,17 @@ from homeassistant.util.dt import utcnow
|
||||
|
||||
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"}
|
||||
RELOAD_AFTER_UPDATE_DELAY = timedelta(seconds=31)
|
||||
|
||||
@ -83,3 +94,71 @@ async def test_sensors(
|
||||
|
||||
state = hass.states.get("sensor.home_wi_fi_rssi")
|
||||
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 pydeconz.websocket import SIGNAL_CONNECTION_STATE, SIGNAL_DATA
|
||||
from pydeconz.websocket import Signal
|
||||
import pytest
|
||||
|
||||
from tests.components.light.conftest import mock_light_profiles # noqa: F401
|
||||
@ -20,10 +20,10 @@ def mock_deconz_websocket():
|
||||
|
||||
if data:
|
||||
mock.return_value.data = data
|
||||
await pydeconz_gateway_session_handler(signal=SIGNAL_DATA)
|
||||
await pydeconz_gateway_session_handler(signal=Signal.DATA)
|
||||
elif 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:
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -213,3 +213,111 @@ async def test_tilt_cover(hass, aioclient_mock):
|
||||
blocking=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."""
|
||||
|
||||
from pydeconz.websocket import STATE_RUNNING
|
||||
from pydeconz.websocket import State
|
||||
|
||||
from homeassistant.components.deconz.const import CONF_MASTER_GATEWAY
|
||||
from homeassistant.components.diagnostics import REDACTED
|
||||
@ -17,7 +17,7 @@ async def test_entry_diagnostics(
|
||||
"""Test config entry diagnostics."""
|
||||
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()
|
||||
|
||||
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
|
||||
@ -44,7 +44,7 @@ async def test_entry_diagnostics(
|
||||
"uuid": "1234",
|
||||
"websocketport": 1234,
|
||||
},
|
||||
"websocket_state": STATE_RUNNING,
|
||||
"websocket_state": State.RUNNING.value,
|
||||
"deconz_ids": {},
|
||||
"entities": {
|
||||
str(Platform.ALARM_CONTROL_PANEL): [],
|
||||
|
@ -5,7 +5,7 @@ from copy import deepcopy
|
||||
from unittest.mock import patch
|
||||
|
||||
import pydeconz
|
||||
from pydeconz.websocket import STATE_RETRYING, STATE_RUNNING
|
||||
from pydeconz.websocket import State
|
||||
import pytest
|
||||
|
||||
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
|
||||
|
||||
await mock_deconz_websocket(state=STATE_RETRYING)
|
||||
await mock_deconz_websocket(state=State.RETRYING)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
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()
|
||||
|
||||
assert hass.states.get("binary_sensor.presence").state == STATE_OFF
|
||||
|
Loading…
x
Reference in New Issue
Block a user