24 files
This commit is contained in:
@@ -30,9 +30,9 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from midealocal.device import DeviceType, MideaDevice, ProtocolVersion
|
||||
from midealocal.devices import device_selector
|
||||
from midealocal.discover import discover
|
||||
from midealan.device import DeviceType, MideaDevice, ProtocolVersion
|
||||
from midealan.devices import device_selector
|
||||
from midealan.discover import discover
|
||||
|
||||
from .const import (
|
||||
ALL_PLATFORM,
|
||||
@@ -52,31 +52,43 @@ from .midea_devices import MIDEA_DEVICES
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _close_device(device: MideaDevice) -> None:
|
||||
"""Close a Midea device connection without failing unload/setup cleanup."""
|
||||
try:
|
||||
device.close()
|
||||
except (OSError, ConnectionError, AttributeError) as e:
|
||||
_LOGGER.warning("Failed to close Midea socket cleanly: %s", e)
|
||||
|
||||
|
||||
def _device_store(hass: HomeAssistant) -> dict[int, MideaDevice]:
|
||||
"""Return the integration's loaded device map.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Device id to Midea device mapping.
|
||||
|
||||
"""
|
||||
return cast(
|
||||
"dict[int, MideaDevice]",
|
||||
hass.data.setdefault(DOMAIN, {}).setdefault(DEVICES, {}),
|
||||
)
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
"""Option flow signal update.
|
||||
|
||||
register update listener for config entry that will be called when entry is updated.
|
||||
A listener is registered by adding the following to the `async_setup_entry`:
|
||||
`config_entry.async_on_unload(config_entry.add_update_listener(update_listener))`
|
||||
means the Listener is attached when the entry is loaded and detached at unload
|
||||
Registered in `async_setup_entry` via
|
||||
`config_entry.async_on_unload(config_entry.add_update_listener(...))`, so it
|
||||
is attached when the entry loads and detached at unload. Reload the entry so
|
||||
the changed options (customize JSON, IP override, refresh interval, and the
|
||||
extra sensor/switch selection) are re-applied through the normal setup path.
|
||||
|
||||
Reloading avoids the previous fire-and-forget re-setup, which awaited the
|
||||
platform unload but then re-forwarded setup via an untracked
|
||||
`async_create_task` — swallowing any setup error and racing the customize/
|
||||
ip/refresh application that followed.
|
||||
"""
|
||||
# Forward the unloading of an entry to platforms.
|
||||
await hass.config_entries.async_unload_platforms(config_entry, ALL_PLATFORM)
|
||||
# forward the Config Entry to the platforms
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setups(config_entry, ALL_PLATFORM),
|
||||
)
|
||||
device_id: int = cast("int", config_entry.data.get(CONF_DEVICE_ID))
|
||||
customize = config_entry.options.get(CONF_CUSTOMIZE, "")
|
||||
ip_address = config_entry.options.get(CONF_IP_ADDRESS, None)
|
||||
refresh_interval = config_entry.options.get(CONF_REFRESH_INTERVAL, None)
|
||||
dev: MideaDevice = hass.data[DOMAIN][DEVICES].get(device_id)
|
||||
if dev:
|
||||
dev.set_customize(customize)
|
||||
if ip_address is not None:
|
||||
dev.set_ip_address(ip_address)
|
||||
if refresh_interval is not None:
|
||||
dev.set_refresh_interval(refresh_interval)
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # ruff:ignore[unused-function-argument]
|
||||
@@ -217,7 +229,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
if protocol == ProtocolVersion.V3 and (key == "" or token == ""):
|
||||
_LOGGER.error("For V3 devices, the key and the token is required")
|
||||
return False
|
||||
# device_selector in `midealocal/devices/__init__.py`
|
||||
# device_selector in `midealan/devices/__init__.py`
|
||||
# hass core version >= 2024.3
|
||||
if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 3):
|
||||
device = await hass.async_add_import_executor_job(
|
||||
@@ -257,13 +269,17 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
if refresh_interval is not None:
|
||||
device.set_refresh_interval(refresh_interval)
|
||||
device.open()
|
||||
if DOMAIN not in hass.data:
|
||||
hass.data[DOMAIN] = {}
|
||||
if DEVICES not in hass.data[DOMAIN]:
|
||||
hass.data[DOMAIN][DEVICES] = {}
|
||||
hass.data[DOMAIN][DEVICES][device_id] = device
|
||||
# Forward the setup of an entry to all platforms
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, ALL_PLATFORM)
|
||||
_device_store(hass)[device_id] = device
|
||||
try:
|
||||
# Forward the setup of an entry to all platforms
|
||||
await hass.config_entries.async_forward_entry_setups(
|
||||
config_entry,
|
||||
ALL_PLATFORM,
|
||||
)
|
||||
except Exception:
|
||||
_device_store(hass).pop(device_id, None)
|
||||
_close_device(device)
|
||||
raise
|
||||
# Listener `update_listener` is
|
||||
# attached when the entry is loaded
|
||||
# and detached when it's unloaded
|
||||
@@ -283,18 +299,22 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
||||
device_type = config_entry.data.get(CONF_TYPE)
|
||||
if device_type == CONF_ACCOUNT:
|
||||
return True
|
||||
device_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||
if device_id is not None:
|
||||
dm = hass.data[DOMAIN][DEVICES].get(device_id)
|
||||
if dm is not None:
|
||||
try:
|
||||
dm.close()
|
||||
except (OSError, ConnectionError, AttributeError) as e:
|
||||
_LOGGER.warning("Failed to close Midea socket cleanly: %s", e)
|
||||
hass.data[DOMAIN][DEVICES].pop(device_id)
|
||||
# Forward the unloading of an entry to platforms
|
||||
await hass.config_entries.async_unload_platforms(config_entry, ALL_PLATFORM)
|
||||
return True
|
||||
# Unload the platforms first; only tear the device down if that succeeded,
|
||||
# and report the real result so a failed platform unload isn't masked.
|
||||
# bool() keeps mypy happy: async_unload_platforms is typed to return Any.
|
||||
unload_ok = bool(
|
||||
await hass.config_entries.async_unload_platforms(
|
||||
config_entry,
|
||||
ALL_PLATFORM,
|
||||
),
|
||||
)
|
||||
if unload_ok:
|
||||
device_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||
if device_id is not None:
|
||||
dm = _device_store(hass).pop(device_id, None)
|
||||
if dm is not None:
|
||||
_close_device(dm)
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
|
||||
@@ -40,17 +40,17 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from midealocal.device import DeviceType
|
||||
from midealocal.devices.ac import DeviceAttributes as ACAttributes
|
||||
from midealocal.devices.ac import MideaACDevice
|
||||
from midealocal.devices.c3 import DeviceAttributes as C3Attributes
|
||||
from midealocal.devices.c3 import MideaC3Device
|
||||
from midealocal.devices.cc import DeviceAttributes as CCAttributes
|
||||
from midealocal.devices.cc import MideaCCDevice
|
||||
from midealocal.devices.cf import DeviceAttributes as CFAttributes
|
||||
from midealocal.devices.cf import MideaCFDevice
|
||||
from midealocal.devices.fb import DeviceAttributes as FBAttributes
|
||||
from midealocal.devices.fb import MideaFBDevice
|
||||
from midealan.device import DeviceType
|
||||
from midealan.devices.ac import DeviceAttributes as ACAttributes
|
||||
from midealan.devices.ac import MideaACDevice
|
||||
from midealan.devices.c3 import DeviceAttributes as C3Attributes
|
||||
from midealan.devices.c3 import MideaC3Device
|
||||
from midealan.devices.cc import DeviceAttributes as CCAttributes
|
||||
from midealan.devices.cc import MideaCCDevice
|
||||
from midealan.devices.cf import DeviceAttributes as CFAttributes
|
||||
from midealan.devices.cf import MideaCFDevice
|
||||
from midealan.devices.fb import DeviceAttributes as FBAttributes
|
||||
from midealan.devices.fb import MideaFBDevice
|
||||
|
||||
from .const import DEVICES, DOMAIN, FanSpeed
|
||||
from .midea_devices import MIDEA_DEVICES
|
||||
@@ -146,7 +146,10 @@ class MideaClimate(MideaEntity, ClimateEntity):
|
||||
"""Midea Climate hvac mode."""
|
||||
if self._device.get_attribute("power"):
|
||||
mode = cast("int", self._device.get_attribute("mode"))
|
||||
return self.hvac_modes[mode]
|
||||
# Guard against an out-of-range/undefined device mode so a malformed
|
||||
# frame cannot raise IndexError on every state render.
|
||||
if 0 <= mode < len(self.hvac_modes):
|
||||
return self.hvac_modes[mode]
|
||||
return HVACMode.OFF
|
||||
|
||||
@property
|
||||
@@ -450,7 +453,10 @@ class MideaACClimate(MideaClimate):
|
||||
"""Midea AC Climate hvac mode (device mode int -> fixed map)."""
|
||||
if self._device.get_attribute("power"):
|
||||
mode = cast("int", self._device.get_attribute("mode"))
|
||||
return self._mode_index[mode]
|
||||
# The AC `mode` field is a 3-bit value (0-7) but `_mode_index` only
|
||||
# maps 0-5; guard so an out-of-spec 6/7 cannot raise IndexError.
|
||||
if 0 <= mode < len(self._mode_index):
|
||||
return self._mode_index[mode]
|
||||
return HVACMode.OFF
|
||||
|
||||
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
@@ -814,7 +820,11 @@ class MideaC3Climate(MideaClimate):
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
"""Midea C3 Climate hvac mode."""
|
||||
mode = self._device.get_attribute(C3Attributes.mode)
|
||||
if self._device.get_attribute(self._power_attr) and isinstance(mode, int):
|
||||
if (
|
||||
self._device.get_attribute(self._power_attr)
|
||||
and isinstance(mode, int)
|
||||
and 0 <= mode < len(self.hvac_modes)
|
||||
):
|
||||
return self.hvac_modes[mode]
|
||||
return HVACMode.OFF
|
||||
|
||||
|
||||
@@ -47,15 +47,14 @@ from homeassistant.core import callback
|
||||
from homeassistant.helpers.aiohttp_client import async_create_clientsession
|
||||
from homeassistant.helpers.json import save_json
|
||||
from homeassistant.util.json import load_json
|
||||
from midealocal.cloud import (
|
||||
from midealan.cloud import (
|
||||
PRESET_ACCOUNT_DATA,
|
||||
SUPPORTED_CLOUDS,
|
||||
MideaCloud,
|
||||
get_midea_cloud,
|
||||
)
|
||||
from midealocal.device import AuthException, MideaDevice, ProtocolVersion
|
||||
from midealocal.discover import discover
|
||||
from midealocal.exceptions import SocketException
|
||||
from midealan.device import MideaDevice, ProtocolVersion
|
||||
from midealan.discover import discover
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from aiohttp import ClientSession
|
||||
@@ -144,7 +143,7 @@ class MideaLanConfigFlow(ConfigFlow, domain=DOMAIN): # type: ignore[call-arg]
|
||||
record_file = storage_path.joinpath(f"{data[CONF_DEVICE_ID]!s}.json")
|
||||
save_json(str(record_file), data)
|
||||
|
||||
def _load_device_config(self, device_id: str) -> Any: # ruff:ignore[any-type]
|
||||
def _load_device_config(self, device_id: int | str) -> Any: # ruff:ignore[any-type]
|
||||
"""Load device config from json file with device id.
|
||||
|
||||
Returns
|
||||
@@ -356,7 +355,7 @@ class MideaLanConfigFlow(ConfigFlow, domain=DOMAIN): # type: ignore[call-arg]
|
||||
|
||||
"""
|
||||
# get all devices list
|
||||
all_devices = discover()
|
||||
all_devices = await self.hass.async_add_executor_job(discover)
|
||||
# available devices exist
|
||||
if len(all_devices) > 0:
|
||||
table = (
|
||||
@@ -402,8 +401,10 @@ class MideaLanConfigFlow(ConfigFlow, domain=DOMAIN): # type: ignore[call-arg]
|
||||
# ip exist
|
||||
else:
|
||||
ip_address = discovery_info[CONF_IP_ADDRESS]
|
||||
# use midea-local discover() to get devices list with ip_address
|
||||
self.devices = discover(list(self.supports.keys()), ip_address=ip_address)
|
||||
# use midea-lan discover() to get devices list with ip_address
|
||||
self.devices = await self.hass.async_add_executor_job(
|
||||
lambda: discover(list(self.supports.keys()), ip_address=ip_address),
|
||||
)
|
||||
self.available_device = {}
|
||||
for device_id, device in self.devices.items():
|
||||
# remove exist devices and only return new devices
|
||||
@@ -512,17 +513,8 @@ class MideaLanConfigFlow(ConfigFlow, domain=DOMAIN): # type: ignore[call-arg]
|
||||
subtype=0,
|
||||
attributes={},
|
||||
)
|
||||
if dm.connect():
|
||||
try:
|
||||
dm.authenticate()
|
||||
except AuthException:
|
||||
_LOGGER.debug("Unable to authenticate.")
|
||||
dm.close_socket()
|
||||
except SocketException:
|
||||
_LOGGER.debug("Socket closed.")
|
||||
else:
|
||||
dm.close_socket()
|
||||
return value
|
||||
if await self.hass.async_add_executor_job(self._try_connect_device, dm):
|
||||
return value
|
||||
# return debug log with failed key
|
||||
_LOGGER.debug(
|
||||
"connect device using method %s token/key failed",
|
||||
@@ -533,6 +525,23 @@ class MideaLanConfigFlow(ConfigFlow, domain=DOMAIN): # type: ignore[call-arg]
|
||||
)
|
||||
return {"error": "connect_error"}
|
||||
|
||||
@staticmethod
|
||||
def _try_connect_device(dm: MideaDevice) -> bool:
|
||||
"""Connect to a device, closing the socket afterwards.
|
||||
|
||||
Runs the blocking socket I/O in an executor so it never blocks the
|
||||
event loop. V3 authentication is handled inside midea-lan's connect().
|
||||
|
||||
Returns
|
||||
-------
|
||||
True if the device connected successfully.
|
||||
|
||||
"""
|
||||
try:
|
||||
return dm.connect()
|
||||
finally:
|
||||
dm.close_socket()
|
||||
|
||||
async def async_step_auto(
|
||||
self,
|
||||
user_input: dict[str, Any] | None = None,
|
||||
@@ -559,7 +568,10 @@ class MideaLanConfigFlow(ConfigFlow, domain=DOMAIN): # type: ignore[call-arg]
|
||||
CONF_PORT: device.get(CONF_PORT),
|
||||
CONF_MODEL: device.get(CONF_MODEL),
|
||||
}
|
||||
storage_device = self._load_device_config(device_id)
|
||||
storage_device = await self.hass.async_add_executor_job(
|
||||
self._load_device_config,
|
||||
device_id,
|
||||
)
|
||||
# device config already exist, load from local json without cloud
|
||||
if self._check_storage_device(device, storage_device):
|
||||
self.found_device = {
|
||||
@@ -697,9 +709,8 @@ class MideaLanConfigFlow(ConfigFlow, domain=DOMAIN): # type: ignore[call-arg]
|
||||
if len(self.devices) < 1:
|
||||
ip = user_input[CONF_IP_ADDRESS]
|
||||
# discover device
|
||||
self.devices = discover(
|
||||
list(self.supports.keys()),
|
||||
ip_address=ip,
|
||||
self.devices = await self.hass.async_add_executor_job(
|
||||
lambda: discover(list(self.supports.keys()), ip_address=ip),
|
||||
)
|
||||
# discover result MUST exist
|
||||
if len(self.devices) != 1:
|
||||
@@ -778,40 +789,31 @@ class MideaLanConfigFlow(ConfigFlow, domain=DOMAIN): # type: ignore[call-arg]
|
||||
subtype=0,
|
||||
attributes={},
|
||||
)
|
||||
if dm.connect():
|
||||
try:
|
||||
if user_input[CONF_PROTOCOL] == ProtocolVersion.V3:
|
||||
dm.authenticate()
|
||||
except SocketException:
|
||||
_LOGGER.exception("Socket closed.")
|
||||
except AuthException:
|
||||
_LOGGER.exception(
|
||||
"Unable to authenticate with provided key and token.",
|
||||
)
|
||||
dm.close_socket()
|
||||
else:
|
||||
dm.close_socket()
|
||||
data = {
|
||||
CONF_NAME: user_input[CONF_NAME],
|
||||
CONF_DEVICE_ID: user_input[CONF_DEVICE_ID],
|
||||
CONF_TYPE: user_input[CONF_TYPE],
|
||||
CONF_PROTOCOL: user_input[CONF_PROTOCOL],
|
||||
CONF_IP_ADDRESS: user_input[CONF_IP_ADDRESS],
|
||||
CONF_PORT: user_input[CONF_PORT],
|
||||
CONF_MODEL: user_input[CONF_MODEL],
|
||||
CONF_SUBTYPE: user_input[CONF_SUBTYPE],
|
||||
CONF_TOKEN: user_input[CONF_TOKEN],
|
||||
CONF_KEY: user_input[CONF_KEY],
|
||||
CONF_MAC: device.get(CONF_MAC),
|
||||
CONF_SN: device.get(CONF_SN),
|
||||
}
|
||||
# save device json config when adding new device
|
||||
self._save_device_config(data)
|
||||
# finish add device entry
|
||||
return self.async_create_entry(
|
||||
title=f"{user_input[CONF_NAME]}",
|
||||
data=data,
|
||||
)
|
||||
if await self.hass.async_add_executor_job(
|
||||
self._try_connect_device,
|
||||
dm,
|
||||
):
|
||||
data = {
|
||||
CONF_NAME: user_input[CONF_NAME],
|
||||
CONF_DEVICE_ID: user_input[CONF_DEVICE_ID],
|
||||
CONF_TYPE: user_input[CONF_TYPE],
|
||||
CONF_PROTOCOL: user_input[CONF_PROTOCOL],
|
||||
CONF_IP_ADDRESS: user_input[CONF_IP_ADDRESS],
|
||||
CONF_PORT: user_input[CONF_PORT],
|
||||
CONF_MODEL: user_input[CONF_MODEL],
|
||||
CONF_SUBTYPE: user_input[CONF_SUBTYPE],
|
||||
CONF_TOKEN: user_input[CONF_TOKEN],
|
||||
CONF_KEY: user_input[CONF_KEY],
|
||||
CONF_MAC: device.get(CONF_MAC),
|
||||
CONF_SN: device.get(CONF_SN),
|
||||
}
|
||||
# save device json config when adding new device
|
||||
await self.hass.async_add_executor_job(self._save_device_config, data)
|
||||
# finish add device entry
|
||||
return self.async_create_entry(
|
||||
title=f"{user_input[CONF_NAME]}",
|
||||
data=data,
|
||||
)
|
||||
return await self.async_step_manually(
|
||||
error="Device auth failed with input config",
|
||||
)
|
||||
@@ -917,14 +919,12 @@ class MideaLanOptionsFlowHandler(OptionsFlow):
|
||||
self._device_type = config_entry.data.get(CONF_TYPE)
|
||||
if self._device_type is None:
|
||||
self._device_type = 0xAC
|
||||
if CONF_SENSORS in self._config_entry.options:
|
||||
for key in self._config_entry.options[CONF_SENSORS]:
|
||||
if key not in MIDEA_DEVICES[self._device_type]["entities"]:
|
||||
self._config_entry.options[CONF_SENSORS].remove(key)
|
||||
if CONF_SWITCHES in self._config_entry.options:
|
||||
for key in self._config_entry.options[CONF_SWITCHES]:
|
||||
if key not in MIDEA_DEVICES[self._device_type]["entities"]:
|
||||
self._config_entry.options[CONF_SWITCHES].remove(key)
|
||||
# Stale keys (attributes no longer in MIDEA_DEVICES) are filtered out
|
||||
# downstream in async_step_init, where the multi-select defaults are
|
||||
# computed as `set(sensors) & set(options)` / `set(switches) & ...` —
|
||||
# both `sensors` and `switches` are built only from valid entities. No
|
||||
# pruning is needed here; doing it in place mutated the list while
|
||||
# iterating (skipping elements) and mutated HA-owned entry state.
|
||||
|
||||
async def async_step_init(
|
||||
self,
|
||||
|
||||
@@ -12,15 +12,15 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from midealocal.device import DeviceType
|
||||
from midealocal.devices.ac import DeviceAttributes as ACAttributes
|
||||
from midealocal.devices.ac import MideaACDevice
|
||||
from midealocal.devices.b6 import MideaB6Device
|
||||
from midealocal.devices.ce import DeviceAttributes as CEAttributes
|
||||
from midealocal.devices.ce import MideaCEDevice
|
||||
from midealocal.devices.fa import MideaFADevice
|
||||
from midealocal.devices.x40 import DeviceAttributes as X40Attributes
|
||||
from midealocal.devices.x40 import MideaX40Device
|
||||
from midealan.device import DeviceType
|
||||
from midealan.devices.ac import DeviceAttributes as ACAttributes
|
||||
from midealan.devices.ac import MideaACDevice
|
||||
from midealan.devices.b6 import MideaB6Device
|
||||
from midealan.devices.ce import DeviceAttributes as CEAttributes
|
||||
from midealan.devices.ce import MideaCEDevice
|
||||
from midealan.devices.fa import MideaFADevice
|
||||
from midealan.devices.x40 import DeviceAttributes as X40Attributes
|
||||
from midealan.devices.x40 import MideaX40Device
|
||||
|
||||
from .const import DEVICES, DOMAIN
|
||||
from .midea_devices import (
|
||||
@@ -122,7 +122,13 @@ class MideaFan(MideaEntity, FanEntity):
|
||||
|
||||
def set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Midea Fan set preset mode."""
|
||||
self._device.set_attribute(attr="mode", value=preset_mode.capitalize())
|
||||
# Pass the preset value through unchanged. `preset_modes` already
|
||||
# returns the device's own strings and HA only ever hands one of them
|
||||
# back, so the value is guaranteed valid. Do NOT `.capitalize()` it:
|
||||
# that corrupts multi-word / mixed-case names such as the CE fan's
|
||||
# "ECO mode" -> "Eco mode", which the device matches case-sensitively
|
||||
# and silently drops, making the ECO preset unselectable.
|
||||
self._device.set_attribute(attr="mode", value=preset_mode)
|
||||
|
||||
@property
|
||||
def percentage(self) -> int | None:
|
||||
|
||||
@@ -12,9 +12,9 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_DEVICE_ID, CONF_SWITCHES, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from midealocal.device import DeviceType
|
||||
from midealocal.devices.a1 import MideaA1Device
|
||||
from midealocal.devices.fd import MideaFDDevice
|
||||
from midealan.device import DeviceType
|
||||
from midealan.devices.a1 import MideaA1Device
|
||||
from midealan.devices.fd import MideaFDDevice
|
||||
|
||||
from .const import DEVICES, DOMAIN
|
||||
from .midea_devices import MIDEA_DEVICES
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"entity": {
|
||||
"climate": {
|
||||
"climate_key": {
|
||||
"state_attributes": {
|
||||
"fan_mode": {
|
||||
"state": {
|
||||
"silent": "mdi:fan-clock",
|
||||
"full": "mdi:fan-alert"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_DEVICE_ID, CONF_SWITCHES, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from midealocal.devices.x13 import DeviceAttributes as X13Attributes
|
||||
from midealocal.devices.x13 import Midea13Device
|
||||
from midealan.devices.x13 import DeviceAttributes as X13Attributes
|
||||
from midealan.devices.x13 import Midea13Device
|
||||
|
||||
from .const import DEVICES, DOMAIN
|
||||
from .midea_devices import MIDEA_DEVICES
|
||||
@@ -73,18 +73,32 @@ def _calc_supported_color_modes(device: Midea13Device) -> set[ColorMode]:
|
||||
class MideaLight(MideaEntity, LightEntity):
|
||||
"""Midea Light Entries."""
|
||||
|
||||
_attr_color_mode: ColorMode | str | None = None
|
||||
_attr_supported_color_modes: set[ColorMode] | set[str] | None = None
|
||||
_attr_supported_features: LightEntityFeature = LightEntityFeature(0)
|
||||
|
||||
_device: Midea13Device
|
||||
|
||||
def __init__(self, device: Midea13Device, entity_key: str) -> None:
|
||||
"""Midea Light entity init."""
|
||||
super().__init__(device, entity_key)
|
||||
self._attr_supported_features = _calc_supported_features(device)
|
||||
self._attr_supported_color_modes = _calc_supported_color_modes(device)
|
||||
self._attr_color_mode = self._calc_color_mode(self._attr_supported_color_modes)
|
||||
# NOTE: supported_features / supported_color_modes / color_mode are computed
|
||||
# as properties (not latched in __init__). The device starts its socket
|
||||
# refresh on a background thread, so at construction time brightness,
|
||||
# color_temperature and rgb_color are all still None. Latching these in
|
||||
# __init__ would lock a dimmable/color-temp light to ONOFF forever, because
|
||||
# update_state never recomputes them. Recomputing per access reflects the
|
||||
# capabilities once the first status arrives.
|
||||
@property
|
||||
def supported_features(self) -> LightEntityFeature:
|
||||
"""Midea Light supported features."""
|
||||
return _calc_supported_features(self._device)
|
||||
|
||||
@property
|
||||
def supported_color_modes(self) -> set[ColorMode] | set[str] | None:
|
||||
"""Midea Light supported color modes."""
|
||||
return _calc_supported_color_modes(self._device)
|
||||
|
||||
@property
|
||||
def color_mode(self) -> ColorMode | str | None:
|
||||
"""Midea Light current color mode."""
|
||||
# Call the concrete helper (returns set[ColorMode]) rather than the
|
||||
# supported_color_modes property, whose type is widened to
|
||||
# set[ColorMode] | set[str] | None to match LightEntity's base override.
|
||||
return self._calc_color_mode(_calc_supported_color_modes(self._device))
|
||||
|
||||
def _calc_color_mode(self, supported: set[ColorMode]) -> ColorMode:
|
||||
"""Midea Light calculate color mode.
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
{
|
||||
"domain": "midea_ac_lan",
|
||||
"name": "Midea AC LAN",
|
||||
"codeowners": ["@wuwentao", "@rokam", "@chemelli74", "@Necroneco"],
|
||||
"codeowners": [
|
||||
"@wuwentao",
|
||||
"@rokam",
|
||||
"@chemelli74",
|
||||
"@caibinqing"
|
||||
],
|
||||
"config_flow": true,
|
||||
"dependencies": [],
|
||||
"documentation": "https://github.com/wuwentao/midea_ac_lan#readme",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"issue_tracker": "https://github.com/wuwentao/midea_ac_lan/issues",
|
||||
"loggers": ["midealocal"],
|
||||
"requirements": ["midea-local==6.11.1"],
|
||||
"version": "0.7.1"
|
||||
}
|
||||
"loggers": [
|
||||
"midealan"
|
||||
],
|
||||
"requirements": [
|
||||
"midea-lan==2026.8.0"
|
||||
],
|
||||
"version": "2026.8.0"
|
||||
}
|
||||
@@ -5,8 +5,8 @@ from typing import Any
|
||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||
from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
MAJOR_VERSION,
|
||||
MINOR_VERSION,
|
||||
PERCENTAGE,
|
||||
REVOLUTIONS_PER_MINUTE,
|
||||
Platform,
|
||||
@@ -19,40 +19,61 @@ from homeassistant.const import (
|
||||
UnitOfTime,
|
||||
UnitOfVolume,
|
||||
)
|
||||
from midealocal.devices.a1 import DeviceAttributes as A1Attributes
|
||||
from midealocal.devices.ac import DeviceAttributes as ACAttributes
|
||||
from midealocal.devices.ad import DeviceAttributes as ADAttributes
|
||||
from midealocal.devices.b0 import DeviceAttributes as B0Attributes
|
||||
from midealocal.devices.b1 import DeviceAttributes as B1Attributes
|
||||
from midealocal.devices.b3 import DeviceAttributes as B3Attributes
|
||||
from midealocal.devices.b4 import DeviceAttributes as B4Attributes
|
||||
from midealocal.devices.b6 import DeviceAttributes as B6Attributes
|
||||
from midealocal.devices.bf import DeviceAttributes as BFAttributes
|
||||
from midealocal.devices.c2 import DeviceAttributes as C2Attributes
|
||||
from midealocal.devices.c3 import DeviceAttributes as C3Attributes
|
||||
from midealocal.devices.ca import DeviceAttributes as CAAttributes
|
||||
from midealocal.devices.cc import DeviceAttributes as CCAttributes
|
||||
from midealocal.devices.cd import DeviceAttributes as CDAttributes
|
||||
from midealocal.devices.ce import DeviceAttributes as CEAttributes
|
||||
from midealocal.devices.cf import DeviceAttributes as CFAttributes
|
||||
from midealocal.devices.da import DeviceAttributes as DAAttributes
|
||||
from midealocal.devices.db import DeviceAttributes as DBAttributes
|
||||
from midealocal.devices.dc import DeviceAttributes as DCAttributes
|
||||
from midealocal.devices.e1 import DeviceAttributes as E1Attributes
|
||||
from midealocal.devices.e2 import DeviceAttributes as E2Attributes
|
||||
from midealocal.devices.e3 import DeviceAttributes as E3Attributes
|
||||
from midealocal.devices.e6 import DeviceAttributes as E6Attributes
|
||||
from midealocal.devices.e8 import DeviceAttributes as E8Attributes
|
||||
from midealocal.devices.ea import DeviceAttributes as EAAttributes
|
||||
from midealocal.devices.ec import DeviceAttributes as ECAttributes
|
||||
from midealocal.devices.ed import DeviceAttributes as EDAttributes
|
||||
from midealocal.devices.fa import DeviceAttributes as FAAttributes
|
||||
from midealocal.devices.fb import DeviceAttributes as FBAttributes
|
||||
from midealocal.devices.fc import DeviceAttributes as FCAttributes
|
||||
from midealocal.devices.fd import DeviceAttributes as FDAttributes
|
||||
from midealocal.devices.x26 import DeviceAttributes as X26Attributes
|
||||
from midealocal.devices.x34 import DeviceAttributes as X34Attributes
|
||||
from midealocal.devices.x40 import DeviceAttributes as X40Attributes
|
||||
|
||||
# HA 2026.7 added UnitOfDensity/UnitOfRatio, and HA 2026.8 started deprecating
|
||||
# CONCENTRATION_MICROGRAMS_PER_CUBIC_METER / CONCENTRATION_PARTS_PER_MILLION in favor of
|
||||
# them (scheduled for removal in HA 2027.8). Both old and new names resolve to the
|
||||
# identical runtime string ("μg/m³" / "ppm"), so this is purely cosmetic. This
|
||||
# integration's floor is HA 2024.4.1, where UnitOfDensity/UnitOfRatio do not exist yet,
|
||||
# so branch on the HA version like the rest of this codebase does for newer HA APIs (see
|
||||
# other MAJOR_VERSION/MINOR_VERSION usages, e.g. midea_entity.py).
|
||||
if (MAJOR_VERSION, MINOR_VERSION) >= (2026, 7):
|
||||
from homeassistant.const import ( # pylint: disable=E0611
|
||||
UnitOfDensity,
|
||||
UnitOfRatio,
|
||||
)
|
||||
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER = UnitOfDensity.MICROGRAMS_PER_CUBIC_METER
|
||||
CONCENTRATION_PARTS_PER_MILLION = UnitOfRatio.PARTS_PER_MILLION
|
||||
else:
|
||||
from homeassistant.const import ( # type: ignore[no-redef]
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
)
|
||||
from midealan.devices.a1 import DeviceAttributes as A1Attributes
|
||||
from midealan.devices.ac import DeviceAttributes as ACAttributes
|
||||
from midealan.devices.ad import DeviceAttributes as ADAttributes
|
||||
from midealan.devices.b0 import DeviceAttributes as B0Attributes
|
||||
from midealan.devices.b1 import DeviceAttributes as B1Attributes
|
||||
from midealan.devices.b3 import DeviceAttributes as B3Attributes
|
||||
from midealan.devices.b4 import DeviceAttributes as B4Attributes
|
||||
from midealan.devices.b6 import DeviceAttributes as B6Attributes
|
||||
from midealan.devices.bf import DeviceAttributes as BFAttributes
|
||||
from midealan.devices.c2 import DeviceAttributes as C2Attributes
|
||||
from midealan.devices.c3 import DeviceAttributes as C3Attributes
|
||||
from midealan.devices.ca import DeviceAttributes as CAAttributes
|
||||
from midealan.devices.cc import DeviceAttributes as CCAttributes
|
||||
from midealan.devices.cd import DeviceAttributes as CDAttributes
|
||||
from midealan.devices.ce import DeviceAttributes as CEAttributes
|
||||
from midealan.devices.cf import DeviceAttributes as CFAttributes
|
||||
from midealan.devices.da import DeviceAttributes as DAAttributes
|
||||
from midealan.devices.db import DeviceAttributes as DBAttributes
|
||||
from midealan.devices.dc import DeviceAttributes as DCAttributes
|
||||
from midealan.devices.e1 import DeviceAttributes as E1Attributes
|
||||
from midealan.devices.e2 import DeviceAttributes as E2Attributes
|
||||
from midealan.devices.e3 import DeviceAttributes as E3Attributes
|
||||
from midealan.devices.e6 import DeviceAttributes as E6Attributes
|
||||
from midealan.devices.e8 import DeviceAttributes as E8Attributes
|
||||
from midealan.devices.ea import DeviceAttributes as EAAttributes
|
||||
from midealan.devices.ec import DeviceAttributes as ECAttributes
|
||||
from midealan.devices.ed import DeviceAttributes as EDAttributes
|
||||
from midealan.devices.fa import DeviceAttributes as FAAttributes
|
||||
from midealan.devices.fb import DeviceAttributes as FBAttributes
|
||||
from midealan.devices.fc import DeviceAttributes as FCAttributes
|
||||
from midealan.devices.fd import DeviceAttributes as FDAttributes
|
||||
from midealan.devices.x26 import DeviceAttributes as X26Attributes
|
||||
from midealan.devices.x34 import DeviceAttributes as X34Attributes
|
||||
from midealan.devices.x40 import DeviceAttributes as X40Attributes
|
||||
|
||||
FRESH_AIR_EXHAUST = "fresh_air_exhaust"
|
||||
FRESH_AIR_EXHAUST_MODE = "fresh_air_exhaust_mode"
|
||||
@@ -647,28 +668,28 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
"unit": UnitOfElectricPotential.VOLT,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
},
|
||||
ACAttributes.indoor_coil_temperature: {
|
||||
ACAttributes.indoor_ambient_temperature: {
|
||||
"type": Platform.SENSOR,
|
||||
"required_attribute": ACAttributes.indoor_coil_temperature,
|
||||
"translation_key": "indoor_coil_temperature",
|
||||
"required_attribute": ACAttributes.indoor_ambient_temperature,
|
||||
"translation_key": "indoor_ambient_temperature",
|
||||
"name": "Indoor Coil Temperature (T1)",
|
||||
"device_class": SensorDeviceClass.TEMPERATURE,
|
||||
"unit": UnitOfTemperature.CELSIUS,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
},
|
||||
ACAttributes.evaporator_temperature: {
|
||||
ACAttributes.indoor_coil_temperature: {
|
||||
"type": Platform.SENSOR,
|
||||
"required_attribute": ACAttributes.evaporator_temperature,
|
||||
"translation_key": "evaporator_temperature",
|
||||
"required_attribute": ACAttributes.indoor_coil_temperature,
|
||||
"translation_key": "indoor_coil_temperature",
|
||||
"name": "Evaporator Temperature (T2)",
|
||||
"device_class": SensorDeviceClass.TEMPERATURE,
|
||||
"unit": UnitOfTemperature.CELSIUS,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
},
|
||||
ACAttributes.condenser_temperature: {
|
||||
ACAttributes.outdoor_coil_temperature: {
|
||||
"type": Platform.SENSOR,
|
||||
"required_attribute": ACAttributes.condenser_temperature,
|
||||
"translation_key": "condenser_temperature",
|
||||
"required_attribute": ACAttributes.outdoor_coil_temperature,
|
||||
"translation_key": "outdoor_coil_temperature",
|
||||
"name": "Condenser Temperature (T3)",
|
||||
"device_class": SensorDeviceClass.TEMPERATURE,
|
||||
"unit": UnitOfTemperature.CELSIUS,
|
||||
@@ -904,6 +925,14 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
"icon": "mdi:box-shadow",
|
||||
"device_class": BinarySensorDeviceClass.DOOR,
|
||||
},
|
||||
# Deliberately NO device_class here. BinarySensorDeviceClass.LOCK
|
||||
# defines on = UNLOCKED, but this attribute is True when the child
|
||||
# lock is ENGAGED, so that device class would display it inverted.
|
||||
B0Attributes.child_lock: {
|
||||
"type": Platform.BINARY_SENSOR,
|
||||
"name": "Child Lock",
|
||||
"icon": "mdi:lock",
|
||||
},
|
||||
B0Attributes.tank_ejected: {
|
||||
"type": Platform.BINARY_SENSOR,
|
||||
"translation_key": "tank_ejected",
|
||||
@@ -938,6 +967,16 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
"name": "Status",
|
||||
"icon": "mdi:information",
|
||||
},
|
||||
B0Attributes.mode: {
|
||||
"type": Platform.SENSOR,
|
||||
"name": "Mode",
|
||||
"icon": "mdi:chef-hat",
|
||||
},
|
||||
B0Attributes.fire_power: {
|
||||
"type": Platform.SENSOR,
|
||||
"name": "Fire Power",
|
||||
"icon": "mdi:fire",
|
||||
},
|
||||
B0Attributes.time_remaining: {
|
||||
"type": Platform.SENSOR,
|
||||
"translation_key": "time_remaining",
|
||||
@@ -1022,7 +1061,7 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
"type": Platform.BINARY_SENSOR,
|
||||
"translation_key": "top_compartment_cooling",
|
||||
"name": "Top Compartment Cooling",
|
||||
"icon": "snowflake-variant",
|
||||
"icon": "mdi:snowflake-variant",
|
||||
"device_class": BinarySensorDeviceClass.RUNNING,
|
||||
},
|
||||
B3Attributes.middle_compartment_door: {
|
||||
@@ -1043,7 +1082,7 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
"type": Platform.BINARY_SENSOR,
|
||||
"translation_key": "middle_compartment_cooling",
|
||||
"name": "Middle Compartment Cooling",
|
||||
"icon": "snowflake-variant",
|
||||
"icon": "mdi:snowflake-variant",
|
||||
"device_class": BinarySensorDeviceClass.RUNNING,
|
||||
},
|
||||
B3Attributes.bottom_compartment_door: {
|
||||
@@ -1064,7 +1103,7 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
"type": Platform.BINARY_SENSOR,
|
||||
"translation_key": "bottom_compartment_cooling",
|
||||
"name": "Bottom Compartment Cooling",
|
||||
"icon": "snowflake-variant",
|
||||
"icon": "mdi:snowflake-variant",
|
||||
"device_class": BinarySensorDeviceClass.RUNNING,
|
||||
},
|
||||
B3Attributes.top_compartment_status: {
|
||||
@@ -1434,7 +1473,7 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
"name": "Silent Mode",
|
||||
"icon": "mdi:fan-remove",
|
||||
},
|
||||
C3Attributes.SILENT_LEVEL: {
|
||||
C3Attributes.silent_level: {
|
||||
"type": Platform.SELECT,
|
||||
"translation_key": "silent_level",
|
||||
"name": "Silent Level",
|
||||
@@ -1750,7 +1789,7 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
"entities": {
|
||||
"climate": {
|
||||
"type": Platform.CLIMATE,
|
||||
"icon": "hass:air-conditioner",
|
||||
"icon": "mdi:air-conditioner",
|
||||
"default": True,
|
||||
},
|
||||
CCAttributes.aux_heating: {
|
||||
@@ -2161,7 +2200,7 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
"entities": {
|
||||
"climate": {
|
||||
"type": Platform.CLIMATE,
|
||||
"icon": "hass:air-conditioner",
|
||||
"icon": "mdi:air-conditioner",
|
||||
"default": True,
|
||||
},
|
||||
CFAttributes.aux_heating: {
|
||||
@@ -2247,7 +2286,7 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
DAAttributes.wash_level: {
|
||||
"type": Platform.SENSOR,
|
||||
"translation_key": "wash_level",
|
||||
"name": "Rinse count",
|
||||
"name": "Wash level",
|
||||
"icon": "mdi:hydraulic-oil-level",
|
||||
},
|
||||
DAAttributes.wash_strength: {
|
||||
@@ -2922,7 +2961,6 @@ MIDEA_DEVICES: dict[int, dict[str, dict[str, Any] | str]] = {
|
||||
"type": Platform.BINARY_SENSOR,
|
||||
"translation_key": "finished",
|
||||
"name": "Finished",
|
||||
"icon": "",
|
||||
},
|
||||
E8Attributes.water_shortage: {
|
||||
"type": Platform.BINARY_SENSOR,
|
||||
|
||||
@@ -14,7 +14,7 @@ else:
|
||||
)
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, format_mac
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from midealocal.device import MideaDevice
|
||||
from midealan.device import MideaDevice
|
||||
|
||||
from .const import DOMAIN
|
||||
from .midea_devices import MIDEA_DEVICES
|
||||
@@ -79,13 +79,15 @@ class MideaEntity(Entity):
|
||||
# Example: device_class = temperature -> "Temperature".
|
||||
elif "device_class" in self._config:
|
||||
self._attr_name = None # Let HA generate from device_class
|
||||
# Step 4: Nothing available,
|
||||
# Step 4: Nothing available (no translation_key, no name, no
|
||||
# device_class). With has_entity_name=True HA already prepends the
|
||||
# device name, so the entity's own name must be None — the entity then
|
||||
# represents the device's main feature and shows just the device name.
|
||||
# (The previous fallback embedded the device name itself, yielding a
|
||||
# doubled "DeviceName DeviceName", or "DeviceName None" when a null
|
||||
# "name" key was present.)
|
||||
else:
|
||||
self._attr_name = (
|
||||
f"{self._device_name} {self._config.get('name')}"
|
||||
if "name" in self._config
|
||||
else f"{self._device_name}"
|
||||
)
|
||||
self._attr_name = None
|
||||
|
||||
@property
|
||||
def device(self) -> MideaDevice:
|
||||
|
||||
@@ -7,7 +7,7 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_DEVICE_ID, CONF_SWITCHES, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from midealocal.device import MideaDevice
|
||||
from midealan.device import MideaDevice
|
||||
|
||||
from .const import DEVICES, DOMAIN
|
||||
from .midea_devices import MIDEA_DEVICES
|
||||
@@ -44,53 +44,41 @@ class MideaNumber(MideaEntity, NumberEntity):
|
||||
self._min_value = self._config.get("min")
|
||||
self._step_value = self._config.get("step")
|
||||
|
||||
def _resolve_bound(self, bound: Any) -> float: # ruff:ignore[any-type]
|
||||
"""Resolve a min/max/step config value to a concrete number.
|
||||
|
||||
A numeric literal is used as-is. Otherwise the value is treated as an
|
||||
attribute name: prefer the device attribute of that name, falling back
|
||||
to a same-named device property populated by ``set_customize``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
The resolved bound as a float.
|
||||
|
||||
"""
|
||||
if isinstance(bound, (int, float)):
|
||||
return cast("float", bound)
|
||||
# `bound` is an attribute name. Use `is not None` (not truthiness) so a
|
||||
# legitimate 0 is not treated as "missing", and read the attribute once.
|
||||
value = self._device.get_attribute(attr=bound)
|
||||
if value is None:
|
||||
value = getattr(self._device, bound)
|
||||
return cast("float", value)
|
||||
|
||||
@property
|
||||
def native_min_value(self) -> float:
|
||||
"""Minimum value allowed."""
|
||||
return cast(
|
||||
"float",
|
||||
(
|
||||
self._min_value
|
||||
if isinstance(self._min_value, int)
|
||||
else (
|
||||
self._device.get_attribute(attr=self._min_value)
|
||||
if self._device.get_attribute(attr=self._min_value)
|
||||
else getattr(self._device, self._min_value)
|
||||
)
|
||||
),
|
||||
)
|
||||
return self._resolve_bound(self._min_value)
|
||||
|
||||
@property
|
||||
def native_max_value(self) -> float:
|
||||
"""Maximum value allowed."""
|
||||
return cast(
|
||||
"float",
|
||||
(
|
||||
self._max_value
|
||||
if isinstance(self._max_value, int)
|
||||
else (
|
||||
self._device.get_attribute(attr=self._max_value)
|
||||
if self._device.get_attribute(attr=self._max_value)
|
||||
else getattr(self._device, self._max_value)
|
||||
)
|
||||
),
|
||||
)
|
||||
return self._resolve_bound(self._max_value)
|
||||
|
||||
@property
|
||||
def native_step(self) -> float:
|
||||
"""Step value between allowed values."""
|
||||
return cast(
|
||||
"float",
|
||||
(
|
||||
self._step_value
|
||||
if isinstance(self._step_value, int)
|
||||
else (
|
||||
self._device.get_attribute(attr=self._step_value)
|
||||
if self._device.get_attribute(attr=self._step_value)
|
||||
else getattr(self._device, self._step_value)
|
||||
)
|
||||
),
|
||||
)
|
||||
return self._resolve_bound(self._step_value)
|
||||
|
||||
@property
|
||||
def native_value(self) -> float:
|
||||
|
||||
@@ -7,14 +7,14 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_DEVICE_ID, CONF_SWITCHES, Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from midealocal.device import MideaDevice
|
||||
from midealan.device import MideaDevice
|
||||
|
||||
from .const import DEVICES, DOMAIN, supports_model
|
||||
from .midea_devices import MIDEA_DEVICES
|
||||
from .midea_entity import MideaEntity
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from midealocal.devices.e1 import MideaE1Device
|
||||
from midealan.devices.e1 import MideaE1Device
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -94,7 +94,7 @@ class MideaSelect(MideaEntity, SelectEntity):
|
||||
self._device.set_attribute(self._attribute_key, option)
|
||||
|
||||
def _get_options_dict(self) -> dict[int, str]:
|
||||
"""Return option dict from the backing midea-local device.
|
||||
"""Return option dict from the backing midea-lan device.
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -104,7 +104,7 @@ class MideaSelect(MideaEntity, SelectEntity):
|
||||
return cast("dict[int, str]", getattr(self._device, self._options_dict_name))
|
||||
|
||||
def _select_e1_work_mode(self, option: str) -> None:
|
||||
"""Set dishwasher work mode via midea-local's public E1 API.
|
||||
"""Set dishwasher work mode via midea-lan's public E1 API.
|
||||
|
||||
Raises
|
||||
------
|
||||
@@ -125,10 +125,9 @@ class MideaSelect(MideaEntity, SelectEntity):
|
||||
if (
|
||||
power_attribute
|
||||
and self.hass
|
||||
and not self.hass.is_stopping
|
||||
and (self._attribute_key in status or power_attribute in status)
|
||||
):
|
||||
self.schedule_update_ha_state()
|
||||
self.schedule_update_if_running()
|
||||
|
||||
@staticmethod
|
||||
def _get_dict_key_by_value(source: dict[int, str], value: str) -> int | None:
|
||||
|
||||
@@ -13,7 +13,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from midealocal.device import MideaDevice
|
||||
from midealan.device import MideaDevice
|
||||
|
||||
from .const import DEVICES, DOMAIN, supports_model
|
||||
from .midea_devices import MIDEA_DEVICES
|
||||
@@ -185,9 +185,7 @@ class MideaEstimatedUsageSensor(MideaSensor, RestoreEntity):
|
||||
|
||||
self._last_progress = current_progress
|
||||
super().update_state(status)
|
||||
if (
|
||||
self.hass
|
||||
and not self.hass.is_stopping
|
||||
and ("progress" in status or "status" in status or "mode" in status)
|
||||
if self.hass and (
|
||||
"progress" in status or "status" in status or "mode" in status
|
||||
):
|
||||
self.schedule_update_ha_state()
|
||||
self.schedule_update_if_running()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"preset_account": "Anmeldung mit voreingestelltem Konto fehlgeschlagen, bitte melden Sie dieses Problem",
|
||||
"preset_account": "Anmeldung mit voreingestelltem Konto fehlgeschlagen, bitte dieses Problem melden",
|
||||
"login_failed": "Anmeldung fehlgeschlagen, Konto oder Passwort ist falsch",
|
||||
"no_devices": "Keine neuen verfügbaren Geräte im Netzwerk gefunden",
|
||||
"device_exist": "Das Gerät ist bereits konfiguriert",
|
||||
@@ -14,7 +14,7 @@
|
||||
"data": {
|
||||
"way": "Hinzufügen von Geräten"
|
||||
},
|
||||
"description": "Wählen Sie die Art des Hinzufügens eines Geräts",
|
||||
"description": "Die Art des Hinzufügens eines Geräts wählen",
|
||||
"title": "Neues Gerät hinzufügen"
|
||||
},
|
||||
"login": {
|
||||
@@ -22,11 +22,11 @@
|
||||
"account": "Konto",
|
||||
"password": "Passwort"
|
||||
},
|
||||
"description": "Melden Sie sich mit Ihrem Midea-Konto an. Ihr Konto wird nur verwendet, um die Geräteinformationen zu erhalten.\nSie können die Konfiguration entfernen, nachdem alle Geräte konfiguriert wurden.",
|
||||
"description": "Mit dem Midea-Konto anmelden. Das Konto wird nur verwendet, um die Geräteinformationen zu erhalten.\nDie Konfiguration kann wieder entfernt werden, nachdem alle Geräte konfiguriert wurden.",
|
||||
"title": "Login"
|
||||
},
|
||||
"discovery": {
|
||||
"description": "IP-Adresse des Geräts, geben Sie \"auto\" ein, um es automatisch zu finden.\nSie können auch eine IP-Adresse verwenden, um innerhalb eines bestimmten Netzwerks zu suchen, z.B. \"192.168.1.255\".",
|
||||
"description": "IP-Adresse des Geräts, \"auto\" eingeben, um es automatisch zu finden.\nEs kann auch eine IP-Adresse verwendet werden, um innerhalb eines bestimmten Netzwerks zu suchen, z.B. \"192.168.1.255\".",
|
||||
"title": "Suche",
|
||||
"data": {
|
||||
"ip_address": "IP-Adresse"
|
||||
@@ -40,7 +40,7 @@
|
||||
"data": {
|
||||
"device": "Geräte"
|
||||
},
|
||||
"description": "Wählen Sie ein Gerät zum Hinzufügen",
|
||||
"description": "Gerät auswählen um es hinzufügen",
|
||||
"title": "Neues Gerät gefunden"
|
||||
},
|
||||
"manually": {
|
||||
@@ -64,85 +64,85 @@
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"bar_door": {
|
||||
"name": "Bar Door"
|
||||
"name": "Bar-Tür"
|
||||
},
|
||||
"bar_door_overtime": {
|
||||
"name": "Bar Door Overtime"
|
||||
"name": "Bar-Tür Overtime"
|
||||
},
|
||||
"bathing_working": {
|
||||
"name": "Bathing Working Status"
|
||||
},
|
||||
"bottom_compartment_cooling": {
|
||||
"name": "Bottom Compartment Cooling"
|
||||
"name": "Bodenfach Kühlung"
|
||||
},
|
||||
"bottom_compartment_door": {
|
||||
"name": "Bottom Compartment Door"
|
||||
"name": "Bodenfach Tür"
|
||||
},
|
||||
"bottom_compartment_preheating": {
|
||||
"name": "Bottom Compartment Preheating"
|
||||
"name": "Bodenfach Vorheizer"
|
||||
},
|
||||
"burning_state": {
|
||||
"name": "Burning State"
|
||||
"name": "Brenner-Status"
|
||||
},
|
||||
"cleaning_reminder": {
|
||||
"name": "Cleaning Reminder"
|
||||
"name": "Reinigungs-Erinnerung"
|
||||
},
|
||||
"compressor_status": {
|
||||
"name": "Compressor Status"
|
||||
"name": "Kompressor-Status"
|
||||
},
|
||||
"cooking": {
|
||||
"name": "Cooking"
|
||||
"name": "Kochen"
|
||||
},
|
||||
"error_code": {
|
||||
"name": "Fehlercode"
|
||||
},
|
||||
"filter_change_reminder": {
|
||||
"name": "Filter Change Reminder"
|
||||
"name": "Filter-Wechsel-Erinnerung"
|
||||
},
|
||||
"filter_cleaning_reminder": {
|
||||
"name": "Filter Cleaning Reminder"
|
||||
"name": "Filter-Reinigungs-Erinnerung"
|
||||
},
|
||||
"finished": {
|
||||
"name": "Finished"
|
||||
"name": "Fertig"
|
||||
},
|
||||
"flex_zone_door": {
|
||||
"name": "Flex Door"
|
||||
"name": "Flex-Tür"
|
||||
},
|
||||
"flex_zone_door_overtime": {
|
||||
"name": "Flex Zone Door"
|
||||
"name": "Flexzonen-Tür"
|
||||
},
|
||||
"freezer_door": {
|
||||
"name": "Freezer Door"
|
||||
"name": "Gefrierfachtür"
|
||||
},
|
||||
"freezer_door_overtime": {
|
||||
"name": "Freezer Door Overtime"
|
||||
"name": "Gefrierfachtür Overtime"
|
||||
},
|
||||
"full_dust": {
|
||||
"name": "Full of Dust"
|
||||
"name": "Voller Staub"
|
||||
},
|
||||
"heating": {
|
||||
"name": "Heating"
|
||||
"name": "Heizen"
|
||||
},
|
||||
"heating_working": {
|
||||
"name": "Heating Working Status"
|
||||
"name": "Heizen Arbeitsstatus"
|
||||
},
|
||||
"keep_warm": {
|
||||
"name": "Keep Warm"
|
||||
"name": "Warmhalten"
|
||||
},
|
||||
"leak_water": {
|
||||
"name": "Leckwasser"
|
||||
},
|
||||
"lid_status": {
|
||||
"name": "Lid Status"
|
||||
"name": "Deckel-Status"
|
||||
},
|
||||
"middle_compartment_cooling": {
|
||||
"name": "Middle Compartment Cooling"
|
||||
"name": "Mittelfach-Kühlung"
|
||||
},
|
||||
"middle_compartment_door": {
|
||||
"name": "Middle Compartment Door"
|
||||
"name": "Mittelfach-Tür"
|
||||
},
|
||||
"middle_compartment_preheating": {
|
||||
"name": "Middle Compartment Preheating"
|
||||
"name": "Mittelfach Vorheizen"
|
||||
},
|
||||
"top_elec_heat": {
|
||||
"name": "Elektroheizung oben"
|
||||
@@ -160,28 +160,28 @@
|
||||
"name": "Multi-Terminal"
|
||||
},
|
||||
"oilcup_full": {
|
||||
"name": "Oil-cup Full"
|
||||
"name": "Ölbehälter voll"
|
||||
},
|
||||
"protection": {
|
||||
"name": "Protection"
|
||||
"name": "Schutz"
|
||||
},
|
||||
"refrigerator_door": {
|
||||
"name": "Refrigerator Door"
|
||||
"name": "Kühlschranktür"
|
||||
},
|
||||
"refrigerator_door_overtime": {
|
||||
"name": "Refrigerator Door Overtime"
|
||||
"name": "Kühlrschranktür Overtime"
|
||||
},
|
||||
"rinse_aid": {
|
||||
"name": "Rinse Aid Shortage"
|
||||
"name": "Spülhilfe leer"
|
||||
},
|
||||
"rsj_stand_by": {
|
||||
"name": "Standby"
|
||||
},
|
||||
"salt": {
|
||||
"name": "Salt Shortage"
|
||||
"name": "Salz leer"
|
||||
},
|
||||
"seat_status": {
|
||||
"name": "Seat Status"
|
||||
"name": "Sitz Status"
|
||||
},
|
||||
"smart_grid": {
|
||||
"name": "Smart Grid"
|
||||
@@ -202,52 +202,52 @@
|
||||
"name": "Zeitplan 2 aktiv"
|
||||
},
|
||||
"status_dhw": {
|
||||
"name": "DHW status"
|
||||
"name": "DHW-Status"
|
||||
},
|
||||
"status_heating": {
|
||||
"name": "Heating status"
|
||||
"name": "Aufheiz-Status"
|
||||
},
|
||||
"status_ibh": {
|
||||
"name": "IBH status"
|
||||
"name": "IBH-Status"
|
||||
},
|
||||
"status_tbh": {
|
||||
"name": "TBH status"
|
||||
"name": "TBH-Status"
|
||||
},
|
||||
"tank_ejected": {
|
||||
"name": "Tank Ejected"
|
||||
"name": "Behälter ausgeworfen"
|
||||
},
|
||||
"tank_full": {
|
||||
"name": "Tank status"
|
||||
"name": "Behälter-Status"
|
||||
},
|
||||
"top_compartment_cooling": {
|
||||
"name": "Top Compartment Cooling"
|
||||
},
|
||||
"top_compartment_door": {
|
||||
"name": "Top Compartment Door"
|
||||
"name": "Oberfach-Tür"
|
||||
},
|
||||
"top_compartment_preheating": {
|
||||
"name": "Top Compartment Preheating"
|
||||
"name": "Oberfach Vorhzeizen"
|
||||
},
|
||||
"water_change_reminder": {
|
||||
"name": "Water Change Reminder"
|
||||
"name": "Wasserwechsel-Erinnerung"
|
||||
},
|
||||
"water_shortage": {
|
||||
"name": "Water Shortage"
|
||||
"name": "Wasser leer"
|
||||
},
|
||||
"with_pressure": {
|
||||
"name": "With Pressure"
|
||||
"name": "Mit Druck"
|
||||
},
|
||||
"zone1_room_temp_mode": {
|
||||
"name": "Zone1 Room-temperature Mode"
|
||||
"name": "Zone1 Taumtemperatur-Modus"
|
||||
},
|
||||
"zone1_water_temp_mode": {
|
||||
"name": "Zone1 Water-temperature Mode"
|
||||
"name": "Zone1 Wassertemperatur-Modus"
|
||||
},
|
||||
"zone2_room_temp_mode": {
|
||||
"name": "Zone2 Room-temperature Mode"
|
||||
"name": "Zone2 Zimmertemperatur-Modus"
|
||||
},
|
||||
"zone2_water_temp_mode": {
|
||||
"name": "Zone2 Water-temperature Mode"
|
||||
"name": "Zone2 Wassertemperatur-Modus"
|
||||
},
|
||||
"microcrystal_fresh": {
|
||||
"name": "Mikrokristall-Frische"
|
||||
@@ -284,7 +284,7 @@
|
||||
},
|
||||
"fan": {
|
||||
"fresh_air": {
|
||||
"name": "Fresh Air"
|
||||
"name": "Frischluft"
|
||||
},
|
||||
"fresh_air_exhaust": {
|
||||
"name": "Frischluft-Abluft"
|
||||
@@ -297,16 +297,16 @@
|
||||
},
|
||||
"number": {
|
||||
"dry_level": {
|
||||
"name": "Dry Level"
|
||||
"name": "Trocken-Level"
|
||||
},
|
||||
"heating_level": {
|
||||
"name": "Heating Level"
|
||||
"name": "Heiz-Level"
|
||||
},
|
||||
"seat_temp_level": {
|
||||
"name": "Seat Temperature Level"
|
||||
"name": "Sitztemperatur-Level"
|
||||
},
|
||||
"water_temp_level": {
|
||||
"name": "Water Temperature Level"
|
||||
"name": "Wassertemperatur-Level"
|
||||
},
|
||||
"fan_speed_percent": {
|
||||
"name": "Lüftergeschwindigkeit (Prozent)"
|
||||
@@ -326,13 +326,13 @@
|
||||
},
|
||||
"select": {
|
||||
"detect_mode": {
|
||||
"name": "Detect Mode"
|
||||
"name": "Detektions-Modus"
|
||||
},
|
||||
"direction": {
|
||||
"name": "Direction"
|
||||
"name": "Richtung"
|
||||
},
|
||||
"fan_speed": {
|
||||
"name": "Fan Speed"
|
||||
"name": "Lüftergeschwindigkeit"
|
||||
},
|
||||
"fresh_air_exhaust_mode": {
|
||||
"name": "Frischluft-Abluftgeschwindigkeit",
|
||||
@@ -354,31 +354,31 @@
|
||||
}
|
||||
},
|
||||
"mode": {
|
||||
"name": "Mode"
|
||||
"name": "Modus"
|
||||
},
|
||||
"oscillation_angle": {
|
||||
"name": "Oscillation Angle"
|
||||
"name": "Oszillationswinkel"
|
||||
},
|
||||
"oscillation_mode": {
|
||||
"name": "Oscillation Mode"
|
||||
"name": "Oszillationsmodus"
|
||||
},
|
||||
"rate_select": {
|
||||
"name": "Power Rate Limit"
|
||||
"name": "Leistungslimit"
|
||||
},
|
||||
"screen_display": {
|
||||
"name": "Screen Display"
|
||||
"name": "Bildschirmanzeige"
|
||||
},
|
||||
"silent_level": {
|
||||
"name": "Silent Level"
|
||||
"name": "Silent-Stufe"
|
||||
},
|
||||
"tilting_angle": {
|
||||
"name": "Tilting Angle"
|
||||
"name": "Kippwinkel"
|
||||
},
|
||||
"wash_mode": {
|
||||
"name": "Waschmodus"
|
||||
},
|
||||
"water_level_set": {
|
||||
"name": "Water Level Setting"
|
||||
"name": "Einstellung Wasserhöhe"
|
||||
},
|
||||
"wind_lr_angle": {
|
||||
"name": "Luftstrom horizontal",
|
||||
@@ -425,19 +425,19 @@
|
||||
"name": "Bottom Temperature"
|
||||
},
|
||||
"bright": {
|
||||
"name": "Bright Level"
|
||||
"name": "Helligkeitsstufe"
|
||||
},
|
||||
"compressor_temperature": {
|
||||
"name": "Compressor Temperature"
|
||||
"name": "Kompressor-Temperatur"
|
||||
},
|
||||
"condenser_temperature": {
|
||||
"name": "Condenser Temperature"
|
||||
"name": "Kondensator-Temperature"
|
||||
},
|
||||
"current_energy_consumption": {
|
||||
"name": "Current Energy Consumption"
|
||||
"name": "Aktueller Energieverbrauch"
|
||||
},
|
||||
"current_temperature": {
|
||||
"name": "Current Temperature"
|
||||
"name": "Aktuelle Temperatur"
|
||||
},
|
||||
"dehydration_speed": {
|
||||
"name": "dehydration speed"
|
||||
@@ -461,7 +461,7 @@
|
||||
"name": "Verschmutzungsgrad"
|
||||
},
|
||||
"detergent": {
|
||||
"name": "detergent"
|
||||
"name": "Reinigungsmittel"
|
||||
},
|
||||
"intensity": {
|
||||
"name": "Intensität"
|
||||
@@ -502,7 +502,7 @@
|
||||
}
|
||||
},
|
||||
"error_code": {
|
||||
"name": "Error Code"
|
||||
"name": "Fehlercode"
|
||||
},
|
||||
"estimated_energy_consumption": {
|
||||
"name": "Geschätzter Energieverbrauch"
|
||||
@@ -511,7 +511,7 @@
|
||||
"name": "Geschätzter Wasserverbrauch"
|
||||
},
|
||||
"fan_level": {
|
||||
"name": "Fan level"
|
||||
"name": "Lüfterstufe"
|
||||
},
|
||||
"filter_life": {
|
||||
"name": "Filter Life"
|
||||
@@ -520,19 +520,19 @@
|
||||
"name": "Filter1 Verfügbare Tage"
|
||||
},
|
||||
"filter1_life": {
|
||||
"name": "Filter1 Lebensdauer Level"
|
||||
"name": "Filter1 Lebensdauer Stufe"
|
||||
},
|
||||
"filter2_days": {
|
||||
"name": "Filter2 Verfügbare Tage"
|
||||
},
|
||||
"filter2_life": {
|
||||
"name": "Filter2 Lebensdauer Level"
|
||||
"name": "Filter2 Lebensdauer Stufe"
|
||||
},
|
||||
"filter3_days": {
|
||||
"name": "Filter3 Verfügbare Tage"
|
||||
},
|
||||
"filter3_life": {
|
||||
"name": "Filter3 Lebensdauer Level"
|
||||
"name": "Filter3 Lebensdauer Stufe"
|
||||
},
|
||||
"flex_zone_actual_temp": {
|
||||
"name": "Flex Zone Actual Temperature"
|
||||
@@ -562,10 +562,10 @@
|
||||
"name": "TDS-Wert rein"
|
||||
},
|
||||
"indoor_humidity": {
|
||||
"name": "Indoor Humidity"
|
||||
"name": "Innenraumluftfeuchtigkeit"
|
||||
},
|
||||
"indoor_temperature": {
|
||||
"name": "Indoor Temperature"
|
||||
"name": "Innenraumtemperatur"
|
||||
},
|
||||
"keep_warm_remaining": {
|
||||
"name": "Keep Warm Remaining"
|
||||
@@ -592,7 +592,7 @@
|
||||
"name": "TDS-Wert raus"
|
||||
},
|
||||
"outdoor_temperature": {
|
||||
"name": "Outdoor Temperature"
|
||||
"name": "Außentemperatur"
|
||||
},
|
||||
"program": {
|
||||
"name": "Program"
|
||||
@@ -601,7 +601,7 @@
|
||||
"name": "Progress"
|
||||
},
|
||||
"realtime_power": {
|
||||
"name": "Realtime Power"
|
||||
"name": "Echtzeitleistung"
|
||||
},
|
||||
"pmv": {
|
||||
"name": "PMV"
|
||||
@@ -661,10 +661,10 @@
|
||||
"name": "Tank Actual Temperature"
|
||||
},
|
||||
"target_temperature": {
|
||||
"name": "Target Temperature"
|
||||
"name": "Temperaturziel"
|
||||
},
|
||||
"time_remaining": {
|
||||
"name": "Time Remaining"
|
||||
"name": "Verbleibende Zeit"
|
||||
},
|
||||
"top_compartment_remaining": {
|
||||
"name": "Top Compartment Remaining"
|
||||
@@ -679,16 +679,16 @@
|
||||
"name": "Top Temperature"
|
||||
},
|
||||
"total_energy_consumption": {
|
||||
"name": "Total Energy Consumption"
|
||||
"name": "Gesamter Energieverbrauch"
|
||||
},
|
||||
"total_produced_energy": {
|
||||
"name": "Total produced energy"
|
||||
"name": "Gesamte produzierte Energie"
|
||||
},
|
||||
"tvoc": {
|
||||
"name": "TVOC"
|
||||
},
|
||||
"wash_level": {
|
||||
"name": "rinse count"
|
||||
"name": "Waschstufe"
|
||||
},
|
||||
"wash_strength": {
|
||||
"name": "wash strength"
|
||||
@@ -739,7 +739,7 @@
|
||||
"name": "Desinfektionstemperatur"
|
||||
},
|
||||
"max_temperature": {
|
||||
"name": "Maximale Zieltemperatur"
|
||||
"name": "Maximales Temperaturziel"
|
||||
},
|
||||
"vacation_start_year": {
|
||||
"name": "Urlaubsbeginn Jahr"
|
||||
@@ -772,7 +772,7 @@
|
||||
"name": "Kompressorfrequenz"
|
||||
},
|
||||
"target_compressor_frequency": {
|
||||
"name": "Ziel-Kompressorfrequenz"
|
||||
"name": "Kompressorfrequenz-Ziel"
|
||||
},
|
||||
"compressor_current": {
|
||||
"name": "Kompressorstrom"
|
||||
@@ -783,23 +783,26 @@
|
||||
"compressor_power": {
|
||||
"name": "Kompressorleistung"
|
||||
},
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Innenraum-Wärmetauschertemperatur (T1)"
|
||||
"indoor_ambient_temperature": {
|
||||
"name": "Innenraum-Umgebungstemperatur(T1)"
|
||||
},
|
||||
"evaporator_temperature": {
|
||||
"name": "Verdampfertemperatur (T2)"
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Innentemperatur der Heizschlange(T2)"
|
||||
},
|
||||
"outdoor_coil_temperature": {
|
||||
"name": "Außentemperatur der Heizschlange(T3)"
|
||||
},
|
||||
"outdoor_ambient_temperature": {
|
||||
"name": "Außenumgebungstemperatur (T4)"
|
||||
"name": "Außenumgebungstemperatur(T4)"
|
||||
},
|
||||
"discharge_pipe_temperature": {
|
||||
"name": "Druckleitungstemperatur (TP)"
|
||||
"name": "Druckleitungstemperatur(TP)"
|
||||
},
|
||||
"indoor_fan_speed": {
|
||||
"name": "Innenlüftergeschwindigkeit"
|
||||
"name": "Innengerät Lüftergeschwindigkeit"
|
||||
},
|
||||
"target_indoor_fan_speed": {
|
||||
"name": "Ziel-Innenlüftergeschwindigkeit"
|
||||
"name": "Innengerät Lüftergeschwindigkeit-Ziel"
|
||||
},
|
||||
"velocity": {
|
||||
"name": "Durchflussgeschwindigkeit"
|
||||
@@ -813,7 +816,7 @@
|
||||
"name": "Aux Heating"
|
||||
},
|
||||
"boost_mode": {
|
||||
"name": "Boost Mode"
|
||||
"name": "Turbo-Modus"
|
||||
},
|
||||
"breezeless": {
|
||||
"name": "Breezeless"
|
||||
@@ -825,19 +828,19 @@
|
||||
"name": "CL-Sterilisation"
|
||||
},
|
||||
"comfort_mode": {
|
||||
"name": "Comfort Mode"
|
||||
"name": "Comfort-Modus"
|
||||
},
|
||||
"dhw_power": {
|
||||
"name": "DHW Power"
|
||||
},
|
||||
"disinfect": {
|
||||
"name": "Disinfect"
|
||||
"name": "Desinfizieren"
|
||||
},
|
||||
"vacation_mode": {
|
||||
"name": "Urlaubsmodus"
|
||||
},
|
||||
"dry": {
|
||||
"name": "Dry"
|
||||
"name": "Trocknen"
|
||||
},
|
||||
"cold_water_single": {
|
||||
"name": "Kaltwasser einzeln"
|
||||
@@ -846,7 +849,7 @@
|
||||
"name": "Kaltwasser Punkt"
|
||||
},
|
||||
"eco_mode": {
|
||||
"name": "ECO Mode"
|
||||
"name": "ECO-Modus"
|
||||
},
|
||||
"memory": {
|
||||
"name": "Memo U"
|
||||
@@ -858,13 +861,13 @@
|
||||
"name": "Foam Shield"
|
||||
},
|
||||
"frost_protect": {
|
||||
"name": "Frost Protect"
|
||||
"name": "Frostschutz"
|
||||
},
|
||||
"heating_power": {
|
||||
"name": "Heating Power"
|
||||
"name": "Heizleistung"
|
||||
},
|
||||
"indirect_wind": {
|
||||
"name": "Indirect Wind"
|
||||
"name": "Indirekter Wind"
|
||||
},
|
||||
"leak_water_protection": {
|
||||
"name": "Leckschutz"
|
||||
@@ -882,13 +885,13 @@
|
||||
"name": "Main Power"
|
||||
},
|
||||
"natural_wind": {
|
||||
"name": "Natural Wind"
|
||||
"name": "Natürlicher Wind"
|
||||
},
|
||||
"night_light": {
|
||||
"name": "Night Light"
|
||||
"name": "Nachtlicht"
|
||||
},
|
||||
"oscillate": {
|
||||
"name": "Oscillate"
|
||||
"name": "Oszillieren"
|
||||
},
|
||||
"power": {
|
||||
"name": "Strom"
|
||||
@@ -897,7 +900,7 @@
|
||||
"name": "Powerful Purification"
|
||||
},
|
||||
"prompt_tone": {
|
||||
"name": "Prompt Tone"
|
||||
"name": "Bestätigungston"
|
||||
},
|
||||
"regeneration": {
|
||||
"name": "Regeneration"
|
||||
@@ -909,19 +912,19 @@
|
||||
"name": "Wasserweg"
|
||||
},
|
||||
"screen_display": {
|
||||
"name": "Screen Display"
|
||||
"name": "Bildschirmanzeige"
|
||||
},
|
||||
"screen_display_alternate": {
|
||||
"name": "Screen Display Alternate"
|
||||
"name": "Bildschirmanzeige Alternative"
|
||||
},
|
||||
"sensor_light": {
|
||||
"name": "Sensor Light"
|
||||
},
|
||||
"silent_mode": {
|
||||
"name": "Silent Mode"
|
||||
"name": "Silent-Modus"
|
||||
},
|
||||
"sleep_mode": {
|
||||
"name": "Sleep Mode"
|
||||
"name": "Sleep-Modus"
|
||||
},
|
||||
"out_silent": {
|
||||
"name": "Außengerät Leisemodus"
|
||||
@@ -948,22 +951,22 @@
|
||||
"name": "Sterilisation"
|
||||
},
|
||||
"storage": {
|
||||
"name": "Storage"
|
||||
"name": "Aufbewahrung"
|
||||
},
|
||||
"swing": {
|
||||
"name": "swing"
|
||||
"name": "Schwingen"
|
||||
},
|
||||
"swing_horizontal": {
|
||||
"name": "Swing Horizontal"
|
||||
"name": "Horizontal schwingen"
|
||||
},
|
||||
"swing_vertical": {
|
||||
"name": "Swing Vertical"
|
||||
"name": "Vertikal schwingen"
|
||||
},
|
||||
"self_clean": {
|
||||
"name": "Self Clean"
|
||||
"name": "Selbstreinigung"
|
||||
},
|
||||
"sound": {
|
||||
"name": "Sound"
|
||||
"name": "Ton"
|
||||
},
|
||||
"tbh": {
|
||||
"name": "TBH"
|
||||
@@ -998,13 +1001,13 @@
|
||||
},
|
||||
"water_heater": {
|
||||
"domestic_hot_water": {
|
||||
"name": "Domestic hot water"
|
||||
"name": "Heimisches heißes Wasser"
|
||||
},
|
||||
"bathing": {
|
||||
"name": "Bathing"
|
||||
"name": "Baden"
|
||||
},
|
||||
"heating": {
|
||||
"name": "Heating"
|
||||
"name": "Heizen"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -688,7 +688,7 @@
|
||||
"name": "TVOC"
|
||||
},
|
||||
"wash_level": {
|
||||
"name": "Rinse count"
|
||||
"name": "Wash level"
|
||||
},
|
||||
"wash_strength": {
|
||||
"name": "Wash strength"
|
||||
@@ -783,17 +783,20 @@
|
||||
"compressor_power": {
|
||||
"name": "Compressor Power"
|
||||
},
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Indoor Coil Temperature (T1)"
|
||||
"indoor_ambient_temperature": {
|
||||
"name": "Indoor Ambient Temperature(T1)"
|
||||
},
|
||||
"evaporator_temperature": {
|
||||
"name": "Evaporator Temperature (T2)"
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Indoor Coil Temperature(T2)"
|
||||
},
|
||||
"outdoor_coil_temperature": {
|
||||
"name": "Outdoor Coil Temperature(T3)"
|
||||
},
|
||||
"outdoor_ambient_temperature": {
|
||||
"name": "Outdoor Ambient Temperature (T4)"
|
||||
"name": "Outdoor Ambient Temperature(T4)"
|
||||
},
|
||||
"discharge_pipe_temperature": {
|
||||
"name": "Discharge Pipe Temperature (TP)"
|
||||
"name": "Discharge Pipe Temperature(TP)"
|
||||
},
|
||||
"indoor_fan_speed": {
|
||||
"name": "Indoor Fan Speed"
|
||||
|
||||
@@ -688,7 +688,7 @@
|
||||
"name": "COVT"
|
||||
},
|
||||
"wash_level": {
|
||||
"name": "Recuento de aclarados"
|
||||
"name": "Nivel de lavado"
|
||||
},
|
||||
"wash_strength": {
|
||||
"name": "Potencia de lavado"
|
||||
@@ -783,17 +783,20 @@
|
||||
"compressor_power": {
|
||||
"name": "Potencia del compresor"
|
||||
},
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Temperatura del serpentín interior (T1)"
|
||||
"indoor_ambient_temperature": {
|
||||
"name": "Temperatura ambiente interior(T1)"
|
||||
},
|
||||
"evaporator_temperature": {
|
||||
"name": "Temperatura del evaporador (T2)"
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Temperatura del serpentín interior(T2)"
|
||||
},
|
||||
"outdoor_coil_temperature": {
|
||||
"name": "Temperatura del serpentín exterior(T3)"
|
||||
},
|
||||
"outdoor_ambient_temperature": {
|
||||
"name": "Temperatura ambiente exterior (T4)"
|
||||
"name": "Temperatura ambiente exterior(T4)"
|
||||
},
|
||||
"discharge_pipe_temperature": {
|
||||
"name": "Temperatura del tubo de descarga (TP)"
|
||||
"name": "Temperatura del tubo de descarga(TP)"
|
||||
},
|
||||
"indoor_fan_speed": {
|
||||
"name": "Velocidad del ventilador interior"
|
||||
|
||||
@@ -688,7 +688,7 @@
|
||||
"name": "TVOC"
|
||||
},
|
||||
"wash_level": {
|
||||
"name": "rinse count"
|
||||
"name": "Niveau de lavage"
|
||||
},
|
||||
"wash_strength": {
|
||||
"name": "wash strength"
|
||||
@@ -783,17 +783,20 @@
|
||||
"compressor_power": {
|
||||
"name": "Puissance du compresseur"
|
||||
},
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Température de la batterie intérieure (T1)"
|
||||
"indoor_ambient_temperature": {
|
||||
"name": "Température ambiante intérieure(T1)"
|
||||
},
|
||||
"evaporator_temperature": {
|
||||
"name": "Température de l'évaporateur (T2)"
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Température del'échangeur intérieur(T2)"
|
||||
},
|
||||
"outdoor_coil_temperature": {
|
||||
"name": "Température de l'échangeur extérieur(T3)"
|
||||
},
|
||||
"outdoor_ambient_temperature": {
|
||||
"name": "Température ambiante extérieure (T4)"
|
||||
"name": "Température ambiante extérieure(T4)"
|
||||
},
|
||||
"discharge_pipe_temperature": {
|
||||
"name": "Température du tuyau de refoulement (TP)"
|
||||
"name": "Température du tuyau de refoulement(TP)"
|
||||
},
|
||||
"indoor_fan_speed": {
|
||||
"name": "Vitesse du ventilateur intérieur"
|
||||
|
||||
@@ -688,7 +688,7 @@
|
||||
"name": "TVOC"
|
||||
},
|
||||
"wash_level": {
|
||||
"name": "rinse count"
|
||||
"name": "Mosási szint"
|
||||
},
|
||||
"wash_strength": {
|
||||
"name": "wash strength"
|
||||
@@ -783,17 +783,20 @@
|
||||
"compressor_power": {
|
||||
"name": "Kompresszor teljesítmény"
|
||||
},
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Beltéri hőcserélő hőmérséklet (T1)"
|
||||
"indoor_ambient_temperature": {
|
||||
"name": "Beltéri környezeti hőmérséklet(T1)"
|
||||
},
|
||||
"evaporator_temperature": {
|
||||
"name": "Párologtató hőmérséklet (T2)"
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Beltéri hőcserélő hőmérséklet(T2)"
|
||||
},
|
||||
"outdoor_coil_temperature": {
|
||||
"name": "Kültéri hőcserélő hőmérséklet(T3)"
|
||||
},
|
||||
"outdoor_ambient_temperature": {
|
||||
"name": "Kültéri környezeti hőmérséklet (T4)"
|
||||
"name": "Kültéri környezeti hőmérséklet(T4)"
|
||||
},
|
||||
"discharge_pipe_temperature": {
|
||||
"name": "Nyomócső hőmérséklet (TP)"
|
||||
"name": "Nyomócső hőmérséklet(TP)"
|
||||
},
|
||||
"indoor_fan_speed": {
|
||||
"name": "Beltéri ventilátor sebesség"
|
||||
|
||||
@@ -688,7 +688,7 @@
|
||||
"name": "TVOC"
|
||||
},
|
||||
"wash_level": {
|
||||
"name": "Numero Risciacqui"
|
||||
"name": "Livello di lavaggio"
|
||||
},
|
||||
"wash_strength": {
|
||||
"name": "Intensità Lavaggio"
|
||||
@@ -783,17 +783,20 @@
|
||||
"compressor_power": {
|
||||
"name": "Potenza Compressore"
|
||||
},
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Temperatura Batteria Interna (T1)"
|
||||
"indoor_ambient_temperature": {
|
||||
"name": "Temperatura ambiente interna(T1)"
|
||||
},
|
||||
"evaporator_temperature": {
|
||||
"name": "Temperatura Evaporatore (T2)"
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Temperatura della serpentina interna(T2)"
|
||||
},
|
||||
"outdoor_coil_temperature": {
|
||||
"name": "Temperatura della serpentina esterna(T3)"
|
||||
},
|
||||
"outdoor_ambient_temperature": {
|
||||
"name": "Temperatura Ambiente Esterna (T4)"
|
||||
"name": "Temperatura Ambiente Esterna(T4)"
|
||||
},
|
||||
"discharge_pipe_temperature": {
|
||||
"name": "Temperatura Tubo di Scarico (TP)"
|
||||
"name": "Temperatura Tubo di Scarico(TP)"
|
||||
},
|
||||
"indoor_fan_speed": {
|
||||
"name": "Velocità Ventola Interna"
|
||||
|
||||
@@ -688,7 +688,7 @@
|
||||
"name": "TVOC"
|
||||
},
|
||||
"wash_level": {
|
||||
"name": "Количество полосканий"
|
||||
"name": "Уровень стирки"
|
||||
},
|
||||
"wash_strength": {
|
||||
"name": "Wash strength"
|
||||
@@ -783,17 +783,20 @@
|
||||
"compressor_power": {
|
||||
"name": "Мощность компрессора"
|
||||
},
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Температура внутреннего теплообменника (T1)"
|
||||
"indoor_ambient_temperature": {
|
||||
"name": "Комнатная температура(T1)"
|
||||
},
|
||||
"evaporator_temperature": {
|
||||
"name": "Температура испарителя (T2)"
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Температура внутреннего теплообменника(T2)"
|
||||
},
|
||||
"outdoor_coil_temperature": {
|
||||
"name": "Температура наружного теплообменника(T3)"
|
||||
},
|
||||
"outdoor_ambient_temperature": {
|
||||
"name": "Наружная температура окружающей среды (T4)"
|
||||
"name": "Наружная температура окружающей среды(T4)"
|
||||
},
|
||||
"discharge_pipe_temperature": {
|
||||
"name": "Температура нагнетательной трубы (TP)"
|
||||
"name": "Температура нагнетательной трубы(TP)"
|
||||
},
|
||||
"indoor_fan_speed": {
|
||||
"name": "Скорость внутреннего вентилятора"
|
||||
|
||||
@@ -688,7 +688,7 @@
|
||||
"name": "TVOC"
|
||||
},
|
||||
"wash_level": {
|
||||
"name": "rinse count"
|
||||
"name": "Úroveň prania"
|
||||
},
|
||||
"wash_strength": {
|
||||
"name": "wash strength"
|
||||
@@ -783,17 +783,20 @@
|
||||
"compressor_power": {
|
||||
"name": "Výkon kompresora"
|
||||
},
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Teplota vnútorného výmenníka (T1)"
|
||||
"indoor_ambient_temperature": {
|
||||
"name": "Vnútorná teplota prostredia(T1)"
|
||||
},
|
||||
"evaporator_temperature": {
|
||||
"name": "Teplota výparníka (T2)"
|
||||
"indoor_coil_temperature": {
|
||||
"name": "Teplota vnútornej špirály(T2)"
|
||||
},
|
||||
"outdoor_coil_temperature": {
|
||||
"name": "Teplota vonkajšej špirály(T3)"
|
||||
},
|
||||
"outdoor_ambient_temperature": {
|
||||
"name": "Vonkajšia teplota okolia (T4)"
|
||||
"name": "Vonkajšia teplota okolia(T4)"
|
||||
},
|
||||
"discharge_pipe_temperature": {
|
||||
"name": "Teplota výtlačného potrubia (TP)"
|
||||
"name": "Teplota výtlačného potrubia(TP)"
|
||||
},
|
||||
"indoor_fan_speed": {
|
||||
"name": "Rýchlosť vnútorného ventilátora"
|
||||
|
||||
@@ -783,17 +783,20 @@
|
||||
"compressor_power": {
|
||||
"name": "压缩机功率"
|
||||
},
|
||||
"indoor_coil_temperature": {
|
||||
"name": "室内盘管温度 (T1)"
|
||||
"indoor_ambient_temperature": {
|
||||
"name": "室内环境温度(T1)"
|
||||
},
|
||||
"evaporator_temperature": {
|
||||
"name": "蒸发器温度 (T2)"
|
||||
"indoor_coil_temperature": {
|
||||
"name": "室内盘管温度(T2)"
|
||||
},
|
||||
"outdoor_coil_temperature": {
|
||||
"name": "室外盘管温度(T3)"
|
||||
},
|
||||
"outdoor_ambient_temperature": {
|
||||
"name": "室外环境温度 (T4)"
|
||||
"name": "室外环境温度(T4)"
|
||||
},
|
||||
"discharge_pipe_temperature": {
|
||||
"name": "排气管温度 (TP)"
|
||||
"name": "排气管温度(TP)"
|
||||
},
|
||||
"indoor_fan_speed": {
|
||||
"name": "室内风机转速"
|
||||
|
||||
@@ -22,16 +22,16 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from midealocal.device import DeviceType
|
||||
from midealocal.devices.c3 import DeviceAttributes as C3Attributes
|
||||
from midealocal.devices.c3 import MideaC3Device
|
||||
from midealocal.devices.cd import DeviceAttributes as CDAttributes
|
||||
from midealocal.devices.cd import MideaCDDevice
|
||||
from midealocal.devices.e2 import DeviceAttributes as E2Attributes
|
||||
from midealocal.devices.e2 import MideaE2Device
|
||||
from midealocal.devices.e3 import MideaE3Device
|
||||
from midealocal.devices.e6 import DeviceAttributes as E6Attributes
|
||||
from midealocal.devices.e6 import MideaE6Device
|
||||
from midealan.device import DeviceType
|
||||
from midealan.devices.c3 import DeviceAttributes as C3Attributes
|
||||
from midealan.devices.c3 import MideaC3Device
|
||||
from midealan.devices.cd import DeviceAttributes as CDAttributes
|
||||
from midealan.devices.cd import MideaCDDevice
|
||||
from midealan.devices.e2 import DeviceAttributes as E2Attributes
|
||||
from midealan.devices.e2 import MideaE2Device
|
||||
from midealan.devices.e3 import MideaE3Device
|
||||
from midealan.devices.e6 import DeviceAttributes as E6Attributes
|
||||
from midealan.devices.e6 import MideaE6Device
|
||||
|
||||
from .const import DEVICES, DOMAIN
|
||||
from .midea_devices import MIDEA_DEVICES
|
||||
@@ -242,6 +242,16 @@ class MideaE3WaterHeater(MideaWaterHeater):
|
||||
"""Midea E3 Water Heater entity init."""
|
||||
super().__init__(device, entity_key)
|
||||
|
||||
@property
|
||||
def supported_features(self) -> WaterHeaterEntityFeature:
|
||||
"""Midea E3 Water Heater supported features."""
|
||||
# E3 implements turn_on/turn_off and reports on/off state, so advertise
|
||||
# ON_OFF to self-document that support (matches E2).
|
||||
return (
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE
|
||||
| WaterHeaterEntityFeature.ON_OFF
|
||||
)
|
||||
|
||||
@property
|
||||
def min_temp(self) -> float:
|
||||
"""Midea E3 Water Heater min temperature."""
|
||||
@@ -276,6 +286,16 @@ class MideaC3WaterHeater(MideaWaterHeater):
|
||||
"""Midea C3 Water Heater entity init."""
|
||||
super().__init__(device, entity_key)
|
||||
|
||||
@property
|
||||
def supported_features(self) -> WaterHeaterEntityFeature:
|
||||
"""Midea C3 Water Heater supported features."""
|
||||
# C3 implements turn_on/turn_off (dhw_power) and reports on/off state,
|
||||
# so advertise ON_OFF to self-document that support (matches E2).
|
||||
return (
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE
|
||||
| WaterHeaterEntityFeature.ON_OFF
|
||||
)
|
||||
|
||||
@property
|
||||
def current_operation(self) -> str:
|
||||
"""Midea C3 Water Heater current operation."""
|
||||
@@ -356,6 +376,16 @@ class MideaE6WaterHeater(MideaWaterHeater):
|
||||
self._use
|
||||
]
|
||||
|
||||
@property
|
||||
def supported_features(self) -> WaterHeaterEntityFeature:
|
||||
"""Midea E6 Water Heater supported features."""
|
||||
# E6 implements turn_on/turn_off and reports on/off state, so advertise
|
||||
# ON_OFF to self-document that support (matches E2).
|
||||
return (
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE
|
||||
| WaterHeaterEntityFeature.ON_OFF
|
||||
)
|
||||
|
||||
@property
|
||||
def current_operation(self) -> str:
|
||||
"""Midea E6 Water Heater current operation."""
|
||||
|
||||
Reference in New Issue
Block a user