Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
22de432
Speed up the Aurora ABB Powerone tests (#179789)
balloob Aug 22, 2026
41d1fe7
Create repair issue if RTSP is disabled for Shelly Camera (#179733)
bieniu Aug 22, 2026
a3bb68c
Speed up VoIP tests (#179786)
balloob Aug 22, 2026
923ad9d
Do not use a naive `datetime.now()` in BraviaTV (#179806)
bieniu Aug 22, 2026
46a6429
VoIP test improvements (#179456)
jaminh Aug 22, 2026
59ceabb
Extract Z-Wave JS config flow security keys and add-on handling (#179…
balloobbot Aug 22, 2026
9dfde34
Add select platform to Midea (#179245)
chemelli74 Aug 22, 2026
49db63b
Mock the DoorBird API while finishing the repair flow (#179823)
balloob Aug 22, 2026
ca8f427
Mock the source IP probe in the network url websocket test (#179834)
balloob Aug 22, 2026
32ed840
Use a 2048 bit dummy RSA key in the Google Assistant tests (#179830)
balloob Aug 22, 2026
9ca37c6
Import components concurrently in the circular import test (#179795)
balloob Aug 22, 2026
f64340c
Use a long enough JWT signing key in the Flume tests (#179827)
balloob Aug 22, 2026
d315c85
Keep caller supplied default headers in async_create_clientsession (#…
balloob Aug 22, 2026
3befded
Use a long enough JWT signing key in the Elmax tests (#179826)
balloob Aug 22, 2026
daaf982
Use a long enough JWT signing key in the Enphase Envoy tests (#179820)
balloob Aug 22, 2026
5d064af
Use a long enough JWT signing key in the cloud tests (#179821)
balloob Aug 22, 2026
d6c13ee
Silence two pytest warnings in the pylint plugin tests (#179833)
balloob Aug 22, 2026
f77bc02
Use a long enough JWT signing key in the auth token tests (#179835)
balloob Aug 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions homeassistant/components/braviatv/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from homeassistant.exceptions import ConfigEntryAuthFailed, HomeAssistantError
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util

from .const import (
CONF_NICKNAME,
Expand Down Expand Up @@ -240,11 +241,13 @@ async def async_update_playing(self) -> None:
self.source = None
if start_datetime := playing_info.get("startDateTime"):
start_datetime = datetime.fromisoformat(start_datetime)
current_datetime = datetime.now().replace(tzinfo=start_datetime.tzinfo) # pylint: disable=home-assistant-enforce-naive-now
self.media_position = int(
(current_datetime - start_datetime).total_seconds()
)
self.media_position_updated_at = datetime.now() # pylint: disable=home-assistant-enforce-naive-now
if start_datetime.tzinfo is None:
start_datetime = start_datetime.replace(
tzinfo=dt_util.get_default_time_zone()
)
now = dt_util.utcnow()
self.media_position = int((now - start_datetime).total_seconds())
self.media_position_updated_at = now
else:
self.media_position = None
self.media_position_updated_at = None
Expand Down
6 changes: 5 additions & 1 deletion homeassistant/components/midea/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
from .const import CONF_KEY, CONF_SUBTYPE
from .entity import MideaConfigEntry

_PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.HUMIDIFIER]
_PLATFORMS: list[Platform] = [
Platform.CLIMATE,
Platform.HUMIDIFIER,
Platform.SELECT,
]


async def async_setup_entry(hass: HomeAssistant, entry: MideaConfigEntry) -> bool:
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/midea/device_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
DeviceType.CC: "MDV Wi-Fi Controller",
DeviceType.CF: "Heat Pump",
DeviceType.FB: "Electric Heater",
DeviceType.X40: "Integrated Ceiling Fan",
DeviceType.A1: "Dehumidifier",
DeviceType.FA: "Fan",
DeviceType.FC: "Air Purifier",
DeviceType.FD: "Humidifier",
}
148 changes: 148 additions & 0 deletions homeassistant/components/midea/select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
"""Select for Midea."""

from dataclasses import dataclass
from typing import override

from midealocal.const import DeviceType

from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .entity import MideaConfigEntry, MideaEntity, midea_api_call

PARALLEL_UPDATES = 0


@dataclass(kw_only=True, frozen=True)
class MideaSelectEntityDescription(SelectEntityDescription):
"""Description for a Midea select entity."""

models: list[DeviceType]
options_attribute: str
"""Device property name returning the list of valid option strings."""


SELECTS: list[MideaSelectEntityDescription] = [
MideaSelectEntityDescription(
key="direction",
translation_key="direction",
models=[DeviceType.X40],
options_attribute="directions",
),
MideaSelectEntityDescription(
key="fan_speed",
translation_key="fan_speed",
models=[DeviceType.A1, DeviceType.FC, DeviceType.FD],
options_attribute="fan_speeds",
),
MideaSelectEntityDescription(
key="water_level_set",
translation_key="water_level_set",
models=[DeviceType.A1],
options_attribute="water_level_sets",
),
MideaSelectEntityDescription(
key="wind_lr_angle",
translation_key="wind_lr_angle",
models=[DeviceType.AC],
options_attribute="wind_lr_angles",
),
MideaSelectEntityDescription(
key="wind_ud_angle",
translation_key="wind_ud_angle",
models=[DeviceType.AC],
options_attribute="wind_ud_angles",
),
MideaSelectEntityDescription(
key="rate_select",
translation_key="rate_select",
models=[DeviceType.AC],
options_attribute="rate_selects",
),
MideaSelectEntityDescription(
key="silent_level",
translation_key="silent_level",
models=[DeviceType.C3],
options_attribute="silent_modes",
),
MideaSelectEntityDescription(
key="oscillation_mode",
translation_key="oscillation_mode",
models=[DeviceType.FA],
options_attribute="oscillation_modes",
),
MideaSelectEntityDescription(
key="oscillation_angle",
translation_key="oscillation_angle",
models=[DeviceType.FA],
options_attribute="oscillation_angles",
),
MideaSelectEntityDescription(
key="tilting_angle",
translation_key="tilting_angle",
models=[DeviceType.FA],
options_attribute="tilting_angles",
),
MideaSelectEntityDescription(
key="detect_mode",
translation_key="detect_mode",
models=[DeviceType.FC],
options_attribute="detect_modes",
),
MideaSelectEntityDescription(
key="mode",
translation_key="mode",
models=[DeviceType.FC],
options_attribute="modes",
),
MideaSelectEntityDescription(
key="screen_display",
translation_key="screen_display",
models=[DeviceType.FC, DeviceType.FD],
options_attribute="screen_displays",
),
]


async def async_setup_entry(
hass: HomeAssistant,
config_entry: MideaConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up selects for device."""
device = config_entry.runtime_data

async_add_entities(
MideaSelect(device, description)
for description in SELECTS
if device.device_type in description.models
and description.key in device.attributes
)


class MideaSelect(MideaEntity, SelectEntity):
"""Represent a Midea select."""

entity_description: MideaSelectEntityDescription

@property
@override
def options(self) -> list[str]:
"""Return the list of valid options."""
return getattr(self._device, self.entity_description.options_attribute)

@property
@override
def current_option(self) -> str | None:
"""Return the currently selected option."""
value = self._device.get_attribute(self.entity_description.key)
if not isinstance(value, str):
return None
return value

@override
def select_option(self, option: str) -> None:
"""Select an option."""
with midea_api_call():
self._device.set_attribute(attr=self.entity_description.key, value=option)
109 changes: 109 additions & 0 deletions homeassistant/components/midea/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,115 @@
"climate_zone2": {
"name": "Zone 2 thermostat"
}
},
"select": {
"detect_mode": {
"name": "Detect mode",
"state": {
"methanal": "Formaldehyde",
"off": "Off",
"pm_25": "PM2.5"
}
},
"direction": {
"name": "Direction",
"state": {
"oscillate": "Oscillate"
}
},
"fan_speed": {
"name": "Fan speed",
"state": {
"auto": "Auto",
"high": "High",
"low": "Low",
"lowest": "Lowest",
"medium": "Medium",
"off": "Off",
"standby": "Standby"
}
},
"mode": {
"name": "Mode",
"state": {
"auto": "Auto",
"fast": "Fast",
"manual": "Manual",
"sleep": "Sleep",
"smoke": "Smoke",
"standby": "Standby"
}
},
"oscillation_angle": {
"name": "Oscillation angle",
"state": {
"off": "Off"
}
},
"oscillation_mode": {
"name": "Oscillation mode",
"state": {
"both": "Both",
"curve_8": "Curve 8",
"curve_w": "Curve W",
"off": "Off",
"oscillation": "Oscillation",
"reserved": "Reserved",
"tilting": "Tilting"
}
},
"rate_select": {
"name": "Power rate limit"
},
"screen_display": {
"name": "Screen display",
"state": {
"bright": "Bright",
"dim": "Dim",
"off": "Off"
}
},
"silent_level": {
"name": "Silent level",
"state": {
"off": "Off",
"silent": "Silent",
"super_silent": "Super silent"
}
},
"tilting_angle": {
"name": "Tilting angle",
"state": {
"minus_60": "Minus 60",
"off": "Off",
"plus_60": "Plus 60"
}
},
"water_level_set": {
"name": "Water level setting"
},
"wind_lr_angle": {
"name": "Airflow horizontal",
"state": {
"left": "Left",
"left_mid": "Left middle",
"middle": "Middle",
"off": "Off",
"right": "Right",
"right_mid": "Right middle"
}
},
"wind_ud_angle": {
"name": "Airflow vertical",
"state": {
"down": "Down",
"down_mid": "Down middle",
"middle": "Middle",
"off": "Off",
"up": "Up",
"up_mid": "Up middle"
}
}
}
},
"exceptions": {
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/shelly/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
async_manage_deprecated_firmware_issue,
async_manage_open_wifi_ap_issue,
async_manage_outbound_websocket_incorrectly_enabled_issue,
async_manage_rtsp_disabled_issue,
)
from .services import async_setup_services
from .utils import (
Expand Down Expand Up @@ -393,6 +394,7 @@ async def _async_setup_rpc_entry(hass: HomeAssistant, entry: ShellyConfigEntry)
entry,
)
async_manage_open_wifi_ap_issue(hass, entry)
async_manage_rtsp_disabled_issue(hass, entry)
remove_empty_sub_devices(hass, entry)
elif (
sleep_period is None
Expand Down
5 changes: 3 additions & 2 deletions homeassistant/components/shelly/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@ class RpcCameraEntityDescription(RpcEntityDescription, CameraEntityDescription):
stream=0,
translation_key="stream",
translation_placeholders={"stream_id": "0"},
removal_condition=lambda config, _, key: not config[key]["rtsp"]["enable"],
),
"stream_1": RpcCameraEntityDescription(
key="camera",
stream=1,
translation_key="stream",
translation_placeholders={"stream_id": "1"},
entity_registry_enabled_default=False,
removal_condition=lambda config, _, key: not config[key]["rtsp"]["enable"],
),
}

Expand Down Expand Up @@ -94,8 +96,7 @@ def available(self) -> bool:
if not available:
return False

config = self.coordinator.device.config[self.key]
return not self.status["privacy"] and config["rtsp"]["enable"]
return not self.status["privacy"]

@override
@property
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/shelly/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ class BLEScannerMode(StrEnum):
)
DEPRECATED_FIRMWARE_ISSUE_ID = "deprecated_firmware_{unique}"
OPEN_WIFI_AP_ISSUE_ID = "open_wifi_ap_{unique}"
RTSP_DISABLED_ISSUE_ID = "rtsp_disabled_{unique}"
COIOT_UNCONFIGURED_ISSUE_ID = "coiot_unconfigured_{unique}"


Expand Down
Loading
Loading