mirror of
https://github.com/home-assistant/core.git
synced 2025-08-05 05:35:11 +02:00
Merge pull request #68493 from home-assistant/rc
This commit is contained in:
@@ -149,9 +149,19 @@ def _async_register_mac(
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Make sure entity has a config entry and was disabled by the
|
# Make sure entity has a config entry and was disabled by the
|
||||||
# default disable logic in the integration.
|
# default disable logic in the integration and new entities
|
||||||
|
# are allowed to be added.
|
||||||
if (
|
if (
|
||||||
entity_entry.config_entry_id is None
|
entity_entry.config_entry_id is None
|
||||||
|
or (
|
||||||
|
(
|
||||||
|
config_entry := hass.config_entries.async_get_entry(
|
||||||
|
entity_entry.config_entry_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
is not None
|
||||||
|
and config_entry.pref_disable_new_entities
|
||||||
|
)
|
||||||
or entity_entry.disabled_by != er.RegistryEntryDisabler.INTEGRATION
|
or entity_entry.disabled_by != er.RegistryEntryDisabler.INTEGRATION
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
@@ -16,6 +16,7 @@ from homeassistant.core import HomeAssistant, callback
|
|||||||
from homeassistant.data_entry_flow import FlowResult
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.httpx_client import get_async_client
|
from homeassistant.helpers.httpx_client import get_async_client
|
||||||
|
from homeassistant.util.network import is_ipv4_address
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
@@ -86,6 +87,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self, discovery_info: zeroconf.ZeroconfServiceInfo
|
self, discovery_info: zeroconf.ZeroconfServiceInfo
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Handle a flow initialized by zeroconf discovery."""
|
"""Handle a flow initialized by zeroconf discovery."""
|
||||||
|
if not is_ipv4_address(discovery_info.host):
|
||||||
|
return self.async_abort(reason="not_ipv4_address")
|
||||||
serial = discovery_info.properties["serialnum"]
|
serial = discovery_info.properties["serialnum"]
|
||||||
await self.async_set_unique_id(serial)
|
await self.async_set_unique_id(serial)
|
||||||
self.ip_address = discovery_info.host
|
self.ip_address = discovery_info.host
|
||||||
|
@@ -2,7 +2,8 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Device is already configured",
|
"already_configured": "Device is already configured",
|
||||||
"reauth_successful": "Re-authentication was successful"
|
"reauth_successful": "Re-authentication was successful",
|
||||||
|
"not_ipv4_address": "Only IPv4 addresess are supported"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "Failed to connect",
|
"cannot_connect": "Failed to connect",
|
||||||
|
@@ -6,6 +6,7 @@ import logging
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
from aiohue import LinkButtonNotPressed, create_app_key
|
from aiohue import LinkButtonNotPressed, create_app_key
|
||||||
from aiohue.discovery import DiscoveredHueBridge, discover_bridge, discover_nupnp
|
from aiohue.discovery import DiscoveredHueBridge, discover_bridge, discover_nupnp
|
||||||
from aiohue.util import normalize_bridge_id
|
from aiohue.util import normalize_bridge_id
|
||||||
@@ -70,9 +71,12 @@ class HueFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self, host: str, bridge_id: str | None = None
|
self, host: str, bridge_id: str | None = None
|
||||||
) -> DiscoveredHueBridge:
|
) -> DiscoveredHueBridge:
|
||||||
"""Return a DiscoveredHueBridge object."""
|
"""Return a DiscoveredHueBridge object."""
|
||||||
bridge = await discover_bridge(
|
try:
|
||||||
host, websession=aiohttp_client.async_get_clientsession(self.hass)
|
bridge = await discover_bridge(
|
||||||
)
|
host, websession=aiohttp_client.async_get_clientsession(self.hass)
|
||||||
|
)
|
||||||
|
except aiohttp.ClientError:
|
||||||
|
return None
|
||||||
if bridge_id is not None:
|
if bridge_id is not None:
|
||||||
bridge_id = normalize_bridge_id(bridge_id)
|
bridge_id = normalize_bridge_id(bridge_id)
|
||||||
assert bridge_id == bridge.id
|
assert bridge_id == bridge.id
|
||||||
|
@@ -42,6 +42,14 @@ ALLOWED_ERRORS = [
|
|||||||
'device (groupedLight) is "soft off", command (on) may not have effect',
|
'device (groupedLight) is "soft off", command (on) may not have effect',
|
||||||
"device (light) has communication issues, command (on) may not have effect",
|
"device (light) has communication issues, command (on) may not have effect",
|
||||||
'device (light) is "soft off", command (on) may not have effect',
|
'device (light) is "soft off", command (on) may not have effect',
|
||||||
|
"device (grouped_light) has communication issues, command (.on) may not have effect",
|
||||||
|
'device (grouped_light) is "soft off", command (.on) may not have effect'
|
||||||
|
"device (grouped_light) has communication issues, command (.on.on) may not have effect",
|
||||||
|
'device (grouped_light) is "soft off", command (.on.on) may not have effect'
|
||||||
|
"device (light) has communication issues, command (.on) may not have effect",
|
||||||
|
'device (light) is "soft off", command (.on) may not have effect',
|
||||||
|
"device (light) has communication issues, command (.on.on) may not have effect",
|
||||||
|
'device (light) is "soft off", command (.on.on) may not have effect',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@@ -39,6 +39,10 @@ from .helpers import (
|
|||||||
ALLOWED_ERRORS = [
|
ALLOWED_ERRORS = [
|
||||||
"device (light) has communication issues, command (on) may not have effect",
|
"device (light) has communication issues, command (on) may not have effect",
|
||||||
'device (light) is "soft off", command (on) may not have effect',
|
'device (light) is "soft off", command (on) may not have effect',
|
||||||
|
"device (light) has communication issues, command (.on) may not have effect",
|
||||||
|
'device (light) is "soft off", command (.on) may not have effect',
|
||||||
|
"device (light) has communication issues, command (.on.on) may not have effect",
|
||||||
|
'device (light) is "soft off", command (.on.on) may not have effect',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
"""Support for Honeywell Lyric climate platform."""
|
"""Support for Honeywell Lyric climate platform."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from time import localtime, strftime, time
|
from time import localtime, strftime, time
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ from homeassistant.components.climate.const import (
|
|||||||
HVAC_MODE_OFF,
|
HVAC_MODE_OFF,
|
||||||
SUPPORT_PRESET_MODE,
|
SUPPORT_PRESET_MODE,
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
SUPPORT_TARGET_TEMPERATURE,
|
||||||
|
SUPPORT_TARGET_TEMPERATURE_RANGE,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import ATTR_TEMPERATURE
|
from homeassistant.const import ATTR_TEMPERATURE
|
||||||
@@ -45,7 +47,11 @@ from .const import (
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
|
# Only LCC models support presets
|
||||||
|
SUPPORT_FLAGS_LCC = (
|
||||||
|
SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE | SUPPORT_TARGET_TEMPERATURE_RANGE
|
||||||
|
)
|
||||||
|
SUPPORT_FLAGS_TCC = SUPPORT_TARGET_TEMPERATURE | SUPPORT_TARGET_TEMPERATURE_RANGE
|
||||||
|
|
||||||
LYRIC_HVAC_ACTION_OFF = "EquipmentOff"
|
LYRIC_HVAC_ACTION_OFF = "EquipmentOff"
|
||||||
LYRIC_HVAC_ACTION_HEAT = "Heat"
|
LYRIC_HVAC_ACTION_HEAT = "Heat"
|
||||||
@@ -166,7 +172,11 @@ class LyricClimate(LyricDeviceEntity, ClimateEntity):
|
|||||||
@property
|
@property
|
||||||
def supported_features(self) -> int:
|
def supported_features(self) -> int:
|
||||||
"""Return the list of supported features."""
|
"""Return the list of supported features."""
|
||||||
return SUPPORT_FLAGS
|
if self.device.changeableValues.thermostatSetpointStatus:
|
||||||
|
support_flags = SUPPORT_FLAGS_LCC
|
||||||
|
else:
|
||||||
|
support_flags = SUPPORT_FLAGS_TCC
|
||||||
|
return support_flags
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def temperature_unit(self) -> str:
|
def temperature_unit(self) -> str:
|
||||||
@@ -200,25 +210,28 @@ class LyricClimate(LyricDeviceEntity, ClimateEntity):
|
|||||||
def target_temperature(self) -> float | None:
|
def target_temperature(self) -> float | None:
|
||||||
"""Return the temperature we try to reach."""
|
"""Return the temperature we try to reach."""
|
||||||
device = self.device
|
device = self.device
|
||||||
if not device.hasDualSetpointStatus:
|
if (
|
||||||
|
not device.changeableValues.autoChangeoverActive
|
||||||
|
and HVAC_MODES[device.changeableValues.mode] != HVAC_MODE_OFF
|
||||||
|
):
|
||||||
if self.hvac_mode == HVAC_MODE_COOL:
|
if self.hvac_mode == HVAC_MODE_COOL:
|
||||||
return device.changeableValues.coolSetpoint
|
return device.changeableValues.coolSetpoint
|
||||||
return device.changeableValues.heatSetpoint
|
return device.changeableValues.heatSetpoint
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_temperature_low(self) -> float | None:
|
def target_temperature_high(self) -> float | None:
|
||||||
"""Return the upper bound temperature we try to reach."""
|
"""Return the highbound target temperature we try to reach."""
|
||||||
device = self.device
|
device = self.device
|
||||||
if device.hasDualSetpointStatus:
|
if device.changeableValues.autoChangeoverActive:
|
||||||
return device.changeableValues.coolSetpoint
|
return device.changeableValues.coolSetpoint
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_temperature_high(self) -> float | None:
|
def target_temperature_low(self) -> float | None:
|
||||||
"""Return the upper bound temperature we try to reach."""
|
"""Return the lowbound target temperature we try to reach."""
|
||||||
device = self.device
|
device = self.device
|
||||||
if device.hasDualSetpointStatus:
|
if device.changeableValues.autoChangeoverActive:
|
||||||
return device.changeableValues.heatSetpoint
|
return device.changeableValues.heatSetpoint
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -256,11 +269,11 @@ class LyricClimate(LyricDeviceEntity, ClimateEntity):
|
|||||||
|
|
||||||
async def async_set_temperature(self, **kwargs) -> None:
|
async def async_set_temperature(self, **kwargs) -> None:
|
||||||
"""Set new target temperature."""
|
"""Set new target temperature."""
|
||||||
|
device = self.device
|
||||||
target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
|
target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
|
||||||
target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
|
target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
|
||||||
|
|
||||||
device = self.device
|
if device.changeableValues.autoChangeoverActive:
|
||||||
if device.hasDualSetpointStatus:
|
|
||||||
if target_temp_low is None or target_temp_high is None:
|
if target_temp_low is None or target_temp_high is None:
|
||||||
raise HomeAssistantError(
|
raise HomeAssistantError(
|
||||||
"Could not find target_temp_low and/or target_temp_high in arguments"
|
"Could not find target_temp_low and/or target_temp_high in arguments"
|
||||||
@@ -270,11 +283,13 @@ class LyricClimate(LyricDeviceEntity, ClimateEntity):
|
|||||||
await self._update_thermostat(
|
await self._update_thermostat(
|
||||||
self.location,
|
self.location,
|
||||||
device,
|
device,
|
||||||
coolSetpoint=target_temp_low,
|
coolSetpoint=target_temp_high,
|
||||||
heatSetpoint=target_temp_high,
|
heatSetpoint=target_temp_low,
|
||||||
|
mode=HVAC_MODES[device.changeableValues.heatCoolMode],
|
||||||
)
|
)
|
||||||
except LYRIC_EXCEPTIONS as exception:
|
except LYRIC_EXCEPTIONS as exception:
|
||||||
_LOGGER.error(exception)
|
_LOGGER.error(exception)
|
||||||
|
await self.coordinator.async_refresh()
|
||||||
else:
|
else:
|
||||||
temp = kwargs.get(ATTR_TEMPERATURE)
|
temp = kwargs.get(ATTR_TEMPERATURE)
|
||||||
_LOGGER.debug("Set temperature: %s", temp)
|
_LOGGER.debug("Set temperature: %s", temp)
|
||||||
@@ -289,15 +304,58 @@ class LyricClimate(LyricDeviceEntity, ClimateEntity):
|
|||||||
)
|
)
|
||||||
except LYRIC_EXCEPTIONS as exception:
|
except LYRIC_EXCEPTIONS as exception:
|
||||||
_LOGGER.error(exception)
|
_LOGGER.error(exception)
|
||||||
await self.coordinator.async_refresh()
|
await self.coordinator.async_refresh()
|
||||||
|
|
||||||
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
|
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
|
||||||
"""Set hvac mode."""
|
"""Set hvac mode."""
|
||||||
_LOGGER.debug("Set hvac mode: %s", hvac_mode)
|
_LOGGER.debug("HVAC mode: %s", hvac_mode)
|
||||||
try:
|
try:
|
||||||
await self._update_thermostat(
|
if LYRIC_HVAC_MODES[hvac_mode] == LYRIC_HVAC_MODE_HEAT_COOL:
|
||||||
self.location, self.device, mode=LYRIC_HVAC_MODES[hvac_mode]
|
# If the system is off, turn it to Heat first then to Auto, otherwise it turns to
|
||||||
)
|
# Auto briefly and then reverts to Off (perhaps related to heatCoolMode). This is the
|
||||||
|
# behavior that happens with the native app as well, so likely a bug in the api itself
|
||||||
|
|
||||||
|
if HVAC_MODES[self.device.changeableValues.mode] == HVAC_MODE_OFF:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"HVAC mode passed to lyric: %s",
|
||||||
|
HVAC_MODES[LYRIC_HVAC_MODE_COOL],
|
||||||
|
)
|
||||||
|
await self._update_thermostat(
|
||||||
|
self.location,
|
||||||
|
self.device,
|
||||||
|
mode=HVAC_MODES[LYRIC_HVAC_MODE_HEAT],
|
||||||
|
autoChangeoverActive=False,
|
||||||
|
)
|
||||||
|
# Sleep 3 seconds before proceeding
|
||||||
|
await asyncio.sleep(3)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"HVAC mode passed to lyric: %s",
|
||||||
|
HVAC_MODES[LYRIC_HVAC_MODE_HEAT],
|
||||||
|
)
|
||||||
|
await self._update_thermostat(
|
||||||
|
self.location,
|
||||||
|
self.device,
|
||||||
|
mode=HVAC_MODES[LYRIC_HVAC_MODE_HEAT],
|
||||||
|
autoChangeoverActive=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"HVAC mode passed to lyric: %s",
|
||||||
|
HVAC_MODES[self.device.changeableValues.mode],
|
||||||
|
)
|
||||||
|
await self._update_thermostat(
|
||||||
|
self.location, self.device, autoChangeoverActive=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"HVAC mode passed to lyric: %s", LYRIC_HVAC_MODES[hvac_mode]
|
||||||
|
)
|
||||||
|
await self._update_thermostat(
|
||||||
|
self.location,
|
||||||
|
self.device,
|
||||||
|
mode=LYRIC_HVAC_MODES[hvac_mode],
|
||||||
|
autoChangeoverActive=False,
|
||||||
|
)
|
||||||
except LYRIC_EXCEPTIONS as exception:
|
except LYRIC_EXCEPTIONS as exception:
|
||||||
_LOGGER.error(exception)
|
_LOGGER.error(exception)
|
||||||
await self.coordinator.async_refresh()
|
await self.coordinator.async_refresh()
|
||||||
|
@@ -243,7 +243,10 @@ class MatrixBot:
|
|||||||
room.update_aliases()
|
room.update_aliases()
|
||||||
self._aliases_fetched_for.add(room.room_id)
|
self._aliases_fetched_for.add(room.room_id)
|
||||||
|
|
||||||
if room_id_or_alias in room.aliases:
|
if (
|
||||||
|
room_id_or_alias in room.aliases
|
||||||
|
or room_id_or_alias == room.canonical_alias
|
||||||
|
):
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Already in room %s (known as %s)", room.room_id, room_id_or_alias
|
"Already in room %s (known as %s)", room.room_id, room_id_or_alias
|
||||||
)
|
)
|
||||||
|
@@ -43,7 +43,7 @@ async def async_setup_platform(
|
|||||||
station_id = config[CONF_STATION_ID]
|
station_id = config[CONF_STATION_ID]
|
||||||
|
|
||||||
session = async_get_clientsession(hass)
|
session = async_get_clientsession(hass)
|
||||||
osm_api = OpenSenseMapData(OpenSenseMap(station_id, hass.loop, session))
|
osm_api = OpenSenseMapData(OpenSenseMap(station_id, session))
|
||||||
|
|
||||||
await osm_api.async_update()
|
await osm_api.async_update()
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"domain": "opensensemap",
|
"domain": "opensensemap",
|
||||||
"name": "openSenseMap",
|
"name": "openSenseMap",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/opensensemap",
|
"documentation": "https://www.home-assistant.io/integrations/opensensemap",
|
||||||
"requirements": ["opensensemap-api==0.1.5"],
|
"requirements": ["opensensemap-api==0.2.0"],
|
||||||
"codeowners": [],
|
"codeowners": [],
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["opensensemap_api"]
|
"loggers": ["opensensemap_api"]
|
||||||
|
@@ -97,6 +97,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
token_saver=token_saver,
|
token_saver=token_saver,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
|
# pylint: disable-next=fixme
|
||||||
|
# TODO Remove authlib constraint when refactoring this code
|
||||||
await session.ensure_active_token()
|
await session.ensure_active_token()
|
||||||
except ConnectTimeout as err:
|
except ConnectTimeout as err:
|
||||||
_LOGGER.debug("Connection Timeout")
|
_LOGGER.debug("Connection Timeout")
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/renault",
|
"documentation": "https://www.home-assistant.io/integrations/renault",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"renault-api==0.1.9"
|
"renault-api==0.1.10"
|
||||||
],
|
],
|
||||||
"codeowners": [
|
"codeowners": [
|
||||||
"@epenet"
|
"@epenet"
|
||||||
|
@@ -319,8 +319,9 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
|||||||
def _get_app_list(self) -> dict[str, str] | None:
|
def _get_app_list(self) -> dict[str, str] | None:
|
||||||
"""Get installed app list."""
|
"""Get installed app list."""
|
||||||
if self._app_list is None and (remote := self._get_remote()):
|
if self._app_list is None and (remote := self._get_remote()):
|
||||||
with contextlib.suppress(WebSocketTimeoutException):
|
with contextlib.suppress(TypeError, WebSocketTimeoutException):
|
||||||
raw_app_list: list[dict[str, str]] = remote.app_list()
|
raw_app_list: list[dict[str, str]] = remote.app_list()
|
||||||
|
LOGGER.debug("Received app list: %s", raw_app_list)
|
||||||
self._app_list = {
|
self._app_list = {
|
||||||
app["name"]: app["appId"]
|
app["name"]: app["appId"]
|
||||||
for app in sorted(raw_app_list, key=lambda app: app["name"])
|
for app in sorted(raw_app_list, key=lambda app: app["name"])
|
||||||
|
@@ -88,7 +88,10 @@ class TPLinkSmartBulb(CoordinatedTPLinkEntity, LightEntity):
|
|||||||
|
|
||||||
# Handle turning to temp mode
|
# Handle turning to temp mode
|
||||||
if ATTR_COLOR_TEMP in kwargs:
|
if ATTR_COLOR_TEMP in kwargs:
|
||||||
color_tmp = mired_to_kelvin(int(kwargs[ATTR_COLOR_TEMP]))
|
# Handle temp conversion mireds -> kelvin being slightly outside of valid range
|
||||||
|
kelvin = mired_to_kelvin(int(kwargs[ATTR_COLOR_TEMP]))
|
||||||
|
kelvin_range = self.device.valid_temperature_range
|
||||||
|
color_tmp = max(kelvin_range.min, min(kelvin_range.max, kelvin))
|
||||||
_LOGGER.debug("Changing color temp to %s", color_tmp)
|
_LOGGER.debug("Changing color temp to %s", color_tmp)
|
||||||
await self.device.set_color_temp(
|
await self.device.set_color_temp(
|
||||||
color_tmp, brightness=brightness, transition=transition
|
color_tmp, brightness=brightness, transition=transition
|
||||||
|
@@ -78,4 +78,4 @@ class VelbusCover(VelbusEntity, CoverEntity):
|
|||||||
|
|
||||||
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
||||||
"""Move the cover to a specific position."""
|
"""Move the cover to a specific position."""
|
||||||
self._channel.set_position(100 - kwargs[ATTR_POSITION])
|
await self._channel.set_position(100 - kwargs[ATTR_POSITION])
|
||||||
|
@@ -7,7 +7,7 @@ from .backports.enum import StrEnum
|
|||||||
|
|
||||||
MAJOR_VERSION: Final = 2022
|
MAJOR_VERSION: Final = 2022
|
||||||
MINOR_VERSION: Final = 3
|
MINOR_VERSION: Final = 3
|
||||||
PATCH_VERSION: Final = "5"
|
PATCH_VERSION: Final = "6"
|
||||||
__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)
|
||||||
|
@@ -95,3 +95,7 @@ python-socketio>=4.6.0,<5.0
|
|||||||
# Constrain multidict to avoid typing issues
|
# Constrain multidict to avoid typing issues
|
||||||
# https://github.com/home-assistant/core/pull/67046
|
# https://github.com/home-assistant/core/pull/67046
|
||||||
multidict>=6.0.2
|
multidict>=6.0.2
|
||||||
|
|
||||||
|
# Required for compatibility with point integration - ensure_active_token
|
||||||
|
# https://github.com/home-assistant/core/pull/68176
|
||||||
|
authlib<1.0
|
||||||
|
@@ -1180,7 +1180,7 @@ openevsewifi==1.1.0
|
|||||||
openhomedevice==2.0.1
|
openhomedevice==2.0.1
|
||||||
|
|
||||||
# homeassistant.components.opensensemap
|
# homeassistant.components.opensensemap
|
||||||
opensensemap-api==0.1.5
|
opensensemap-api==0.2.0
|
||||||
|
|
||||||
# homeassistant.components.enigma2
|
# homeassistant.components.enigma2
|
||||||
openwebifpy==3.2.7
|
openwebifpy==3.2.7
|
||||||
@@ -2097,7 +2097,7 @@ raspyrfm-client==1.2.8
|
|||||||
regenmaschine==2022.01.0
|
regenmaschine==2022.01.0
|
||||||
|
|
||||||
# homeassistant.components.renault
|
# homeassistant.components.renault
|
||||||
renault-api==0.1.9
|
renault-api==0.1.10
|
||||||
|
|
||||||
# homeassistant.components.python_script
|
# homeassistant.components.python_script
|
||||||
restrictedpython==5.2
|
restrictedpython==5.2
|
||||||
|
@@ -1301,7 +1301,7 @@ radios==0.1.1
|
|||||||
regenmaschine==2022.01.0
|
regenmaschine==2022.01.0
|
||||||
|
|
||||||
# homeassistant.components.renault
|
# homeassistant.components.renault
|
||||||
renault-api==0.1.9
|
renault-api==0.1.10
|
||||||
|
|
||||||
# homeassistant.components.python_script
|
# homeassistant.components.python_script
|
||||||
restrictedpython==5.2
|
restrictedpython==5.2
|
||||||
|
@@ -124,6 +124,10 @@ python-socketio>=4.6.0,<5.0
|
|||||||
# Constrain multidict to avoid typing issues
|
# Constrain multidict to avoid typing issues
|
||||||
# https://github.com/home-assistant/core/pull/67046
|
# https://github.com/home-assistant/core/pull/67046
|
||||||
multidict>=6.0.2
|
multidict>=6.0.2
|
||||||
|
|
||||||
|
# Required for compatibility with point integration - ensure_active_token
|
||||||
|
# https://github.com/home-assistant/core/pull/68176
|
||||||
|
authlib<1.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
IGNORE_PRE_COMMIT_HOOK_ID = (
|
IGNORE_PRE_COMMIT_HOOK_ID = (
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = homeassistant
|
name = homeassistant
|
||||||
version = 2022.3.5
|
version = 2022.3.6
|
||||||
author = The Home Assistant Authors
|
author = The Home Assistant Authors
|
||||||
author_email = hello@home-assistant.io
|
author_email = hello@home-assistant.io
|
||||||
license = Apache-2.0
|
license = Apache-2.0
|
||||||
|
@@ -137,6 +137,39 @@ async def test_register_mac(hass):
|
|||||||
assert entity_entry_1.disabled_by is None
|
assert entity_entry_1.disabled_by is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_register_mac_ignored(hass):
|
||||||
|
"""Test ignoring registering a mac."""
|
||||||
|
dev_reg = dr.async_get(hass)
|
||||||
|
ent_reg = er.async_get(hass)
|
||||||
|
|
||||||
|
config_entry = MockConfigEntry(domain="test", pref_disable_new_entities=True)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
mac1 = "12:34:56:AB:CD:EF"
|
||||||
|
|
||||||
|
entity_entry_1 = ent_reg.async_get_or_create(
|
||||||
|
"device_tracker",
|
||||||
|
"test",
|
||||||
|
mac1 + "yo1",
|
||||||
|
original_name="name 1",
|
||||||
|
config_entry=config_entry,
|
||||||
|
disabled_by=er.RegistryEntryDisabler.INTEGRATION,
|
||||||
|
)
|
||||||
|
|
||||||
|
ce._async_register_mac(hass, "test", mac1, mac1 + "yo1")
|
||||||
|
|
||||||
|
dev_reg.async_get_or_create(
|
||||||
|
config_entry_id=config_entry.entry_id,
|
||||||
|
connections={(dr.CONNECTION_NETWORK_MAC, mac1)},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entity_entry_1 = ent_reg.async_get(entity_entry_1.entity_id)
|
||||||
|
|
||||||
|
assert entity_entry_1.disabled_by == er.RegistryEntryDisabler.INTEGRATION
|
||||||
|
|
||||||
|
|
||||||
async def test_connected_device_registered(hass):
|
async def test_connected_device_registered(hass):
|
||||||
"""Test dispatch on connected device being registered."""
|
"""Test dispatch on connected device being registered."""
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ import httpx
|
|||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components import zeroconf
|
from homeassistant.components import zeroconf
|
||||||
from homeassistant.components.enphase_envoy.const import DOMAIN
|
from homeassistant.components.enphase_envoy.const import DOMAIN
|
||||||
|
from homeassistant.const import CONF_HOST
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
@@ -312,8 +313,8 @@ async def test_zeroconf_serial_already_exists(hass: HomeAssistant) -> None:
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||||
data=zeroconf.ZeroconfServiceInfo(
|
data=zeroconf.ZeroconfServiceInfo(
|
||||||
host="1.1.1.1",
|
host="4.4.4.4",
|
||||||
addresses=["1.1.1.1"],
|
addresses=["4.4.4.4"],
|
||||||
hostname="mock_hostname",
|
hostname="mock_hostname",
|
||||||
name="mock_name",
|
name="mock_name",
|
||||||
port=None,
|
port=None,
|
||||||
@@ -324,6 +325,42 @@ async def test_zeroconf_serial_already_exists(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
assert result["type"] == "abort"
|
assert result["type"] == "abort"
|
||||||
assert result["reason"] == "already_configured"
|
assert result["reason"] == "already_configured"
|
||||||
|
assert config_entry.data[CONF_HOST] == "4.4.4.4"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_zeroconf_serial_already_exists_ignores_ipv6(hass: HomeAssistant) -> None:
|
||||||
|
"""Test serial number already exists from zeroconf but the discovery is ipv6."""
|
||||||
|
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
"host": "1.1.1.1",
|
||||||
|
"name": "Envoy",
|
||||||
|
"username": "test-username",
|
||||||
|
"password": "test-password",
|
||||||
|
},
|
||||||
|
unique_id="1234",
|
||||||
|
title="Envoy",
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||||
|
data=zeroconf.ZeroconfServiceInfo(
|
||||||
|
host="fd00::b27c:63bb:cc85:4ea0",
|
||||||
|
addresses=["fd00::b27c:63bb:cc85:4ea0"],
|
||||||
|
hostname="mock_hostname",
|
||||||
|
name="mock_name",
|
||||||
|
port=None,
|
||||||
|
properties={"serialnum": "1234"},
|
||||||
|
type="mock_type",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "not_ipv4_address"
|
||||||
|
assert config_entry.data[CONF_HOST] == "1.1.1.1"
|
||||||
|
|
||||||
|
|
||||||
async def test_zeroconf_host_already_exists(hass: HomeAssistant) -> None:
|
async def test_zeroconf_host_already_exists(hass: HomeAssistant) -> None:
|
||||||
|
Reference in New Issue
Block a user