diff --git a/.github/copilot-instructions.md b/AGENTS.md similarity index 100% rename from .github/copilot-instructions.md rename to AGENTS.md diff --git a/CLAUDE.md b/CLAUDE.md index 02dd134122ea96..47dc3e3d863cfb 120000 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1 +1 @@ -.github/copilot-instructions.md \ No newline at end of file +AGENTS.md \ No newline at end of file diff --git a/homeassistant/components/acaia/coordinator.py b/homeassistant/components/acaia/coordinator.py index b42cbccaee50af..9f29c844235f4f 100644 --- a/homeassistant/components/acaia/coordinator.py +++ b/homeassistant/components/acaia/coordinator.py @@ -12,11 +12,13 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_ADDRESS from homeassistant.core import HomeAssistant +from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import CONF_IS_NEW_STYLE_SCALE SCAN_INTERVAL = timedelta(seconds=15) +UPDATE_DEBOUNCE_TIME = 0.2 _LOGGER = logging.getLogger(__name__) @@ -38,11 +40,19 @@ def __init__(self, hass: HomeAssistant, entry: AcaiaConfigEntry) -> None: config_entry=entry, ) + debouncer = Debouncer( + hass=hass, + logger=_LOGGER, + cooldown=UPDATE_DEBOUNCE_TIME, + immediate=True, + function=self.async_update_listeners, + ) + self._scale = AcaiaScale( address_or_ble_device=entry.data[CONF_ADDRESS], name=entry.title, is_new_style_scale=entry.data[CONF_IS_NEW_STYLE_SCALE], - notify_callback=self.async_update_listeners, + notify_callback=debouncer.async_schedule_call, scanner=async_get_scanner(hass), ) diff --git a/homeassistant/components/airos/manifest.json b/homeassistant/components/airos/manifest.json index a1aa96cff71a8d..02a1ca997fb2f2 100644 --- a/homeassistant/components/airos/manifest.json +++ b/homeassistant/components/airos/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/airos", "iot_class": "local_polling", "quality_scale": "bronze", - "requirements": ["airos==0.5.4"] + "requirements": ["airos==0.5.5"] } diff --git a/homeassistant/components/airthings_ble/config_flow.py b/homeassistant/components/airthings_ble/config_flow.py index fa6a52a5a79834..6a6857d95b34fb 100644 --- a/homeassistant/components/airthings_ble/config_flow.py +++ b/homeassistant/components/airthings_ble/config_flow.py @@ -8,6 +8,7 @@ from airthings_ble import AirthingsBluetoothDeviceData, AirthingsDevice from bleak import BleakError +from habluetooth import BluetoothServiceInfoBleak import voluptuous as vol from homeassistant.components import bluetooth @@ -44,7 +45,7 @@ def get_name(device: AirthingsDevice) -> str: name = device.friendly_name() if identifier := device.identifier: - name += f" ({identifier})" + name += f" ({device.model.value}{identifier})" return name @@ -117,6 +118,12 @@ async def async_step_bluetooth_confirm( ) -> ConfigFlowResult: """Confirm discovery.""" if user_input is not None: + if ( + self._discovered_device is not None + and self._discovered_device.device.firmware.need_firmware_upgrade + ): + return self.async_abort(reason="firmware_upgrade_required") + return self.async_create_entry( title=self.context["title_placeholders"]["name"], data={} ) @@ -137,6 +144,9 @@ async def async_step_user( self._abort_if_unique_id_configured() discovery = self._discovered_devices[address] + if discovery.device.firmware.need_firmware_upgrade: + return self.async_abort(reason="firmware_upgrade_required") + self.context["title_placeholders"] = { "name": discovery.name, } @@ -146,21 +156,27 @@ async def async_step_user( return self.async_create_entry(title=discovery.name, data={}) current_addresses = self._async_current_ids(include_ignore=False) + devices: list[BluetoothServiceInfoBleak] = [] for discovery_info in async_discovered_service_info(self.hass): address = discovery_info.address if address in current_addresses or address in self._discovered_devices: continue - if MFCT_ID not in discovery_info.manufacturer_data: continue - if not any(uuid in SERVICE_UUIDS for uuid in discovery_info.service_uuids): continue + devices.append(discovery_info) + for discovery_info in devices: + address = discovery_info.address try: device = await self._get_device_data(discovery_info) except AirthingsDeviceUpdateError: - return self.async_abort(reason="cannot_connect") + _LOGGER.error( + "Error connecting to and getting data from %s", + discovery_info.address, + ) + continue except Exception: _LOGGER.exception("Unknown error occurred") return self.async_abort(reason="unknown") diff --git a/homeassistant/components/airthings_ble/strings.json b/homeassistant/components/airthings_ble/strings.json index f73546bbe422e6..f5639e8da8f133 100644 --- a/homeassistant/components/airthings_ble/strings.json +++ b/homeassistant/components/airthings_ble/strings.json @@ -20,6 +20,7 @@ "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "firmware_upgrade_required": "Your device requires a firmware upgrade. Please use the Airthings app (Android/iOS) to upgrade it.", "unknown": "[%key:common::config_flow::error::unknown%]" } }, diff --git a/homeassistant/components/alexa_devices/binary_sensor.py b/homeassistant/components/alexa_devices/binary_sensor.py index 010a561fa77ea8..8347fa34423a62 100644 --- a/homeassistant/components/alexa_devices/binary_sensor.py +++ b/homeassistant/components/alexa_devices/binary_sensor.py @@ -51,7 +51,9 @@ class AmazonBinarySensorEntityDescription(BinarySensorEntityDescription): ), is_supported=lambda device, key: device.sensors.get(key) is not None, is_available_fn=lambda device, key: ( - device.online and device.sensors[key].error is False + device.online + and (sensor := device.sensors.get(key)) is not None + and sensor.error is False ), ), ) diff --git a/homeassistant/components/alexa_devices/sensor.py b/homeassistant/components/alexa_devices/sensor.py index e6dbc251b9507a..57332b8ce3b146 100644 --- a/homeassistant/components/alexa_devices/sensor.py +++ b/homeassistant/components/alexa_devices/sensor.py @@ -32,7 +32,9 @@ class AmazonSensorEntityDescription(SensorEntityDescription): native_unit_of_measurement_fn: Callable[[AmazonDevice, str], str] | None = None is_available_fn: Callable[[AmazonDevice, str], bool] = lambda device, key: ( - device.online and device.sensors[key].error is False + device.online + and (sensor := device.sensors.get(key)) is not None + and sensor.error is False ) @@ -40,9 +42,9 @@ class AmazonSensorEntityDescription(SensorEntityDescription): AmazonSensorEntityDescription( key="temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement_fn=lambda device, _key: ( + native_unit_of_measurement_fn=lambda device, key: ( UnitOfTemperature.CELSIUS - if device.sensors[_key].scale == "CELSIUS" + if key in device.sensors and device.sensors[key].scale == "CELSIUS" else UnitOfTemperature.FAHRENHEIT ), state_class=SensorStateClass.MEASUREMENT, diff --git a/homeassistant/components/alexa_devices/switch.py b/homeassistant/components/alexa_devices/switch.py index 2994ab777514dc..003f57620798c0 100644 --- a/homeassistant/components/alexa_devices/switch.py +++ b/homeassistant/components/alexa_devices/switch.py @@ -29,7 +29,9 @@ class AmazonSwitchEntityDescription(SwitchEntityDescription): is_on_fn: Callable[[AmazonDevice], bool] is_available_fn: Callable[[AmazonDevice, str], bool] = lambda device, key: ( - device.online and device.sensors[key].error is False + device.online + and (sensor := device.sensors.get(key)) is not None + and sensor.error is False ) method: str diff --git a/homeassistant/components/google_generative_ai_conversation/entity.py b/homeassistant/components/google_generative_ai_conversation/entity.py index 74b76d9bb83464..54ef22bd1a58ed 100644 --- a/homeassistant/components/google_generative_ai_conversation/entity.py +++ b/homeassistant/components/google_generative_ai_conversation/entity.py @@ -456,6 +456,7 @@ def __init__( """Initialize the agent.""" self.entry = entry self.subentry = subentry + self.default_model = default_model self._attr_name = subentry.title self._genai_client = entry.runtime_data self._attr_unique_id = subentry.subentry_id @@ -489,7 +490,7 @@ async def _async_handle_chat_log( tools = tools or [] tools.append(Tool(google_search=GoogleSearch())) - model_name = options.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL) + model_name = options.get(CONF_CHAT_MODEL, self.default_model) # Avoid INVALID_ARGUMENT Developer instruction is not enabled for supports_system_instruction = ( "gemma" not in model_name @@ -620,7 +621,7 @@ async def _async_handle_chat_log( def create_generate_content_config(self) -> GenerateContentConfig: """Create the GenerateContentConfig for the LLM.""" options = self.subentry.data - model = options.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL) + model = options.get(CONF_CHAT_MODEL, self.default_model) thinking_config: ThinkingConfig | None = None if model.startswith("models/gemini-2.5") and not model.endswith( ("tts", "image", "image-preview") diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 1acaae2b583426..09cd880a4929b7 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -14,6 +14,6 @@ "documentation": "https://www.home-assistant.io/integrations/homekit_controller", "iot_class": "local_push", "loggers": ["aiohomekit", "commentjson"], - "requirements": ["aiohomekit==3.2.19"], + "requirements": ["aiohomekit==3.2.20"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."] } diff --git a/homeassistant/components/lamarzocco/sensor.py b/homeassistant/components/lamarzocco/sensor.py index 1f4983a03a8f22..2e36db85fc4b35 100644 --- a/homeassistant/components/lamarzocco/sensor.py +++ b/homeassistant/components/lamarzocco/sensor.py @@ -5,7 +5,7 @@ from datetime import datetime from typing import cast -from pylamarzocco.const import BackFlushStatus, ModelName, WidgetType +from pylamarzocco.const import BackFlushStatus, MachineState, ModelName, WidgetType from pylamarzocco.models import ( BackFlush, BaseWidgetOutput, @@ -97,7 +97,14 @@ class LaMarzoccoSensorEntityDescription( ).brewing_start_time ), entity_category=EntityCategory.DIAGNOSTIC, - available_fn=(lambda coordinator: not coordinator.websocket_terminated), + available_fn=( + lambda coordinator: not coordinator.websocket_terminated + and cast( + MachineStatus, + coordinator.device.dashboard.config[WidgetType.CM_MACHINE_STATUS], + ).status + is MachineState.BREWING + ), ), LaMarzoccoSensorEntityDescription( key="steam_boiler_ready_time", diff --git a/homeassistant/components/ntfy/manifest.json b/homeassistant/components/ntfy/manifest.json index 95e0a7857c9508..279d30d9f9fbc8 100644 --- a/homeassistant/components/ntfy/manifest.json +++ b/homeassistant/components/ntfy/manifest.json @@ -7,5 +7,5 @@ "iot_class": "cloud_push", "loggers": ["aionfty"], "quality_scale": "platinum", - "requirements": ["aiontfy==0.6.0"] + "requirements": ["aiontfy==0.6.1"] } diff --git a/homeassistant/components/ntfy/sensor.py b/homeassistant/components/ntfy/sensor.py index 0180d9fce72b53..8948dc3f5f61ec 100644 --- a/homeassistant/components/ntfy/sensor.py +++ b/homeassistant/components/ntfy/sensor.py @@ -163,7 +163,7 @@ class NtfySensor(StrEnum): device_class=SensorDeviceClass.DATA_SIZE, native_unit_of_measurement=UnitOfInformation.BYTES, suggested_unit_of_measurement=UnitOfInformation.MEBIBYTES, - suggested_display_precision=0, + suggested_display_precision=2, ), NtfySensorEntityDescription( key=NtfySensor.ATTACHMENT_TOTAL_SIZE_REMAINING, @@ -172,7 +172,7 @@ class NtfySensor(StrEnum): device_class=SensorDeviceClass.DATA_SIZE, native_unit_of_measurement=UnitOfInformation.BYTES, suggested_unit_of_measurement=UnitOfInformation.MEBIBYTES, - suggested_display_precision=0, + suggested_display_precision=2, entity_registry_enabled_default=False, ), NtfySensorEntityDescription( diff --git a/homeassistant/components/shelly/binary_sensor.py b/homeassistant/components/shelly/binary_sensor.py index d292e2baf38b4b..3cce2f0183f555 100644 --- a/homeassistant/components/shelly/binary_sensor.py +++ b/homeassistant/components/shelly/binary_sensor.py @@ -319,7 +319,7 @@ def __init__( ), "presencezone_state": RpcBinarySensorDescription( key="presencezone", - sub_key="state", + sub_key="value", name="Occupancy", device_class=BinarySensorDeviceClass.OCCUPANCY, entity_class=RpcPresenceBinarySensor, diff --git a/homeassistant/components/shelly/const.py b/homeassistant/components/shelly/const.py index 47a31163fe5f4f..5606d3a8ce9446 100644 --- a/homeassistant/components/shelly/const.py +++ b/homeassistant/components/shelly/const.py @@ -268,15 +268,7 @@ class BLEScannerMode(StrEnum): CONF_GEN = "gen" -VIRTUAL_COMPONENTS = ( - "boolean", - "button", - "enum", - "input", - "number", - "presencezone", - "text", -) +VIRTUAL_COMPONENTS = ("boolean", "button", "enum", "input", "number", "text") VIRTUAL_COMPONENTS_MAP = { "binary_sensor": {"types": ["boolean"], "modes": ["label"]}, "button": {"types": ["button"], "modes": ["button"]}, diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index bfdbcee74a1cb9..6cd90f1feb9d9a 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -402,7 +402,7 @@ def get_rpc_channel_name(device: RpcDevice, key: str) -> str | None: if key in device.config and key != "em:0": # workaround for Pro 3EM, we don't want to get name for em:0 if component_name := device.config[key].get("name"): - if component in (*VIRTUAL_COMPONENTS, "script"): + if component in (*VIRTUAL_COMPONENTS, "presencezone", "script"): return cast(str, component_name) return cast(str, component_name) if instances == 1 else None diff --git a/homeassistant/components/upcloud/manifest.json b/homeassistant/components/upcloud/manifest.json index bca246ad9e54d3..ab79d3f5c1a17e 100644 --- a/homeassistant/components/upcloud/manifest.json +++ b/homeassistant/components/upcloud/manifest.json @@ -5,5 +5,5 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/upcloud", "iot_class": "cloud_polling", - "requirements": ["upcloud-api==2.8.0"] + "requirements": ["upcloud-api==2.9.0"] } diff --git a/homeassistant/components/vicare/number.py b/homeassistant/components/vicare/number.py index 04c4088bd3e55b..68e310c089e781 100644 --- a/homeassistant/components/vicare/number.py +++ b/homeassistant/components/vicare/number.py @@ -24,6 +24,7 @@ NumberDeviceClass, NumberEntity, NumberEntityDescription, + NumberMode, ) from homeassistant.const import EntityCategory, UnitOfTemperature from homeassistant.core import HomeAssistant @@ -59,6 +60,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDomesticHotWaterConfiguredTemperature(), value_setter=lambda api, value: api.setDomesticHotWaterTemperature(value), min_value_getter=lambda api: api.getDomesticHotWaterMinTemperature(), @@ -71,6 +73,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDomesticHotWaterConfiguredTemperature2(), value_setter=lambda api, value: api.setDomesticHotWaterTemperature2(value), # no getters for min, max, stepping exposed yet, using static values @@ -84,6 +87,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.KELVIN, + mode=NumberMode.BOX, value_getter=lambda api: api.getDomesticHotWaterHysteresisSwitchOn(), value_setter=lambda api, value: api.setDomesticHotWaterHysteresisSwitchOn( value @@ -98,6 +102,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.KELVIN, + mode=NumberMode.BOX, value_getter=lambda api: api.getDomesticHotWaterHysteresisSwitchOff(), value_setter=lambda api, value: api.setDomesticHotWaterHysteresisSwitchOff( value @@ -116,6 +121,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getHeatingCurveShift(), value_setter=lambda api, shift: ( api.setHeatingCurve(shift, api.getHeatingCurveSlope()) @@ -131,6 +137,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM key="heating curve slope", translation_key="heating_curve_slope", entity_category=EntityCategory.CONFIG, + mode=NumberMode.BOX, value_getter=lambda api: api.getHeatingCurveSlope(), value_setter=lambda api, slope: ( api.setHeatingCurve(api.getHeatingCurveShift(), slope) @@ -148,6 +155,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDesiredTemperatureForProgram( HeatingProgram.NORMAL ), @@ -168,6 +176,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDesiredTemperatureForProgram( HeatingProgram.REDUCED ), @@ -188,6 +197,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDesiredTemperatureForProgram( HeatingProgram.COMFORT ), @@ -208,6 +218,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDesiredTemperatureForProgram( HeatingProgram.NORMAL_HEATING ), @@ -230,6 +241,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDesiredTemperatureForProgram( HeatingProgram.REDUCED_HEATING ), @@ -252,6 +264,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDesiredTemperatureForProgram( HeatingProgram.COMFORT_HEATING ), @@ -274,6 +287,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDesiredTemperatureForProgram( HeatingProgram.NORMAL_COOLING ), @@ -296,6 +310,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDesiredTemperatureForProgram( HeatingProgram.REDUCED_COOLING ), @@ -318,6 +333,7 @@ class ViCareNumberEntityDescription(NumberEntityDescription, ViCareRequiredKeysM entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + mode=NumberMode.BOX, value_getter=lambda api: api.getDesiredTemperatureForProgram( HeatingProgram.COMFORT_COOLING ), diff --git a/requirements_all.txt b/requirements_all.txt index 7d0fd6fb2f2c79..09330429e91c52 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -271,7 +271,7 @@ aiohasupervisor==0.3.3 aiohomeconnect==0.20.0 # homeassistant.components.homekit_controller -aiohomekit==3.2.19 +aiohomekit==3.2.20 # homeassistant.components.mcp_server aiohttp_sse==2.2.0 @@ -325,7 +325,7 @@ aionanoleaf==0.2.1 aionotion==2024.03.0 # homeassistant.components.ntfy -aiontfy==0.6.0 +aiontfy==0.6.1 # homeassistant.components.nut aionut==4.3.4 @@ -453,7 +453,7 @@ airgradient==0.9.2 airly==1.1.0 # homeassistant.components.airos -airos==0.5.4 +airos==0.5.5 # homeassistant.components.airthings_ble airthings-ble==1.1.1 @@ -3063,7 +3063,7 @@ universal-silabs-flasher==0.0.35 upb-lib==0.6.1 # homeassistant.components.upcloud -upcloud-api==2.8.0 +upcloud-api==2.9.0 # homeassistant.components.huawei_lte # homeassistant.components.syncthru diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9ef242b1b3d3de..0fd7e67198f2fe 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -256,7 +256,7 @@ aiohasupervisor==0.3.3 aiohomeconnect==0.20.0 # homeassistant.components.homekit_controller -aiohomekit==3.2.19 +aiohomekit==3.2.20 # homeassistant.components.mcp_server aiohttp_sse==2.2.0 @@ -307,7 +307,7 @@ aionanoleaf==0.2.1 aionotion==2024.03.0 # homeassistant.components.ntfy -aiontfy==0.6.0 +aiontfy==0.6.1 # homeassistant.components.nut aionut==4.3.4 @@ -435,7 +435,7 @@ airgradient==0.9.2 airly==1.1.0 # homeassistant.components.airos -airos==0.5.4 +airos==0.5.5 # homeassistant.components.airthings_ble airthings-ble==1.1.1 @@ -2537,7 +2537,7 @@ universal-silabs-flasher==0.0.35 upb-lib==0.6.1 # homeassistant.components.upcloud -upcloud-api==2.8.0 +upcloud-api==2.9.0 # homeassistant.components.huawei_lte # homeassistant.components.syncthru diff --git a/tests/components/airthings_ble/test_config_flow.py b/tests/components/airthings_ble/test_config_flow.py index 42db22a99153ce..a65c51b3fd6fa0 100644 --- a/tests/components/airthings_ble/test_config_flow.py +++ b/tests/components/airthings_ble/test_config_flow.py @@ -47,7 +47,7 @@ async def test_bluetooth_discovery(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["step_id"] == "bluetooth_confirm" assert result["description_placeholders"] == { - "name": "Airthings Wave Plus (123456)" + "name": "Airthings Wave Plus (2930123456)" } with patch_async_setup_entry(): @@ -56,7 +56,7 @@ async def test_bluetooth_discovery(hass: HomeAssistant) -> None: ) await hass.async_block_till_done() assert result["type"] is FlowResultType.CREATE_ENTRY - assert result["title"] == "Airthings Wave Plus (123456)" + assert result["title"] == "Airthings Wave Plus (2930123456)" assert result["result"].unique_id == "cc:cc:cc:cc:cc:cc" @@ -136,7 +136,7 @@ async def test_user_setup(hass: HomeAssistant) -> None: schema = result["data_schema"].schema assert schema.get(CONF_ADDRESS).container == { - "cc:cc:cc:cc:cc:cc": "Airthings Wave Plus (123456)" + "cc:cc:cc:cc:cc:cc": "Airthings Wave Plus (2930123456)" } with patch( @@ -149,7 +149,7 @@ async def test_user_setup(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert result["type"] is FlowResultType.CREATE_ENTRY - assert result["title"] == "Airthings Wave Plus (123456)" + assert result["title"] == "Airthings Wave Plus (2930123456)" assert result["result"].unique_id == "cc:cc:cc:cc:cc:cc" @@ -186,7 +186,7 @@ async def test_user_setup_replaces_ignored_device(hass: HomeAssistant) -> None: schema = result["data_schema"].schema assert schema.get(CONF_ADDRESS).container == { - "cc:cc:cc:cc:cc:cc": "Airthings Wave Plus (123456)" + "cc:cc:cc:cc:cc:cc": "Airthings Wave Plus (2930123456)" } with patch( @@ -199,7 +199,7 @@ async def test_user_setup_replaces_ignored_device(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert result["type"] is FlowResultType.CREATE_ENTRY - assert result["title"] == "Airthings Wave Plus (123456)" + assert result["title"] == "Airthings Wave Plus (2930123456)" assert result["result"].unique_id == "cc:cc:cc:cc:cc:cc" @@ -267,7 +267,7 @@ async def test_user_setup_unable_to_connect(hass: HomeAssistant) -> None: ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "cannot_connect" + assert result["reason"] == "no_devices_found" async def test_unsupported_device(hass: HomeAssistant) -> None: @@ -281,3 +281,72 @@ async def test_unsupported_device(hass: HomeAssistant) -> None: ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == "no_devices_found" + + +async def test_bluetooth_confirm_firmware_required(hass: HomeAssistant) -> None: + """Test discovery via bluetooth with a valid device.""" + device = AirthingsDevice( + manufacturer="Airthings AS", + model=AirthingsDeviceType.WAVE_ENHANCE_EU, + name="Airthings Wave Enhance", + identifier="123456", + ) + device.firmware.update_current_version("1.0.0") + device.firmware.update_required_version("2.6.1") + with ( + patch_async_ble_device_from_address(WAVE_SERVICE_INFO), + patch_airthings_ble(device), + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_BLUETOOTH}, + data=WAVE_SERVICE_INFO, + ) + + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + + with patch_async_setup_entry(): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"not": "empty"} + ) + await hass.async_block_till_done() + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "firmware_upgrade_required" + + +async def test_step_user_firmware_required(hass: HomeAssistant) -> None: + """Test the user has selected a device with a firmware upgrade required.""" + device = AirthingsDevice( + manufacturer="Airthings AS", + model=AirthingsDeviceType.WAVE_ENHANCE_EU, + name="Airthings Wave Enhance", + identifier="123456", + ) + device.firmware.update_current_version("1.0.0") + device.firmware.update_required_version("2.6.1") + + with ( + patch( + "homeassistant.components.airthings_ble.config_flow.async_discovered_service_info", + return_value=[WAVE_SERVICE_INFO], + ), + patch_async_ble_device_from_address(WAVE_SERVICE_INFO), + patch_airthings_ble(device), + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + + with patch( + "homeassistant.components.airthings_ble.async_setup_entry", + return_value=True, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_ADDRESS: "cc:cc:cc:cc:cc:cc"} + ) + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "firmware_upgrade_required" diff --git a/tests/components/lamarzocco/snapshots/test_sensor.ambr b/tests/components/lamarzocco/snapshots/test_sensor.ambr index 3dd1ff9b6652cf..1ded7231287bc5 100644 --- a/tests/components/lamarzocco/snapshots/test_sensor.ambr +++ b/tests/components/lamarzocco/snapshots/test_sensor.ambr @@ -45,7 +45,7 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '2025-05-07T18:04:20+00:00', + 'state': 'unavailable', }) # --- # name: test_sensors[sensor.gs012345_coffee_boiler_ready_time-entry] diff --git a/tests/components/ntfy/snapshots/test_sensor.ambr b/tests/components/ntfy/snapshots/test_sensor.ambr index fd0dd3c4bd410a..b475b1ee0bcd73 100644 --- a/tests/components/ntfy/snapshots/test_sensor.ambr +++ b/tests/components/ntfy/snapshots/test_sensor.ambr @@ -190,7 +190,7 @@ 'name': None, 'options': dict({ 'sensor': dict({ - 'suggested_display_precision': 0, + 'suggested_display_precision': 2, }), 'sensor.private': dict({ 'suggested_unit_of_measurement': , @@ -302,7 +302,7 @@ 'name': None, 'options': dict({ 'sensor': dict({ - 'suggested_display_precision': 0, + 'suggested_display_precision': 2, }), 'sensor.private': dict({ 'suggested_unit_of_measurement': , diff --git a/tests/components/shelly/test_binary_sensor.py b/tests/components/shelly/test_binary_sensor.py index ed764ddf601ae5..090a0b47c3c19f 100644 --- a/tests/components/shelly/test_binary_sensor.py +++ b/tests/components/shelly/test_binary_sensor.py @@ -641,7 +641,7 @@ async def test_rpc_presencezone_component( monkeypatch.setattr(mock_rpc_device, "config", config) status = deepcopy(mock_rpc_device.status) - status["presencezone:200"] = {"state": True, "num_objects": 3} + status["presencezone:200"] = {"value": True, "num_objects": 3} monkeypatch.setattr(mock_rpc_device, "status", status) mock_config_entry = await init_integration(hass, 4) @@ -655,7 +655,7 @@ async def test_rpc_presencezone_component( assert entry.unique_id == "123456789ABC-presencezone:200-presencezone_state" mutate_rpc_device_status( - monkeypatch, mock_rpc_device, "presencezone:200", "state", False + monkeypatch, mock_rpc_device, "presencezone:200", "value", False ) mock_rpc_device.mock_update() diff --git a/tests/components/vicare/snapshots/test_number.ambr b/tests/components/vicare/snapshots/test_number.ambr index 729d1403ad81a1..8a271d5d0f4051 100644 --- a/tests/components/vicare/snapshots/test_number.ambr +++ b/tests/components/vicare/snapshots/test_number.ambr @@ -7,7 +7,7 @@ 'capabilities': dict({ 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, }), 'config_entry_id': , @@ -46,7 +46,7 @@ 'friendly_name': 'model0 Comfort temperature', 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, 'unit_of_measurement': , }), @@ -66,7 +66,7 @@ 'capabilities': dict({ 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, }), 'config_entry_id': , @@ -105,7 +105,7 @@ 'friendly_name': 'model0 Comfort temperature', 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, 'unit_of_measurement': , }), @@ -125,7 +125,7 @@ 'capabilities': dict({ 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1, }), 'config_entry_id': , @@ -164,7 +164,7 @@ 'friendly_name': 'model0 DHW temperature', 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1, 'unit_of_measurement': , }), @@ -184,7 +184,7 @@ 'capabilities': dict({ 'max': 40, 'min': -13, - 'mode': , + 'mode': , 'step': 1, }), 'config_entry_id': , @@ -223,7 +223,7 @@ 'friendly_name': 'model0 Heating curve shift', 'max': 40, 'min': -13, - 'mode': , + 'mode': , 'step': 1, 'unit_of_measurement': , }), @@ -243,7 +243,7 @@ 'capabilities': dict({ 'max': 40, 'min': -13, - 'mode': , + 'mode': , 'step': 1, }), 'config_entry_id': , @@ -282,7 +282,7 @@ 'friendly_name': 'model0 Heating curve shift', 'max': 40, 'min': -13, - 'mode': , + 'mode': , 'step': 1, 'unit_of_measurement': , }), @@ -302,7 +302,7 @@ 'capabilities': dict({ 'max': 3.5, 'min': 0.2, - 'mode': , + 'mode': , 'step': 0.1, }), 'config_entry_id': , @@ -340,7 +340,7 @@ 'friendly_name': 'model0 Heating curve slope', 'max': 3.5, 'min': 0.2, - 'mode': , + 'mode': , 'step': 0.1, }), 'context': , @@ -359,7 +359,7 @@ 'capabilities': dict({ 'max': 3.5, 'min': 0.2, - 'mode': , + 'mode': , 'step': 0.1, }), 'config_entry_id': , @@ -397,7 +397,7 @@ 'friendly_name': 'model0 Heating curve slope', 'max': 3.5, 'min': 0.2, - 'mode': , + 'mode': , 'step': 0.1, }), 'context': , @@ -416,7 +416,7 @@ 'capabilities': dict({ 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, }), 'config_entry_id': , @@ -455,7 +455,7 @@ 'friendly_name': 'model0 Normal temperature', 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, 'unit_of_measurement': , }), @@ -475,7 +475,7 @@ 'capabilities': dict({ 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, }), 'config_entry_id': , @@ -514,7 +514,7 @@ 'friendly_name': 'model0 Normal temperature', 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, 'unit_of_measurement': , }), @@ -534,7 +534,7 @@ 'capabilities': dict({ 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, }), 'config_entry_id': , @@ -573,7 +573,7 @@ 'friendly_name': 'model0 Reduced temperature', 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, 'unit_of_measurement': , }), @@ -593,7 +593,7 @@ 'capabilities': dict({ 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, }), 'config_entry_id': , @@ -632,7 +632,7 @@ 'friendly_name': 'model0 Reduced temperature', 'max': 100.0, 'min': 0.0, - 'mode': , + 'mode': , 'step': 1.0, 'unit_of_measurement': , }),