mirror of
https://github.com/home-assistant/core.git
synced 2026-05-22 08:45:16 +02:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3257275c5a | |||
| cb54fd4921 | |||
| b391fc61ea | |||
| fcd4e4939c | |||
| deb8b5da05 | |||
| c7754a6ce9 | |||
| 242724bd50 | |||
| 42454563db | |||
| bf03d0c216 | |||
| 568107e06b | |||
| 7da44428b6 | |||
| 0a27f31949 | |||
| 905b868c82 | |||
| 3187289913 | |||
| 87cecd4a44 | |||
| fed38b0e38 | |||
| 6a36d1260b | |||
| 49fc1b413d | |||
| bffb0417cc | |||
| 8b8c687fc3 | |||
| e3dd6b5fc5 | |||
| 94d620438b | |||
| 8867b792dc | |||
| 97967abfeb | |||
| af8fea272d | |||
| 2db0eed570 | |||
| ded1628c20 |
@@ -25,6 +25,7 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
|
||||
|
||||
- When entering a new environment or worktree, run `script/setup` to set up the virtual environment with all development dependencies (pylint, pre-commit hooks, etc.). This is required before committing.
|
||||
- .vscode/tasks.json contains useful commands used for development.
|
||||
- After finishing a code session, run `uv run prek run --all-files` to check for linting and formatting issues.
|
||||
|
||||
## Python Syntax Notes
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
|
||||
|
||||
- When entering a new environment or worktree, run `script/setup` to set up the virtual environment with all development dependencies (pylint, pre-commit hooks, etc.). This is required before committing.
|
||||
- .vscode/tasks.json contains useful commands used for development.
|
||||
- After finishing a code session, run `uv run prek run --all-files` to check for linting and formatting issues.
|
||||
|
||||
## Python Syntax Notes
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"bluetooth-adapters==2.1.0",
|
||||
"bluetooth-auto-recovery==1.5.3",
|
||||
"bluetooth-data-tools==1.29.11",
|
||||
"dbus-fast==5.0.0",
|
||||
"habluetooth==6.1.0"
|
||||
"dbus-fast==5.0.3",
|
||||
"habluetooth==6.2.0"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -26,12 +26,12 @@ from homeassistant.components.climate import (
|
||||
HVACAction,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||
from homeassistant.const import ATTR_LOCKED, ATTR_TEMPERATURE, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import DeconzConfigEntry
|
||||
from .const import ATTR_LOCKED, ATTR_OFFSET, ATTR_VALVE
|
||||
from .const import ATTR_OFFSET, ATTR_VALVE
|
||||
from .entity import DeconzDevice
|
||||
from .hub import DeconzHub
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ PLATFORMS = [
|
||||
]
|
||||
|
||||
ATTR_DARK = "dark"
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
ATTR_LOCKED = "locked"
|
||||
ATTR_OFFSET = "offset"
|
||||
ATTR_ON = "on"
|
||||
ATTR_VALVE = "valve"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"quality_scale": "internal",
|
||||
"requirements": [
|
||||
"aiodhcpwatcher==1.2.1",
|
||||
"aiodiscover==3.2.0",
|
||||
"aiodiscover==3.2.3",
|
||||
"cached-ipaddress==1.0.1"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
):
|
||||
|
||||
@@ -6,7 +6,6 @@ import logging
|
||||
|
||||
import aiodns
|
||||
from aiodns.error import DNSError
|
||||
from pycares import AresError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PORT
|
||||
@@ -78,11 +77,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: DnsIPConfigEntry) -> boo
|
||||
) from err
|
||||
|
||||
errors = [
|
||||
result
|
||||
for result in results
|
||||
if isinstance(
|
||||
result, (TimeoutError, DNSError, AresError, asyncio.CancelledError)
|
||||
)
|
||||
result for result in results if isinstance(result, (TimeoutError, DNSError))
|
||||
]
|
||||
if errors and len(errors) == len(results):
|
||||
await _close_resolvers()
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["guntamatic==1.8.0"]
|
||||
"requirements": ["guntamatic==1.9.0"]
|
||||
}
|
||||
|
||||
@@ -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}"
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
):
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -4,12 +4,13 @@ from dataclasses import dataclass
|
||||
from typing import cast
|
||||
|
||||
from homeassistant.components.application_credentials import AuthorizationServer
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_entry_oauth2_flow, llm
|
||||
|
||||
from .application_credentials import authorization_server_context
|
||||
from .const import CONF_ACCESS_TOKEN, CONF_AUTHORIZATION_URL, CONF_TOKEN_URL, DOMAIN
|
||||
from .const import CONF_AUTHORIZATION_URL, CONF_TOKEN_URL, DOMAIN
|
||||
from .coordinator import ModelContextProtocolCoordinator, TokenManager
|
||||
from .types import ModelContextProtocolConfigEntry
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from yarl import URL
|
||||
|
||||
from homeassistant.components.application_credentials import AuthorizationServer
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
|
||||
from homeassistant.const import CONF_TOKEN, CONF_URL
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN, CONF_URL
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
@@ -24,13 +24,7 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
|
||||
from . import async_get_config_entry_implementation
|
||||
from .application_credentials import authorization_server_context
|
||||
from .const import (
|
||||
CONF_ACCESS_TOKEN,
|
||||
CONF_AUTHORIZATION_URL,
|
||||
CONF_SCOPE,
|
||||
CONF_TOKEN_URL,
|
||||
DOMAIN,
|
||||
)
|
||||
from .const import CONF_AUTHORIZATION_URL, CONF_SCOPE, CONF_TOKEN_URL, DOMAIN
|
||||
from .coordinator import TokenManager, mcp_client
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
DOMAIN = "mcp"
|
||||
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
CONF_ACCESS_TOKEN = "access_token"
|
||||
CONF_AUTHORIZATION_URL = "authorization_url"
|
||||
CONF_TOKEN_URL = "token_url"
|
||||
CONF_SCOPE = "scope"
|
||||
|
||||
@@ -41,7 +41,7 @@ from mcp.shared.message import SessionMessage
|
||||
|
||||
from homeassistant.components import conversation
|
||||
from homeassistant.components.http import KEY_HASS, HomeAssistantView
|
||||
from homeassistant.const import CONF_LLM_HASS_API
|
||||
from homeassistant.const import CONF_LLM_HASS_API, CONTENT_TYPE_JSON
|
||||
from homeassistant.core import Context, HomeAssistant, callback
|
||||
from homeassistant.helpers import llm
|
||||
|
||||
@@ -56,10 +56,6 @@ _LOGGER = logging.getLogger(__name__)
|
||||
STREAMABLE_API = "/api/mcp"
|
||||
TIMEOUT = 60 # Seconds
|
||||
|
||||
# Content types
|
||||
# pylint: disable-next=home-assistant-duplicate-const
|
||||
CONTENT_TYPE_JSON = "application/json"
|
||||
|
||||
# Legacy SSE endpoint
|
||||
SSE_API = f"/{DOMAIN}/sse"
|
||||
MESSAGES_API = f"/{DOMAIN}/messages/{{session_id}}"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -259,7 +259,7 @@ class OllamaSubentryFlowHandler(ConfigSubentryFlow):
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> SubentryFlowResult:
|
||||
"""Handle model selection and configuration 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")
|
||||
|
||||
if user_input is None:
|
||||
|
||||
@@ -160,6 +160,6 @@ class ChannelMutingCoordinator(DataUpdateCoordinator[ChannelMutingData]):
|
||||
self._desired = {
|
||||
channel: desired
|
||||
for channel, desired in self._desired.items()
|
||||
if self.data[channel] != desired
|
||||
if self.data[channel] is not desired
|
||||
}
|
||||
self.async_set_updated_data(self.data)
|
||||
|
||||
@@ -197,7 +197,7 @@ class OnkyoMediaPlayer(MediaPlayerEntity):
|
||||
|
||||
name = manager.info.model_name
|
||||
identifier = manager.info.identifier
|
||||
self._attr_name = f"{name}{' ' + ZONES[zone] if zone != Zone.MAIN else ''}"
|
||||
self._attr_name = f"{name}{' ' + ZONES[zone] if zone is not Zone.MAIN else ''}"
|
||||
self._attr_unique_id = f"{identifier}_{zone.value}"
|
||||
|
||||
self._volume_resolution = volume_resolution
|
||||
@@ -226,7 +226,7 @@ class OnkyoMediaPlayer(MediaPlayerEntity):
|
||||
self._attr_sound_mode_list = list(self._rev_sound_mode_mapping)
|
||||
|
||||
self._attr_supported_features = SUPPORTED_FEATURES_BASE
|
||||
if zone == Zone.MAIN:
|
||||
if zone is Zone.MAIN:
|
||||
self._attr_supported_features |= SUPPORTED_FEATURES_VOLUME
|
||||
self._supports_volume = True
|
||||
self._attr_supported_features |= MediaPlayerEntityFeature.SELECT_SOUND_MODE
|
||||
@@ -259,7 +259,7 @@ class OnkyoMediaPlayer(MediaPlayerEntity):
|
||||
await self._manager.write(query.TunerPreset(self._zone))
|
||||
if self._supports_sound_mode is not None:
|
||||
await self._manager.write(query.ListeningMode(self._zone))
|
||||
if self._zone == Zone.MAIN:
|
||||
if self._zone is Zone.MAIN:
|
||||
await self._manager.write(query.HDMIOutput())
|
||||
await self._manager.write(query.AudioInformation())
|
||||
await self._manager.write(query.VideoInformation())
|
||||
@@ -386,7 +386,7 @@ class OnkyoMediaPlayer(MediaPlayerEntity):
|
||||
self._attr_volume_level = min(1, volume_level)
|
||||
|
||||
case status.Muting(param=muting):
|
||||
self._attr_is_volume_muted = bool(muting == status.Muting.Param.ON)
|
||||
self._attr_is_volume_muted = bool(muting is status.Muting.Param.ON)
|
||||
|
||||
case status.InputSource(param=source):
|
||||
if source in self._source_mapping:
|
||||
|
||||
@@ -89,6 +89,6 @@ class OnkyoChannelMutingSwitch(
|
||||
"""Handle updated data from the coordinator."""
|
||||
value = self.coordinator.data.get(self._channel)
|
||||
self._attr_is_on = (
|
||||
None if value is None else value == status.ChannelMuting.Param.ON
|
||||
None if value is None else value is status.ChannelMuting.Param.ON
|
||||
)
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@@ -127,8 +127,8 @@ class EventManager:
|
||||
def started(self) -> bool:
|
||||
"""Return True if event manager is started."""
|
||||
return (
|
||||
self.webhook_manager.state == WebHookManagerState.STARTED
|
||||
or self.pullpoint_manager.state == PullPointManagerState.STARTED
|
||||
self.webhook_manager.state is WebHookManagerState.STARTED
|
||||
or self.pullpoint_manager.state is PullPointManagerState.STARTED
|
||||
)
|
||||
|
||||
@callback
|
||||
@@ -260,7 +260,7 @@ class EventManager:
|
||||
@callback
|
||||
def async_webhook_failed(self) -> None:
|
||||
"""Mark webhook as failed."""
|
||||
if self.pullpoint_manager.state != PullPointManagerState.PAUSED:
|
||||
if self.pullpoint_manager.state is not PullPointManagerState.PAUSED:
|
||||
return
|
||||
LOGGER.debug("%s: Switching to PullPoint for events", self.name)
|
||||
self.pullpoint_manager.async_resume()
|
||||
@@ -268,7 +268,7 @@ class EventManager:
|
||||
@callback
|
||||
def async_webhook_working(self) -> None:
|
||||
"""Mark webhook as working."""
|
||||
if self.pullpoint_manager.state != PullPointManagerState.STARTED:
|
||||
if self.pullpoint_manager.state is not PullPointManagerState.STARTED:
|
||||
return
|
||||
LOGGER.debug("%s: Switching to webhook for events", self.name)
|
||||
self.pullpoint_manager.async_pause()
|
||||
@@ -308,7 +308,7 @@ class PullPointManager:
|
||||
|
||||
async def async_start(self) -> bool:
|
||||
"""Start pullpoint subscription."""
|
||||
assert self.state == PullPointManagerState.STOPPED, (
|
||||
assert self.state is PullPointManagerState.STOPPED, (
|
||||
"PullPoint manager already started"
|
||||
)
|
||||
LOGGER.debug("%s: Starting PullPoint manager", self._name)
|
||||
@@ -462,7 +462,7 @@ class PullPointManager:
|
||||
finally:
|
||||
self.async_schedule_pull_messages(next_pull_delay)
|
||||
|
||||
if self.state != PullPointManagerState.STARTED:
|
||||
if self.state is not PullPointManagerState.STARTED:
|
||||
# If the webhook became started working during the long poll,
|
||||
# and we got paused, our data is stale and we should not process it.
|
||||
LOGGER.debug(
|
||||
@@ -507,7 +507,7 @@ class PullPointManager:
|
||||
Must not check if the webhook is working.
|
||||
"""
|
||||
self.async_cancel_pull_messages()
|
||||
if self.state != PullPointManagerState.STARTED:
|
||||
if self.state is not PullPointManagerState.STARTED:
|
||||
return
|
||||
if self._pullpoint_manager:
|
||||
when = delay if delay is not None else PULLPOINT_COOLDOWN_TIME
|
||||
@@ -566,7 +566,7 @@ class WebHookManager:
|
||||
async def async_start(self) -> bool:
|
||||
"""Start polling events."""
|
||||
LOGGER.debug("%s: Starting webhook manager", self._name)
|
||||
assert self.state == WebHookManagerState.STOPPED, (
|
||||
assert self.state is WebHookManagerState.STOPPED, (
|
||||
"Webhook manager already started"
|
||||
)
|
||||
assert self._webhook_url is None, "Webhook already registered"
|
||||
|
||||
@@ -141,7 +141,7 @@ class ConversationFlowHandler(OpenRouterSubentryFlowHandler):
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> SubentryFlowResult:
|
||||
"""Manage conversation agent configuration."""
|
||||
if self._get_entry().state != ConfigEntryState.LOADED:
|
||||
if self._get_entry().state is not ConfigEntryState.LOADED:
|
||||
return self.async_abort(reason="entry_not_loaded")
|
||||
|
||||
if user_input is not None:
|
||||
@@ -256,7 +256,7 @@ class AITaskDataFlowHandler(OpenRouterSubentryFlowHandler):
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> SubentryFlowResult:
|
||||
"""Manage AI task configuration."""
|
||||
if self._get_entry().state != ConfigEntryState.LOADED:
|
||||
if self._get_entry().state is not ConfigEntryState.LOADED:
|
||||
return self.async_abort(reason="entry_not_loaded")
|
||||
|
||||
if user_input is not None:
|
||||
|
||||
@@ -255,7 +255,7 @@ class OpenAISubentryFlowHandler(ConfigSubentryFlow):
|
||||
) -> SubentryFlowResult:
|
||||
"""Manage 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")
|
||||
|
||||
options = self.options
|
||||
@@ -710,7 +710,7 @@ class OpenAISubentrySTTFlowHandler(ConfigSubentryFlow):
|
||||
) -> SubentryFlowResult:
|
||||
"""Manage 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")
|
||||
|
||||
options = self.options
|
||||
@@ -799,7 +799,7 @@ class OpenAISubentryTTSFlowHandler(ConfigSubentryFlow):
|
||||
) -> SubentryFlowResult:
|
||||
"""Manage 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")
|
||||
|
||||
options = self.options
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["opendisplay"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["py-opendisplay==5.9.0"]
|
||||
"requirements": ["py-opendisplay==7.2.3"]
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user