Compare commits

...

29 Commits

Author SHA1 Message Date
J. Nick Koston 974b5ece19 Verify state_changed unsubscribe is invoked on overflow
Add a spy on async_track_state_change_event so the test directly
asserts the live stream's unsubscribe callable is invoked when the
queue overflows, instead of relying on the (unreliable) shared
EVENT_STATE_CHANGED bus listener count.
2026-05-21 14:21:35 -05:00
J. Nick Koston 47d21a7204 Fix flaky test_overflow_queue in history websocket tests
Comparing the full bus listener dict can fail when unrelated components
asynchronously add or remove EVENT_STATE_CHANGED listeners during the
test; capture listeners before and after via a context manager that
excludes state_changed.
2026-05-21 14:08:03 -05:00
Lukas e3dd6b5fc5 Fix hardcoded exception strings in pooldose integration (#171652)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 20:36:17 +02:00
Ariel Ebersberger 94d620438b Use is/is not for same-enum identity comparisons (source) (#171591) 2026-05-21 19:30:55 +02:00
Erik Montnemery 8867b792dc Remove use of advanced mode from the zha integration (#171753) 2026-05-21 19:26:24 +02:00
G Johansson 97967abfeb Fix missing string in smhi (#171756) 2026-05-21 19:25:03 +02:00
mhuiskes af8fea272d Declare Bronze quality scale for Zeversolar integration (#170410) 2026-05-21 19:12:54 +02:00
Simone Chemelli 2db0eed570 Fix hardcoded exception strings in samsungtv (#171745) 2026-05-21 18:59:37 +02:00
Erwin Douna ded1628c20 Downloader add missing data description (#171727) 2026-05-21 18:59:24 +02:00
Petro31 a02e54f332 Update documentation link to point to each domain/platform (#171734)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 18:47:39 +02:00
Erik Montnemery 1858649bc7 Improve tests of trigger variables (#171742) 2026-05-21 17:55:41 +02:00
Leonardo Merza 109e09c3ec Add fan minimum on time number entity to ecobee (#171419)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 17:53:02 +02:00
Manu ad139b259b Add notify entity to System Bridge integration (#171736)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 17:49:25 +02:00
Scott Giminiani a1a76874fd Fix name of config flow form field (#171741) 2026-05-21 17:33:53 +02:00
Ariel Ebersberger e7bd56325b Use is for IntentResponseType identity check in conversation (#171699)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: arturpragacz <49985303+arturpragacz@users.noreply.github.com>
2026-05-21 17:31:14 +02:00
J. Nick Koston ef2ef0c8ba Bump zeroconf to 0.149.16 (#171737) 2026-05-21 17:28:26 +02:00
Max Michels a8381e923a Replace duplicate constants with homeassistant.const imports (#171675) 2026-05-21 17:13:31 +02:00
Max Michels b7adba559b Replace duplicate constants with homeassistant.const imports (#171677) 2026-05-21 17:13:20 +02:00
Matthias Alphart 5cf6dceb04 Normalize empty string to None in knx config flow (#171693) 2026-05-21 17:13:11 +02:00
Paul Bottein 975bcc5431 Reorganize Freebox entity categories (#171480) 2026-05-21 16:55:27 +02:00
Michael Barrett f24a44e81f Update aioghost to 0.4.16 (#171690) 2026-05-21 16:53:45 +02:00
Phil-Rad 43c91843cd Remove unreachable import config flow path from cert_expiry (#171733) 2026-05-21 16:50:49 +02:00
Chris dbce1d328a Bump python-openevse-http to 0.3.4 (#171621) 2026-05-21 16:46:54 +02:00
Petro31 d294b04b79 Add EntityComponent to device_tracker (#171507) 2026-05-21 16:10:20 +02:00
Markus Tuominen 8b0e9060b3 Set _attr_has_entity_name on tplink_omada OmadaClientScannerEntity (#171680) 2026-05-21 16:41:37 +03:00
MoonDevLT 39066b6e3a Fix missing exceptions translation key missing_device_info in lunatone (#171569) 2026-05-21 14:59:48 +02:00
Max Michels a23a9b350b Replace duplicate constants with homeassistant.const imports (#171701) 2026-05-21 14:57:58 +02:00
chiro79 fdaa807ca8 Switch to aiopvpc-ng (#171025) 2026-05-21 14:54:23 +02:00
A. Gideonse f290dcc03f Update Indevolt integration quality scale to platinum (#170320) 2026-05-21 14:53:06 +02:00
224 changed files with 1777 additions and 658 deletions
Generated
+2 -2
View File
@@ -1413,8 +1413,8 @@ CLAUDE.md @home-assistant/core
/tests/components/pushover/ @engrbm87
/homeassistant/components/pvoutput/ @frenck
/tests/components/pvoutput/ @frenck
/homeassistant/components/pvpc_hourly_pricing/ @azogue
/tests/components/pvpc_hourly_pricing/ @azogue
/homeassistant/components/pvpc_hourly_pricing/ @azogue @chiro79
/tests/components/pvpc_hourly_pricing/ @azogue @chiro79
/homeassistant/components/pyload/ @tr4nt0r
/tests/components/pyload/ @tr4nt0r
/homeassistant/components/qbittorrent/ @geoffreylagaisse @finder39
+1 -1
View File
@@ -134,7 +134,7 @@ class AuthManagerFlowManager(
"""
flow = cast(LoginFlow, flow)
if result["type"] != FlowResultType.CREATE_ENTRY:
if result["type"] is not FlowResultType.CREATE_ENTRY:
return result
# we got final result
@@ -226,7 +226,7 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
) -> SubentryFlowResult:
"""Set initial options."""
# abort if entry is not loaded
if self._get_entry().state != ConfigEntryState.LOADED:
if self._get_entry().state is not ConfigEntryState.LOADED:
return self.async_abort(reason="entry_not_loaded")
hass_apis: list[SelectOptionDict] = [
@@ -89,7 +89,7 @@ class AOSmithWaterHeaterEntity(AOSmithStatusEntity, WaterHeaterEntity):
def supported_features(self) -> WaterHeaterEntityFeature:
"""Return the list of supported features."""
supports_vacation_mode = any(
supported_mode.mode == AOSmithOperationMode.VACATION
supported_mode.mode is AOSmithOperationMode.VACATION
for supported_mode in self.device.supported_modes
)
@@ -122,7 +122,7 @@ class AOSmithWaterHeaterEntity(AOSmithStatusEntity, WaterHeaterEntity):
@property
def is_away_mode_on(self) -> bool:
"""Return True if away mode is on."""
return self.device.status.current_mode == AOSmithOperationMode.VACATION
return self.device.status.current_mode is AOSmithOperationMode.VACATION
async def async_set_operation_mode(self, operation_mode: str) -> None:
"""Set new target operation mode."""
@@ -369,7 +369,7 @@ class AppleTVManager(DeviceListener):
attrs[ATTR_MODEL] = (
dev_info.raw_model
if dev_info.model == DeviceModel.Unknown and dev_info.raw_model
if dev_info.model is DeviceModel.Unknown and dev_info.raw_model
else model_str(dev_info.model)
)
attrs[ATTR_SW_VERSION] = dev_info.version
@@ -63,7 +63,7 @@ class AppleTVKeyboardFocused(AppleTVEntity, BinarySensorEntity, KeyboardListener
# Listen to keyboard updates
atv.keyboard.listener = self
# Set initial state based on current focus state
self._update_state(atv.keyboard.text_focus_state == KeyboardFocusState.Focused)
self._update_state(atv.keyboard.text_focus_state is KeyboardFocusState.Focused)
@callback
def async_device_disconnected(self) -> None:
@@ -78,7 +78,7 @@ class AppleTVKeyboardFocused(AppleTVEntity, BinarySensorEntity, KeyboardListener
This is a callback function from pyatv.interface.KeyboardListener.
"""
self._update_state(new_state == KeyboardFocusState.Focused)
self._update_state(new_state is KeyboardFocusState.Focused)
def _update_state(self, new_state: bool) -> None:
"""Update and report."""
@@ -354,7 +354,7 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
"name": self.atv.name,
"type": (
dev_info.raw_model
if dev_info.model == DeviceModel.Unknown and dev_info.raw_model
if dev_info.model is DeviceModel.Unknown and dev_info.raw_model
else model_str(dev_info.model)
),
}
@@ -441,12 +441,12 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
return await self.async_step_password()
# Figure out, depending on protocol, what kind of pairing is needed
if service.pairing == PairingRequirement.Unsupported:
if service.pairing is PairingRequirement.Unsupported:
_LOGGER.debug("%s does not support pairing", self.protocol)
return await self.async_pair_next_protocol()
if service.pairing == PairingRequirement.Disabled:
if service.pairing is PairingRequirement.Disabled:
return await self.async_step_protocol_disabled()
if service.pairing == PairingRequirement.NotNeeded:
if service.pairing is PairingRequirement.NotNeeded:
_LOGGER.debug("%s does not require pairing", self.protocol)
self.credentials[self.protocol.value] = None
return await self.async_pair_next_protocol()
@@ -457,7 +457,7 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
pair_args: dict[str, Any] = {}
if self.protocol in {Protocol.AirPlay, Protocol.Companion, Protocol.DMAP}:
pair_args["name"] = "Home Assistant"
if self.protocol == Protocol.DMAP:
if self.protocol is Protocol.DMAP:
pair_args["zeroconf"] = await zeroconf.async_get_instance(self.hass)
# Initiate the pairing process
@@ -139,7 +139,7 @@ class AppleTvMediaPlayer(
all_features = atv.features.all_features()
for feature_name, support_flag in SUPPORT_FEATURE_MAPPING.items():
feature_info = all_features.get(feature_name)
if feature_info and feature_info.state != FeatureState.Unsupported:
if feature_info and feature_info.state is not FeatureState.Unsupported:
self._attr_supported_features |= support_flag
# No need to schedule state update here as that will happen when the first
@@ -188,14 +188,14 @@ class AppleTvMediaPlayer(
return MediaPlayerState.OFF
if (
self._is_feature_available(FeatureName.PowerState)
and self.atv.power.power_state == PowerState.Off
and self.atv.power.power_state is PowerState.Off
):
return MediaPlayerState.OFF
if self._playing:
state = self._playing.device_state
if state in (DeviceState.Idle, DeviceState.Loading):
return MediaPlayerState.IDLE
if state == DeviceState.Playing:
if state is DeviceState.Playing:
return MediaPlayerState.PLAYING
if state in (DeviceState.Paused, DeviceState.Seeking, DeviceState.Stopped):
return MediaPlayerState.PAUSED
@@ -446,7 +446,7 @@ class AppleTvMediaPlayer(
def shuffle(self) -> bool | None:
"""Boolean if shuffle is enabled."""
if self._playing and self._is_feature_available(FeatureName.Shuffle):
return self._playing.shuffle != ShuffleState.Off
return self._playing.shuffle is not ShuffleState.Off
return None
def _is_feature_available(self, feature: FeatureName) -> bool:
@@ -506,7 +506,7 @@ class AppleTvMediaPlayer(
and (self._is_feature_available(FeatureName.TurnOff))
and (
not self._is_feature_available(FeatureName.PowerState)
or self.atv.power.power_state == PowerState.On
or self.atv.power.power_state is PowerState.On
)
):
await self.atv.power.turn_off()
@@ -59,7 +59,7 @@ def _check_keyboard_focus(atv: AppleTVInterface) -> None:
translation_domain=DOMAIN,
translation_key="keyboard_not_available",
) from err
if focus_state != KeyboardFocusState.Focused:
if focus_state is not KeyboardFocusState.Focused:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="keyboard_not_focused",
@@ -263,9 +263,9 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
def media_channel(self) -> str | None:
"""Channel currently playing."""
source = self._state.get_source()
if source == SourceCodes.DAB:
if source is SourceCodes.DAB:
value = self._state.get_dab_station()
elif source == SourceCodes.FM:
elif source is SourceCodes.FM:
value = self._state.get_rds_information()
else:
value = None
@@ -274,7 +274,7 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
@property
def media_artist(self) -> str | None:
"""Artist of current playing media, music track only."""
if self._state.get_source() == SourceCodes.DAB:
if self._state.get_source() is SourceCodes.DAB:
value = self._state.get_dls_pdt()
else:
value = None
@@ -1355,7 +1355,7 @@ class PipelineRun:
) -> bool:
"""Return true if all targeted entities were in the same area as the device."""
if (
intent_response.response_type != intent.IntentResponseType.ACTION_DONE
intent_response.response_type is not intent.IntentResponseType.ACTION_DONE
or not intent_response.matched_states
):
return False
+4 -4
View File
@@ -251,12 +251,12 @@ class AuthProvidersView(HomeAssistantView):
def _prepare_result_json(result: AuthFlowResult) -> dict[str, Any]:
"""Convert result to JSON serializable dict."""
if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY:
return {
key: val for key, val in result.items() if key not in ("result", "data")
}
if result["type"] != data_entry_flow.FlowResultType.FORM:
if result["type"] is not data_entry_flow.FlowResultType.FORM:
return result # type: ignore[return-value]
data = dict(result)
@@ -289,11 +289,11 @@ class LoginFlowBaseView(HomeAssistantView):
result: AuthFlowResult,
) -> web.Response:
"""Convert the flow result to a response."""
if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is not data_entry_flow.FlowResultType.CREATE_ENTRY:
# @log_invalid_auth does not work here since it returns HTTP 200.
# We need to manually log failed login attempts.
if (
result["type"] == data_entry_flow.FlowResultType.FORM
result["type"] is data_entry_flow.FlowResultType.FORM
and (errors := result.get("errors"))
and errors.get("base")
in (
@@ -142,9 +142,9 @@ def websocket_depose_mfa(
def _prepare_result_json(result: data_entry_flow.FlowResult) -> dict[str, Any]:
"""Convert result to JSON serializable dict."""
if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY:
return dict(result)
if result["type"] != data_entry_flow.FlowResultType.FORM:
if result["type"] is not data_entry_flow.FlowResultType.FORM:
return result # type: ignore[return-value]
data = dict(result)
@@ -273,7 +273,7 @@ async def ws_subscribe_scanner_details(
def _async_registration_changed(registration: HaScannerRegistration) -> None:
added_event = HaScannerRegistrationEvent.ADDED
event_type = "add" if registration.event == added_event else "remove"
event_type = "add" if registration.event is added_event else "remove"
_async_event_message({event_type: [registration.scanner.details]})
manager = _get_manager(hass)
+4 -1
View File
@@ -158,7 +158,10 @@ def process_service_info(
)
# If payload is encrypted and the bindkey is not verified then we need to reauth
if data.encryption_scheme != EncryptionScheme.NONE and not data.bindkey_verified:
if (
data.encryption_scheme is not EncryptionScheme.NONE
and not data.bindkey_verified
):
entry.async_start_reauth(hass, data={"device": data})
return update
@@ -59,7 +59,7 @@ class BTHomeConfigFlow(ConfigFlow, domain=DOMAIN):
self._discovery_info = discovery_info
self._discovered_device = device
if device.encryption_scheme == EncryptionScheme.BTHOME_BINDKEY:
if device.encryption_scheme is EncryptionScheme.BTHOME_BINDKEY:
return await self.async_step_get_encryption_key()
return await self.async_step_bluetooth_confirm()
@@ -125,7 +125,7 @@ class BTHomeConfigFlow(ConfigFlow, domain=DOMAIN):
self._discovery_info = discovery.discovery_info
self._discovered_device = discovery.device
if discovery.device.encryption_scheme == EncryptionScheme.BTHOME_BINDKEY:
if discovery.device.encryption_scheme is EncryptionScheme.BTHOME_BINDKEY:
return await self.async_step_get_encryption_key()
return self._async_get_or_create_entry()
@@ -164,7 +164,7 @@ class BTHomeConfigFlow(ConfigFlow, domain=DOMAIN):
self._discovery_info = device.last_service_info
if device.encryption_scheme == EncryptionScheme.BTHOME_BINDKEY:
if device.encryption_scheme is EncryptionScheme.BTHOME_BINDKEY:
return await self.async_step_get_encryption_key()
# Otherwise there wasn't actually encryption so abort
@@ -1,12 +1,11 @@
"""Config flow for the Cert Expiry platform."""
from collections.abc import Mapping
import logging
from typing import Any
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PORT
from .const import DEFAULT_PORT, DOMAIN
@@ -19,8 +18,6 @@ from .errors import (
)
from .helper import get_cert_expiry_timestamp
_LOGGER = logging.getLogger(__name__)
class CertexpiryConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow."""
@@ -75,9 +72,6 @@ class CertexpiryConfigFlow(ConfigFlow, domain=DOMAIN):
title=title,
data={CONF_HOST: host, CONF_PORT: port},
)
if self.source == SOURCE_IMPORT:
_LOGGER.error("Config import failed for %s", user_input[CONF_HOST])
return self.async_abort(reason="import_failed")
else:
user_input = {}
user_input[CONF_HOST] = ""
@@ -2,7 +2,6 @@
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
"import_failed": "Import from config failed",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
@@ -110,7 +110,7 @@ class ComelitAlarmEntity(
@property
def available(self) -> bool:
"""Return True if alarm is available."""
if self._area.human_status == AlarmAreaState.UNKNOWN:
if self._area.human_status is AlarmAreaState.UNKNOWN:
return False
return super().available
@@ -124,7 +124,7 @@ class ComelitAlarmEntity(
self._area.human_status,
self._area.armed,
)
if self._area.human_status == AlarmAreaState.ARMED:
if self._area.human_status is AlarmAreaState.ARMED:
if self._area.armed == ALARM_AREA_ARMED_STATUS[AWAY]:
return AlarmControlPanelState.ARMED_AWAY
if self._area.armed == ALARM_AREA_ARMED_STATUS[NIGHT]:
@@ -43,7 +43,7 @@ BINARY_SENSOR_TYPES: Final[tuple[ComelitBinarySensorEntityDescription, ...]] = (
device_class=BinarySensorDeviceClass.PROBLEM,
is_on_fn=lambda obj: cast(ComelitVedoAreaObject, obj).anomaly,
available_fn=lambda obj: (
cast(ComelitVedoAreaObject, obj).human_status != AlarmAreaState.UNKNOWN
cast(ComelitVedoAreaObject, obj).human_status is not AlarmAreaState.UNKNOWN
),
),
ComelitBinarySensorEntityDescription(
@@ -67,7 +67,7 @@ BINARY_SENSOR_TYPES: Final[tuple[ComelitBinarySensorEntityDescription, ...]] = (
object_type=ALARM_ZONE,
device_class=BinarySensorDeviceClass.PROBLEM,
is_on_fn=lambda obj: (
cast(ComelitVedoZoneObject, obj).human_status == AlarmZoneState.FAULTY
cast(ComelitVedoZoneObject, obj).human_status is AlarmZoneState.FAULTY
),
available_fn=lambda obj: (
cast(ComelitVedoZoneObject, obj).human_status
+2 -2
View File
@@ -166,12 +166,12 @@ class ComelitVedoSensorEntity(
@property
def available(self) -> bool:
"""Sensor availability."""
return self._zone_object.human_status != AlarmZoneState.UNAVAILABLE
return self._zone_object.human_status is not AlarmZoneState.UNAVAILABLE
@property
def native_value(self) -> StateType:
"""Sensor value."""
if (status := self._zone_object.human_status) == AlarmZoneState.UNKNOWN:
if (status := self._zone_object.human_status) is AlarmZoneState.UNKNOWN:
return None
return cast(str, status.value)
@@ -148,7 +148,7 @@ def _prepare_config_flow_result_json(
prepare_result_json: Callable[[data_entry_flow.FlowResult], dict[str, Any]],
) -> dict[str, Any]:
"""Convert result to JSON."""
if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is not data_entry_flow.FlowResultType.CREATE_ENTRY:
return prepare_result_json(result)
data = {key: val for key, val in result.items() if key not in ("data", "context")}
@@ -646,7 +646,7 @@ class DefaultAgent(ConversationEntity):
cache_value = self._intent_cache.get(cache_key)
if cache_value is not None:
if (cache_value.result is not None) and (
cache_value.stage == IntentMatchingStage.EXPOSED_ENTITIES_ONLY
cache_value.stage is IntentMatchingStage.EXPOSED_ENTITIES_ONLY
):
_LOGGER.debug("Got cached result for exposed entities")
return cache_value.result
@@ -686,7 +686,7 @@ class DefaultAgent(ConversationEntity):
skip_unexposed_entities_match = False
if cache_value is not None:
if (cache_value.result is not None) and (
cache_value.stage == IntentMatchingStage.UNEXPOSED_ENTITIES
cache_value.stage is IntentMatchingStage.UNEXPOSED_ENTITIES
):
_LOGGER.debug("Got cached result for all entities")
return cache_value.result
@@ -731,7 +731,7 @@ class DefaultAgent(ConversationEntity):
skip_unknown_names = False
if cache_value is not None:
if (cache_value.result is not None) and (
cache_value.stage == IntentMatchingStage.UNKNOWN_NAMES
cache_value.stage is IntentMatchingStage.UNKNOWN_NAMES
):
_LOGGER.debug("Got cached result for unknown names")
return cache_value.result
@@ -1447,7 +1447,7 @@ class DefaultAgent(ConversationEntity):
response = await self._async_process_intent_result(result, user_input, chat_log)
if (
response.response_type == intent.IntentResponseType.ERROR
response.response_type is intent.IntentResponseType.ERROR
and response.error_code
not in (
intent.IntentResponseErrorCode.FAILED_TO_HANDLE,
@@ -1546,7 +1546,7 @@ def _get_match_error_response(
# device_class only
return ErrorKey.NO_DEVICE_CLASS, {"device_class": device_class}
if (reason == intent.MatchFailedReason.DOMAIN) and constraints.domains:
if (reason is intent.MatchFailedReason.DOMAIN) and constraints.domains:
domain = next(iter(constraints.domains)) # first domain
if constraints.area_name:
# domain in area
@@ -1565,7 +1565,7 @@ def _get_match_error_response(
# domain only
return ErrorKey.NO_DOMAIN, {"domain": domain}
if reason == intent.MatchFailedReason.DUPLICATE_NAME:
if reason is intent.MatchFailedReason.DUPLICATE_NAME:
if constraints.floor_name:
# duplicate on floor
return ErrorKey.DUPLICATE_ENTITIES_IN_FLOOR, {
@@ -1582,26 +1582,26 @@ def _get_match_error_response(
return ErrorKey.DUPLICATE_ENTITIES, {"entity": result.no_match_name}
if reason == intent.MatchFailedReason.INVALID_AREA:
if reason is intent.MatchFailedReason.INVALID_AREA:
# Invalid area name
return ErrorKey.NO_AREA, {"area": result.no_match_name}
if reason == intent.MatchFailedReason.INVALID_FLOOR:
if reason is intent.MatchFailedReason.INVALID_FLOOR:
# Invalid floor name
return ErrorKey.NO_FLOOR, {"floor": result.no_match_name}
if reason == intent.MatchFailedReason.FEATURE:
if reason is intent.MatchFailedReason.FEATURE:
# Feature not supported by entity
return ErrorKey.FEATURE_NOT_SUPPORTED, {}
if reason == intent.MatchFailedReason.STATE:
if reason is intent.MatchFailedReason.STATE:
# Entity is not in correct state
assert constraints.states
state = next(iter(constraints.states))
return ErrorKey.ENTITY_WRONG_STATE, {"state": state}
if reason == intent.MatchFailedReason.ASSISTANT:
if reason is intent.MatchFailedReason.ASSISTANT:
# Not exposed
if constraints.name:
if constraints.area_name:
@@ -80,7 +80,7 @@ async def async_validate_device_automation_config(
# the checks below which look for a config entry matching the device automation
# domain
if (
automation_type == DeviceAutomationType.ACTION
automation_type is DeviceAutomationType.ACTION
and validated_config[CONF_DOMAIN] in ENTITY_PLATFORMS
):
# Pass the unvalidated config to avoid mutating the raw config twice
@@ -1,11 +1,18 @@
"""Provide functionality to keep track of devices."""
import asyncio
from typing import Any
from homeassistant.const import ATTR_GPS_ACCURACY, STATE_HOME # noqa: F401
from homeassistant.core import HomeAssistant
from homeassistant.helpers import discovery
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
from .config_entry import ( # noqa: F401
DATA_COMPONENT,
BaseScannerEntity,
BaseTrackerEntity,
ScannerEntity,
ScannerEntityDescription,
TrackerEntity,
@@ -33,6 +40,8 @@ from .const import ( # noqa: F401
DEFAULT_TRACK_NEW,
DOMAIN,
ENTITY_ID_FORMAT,
LOGGER,
PLATFORM_TYPE_LEGACY,
SCAN_INTERVAL,
SourceType,
)
@@ -44,7 +53,9 @@ from .legacy import ( # noqa: F401
SOURCE_TYPES,
AsyncSeeCallback,
DeviceScanner,
DeviceTracker,
SeeCallback,
async_create_platform_type,
async_setup_integration as async_setup_legacy_integration,
see,
)
@@ -57,5 +68,43 @@ def is_on(hass: HomeAssistant, entity_id: str) -> bool:
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the device tracker."""
async_setup_legacy_integration(hass, config)
component = hass.data[DATA_COMPONENT] = EntityComponent[BaseTrackerEntity](
LOGGER, DOMAIN, hass, SCAN_INTERVAL
)
component.config = {}
component.register_shutdown()
# The tracker is loaded in the async_setup_legacy_integration task so
# we create a future to avoid waiting on it here so that only
# async_platform_discovered will have to wait in the rare event
# a custom component still uses the legacy device tracker discovery.
tracker_future: asyncio.Future[DeviceTracker] = hass.loop.create_future()
async def async_platform_discovered(
p_type: str, info: dict[str, Any] | None
) -> None:
"""Load a platform."""
platform = await async_create_platform_type(hass, config, p_type, {})
if platform is None:
return
if platform.type != PLATFORM_TYPE_LEGACY:
await component.async_setup_platform(p_type, {}, info)
return
tracker = await tracker_future
await platform.async_setup_legacy(hass, tracker, info)
discovery.async_listen_platform(hass, DOMAIN, async_platform_discovered)
#
# Legacy and platforms load in a non-awaited tracked task
# to ensure device tracker setup can continue and config
# entry integrations are not waiting for legacy device
# tracker platforms to be set up.
#
hass.async_create_task(
async_setup_legacy_integration(hass, config, tracker_future),
eager_start=True,
)
return True
@@ -37,11 +37,7 @@ from homeassistant.const import (
)
from homeassistant.core import Event, HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
config_validation as cv,
discovery,
entity_registry as er,
)
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers.event import (
async_track_time_interval,
async_track_utc_time_change,
@@ -204,40 +200,7 @@ def see(
hass.services.call(DOMAIN, SERVICE_SEE, data)
@callback
def async_setup_integration(hass: HomeAssistant, config: ConfigType) -> None:
"""Set up the legacy integration."""
# The tracker is loaded in the _async_setup_integration task so
# we create a future to avoid waiting on it here so that only
# async_platform_discovered will have to wait in the rare event
# a custom component still uses the legacy device tracker discovery.
tracker_future: asyncio.Future[DeviceTracker] = hass.loop.create_future()
async def async_platform_discovered(
p_type: str, info: dict[str, Any] | None
) -> None:
"""Load a platform."""
platform = await async_create_platform_type(hass, config, p_type, {})
if platform is None or platform.type != PLATFORM_TYPE_LEGACY:
return
tracker = await tracker_future
await platform.async_setup_legacy(hass, tracker, info)
discovery.async_listen_platform(hass, DOMAIN, async_platform_discovered)
#
# Legacy and platforms load in a non-awaited tracked task
# to ensure device tracker setup can continue and config
# entry integrations are not waiting for legacy device
# tracker platforms to be set up.
#
hass.async_create_task(
_async_setup_integration(hass, config, tracker_future), eager_start=True
)
async def _async_setup_integration(
async def async_setup_integration(
hass: HomeAssistant,
config: ConfigType,
tracker_future: asyncio.Future[DeviceTracker],
@@ -261,7 +261,7 @@ class DlnaDmrEntity(MediaPlayerEntity):
except KeyError, ValueError:
bootid = None
if change == ssdp.SsdpChange.UPDATE:
if change is ssdp.SsdpChange.UPDATE:
# This is an announcement that bootid is about to change
if self._bootid is not None and self._bootid == bootid:
# Store the new value (because our old value matches) so that we
@@ -281,7 +281,7 @@ class DlnaDmrEntity(MediaPlayerEntity):
await self._device_disconnect()
self._bootid = bootid
if change == ssdp.SsdpChange.BYEBYE:
if change is ssdp.SsdpChange.BYEBYE:
# Device is going away
if self._device:
# Disconnect from gone device
@@ -290,7 +290,7 @@ class DlnaDmrEntity(MediaPlayerEntity):
self._ssdp_connect_failed = False
if (
change == ssdp.SsdpChange.ALIVE
change is ssdp.SsdpChange.ALIVE
and not self._device
and not self._ssdp_connect_failed
):
@@ -718,7 +718,7 @@ class DlnaDmrEntity(MediaPlayerEntity):
# If already playing, or don't want to autoplay, no need to call Play
autoplay = extra.get("autoplay", True)
if self._device.transport_state == TransportState.PLAYING or not autoplay:
if self._device.transport_state is TransportState.PLAYING or not autoplay:
return
# Play it
@@ -748,7 +748,7 @@ class DlnaDmrEntity(MediaPlayerEntity):
if not (play_mode := self._device.play_mode):
return None
if play_mode == PlayMode.VENDOR_DEFINED:
if play_mode is PlayMode.VENDOR_DEFINED:
return None
return play_mode in (PlayMode.SHUFFLE, PlayMode.RANDOM)
@@ -782,10 +782,10 @@ class DlnaDmrEntity(MediaPlayerEntity):
if not (play_mode := self._device.play_mode):
return None
if play_mode == PlayMode.VENDOR_DEFINED:
if play_mode is PlayMode.VENDOR_DEFINED:
return None
if play_mode == PlayMode.REPEAT_ONE:
if play_mode is PlayMode.REPEAT_ONE:
return RepeatMode.ONE
if play_mode in (PlayMode.REPEAT_ALL, PlayMode.RANDOM):
+3 -3
View File
@@ -236,7 +236,7 @@ class DmsDeviceSource:
except KeyError, ValueError:
bootid = None
if change == ssdp.SsdpChange.UPDATE:
if change is ssdp.SsdpChange.UPDATE:
# This is an announcement that bootid is about to change
if self._bootid is not None and self._bootid == bootid:
# Store the new value (because our old value matches) so that we
@@ -258,7 +258,7 @@ class DmsDeviceSource:
await self.device_disconnect()
self._bootid = bootid
if change == ssdp.SsdpChange.BYEBYE:
if change is ssdp.SsdpChange.BYEBYE:
# Device is going away
if self._device:
# Disconnect from gone device
@@ -267,7 +267,7 @@ class DmsDeviceSource:
self._ssdp_connect_failed = False
if (
change == ssdp.SsdpChange.ALIVE
change is ssdp.SsdpChange.ALIVE
and not self._device
and not self._ssdp_connect_failed
):
@@ -8,6 +8,12 @@
},
"step": {
"user": {
"data": {
"download_dir": "Download directory"
},
"data_description": {
"download_dir": "The directory where downloaded files will be stored. This can be an absolute path or a path relative to the Home Assistant configuration directory."
},
"description": "Select a location to get to store downloads. The setup will check if the directory exists."
}
}
@@ -15,6 +15,8 @@ _LOGGER = logging.getLogger(__name__)
class EcobeeBaseEntity(Entity):
"""Base methods for Ecobee entities."""
_attr_has_entity_name = True
def __init__(self, data: EcobeeData, thermostat_index: int) -> None:
"""Initiate base methods for Ecobee entities."""
self.data = data
@@ -1,4 +1,11 @@
{
"entity": {
"number": {
"fan_min_on_time": {
"default": "mdi:fan-clock"
}
}
},
"services": {
"create_vacation": {
"service": "mdi:umbrella-beach"
@@ -24,7 +24,6 @@ class EcobeeNotifyEntity(EcobeeBaseEntity, NotifyEntity):
"""Implement the notification entity for the Ecobee thermostat."""
_attr_name = None
_attr_has_entity_name = True
def __init__(self, data: EcobeeData, thermostat_index: int) -> None:
"""Initialize the thermostat."""
+40 -2
View File
@@ -74,6 +74,10 @@ async def async_setup_entry(
)
)
entities.extend(
EcobeeFanMinOnTime(data, index) for index in range(len(data.ecobee.thermostats))
)
async_add_entities(entities, True)
@@ -86,7 +90,6 @@ class EcobeeVentilatorMinTime(EcobeeBaseEntity, NumberEntity):
_attr_native_max_value = 60
_attr_native_step = 5
_attr_native_unit_of_measurement = UnitOfTime.MINUTES
_attr_has_entity_name = True
def __init__(
self,
@@ -130,7 +133,6 @@ class EcobeeCompressorMinTemp(EcobeeBaseEntity, NumberEntity):
"""
_attr_device_class = NumberDeviceClass.TEMPERATURE
_attr_has_entity_name = True
_attr_icon = "mdi:thermometer-off"
_attr_mode = NumberMode.BOX
_attr_native_min_value = -25
@@ -165,3 +167,39 @@ class EcobeeCompressorMinTemp(EcobeeBaseEntity, NumberEntity):
"""Set new compressor minimum temperature."""
self.data.ecobee.set_aux_cutover_threshold(self.thermostat_index, value)
self.update_without_throttle = True
class EcobeeFanMinOnTime(EcobeeBaseEntity, NumberEntity):
"""Minimum minutes per hour that the fan must run on an ecobee thermostat."""
_attr_native_min_value = 0
_attr_native_max_value = 60
_attr_native_step = 5
_attr_native_unit_of_measurement = UnitOfTime.MINUTES
_attr_translation_key = "fan_min_on_time"
def __init__(
self,
data: EcobeeData,
thermostat_index: int,
) -> None:
"""Initialize ecobee fan minimum on time."""
super().__init__(data, thermostat_index)
self._attr_unique_id = f"{self.base_unique_id}_fan_min_on_time"
self.update_without_throttle = False
async def async_update(self) -> None:
"""Get the latest state from the thermostat."""
if self.update_without_throttle:
await self.data.update(no_throttle=True)
self.update_without_throttle = False
else:
await self.data.update()
self._attr_native_value = self.thermostat["settings"]["fanMinOnTime"]
def set_native_value(self, value: float) -> None:
"""Set new fan minimum on time value."""
step = self._attr_native_step
aligned_value = int(round(value / step) * step)
self.data.ecobee.set_fan_min_on_time(self.thermostat_index, aligned_value)
self.update_without_throttle = True
@@ -37,6 +37,9 @@
"compressor_protection_min_temp": {
"name": "Compressor minimum temperature"
},
"fan_min_on_time": {
"name": "Fan minimum on time"
},
"ventilator_min_type_away": {
"name": "Ventilator minimum time away"
},
@@ -53,7 +53,6 @@ async def async_setup_entry(
class EcobeeVentilator20MinSwitch(EcobeeBaseEntity, SwitchEntity):
"""Represent 20 min timer for an ecobee thermostat with ventilator."""
_attr_has_entity_name = True
_attr_name = "Ventilator 20m Timer"
def __init__(
@@ -104,7 +103,6 @@ class EcobeeVentilator20MinSwitch(EcobeeBaseEntity, SwitchEntity):
class EcobeeSwitchAuxHeatOnly(EcobeeBaseEntity, SwitchEntity):
"""Representation of a aux_heat_only ecobee switch."""
_attr_has_entity_name = True
_attr_translation_key = "aux_heat_only"
def __init__(
+3 -3
View File
@@ -150,9 +150,9 @@ class ElevenLabsSTTEntity(SpeechToTextEntity):
raw_pcm_compatible = (
metadata.codec == AudioCodecs.PCM
and metadata.sample_rate == AudioSampleRates.SAMPLERATE_16000
and metadata.channel == AudioChannels.CHANNEL_MONO
and metadata.bit_rate == AudioBitRates.BITRATE_16
and metadata.sample_rate is AudioSampleRates.SAMPLERATE_16000
and metadata.channel is AudioChannels.CHANNEL_MONO
and metadata.bit_rate is AudioBitRates.BITRATE_16
)
if raw_pcm_compatible:
file_format = "pcm_s16le_16"
@@ -50,5 +50,5 @@ class ElkBinarySensor(ElkAttachedEntity, BinarySensorEntity):
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
# Zone in NORMAL state is OFF; any other state is ON
self._attr_is_on = bool(
self._element.logical_status != ZoneLogicalStatus.NORMAL
self._element.logical_status is not ZoneLogicalStatus.NORMAL
)
+2 -2
View File
@@ -104,7 +104,7 @@ class ElkThermostat(ElkEntity, ClimateEntity):
ThermostatMode.EMERGENCY_HEAT,
):
return self._element.heat_setpoint
if self._element.mode == ThermostatMode.COOL:
if self._element.mode is ThermostatMode.COOL:
return self._element.cool_setpoint
return None
@@ -162,6 +162,6 @@ class ElkThermostat(ElkEntity, ClimateEntity):
self._attr_hvac_mode = ELK_TO_HASS_HVAC_MODES[self._element.mode]
if (
self._attr_hvac_mode == HVACMode.OFF
and self._element.fan == ThermostatFan.ON
and self._element.fan is ThermostatFan.ON
):
self._attr_hvac_mode = HVACMode.FAN_ONLY
+1 -1
View File
@@ -56,7 +56,7 @@ class ElkNumberSetting(ElkAttachedEntity, NumberEntity):
def __init__(self, element: Setting, elk: Any, elk_data: ELKM1Data) -> None:
"""Initialize the number setting."""
super().__init__(element, elk, elk_data)
if element.value_format == SettingFormat.TIMER:
if element.value_format is SettingFormat.TIMER:
self._attr_device_class = NumberDeviceClass.DURATION
self._attr_native_unit_of_measurement = UnitOfTime.SECONDS
+6 -6
View File
@@ -75,7 +75,7 @@ async def async_setup_entry(
for setting in elk.settings:
setting = cast(Setting, setting)
domain = (
"time" if setting.value_format == SettingFormat.TIME_OF_DAY else "number"
"time" if setting.value_format is SettingFormat.TIME_OF_DAY else "number"
)
orig_unique_id = generate_unique_id(elk_data.prefix, setting)
@@ -288,7 +288,7 @@ class ElkZone(ElkSensor):
@property
def temperature_unit(self) -> str | None:
"""Return the temperature unit."""
if self._element.definition == ZoneType.TEMPERATURE:
if self._element.definition is ZoneType.TEMPERATURE:
return self._temperature_unit
return None
@@ -305,18 +305,18 @@ class ElkZone(ElkSensor):
@property
def native_unit_of_measurement(self) -> str | None:
"""Return the unit of measurement."""
if self._element.definition == ZoneType.TEMPERATURE:
if self._element.definition is ZoneType.TEMPERATURE:
return self._temperature_unit
if self._element.definition == ZoneType.ANALOG_ZONE:
if self._element.definition is ZoneType.ANALOG_ZONE:
return UnitOfElectricPotential.VOLT
return None
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
if self._element.definition == ZoneType.TEMPERATURE:
if self._element.definition is ZoneType.TEMPERATURE:
self._attr_native_value = temperature_to_state(
self._element.temperature, UNDEFINED_TEMPERATURE
)
elif self._element.definition == ZoneType.ANALOG_ZONE:
elif self._element.definition is ZoneType.ANALOG_ZONE:
self._attr_native_value = f"{self._element.voltage}"
else:
self._attr_native_value = pretty_const(self._element.logical_status.name)
+1 -1
View File
@@ -66,7 +66,7 @@ class ElkThermostatEMHeat(ElkEntity, SwitchEntity):
@property
def is_on(self) -> bool:
"""Get the current emergency heat status."""
return self._element.mode == ThermostatMode.EMERGENCY_HEAT
return self._element.mode is ThermostatMode.EMERGENCY_HEAT
def _elk_set(self, mode: ThermostatMode) -> None:
"""Set the thermostat mode."""
+1 -1
View File
@@ -30,7 +30,7 @@ async def async_setup_entry(
time_settings = [
setting
for setting in cast(list[Setting], elk.settings)
if setting.value_format == SettingFormat.TIME_OF_DAY
if setting.value_format is SettingFormat.TIME_OF_DAY
]
create_elk_entities(
@@ -96,7 +96,7 @@ def __get_coordinator(call: ServiceCall) -> EnergyZeroDataUpdateCoordinator:
"config_entry": entry_id,
},
)
if entry.state != ConfigEntryState.LOADED:
if entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="unloaded_config_entry",
@@ -125,7 +125,7 @@ async def __get_prices(
data: Electricity | Gas
if price_type == PriceType.GAS:
if price_type is PriceType.GAS:
data = await coordinator.energyzero.get_gas_prices_legacy(
start_date=start,
end_date=end,
@@ -24,7 +24,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: FastdotcomConfigEntry) -
async def _async_finish_startup(hass: HomeAssistant) -> None:
"""Run this only when HA has finished its startup."""
if entry.state == ConfigEntryState.LOADED:
if entry.state is ConfigEntryState.LOADED:
await coordinator.async_refresh()
else:
await coordinator.async_config_entry_first_refresh()
@@ -284,7 +284,7 @@ class FishAudioSubentryFlowHandler(ConfigSubentryFlow):
) -> SubentryFlowResult:
"""Manage initial options."""
entry = self._get_entry()
if entry.state != ConfigEntryState.LOADED:
if entry.state is not ConfigEntryState.LOADED:
return self.async_abort(reason="entry_not_loaded")
self.client = entry.runtime_data
+1 -1
View File
@@ -109,7 +109,7 @@ def setup_service(hass: HomeAssistant) -> None:
entry: FlumeConfigEntry | None = hass.config_entries.async_get_entry(entry_id)
if not entry:
raise ValueError(f"Invalid config entry: {entry_id}")
if not entry.state == ConfigEntryState.LOADED:
if entry.state is not ConfigEntryState.LOADED:
raise ValueError(f"Config entry not loaded: {entry_id}")
return {
"notifications": entry.runtime_data.notifications_coordinator.notifications # type: ignore[dict-item]
@@ -136,7 +136,7 @@ class FluxLedConfigFlow(ConfigFlow, domain=DOMAIN):
ConfigEntryState.SETUP_IN_PROGRESS,
ConfigEntryState.NOT_LOADED,
)
) or entry.state == ConfigEntryState.SETUP_RETRY:
) or entry.state is ConfigEntryState.SETUP_RETRY:
self.hass.config_entries.async_schedule_reload(entry.entry_id)
else:
async_dispatcher_send(
+1 -1
View File
@@ -51,7 +51,7 @@ async def async_setup_entry(
entry.data.get(CONF_NAME, entry.title)
base_unique_id = entry.unique_id or entry.entry_id
if device.device_type == DeviceType.Switch:
if device.device_type is DeviceType.Switch:
entities.append(FluxPowerStateSelect(coordinator.device, entry))
if device.operating_modes:
entities.append(
+1 -1
View File
@@ -32,7 +32,7 @@ async def async_setup_entry(
entities: list[FluxSwitch | FluxRemoteAccessSwitch | FluxMusicSwitch] = []
base_unique_id = entry.unique_id or entry.entry_id
if coordinator.device.device_type == DeviceType.Switch:
if coordinator.device.device_type is DeviceType.Switch:
entities.append(FluxSwitch(coordinator, base_unique_id, None))
if entry.data.get(CONF_REMOTE_ACCESS_HOST):
+9 -1
View File
@@ -9,7 +9,12 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import PERCENTAGE, UnitOfDataRate, UnitOfTemperature
from homeassistant.const import (
PERCENTAGE,
EntityCategory,
UnitOfDataRate,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -45,6 +50,7 @@ CALL_SENSORS: tuple[SensorEntityDescription, ...] = (
translation_key="missed",
native_unit_of_measurement="calls",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
@@ -53,6 +59,7 @@ DISK_PARTITION_SENSORS: tuple[SensorEntityDescription, ...] = (
key="partition_free_space",
translation_key="partition_free_space",
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
@@ -80,6 +87,7 @@ async def async_setup_entry(
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
for sensor_id, sensor_name in router.sensors_temperature_names.items()
@@ -6,7 +6,6 @@ from typing import Any
from freebox_api.exceptions import InsufficientPermissionsError
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -19,7 +18,6 @@ SWITCH_DESCRIPTIONS = [
SwitchEntityDescription(
key="wifi",
translation_key="wifi",
entity_category=EntityCategory.CONFIG,
)
]
+3 -3
View File
@@ -212,7 +212,7 @@ class FroniusSolarNet:
inverter_info=_inverter_info,
config_entry=self.config_entry,
)
if self.config_entry.state == ConfigEntryState.LOADED:
if self.config_entry.state is ConfigEntryState.LOADED:
await _coordinator.async_refresh()
else:
await _coordinator.async_config_entry_first_refresh()
@@ -220,7 +220,7 @@ class FroniusSolarNet:
# Only for re-scans. Initial setup adds entities
# through sensor.async_setup_entry
if self.config_entry.state == ConfigEntryState.LOADED:
if self.config_entry.state is ConfigEntryState.LOADED:
async_dispatcher_send(self.hass, SOLAR_NET_DISCOVERY_NEW, _coordinator)
_LOGGER.debug(
@@ -235,7 +235,7 @@ class FroniusSolarNet:
try:
_inverter_info = await self.fronius.inverter_info()
except FroniusError as err:
if self.config_entry.state == ConfigEntryState.LOADED:
if self.config_entry.state is ConfigEntryState.LOADED:
# During a re-scan we will attempt again as per schedule.
_LOGGER.debug("Re-scan failed for %s", self.host)
return inverter_infos
@@ -42,7 +42,7 @@ async def _collect_coordinators(
raise HomeAssistantError(f"Device '{target}' not found in device registry")
coordinators = list[FullyKioskDataUpdateCoordinator]()
for config_entry in config_entries:
if config_entry.state != ConfigEntryState.LOADED:
if config_entry.state is not ConfigEntryState.LOADED:
raise HomeAssistantError(f"{config_entry.title} is not loaded")
coordinators.append(config_entry.runtime_data)
return coordinators
@@ -75,7 +75,7 @@ async def async_setup_entry(
mfg_data = await async_get_manufacturer_data({address})
product_type = mfg_data[address].product_type
if product_type == ProductType.UNKNOWN:
if product_type is ProductType.UNKNOWN:
raise ConfigEntryNotReady("Unable to find product type")
client = Client(get_connection(hass, address), product_type)
+1 -1
View File
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioghost"],
"quality_scale": "gold",
"requirements": ["aioghost==0.4.0"]
"requirements": ["aioghost==0.4.16"]
}
+3 -3
View File
@@ -143,7 +143,7 @@ def _get_entity_descriptions(
local_sync = True
if (
search := data.get(CONF_SEARCH)
) or calendar_item.access_role == AccessRole.FREE_BUSY_READER:
) or calendar_item.access_role is AccessRole.FREE_BUSY_READER:
read_only = True
local_sync = False
entity_description = GoogleCalendarEntityDescription(
@@ -386,14 +386,14 @@ class GoogleCalendarEntity(
"""Return True if the event is visible and not declined."""
if any(
attendee.is_self and attendee.response_status == ResponseStatus.DECLINED
attendee.is_self and attendee.response_status is ResponseStatus.DECLINED
for attendee in event.attendees
):
return False
# Calendar enttiy may be limited to a specific event type
if (
self.entity_description.event_type is not None
and self.entity_description.event_type != event.event_type
and self.entity_description.event_type is not event.event_type
):
return False
# Default calendar entity omits the special types but includes all the others
@@ -247,7 +247,7 @@ class LocationSubentryFlowHandler(ConfigSubentryFlow):
user_input: dict[str, Any] | None = None,
) -> SubentryFlowResult:
"""Handle the location step."""
if self._get_entry().state != ConfigEntryState.LOADED:
if self._get_entry().state is not ConfigEntryState.LOADED:
return self.async_abort(reason="entry_not_loaded")
errors: dict[str, str] = {}
@@ -225,7 +225,7 @@ class LLMSubentryFlowHandler(ConfigSubentryFlow):
) -> SubentryFlowResult:
"""Set conversation options."""
# abort if entry is not loaded
if self._get_entry().state != ConfigEntryState.LOADED:
if self._get_entry().state is not ConfigEntryState.LOADED:
return self.async_abort(reason="entry_not_loaded")
errors: dict[str, str] = {}
@@ -754,7 +754,7 @@ async def async_prepare_files_for_prompt(
config={"http_options": {"timeout": TIMEOUT_MILLIS}},
)
if uploaded_file.state == FileState.FAILED:
if uploaded_file.state is FileState.FAILED:
raise HomeAssistantError(
f"File `{uploaded_file.name}` processing"
" failed, reason:"
@@ -766,7 +766,7 @@ async def async_prepare_files_for_prompt(
tasks = [
asyncio.create_task(wait_for_file_processing(part))
for part in prompt_parts
if part.state != FileState.ACTIVE
if part.state is not FileState.ACTIVE
]
async with asyncio.timeout(TIMEOUT_MILLIS / 1000):
await asyncio.gather(*tasks)
@@ -237,7 +237,7 @@ class LocationSubentryFlowHandler(ConfigSubentryFlow):
user_input: dict[str, Any] | None = None,
) -> SubentryFlowResult:
"""Handle the location step."""
if self._get_entry().state != ConfigEntryState.LOADED:
if self._get_entry().state is not ConfigEntryState.LOADED:
return self.async_abort(reason="entry_not_loaded")
errors: dict[str, str] = {}
@@ -26,7 +26,7 @@ def _get_coordinators(
coordinators: dict[str, GrowattCoordinator] = {}
for entry in hass.config_entries.async_entries(DOMAIN):
if entry.state != ConfigEntryState.LOADED:
if entry.state is not ConfigEntryState.LOADED:
continue
for coord in entry.runtime_data.devices.values():
+2 -2
View File
@@ -247,7 +247,7 @@ class SupervisorOSUpdateEntity(HassioOSEntity, UpdateEntity):
def release_url(self) -> str | None:
"""URL to the full release notes of the latest version available."""
version = AwesomeVersion(self.latest_version)
if version.dev or version.strategy == AwesomeVersionStrategy.UNKNOWN:
if version.dev or version.strategy is AwesomeVersionStrategy.UNKNOWN:
return "https://github.com/home-assistant/operating-system/commits/dev"
return (
f"https://github.com/home-assistant/operating-system/releases/tag/{version}"
@@ -304,7 +304,7 @@ class SupervisorSupervisorUpdateEntity(HassioSupervisorEntity, UpdateEntity):
def release_url(self) -> str | None:
"""URL to the full release notes of the latest version available."""
version = AwesomeVersion(self.latest_version)
if version.dev or version.strategy == AwesomeVersionStrategy.UNKNOWN:
if version.dev or version.strategy is AwesomeVersionStrategy.UNKNOWN:
return "https://github.com/home-assistant/supervisor/commits/main"
return f"https://github.com/home-assistant/supervisor/releases/tag/{version}"
+1 -1
View File
@@ -150,7 +150,7 @@ def _get_controller(hass: HomeAssistant) -> Heos:
hass.config_entries.async_entry_for_domain_unique_id(DOMAIN, DOMAIN)
)
if not entry or not entry.state == ConfigEntryState.LOADED:
if not entry or entry.state is not ConfigEntryState.LOADED:
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="integration_not_loaded"
)
@@ -418,10 +418,10 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
otbr_manager = get_otbr_addon_manager(self.hass)
addon_info = await self._async_get_addon_info(otbr_manager)
if addon_info.state == AddonState.NOT_INSTALLED:
if addon_info.state is AddonState.NOT_INSTALLED:
return await self.async_step_install_otbr_addon()
if addon_info.state == AddonState.RUNNING:
if addon_info.state is AddonState.RUNNING:
await otbr_manager.async_stop_addon()
return await self.async_step_start_otbr_addon()
@@ -111,7 +111,7 @@ class WaitingAddonManager(AddonManager):
info = None
# Do not try to uninstall an addon if it is already uninstalled
if info is not None and info.state == AddonState.NOT_INSTALLED:
if info is not None and info.state is AddonState.NOT_INSTALLED:
return
await self.async_uninstall_addon()
@@ -383,7 +383,7 @@ class OptionsFlowHandler(OptionsFlow, ABC):
multipan_manager = await get_multiprotocol_addon_manager(self.hass)
addon_info = await self._async_get_addon_info(multipan_manager)
if addon_info.state == AddonState.NOT_INSTALLED:
if addon_info.state is AddonState.NOT_INSTALLED:
return await self.async_step_addon_not_installed()
return await self.async_step_addon_installed()
@@ -691,10 +691,10 @@ class OptionsFlowHandler(OptionsFlow, ABC):
flasher_manager = get_flasher_addon_manager(self.hass)
addon_info = await self._async_get_addon_info(flasher_manager)
if addon_info.state == AddonState.NOT_INSTALLED:
if addon_info.state is AddonState.NOT_INSTALLED:
return await self.async_step_install_flasher_addon()
if addon_info.state == AddonState.NOT_RUNNING:
if addon_info.state is AddonState.NOT_RUNNING:
return await self.async_step_configure_flasher_addon()
# If the addon is already installed and running, fail
@@ -907,7 +907,7 @@ async def check_multi_pan_addon(hass: HomeAssistant) -> None:
# Request the addon to start if it's not started
# `async_start_addon` returns as soon as the start request has been sent
# and does not wait for the addon to be started, so we raise below
if addon_info.state == AddonState.NOT_RUNNING:
if addon_info.state is AddonState.NOT_RUNNING:
await multipan_manager.async_start_addon()
if addon_info.state not in (AddonState.NOT_INSTALLED, AddonState.RUNNING):
@@ -927,7 +927,7 @@ async def multi_pan_addon_using_device(hass: HomeAssistant, device_path: str) ->
multipan_manager = await get_multiprotocol_addon_manager(hass)
addon_info: AddonInfo = await multipan_manager.async_get_addon_info()
if addon_info.state != AddonState.RUNNING:
if addon_info.state is not AddonState.RUNNING:
return False
if addon_info.options["device"] != device_path:
@@ -124,7 +124,7 @@ class OwningAddon:
except AddonError:
return False
else:
return addon_info.state == AddonState.RUNNING
return addon_info.state is AddonState.RUNNING
@asynccontextmanager
async def temporarily_stop(self, hass: HomeAssistant) -> AsyncGenerator[None]:
@@ -137,7 +137,7 @@ class OwningAddon:
yield
return
if addon_info.state != AddonState.RUNNING:
if addon_info.state is not AddonState.RUNNING:
yield
return
@@ -173,7 +173,7 @@ class OwningIntegration:
yield
return
if entry.state != ConfigEntryState.LOADED:
if entry.state is not ConfigEntryState.LOADED:
yield
return
@@ -213,7 +213,7 @@ async def get_otbr_addon_firmware_info(
except AddonError:
return None
if otbr_addon_info.state == AddonState.NOT_INSTALLED:
if otbr_addon_info.state is AddonState.NOT_INSTALLED:
return None
if (otbr_path := otbr_addon_info.options.get("device")) is None:
@@ -238,7 +238,7 @@ async def get_z2m_addon_firmware_info(
except AddonError:
return None
if z2m_addon_info.state == AddonState.NOT_INSTALLED:
if z2m_addon_info.state is AddonState.NOT_INSTALLED:
return None
serial = z2m_addon_info.options.get("serial")
@@ -286,7 +286,7 @@ async def guess_hardware_owners(
except AddonError:
pass
else:
if multipan_addon_info.state != AddonState.NOT_INSTALLED:
if multipan_addon_info.state is not AddonState.NOT_INSTALLED:
multipan_path = multipan_addon_info.options.get("device")
if multipan_path is not None:
@@ -122,7 +122,7 @@ class HomeeConfigFlow(ConfigFlow, domain=DOMAIN):
existing_entry = await self.async_set_unique_id(self._name)
if (
existing_entry
and existing_entry.state == ConfigEntryState.LOADED
and existing_entry.state is ConfigEntryState.LOADED
and existing_entry.runtime_data.connected
and existing_entry.data[CONF_HOST] != self._host
):
@@ -298,7 +298,7 @@ class HKDevice:
# yet.
attempts = None if self.hass.state is CoreState.running else 1
if (
transport == Transport.BLE
transport is Transport.BLE
and pairing.accessories
and pairing.accessories.has_aid(1)
):
@@ -328,7 +328,7 @@ class HKDevice:
)
entry.async_on_unload(self._async_cancel_subscription_timer)
if transport != Transport.BLE:
if transport is not Transport.BLE:
# Although async_populate_accessories_state fetched the accessory database,
# the /accessories endpoint may return cached values from the accessory's
# perspective. For example, Ecobee thermostats may report stale temperature
@@ -349,7 +349,7 @@ class HKDevice:
await self.async_process_entity_map()
if transport != Transport.BLE:
if transport is not Transport.BLE:
# Start regular polling after entity map is processed
self._async_start_polling()
@@ -359,7 +359,7 @@ class HKDevice:
self.async_set_available_state(self.pairing.is_available)
if transport == Transport.BLE:
if transport is Transport.BLE:
# If we are using BLE, we need to periodically check of the
# BLE device is available since we won't get callbacks
# when it goes away since we HomeKit supports disconnected
@@ -420,7 +420,7 @@ class HKDevice:
identifiers.add((IDENTIFIER_SERIAL_NUMBER, accessory.serial_number))
connections: set[tuple[str, str]] = set()
if self.pairing.transport == Transport.BLE and (
if self.pairing.transport is Transport.BLE and (
discovery := self.pairing.controller.discoveries.get(
normalize_hkid(self.unique_id)
)
@@ -622,7 +622,7 @@ class HKDevice:
current_unique_id.add((accessory.aid, service.iid, None))
for char in service.characteristics:
if self.pairing.transport != Transport.BLE:
if self.pairing.transport is not Transport.BLE:
if char.type == CharacteristicsTypes.THREAD_CONTROL_POINT:
continue
@@ -1057,7 +1057,7 @@ class HKDevice:
@property
def is_unprovisioned_thread_device(self) -> bool:
"""Is this a thread capable device not connected by CoAP."""
if self.pairing.controller.transport_type != TransportType.BLE:
if self.pairing.controller.transport_type is not TransportType.BLE:
return False
if not self.entity_map.aid(1).services.first(
@@ -1069,7 +1069,7 @@ class HKDevice:
async def async_thread_provision(self) -> None:
"""Migrate a HomeKit pairing to CoAP (Thread)."""
if self.pairing.controller.transport_type == TransportType.COAP:
if self.pairing.controller.transport_type is TransportType.COAP:
raise HomeAssistantError("Already connected to a thread network")
if not (dataset := await async_get_preferred_dataset(self.hass)):
@@ -700,7 +700,7 @@ async def async_setup_entry(
@callback
def async_add_accessory(accessory: Accessory) -> bool:
if conn.pairing.transport != Transport.BLE:
if conn.pairing.transport is not Transport.BLE:
return False
accessory_info = accessory.services.first(
@@ -69,7 +69,7 @@ class HusqvarnaAutomowerBleConfigFlow(ConfigFlow, domain=DOMAIN):
await async_get_manufacturer_data({discovery_info.address})
)[discovery_info.address]
if manufacturer_data.product_type != ProductType.MOWER:
if manufacturer_data.product_type is not ProductType.MOWER:
LOGGER.debug(
"Unsupported device: %s (%s)", manufacturer_data, discovery_info
)
@@ -12,6 +12,6 @@
"documentation": "https://www.home-assistant.io/integrations/indevolt",
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "silver",
"quality_scale": "platinum",
"requirements": ["indevolt-api==1.8.1"]
}
@@ -567,7 +567,7 @@ class IntegrationSensor(RestoreSensor):
assert old_timestamp is not None
elapsed_seconds = Decimal(
(new_timestamp - old_timestamp).total_seconds()
if self._last_integration_trigger == _IntegrationTrigger.StateEvent
if self._last_integration_trigger is _IntegrationTrigger.StateEvent
else (new_timestamp - self._last_integration_time).total_seconds()
)
@@ -192,11 +192,11 @@ async def async_update_options(
current_control_mode = fireplace.control_mode
# Only update modes that actually changed
if new_read_mode != current_read_mode:
if new_read_mode is not current_read_mode:
LOGGER.debug("Updating read mode: %s -> %s", current_read_mode, new_read_mode)
await fireplace.set_read_mode(new_read_mode)
if new_control_mode != current_control_mode:
if new_control_mode is not current_control_mode:
LOGGER.debug(
"Updating control mode: %s -> %s", current_control_mode, new_control_mode
)
+3 -3
View File
@@ -126,19 +126,19 @@ class IottyShutter(IottyEntity, CoverEntity):
self._iotty_device.percentage,
)
return (
self._iotty_device.status == ShutterState.STATIONARY
self._iotty_device.status is ShutterState.STATIONARY
and self._iotty_device.percentage == 0
)
@property
def is_opening(self) -> bool:
"""Return true if the Shutter is opening."""
return self._iotty_device.status == ShutterState.OPENING
return self._iotty_device.status is ShutterState.OPENING
@property
def is_closing(self) -> bool:
"""Return true if the Shutter is closing."""
return self._iotty_device.status == ShutterState.CLOSING
return self._iotty_device.status is ShutterState.CLOSING
@property
def supported_features(self) -> CoverEntityFeature:
+3 -3
View File
@@ -380,7 +380,7 @@ class _KnxClimate(ClimateEntity, _KnxEntityBase):
self._attr_fan_modes = [fan_zero_mode, FAN_LOW, FAN_HIGH]
elif fan_max_step == 1:
self._attr_fan_modes = [fan_zero_mode, FAN_ON]
elif device.fan_speed_mode == FanSpeedMode.STEP:
elif device.fan_speed_mode is FanSpeedMode.STEP:
self._attr_fan_modes = [fan_zero_mode] + [
str(i) for i in range(1, fan_max_step + 1)
]
@@ -550,7 +550,7 @@ class _KnxClimate(ClimateEntity, _KnxEntityBase):
if not fan_speed or self._attr_fan_modes is None:
return self.fan_zero_mode
if self._device.fan_speed_mode == FanSpeedMode.STEP:
if self._device.fan_speed_mode is FanSpeedMode.STEP:
return self._attr_fan_modes[fan_speed]
# Find the closest fan mode percentage
@@ -570,7 +570,7 @@ class _KnxClimate(ClimateEntity, _KnxEntityBase):
fan_mode_index = self._attr_fan_modes.index(fan_mode)
if self._device.fan_speed_mode == FanSpeedMode.STEP:
if self._device.fan_speed_mode is FanSpeedMode.STEP:
await self._device.set_fan_speed(fan_mode_index)
return
+2 -6
View File
@@ -373,7 +373,6 @@ class KNXConfigFlow(ConfigFlow, domain=DOMAIN):
)
)
}
return self.async_show_form(step_id="tunnel", data_schema=vol.Schema(fields))
async def async_step_tcp_tunnel_endpoint(
@@ -459,7 +458,7 @@ class KNXConfigFlow(ConfigFlow, domain=DOMAIN):
errors[CONF_HOST] = "invalid_ip_address"
_local_ip = None
if _local := user_input.get(CONF_KNX_LOCAL_IP):
if _local := (user_input.get(CONF_KNX_LOCAL_IP) or None):
try:
_local_ip = await xknx_validate_ip(_local)
ip_v4_validator(_local_ip, multicast=False)
@@ -606,7 +605,6 @@ class KNXConfigFlow(ConfigFlow, domain=DOMAIN):
selector.TextSelectorConfig(type=selector.TextSelectorType.PASSWORD),
),
}
return self.async_show_form(
step_id="secure_tunnel_manual",
data_schema=vol.Schema(fields),
@@ -662,7 +660,6 @@ class KNXConfigFlow(ConfigFlow, domain=DOMAIN):
vol.Coerce(int),
),
}
return self.async_show_form(
step_id="secure_routing_manual",
data_schema=vol.Schema(fields),
@@ -843,7 +840,7 @@ class KNXConfigFlow(ConfigFlow, domain=DOMAIN):
ip_v4_validator(_multicast_group, multicast=True)
except vol.Invalid:
errors[CONF_KNX_MCAST_GRP] = "invalid_ip_address"
if _local := user_input.get(CONF_KNX_LOCAL_IP):
if _local := (user_input.get(CONF_KNX_LOCAL_IP) or None):
try:
_local_ip = await xknx_validate_ip(_local)
ip_v4_validator(_local_ip, multicast=False)
@@ -891,7 +888,6 @@ class KNXConfigFlow(ConfigFlow, domain=DOMAIN):
vol.Required(CONF_KNX_MCAST_PORT, default=_multicast_port): _PORT_SELECTOR,
vol.Optional(CONF_KNX_LOCAL_IP): _IP_SELECTOR,
}
return self.async_show_form(
step_id="routing", data_schema=vol.Schema(fields), errors=errors
)
+1 -1
View File
@@ -258,7 +258,7 @@ class KNXModule:
def connection_state_changed_cb(self, state: XknxConnectionState) -> None:
"""Call invoked after a KNX connection state change was received."""
self.connected = state == XknxConnectionState.CONNECTED
self.connected = state is XknxConnectionState.CONNECTED
for device in self.xknx.devices:
device.after_update()
+3 -3
View File
@@ -246,7 +246,7 @@ def async_host_event_received(
):
_LOGGER.info('The connection to host "%s" has been lost', config_entry.title)
hass.async_create_task(reload_config_entry())
elif event == LcnEvent.BUS_DISCONNECTED:
elif event is LcnEvent.BUS_DISCONNECTED:
_LOGGER.info(
'The connection to the LCN bus via host "%s" has been disconnected',
config_entry.title,
@@ -296,7 +296,7 @@ def _async_fire_access_control_event(
if device is not None:
event_data.update({CONF_DEVICE_ID: device.id})
if inp.periphery == pypck.lcn_defs.AccessControlPeriphery.TRANSMITTER:
if inp.periphery is pypck.lcn_defs.AccessControlPeriphery.TRANSMITTER:
event_data.update(
{
"level": inp.level,
@@ -317,7 +317,7 @@ def _async_fire_send_keys_event(
) -> None:
"""Fire send_keys event."""
for table, action in enumerate(inp.actions):
if action == pypck.lcn_defs.SendKeyCommand.DONTSEND:
if action is pypck.lcn_defs.SendKeyCommand.DONTSEND:
continue
for key, selected in enumerate(inp.keys):
+3 -3
View File
@@ -116,7 +116,7 @@ class LcnClimate(LcnEntity, ClimateEntity):
# Config schema only allows for:
# UnitOfTemperature.CELSIUS and
# UnitOfTemperature.FAHRENHEIT
if self.unit == pypck.lcn_defs.VarUnit.FAHRENHEIT:
if self.unit is pypck.lcn_defs.VarUnit.FAHRENHEIT:
return UnitOfTemperature.FAHRENHEIT
return UnitOfTemperature.CELSIUS
@@ -188,11 +188,11 @@ class LcnClimate(LcnEntity, ClimateEntity):
if not isinstance(input_obj, pypck.inputs.ModStatusVar):
return
self._attr_available = True
if input_obj.get_var() == self.variable:
if input_obj.get_var() is self.variable:
self._attr_current_temperature = float(
input_obj.get_value().to_var_unit(self.unit)
)
elif input_obj.get_var() == self.setpoint:
elif input_obj.get_var() is self.setpoint:
self._is_on = not input_obj.get_value().is_locked_regulator()
if self._is_on:
self._attr_target_temperature = float(
+3 -3
View File
@@ -191,7 +191,7 @@ class LcnRelayCover(LcnEntity, CoverEntity):
)
)
if self.positioning_mode != pypck.lcn_defs.MotorPositioningMode.NONE:
if self.positioning_mode is not pypck.lcn_defs.MotorPositioningMode.NONE:
self._attr_supported_features |= CoverEntityFeature.SET_POSITION
self.motor = pypck.lcn_defs.MotorPort[config[CONF_DOMAIN_DATA][CONF_MOTOR]]
@@ -267,7 +267,7 @@ class LcnRelayCover(LcnEntity, CoverEntity):
| None,
]
] = [self.device_connection.request_status_relays(SCAN_INTERVAL.seconds)]
if self.positioning_mode == pypck.lcn_defs.MotorPositioningMode.BS4:
if self.positioning_mode is pypck.lcn_defs.MotorPositioningMode.BS4:
coros.append(
self.device_connection.request_status_motor_position(
self.motor, self.positioning_mode, SCAN_INTERVAL.seconds
@@ -282,7 +282,7 @@ class LcnRelayCover(LcnEntity, CoverEntity):
self._attr_is_opening = input_obj.is_opening(self.motor.value)
self._attr_is_closing = input_obj.is_closing(self.motor.value)
if self.positioning_mode == pypck.lcn_defs.MotorPositioningMode.NONE:
if self.positioning_mode is pypck.lcn_defs.MotorPositioningMode.NONE:
self._attr_is_closed = input_obj.is_assumed_closed(self.motor.value)
self.async_write_ha_state()
elif (
+1 -1
View File
@@ -144,7 +144,7 @@ class LcnVariableSensor(LcnEntity, SensorEntity):
"""Set sensor value when LCN input object (command) is received."""
if (
not isinstance(input_obj, pypck.inputs.ModStatusVar)
or input_obj.get_var() != self.variable
or input_obj.get_var() is not self.variable
):
return
self._attr_available = True
+1 -1
View File
@@ -330,7 +330,7 @@ class SendKeys(LcnServiceCall):
if (delay_time := service.data[CONF_TIME]) != 0:
hit = pypck.lcn_defs.SendKeyCommand.HIT
if pypck.lcn_defs.SendKeyCommand[service.data[CONF_STATE]] != hit:
if pypck.lcn_defs.SendKeyCommand[service.data[CONF_STATE]] is not hit:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_send_keys_action",
+1 -1
View File
@@ -200,7 +200,7 @@ class LcnRegulatorLockSwitch(LcnEntity, SwitchEntity):
"""Set switch state when LCN input object (command) is received."""
if (
not isinstance(input_obj, pypck.inputs.ModStatusVar)
or input_obj.get_var() != self.setpoint_variable
or input_obj.get_var() is not self.setpoint_variable
):
return
self._attr_available = True
@@ -77,7 +77,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: LunatoneConfigEntry) ->
await coordinator_info.async_config_entry_first_refresh()
if info_api.data is None or info_api.serial_number is None:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryError(
translation_domain=DOMAIN, translation_key="missing_device_info"
)
@@ -35,5 +35,10 @@
"description": "Enter the URL of your Lunatone device.\nHome Assistant will use this address to connect to the device API."
}
}
},
"exceptions": {
"missing_device_info": {
"message": "Unable to read device information. Please verify the device's network connection."
}
}
}
@@ -54,4 +54,4 @@ class LutronOccupancySensor(LutronDevice, BinarySensorEntity):
def _update_attrs(self) -> None:
"""Update the state attributes."""
self._attr_is_on = self._lutron_device.state == OccupancyGroup.State.OCCUPIED
self._attr_is_on = self._lutron_device.state is OccupancyGroup.State.OCCUPIED
@@ -79,7 +79,7 @@ class LutronCasetaShade(LutronCasetaUpdatableEntity, CoverEntity):
"""Stop the cover."""
# Send appropriate directional command before stop to ensure it works correctly
# Use tracked direction if moving, otherwise use position-based heuristic
if self._movement_direction == ShadeMovementDirection.OPENING or (
if self._movement_direction is ShadeMovementDirection.OPENING or (
self._movement_direction in (ShadeMovementDirection.STOPPED, None)
and self.current_cover_position >= 50
):
+4 -4
View File
@@ -179,13 +179,13 @@ async def _client_listen(
try:
await matter_client.start_listening(init_ready)
except MatterError as err:
if entry.state != ConfigEntryState.LOADED:
if entry.state is not ConfigEntryState.LOADED:
raise
LOGGER.error("Failed to listen: %s", err)
except Exception as err:
# We need to guard against unknown exceptions to not crash this task.
LOGGER.exception("Unexpected exception: %s", err)
if entry.state != ConfigEntryState.LOADED:
if entry.state is not ConfigEntryState.LOADED:
raise
if not hass.is_stopping:
@@ -293,14 +293,14 @@ async def _async_ensure_addon_running(
addon_state = addon_info.state
if addon_state == AddonState.NOT_INSTALLED:
if addon_state is AddonState.NOT_INSTALLED:
addon_manager.async_schedule_install_setup_addon(
addon_info.options,
catch_error=True,
)
raise ConfigEntryNotReady
if addon_state == AddonState.NOT_RUNNING:
if addon_state is AddonState.NOT_RUNNING:
addon_manager.async_schedule_start_addon(catch_error=True)
raise ConfigEntryNotReady
@@ -288,10 +288,10 @@ class MatterConfigFlow(ConfigFlow, domain=DOMAIN):
addon_info = await self._async_get_addon_info()
if addon_info.state == AddonState.RUNNING:
if addon_info.state is AddonState.RUNNING:
return await self.async_step_finish_addon_setup()
if addon_info.state == AddonState.NOT_RUNNING:
if addon_info.state is AddonState.NOT_RUNNING:
return await self.async_step_start_addon()
return await self.async_step_install_addon()
+1 -1
View File
@@ -173,7 +173,7 @@ class MatterUpdate(MatterEntity, UpdateEntity):
release_notes = ""
# insert extra heavy warning case the update is not from the main net
if self._software_update.update_source != UpdateSource.MAIN_NET_DCL:
if self._software_update.update_source is not UpdateSource.MAIN_NET_DCL:
release_notes += (
"\n\n<ha-alert alert-type='warning'>"
"Update provided by "
@@ -437,7 +437,7 @@ def _get_media_event_data(
if (
not config_entry_id
or not (entry := hass.config_entries.async_get_entry(config_entry_id))
or entry.state != ConfigEntryState.LOADED
or entry.state is not ConfigEntryState.LOADED
):
return {}
@@ -122,7 +122,7 @@ class MotionEyeMediaSource(MediaSource):
def _get_config_or_raise(self, config_id: str) -> MotionEyeConfigEntry:
"""Get a config entry from a URL."""
entry = self.hass.config_entries.async_get_entry(config_id)
if not entry or entry.state != ConfigEntryState.LOADED:
if not entry or entry.state is not ConfigEntryState.LOADED:
raise MediaSourceError(f"Unable to find config entry with id: {config_id}")
return entry
+2 -2
View File
@@ -4154,11 +4154,11 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
description_placeholders={"addon": self._addon_manager.addon_name},
) from err
if addon_info.state == AddonState.RUNNING:
if addon_info.state is AddonState.RUNNING:
# Finish setup using discovery info
return await self.async_step_setup_entry_from_discovery()
if addon_info.state == AddonState.NOT_RUNNING:
if addon_info.state is AddonState.NOT_RUNNING:
return await self.async_step_start_addon()
# Install the add-on and start it
+1 -1
View File
@@ -219,7 +219,7 @@ async def async_wait_for_mqtt_client(hass: HomeAssistant) -> bool:
return False
entry = hass.config_entries.async_entries(DOMAIN)[0]
if entry.state == ConfigEntryState.LOADED:
if entry.state is ConfigEntryState.LOADED:
return True
state_reached_future: asyncio.Future[bool]
@@ -23,7 +23,7 @@ from music_assistant_models.errors import (
from music_assistant_models.player import Player
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.const import CONF_URL, EVENT_HOMEASSISTANT_STOP, Platform
from homeassistant.const import CONF_TOKEN, CONF_URL, EVENT_HOMEASSISTANT_STOP, Platform
from homeassistant.core import Event, HomeAssistant
from homeassistant.exceptions import (
ConfigEntryAuthFailed,
@@ -38,7 +38,7 @@ from homeassistant.helpers.issue_registry import (
async_delete_issue,
)
from .const import ATTR_CONF_EXPOSE_PLAYER_TO_HA, CONF_TOKEN, DOMAIN, LOGGER
from .const import ATTR_CONF_EXPOSE_PLAYER_TO_HA, DOMAIN, LOGGER
from .helpers import get_music_assistant_client
from .services import register_actions
@@ -265,12 +265,12 @@ async def _client_listen(
try:
await mass.start_listening(init_ready)
except MusicAssistantError as err:
if entry.state != ConfigEntryState.LOADED:
if entry.state is not ConfigEntryState.LOADED:
raise
LOGGER.error("Failed to listen: %s", err)
except Exception as err: # pylint: disable=broad-except
# We need to guard against unknown exceptions to not crash this task.
if entry.state != ConfigEntryState.LOADED:
if entry.state is not ConfigEntryState.LOADED:
raise
LOGGER.exception("Unexpected exception: %s", err)
@@ -21,7 +21,7 @@ from homeassistant.config_entries import (
ConfigFlow,
ConfigFlowResult,
)
from homeassistant.const import CONF_URL
from homeassistant.const import CONF_TOKEN, CONF_URL
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.config_entry_oauth2_flow import (
@@ -31,13 +31,7 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import (
AUTH_SCHEMA_VERSION,
CONF_TOKEN,
DOMAIN,
HASSIO_DISCOVERY_SCHEMA_VERSION,
LOGGER,
)
from .const import AUTH_SCHEMA_VERSION, DOMAIN, HASSIO_DISCOVERY_SCHEMA_VERSION, LOGGER
DEFAULT_TITLE = "Music Assistant"
DEFAULT_URL = "http://mass.local:8095"
@@ -12,9 +12,6 @@ AUTH_SCHEMA_VERSION = 28
# Schema version where hassio discovery support was added
HASSIO_DISCOVERY_SCHEMA_VERSION = 28
# pylint: disable-next=home-assistant-duplicate-const
CONF_TOKEN = "token"
ATTR_IS_GROUP = "is_group"
ATTR_GROUP_MEMBERS = "group_members"
ATTR_GROUP_PARENTS = "group_parents"
+3 -3
View File
@@ -81,17 +81,17 @@ class MySensorsCover(MySensorsChildEntity, CoverEntity):
@property
def is_closed(self) -> bool:
"""Return True if the cover is closed."""
return self.get_cover_state() == CoverState.CLOSED
return self.get_cover_state() is CoverState.CLOSED
@property
def is_closing(self) -> bool:
"""Return True if the cover is closing."""
return self.get_cover_state() == CoverState.CLOSING
return self.get_cover_state() is CoverState.CLOSING
@property
def is_opening(self) -> bool:
"""Return True if the cover is opening."""
return self.get_cover_state() == CoverState.OPENING
return self.get_cover_state() is CoverState.OPENING
@property
def current_cover_position(self) -> int | None:
@@ -144,7 +144,7 @@ class MyUplinkDevicePointBinarySensor(MyUplinkEntity, BinarySensorEntity):
"""Return device data availability."""
return super().available and (
self.coordinator.data.devices[self.device_id].connectionState
== DeviceConnectionState.Connected
is DeviceConnectionState.Connected
)
@@ -172,7 +172,7 @@ class MyUplinkDeviceBinarySensor(MyUplinkEntity, BinarySensorEntity):
"""Binary sensor state value."""
return (
self.coordinator.data.devices[self.device_id].connectionState
== DeviceConnectionState.Connected
is DeviceConnectionState.Connected
)
@@ -244,7 +244,7 @@ def async_get_client_for_service_call(
if device_entry := device_registry.async_get(device_id):
for entry_id in device_entry.config_entries:
if entry := hass.config_entries.async_get_entry(entry_id):
if entry.domain == DOMAIN and entry.state == ConfigEntryState.LOADED:
if entry.domain == DOMAIN and entry.state is ConfigEntryState.LOADED:
return cast(OctoprintConfigEntry, entry).runtime_data.octoprint
raise ServiceValidationError(

Some files were not shown because too many files have changed in this diff Show More