Added Midea Dishwasher Integration

This commit is contained in:
2026-06-12 11:51:37 -04:00
parent 8843bf77a5
commit 89ac3bd24f
47 changed files with 13150 additions and 245 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

+1 -1
View File
@@ -1 +1 @@
{"pid": 71, "version": 1, "ha_version": "2026.6.2", "start_ts": 1781272512.3211384} {"pid": 71, "version": 1, "ha_version": "2026.6.2", "start_ts": 1781274194.4665778}
+97 -64
View File
@@ -1,7 +1,6 @@
- id: "1780072464609" - id: '1780072464609'
alias: "Camera: Send Motion Notifications" alias: 'Camera: Send Motion Notifications'
description: description: Sends a push notification to your primary mobile device when Nest cameras
Sends a push notification to your primary mobile device when Nest cameras
detect activity. detect activity.
triggers: triggers:
- entity_id: - entity_id:
@@ -18,8 +17,8 @@
message: Activity detected on one of your Nest security cameras. message: Activity detected on one of your Nest security cameras.
mode: parallel mode: parallel
max: 10 max: 10
- id: "1780125759253" - id: '1780125759253'
alias: "Tablet: Dim Screen at Night" alias: 'Tablet: Dim Screen at Night'
description: Drops tablet brightness to zero at night description: Drops tablet brightness to zero at night
triggers: triggers:
- at: 01:00:00 - at: 01:00:00
@@ -30,8 +29,8 @@
message: command_screen_brightness_level message: command_screen_brightness_level
data: data:
command: 0 command: 0
- id: "1780125803824" - id: '1780125803824'
alias: "Tablet: Restore Screen in Morning" alias: 'Tablet: Restore Screen in Morning'
description: Restores tablet brightness to normal in the morning description: Restores tablet brightness to normal in the morning
triggers: triggers:
- at: 07:00:00 - at: 07:00:00
@@ -42,24 +41,24 @@
message: command_screen_brightness_level message: command_screen_brightness_level
data: data:
command: 180 command: 180
- id: "1780343863793" - id: '1780343863793'
alias: Front Door Notification alias: Front Door Notification
description: "" description: ''
triggers: triggers:
- trigger: state - trigger: state
entity_id: entity_id:
- binary_sensor.front_door - binary_sensor.front_door
from: from:
- "off" - 'off'
to: to:
- "on" - 'on'
- trigger: state - trigger: state
entity_id: entity_id:
- binary_sensor.patio_door - binary_sensor.patio_door
from: from:
- "off" - 'off'
to: to:
- "on" - 'on'
conditions: [] conditions: []
actions: actions:
- target: - target:
@@ -100,10 +99,9 @@
y: -555 y: -555
graph_id: 7ad98446-4ce0-4b88-a400-430ac474a617 graph_id: 7ad98446-4ce0-4b88-a400-430ac474a617
graph_version: 1 graph_version: 1
- id: "1780441470361" - id: '1780441470361'
alias: Reload Hilo Integrations on Reboot alias: Reload Hilo Integrations on Reboot
description: description: Reloads problematic integrations 2 minutes after Home Assistant starts
Reloads problematic integrations 2 minutes after Home Assistant starts
up. up.
triggers: triggers:
- trigger: homeassistant - trigger: homeassistant
@@ -123,9 +121,9 @@
- baad176ac2f415f3642e2d075bb6a977 - baad176ac2f415f3642e2d075bb6a977
data: {} data: {}
mode: single mode: single
- id: "1780509027967" - id: '1780509027967'
alias: Notify When Ring Console Battery Low alias: Notify When Ring Console Battery Low
description: "" description: ''
triggers: triggers:
- trigger: numeric_state - trigger: numeric_state
entity_id: entity_id:
@@ -151,9 +149,9 @@
y: 45 y: 45
graph_id: 816af3b7-3eac-47c3-8da3-8797d1774c0c graph_id: 816af3b7-3eac-47c3-8da3-8797d1774c0c
graph_version: 1 graph_version: 1
- id: "1780516946807" - id: '1780516946807'
alias: Notify When Front Door Sensor Battery Low alias: Notify When Front Door Sensor Battery Low
description: "" description: ''
triggers: triggers:
- trigger: numeric_state - trigger: numeric_state
entity_id: entity_id:
@@ -179,10 +177,9 @@
y: 45 y: 45
graph_id: 816af3b7-3eac-47c3-8da3-8797d1774c0c graph_id: 816af3b7-3eac-47c3-8da3-8797d1774c0c
graph_version: 1 graph_version: 1
- id: "1780813227139" - id: '1780813227139'
alias: "Alert: Fridge or Freezer Power Outage" alias: 'Alert: Fridge or Freezer Power Outage'
description: description: Triggers an alert if the fridge or freezer smart plug power consumption
Triggers an alert if the fridge or freezer smart plug power consumption
drops to 0W for too long. drops to 0W for too long.
triggers: triggers:
- trigger: numeric_state - trigger: numeric_state
@@ -211,8 +208,7 @@
then: then:
- data: - data:
title: "\U0001F6A8 URGENT: Freezer Power Failure" title: "\U0001F6A8 URGENT: Freezer Power Failure"
message: message: The Freezer smart plug has drawn 0W of power for over 40 minutes!
The Freezer smart plug has drawn 0W of power for over 40 minutes!
Check the physical appliance immediately. Check the physical appliance immediately.
action: notify.notify action: notify.notify
else: else:
@@ -222,8 +218,7 @@
then: then:
- data: - data:
title: "\U0001F6A8 URGENT: Fridge Power Failure" title: "\U0001F6A8 URGENT: Fridge Power Failure"
message: message: The Fridge smart plug has drawn 0W of power for over 20 minutes!
The Fridge smart plug has drawn 0W of power for over 20 minutes!
Check the physical appliance immediately. Check the physical appliance immediately.
action: notify.notify action: notify.notify
else: [] else: []
@@ -253,10 +248,9 @@
y: 102 y: 102
graph_id: 5948b900-b45d-4c2e-ae73-c880acc3a1bf graph_id: 5948b900-b45d-4c2e-ae73-c880acc3a1bf
graph_version: 1 graph_version: 1
- id: "1780989340205" - id: '1780989340205'
alias: "Kitchen: Coffee Maker Active Notification" alias: 'Kitchen: Coffee Maker Active Notification'
description: description: Sends a mobile application push notification when the coffee maker
Sends a mobile application push notification when the coffee maker
power exceeds 1000W for 5 seconds. power exceeds 1000W for 5 seconds.
triggers: triggers:
- entity_id: sensor.coffee_maker_power - entity_id: sensor.coffee_maker_power
@@ -283,60 +277,56 @@
seconds: 0 seconds: 0
milliseconds: 0 milliseconds: 0
mode: single mode: single
- id: "1781024962776" - id: '1781024962776'
alias: "Office: Turn On Lights on Motion then Door Open" alias: 'Office: Turn On Lights on Motion then Door Open'
description: description: Triggers when office door opens, but only if living room motion happened
Triggers when office door opens, but only if living room motion happened
in the last 30 seconds and light is off. in the last 30 seconds and light is off.
triggers: triggers:
- trigger: state - trigger: state
entity_id: binary_sensor.office_door_contact entity_id: binary_sensor.office_door_contact
to: "on" to: 'on'
conditions: conditions:
- condition: time - condition: time
after: "10:00:00" after: '10:00:00'
before: "21:00:00" before: '21:00:00'
- condition: state - condition: state
entity_id: light.playroom_light entity_id: light.playroom_light
state: "off" state: 'off'
- condition: template - condition: template
value_template: value_template: '{{ (now() - states.binary_sensor.basement.last_changed).total_seconds()
"{{ (now() - states.binary_sensor.basement.last_changed).total_seconds() < 30 }}'
< 30 }}"
actions: actions:
- action: light.turn_on - action: light.turn_on
target: target:
entity_id: light.playroom_light entity_id: light.playroom_light
data: {} data: {}
mode: single mode: single
- id: "1781034724187" - id: '1781034724187'
alias: "Office: Turn Off Lights on Exit" alias: 'Office: Turn Off Lights on Exit'
description: description: Turns off office lights 2 minutes after the door closes, but only if living
Turns off office lights 2 minutes after the door closes, but only if living
room motion was detected after the door shut. room motion was detected after the door shut.
triggers: triggers:
- trigger: state - trigger: state
entity_id: binary_sensor.office_door_contact entity_id: binary_sensor.office_door_contact
to: "off" to: 'off'
for: for:
minutes: 2 minutes: 2
conditions: conditions:
- condition: state - condition: state
entity_id: light.playroom_light entity_id: light.playroom_light
state: "on" state: 'on'
- condition: template - condition: template
value_template: value_template: '{{ states.binary_sensor.basement.last_changed > states.binary_sensor.office_door_contact.last_changed
"{{ states.binary_sensor.basement.last_changed > states.binary_sensor.office_door_contact.last_changed }}'
}}"
actions: actions:
- action: light.turn_off - action: light.turn_off
target: target:
entity_id: light.playroom_light entity_id: light.playroom_light
data: {} data: {}
mode: single mode: single
- id: "1781102033733" - id: '1781102033733'
alias: Water Leak Detected alias: Water Leak Detected
description: "" description: ''
triggers: triggers:
- type: moist - type: moist
device_id: 59bc102f385d7a3524c8f21a5c53592b device_id: 59bc102f385d7a3524c8f21a5c53592b
@@ -358,29 +348,72 @@
title: "\U0001F6A8 URGENT: Water Leak Detected" title: "\U0001F6A8 URGENT: Water Leak Detected"
message: Water Leak Detected in Basement. Check Water Heater for Leaks. message: Water Leak Detected in Basement. Check Water Heater for Leaks.
mode: single mode: single
- id: "1781112110968" - id: '1781112110968'
alias: "Freezer Safety: Alert if Plug Turns Off" alias: 'Freezer Safety: Alert if Plug Turns Off'
description: description: Triggers an immediate notification if the freezer smart plug is turned
Triggers an immediate notification if the freezer smart plug is turned
off. off.
triggers: triggers:
- trigger: state - trigger: state
entity_id: switch.freezer entity_id: switch.freezer
to: "off" to: 'off'
for: for:
minutes: 1 minutes: 1
actions: actions:
- action: notify.notify - action: notify.notify
data: data:
title: "\U0001F6A8 FREEZER PLUG TRIPPED!" title: "\U0001F6A8 FREEZER PLUG TRIPPED!"
message: message: The freezer smart plug turned off. Check it immediately to prevent
The freezer smart plug turned off. Check it immediately to prevent
food spoilage. food spoilage.
- action: notify.persistent_notification - action: notify.persistent_notification
metadata: {} metadata: {}
data: data:
title: "\U0001F6A8 FREEZER PLUG TRIPPED!" title: "\U0001F6A8 FREEZER PLUG TRIPPED!"
message: message: The freezer smart plug turned off. Check it immediately to prevent
The freezer smart plug turned off. Check it immediately to prevent
food spoilage. food spoilage.
mode: single mode: single
- id: '1781275767970'
alias: 'Dishwasher: Cycle Finished Notification'
description: Alerts when the dishwasher finishes washing
triggers:
- entity_id: sensor.150633095332665_progress
from: Running
to: Idle
trigger: state
actions:
- action: notify.notify
data:
title: Dishwasher Done
message: The dishwasher cycle has completed successfully!
mode: single
- id: '1781275801564'
alias: 'Dishwasher: Rinse Aid Low Warning'
description: Alerts when rinse aid needs a refill
triggers:
- entity_id: binary_sensor.150633095332665_rinse_aid
to: 'on'
trigger: state
actions:
- action: notify.notify
data:
title: Dishwasher Maintenance
message: The dishwasher is low on rinse aid. Please refill it before the next
cycle!
mode: single
- id: '1781278655292'
alias: 'Dishwasher: Dishwasher Started Notification'
description: Alerts when the dishwasher starts washing
triggers:
- entity_id:
- sensor.150633095332665_progress
from:
- idle
to:
- running
trigger: state
conditions: []
actions:
- action: notify.notify
data:
title: Dishwasher Started
message: The dishwasher cycle has started!
mode: single
+346
View File
@@ -0,0 +1,346 @@
"""Init for Midea LAN.
integration load process:
1. component setup: `async_setup`
1.1 use `hass.services.async_register` to register service
2. config entry setup: `async_setup_entry`
2.1 forward the Config Entry to the platform `async_forward_entry_setups`
2.2 register listener `update_listener`
3. unloading a config entry: `async_unload_entry`
"""
import logging
from typing import Any, cast
import homeassistant.helpers.config_validation as cv
import homeassistant.helpers.device_registry as dr
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_CUSTOMIZE,
CONF_DEVICE_ID,
CONF_IP_ADDRESS,
CONF_NAME,
CONF_PORT,
CONF_PROTOCOL,
CONF_TOKEN,
CONF_TYPE,
MAJOR_VERSION,
MINOR_VERSION,
)
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 .const import (
ALL_PLATFORM,
CONF_ACCOUNT,
CONF_KEY,
CONF_MODEL,
CONF_REFRESH_INTERVAL,
CONF_SUBTYPE,
DEVICES,
DOMAIN,
EXTRA_SWITCH,
)
from .midea_devices import MIDEA_DEVICES
_LOGGER = logging.getLogger(__name__)
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
"""
# 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)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: ARG001
"""Set up midea_lan component when load this integration.
Returns
-------
True if entry is configured.
"""
hass.data.setdefault(DOMAIN, {})
attributes = []
for device_entities in MIDEA_DEVICES.values():
for attribute_name, attribute in cast(
"dict",
device_entities["entities"],
).items():
if (
attribute.get("type") in EXTRA_SWITCH
and attribute_name.value not in attributes
):
attributes.append(attribute_name.value)
def service_set_attribute(service: Any) -> None: # noqa: ANN401
"""Set service attribute func."""
device_id: int = service.data["device_id"]
attr = service.data["attribute"]
value = service.data["value"]
dev: MideaDevice = hass.data[DOMAIN][DEVICES].get(device_id)
if dev:
if attr == "fan_speed" and value == "auto":
value = 102
item = None
if dev_ := MIDEA_DEVICES.get(dev.device_type):
item = cast("dict", dev_["entities"]).get(attr)
if (
item
and (item.get("type") in EXTRA_SWITCH)
or (
dev.device_type == DeviceType.AC
and attr == "fan_speed"
and value in range(103)
)
):
dev.set_attribute(attr=attr, value=value)
else:
_LOGGER.error(
"Appliance [%s] has no attribute %s or value is invalid",
device_id,
attr,
)
def service_send_command(service: Any) -> None: # noqa: ANN401
"""Send command to service func."""
device_id = service.data.get("device_id")
cmd_type = service.data.get("cmd_type")
cmd_body = service.data.get("cmd_body")
try:
cmd_body = bytearray.fromhex(cmd_body)
except ValueError:
_LOGGER.exception(
"Appliance [%s] invalid cmd_body, a hexadecimal string required",
device_id,
)
return
dev = hass.data[DOMAIN][DEVICES].get(device_id)
if dev:
dev.send_command(cmd_type, cmd_body)
# register service func calls:
# `service_set_attribute`, service.yaml key: `set_attribute`
hass.services.async_register(
DOMAIN,
"set_attribute",
service_set_attribute,
schema=vol.Schema(
{
vol.Required("device_id"): vol.Coerce(int),
vol.Required("attribute"): vol.In(attributes),
vol.Required("value"): vol.Any(int, cv.boolean, str),
},
),
)
# register service func calls:
# `service_send_command`, service.yaml key: `send_command`
hass.services.async_register(
DOMAIN,
"send_command",
service_send_command,
schema=vol.Schema(
{
vol.Required("device_id"): vol.Coerce(int),
vol.Required("cmd_type"): vol.In([2, 3]),
vol.Required("cmd_body"): str,
},
),
)
return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Set up platform for current integration.
Returns
-------
True if entry is configured.
"""
device_type = config_entry.data.get(CONF_TYPE)
if device_type == CONF_ACCOUNT:
return True
name = config_entry.data.get(CONF_NAME)
device_id: int = config_entry.data[CONF_DEVICE_ID]
if name is None:
name = f"{device_id}"
if device_type is None:
device_type = 0xAC
token: str = config_entry.data.get(CONF_TOKEN) or ""
key: str = config_entry.data.get(CONF_KEY) or ""
ip_address = config_entry.options.get(CONF_IP_ADDRESS, None)
if ip_address is None:
ip_address = config_entry.data.get(CONF_IP_ADDRESS)
refresh_interval = config_entry.options.get(CONF_REFRESH_INTERVAL)
port: int = config_entry.data[CONF_PORT]
model: str = config_entry.data[CONF_MODEL]
subtype = config_entry.data.get(CONF_SUBTYPE, 0)
protocol: ProtocolVersion = ProtocolVersion(config_entry.data[CONF_PROTOCOL])
customize: str = config_entry.options.get(CONF_CUSTOMIZE, "")
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`
# hass core version >= 2024.3
if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 3):
device = await hass.async_add_import_executor_job(
device_selector,
name,
device_id,
device_type,
ip_address,
port,
token,
key,
protocol,
model,
subtype,
customize,
)
# hass core version < 2024.3
else:
device = device_selector(
name=name,
device_id=device_id,
device_type=device_type,
ip_address=ip_address,
port=port,
token=token,
key=key,
device_protocol=protocol,
model=model,
subtype=subtype,
customize=customize,
)
if device:
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)
# Listener `update_listener` is
# attached when the entry is loaded
# and detached when it's unloaded
config_entry.async_on_unload(config_entry.add_update_listener(update_listener))
return True
return False
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Clean up entities, unsubscribe event listener and close all connections.
Returns
-------
True if entry is unloaded.
"""
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
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Migrate old entry.
Returns
-------
True if entry is migrated.
"""
# 1 -> 2: convert device identifiers from int to str
if config_entry.version == 1:
_LOGGER.debug("Migrating configuration from version 1")
if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 3):
hass.config_entries.async_update_entry(config_entry, version=2)
else:
config_entry.version = 2
hass.config_entries.async_update_entry(config_entry)
# Migrate device.
await _async_migrate_device_identifiers(hass, config_entry)
_LOGGER.debug("Migration to configuration version 2 successful")
return True
async def _async_migrate_device_identifiers(
hass: HomeAssistant,
config_entry: ConfigEntry,
) -> None:
"""Migrate the device identifiers to the new format."""
device_registry = dr.async_get(hass)
device_entry_found = False
for device_entry in dr.async_entries_for_config_entry(
device_registry,
config_entry.entry_id,
):
for identifier in device_entry.identifiers:
# Device found in registry. Update identifiers.
new_identifiers = {
(
DOMAIN,
str(identifier[1]),
),
}
_LOGGER.debug(
"Migrating device identifiers from %s to %s",
device_entry.identifiers,
new_identifiers,
)
device_registry.async_update_device(
device_id=device_entry.id,
new_identifiers=new_identifiers,
)
# Device entry found. Leave inner for loop.
device_entry_found = True
break
# Leave outer for loop if device entry is already found.
if device_entry_found:
break
@@ -0,0 +1,50 @@
"""Binary sensor for Midea Lan."""
from typing import cast
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE_ID, CONF_SENSORS, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up sensors for device."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_sensors = config_entry.options.get(CONF_SENSORS, [])
binary_sensors = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.BINARY_SENSOR and entity_key in extra_sensors:
sensor = MideaBinarySensor(device, entity_key)
binary_sensors.append(sensor)
async_add_entities(binary_sensors)
class MideaBinarySensor(MideaEntity, BinarySensorEntity):
"""Represent a Midea binary sensor."""
@property
def device_class(self) -> BinarySensorDeviceClass | None:
"""Return device class."""
return cast("BinarySensorDeviceClass", self._config.get("device_class"))
@property
def is_on(self) -> bool:
"""Return true if sensor state is on."""
return cast("bool", self._device.get_attribute(self._entity_key))
+707
View File
@@ -0,0 +1,707 @@
"""Midea Climate entries."""
import logging
from typing import Any, ClassVar, TypeAlias, cast
from homeassistant.components.climate import (
ATTR_HVAC_MODE,
FAN_AUTO,
FAN_HIGH,
FAN_LOW,
FAN_MEDIUM,
PRESET_AWAY,
PRESET_BOOST,
PRESET_COMFORT,
PRESET_ECO,
PRESET_NONE,
PRESET_SLEEP,
SWING_BOTH,
SWING_HORIZONTAL,
SWING_OFF,
SWING_ON,
SWING_VERTICAL,
ClimateEntity,
ClimateEntityFeature,
HVACMode,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_TEMPERATURE,
CONF_DEVICE_ID,
CONF_SWITCHES,
MAJOR_VERSION,
MINOR_VERSION,
PRECISION_HALVES,
PRECISION_WHOLE,
Platform,
UnitOfTemperature,
)
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 .const import DEVICES, DOMAIN, FanSpeed
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
_LOGGER = logging.getLogger(__name__)
TEMPERATURE_MAX = 30
TEMPERATURE_MIN = 16
FAN_SILENT = "silent"
FAN_FULL_SPEED = "full"
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up climate entries."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
devs: list[
MideaACClimate
| MideaCCClimate
| MideaCFClimate
| MideaC3Climate
| MideaFBClimate
] = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.CLIMATE and (
config.get("default") or entity_key in extra_switches
):
if device.device_type == DeviceType.AC:
# add config_entry args to fix indoor_humidity error bug
devs.append(MideaACClimate(device, entity_key, config_entry))
elif device.device_type == DeviceType.CC:
devs.append(MideaCCClimate(device, entity_key))
elif device.device_type == DeviceType.CF:
devs.append(MideaCFClimate(device, entity_key))
elif device.device_type == DeviceType.C3:
devs.append(MideaC3Climate(device, entity_key, config["zone"]))
elif device.device_type == DeviceType.FB:
devs.append(MideaFBClimate(device, entity_key))
async_add_entities(devs)
MideaClimateDevice: TypeAlias = (
MideaACDevice | MideaCCDevice | MideaCFDevice | MideaC3Device | MideaFBDevice
)
class MideaClimate(MideaEntity, ClimateEntity):
"""Midea Climate Entries Base Class."""
# https://developers.home-assistant.io/blog/2024/01/24/climate-climateentityfeatures-expanded
_enable_turn_on_off_backwards_compatibility: bool = (
False # maybe remove after 2025.1
)
_device: MideaClimateDevice
_attr_max_temp: float = TEMPERATURE_MAX
_attr_min_temp: float = TEMPERATURE_MIN
_attr_target_temperature_high: float | None = TEMPERATURE_MAX
_attr_target_temperature_low: float | None = TEMPERATURE_MIN
_attr_temperature_unit: str = UnitOfTemperature.CELSIUS
def __init__(self, device: MideaClimateDevice, entity_key: str) -> None:
"""Midea Climate entity init."""
super().__init__(device, entity_key)
@property
def supported_features(self) -> ClimateEntityFeature:
"""Midea Climate supported features."""
features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.SWING_MODE
)
if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 2):
features |= ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
return features
@property
def hvac_mode(self) -> HVACMode:
"""Midea Climate hvac mode."""
if self._device.get_attribute("power"):
mode = cast("int", self._device.get_attribute("mode"))
return self.hvac_modes[mode]
return HVACMode.OFF
@property
def target_temperature(self) -> float:
"""Midea Climate target temperature."""
return cast("float", self._device.get_attribute("target_temperature"))
@property
def current_temperature(self) -> float | None:
"""Midea Climate current temperature."""
return cast("float | None", self._device.get_attribute("indoor_temperature"))
@property
def preset_mode(self) -> str:
"""Midea Climate preset mode."""
if self._device.get_attribute("comfort_mode"):
mode = PRESET_COMFORT
elif self._device.get_attribute("eco_mode"):
mode = PRESET_ECO
elif self._device.get_attribute("boost_mode"):
mode = PRESET_BOOST
elif self._device.get_attribute("sleep_mode"):
mode = PRESET_SLEEP
elif self._device.get_attribute("frost_protect"):
mode = PRESET_AWAY
else:
mode = PRESET_NONE
return str(mode)
@property
def extra_state_attributes(self) -> dict:
"""Midea Climate extra state attributes."""
return cast("dict", self._device.attributes)
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea Climate turn on."""
self._device.set_attribute(attr="power", value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea Climate turn off."""
self._device.set_attribute(attr="power", value=False)
def set_temperature(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea Climate set temperature."""
if ATTR_TEMPERATURE not in kwargs:
return
temperature = float(int((float(kwargs[ATTR_TEMPERATURE]) * 2) + 0.5)) / 2
hvac_mode = kwargs.get(ATTR_HVAC_MODE)
if hvac_mode == HVACMode.OFF:
self.turn_off()
else:
try:
mode = self.hvac_modes.index(hvac_mode.lower()) if hvac_mode else None
self._device.set_target_temperature(
target_temperature=temperature,
mode=mode,
zone=None,
)
except ValueError:
_LOGGER.exception("Error setting temperature with: %s", kwargs)
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Midea Climate set hvac mode."""
if hvac_mode == HVACMode.OFF:
self.turn_off()
else:
self._device.set_attribute(
attr="mode",
value=self.hvac_modes.index(hvac_mode),
)
def set_preset_mode(self, preset_mode: str) -> None:
"""Midea Climate set preset mode."""
old_mode = self.preset_mode
preset_mode = preset_mode.lower()
if preset_mode == PRESET_AWAY:
self._device.set_attribute(attr="frost_protect", value=True)
elif preset_mode == PRESET_COMFORT:
self._device.set_attribute(attr="comfort_mode", value=True)
elif preset_mode == PRESET_SLEEP:
self._device.set_attribute(attr="sleep_mode", value=True)
elif preset_mode == PRESET_ECO:
self._device.set_attribute(attr="eco_mode", value=True)
elif preset_mode == PRESET_BOOST:
self._device.set_attribute(attr="boost_mode", value=True)
elif old_mode == PRESET_AWAY:
self._device.set_attribute(attr="frost_protect", value=False)
elif old_mode == PRESET_COMFORT:
self._device.set_attribute(attr="comfort_mode", value=False)
elif old_mode == PRESET_SLEEP:
self._device.set_attribute(attr="sleep_mode", value=False)
elif old_mode == PRESET_ECO:
self._device.set_attribute(attr="eco_mode", value=False)
elif old_mode == PRESET_BOOST:
self._device.set_attribute(attr="boost_mode", value=False)
def update_state(self, status: Any) -> None: # noqa: ANN401, ARG002
"""Midea Climate update state."""
if not self.hass:
_LOGGER.warning(
"Climate update_state skipped for %s [%s]: HASS is None",
self.name,
type(self),
)
return
self.schedule_update_ha_state()
class MideaACClimate(MideaClimate):
"""Midea AC Climate Entries."""
_device: MideaACDevice
def __init__(
self,
device: MideaACDevice,
entity_key: str,
config_entry: ConfigEntry,
) -> None:
"""Midea AC Climate entity init."""
super().__init__(device, entity_key)
self._attr_hvac_modes = [
HVACMode.OFF,
HVACMode.AUTO,
HVACMode.COOL,
HVACMode.DRY,
HVACMode.HEAT,
HVACMode.FAN_ONLY,
]
self._fan_speeds: dict[str, int] = {
FAN_SILENT: 20,
FAN_LOW: 40,
FAN_MEDIUM: 60,
FAN_HIGH: 80,
FAN_FULL_SPEED: 100,
FAN_AUTO: 102,
}
self._attr_swing_modes: list[str] = [
SWING_OFF,
SWING_VERTICAL,
SWING_HORIZONTAL,
SWING_BOTH,
]
self._attr_preset_modes = [
PRESET_NONE,
PRESET_COMFORT,
PRESET_ECO,
PRESET_BOOST,
PRESET_SLEEP,
PRESET_AWAY,
]
self._attr_fan_modes = list(self._fan_speeds.keys())
# fix error humidity value, disable indoor_humidity
# add config_entry args to fix indoor_humidity error bug
self._indoor_humidity_enabled = (
"sensors" in config_entry.options
and "indoor_humidity" in config_entry.options["sensors"]
)
@property
def fan_mode(self) -> str:
"""Midea AC Climate fan mode."""
fan_speed = cast("int", self._device.get_attribute(ACAttributes.fan_speed))
if fan_speed > FanSpeed.AUTO:
return str(FAN_AUTO)
if fan_speed > FanSpeed.FULL_SPEED:
return str(FAN_FULL_SPEED)
if fan_speed > FanSpeed.HIGH:
return str(FAN_HIGH)
if fan_speed > FanSpeed.MEDIUM:
return str(FAN_MEDIUM)
if fan_speed > FanSpeed.LOW:
return str(FAN_LOW)
return str(FAN_SILENT)
@property
def target_temperature_step(self) -> float:
"""Midea AC Climate target temperature step."""
return float(
PRECISION_WHOLE if self._device.temperature_step == 1 else PRECISION_HALVES,
)
@property
def swing_mode(self) -> str:
"""Midea AC Climate swing mode."""
swing_mode = (
1 if self._device.get_attribute(ACAttributes.swing_vertical) else 0
) + (2 if self._device.get_attribute(ACAttributes.swing_horizontal) else 0)
return self._attr_swing_modes[swing_mode]
@property
def current_humidity(self) -> float | None:
"""Return the current indoor humidity, or None if unavailable."""
# fix error humidity, disable indoor_humidity in web UI
# https://github.com/wuwentao/midea_ac_lan/pull/641
if not self._indoor_humidity_enabled:
return None
raw = self._device.get_attribute("indoor_humidity")
if isinstance(raw, (int, float)) and raw not in {0, 0xFF}:
return float(raw)
# indoor_humidity is 0 or 255, return None
# https://github.com/wuwentao/midea_ac_lan/pull/614
return None
@property
def outdoor_temperature(self) -> float:
"""Midea AC Climate outdoor temperature."""
return cast(
"float",
self._device.get_attribute(ACAttributes.outdoor_temperature),
)
def set_fan_mode(self, fan_mode: str) -> None:
"""Midea AC Climate set fan mode."""
fan_speed = self._fan_speeds.get(fan_mode)
if fan_speed:
self._device.set_attribute(attr=ACAttributes.fan_speed, value=fan_speed)
def set_swing_mode(self, swing_mode: str) -> None:
"""Midea AC Climate set swing mode."""
swing = self._attr_swing_modes.index(swing_mode)
swing_vertical = swing & 1 > 0
swing_horizontal = swing & 2 > 0
self._device.set_swing(
swing_vertical=swing_vertical,
swing_horizontal=swing_horizontal,
)
class MideaCCClimate(MideaClimate):
"""Midea CC Climate Entries Base Class."""
_device: MideaCCDevice
def __init__(self, device: MideaCCDevice, entity_key: str) -> None:
"""Midea CC Climate entity init."""
super().__init__(device, entity_key)
self._attr_hvac_modes = [
HVACMode.OFF,
HVACMode.FAN_ONLY,
HVACMode.DRY,
HVACMode.HEAT,
HVACMode.COOL,
HVACMode.AUTO,
]
self._attr_swing_modes = [SWING_OFF, SWING_ON]
self._attr_preset_modes = [PRESET_NONE, PRESET_SLEEP, PRESET_ECO]
@property
def fan_modes(self) -> list[str] | None:
"""Midea CC Climate fan modes."""
return cast("list", self._device.fan_modes)
@property
def fan_mode(self) -> str:
"""Midea CC Climate fan mode."""
return cast("str", self._device.get_attribute(CCAttributes.fan_speed))
@property
def target_temperature_step(self) -> float:
"""Midea CC Climate target temperature step."""
return cast(
"float",
self._device.get_attribute(CCAttributes.temperature_precision),
)
@property
def swing_mode(self) -> str:
"""Midea CC Climate swing mode."""
return str(
SWING_ON if self._device.get_attribute(CCAttributes.swing) else SWING_OFF,
)
def set_fan_mode(self, fan_mode: str) -> None:
"""Midea CC Climate set fan mode."""
self._device.set_attribute(attr=CCAttributes.fan_speed, value=fan_mode)
def set_swing_mode(self, swing_mode: str) -> None:
"""Midea CC Climate set swing mode."""
self._device.set_attribute(
attr=CCAttributes.swing,
value=swing_mode == SWING_ON,
)
class MideaCFClimate(MideaClimate):
"""Midea CF Climate Entries."""
_device: MideaCFDevice
_attr_target_temperature_step: float | None = PRECISION_WHOLE
def __init__(self, device: MideaCFDevice, entity_key: str) -> None:
"""Midea CF Climate entity init."""
super().__init__(device, entity_key)
self._attr_hvac_modes = [
HVACMode.OFF,
HVACMode.AUTO,
HVACMode.COOL,
HVACMode.HEAT,
]
@property
def supported_features(self) -> ClimateEntityFeature:
"""Midea CF Climate supported features."""
features = ClimateEntityFeature.TARGET_TEMPERATURE
if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 2):
features |= ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
return features
@property
def min_temp(self) -> float:
"""Midea CF Climate min temperature."""
return cast("float", self._device.get_attribute(CFAttributes.min_temperature))
@property
def max_temp(self) -> float:
"""Midea CF Climate max temperature."""
return cast("float", self._device.get_attribute(CFAttributes.max_temperature))
@property
def target_temperature_low(self) -> float:
"""Midea CF Climate target temperature."""
return cast("float", self._device.get_attribute(CFAttributes.min_temperature))
@property
def target_temperature_high(self) -> float:
"""Midea CF Climate target temperature high."""
return cast("float", self._device.get_attribute(CFAttributes.max_temperature))
@property
def current_temperature(self) -> float:
"""Midea CF Climate current temperature."""
return cast(
"float",
self._device.get_attribute(CFAttributes.current_temperature),
)
class MideaC3Climate(MideaClimate):
"""Midea C3 Climate Entries."""
_device: MideaC3Device
_powers: ClassVar[list[C3Attributes]] = [
C3Attributes.zone1_power,
C3Attributes.zone2_power,
]
def __init__(self, device: MideaC3Device, entity_key: str, zone: int) -> None:
"""Midea C3 Climate entity init."""
super().__init__(device, entity_key)
self._zone = zone
self._attr_hvac_modes = [
HVACMode.OFF,
HVACMode.AUTO,
HVACMode.COOL,
HVACMode.HEAT,
]
self._power_attr = MideaC3Climate._powers[zone]
def _temperature(self, minimum: bool) -> list[str]:
"""Midea C3 Climate temperature.
Returns
-------
List of temperatures
"""
# fmt: off
value = (C3Attributes.temperature_min
if minimum
else C3Attributes.temperature_max)
# fmt: on
return cast("list[str]", self._device.get_attribute(value))
@property
def supported_features(self) -> ClimateEntityFeature:
"""Midea C3 Climate supported features."""
features = ClimateEntityFeature.TARGET_TEMPERATURE
if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 2):
features |= ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
return features
@property
def target_temperature_step(self) -> float:
"""Midea C3 Climate target temperature step."""
zone_temp_type = cast(
"list[str]",
self._device.get_attribute(C3Attributes.zone_temp_type),
)
return float(
PRECISION_WHOLE if zone_temp_type[self._zone] else PRECISION_HALVES,
)
@property
def min_temp(self) -> float:
"""Midea C3 Climate min temperature."""
return cast(
"float",
self._temperature(True)[self._zone],
)
@property
def max_temp(self) -> float:
"""Midea C3 Climate max temperature."""
return cast(
"float",
self._temperature(False)[self._zone],
)
@property
def target_temperature_low(self) -> float:
"""Midea C3 Climate target temperature low."""
return cast(
"float",
self._temperature(True)[self._zone],
)
@property
def target_temperature_high(self) -> float:
"""Midea C3 Climate target temperature high."""
return cast(
"float",
self._temperature(False)[self._zone],
)
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea C3 Climate turn on."""
self._device.set_attribute(attr=self._power_attr, value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea C3 Climate turn off."""
self._device.set_attribute(attr=self._power_attr, value=False)
@property
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):
return self.hvac_modes[mode]
return HVACMode.OFF
@property
def target_temperature(self) -> float:
"""Midea C3 Climate target temperature."""
target_temperature = cast(
"list[str]",
self._device.get_attribute(C3Attributes.target_temperature),
)
return cast(
"float",
target_temperature[self._zone],
)
@property
def current_temperature(self) -> float | None:
"""Midea C3 Climate current temperature."""
return None
def set_temperature(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea C3 Climate set temperature."""
if ATTR_TEMPERATURE not in kwargs:
return
temperature = float(int((float(kwargs[ATTR_TEMPERATURE]) * 2) + 0.5)) / 2
hvac_mode = kwargs.get(ATTR_HVAC_MODE)
if hvac_mode == HVACMode.OFF:
self.turn_off()
else:
try:
mode = self.hvac_modes.index(hvac_mode.lower()) if hvac_mode else None
self._device.set_target_temperature(
target_temperature=temperature,
mode=mode,
zone=self._zone,
)
except ValueError:
_LOGGER.exception("Error setting temperature with: %s", kwargs)
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Midea C3 Climate set hvac mode."""
if hvac_mode == HVACMode.OFF:
self.turn_off()
else:
self._device.set_mode(self._zone, self.hvac_modes.index(hvac_mode))
class MideaFBClimate(MideaClimate):
"""Midea FB Climate Entries."""
_device: MideaFBDevice
_attr_max_temp: float = 35
_attr_min_temp: float = 5
_attr_target_temperature_high: float | None = 35
_attr_target_temperature_low: float | None = 5
_attr_target_temperature_step: float | None = PRECISION_WHOLE
def __init__(self, device: MideaFBDevice, entity_key: str) -> None:
"""Midea FB Climate entity init."""
super().__init__(device, entity_key)
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT]
self._attr_preset_modes: list[str] = self._device.modes
@property
def supported_features(self) -> ClimateEntityFeature:
"""Midea FB Climate supported features."""
features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
)
if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 2):
features |= ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
return features
@property
def preset_mode(self) -> str:
"""Midea FB Climate preset mode."""
return cast("str", self._device.get_attribute(attr=FBAttributes.mode))
@property
def hvac_mode(self) -> HVACMode:
"""Midea FB Climate hvac mode."""
return (
HVACMode.HEAT
if self._device.get_attribute(attr=FBAttributes.power)
else HVACMode.OFF
)
@property
def current_temperature(self) -> float:
"""Midea FB Climate current temperature."""
return cast(
"float",
self._device.get_attribute(FBAttributes.current_temperature),
)
def set_temperature(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea FB Climate set temperature."""
if ATTR_TEMPERATURE not in kwargs:
return
temperature = float(int((float(kwargs[ATTR_TEMPERATURE]) * 2) + 0.5)) / 2
hvac_mode = kwargs.get(ATTR_HVAC_MODE)
if hvac_mode == HVACMode.OFF:
self.turn_off()
else:
self._device.set_target_temperature(
target_temperature=temperature,
mode=None,
)
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Midea FB Climate set hvac mode."""
if hvac_mode == HVACMode.OFF:
self.turn_off()
else:
self.turn_on()
def set_preset_mode(self, preset_mode: str) -> None:
"""Midea FB Climate set preset mode."""
self._device.set_attribute(attr=FBAttributes.mode, value=preset_mode)
@@ -0,0 +1,998 @@
"""Config flow for Midea LAN.
Setup current integration and add device entry via the web UI
enable by adding `config_flow: true` in `manifest.json`
`MideaLanConfigFlow`: add device entry
`MideaLanOptionsFlowHandler`: update the options of a config entry
job process:
1. run `async_step_user` when select `Add Device` from web UI
2. default auto discovery action run `async_step_discovery`
3. device available, run `async_step_auto` to show available device list in web UI
3.1 check local device json with `_load_device_config`
3.1.1 device exist with `_check_storage_device`,
send json data to `async_step_manually`
3.1.2 device NOT exist, get device data from cloud,
send to `async_step_manually`
- check login with `async_step_login`
4. add selected device detail with `async_step_manually`
5. run `_save_device_config` and `async_create_entry`
"""
import logging
from pathlib import Path
from typing import TYPE_CHECKING, Any, cast
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
from homeassistant.const import (
CONF_CUSTOMIZE,
CONF_DEVICE,
CONF_DEVICE_ID,
CONF_IP_ADDRESS,
CONF_NAME,
CONF_PASSWORD,
CONF_PORT,
CONF_PROTOCOL,
CONF_SENSORS,
CONF_SWITCHES,
CONF_TOKEN,
CONF_TYPE,
MAJOR_VERSION,
MINOR_VERSION,
)
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 (
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
if TYPE_CHECKING:
from aiohttp import ClientSession
if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 4):
from homeassistant.config_entries import ConfigFlowResult # pylint: disable=E0611
else:
from homeassistant.data_entry_flow import ( # type: ignore[assignment]
FlowResult as ConfigFlowResult,
)
from .const import (
CONF_ACCOUNT,
CONF_KEY,
CONF_MODEL,
CONF_REFRESH_INTERVAL,
CONF_SERVER,
CONF_SUBTYPE,
DOMAIN,
EXTRA_CONTROL,
EXTRA_SENSOR,
)
from .midea_devices import MIDEA_DEVICES
_LOGGER = logging.getLogger(__name__)
ADD_WAY = {
"discovery": "Discover automatically",
"manually": "Configure manually",
"list": "List all appliances only",
"cache": "Remove login cache",
}
# Select DEFAULT_CLOUD from the list of supported cloud
DEFAULT_CLOUD: str = list(SUPPORTED_CLOUDS)[3]
STORAGE_PATH = f".storage/{DOMAIN}"
SKIP_LOGIN = "Skip Login (input any user/password)"
class MideaLanConfigFlow(ConfigFlow, domain=DOMAIN): # type: ignore[call-arg]
"""Define current integration setup steps.
Use ConfigFlow handle to support config entries
ConfigFlow will manage the creation of entries from user input, discovery
"""
VERSION = 2
MINOR_VERSION = 1
def __init__(self) -> None:
"""MideaLanConfigFlow class."""
self.available_device: dict = {}
self.devices: dict = {}
self.found_device: dict[str, Any] = {}
self.supports: dict = {}
self.unsorted: dict[int, Any] = {}
self.account: dict = {}
self.cloud: MideaCloud | None = None
self.session: ClientSession | None = None
for device_type, device_info in MIDEA_DEVICES.items():
self.unsorted[device_type] = device_info["name"]
sorted_device_names = sorted(self.unsorted.items(), key=lambda x: x[1])
for item in sorted_device_names:
self.supports[item[0]] = item[1]
# preset account
self.preset_account: str = bytes.fromhex(
format((PRESET_ACCOUNT_DATA[0] ^ PRESET_ACCOUNT_DATA[1]), "X"),
).decode("utf-8", errors="ignore")
# preset password
self.preset_password: str = bytes.fromhex(
format((PRESET_ACCOUNT_DATA[0] ^ PRESET_ACCOUNT_DATA[2]), "X"),
).decode("utf-8", errors="ignore")
self.preset_cloud_name: str = DEFAULT_CLOUD
def _save_device_config(self, data: dict[str, Any]) -> None:
"""Save device config to json file with device id."""
storage_path = Path(self.hass.config.path(STORAGE_PATH))
storage_path.mkdir(parents=True, exist_ok=True)
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: # noqa: ANN401
"""Load device config from json file with device id.
Returns
-------
Device configuration (json)
"""
record_file = Path(
self.hass.config.path(f"{STORAGE_PATH}", f"{device_id}.json"),
)
if record_file.exists():
with record_file.open(encoding="utf-8") as f:
return load_json(f.name, default={})
return {}
@staticmethod
def _check_storage_device(device: dict, storage_device: dict) -> bool:
"""Check input device with storage_device.
Returns
-------
True if storage device exist
"""
if storage_device.get(CONF_SUBTYPE) is None:
return False
return not (
device.get(CONF_PROTOCOL) == ProtocolVersion.V3
and (
storage_device.get(CONF_TOKEN) is None
or storage_device.get(CONF_KEY) is None
)
)
def _already_configured(self, device_id: str, ip_address: str) -> bool:
"""Check device from json with device_id or ip address.
Returns
-------
True if device is already configured
"""
for entry in self._async_current_entries():
if device_id == entry.data.get(
CONF_DEVICE_ID,
) or ip_address == entry.data.get(CONF_IP_ADDRESS):
return True
return False
async def async_step_user(
self,
user_input: dict[str, Any] | None = None,
error: str | None = None,
) -> ConfigFlowResult:
"""Define config flow steps.
Using `async_step_<step_id>` and `async_step_user` will be the first step,
then select discovery mode
Returns
-------
Config flow result
"""
# user select a device discovery mode
if user_input is not None:
# default is auto discovery mode
if user_input["action"] == "discovery":
return await self.async_step_discovery()
# manual input device detail
if user_input["action"] == "manually":
self.found_device = {}
return await self.async_step_manually()
# remove cached login data and input new one
if user_input["action"] == "cache":
return await self.async_step_cache()
# only list all devices
return await self.async_step_list()
# user not input, show device discovery select form in UI
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{vol.Required("action", default="discovery"): vol.In(ADD_WAY)},
),
errors={"base": error} if error else None,
)
async def async_step_cache(
self,
user_input: dict[str, Any] | None = None,
error: str | None = None,
) -> ConfigFlowResult:
"""Remove cached login data and can input a new one.
Returns
-------
Config flow result
"""
# user input data exist
if user_input is not None:
# key is not None
if self.hass.data.get(DOMAIN):
self.hass.data[DOMAIN].pop("login_data", None)
self.hass.data[DOMAIN].pop("login_mode", None)
return await self.async_step_user()
# show cache info form in UI
return self.async_show_form(
step_id="cache",
data_schema=vol.Schema(
{
vol.Required("action", default="remove"): vol.In(
{"action": "remove"},
),
},
),
errors={"base": error} if error else None,
)
async def async_step_login(
self,
user_input: dict[str, Any] | None = None,
error: str | None = None,
) -> ConfigFlowResult:
"""User login steps.
Returns
-------
Config flow result
"""
# get cloud servers configs
cloud_servers = await MideaCloud.get_cloud_servers()
default_keys = await MideaCloud.get_default_keys()
# add skip login option to web UI with key 99
cloud_servers[next(iter(default_keys))] = SKIP_LOGIN
# user input data exist
if user_input is not None:
if not self.hass.data.get(DOMAIN):
self.hass.data[DOMAIN] = {}
# check skip login option
if user_input[CONF_SERVER] == next(iter(default_keys)):
# use preset account and DEFAULT_CLOUD cloud
_LOGGER.debug("skip login matched, cloud_servers: %s", cloud_servers)
# get DEFAULT_CLOUD key from dict
key = next(
key
for key, value in cloud_servers.items()
if value == DEFAULT_CLOUD
)
cloud_server = cloud_servers[key]
account = self.preset_account
password = self.preset_password
# set a login_mode flag
self.hass.data[DOMAIN]["login_mode"] = "preset"
# use input data
else:
_LOGGER.debug("user input login matched")
cloud_server = cloud_servers[user_input[CONF_SERVER]]
account = user_input[CONF_ACCOUNT]
password = user_input[CONF_PASSWORD]
# set a login_mode flag
self.hass.data[DOMAIN]["login_mode"] = "input"
# cloud login MUST pass with user input or perset account
if await self._check_cloud_login(
cloud_name=cloud_server,
account=account,
password=password,
force_login=True,
):
# save passed account to cache, available before HA reboot
self.hass.data[DOMAIN]["login_data"] = {
CONF_ACCOUNT: account,
CONF_PASSWORD: password,
CONF_SERVER: cloud_server,
}
# return to next step after login pass
return await self.async_step_auto()
# return error with login failed
_LOGGER.debug(
"ERROR: Failed to login with %s account in %s server",
self.hass.data[DOMAIN]["login_mode"],
cloud_server,
)
return await self.async_step_login(error="login_failed")
# user not login, show login form in UI
return self.async_show_form(
step_id="login",
data_schema=vol.Schema(
{
vol.Required(CONF_ACCOUNT): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_SERVER, default=1): vol.In(cloud_servers),
},
),
errors={"base": error} if error else None,
)
async def async_step_list(
self,
error: str | None = None,
) -> ConfigFlowResult:
"""List all devices and show device info in web UI.
Returns
-------
Config flow result
"""
# get all devices list
all_devices = discover()
# available devices exist
if len(all_devices) > 0:
table = (
"Appliance code|Type|IP address|SN|Supported\n:--:|:--:|:--:|:--:|:--:"
)
green = "<font color=gree>YES</font>"
red = "<font color=red>NO</font>"
for device_id, device in all_devices.items():
supported = device.get(CONF_TYPE) in self.supports
table += (
f"\n{device_id}|{f'{device.get(CONF_TYPE):02X}'}|"
f"{device.get(CONF_IP_ADDRESS)}|"
f"{device.get('sn')}|"
f"{green if supported else red}"
)
# no available device
else:
table = "Not found"
# show devices list result in UI
return self.async_show_form(
step_id="list",
description_placeholders={"table": table},
errors={"base": error} if error else None,
)
async def async_step_discovery(
self,
discovery_info: dict[str, Any] | None = None,
error: str | None = None,
) -> ConfigFlowResult:
"""Discovery device with auto mode or ip address.
Returns
-------
Config flow result
"""
# input is not None, using ip_address to discovery device
if discovery_info is not None:
# auto mode, ip_address is None
if discovery_info[CONF_IP_ADDRESS].lower() == "auto":
ip_address = None
# 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)
self.available_device = {}
for device_id, device in self.devices.items():
# remove exist devices and only return new devices
if not self._already_configured(
str(device_id),
device[CONF_IP_ADDRESS],
):
# fmt: off
self.available_device[device_id] = (
f"{device_id} ({self.supports.get(device.get(CONF_TYPE))})"
)
# fmt: on
if len(self.available_device) > 0:
return await self.async_step_auto()
return await self.async_step_discovery(error="no_devices")
# show discovery device input form with auto or ip address in web UI
return self.async_show_form(
step_id="discovery",
data_schema=vol.Schema(
{vol.Required(CONF_IP_ADDRESS, default="auto"): str},
),
errors={"base": error} if error else None,
)
async def _check_cloud_login(
self,
cloud_name: str | None = None,
account: str | None = None,
password: str | None = None,
force_login: bool = False,
) -> bool:
"""Check cloud login.
Returns
-------
True if cloud login succeeded
"""
# set default args with perset account
if cloud_name is None or account is None or password is None:
cloud_name = self.preset_cloud_name
account = self.preset_account
password = self.preset_password
if self.session is None:
self.session = async_create_clientsession(self.hass)
# init cloud object or force reinit with new one
if self.cloud is None or force_login:
self.cloud = get_midea_cloud(
cloud_name,
self.session,
account,
password,
)
# check cloud login after self.cloud exist
if await self.cloud.login():
_LOGGER.debug(
"Using account %s login to %s cloud pass",
account,
cloud_name,
)
return True
_LOGGER.debug(
"ERROR: unable to use account %s login to %s cloud",
account,
cloud_name,
)
return False
async def _check_key_from_cloud(
self,
appliance_id: int,
default_key: bool = True,
) -> dict[str, Any]:
"""Use perset DEFAULT_CLOUD account to get v3 device token and key.
Returns
-------
Dictionary of keys
"""
device = self.devices[appliance_id]
if self.cloud is None:
return {"error": "cloud_none"}
# get device token/key from cloud
keys = await self.cloud.get_cloud_keys(appliance_id)
default_keys = await MideaCloud.get_default_keys()
# use token/key to connect device and confirm token result
for k, value in keys.items():
# skip default_key
if not default_key and k == next(iter(default_keys)):
continue
dm = MideaDevice(
name="",
device_id=appliance_id,
device_type=device.get(CONF_TYPE),
ip_address=device.get(CONF_IP_ADDRESS),
port=device.get(CONF_PORT),
token=value["token"],
key=value["key"],
device_protocol=ProtocolVersion.V3,
model=device.get(CONF_MODEL),
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
# return debug log with failed key
_LOGGER.debug(
"connect device using method %s token/key failed",
k,
)
_LOGGER.debug(
"Unable to connect device with all the token/key",
)
return {"error": "connect_error"}
async def async_step_auto(
self,
user_input: dict[str, Any] | None = None,
error: str | None = None,
) -> ConfigFlowResult:
"""Discovery device detail info.
Returns
-------
Config flow result
"""
# input device exist
if user_input is not None:
device_id = user_input[CONF_DEVICE]
device = self.devices[device_id]
# set device args with protocol decode data
# then get subtype from cloud, get v3 device token/key from cloud
self.found_device = {
CONF_DEVICE_ID: device_id,
CONF_TYPE: device.get(CONF_TYPE),
CONF_PROTOCOL: device.get(CONF_PROTOCOL),
CONF_IP_ADDRESS: device.get(CONF_IP_ADDRESS),
CONF_PORT: device.get(CONF_PORT),
CONF_MODEL: device.get(CONF_MODEL),
}
storage_device = 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 = {
CONF_DEVICE_ID: device_id,
CONF_TYPE: device.get(CONF_TYPE),
CONF_PROTOCOL: device.get(CONF_PROTOCOL),
CONF_IP_ADDRESS: device.get(CONF_IP_ADDRESS),
CONF_PORT: device.get(CONF_PORT),
CONF_MODEL: device.get(CONF_MODEL),
CONF_NAME: storage_device.get(CONF_NAME),
CONF_SUBTYPE: storage_device.get(CONF_SUBTYPE),
CONF_TOKEN: storage_device.get(CONF_TOKEN),
CONF_KEY: storage_device.get(CONF_KEY),
}
_LOGGER.debug(
"Loaded configuration for device %s from storage",
device_id,
)
return await self.async_step_manually()
# device config not exist in local
# check login cache, show login web if no cache
if not self.hass.data.get(DOMAIN) or not self.hass.data[DOMAIN].get(
"login_data",
):
return await self.async_step_login()
# login cached exist, cloud is None, reinit and login
if self.cloud is None and not await self._check_cloud_login(
cloud_name=self.hass.data[DOMAIN]["login_data"][CONF_SERVER],
account=self.hass.data[DOMAIN]["login_data"][CONF_ACCOUNT],
password=self.hass.data[DOMAIN]["login_data"][CONF_PASSWORD],
):
# print error in debug log and show login web
_LOGGER.debug(
"Login with cached %s account %s failed in %s server",
self.hass.data[DOMAIN].get("login_mode"),
self.hass.data[DOMAIN]["login_data"][CONF_ACCOUNT],
self.hass.data[DOMAIN]["login_data"][CONF_SERVER],
)
# remove error cache and relogin
self.hass.data[DOMAIN].pop("login_data", None)
self.hass.data[DOMAIN].pop("login_mode", None)
return await self.async_step_login()
# get subtype from cloud
if self.cloud is not None and (
device_info := await self.cloud.get_device_info(device_id)
):
# set subtype with model_number
self.found_device[CONF_NAME] = device_info.get("name")
self.found_device[CONF_SUBTYPE] = device_info.get("model_number")
# MUST get a auth passed token/key for v3 device, disable add before pass
if device.get(CONF_PROTOCOL) == ProtocolVersion.V3:
# phase 1, try with user input login data
keys = await self._check_key_from_cloud(device_id)
# no available key, continue the phase 2
if not keys.get("token") or not keys.get("key"):
_LOGGER.debug(
"Can't get valid token with %s account in %s server",
self.hass.data[DOMAIN]["login_mode"],
self.hass.data[DOMAIN]["login_data"][CONF_SERVER],
)
# exclude: user selected not login and phase 1 is preset account
if self.hass.data[DOMAIN]["login_mode"] == "preset":
return await self.async_step_auto(
error="can't get valid token from Midea server",
)
# get key phase 2: reinit cloud with preset account
if not await self._check_cloud_login(force_login=True):
return await self.async_step_auto(
error="Perset account login failed!",
)
# try to get a passed key, without default_key
keys = await self._check_key_from_cloud(
device_id,
default_key=False,
)
# phase 2 got no available token/key, disable device add
if not keys.get("token") or not keys.get("key"):
_LOGGER.debug(
"Can't get available token from Midea server for device %s",
device_id,
)
return await self.async_step_auto(
error=(
f"Can't get available token from Midea server"
f" for device {device_id}"
),
)
# get key pass
self.found_device[CONF_TOKEN] = keys["token"]
self.found_device[CONF_KEY] = keys["key"]
return await self.async_step_manually()
# v1/v2 device add without token/key
return await self.async_step_manually()
# show available device list in UI
return self.async_show_form(
step_id="auto",
data_schema=vol.Schema(
{
vol.Required(
CONF_DEVICE,
default=next(iter(self.available_device.keys())),
): vol.In(self.available_device),
},
),
errors={"base": error} if error else None,
)
async def async_step_manually(
self,
user_input: dict[str, Any] | None = None,
error: str | None = None,
) -> ConfigFlowResult:
"""Add device with device detail info.
Returns
-------
Config flow result
"""
if user_input is not None:
try:
bytearray.fromhex(user_input[CONF_TOKEN])
bytearray.fromhex(user_input[CONF_KEY])
except ValueError:
return await self.async_step_manually(error="invalid_token")
device_id = user_input[CONF_DEVICE_ID]
# check device, discover already done or only manual add
if len(self.devices) < 1:
ip = user_input[CONF_IP_ADDRESS]
# discover device
self.devices = discover(
list(self.supports.keys()),
ip_address=ip,
)
# discover result MUST exist
if len(self.devices) != 1:
return await self.async_step_manually(error="invalid_device_ip")
# check all the input, disable error add
device_id = next(iter(self.devices.keys()))
# check if device_id is correctly set for that IP
if user_input[CONF_DEVICE_ID] != device_id:
return await self.async_step_manually(
error=f"For ip {ip} the device_id MUST be {device_id}",
)
device = self.devices[device_id]
if user_input[CONF_IP_ADDRESS] != device.get(CONF_IP_ADDRESS):
return await self.async_step_manually(
error=f"ip_address MUST be {device.get(CONF_IP_ADDRESS)}",
)
if user_input[CONF_PROTOCOL] != device.get(CONF_PROTOCOL):
return await self.async_step_manually(
error=f"protocol MUST be {device.get(CONF_PROTOCOL)}",
)
# try to get token/key with preset account
if user_input[CONF_PROTOCOL] == ProtocolVersion.V3 and (
len(user_input[CONF_TOKEN]) == 0 or len(user_input[CONF_KEY]) == 0
):
# init cloud with preset account
result = await self._check_cloud_login()
if not result:
return await self.async_step_manually(
error="Perset account login failed!",
)
# try to get a passed key
keys = await self._check_key_from_cloud(int(user_input[CONF_DEVICE_ID]))
# no available token/key, disable device add
if not keys.get("token") or not keys.get("key"):
_LOGGER.debug(
"Can't get a valid token from Midea server for device %s",
user_input[CONF_DEVICE_ID],
)
return await self.async_step_manually(
error=(
f"Can't get a valid token from Midea server"
f" for device {user_input[CONF_DEVICE_ID]}"
),
)
# set token/key from preset account
user_input[CONF_KEY] = keys["key"]
user_input[CONF_TOKEN] = keys["token"]
self.found_device = {
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_TOKEN: user_input[CONF_TOKEN],
CONF_KEY: user_input[CONF_KEY],
}
# check device connection with all the input
dm = MideaDevice(
name="",
device_id=user_input[CONF_DEVICE_ID],
device_type=user_input[CONF_TYPE],
ip_address=user_input[CONF_IP_ADDRESS],
port=user_input[CONF_PORT],
token=user_input[CONF_TOKEN],
key=user_input[CONF_KEY],
device_protocol=user_input[CONF_PROTOCOL],
model=user_input[CONF_MODEL],
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],
}
# 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,
)
return await self.async_step_manually(
error="Device auth failed with input config",
)
protocol = self.found_device.get(CONF_PROTOCOL)
return self.async_show_form(
step_id="manually",
data_schema=vol.Schema(
{
vol.Required(
CONF_NAME,
default=(
self.found_device.get(CONF_NAME)
if self.found_device.get(CONF_NAME)
else self.supports.get(self.found_device.get(CONF_TYPE))
),
): str,
vol.Required(
CONF_DEVICE_ID,
default=self.found_device.get(CONF_DEVICE_ID),
): int,
vol.Required(
CONF_TYPE,
default=(
self.found_device.get(CONF_TYPE)
if self.found_device.get(CONF_TYPE)
else 0xAC
),
): vol.In(self.supports),
vol.Required(
CONF_IP_ADDRESS,
default=self.found_device.get(CONF_IP_ADDRESS),
): str,
vol.Required(
CONF_PORT,
default=(
self.found_device.get(CONF_PORT)
if self.found_device.get(CONF_PORT)
else 6444
),
): int,
vol.Required(
CONF_PROTOCOL,
default=protocol or ProtocolVersion.V3,
): vol.In(
[protocol] if protocol else ProtocolVersion,
),
vol.Required(
CONF_MODEL,
default=(
self.found_device.get(CONF_MODEL)
if self.found_device.get(CONF_MODEL)
else "Unknown"
),
): str,
vol.Required(
CONF_SUBTYPE,
default=(
self.found_device.get(CONF_SUBTYPE)
if self.found_device.get(CONF_SUBTYPE)
else 0
),
): int,
vol.Optional(
CONF_TOKEN,
default=(
self.found_device.get(CONF_TOKEN)
if self.found_device.get(CONF_TOKEN)
else ""
),
): str,
vol.Optional(
CONF_KEY,
default=(
self.found_device.get(CONF_KEY)
if self.found_device.get(CONF_KEY)
else ""
),
): str,
},
),
errors={"base": error} if error else None,
)
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
"""Create the options flow with MideaLanOptionsFlowHandler.
Returns
-------
Config flow options handler
"""
return MideaLanOptionsFlowHandler(config_entry)
class MideaLanOptionsFlowHandler(OptionsFlow):
"""define an Options Flow Handler to update the options of a config entry."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self._config_entry = config_entry
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)
async def async_step_init(
self,
user_input: dict[str, Any] | None = None,
) -> ConfigFlowResult:
"""Manage the options.
Returns
-------
Config flow result
"""
if self._device_type == CONF_ACCOUNT:
return self.async_abort(reason="account_option")
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
sensors = {}
switches = {}
for attribute, attribute_config in cast(
"dict",
MIDEA_DEVICES[cast("int", self._device_type)]["entities"],
).items():
attribute_name = (
attribute if isinstance(attribute, str) else attribute.value
)
if attribute_config.get("type") in EXTRA_SENSOR:
sensors[attribute_name] = attribute_config.get("name")
elif attribute_config.get(
"type",
) in EXTRA_CONTROL and not attribute_config.get("default"):
switches[attribute_name] = attribute_config.get("name")
ip_address = self._config_entry.options.get(CONF_IP_ADDRESS, None)
if ip_address is None:
ip_address = self._config_entry.data.get(CONF_IP_ADDRESS, None)
refresh_interval = self._config_entry.options.get(CONF_REFRESH_INTERVAL, 30)
extra_sensors = list(
set(sensors.keys()) & set(self._config_entry.options.get(CONF_SENSORS, [])),
)
extra_switches = list(
set(switches.keys())
& set(self._config_entry.options.get(CONF_SWITCHES, [])),
)
customize = self._config_entry.options.get(CONF_CUSTOMIZE, "")
data_schema = vol.Schema(
{
vol.Required(CONF_IP_ADDRESS, default=ip_address): str,
vol.Required(CONF_REFRESH_INTERVAL, default=refresh_interval): int,
},
)
if len(sensors) > 0:
data_schema = data_schema.extend(
{
vol.Required(
CONF_SENSORS,
default=extra_sensors,
): cv.multi_select(sensors),
},
)
if len(switches) > 0:
data_schema = data_schema.extend(
{
vol.Required(
CONF_SWITCHES,
default=extra_switches,
): cv.multi_select(switches),
},
)
data_schema = data_schema.extend(
{
vol.Optional(
CONF_CUSTOMIZE,
default=customize,
): str,
},
)
return self.async_show_form(step_id="init", data_schema=data_schema)
+38
View File
@@ -0,0 +1,38 @@
"""Const for Midea Lan."""
from enum import IntEnum
from homeassistant.const import Platform
DOMAIN = "midea_ac_lan"
COMPONENT = "component"
DEVICES = "devices"
CONF_KEY = "key"
CONF_MODEL = "model"
CONF_SUBTYPE = "subtype"
CONF_ACCOUNT = "account"
CONF_SERVER = "server"
CONF_REFRESH_INTERVAL = "refresh_interval"
EXTRA_SENSOR = [Platform.SENSOR, Platform.BINARY_SENSOR]
EXTRA_SWITCH = [Platform.SWITCH, Platform.LOCK, Platform.SELECT, Platform.NUMBER]
EXTRA_CONTROL = [
Platform.CLIMATE,
Platform.WATER_HEATER,
Platform.FAN,
Platform.HUMIDIFIER,
Platform.LIGHT,
*EXTRA_SWITCH,
]
ALL_PLATFORM = EXTRA_SENSOR + EXTRA_CONTROL
class FanSpeed(IntEnum):
"""FanSpeed reference values."""
LOW = 20
MEDIUM = 40
HIGH = 60
FULL_SPEED = 80
AUTO = 100
@@ -0,0 +1,32 @@
"""Diagnostics support for Midea AC LAN."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import (
CONF_TOKEN,
)
if TYPE_CHECKING:
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import CONF_KEY
TO_REDACT = {CONF_TOKEN, CONF_KEY}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, # noqa: ARG001
entry: ConfigEntry,
) -> dict[str, Any]:
"""Return diagnostics for a config entry.
Returns
-------
Dictionary of config
"""
return {"entry": async_redact_data(entry.as_dict(), TO_REDACT)}
+320
View File
@@ -0,0 +1,320 @@
"""Midea Fan entries."""
import logging
from typing import Any, cast
from homeassistant.components.fan import FanEntity, FanEntityFeature
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.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 .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up fan entries."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
devs: list[
MideaFAFan | MideaB6Fan | MideaACFreshAirFan | MideaCEFan | MideaX40Fan
] = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.FAN and (
config.get("default") or entity_key in extra_switches
):
if device.device_type == DeviceType.FA:
devs.append(MideaFAFan(device, entity_key))
elif device.device_type == DeviceType.B6:
devs.append(MideaB6Fan(device, entity_key))
elif device.device_type == DeviceType.AC:
devs.append(MideaACFreshAirFan(device, entity_key))
elif device.device_type == DeviceType.CE:
devs.append(MideaCEFan(device, entity_key))
elif device.device_type == DeviceType.X40:
devs.append(MideaX40Fan(device, entity_key))
async_add_entities(devs)
# HA version >= 2024,8 support TURN_ON | TURN_OFF,for future changes, ref PR #285
try:
FAN_FEATURE_TURN_ON_OFF = FanEntityFeature["TURN_ON"] | FanEntityFeature["TURN_OFF"]
except KeyError:
FAN_FEATURE_TURN_ON_OFF = FanEntityFeature(0)
class MideaFan(MideaEntity, FanEntity):
"""Midea Fan Entries Base Class."""
_enable_turn_on_off_backwards_compatibility = False # 2024.8~2025.1
@property
def preset_modes(self) -> list[str] | None:
"""Midea Fan preset modes."""
return (
self._device.preset_modes if hasattr(self._device, "preset_modes") else None
)
@property
def is_on(self) -> bool:
"""Midea Fan is on."""
return cast("bool", self._device.get_attribute("power"))
@property
def oscillating(self) -> bool:
"""Midea Fan oscillating."""
return cast("bool", self._device.get_attribute("oscillate"))
@property
def preset_mode(self) -> str | None:
"""Midea Fan preset mode."""
return cast("str", self._device.get_attribute("mode"))
@property
def fan_speed(self) -> int | None:
"""Midea Fan fan speed."""
return cast("int", self._device.get_attribute("fan_speed"))
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea Fan turn off."""
self._device.set_attribute(attr="power", value=False)
def oscillate(self, oscillating: bool) -> None:
"""Midea Fan oscillate."""
self._device.set_attribute(attr="oscillate", value=oscillating)
def set_preset_mode(self, preset_mode: str) -> None:
"""Midea Fan set preset mode."""
self._device.set_attribute(attr="mode", value=preset_mode.capitalize())
@property
def percentage(self) -> int | None:
"""Midea Fan percentage."""
if not self.fan_speed:
return None
return int(round(self.fan_speed * self.percentage_step)) # noqa: RUF046
def set_percentage(self, percentage: int) -> None:
"""Midea Fan set percentage."""
fan_speed = round(percentage / self.percentage_step)
self._device.set_attribute(attr="fan_speed", value=fan_speed)
async def async_set_percentage(self, percentage: int) -> None:
"""Midea Fan async set percentage."""
if percentage == 0:
await self.async_turn_off()
else:
await self.hass.async_add_executor_job(self.set_percentage, percentage)
def update_state(self, status: Any) -> None: # noqa: ANN401, ARG002
"""Midea Fan update state."""
if not self.hass:
_LOGGER.warning(
"Fan update_state skipped for %s [%s]: HASS is None",
self.name,
type(self),
)
return
self.schedule_update_ha_state()
class MideaFAFan(MideaFan):
"""Midea FA Fan Entries."""
_device: MideaFADevice
def __init__(self, device: MideaFADevice, entity_key: str) -> None:
"""Midea FA Fan entity init."""
super().__init__(device, entity_key)
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.OSCILLATE
| FanEntityFeature.PRESET_MODE
| FAN_FEATURE_TURN_ON_OFF
)
self._attr_speed_count = self._device.speed_count
def turn_on(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs: Any, # noqa: ANN401, ARG002
) -> None:
"""Midea FA Fan turn on."""
fan_speed = int(percentage / self.percentage_step + 0.5) if percentage else None
self._device.turn_on(fan_speed=fan_speed, mode=preset_mode)
class MideaB6Fan(MideaFan):
"""Midea B6 Fan Entries."""
_device: MideaB6Device
def __init__(self, device: MideaB6Device, entity_key: str) -> None:
"""Midea B6 Fan entity init."""
super().__init__(device, entity_key)
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.PRESET_MODE
| FAN_FEATURE_TURN_ON_OFF
)
self._attr_speed_count = self._device.speed_count
def turn_on(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs: Any, # noqa: ANN401, ARG002
) -> None:
"""Midea B6 Fan turn on."""
fan_speed = int(percentage / self.percentage_step + 0.5) if percentage else None
self._device.turn_on(fan_speed=fan_speed, mode=preset_mode)
class MideaACFreshAirFan(MideaFan):
"""Midea AC Fresh Air Fan Entries."""
_device: MideaACDevice
def __init__(self, device: MideaACDevice, entity_key: str) -> None:
"""Midea AC Fresh Air Fan entity init."""
super().__init__(device, entity_key)
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.PRESET_MODE
| FAN_FEATURE_TURN_ON_OFF
)
self._attr_speed_count = 100
@property
def preset_modes(self) -> list[str] | None:
"""Midea AC Fan preset modes."""
return cast("list", self._device.fresh_air_fan_speeds)
@property
def is_on(self) -> bool:
"""Midea AC Fan is on."""
return cast("bool", self._device.get_attribute(ACAttributes.fresh_air_power))
@property
def fan_speed(self) -> int:
"""Midea AC Fan fan speed."""
return cast("int", self._device.get_attribute(ACAttributes.fresh_air_fan_speed))
def turn_on(
self,
percentage: int | None = None, # noqa: ARG002
preset_mode: str | None = None, # noqa: ARG002
**kwargs: Any, # noqa: ANN401, ARG002
) -> None:
"""Midea AC Fan tun on."""
self._device.set_attribute(attr=ACAttributes.fresh_air_power, value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea AC Fan turn off."""
self._device.set_attribute(attr=ACAttributes.fresh_air_power, value=False)
def set_percentage(self, percentage: int) -> None:
"""Midea AC Fan set percentage."""
fan_speed = int(percentage / self.percentage_step + 0.5)
self._device.set_attribute(
attr=ACAttributes.fresh_air_fan_speed,
value=fan_speed,
)
def set_preset_mode(self, preset_mode: str) -> None:
"""Midea AC Fan set preset mode."""
self._device.set_attribute(attr=ACAttributes.fresh_air_mode, value=preset_mode)
@property
def preset_mode(self) -> str | None:
"""Midea AC Fan preset mode."""
return cast("str", self._device.get_attribute(attr=ACAttributes.fresh_air_mode))
class MideaCEFan(MideaFan):
"""Midea CE Fan Entries."""
_device: MideaCEDevice
def __init__(self, device: MideaCEDevice, entity_key: str) -> None:
"""Midea CE Fan entity init."""
super().__init__(device, entity_key)
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.PRESET_MODE
| FAN_FEATURE_TURN_ON_OFF
)
self._attr_speed_count = self._device.speed_count
def turn_on(
self,
percentage: int | None = None, # noqa: ARG002
preset_mode: str | None = None, # noqa: ARG002
**kwargs: Any, # noqa: ANN401, ARG002
) -> None:
"""Midea CE Fan turn on."""
self._device.set_attribute(attr=CEAttributes.power, value=True)
async def async_set_percentage(self, percentage: int) -> None:
"""Midea CE Fan async set percentage."""
await self.hass.async_add_executor_job(self.set_percentage, percentage)
class MideaX40Fan(MideaFan):
"""Midea X40 Fan Entries."""
_device: MideaX40Device
def __init__(self, device: MideaX40Device, entity_key: str) -> None:
"""Midea X40 Fan entity init."""
super().__init__(device, entity_key)
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.OSCILLATE
| FAN_FEATURE_TURN_ON_OFF
)
self._attr_speed_count = 2
@property
def is_on(self) -> bool:
"""Midea X40 Fan is on."""
return cast("int", self._device.get_attribute(attr=X40Attributes.fan_speed)) > 0
def turn_on(
self,
percentage: int | None = None, # noqa: ARG002
preset_mode: str | None = None, # noqa: ARG002
**kwargs: Any, # noqa: ANN401, ARG002
) -> None:
"""Midea X40 Fan turn on."""
self._device.set_attribute(attr=X40Attributes.fan_speed, value=1)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea X40 Fan turn off."""
self._device.set_attribute(attr=X40Attributes.fan_speed, value=0)
@@ -0,0 +1,140 @@
"""Midea Humidifier entries."""
import logging
from typing import Any, TypeAlias, cast
from homeassistant.components.humidifier import (
HumidifierDeviceClass,
HumidifierEntity,
HumidifierEntityFeature,
)
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 .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up humidifier entries."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
devs: list[MideaA1Humidifier | MideaFDHumidifier] = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.HUMIDIFIER and (
config.get("default") or entity_key in extra_switches
):
if device.device_type == DeviceType.A1:
devs.append(MideaA1Humidifier(device, entity_key))
if device.device_type == DeviceType.FD:
devs.append(MideaFDHumidifier(device, entity_key))
async_add_entities(devs)
MideaHumidifierDevice: TypeAlias = MideaFDDevice | MideaA1Device
class MideaHumidifier(MideaEntity, HumidifierEntity):
"""Midea Humidifier Entries Base Class."""
_device: MideaHumidifierDevice
def __init__(self, device: MideaHumidifierDevice, entity_key: str) -> None:
"""Midea Humidifier entity init."""
super().__init__(device, entity_key)
@property
def current_humidity(self) -> float | None:
"""Midea Humidifier current humidity."""
return cast("float", self._device.get_attribute("current_humidity"))
@property
def target_humidity(self) -> float:
"""Midea Humidifier target humidity."""
return cast("float", self._device.get_attribute("target_humidity"))
@property
def mode(self) -> str:
"""Midea Humidifier mode."""
return cast("str", self._device.get_attribute("mode"))
@property
def available_modes(self) -> list[str] | None:
"""Midea Humidifier available modes."""
return cast("list", self._device.modes)
def set_humidity(self, humidity: int) -> None:
"""Midea Humidifier set humidity."""
self._device.set_attribute("target_humidity", humidity)
def set_mode(self, mode: str) -> None:
"""Midea Humidifier set mode."""
self._device.set_attribute("mode", mode)
@property
def is_on(self) -> bool:
"""Midea Humidifier is on."""
return cast("bool", self._device.get_attribute(attr="power"))
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea Humidifier turn on."""
self._device.set_attribute(attr="power", value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea Humidifier turn off."""
self._device.set_attribute(attr="power", value=False)
def update_state(self, status: Any) -> None: # noqa: ANN401, ARG002
"""Midea Humidifier update state."""
if not self.hass:
_LOGGER.warning(
"Humidifier update_state skipped for %s [%s]: HASS is None",
self.name,
type(self),
)
return
self.schedule_update_ha_state()
class MideaA1Humidifier(MideaHumidifier):
"""Midea A1 Humidifier Entries."""
_device: MideaA1Device
def __init__(self, device: MideaA1Device, entity_key: str) -> None:
"""Midea A1 Humidifier entity init."""
super().__init__(device, entity_key)
self._attr_min_humidity: float = 35
self._attr_max_humidity: float = 85
self._attr_device_class = HumidifierDeviceClass.DEHUMIDIFIER
self._attr_supported_features = HumidifierEntityFeature.MODES
class MideaFDHumidifier(MideaHumidifier):
"""Midea FD Humidifier Entries."""
_device: MideaFDDevice
def __init__(self, device: MideaFDDevice, entity_key: str) -> None:
"""Midea FD Humidifier entity init."""
super().__init__(device, entity_key)
self._attr_min_humidity: float = 35
self._attr_max_humidity: float = 85
self._attr_device_class = HumidifierDeviceClass.HUMIDIFIER
self._attr_supported_features = HumidifierEntityFeature.MODES
+184
View File
@@ -0,0 +1,184 @@
"""Midea Light entries."""
import logging
from typing import Any, cast
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP_KELVIN,
ATTR_EFFECT,
ColorMode,
LightEntity,
LightEntityFeature,
)
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 .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up light entries."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
devs = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.LIGHT and (
config.get("default") or entity_key in extra_switches
):
devs.append(MideaLight(device, entity_key))
async_add_entities(devs)
def _calc_supported_features(device: Midea13Device) -> LightEntityFeature:
supported_features = LightEntityFeature(0)
if device.get_attribute(X13Attributes.effect):
supported_features |= LightEntityFeature.EFFECT
return supported_features
def _calc_supported_color_modes(device: Midea13Device) -> set[ColorMode]:
# https://github.com/home-assistant/core/blob/c34731185164aaf44419977c4086e9a7dd6c0a7f/homeassistant/components/light/__init__.py#L1278
supported = set[ColorMode]()
if device.get_attribute(X13Attributes.color_temperature) is not None:
supported.add(ColorMode.COLOR_TEMP)
if device.get_attribute(X13Attributes.rgb_color) is not None:
supported.add(ColorMode.HS)
if not supported and device.get_attribute(X13Attributes.brightness) is not None:
supported = {ColorMode.BRIGHTNESS}
if not supported:
supported = {ColorMode.ONOFF}
return supported
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)
def _calc_color_mode(self, supported: set[ColorMode]) -> ColorMode:
"""Midea Light calculate color mode.
Returns
-------
Calculated color mode
"""
# https://github.com/home-assistant/core/blob/c34731185164aaf44419977c4086e9a7dd6c0a7f/homeassistant/components/light/__init__.py#L925
if ColorMode.HS in supported and self.hs_color is not None:
return ColorMode.HS
if ColorMode.COLOR_TEMP in supported and self.color_temp_kelvin is not None:
return ColorMode.COLOR_TEMP
if ColorMode.BRIGHTNESS in supported and self.brightness is not None:
return ColorMode.BRIGHTNESS
if ColorMode.ONOFF in supported:
return ColorMode.ONOFF
return ColorMode.UNKNOWN
@property
def is_on(self) -> bool:
"""Midea Light is on."""
return cast("bool", self._device.get_attribute(X13Attributes.power))
@property
def brightness(self) -> int | None:
"""Midea Light brightness."""
return cast("int", self._device.get_attribute(X13Attributes.brightness))
@property
def rgb_color(self) -> tuple[int, int, int] | None:
"""Midea Light rgb color."""
return cast("tuple", self._device.get_attribute(X13Attributes.rgb_color))
@property
def color_temp(self) -> int | None:
"""Midea Light color temperature."""
if not self.color_temp_kelvin:
return None
return round(1000000 / self.color_temp_kelvin)
# https://developers.home-assistant.io/blog/2024/12/14/kelvin-preferred-color-temperature-unit
@property
def color_temp_kelvin(self) -> int | None:
"""Midea Light color temperature kelvin."""
return cast("int", self._device.get_attribute(X13Attributes.color_temperature))
@property
def min_color_temp_kelvin(self) -> int:
"""Midea Light min color temperature kelvin."""
return self._device.color_temp_range[0]
@property
def max_color_temp_kelvin(self) -> int:
"""Midea Light max color temperature kelvin."""
return self._device.color_temp_range[1]
@property
def effect_list(self) -> list[str] | None:
"""Midea Light effect list."""
return cast("list", self._device.effects)
@property
def effect(self) -> str | None:
"""Midea Light effect."""
return cast("str", self._device.get_attribute(X13Attributes.effect))
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea Light turn on."""
if not self.is_on:
self._device.set_attribute(attr=X13Attributes.power, value=True)
for key, value in kwargs.items():
if key == ATTR_BRIGHTNESS:
self._device.set_attribute(attr=X13Attributes.brightness, value=value)
if key == ATTR_COLOR_TEMP_KELVIN:
self._device.set_attribute(
attr=X13Attributes.color_temperature,
value=value,
)
if key == ATTR_EFFECT:
self._device.set_attribute(attr=X13Attributes.effect, value=value)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea Light turn off."""
self._device.set_attribute(attr=X13Attributes.power, value=False)
def update_state(self, status: Any) -> None: # noqa: ANN401,ARG002
"""Midea Light update state."""
if not self.hass:
_LOGGER.warning(
"Light update_state skipped for %s [%s]: HASS is None",
self.name,
type(self),
)
return
self.schedule_update_ha_state()
+54
View File
@@ -0,0 +1,54 @@
"""Lock entities for Midea Lan."""
from typing import Any, cast
from homeassistant.components.lock import LockEntity
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 .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up entities for device."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
locks = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.LOCK and entity_key in extra_switches:
dev = MideaLock(device, entity_key)
locks.append(dev)
async_add_entities(locks)
class MideaLock(MideaEntity, LockEntity):
"""Represent a Midea lock entity."""
@property
def is_locked(self) -> bool:
"""Return true if state is locked."""
return cast("bool", self._device.get_attribute(self._entity_key))
def lock(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Lock the lock."""
self._device.set_attribute(attr=self._entity_key, value=True)
def unlock(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Unlock the lock."""
self._device.set_attribute(attr=self._entity_key, value=False)
def open(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Open the lock."""
self.unlock()
@@ -0,0 +1,14 @@
{
"domain": "midea_ac_lan",
"name": "Midea AC LAN",
"codeowners": ["@wuwentao", "@rokam", "@chemelli74", "@Necroneco"],
"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.6.0"],
"version": "v0.6.11"
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,144 @@
"""Base entity for Midea Lan."""
import logging
from typing import Any, cast
from homeassistant.const import MAJOR_VERSION, MINOR_VERSION
from homeassistant.core import callback
if (MAJOR_VERSION, MINOR_VERSION) >= (2023, 9):
from homeassistant.helpers.device_registry import DeviceInfo
else:
from homeassistant.helpers.entity import ( # type: ignore[attr-defined]
DeviceInfo,
)
from homeassistant.helpers.entity import Entity
from midealocal.device import MideaDevice
from .const import DOMAIN
from .midea_devices import MIDEA_DEVICES
_LOGGER = logging.getLogger(__name__)
class MideaEntity(Entity):
"""Base Midea entity."""
def __init__(self, device: MideaDevice, entity_key: str) -> None:
"""Initialize Midea base entity."""
self._device = device
self._device.register_update(self.update_state)
self._config = cast(
"dict",
MIDEA_DEVICES[self._device.device_type]["entities"],
)[entity_key]
self._entity_key = entity_key
self._unique_id = f"{DOMAIN}.{self._device.device_id}_{entity_key}"
self.entity_id = self._unique_id
self._device_name = self._device.name
# HA language setting:
# 1. hass.config.language: Settings / System / General settings
# 2. user language setting in user profile setting
# Entity name translation based on hass.config.language
# add language in /config/configuration.yaml will disable web UI setting
# homeassistant:
# language: zh-Hans # noqa: ERA001
# Translating the name and attributes of entities:
# https://developers.home-assistant.io/blog/2023/03/27/entity_name_translations/#translating-entity-name
# https://developers.home-assistant.io/docs/internationalization/core
# translation_key: if defined, Home Assistant will try to find a
# translation in translations/<lang>.json.
# If translation exists -> UI shows the translated string.
# If translation not found -> fallback to "name" / device_class / entity_id.
self._attr_translation_key = self._config.get("translation_key")
# has_entity_name: MUST be True in modern HA (old False behavior is deprecated).
self._attr_has_entity_name = True
# Step 1: translation_key is defined
# - If translation is found in the current language:
# -> UI displays the translated string.
if self._attr_translation_key is not None:
# skip set attr_name and use translation_key
pass
# set attr_name to None will only show device name without translaion_key
# Step 2: No translation_key
# but english "name" is explicitly set in config:
# -> UI displays this name directly (highest priority).
elif self._config.get("name") is not None:
self._attr_name = self._config["name"]
# Step 3: No translation_key, no name,
# fallback to device_class default label.
# 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,
else:
self._attr_name = (
f"{self._device_name} {self._config.get('name')}"
if "name" in self._config
else f"{self._device_name}"
)
@property
def device(self) -> MideaDevice:
"""Return device structure."""
return self._device
@property
def device_info(self) -> DeviceInfo:
"""Return device info."""
return {
"manufacturer": "Midea",
"model": f"{MIDEA_DEVICES[self._device.device_type]['name']} "
f"{self._device.model}"
f" ({self._device.subtype})",
"identifiers": {(DOMAIN, str(self._device.device_id))},
"name": self._device_name,
}
@property
def unique_id(self) -> str:
"""Return entity unique id."""
return self._unique_id
@property
def should_poll(self) -> bool:
"""Return true is integration should poll."""
return False
@property
def available(self) -> bool:
"""Return entity availability."""
return bool(self._device.available)
@property
def icon(self) -> str:
"""Return entity icon."""
return cast("str", self._config.get("icon"))
@callback
def update_state(self, status: Any) -> None: # noqa: ANN401
"""Update entity state."""
if not self.hass:
_LOGGER.warning(
"MideaEntity update_state for %s [%s] with status %s: HASS is None",
self.name,
type(self),
status,
)
return
if self.hass.is_stopping:
_LOGGER.debug(
"MideaEntity update_state for %s [%s] with status %s: HASS is stopping",
self.name,
type(self),
status,
)
return
if self._entity_key in status or "available" in status:
self.schedule_update_ha_state()
+102
View File
@@ -0,0 +1,102 @@
"""Number for Midea Lan."""
from typing import Any, cast
from homeassistant.components.number import NumberEntity
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 .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up numbers for device."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
numbers = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.NUMBER and entity_key in extra_switches:
dev = MideaNumber(device, entity_key)
numbers.append(dev)
async_add_entities(numbers)
class MideaNumber(MideaEntity, NumberEntity):
"""Represent a Midea number sensor."""
def __init__(self, device: MideaDevice, entity_key: str) -> None:
"""Midea number sensor init."""
super().__init__(device, entity_key)
self._max_value = self._config.get("max")
self._min_value = self._config.get("min")
self._step_value = self._config.get("step")
@property
def native_min_value(self) -> float:
"""Return minimum value."""
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)
)
),
)
@property
def native_max_value(self) -> float:
"""Return maximum value."""
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)
)
),
)
@property
def native_step(self) -> float:
"""Return step value."""
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)
)
),
)
@property
def native_value(self) -> float:
"""Return value."""
return cast("float", self._device.get_attribute(self._entity_key))
def set_native_value(self, value: Any) -> None: # noqa: ANN401
"""Set value."""
self._device.set_attribute(self._entity_key, value)
+57
View File
@@ -0,0 +1,57 @@
"""Select for Midea Lan."""
from typing import cast
from homeassistant.components.select import SelectEntity
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 .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up selects for device."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
selects = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.SELECT and entity_key in extra_switches:
dev = MideaSelect(device, entity_key)
selects.append(dev)
async_add_entities(selects)
class MideaSelect(MideaEntity, SelectEntity):
"""Represent a Midea select."""
def __init__(self, device: MideaDevice, entity_key: str) -> None:
"""Midea select init."""
super().__init__(device, entity_key)
self._options_name = self._config.get("options")
@property
def options(self) -> list[str]:
"""Return entity options."""
return cast("list", getattr(self._device, self._options_name))
@property
def current_option(self) -> str:
"""Return entity current option."""
return cast("str", self._device.get_attribute(self._entity_key))
def select_option(self, option: str) -> None:
"""Select entity option."""
self._device.set_attribute(self._entity_key, option)
+67
View File
@@ -0,0 +1,67 @@
"""Sensor for Midea Lan."""
from typing import Any, cast
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE_ID, CONF_SENSORS, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up sensors for device."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_sensors = config_entry.options.get(CONF_SENSORS, [])
sensors = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.SENSOR and entity_key in extra_sensors:
sensor = MideaSensor(device, entity_key)
sensors.append(sensor)
async_add_entities(sensors)
class MideaSensor(MideaEntity, SensorEntity):
"""Represent a Midea sensor."""
@property
def native_value(self) -> StateType:
"""Return entity value."""
return cast("StateType", self._device.get_attribute(self._entity_key))
@property
def device_class(self) -> SensorDeviceClass:
"""Return device class."""
return cast("SensorDeviceClass", self._config.get("device_class"))
@property
def state_class(self) -> SensorStateClass | None:
"""Return state state."""
return cast("SensorStateClass | None", self._config.get("state_class"))
@property
def native_unit_of_measurement(self) -> str | None:
"""Return unit of measurement."""
return cast("str | None", self._config.get("unit"))
@property
def capability_attributes(self) -> dict[str, Any] | None:
"""Return capabilities."""
return {"state_class": self.state_class} if self.state_class else {}
@@ -0,0 +1,18 @@
---
set_attribute:
fields:
device_id:
example: "1234567890"
attribute:
example: "eco_mode"
value:
example: true
send_command:
fields:
device_id:
example: "1234567890"
cmd_type:
example: 2
cmd_body:
example: "B0FF01370E0000A500"
+50
View File
@@ -0,0 +1,50 @@
"""Switch for Midea Lan."""
from typing import Any, cast
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 import ToggleEntity
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up switches for device."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
switches = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.SWITCH and entity_key in extra_switches:
dev = MideaSwitch(device, entity_key)
switches.append(dev)
async_add_entities(switches)
class MideaSwitch(MideaEntity, ToggleEntity):
"""Represent a Midea switch."""
@property
def is_on(self) -> bool:
"""Return true if switch is on."""
return cast("bool", self._device.get_attribute(self._entity_key))
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Turn on switch."""
self._device.set_attribute(attr=self._entity_key, value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Turn off switch."""
self._device.set_attribute(attr=self._entity_key, value=False)
@@ -0,0 +1,715 @@
{
"config": {
"error": {
"preset_account": "Anmeldung mit voreingestelltem Konto fehlgeschlagen, bitte melden Sie dieses Problem",
"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",
"config_incorrect": "Die Konfiguration ist falsch",
"connect_error": "Kann keine Verbindung zum Gerät herstellen",
"invalid_token": "Token oder Schlüssel hat ein falsches Format"
},
"step": {
"user": {
"data": {
"way": "Hinzufügen von Geräten"
},
"description": "Wählen Sie die Art des Hinzufügens eines Geräts",
"title": "Neues Gerät hinzufügen"
},
"login": {
"data": {
"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.",
"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\".",
"title": "Suche",
"data": {
"ip_address": "IP-Adresse"
}
},
"list": {
"description": "{table}",
"title": "Geräte"
},
"auto": {
"data": {
"device": "Geräte"
},
"description": "Wählen Sie ein Gerät zum Hinzufügen",
"title": "Neues Gerät gefunden"
},
"manually": {
"data": {
"name": "Name (z.B. Klimaanlage Wohnzimmer)",
"device_id": "Geräte-ID",
"type": "Typ",
"ip_address": "IP-Adresse",
"port": "Port",
"model": "Modell",
"subtype": "Untertyp",
"protocol": "Protokoll",
"token": "Token",
"key": "Schlüssel"
},
"description": "Konfiguration des Geräts",
"title": "Neues Gerät"
}
}
},
"entity": {
"binary_sensor": {
"bar_door": {
"name": "Bar Door"
},
"bar_door_overtime": {
"name": "Bar Door Overtime"
},
"bathing_working": {
"name": "Bathing Working Status"
},
"bottom_compartment_cooling": {
"name": "Bottom Compartment Cooling"
},
"bottom_compartment_door": {
"name": "Bottom Compartment Door"
},
"bottom_compartment_preheating": {
"name": "Bottom Compartment Preheating"
},
"burning_state": {
"name": "Burning State"
},
"cleaning_reminder": {
"name": "Cleaning Reminder"
},
"compressor_status": {
"name": "Compressor Status"
},
"cooking": {
"name": "Cooking"
},
"filter_change_reminder": {
"name": "Filter Change Reminder"
},
"filter_cleaning_reminder": {
"name": "Filter Cleaning Reminder"
},
"finished": {
"name": "Finished"
},
"flex_zone_door": {
"name": "Flex Door"
},
"flex_zone_door_overtime": {
"name": "Flex Zone Door"
},
"freezer_door": {
"name": "Freezer Door"
},
"freezer_door_overtime": {
"name": "Freezer Door Overtime"
},
"full_dust": {
"name": "Full of Dust"
},
"heating": {
"name": "Heating"
},
"heating_working": {
"name": "Heating Working Status"
},
"keep_warm": {
"name": "Keep Warm"
},
"lid_status": {
"name": "Lid Status"
},
"middle_compartment_cooling": {
"name": "Middle Compartment Cooling"
},
"middle_compartment_door": {
"name": "Middle Compartment Door"
},
"middle_compartment_preheating": {
"name": "Middle Compartment Preheating"
},
"oilcup_full": {
"name": "Oil-cup Full"
},
"protection": {
"name": "Protection"
},
"refrigerator_door": {
"name": "Refrigerator Door"
},
"refrigerator_door_overtime": {
"name": "Refrigerator Door Overtime"
},
"rinse_aid": {
"name": "Rinse Aid Shortage"
},
"salt": {
"name": "Salt Shortage"
},
"seat_status": {
"name": "Seat Status"
},
"status_dhw": {
"name": "DHW status"
},
"status_heating": {
"name": "Heating status"
},
"status_ibh": {
"name": "IBH status"
},
"status_tbh": {
"name": "TBH status"
},
"tank_ejected": {
"name": "Tank Ejected"
},
"tank_full": {
"name": "Tank status"
},
"top_compartment_cooling": {
"name": "Top Compartment Cooling"
},
"top_compartment_door": {
"name": "Top Compartment Door"
},
"top_compartment_preheating": {
"name": "Top Compartment Preheating"
},
"water_change_reminder": {
"name": "Water Change Reminder"
},
"water_shortage": {
"name": "Water Shortage"
},
"with_pressure": {
"name": "With Pressure"
},
"zone1_room_temp_mode": {
"name": "Zone1 Room-temperature Mode"
},
"zone1_water_temp_mode": {
"name": "Zone1 Water-temperature Mode"
},
"zone2_room_temp_mode": {
"name": "Zone2 Room-temperature Mode"
},
"zone2_water_temp_mode": {
"name": "Zone2 Water-temperature Mode"
}
},
"climate": {
"climate_zone1": {
"name": "Zone1 Thermostat"
},
"climate_zone2": {
"name": "Zone2 Thermostat"
}
},
"fan": {
"fresh_air": {
"name": "Fresh Air"
}
},
"lock": {
"child_lock": {
"name": "Kindersicherung"
}
},
"number": {
"dry_level": {
"name": "Dry Level"
},
"heating_level": {
"name": "Heating Level"
},
"seat_temp_level": {
"name": "Seat Temperature Level"
},
"water_temp_level": {
"name": "Water Temperature Level"
}
},
"select": {
"detect_mode": {
"name": "Detect Mode"
},
"direction": {
"name": "Direction"
},
"fan_speed": {
"name": "Fan Speed"
},
"mode": {
"name": "Mode"
},
"oscillation_angle": {
"name": "Oscillation Angle"
},
"oscillation_mode": {
"name": "Oscillation Mode"
},
"screen_display": {
"name": "Screen Display"
},
"silent_level": {
"name": "Silent Level"
},
"tilting_angle": {
"name": "Tilting Angle"
},
"water_level_set": {
"name": "Water Level Setting"
}
},
"sensor": {
"bathing_leaving_temperature": {
"name": "Bathing Leaving Water Temperature"
},
"bottom_compartment_remaining": {
"name": "Bottom Compartment Remaining"
},
"bottom_compartment_status": {
"name": "Bottom Compartment Status"
},
"bottom_compartment_temperature": {
"name": "Bottom Compartment Temperature"
},
"bottom_temperature": {
"name": "Bottom Temperature"
},
"bright": {
"name": "Bright Level"
},
"compressor_temperature": {
"name": "Compressor Temperature"
},
"condenser_temperature": {
"name": "Condenser Temperature"
},
"current_energy_consumption": {
"name": "Current Energy Consumption"
},
"current_temperature": {
"name": "Current Temperature"
},
"dehydration_speed": {
"name": "dehydration speed"
},
"dehydration_time": {
"name": "dehydration time"
},
"detergent": {
"name": "detergent"
},
"energy_consumption": {
"name": "Energy Consumption"
},
"error_code": {
"name": "Error Code"
},
"fan_level": {
"name": "Fan level"
},
"filter_life": {
"name": "Filter Life"
},
"filter1_days": {
"name": "Filter1 Verfügbare Tage"
},
"filter1_life": {
"name": "Filter1 Lebensdauer Level"
},
"filter2_days": {
"name": "Filter2 Verfügbare Tage"
},
"filter2_life": {
"name": "Filter2 Lebensdauer Level"
},
"filter3_days": {
"name": "Filter3 Verfügbare Tage"
},
"filter3_life": {
"name": "Filter3 Lebensdauer Level"
},
"flex_zone_actual_temp": {
"name": "Flex Zone Actual Temperature"
},
"flex_zone_setting_temp": {
"name": "Flex Zone Setting Temperature"
},
"freezer_actual_temp": {
"name": "Freezer Actual Temperature"
},
"freezer_setting_temp": {
"name": "Freezer Setting Temperature"
},
"hcho": {
"name": "Methanal"
},
"heating_leaving_temperature": {
"name": "Heating Leaving Water Temperature"
},
"heating_power": {
"name": "Heating Power"
},
"heating_time_remaining": {
"name": "Heating Time Remaining"
},
"in_tds": {
"name": "TDS-Wert rein"
},
"indoor_humidity": {
"name": "Indoor Humidity"
},
"indoor_temperature": {
"name": "Indoor Temperature"
},
"keep_warm_remaining": {
"name": "Keep Warm Remaining"
},
"keep_warm_time": {
"name": "Keep Warm Time"
},
"middle_compartment_remaining": {
"name": "Middle Compartment Remaining"
},
"middle_compartment_status": {
"name": "Middle Compartment Status"
},
"middle_compartment_temperature": {
"name": "Middle Compartment Temperature"
},
"mode": {
"name": "Mode"
},
"out_tds": {
"name": "TDS-Wert raus"
},
"outdoor_temperature": {
"name": "Outdoor Temperature"
},
"program": {
"name": "Program"
},
"progress": {
"name": "Progress"
},
"realtime_power": {
"name": "Realtime Power"
},
"refrigerator_actual_temp": {
"name": "Refrigerator Actual Temperature"
},
"refrigerator_setting_temp": {
"name": "Refrigerator Setting Temperature"
},
"right_flex_zone_actual_temp": {
"name": "Right Flex Zone Actual Temperature"
},
"right_flex_zone_setting_temp": {
"name": "Right Flex Zone Setting Temperature"
},
"rinse_count": {
"name": "rinse count"
},
"rinse_level": {
"name": "rinse level"
},
"seat_temperature": {
"name": "Seat Temperature"
},
"soak_time": {
"name": "soak time"
},
"softener": {
"name": "softener"
},
"softwater": {
"name": "Softwater Level"
},
"status": {
"name": "Status"
},
"storage_remaining": {
"name": "Storage Remaining"
},
"tank": {
"name": "Tank"
},
"tank_actual_temperature": {
"name": "Tank Actual Temperature"
},
"target_temperature": {
"name": "Target Temperature"
},
"time_remaining": {
"name": "Time Remaining"
},
"top_compartment_remaining": {
"name": "Top Compartment Remaining"
},
"top_compartment_status": {
"name": "Top Compartment Status"
},
"top_compartment_temperature": {
"name": "Top Compartment Temperature"
},
"top_temperature": {
"name": "Top Temperature"
},
"total_energy_consumption": {
"name": "Total Energy Consumption"
},
"total_produced_energy": {
"name": "Total produced energy"
},
"tvoc": {
"name": "TVOC"
},
"wash_level": {
"name": "rinse count"
},
"wash_strength": {
"name": "wash strength"
},
"wash_time": {
"name": "wash time"
},
"water_consumption": {
"name": "Wasserverbrauch"
},
"water_temperature": {
"name": "Water Temperature"
},
"water_level": {
"name": "Water Level"
},
"working_time": {
"name": "Working Time"
}
},
"switch": {
"anion": {
"name": "Anion"
},
"aux_heating": {
"name": "Aux Heating"
},
"boost_mode": {
"name": "Boost Mode"
},
"breezeless": {
"name": "Breezeless"
},
"comfort_mode": {
"name": "Comfort Mode"
},
"dhw_power": {
"name": "DHW Power"
},
"disinfect": {
"name": "Disinfect"
},
"dry": {
"name": "Dry"
},
"eco_mode": {
"name": "ECO Mode"
},
"fast_dhw": {
"name": "Fast DHW"
},
"foam_shield": {
"name": "Foam Shield"
},
"frost_protect": {
"name": "Frost Protect"
},
"heating_power": {
"name": "Heating Power"
},
"indirect_wind": {
"name": "Indirect Wind"
},
"light": {
"name": "Licht"
},
"link_to_ac": {
"name": "Link to AC"
},
"main_light": {
"name": "Main Light"
},
"main_power": {
"name": "Main Power"
},
"natural_wind": {
"name": "Natural Wind"
},
"night_light": {
"name": "Night Light"
},
"oscillate": {
"name": "Oscillate"
},
"power": {
"name": "Strom"
},
"powerful_purify": {
"name": "Powerful Purification"
},
"prompt_tone": {
"name": "Prompt Tone"
},
"screen_display": {
"name": "Screen Display"
},
"screen_display_alternate": {
"name": "Screen Display Alternate"
},
"sensor_light": {
"name": "Sensor Light"
},
"silent_mode": {
"name": "Silent Mode"
},
"sleep_mode": {
"name": "Sleep Mode"
},
"smart_eye": {
"name": "Smart Eye"
},
"smart_volume": {
"name": "Smart Volume"
},
"smelly_sensor": {
"name": "Geruchssensor"
},
"standby": {
"name": "Standby"
},
"start": {
"name": "Start"
},
"storage": {
"name": "Storage"
},
"swing": {
"name": "swing"
},
"swing_horizontal": {
"name": "Swing Horizontal"
},
"swing_vertical": {
"name": "Swing Vertical"
},
"tbh": {
"name": "TBH"
},
"variable_heating": {
"name": "Variable Heating"
},
"ventilation": {
"name": "Lüftung"
},
"whole_tank_heating": {
"name": "Whole Tank Heating"
},
"zero_cold_pulse": {
"name": "Zero Cold Water (Pulse)"
},
"zero_cold_water": {
"name": "Zero Cold Water"
},
"zone1_curve": {
"name": "Zone1 Curve"
},
"zone1_power": {
"name": "Zone1 Power"
},
"zone2_curve": {
"name": "Zone2 Curve"
},
"zone2_power": {
"name": "Zone2 Power"
}
},
"water_heater": {
"domestic_hot_water": {
"name": "Domestic hot water"
},
"bathing": {
"name": "Bathing"
},
"heating": {
"name": "Heating"
}
}
},
"options": {
"step": {
"init": {
"data": {
"ip_address": "IP-Adresse",
"refresh_interval": "Aktualisierungsintervall (0 bedeutet, dass nicht aktiv aktualisiert wird)",
"sensors": "Zusätzliche Sensoren",
"switches": "Zusätzliche Schalter",
"customize": "Anpassen"
},
"title": "Konfiguration"
}
},
"abort": {
"account_option": "Das Konto unterstützt diesen Vorgang nicht.\nKlicken Sie auf \"GERÄT HINZUFÜGEN\" um ein neues Gerät hinzuzufügen."
}
},
"services": {
"set_attribute": {
"name": "Attribut setzen",
"description": "Einstellen des Attributwerts des Geräts",
"fields": {
"device_id": {
"name": "Geräte-ID",
"description": "Die Geräte-ID des Geräts"
},
"attribute": {
"name": "Attribut",
"description": "Der Name des Attributs, das gesetzt werden soll"
},
"value": {
"name": "Wert",
"description": "Der Attributwert, der gesetzt werden soll"
}
}
},
"send_command": {
"name": "Befehl an Gerät senden",
"description": "Senden eines Befehls an das Gerät",
"fields": {
"device_id": {
"name": "Geräte-ID",
"description": "Die Geräte-ID des Geräts"
},
"cmd_type": {
"name": "Befehlstyp",
"description": "Die Art des Befehls, sollte 3 abfragen) oder 2 setzen) sein"
},
"cmd_body": {
"name": "Befehl",
"description": "Der Hauptteil des Befehls (ohne den MSmart-Protokollkopf und die Prüfsumme am Ende)"
}
}
}
}
}
@@ -0,0 +1,834 @@
{
"config": {
"error": {
"preset_account": "Failed to login with preset account, please report this issue",
"login_failed": "Failed to login, account or password was wrong",
"no_devices": "No new available appliances found on the network",
"device_exist": "Appliance is already configured",
"config_incorrect": "The configuration is incorrect",
"connect_error": "Can't connect to appliance",
"invalid_token": "Token or Key in an incorrect format"
},
"step": {
"user": {
"data": {
"way": "Adding appliances"
},
"description": "Choose the way to add a appliance",
"title": "Add new appliance"
},
"login": {
"data": {
"account": "Account",
"password": "Password"
},
"description": "Login and storage your Midea account only for getting the appliance info.\nYou can remove this configuration after all appliance configured.",
"title": "Login"
},
"discovery": {
"description": "IP address for device, enter \"auto\" to discover automatically\nYou can also use an IP address to search within a specified network, such as \"192.168.1.255\"",
"title": "Search",
"data": {
"ip_address": "IP address"
}
},
"list": {
"description": "{table}",
"title": "Appliances"
},
"auto": {
"data": {
"device": "Appliances"
},
"description": "Choose a appliance to add",
"title": "New appliance found"
},
"manually": {
"data": {
"name": "Name (e.g. Living room AC)",
"device_id": "Appliance code",
"type": "Type",
"ip_address": "IP address",
"port": "Port",
"model": "Model",
"subtype": "Subtype",
"protocol": "Protocol",
"token": "Token",
"key": "Key"
},
"description": "Configuration of appliance",
"title": "New appliance"
}
}
},
"entity": {
"binary_sensor": {
"bar_door": {
"name": "Bar Door"
},
"bar_door_overtime": {
"name": "Bar Door Overtime"
},
"bathing_working": {
"name": "Bathing Working Status"
},
"bottom_compartment_cooling": {
"name": "Bottom Compartment Cooling"
},
"bottom_compartment_door": {
"name": "Bottom Compartment Door"
},
"bottom_compartment_preheating": {
"name": "Bottom Compartment Preheating"
},
"burning_state": {
"name": "Burning State"
},
"cleaning_reminder": {
"name": "Cleaning Reminder"
},
"compressor_status": {
"name": "Compressor Status"
},
"cooking": {
"name": "Cooking"
},
"filter_change_reminder": {
"name": "Filter Change Reminder"
},
"filter_cleaning_reminder": {
"name": "Filter Cleaning Reminder"
},
"finished": {
"name": "Finished"
},
"flex_zone_door": {
"name": "Flex Door"
},
"flex_zone_door_overtime": {
"name": "Flex Zone Door"
},
"freezer_door": {
"name": "Freezer Door"
},
"freezer_door_overtime": {
"name": "Freezer Door Overtime"
},
"full_dust": {
"name": "Full of Dust"
},
"heating": {
"name": "Heating"
},
"heating_working": {
"name": "Heating Working Status"
},
"keep_warm": {
"name": "Keep Warm"
},
"lid_status": {
"name": "Lid Status"
},
"middle_compartment_cooling": {
"name": "Middle Compartment Cooling"
},
"middle_compartment_door": {
"name": "Middle Compartment Door"
},
"middle_compartment_preheating": {
"name": "Middle Compartment Preheating"
},
"top_elec_heat": {
"name": "Top Electric Heat"
},
"bottom_elec_heat": {
"name": "Bottom Electric Heat"
},
"mute_effect": {
"name": "Mute Effect"
},
"mute_status": {
"name": "Mute Status"
},
"multi_terminal": {
"name": "Multi Terminal"
},
"oilcup_full": {
"name": "Oil-cup Full"
},
"protection": {
"name": "Protection"
},
"refrigerator_door": {
"name": "Refrigerator Door"
},
"refrigerator_door_overtime": {
"name": "Refrigerator Door Overtime"
},
"rinse_aid": {
"name": "Rinse Aid Shortage"
},
"salt": {
"name": "Salt Shortage"
},
"seat_status": {
"name": "Seat Status"
},
"smart_grid": {
"name": "Smart Grid"
},
"status_dhw": {
"name": "DHW status"
},
"status_heating": {
"name": "Heating status"
},
"status_ibh": {
"name": "IBH status"
},
"status_tbh": {
"name": "TBH status"
},
"tank_ejected": {
"name": "Tank Ejected"
},
"tank_full": {
"name": "Tank status"
},
"top_compartment_cooling": {
"name": "Top Compartment Cooling"
},
"top_compartment_door": {
"name": "Top Compartment Door"
},
"top_compartment_preheating": {
"name": "Top Compartment Preheating"
},
"water_change_reminder": {
"name": "Water Change Reminder"
},
"water_shortage": {
"name": "Water Shortage"
},
"with_pressure": {
"name": "With Pressure"
},
"zone1_room_temp_mode": {
"name": "Zone1 Room-temperature Mode"
},
"zone1_water_temp_mode": {
"name": "Zone1 Water-temperature Mode"
},
"zone2_room_temp_mode": {
"name": "Zone2 Room-temperature Mode"
},
"microcrystal_fresh": {
"name": "Microcrystal Fresh"
},
"electronic_smell": {
"name": "Deodorizing sterilizing"
}
},
"climate": {
"climate_zone1": {
"name": "Zone1 Thermostat"
},
"climate_zone2": {
"name": "Zone2 Thermostat"
},
"climate_key": {
"state_attributes": {
"fan_mode": {
"state": {
"silent": "Silent",
"full": "Full"
}
}
}
}
},
"fan": {
"fresh_air": {
"name": "Fresh Air"
}
},
"lock": {
"child_lock": {
"name": "Child Lock"
}
},
"number": {
"dry_level": {
"name": "Dry Level"
},
"heating_level": {
"name": "Heating Level"
},
"seat_temp_level": {
"name": "Seat Temperature Level"
},
"water_temp_level": {
"name": "Water Temperature Level"
},
"fan_speed_percent": {
"name": "Fan Speed Percent"
}
},
"select": {
"detect_mode": {
"name": "Detect Mode"
},
"direction": {
"name": "Direction"
},
"fan_speed": {
"name": "Fan Speed"
},
"mode": {
"name": "Mode"
},
"oscillation_angle": {
"name": "Oscillation Angle"
},
"oscillation_mode": {
"name": "Oscillation Mode"
},
"screen_display": {
"name": "Screen Display"
},
"silent_level": {
"name": "Silent Level"
},
"tilting_angle": {
"name": "Tilting Angle"
},
"water_level_set": {
"name": "Water Level Setting"
},
"wind_lr_angle": {
"name": "Airflow Horizontal",
"state": {
"off": "Off",
"left": "Left",
"left-mid": "Left-Mid",
"middle": "Middle",
"right-mid": "Right-Mid",
"right": "Right"
}
},
"wind_ud_angle": {
"name": "Airflow Vertical",
"state": {
"off": "Off",
"up": "Up",
"up-mid": "Up-Mid",
"middle": "Middle",
"down-mid": "Down-Mid",
"down": "Down"
}
}
},
"sensor": {
"bathing_leaving_temperature": {
"name": "Bathing Leaving Water Temperature"
},
"bottom_compartment_remaining": {
"name": "Bottom Compartment Remaining"
},
"bottom_compartment_status": {
"name": "Bottom Compartment Status"
},
"bottom_compartment_temperature": {
"name": "Bottom Compartment Temperature"
},
"bottom_temperature": {
"name": "Bottom Temperature"
},
"bright": {
"name": "Bright Level"
},
"compressor_temperature": {
"name": "Compressor Temperature"
},
"condenser_temperature": {
"name": "Condenser Temperature"
},
"current_energy_consumption": {
"name": "Current Energy Consumption"
},
"current_temperature": {
"name": "Current Temperature"
},
"dehydration_speed": {
"name": "Dehydration speed"
},
"dehydration_time": {
"name": "Dehydration time"
},
"dehydration_time_value": {
"name": "Dehydration time value"
},
"wash_time_value": {
"name": "Wash time value"
},
"stains": {
"name": "Stains"
},
"dirty_degree": {
"name": "Dirty degree"
},
"detergent": {
"name": "Detergent"
},
"intensity": {
"name": "Intensity"
},
"dryness_level": {
"name": "Dryness Level"
},
"dry_temperature": {
"name": "Dry Temperature"
},
"door_warn": {
"name": "Door Warn"
},
"ai_switch": {
"name": "AI Switch"
},
"material": {
"name": "Material"
},
"water_box": {
"name": "Water Box"
},
"energy_consumption": {
"name": "Energy Consumption"
},
"error_code": {
"name": "Error Code"
},
"fan_level": {
"name": "Fan level"
},
"filter_life": {
"name": "Filter Life"
},
"filter1_days": {
"name": "Filter1 Available Days"
},
"filter1_life": {
"name": "Filter1 Life Level"
},
"filter2_days": {
"name": "Filter2 Available Days"
},
"filter2_life": {
"name": "Filter2 Life Level"
},
"filter3_days": {
"name": "Filter3 Available Days"
},
"filter3_life": {
"name": "Filter3 Life Level"
},
"flex_zone_actual_temp": {
"name": "Flex Zone Actual Temperature"
},
"flex_zone_setting_temp": {
"name": "Flex Zone Setting Temperature"
},
"freezer_actual_temp": {
"name": "Freezer Actual Temperature"
},
"freezer_setting_temp": {
"name": "Freezer Setting Temperature"
},
"hcho": {
"name": "Methanal"
},
"heating_leaving_temperature": {
"name": "Heating Leaving Water Temperature"
},
"heating_power": {
"name": "Heating Power"
},
"heating_time_remaining": {
"name": "Heating Time Remaining"
},
"in_tds": {
"name": "In TDS"
},
"indoor_humidity": {
"name": "Indoor Humidity"
},
"indoor_temperature": {
"name": "Indoor Temperature"
},
"keep_warm_remaining": {
"name": "Keep Warm Remaining"
},
"keep_warm_time": {
"name": "Keep Warm Time"
},
"middle_compartment_remaining": {
"name": "Middle Compartment Remaining"
},
"middle_compartment_status": {
"name": "Middle Compartment Status"
},
"middle_compartment_temperature": {
"name": "Middle Compartment Temperature"
},
"mode": {
"name": "Mode"
},
"out_tds": {
"name": "Out TDS"
},
"outdoor_temperature": {
"name": "Outdoor Temperature"
},
"program": {
"name": "Program"
},
"progress": {
"name": "Progress"
},
"realtime_power": {
"name": "Realtime Power"
},
"refrigerator_actual_temp": {
"name": "Refrigerator Actual Temperature"
},
"refrigerator_setting_temp": {
"name": "Refrigerator Setting Temperature"
},
"right_flex_zone_actual_temp": {
"name": "Right Flex Zone Actual Temperature"
},
"right_flex_zone_setting_temp": {
"name": "Right Flex Zone Setting Temperature"
},
"rinse_count": {
"name": "Rinse count"
},
"rinse_level": {
"name": "Rinse level"
},
"seat_temperature": {
"name": "Seat Temperature"
},
"soak_time": {
"name": "Soak time"
},
"softener": {
"name": "Softener"
},
"softwater": {
"name": "Softwater Level"
},
"status": {
"name": "Status"
},
"storage_remaining": {
"name": "Storage Remaining"
},
"tank": {
"name": "Tank"
},
"tank_actual_temperature": {
"name": "Tank Actual Temperature"
},
"target_temperature": {
"name": "Target Temperature"
},
"time_remaining": {
"name": "Time Remaining"
},
"top_compartment_remaining": {
"name": "Top Compartment Remaining"
},
"top_compartment_status": {
"name": "Top Compartment Status"
},
"top_compartment_temperature": {
"name": "Top Compartment Temperature"
},
"top_temperature": {
"name": "Top Temperature"
},
"total_energy_consumption": {
"name": "Total Energy Consumption"
},
"total_produced_energy": {
"name": "Total produced energy"
},
"tvoc": {
"name": "TVOC"
},
"wash_strength": {
"name": "Wash strength"
},
"wash_time": {
"name": "Wash time"
},
"water_consumption": {
"name": "Water Consumption"
},
"water_temperature": {
"name": "Water Temperature"
},
"water_level": {
"name": "Water Level"
},
"wind": {
"name": "Wind"
},
"typeinfo": {
"name": "Type Info"
},
"elec_heat": {
"name": "Electric Heat"
},
"water_pump": {
"name": "Water Pump"
},
"four_way": {
"name": "Four Way Valve"
},
"back_water": {
"name": "Back Water"
},
"sterilize": {
"name": "Sterilize"
},
"working_time": {
"name": "Working Time"
},
"humidity": {
"name": "Humidity"
},
"variable_mode": {
"name": "Variable Mode"
}
},
"switch": {
"anion": {
"name": "Anion"
},
"aux_heating": {
"name": "Aux Heating"
},
"boost_mode": {
"name": "Boost Mode"
},
"breezeless": {
"name": "Breezeless"
},
"comfort_mode": {
"name": "Comfort Mode"
},
"dhw_power": {
"name": "DHW Power"
},
"disinfect": {
"name": "Disinfect"
},
"dry": {
"name": "Dry"
},
"cold_water_single": {
"name": "Cold Water Single"
},
"cold_water_dot": {
"name": "Cold Water Dot"
},
"eco_mode": {
"name": "ECO Mode"
},
"fast_dhw": {
"name": "Fast DHW"
},
"foam_shield": {
"name": "Foam Shield"
},
"frost_protect": {
"name": "Frost Protect"
},
"heating_power": {
"name": "Heating Power"
},
"indirect_wind": {
"name": "Indirect Wind"
},
"light": {
"name": "Light"
},
"link_to_ac": {
"name": "Link to AC"
},
"main_light": {
"name": "Main Light"
},
"main_power": {
"name": "Main Power"
},
"natural_wind": {
"name": "Natural Wind"
},
"night_light": {
"name": "Night Light"
},
"oscillate": {
"name": "Oscillate"
},
"power": {
"name": "Power"
},
"powerful_purify": {
"name": "Powerful Purification"
},
"prompt_tone": {
"name": "Prompt Tone"
},
"screen_display": {
"name": "Screen Display"
},
"screen_display_alternate": {
"name": "Screen Display Alternate"
},
"sensor_light": {
"name": "Sensor Light"
},
"silent_mode": {
"name": "Silent Mode"
},
"sleep_mode": {
"name": "Sleep Mode"
},
"smart_eye": {
"name": "Smart Eye"
},
"smart_volume": {
"name": "Smart Volume"
},
"smelly_sensor": {
"name": "Smelly Sensor"
},
"standby": {
"name": "Standby"
},
"start": {
"name": "Start"
},
"storage": {
"name": "Storage"
},
"swing": {
"name": "Swing"
},
"swing_horizontal": {
"name": "Swing Horizontal"
},
"swing_vertical": {
"name": "Swing Vertical"
},
"tbh": {
"name": "TBH"
},
"variable_heating": {
"name": "Variable Heating"
},
"ventilation": {
"name": "Ventilation"
},
"whole_tank_heating": {
"name": "Whole Tank Heating"
},
"zero_cold_pulse": {
"name": "Zero Cold Water (Pulse)"
},
"zero_cold_water": {
"name": "Zero Cold Water"
},
"zone1_curve": {
"name": "Zone1 Curve"
},
"zone1_power": {
"name": "Zone1 Power"
},
"zone2_curve": {
"name": "Zone2 Curve"
},
"zone2_power": {
"name": "Zone2 Power"
}
},
"water_heater": {
"domestic_hot_water": {
"name": "Domestic hot water"
},
"bathing": {
"name": "Bathing"
},
"heating": {
"name": "Heating"
}
}
},
"options": {
"step": {
"init": {
"data": {
"ip_address": "IP address",
"refresh_interval": "Refresh interval (0 means not refreshing actively)",
"sensors": "Extra sensors",
"switches": "Extra controls",
"customize": "Customize"
},
"title": "Configure"
}
},
"abort": {
"account_option": "The account does not supports this operation.\nClick \"ADD DEVICE\" to add a new device."
}
},
"services": {
"set_attribute": {
"name": "Set attribute",
"description": "Set the attribute value of device",
"fields": {
"device_id": {
"name": "Appliance code",
"description": "The appliance code (Device ID) of appliance"
},
"attribute": {
"name": "Attribute",
"description": "The attribute name want to set"
},
"value": {
"name": "Value",
"description": "The attribute value want to set"
}
}
},
"send_command": {
"name": "Customize command",
"description": "Send a customize command to device",
"fields": {
"device_id": {
"name": "Appliance code",
"description": "The appliance code (Device ID) of appliance"
},
"cmd_type": {
"name": "Command type",
"description": "The type of commandshould be 3query) or 2set)"
},
"cmd_body": {
"name": "Command body",
"description": "The body of command, (not include MSmart protocol head and checksum at the end)"
}
}
}
}
}
@@ -0,0 +1,795 @@
{
"config": {
"error": {
"preset_account": "Fallo al iniciar sesión con la cuenta preestablecida, por favor reporte este problema",
"login_failed": "Fallo al iniciar sesión, la cuenta o la contraseña son incorrectas",
"no_devices": "No se encontraron nuevos electrodomésticos disponibles en la red",
"device_exist": "El electrodoméstico ya está configurado",
"config_incorrect": "La configuración es incorrecta",
"connect_error": "No se puede conectar con el electrodoméstico",
"invalid_token": "Identificador o clave en un formato incorrecto"
},
"step": {
"user": {
"data": {
"way": "Añadir electrodomésticos"
},
"description": "Elija la forma de añadir un electrodoméstico",
"title": "Añadir nuevo electrodoméstico"
},
"login": {
"data": {
"account": "Cuenta",
"password": "Contraseña"
},
"description": "Inicie sesión y guarde su cuenta Midea solo para obtener la información del electrodoméstico.\nPuede eliminar esta configuración después de haber configurado todos los dispositivos.",
"title": "Iniciar sesión"
},
"discovery": {
"description": "Dirección IP del dispositivo, escriba \"auto\" para descubrir automáticamente.\nTambién puede usar una dirección IP para buscar dentro de una red específica, como \"192.168.1.255\"",
"title": "Buscar",
"data": {
"ip_address": "Dirección IP"
}
},
"list": {
"description": "{table}",
"title": "Electrodomésticos"
},
"auto": {
"data": {
"device": "Electrodomésticos"
},
"description": "Elija un electrodoméstico para añadir",
"title": "Nuevo electrodoméstico encontrado"
},
"manually": {
"data": {
"name": "Nombre (ej. AA Salón)",
"device_id": "Código del electrodoméstico",
"type": "Tipo",
"ip_address": "Dirección IP",
"port": "Puerto",
"model": "Modelo",
"subtype": "Subtipo",
"protocol": "Protocolo",
"token": "Token",
"key": "Clave"
},
"description": "Configuración del electrodoméstico",
"title": "Nuevo electrodoméstico"
}
}
},
"entity": {
"binary_sensor": {
"bar_door": {
"name": "Puerta del bar"
},
"bar_door_overtime": {
"name": "Puerta del bar abierta demasiado tiempo"
},
"bathing_working": {
"name": "Estado de funcionamiento del baño"
},
"bottom_compartment_cooling": {
"name": "Enfriamiento del compartimento inferior"
},
"bottom_compartment_door": {
"name": "Puerta del compartimento inferior"
},
"bottom_compartment_preheating": {
"name": "Precalentamiento del compartimento inferior"
},
"burning_state": {
"name": "Estado de combustión"
},
"cleaning_reminder": {
"name": "Recordatorio de limpieza"
},
"compressor_status": {
"name": "Estado del compresor"
},
"cooking": {
"name": "Cocinando"
},
"filter_change_reminder": {
"name": "Recordatorio de cambio de filtro"
},
"filter_cleaning_reminder": {
"name": "Recordatorio de limpieza de filtro"
},
"finished": {
"name": "Terminado"
},
"flex_zone_door": {
"name": "Puerta de zona flexible"
},
"flex_zone_door_overtime": {
"name": "Puerta de zona flexible abierta demasiado tiempo"
},
"freezer_door": {
"name": "Puerta del congelador"
},
"freezer_door_overtime": {
"name": "Puerta del congelador abierta demasiado tiempo"
},
"full_dust": {
"name": "Lleno de polvo"
},
"heating": {
"name": "Calentando"
},
"heating_working": {
"name": "Estado de funcionamiento de calefacción"
},
"keep_warm": {
"name": "Mantener caliente"
},
"lid_status": {
"name": "Estado de la tapa"
},
"middle_compartment_cooling": {
"name": "Enfriamiento del compartimento medio"
},
"middle_compartment_door": {
"name": "Puerta del compartimento medio"
},
"middle_compartment_preheating": {
"name": "Precalentamiento del compartimento medio"
},
"oilcup_full": {
"name": "Depósito de aceite lleno"
},
"protection": {
"name": "Protección"
},
"refrigerator_door": {
"name": "Puerta del frigorífico"
},
"refrigerator_door_overtime": {
"name": "Puerta del frigorífico abierta demasiado tiempo"
},
"rinse_aid": {
"name": "Escasez de abrillantador"
},
"salt": {
"name": "Escasez de sal"
},
"seat_status": {
"name": "Estado del asiento"
},
"status_dhw": {
"name": "Estado ACS"
},
"status_heating": {
"name": "Estado calefacción"
},
"status_ibh": {
"name": "Estado IBH"
},
"status_tbh": {
"name": "Estado TBH"
},
"tank_ejected": {
"name": "Depósito extraído"
},
"tank_full": {
"name": "Estado del depósito"
},
"top_compartment_cooling": {
"name": "Enfriamiento del compartimento superior"
},
"top_compartment_door": {
"name": "Puerta del compartimento superior"
},
"top_compartment_preheating": {
"name": "Precalentamiento del compartimento superior"
},
"water_change_reminder": {
"name": "Recordatorio de cambio de agua"
},
"water_shortage": {
"name": "Escasez de agua"
},
"with_pressure": {
"name": "Con presión"
},
"zone1_room_temp_mode": {
"name": "Zona 1 Modo temperatura ambiente"
},
"zone1_water_temp_mode": {
"name": "Zona 1 Modo temperatura del agua"
},
"zone2_room_temp_mode": {
"name": "Zona 2 Modo temperatura ambiente"
},
"microcrystal_fresh": {
"name": "Frescura microcristalina"
},
"electronic_smell": {
"name": "Esterilización desodorizante"
}
},
"climate": {
"climate_zone1": {
"name": "Termostato Zona 1"
},
"climate_zone2": {
"name": "Termostato Zona 2"
},
"climate_key": {
"state_attributes": {
"fan_mode": {
"state": {
"silent": "Silencioso",
"full": "Máximo"
}
}
}
}
},
"fan": {
"fresh_air": {
"name": "Aire fresco"
}
},
"lock": {
"child_lock": {
"name": "Bloqueo infantil"
}
},
"number": {
"dry_level": {
"name": "Nivel de secado"
},
"heating_level": {
"name": "Nivel de calefacción"
},
"seat_temp_level": {
"name": "Nivel de temperatura del asiento"
},
"water_temp_level": {
"name": "Nivel de temperatura del agua"
},
"fan_speed_percent": {
"name": "Porcentaje de velocidad del ventilador"
}
},
"select": {
"detect_mode": {
"name": "Modo de detección"
},
"direction": {
"name": "Dirección"
},
"fan_speed": {
"name": "Velocidad del ventilador"
},
"mode": {
"name": "Modo"
},
"oscillation_angle": {
"name": "Ángulo de oscilación"
},
"oscillation_mode": {
"name": "Modo de oscilación"
},
"screen_display": {
"name": "Visualización de pantalla"
},
"silent_level": {
"name": "Nivel de silencio"
},
"tilting_angle": {
"name": "Ángulo de inclinación"
},
"water_level_set": {
"name": "Ajuste de nivel de agua"
},
"wind_lr_angle": {
"name": "Flujo de aire horizontal",
"state": {
"off": "Apagado",
"left": "Izquierda",
"left-mid": "Izquierda-Medio",
"middle": "Medio",
"right-mid": "Derecha-Medio",
"right": "Derecha"
}
},
"wind_ud_angle": {
"name": "Flujo de aire vertical",
"state": {
"off": "Apagado",
"up": "Arriba",
"up-mid": "Arriba-Medio",
"middle": "Medio",
"down-mid": "Abajo-Medio",
"down": "Abajo"
}
}
},
"sensor": {
"bathing_leaving_temperature": {
"name": "Temperatura de salida del agua de baño"
},
"bottom_compartment_remaining": {
"name": "Tiempo restante compartimento inferior"
},
"bottom_compartment_status": {
"name": "Estado del compartimento inferior"
},
"bottom_compartment_temperature": {
"name": "Temperatura del compartimento inferior"
},
"bottom_temperature": {
"name": "Temperatura inferior"
},
"bright": {
"name": "Nivel de brillo"
},
"compressor_temperature": {
"name": "Temperatura del compresor"
},
"condenser_temperature": {
"name": "Temperatura del condensador"
},
"current_energy_consumption": {
"name": "Consumo de energía actual"
},
"current_temperature": {
"name": "Temperatura actual"
},
"dehydration_speed": {
"name": "Velocidad de centrifugado"
},
"dehydration_time": {
"name": "Tiempo de centrifugado"
},
"dehydration_time_value": {
"name": "Valor del tiempo de centrifugado"
},
"wash_time_value": {
"name": "Valor del tiempo de lavado"
},
"stains": {
"name": "Manchas"
},
"dirty_degree": {
"name": "Grado de suciedad"
},
"detergent": {
"name": "Detergente"
},
"intensity": {
"name": "Intensidad"
},
"dryness_level": {
"name": "Nivel de sequedad"
},
"dry_temperature": {
"name": "Temperatura de secado"
},
"door_warn": {
"name": "Advertencia de puerta"
},
"ai_switch": {
"name": "Interruptor IA"
},
"material": {
"name": "Material"
},
"water_box": {
"name": "Caja de agua"
},
"energy_consumption": {
"name": "Consumo de energía"
},
"error_code": {
"name": "Código de error"
},
"fan_level": {
"name": "Nivel del ventilador"
},
"filter_life": {
"name": "Vida útil del filtro"
},
"filter1_days": {
"name": "Días disponibles Filtro 1"
},
"filter1_life": {
"name": "Nivel de vida Filtro 1"
},
"filter2_days": {
"name": "Días disponibles Filtro 2"
},
"filter2_life": {
"name": "Nivel de vida Filtro 2"
},
"filter3_days": {
"name": "Días disponibles Filtro 3"
},
"filter3_life": {
"name": "Nivel de vida Filtro 3"
},
"flex_zone_actual_temp": {
"name": "Temperatura real de zona flexible"
},
"flex_zone_setting_temp": {
"name": "Temperatura establecida de zona flexible"
},
"freezer_actual_temp": {
"name": "Temperatura real del congelador"
},
"freezer_setting_temp": {
"name": "Temperatura establecida del congelador"
},
"hcho": {
"name": "Formaldehído"
},
"heating_leaving_temperature": {
"name": "Temperatura de salida del agua de calefacción"
},
"heating_power": {
"name": "Potencia de calefacción"
},
"heating_time_remaining": {
"name": "Tiempo restante de calefacción"
},
"in_tds": {
"name": "TDS de entrada"
},
"indoor_humidity": {
"name": "Humedad interior"
},
"indoor_temperature": {
"name": "Temperatura interior"
},
"keep_warm_remaining": {
"name": "Tiempo restante de mantener caliente"
},
"keep_warm_time": {
"name": "Tiempo de mantener caliente"
},
"middle_compartment_remaining": {
"name": "Tiempo restante compartimento medio"
},
"middle_compartment_status": {
"name": "Estado del compartimento medio"
},
"middle_compartment_temperature": {
"name": "Temperatura del compartimento medio"
},
"mode": {
"name": "Modo"
},
"out_tds": {
"name": "TDS de salida"
},
"outdoor_temperature": {
"name": "Temperatura exterior"
},
"program": {
"name": "Programa"
},
"progress": {
"name": "Progreso"
},
"realtime_power": {
"name": "Potencia en tiempo real"
},
"refrigerator_actual_temp": {
"name": "Temperatura real del frigorífico"
},
"refrigerator_setting_temp": {
"name": "Temperatura establecida del frigorífico"
},
"right_flex_zone_actual_temp": {
"name": "Temperatura real de zona flexible derecha"
},
"right_flex_zone_setting_temp": {
"name": "Temperatura establecida de zona flexible derecha"
},
"rinse_count": {
"name": "Recuento de aclarados"
},
"rinse_level": {
"name": "Nivel de aclarado"
},
"seat_temperature": {
"name": "Temperatura del asiento"
},
"soak_time": {
"name": "Tiempo de remojo"
},
"softener": {
"name": "Suavizante"
},
"softwater": {
"name": "Nivel de agua blanda"
},
"status": {
"name": "Estado"
},
"storage_remaining": {
"name": "Almacenamiento restante"
},
"tank": {
"name": "Depósito"
},
"tank_actual_temperature": {
"name": "Temperatura real del depósito"
},
"target_temperature": {
"name": "Temperatura objetivo"
},
"time_remaining": {
"name": "Tiempo restante"
},
"top_compartment_remaining": {
"name": "Tiempo restante compartimento superior"
},
"top_compartment_status": {
"name": "Estado del compartimento superior"
},
"top_compartment_temperature": {
"name": "Temperatura del compartimento superior"
},
"top_temperature": {
"name": "Temperatura superior"
},
"total_energy_consumption": {
"name": "Consumo total de energía"
},
"total_produced_energy": {
"name": "Energía total producida"
},
"tvoc": {
"name": "COVT"
},
"wash_strength": {
"name": "Potencia de lavado"
},
"wash_time": {
"name": "Tiempo de lavado"
},
"water_consumption": {
"name": "Consumo de agua"
},
"water_temperature": {
"name": "Temperatura del agua"
},
"water_level": {
"name": "Nivel de agua"
},
"working_time": {
"name": "Tiempo de funcionamiento"
},
"humidity": {
"name": "Humedad"
},
"variable_mode": {
"name": "Modo variable"
}
},
"switch": {
"anion": {
"name": "Anión"
},
"aux_heating": {
"name": "Calefacción auxiliar"
},
"boost_mode": {
"name": "Modo potente"
},
"breezeless": {
"name": "Sin corrientes"
},
"comfort_mode": {
"name": "Modo confort"
},
"dhw_power": {
"name": "Potencia ACS"
},
"disinfect": {
"name": "Desinfectar"
},
"dry": {
"name": "Secado"
},
"cold_water_single": {
"name": "Agua fría individual"
},
"cold_water_dot": {
"name": "Agua fría punto"
},
"eco_mode": {
"name": "Modo ECO"
},
"fast_dhw": {
"name": "ACS Rápido"
},
"foam_shield": {
"name": "Escudo de espuma"
},
"frost_protect": {
"name": "Protección contra heladas"
},
"heating_power": {
"name": "Potencia de calefacción"
},
"indirect_wind": {
"name": "Viento indirecto"
},
"light": {
"name": "Luz"
},
"link_to_ac": {
"name": "Vincular a AA"
},
"main_light": {
"name": "Luz principal"
},
"main_power": {
"name": "Alimentación principal"
},
"natural_wind": {
"name": "Viento natural"
},
"night_light": {
"name": "Luz nocturna"
},
"oscillate": {
"name": "Oscilar"
},
"power": {
"name": "Encendido"
},
"powerful_purify": {
"name": "Purificación potente"
},
"prompt_tone": {
"name": "Tono de aviso"
},
"screen_display": {
"name": "Visualización de pantalla"
},
"screen_display_alternate": {
"name": "Pantalla alterna"
},
"sensor_light": {
"name": "Luz con sensor"
},
"silent_mode": {
"name": "Modo silencioso"
},
"sleep_mode": {
"name": "Modo sueño"
},
"smart_eye": {
"name": "Ojo inteligente"
},
"smart_volume": {
"name": "Volumen inteligente"
},
"smelly_sensor": {
"name": "Sensor de olores"
},
"standby": {
"name": "En espera"
},
"start": {
"name": "Iniciar"
},
"storage": {
"name": "Almacenamiento"
},
"swing": {
"name": "Oscilación"
},
"swing_horizontal": {
"name": "Oscilación horizontal"
},
"swing_vertical": {
"name": "Oscilación vertical"
},
"tbh": {
"name": "TBH"
},
"variable_heating": {
"name": "Calefacción variable"
},
"ventilation": {
"name": "Ventilación"
},
"whole_tank_heating": {
"name": "Calentamiento de tanque completo"
},
"zero_cold_pulse": {
"name": "Cero agua fría (Pulso)"
},
"zero_cold_water": {
"name": "Cero agua fría"
},
"zone1_curve": {
"name": "Curva Zona 1"
},
"zone1_power": {
"name": "Potencia Zona 1"
},
"zone2_curve": {
"name": "Curva Zona 2"
},
"zone2_power": {
"name": "Potencia Zona 2"
}
},
"water_heater": {
"domestic_hot_water": {
"name": "Agua caliente sanitaria"
},
"bathing": {
"name": "Baño"
},
"heating": {
"name": "Calefacción"
}
}
},
"options": {
"step": {
"init": {
"data": {
"ip_address": "Dirección IP",
"refresh_interval": "Intervalo de actualización (0 significa no actualizar activamente)",
"sensors": "Sensores extra",
"switches": "Controles extra",
"customize": "Personalizar"
},
"title": "Configurar"
}
},
"abort": {
"account_option": "La cuenta no admite esta operación.\nHaga clic en \"AÑADIR DISPOSITIVO\" para agregar un nuevo dispositivo."
}
},
"services": {
"set_attribute": {
"name": "Establecer atributo",
"description": "Establecer el valor del atributo del dispositivo",
"fields": {
"device_id": {
"name": "Código del electrodoméstico",
"description": "El código (ID de dispositivo) del electrodoméstico"
},
"attribute": {
"name": "Atributo",
"description": "El nombre del atributo que desea establecer"
},
"value": {
"name": "Valor",
"description": "El valor del atributo que desea establecer"
}
}
},
"send_command": {
"name": "Comando personalizado",
"description": "Enviar un comando personalizado al dispositivo",
"fields": {
"device_id": {
"name": "Código del electrodoméstico",
"description": "El código (ID de dispositivo) del electrodoméstico"
},
"cmd_type": {
"name": "Tipo de comando",
"description": "El tipo de comando, debe ser 3 (consulta) o 2 (ajuste)"
},
"cmd_body": {
"name": "Cuerpo del comando",
"description": "El cuerpo del comando, (no incluye el encabezado del protocolo MSmart ni la suma de comprobación al final)"
}
}
}
}
}
@@ -0,0 +1,715 @@
{
"config": {
"error": {
"preset_account": "Failed to login with preset account, please report this issue",
"login_failed": "Failed to login, account or password was wrong",
"no_devices": "Aucun nouvel apparel trouvé sur le réseau",
"device_exist": "L'appareil est déjà configurée",
"config_incorrect": "La configuration est incorrecte",
"connect_error": "Impossible de se connecter à l'appareil",
"invalid_token": "Token ou Key dans un format incorrect"
},
"step": {
"user": {
"data": {
"way": "Ajout d'appareil"
},
"description": "Choisissez comment ajouter des appareils",
"title": "Ajouter un nouvel apparel"
},
"login": {
"data": {
"account": "Account",
"password": "Password"
},
"description": "Login and storage your Midea account only for getting the appliance info.\nYou can remove this configuration after all appliance configured.",
"title": "Login"
},
"discovery": {
"description": "Saisissez l'address IP de l'appareil, entrez \"auto\" pour découvrir automatiquement.\nVous pouvez également utiliser une address IP pour rechercher dans un réseau spécifié, comme \"192.168.1.255\".",
"title": "Recherche",
"data": {
"ip_address": "Address IP"
}
},
"list": {
"description": "{table}",
"title": "Apparel"
},
"auto": {
"data": {
"device": "Apparel"
},
"description": "Choisissez un apparel à ajouter",
"title": "Nouvel apparel trouvé"
},
"manually": {
"data": {
"name": "Nom (ex: Clim Salon)",
"device_id": "Code apparel",
"type": "Type",
"ip_address": "Address IP",
"port": "Port",
"model": "Modèle",
"subtype": "Soustype",
"protocol": "Protocole",
"token": "Token",
"key": "Key"
},
"description": "Configuration de l'appareil",
"title": "Nouvel apparel"
}
}
},
"entity": {
"binary_sensor": {
"bar_door": {
"name": "Bar Door"
},
"bar_door_overtime": {
"name": "Bar Door Overtime"
},
"bathing_working": {
"name": "Bathing Working Status"
},
"bottom_compartment_cooling": {
"name": "Bottom Compartment Cooling"
},
"bottom_compartment_door": {
"name": "Bottom Compartment Door"
},
"bottom_compartment_preheating": {
"name": "Bottom Compartment Preheating"
},
"burning_state": {
"name": "Burning State"
},
"cleaning_reminder": {
"name": "Cleaning Reminder"
},
"compressor_status": {
"name": "Compressor Status"
},
"cooking": {
"name": "Cooking"
},
"filter_change_reminder": {
"name": "Filter Change Reminder"
},
"filter_cleaning_reminder": {
"name": "Filter Cleaning Reminder"
},
"finished": {
"name": "Finished"
},
"flex_zone_door": {
"name": "Flex Door"
},
"flex_zone_door_overtime": {
"name": "Flex Zone Door"
},
"freezer_door": {
"name": "Freezer Door"
},
"freezer_door_overtime": {
"name": "Freezer Door Overtime"
},
"full_dust": {
"name": "Full of Dust"
},
"heating": {
"name": "Heating"
},
"heating_working": {
"name": "Heating Working Status"
},
"keep_warm": {
"name": "Keep Warm"
},
"lid_status": {
"name": "Lid Status"
},
"middle_compartment_cooling": {
"name": "Middle Compartment Cooling"
},
"middle_compartment_door": {
"name": "Middle Compartment Door"
},
"middle_compartment_preheating": {
"name": "Middle Compartment Preheating"
},
"oilcup_full": {
"name": "Oil-cup Full"
},
"protection": {
"name": "Protection"
},
"refrigerator_door": {
"name": "Refrigerator Door"
},
"refrigerator_door_overtime": {
"name": "Refrigerator Door Overtime"
},
"rinse_aid": {
"name": "Rinse Aid Shortage"
},
"salt": {
"name": "Salt Shortage"
},
"seat_status": {
"name": "Seat Status"
},
"status_dhw": {
"name": "DHW status"
},
"status_heating": {
"name": "Heating status"
},
"status_ibh": {
"name": "IBH status"
},
"status_tbh": {
"name": "TBH status"
},
"tank_ejected": {
"name": "Tank Ejected"
},
"tank_full": {
"name": "Tank status"
},
"top_compartment_cooling": {
"name": "Top Compartment Cooling"
},
"top_compartment_door": {
"name": "Top Compartment Door"
},
"top_compartment_preheating": {
"name": "Top Compartment Preheating"
},
"water_change_reminder": {
"name": "Water Change Reminder"
},
"water_shortage": {
"name": "Water Shortage"
},
"with_pressure": {
"name": "With Pressure"
},
"zone1_room_temp_mode": {
"name": "Zone1 Room-temperature Mode"
},
"zone1_water_temp_mode": {
"name": "Zone1 Water-temperature Mode"
},
"zone2_room_temp_mode": {
"name": "Zone2 Room-temperature Mode"
},
"zone2_water_temp_mode": {
"name": "Zone2 Water-temperature Mode"
}
},
"climate": {
"climate_zone1": {
"name": "Zone1 Thermostat"
},
"climate_zone2": {
"name": "Zone2 Thermostat"
}
},
"fan": {
"fresh_air": {
"name": "Fresh Air"
}
},
"lock": {
"child_lock": {
"name": "Child Lock"
}
},
"number": {
"dry_level": {
"name": "Dry Level"
},
"heating_level": {
"name": "Heating Level"
},
"seat_temp_level": {
"name": "Seat Temperature Level"
},
"water_temp_level": {
"name": "Water Temperature Level"
}
},
"select": {
"detect_mode": {
"name": "Detect Mode"
},
"direction": {
"name": "Direction"
},
"fan_speed": {
"name": "Fan Speed"
},
"mode": {
"name": "Mode"
},
"oscillation_angle": {
"name": "Oscillation Angle"
},
"oscillation_mode": {
"name": "Oscillation Mode"
},
"screen_display": {
"name": "Screen Display"
},
"silent_level": {
"name": "Silent Level"
},
"tilting_angle": {
"name": "Tilting Angle"
},
"water_level_set": {
"name": "Water Level Setting"
}
},
"sensor": {
"bathing_leaving_temperature": {
"name": "Bathing Leaving Water Temperature"
},
"bottom_compartment_remaining": {
"name": "Bottom Compartment Remaining"
},
"bottom_compartment_status": {
"name": "Bottom Compartment Status"
},
"bottom_compartment_temperature": {
"name": "Bottom Compartment Temperature"
},
"bottom_temperature": {
"name": "Bottom Temperature"
},
"bright": {
"name": "Bright Level"
},
"compressor_temperature": {
"name": "Compressor Temperature"
},
"condenser_temperature": {
"name": "Condenser Temperature"
},
"current_energy_consumption": {
"name": "Current Energy Consumption"
},
"current_temperature": {
"name": "Current Temperature"
},
"dehydration_speed": {
"name": "dehydration speed"
},
"dehydration_time": {
"name": "dehydration time"
},
"detergent": {
"name": "detergent"
},
"energy_consumption": {
"name": "Energy Consumption"
},
"error_code": {
"name": "Error Code"
},
"fan_level": {
"name": "Fan level"
},
"filter_life": {
"name": "Filter Life"
},
"filter1_days": {
"name": "Filter1 Available Days"
},
"filter1_life": {
"name": "Filter1 Life Level"
},
"filter2_days": {
"name": "Filter2 Available Days"
},
"filter2_life": {
"name": "Filter2 Life Level"
},
"filter3_days": {
"name": "Filter3 Available Days"
},
"filter3_life": {
"name": "Filter3 Life Level"
},
"flex_zone_actual_temp": {
"name": "Flex Zone Actual Temperature"
},
"flex_zone_setting_temp": {
"name": "Flex Zone Setting Temperature"
},
"freezer_actual_temp": {
"name": "Freezer Actual Temperature"
},
"freezer_setting_temp": {
"name": "Freezer Setting Temperature"
},
"hcho": {
"name": "Methanal"
},
"heating_leaving_temperature": {
"name": "Heating Leaving Water Temperature"
},
"heating_power": {
"name": "Heating Power"
},
"heating_time_remaining": {
"name": "Heating Time Remaining"
},
"in_tds": {
"name": "In TDS"
},
"indoor_humidity": {
"name": "Indoor Humidity"
},
"indoor_temperature": {
"name": "Indoor Temperature"
},
"keep_warm_remaining": {
"name": "Keep Warm Remaining"
},
"keep_warm_time": {
"name": "Keep Warm Time"
},
"middle_compartment_remaining": {
"name": "Middle Compartment Remaining"
},
"middle_compartment_status": {
"name": "Middle Compartment Status"
},
"middle_compartment_temperature": {
"name": "Middle Compartment Temperature"
},
"mode": {
"name": "Mode"
},
"out_tds": {
"name": "Out TDS"
},
"outdoor_temperature": {
"name": "Outdoor Temperature"
},
"program": {
"name": "Program"
},
"progress": {
"name": "Progress"
},
"realtime_power": {
"name": "Realtime Power"
},
"refrigerator_actual_temp": {
"name": "Refrigerator Actual Temperature"
},
"refrigerator_setting_temp": {
"name": "Refrigerator Setting Temperature"
},
"right_flex_zone_actual_temp": {
"name": "Right Flex Zone Actual Temperature"
},
"right_flex_zone_setting_temp": {
"name": "Right Flex Zone Setting Temperature"
},
"rinse_count": {
"name": "rinse count"
},
"rinse_level": {
"name": "rinse level"
},
"seat_temperature": {
"name": "Seat Temperature"
},
"soak_time": {
"name": "soak time"
},
"softener": {
"name": "softener"
},
"softwater": {
"name": "Softwater Level"
},
"status": {
"name": "Status"
},
"storage_remaining": {
"name": "Storage Remaining"
},
"tank": {
"name": "Tank"
},
"tank_actual_temperature": {
"name": "Tank Actual Temperature"
},
"target_temperature": {
"name": "Target Temperature"
},
"time_remaining": {
"name": "Time Remaining"
},
"top_compartment_remaining": {
"name": "Top Compartment Remaining"
},
"top_compartment_status": {
"name": "Top Compartment Status"
},
"top_compartment_temperature": {
"name": "Top Compartment Temperature"
},
"top_temperature": {
"name": "Top Temperature"
},
"total_energy_consumption": {
"name": "Total Energy Consumption"
},
"total_produced_energy": {
"name": "Total produced energy"
},
"tvoc": {
"name": "TVOC"
},
"wash_level": {
"name": "rinse count"
},
"wash_strength": {
"name": "wash strength"
},
"wash_time": {
"name": "wash time"
},
"water_consumption": {
"name": "Water Consumption"
},
"water_temperature": {
"name": "Water Temperature"
},
"water_level": {
"name": "Water Level"
},
"working_time": {
"name": "Working Time"
}
},
"switch": {
"anion": {
"name": "Anion"
},
"aux_heating": {
"name": "Aux Heating"
},
"boost_mode": {
"name": "Boost Mode"
},
"breezeless": {
"name": "Breezeless"
},
"comfort_mode": {
"name": "Comfort Mode"
},
"dhw_power": {
"name": "DHW Power"
},
"disinfect": {
"name": "Disinfect"
},
"dry": {
"name": "Dry"
},
"eco_mode": {
"name": "ECO Mode"
},
"fast_dhw": {
"name": "Fast DHW"
},
"foam_shield": {
"name": "Foam Shield"
},
"frost_protect": {
"name": "Frost Protect"
},
"heating_power": {
"name": "Heating Power"
},
"indirect_wind": {
"name": "Indirect Wind"
},
"light": {
"name": "Light"
},
"link_to_ac": {
"name": "Link to AC"
},
"main_light": {
"name": "Main Light"
},
"main_power": {
"name": "Main Power"
},
"natural_wind": {
"name": "Natural Wind"
},
"night_light": {
"name": "Night Light"
},
"oscillate": {
"name": "Oscillate"
},
"power": {
"name": "Power"
},
"powerful_purify": {
"name": "Powerful Purification"
},
"prompt_tone": {
"name": "Prompt Tone"
},
"screen_display": {
"name": "Screen Display"
},
"screen_display_alternate": {
"name": "Screen Display Alternate"
},
"sensor_light": {
"name": "Sensor Light"
},
"silent_mode": {
"name": "Silent Mode"
},
"sleep_mode": {
"name": "Sleep Mode"
},
"smart_eye": {
"name": "Smart Eye"
},
"smart_volume": {
"name": "Smart Volume"
},
"smelly_sensor": {
"name": "Smelly Sensor"
},
"standby": {
"name": "Standby"
},
"start": {
"name": "Start"
},
"storage": {
"name": "Storage"
},
"swing": {
"name": "swing"
},
"swing_horizontal": {
"name": "Swing Horizontal"
},
"swing_vertical": {
"name": "Swing Vertical"
},
"tbh": {
"name": "TBH"
},
"variable_heating": {
"name": "Variable Heating"
},
"ventilation": {
"name": "Ventilation"
},
"whole_tank_heating": {
"name": "Whole Tank Heating"
},
"zero_cold_pulse": {
"name": "Zero Cold Water (Pulse)"
},
"zero_cold_water": {
"name": "Zero Cold Water"
},
"zone1_curve": {
"name": "Zone1 Curve"
},
"zone1_power": {
"name": "Zone1 Power"
},
"zone2_curve": {
"name": "Zone2 Curve"
},
"zone2_power": {
"name": "Zone2 Power"
}
},
"water_heater": {
"domestic_hot_water": {
"name": "Domestic hot water"
},
"bathing": {
"name": "Bathing"
},
"heating": {
"name": "Heating"
}
}
},
"options": {
"step": {
"init": {
"data": {
"ip_address": "Address IP",
"refresh_interval": "Refresh interval (0 means not refreshing actively)",
"sensors": "Capteurs supplémentaire",
"switches": "Contrôles supplémentaire",
"customize": "Personnaliser"
},
"title": "Configurer"
}
},
"abort": {
"account_option": "The account does not supports this operation.\nClick \"ADD DEVICE\" to add a new device."
}
},
"services": {
"set_attribute": {
"name": "Définir l'attribut",
"description": "Définir la valeur d'attribut de l'appareil",
"fields": {
"device_id": {
"name": "Code apparel",
"description": "Code de l'appareil (ID de l'appareil)"
},
"attribute": {
"name": "Attribute",
"description": "Nom de l'attribut que vous souhaitez définir"
},
"value": {
"name": "Valeur",
"description": "Valeur d'attribut que vous souhaitez définir"
}
}
},
"send_command": {
"name": "Customize command",
"description": "Send a customize command to device",
"fields": {
"device_id": {
"name": "Code apparel",
"description": "Code de l'appareil (ID de l'appareil)"
},
"cmd_type": {
"name": "command type",
"description": "The type of commandshould be 3query) or 2set)"
},
"cmd_body": {
"name": "command body",
"description": "The body of command, (not include MSmart protocol head and checksum at the end)"
}
}
}
}
}
@@ -0,0 +1,715 @@
{
"config": {
"error": {
"preset_account": "Failed to login with preset account, please report this issue",
"login_failed": "Bejelentkezés sikertelen, a fiók vagy a jelszó helytelen",
"no_devices": "Nem találhatóak új eszközök a hálózaton",
"device_exist": "Az eszköz már be van állítva",
"config_incorrect": "A beállítások helytelenek",
"connect_error": "Nem sikerült csatlakozni az eszközhöz",
"invalid_token": "A Token vagy a Key formátuma helytelen"
},
"step": {
"user": {
"data": {
"way": "Eszközök hozzáadása"
},
"description": "Válassza ki az eszközök hozzáadásának módját",
"title": "Új eszköz hozzáadása"
},
"login": {
"data": {
"account": "Fiók",
"password": "Jelszó"
},
"description": "Jelentkezzen be és tárolja el Midea-fiókját, kizárólag a készülékei információinak a megszerzéséhez.\nAz összes készülékének beállítása után eltávolíthatja ezt a konfigurációt.",
"title": "Bejelentkezés"
},
"discovery": {
"description": "Adja meg az eszköz IP címét vagy írja be az \"auto\" kifejezést az automatikus kereséshez.\nIP-címet is használhat egy megadott hálózaton belüli kereséshez, például \"192.168.1.255\".",
"title": "Keresés",
"data": {
"ip_address": "IP cím"
}
},
"list": {
"description": "{table}",
"title": "Eszközök"
},
"auto": {
"data": {
"device": "Eszközök"
},
"description": "Válassza ki a hozzáadandó eszközt",
"title": "Új eszköz található"
},
"manually": {
"data": {
"name": "Név (pl. Nappali légkondicionáló)",
"device_id": "Eszköz kód",
"type": "Típus",
"ip_address": "IP cím",
"port": "Port",
"model": "Model",
"subtype": "Altípus",
"protocol": "Protokoll",
"token": "Token",
"key": "Key"
},
"description": "Eszköz konfigurációja",
"title": "Új eszköz"
}
}
},
"entity": {
"binary_sensor": {
"bar_door": {
"name": "Bar Door"
},
"bar_door_overtime": {
"name": "Bar Door Overtime"
},
"bathing_working": {
"name": "Bathing Working Status"
},
"bottom_compartment_cooling": {
"name": "Bottom Compartment Cooling"
},
"bottom_compartment_door": {
"name": "Bottom Compartment Door"
},
"bottom_compartment_preheating": {
"name": "Bottom Compartment Preheating"
},
"burning_state": {
"name": "Burning State"
},
"cleaning_reminder": {
"name": "Cleaning Reminder"
},
"compressor_status": {
"name": "Compressor Status"
},
"cooking": {
"name": "Cooking"
},
"filter_change_reminder": {
"name": "Filter Change Reminder"
},
"filter_cleaning_reminder": {
"name": "Filter Cleaning Reminder"
},
"finished": {
"name": "Finished"
},
"flex_zone_door": {
"name": "Flex Door"
},
"flex_zone_door_overtime": {
"name": "Flex Zone Door"
},
"freezer_door": {
"name": "Freezer Door"
},
"freezer_door_overtime": {
"name": "Freezer Door Overtime"
},
"full_dust": {
"name": "Full of Dust"
},
"heating": {
"name": "Heating"
},
"heating_working": {
"name": "Heating Working Status"
},
"keep_warm": {
"name": "Keep Warm"
},
"lid_status": {
"name": "Lid Status"
},
"middle_compartment_cooling": {
"name": "Middle Compartment Cooling"
},
"middle_compartment_door": {
"name": "Middle Compartment Door"
},
"middle_compartment_preheating": {
"name": "Middle Compartment Preheating"
},
"oilcup_full": {
"name": "Oil-cup Full"
},
"protection": {
"name": "Protection"
},
"refrigerator_door": {
"name": "Refrigerator Door"
},
"refrigerator_door_overtime": {
"name": "Refrigerator Door Overtime"
},
"rinse_aid": {
"name": "Rinse Aid Shortage"
},
"salt": {
"name": "Salt Shortage"
},
"seat_status": {
"name": "Seat Status"
},
"status_dhw": {
"name": "DHW status"
},
"status_heating": {
"name": "Heating status"
},
"status_ibh": {
"name": "IBH status"
},
"status_tbh": {
"name": "TBH status"
},
"tank_ejected": {
"name": "Tank Ejected"
},
"tank_full": {
"name": "Tank status"
},
"top_compartment_cooling": {
"name": "Top Compartment Cooling"
},
"top_compartment_door": {
"name": "Top Compartment Door"
},
"top_compartment_preheating": {
"name": "Top Compartment Preheating"
},
"water_change_reminder": {
"name": "Water Change Reminder"
},
"water_shortage": {
"name": "Water Shortage"
},
"with_pressure": {
"name": "With Pressure"
},
"zone1_room_temp_mode": {
"name": "Zone1 Room-temperature Mode"
},
"zone1_water_temp_mode": {
"name": "Zone1 Water-temperature Mode"
},
"zone2_room_temp_mode": {
"name": "Zone2 Room-temperature Mode"
},
"zone2_water_temp_mode": {
"name": "Zone2 Water-temperature Mode"
}
},
"climate": {
"climate_zone1": {
"name": "Zone1 Thermostat"
},
"climate_zone2": {
"name": "Zone2 Thermostat"
}
},
"fan": {
"fresh_air": {
"name": "Fresh Air"
}
},
"lock": {
"child_lock": {
"name": "Child Lock"
}
},
"number": {
"dry_level": {
"name": "Dry Level"
},
"heating_level": {
"name": "Heating Level"
},
"seat_temp_level": {
"name": "Seat Temperature Level"
},
"water_temp_level": {
"name": "Water Temperature Level"
}
},
"select": {
"detect_mode": {
"name": "Detect Mode"
},
"direction": {
"name": "Direction"
},
"fan_speed": {
"name": "Fan Speed"
},
"mode": {
"name": "Mode"
},
"oscillation_angle": {
"name": "Oscillation Angle"
},
"oscillation_mode": {
"name": "Oscillation Mode"
},
"screen_display": {
"name": "Screen Display"
},
"silent_level": {
"name": "Silent Level"
},
"tilting_angle": {
"name": "Tilting Angle"
},
"water_level_set": {
"name": "Water Level Setting"
}
},
"sensor": {
"bathing_leaving_temperature": {
"name": "Bathing Leaving Water Temperature"
},
"bottom_compartment_remaining": {
"name": "Bottom Compartment Remaining"
},
"bottom_compartment_status": {
"name": "Bottom Compartment Status"
},
"bottom_compartment_temperature": {
"name": "Bottom Compartment Temperature"
},
"bottom_temperature": {
"name": "Bottom Temperature"
},
"bright": {
"name": "Bright Level"
},
"compressor_temperature": {
"name": "Compressor Temperature"
},
"condenser_temperature": {
"name": "Condenser Temperature"
},
"current_energy_consumption": {
"name": "Current Energy Consumption"
},
"current_temperature": {
"name": "Current Temperature"
},
"dehydration_speed": {
"name": "dehydration speed"
},
"dehydration_time": {
"name": "dehydration time"
},
"detergent": {
"name": "detergent"
},
"energy_consumption": {
"name": "Energy Consumption"
},
"error_code": {
"name": "Error Code"
},
"fan_level": {
"name": "Fan level"
},
"filter_life": {
"name": "Filter Life"
},
"filter1_days": {
"name": "Filter1 Available Days"
},
"filter1_life": {
"name": "Filter1 Life Level"
},
"filter2_days": {
"name": "Filter2 Available Days"
},
"filter2_life": {
"name": "Filter2 Life Level"
},
"filter3_days": {
"name": "Filter3 Available Days"
},
"filter3_life": {
"name": "Filter3 Life Level"
},
"flex_zone_actual_temp": {
"name": "Flex Zone Actual Temperature"
},
"flex_zone_setting_temp": {
"name": "Flex Zone Setting Temperature"
},
"freezer_actual_temp": {
"name": "Freezer Actual Temperature"
},
"freezer_setting_temp": {
"name": "Freezer Setting Temperature"
},
"hcho": {
"name": "Methanal"
},
"heating_leaving_temperature": {
"name": "Heating Leaving Water Temperature"
},
"heating_power": {
"name": "Heating Power"
},
"heating_time_remaining": {
"name": "Heating Time Remaining"
},
"in_tds": {
"name": "In TDS"
},
"indoor_humidity": {
"name": "Indoor Humidity"
},
"indoor_temperature": {
"name": "Indoor Temperature"
},
"keep_warm_remaining": {
"name": "Keep Warm Remaining"
},
"keep_warm_time": {
"name": "Keep Warm Time"
},
"middle_compartment_remaining": {
"name": "Middle Compartment Remaining"
},
"middle_compartment_status": {
"name": "Middle Compartment Status"
},
"middle_compartment_temperature": {
"name": "Middle Compartment Temperature"
},
"mode": {
"name": "Mode"
},
"out_tds": {
"name": "Out TDS"
},
"outdoor_temperature": {
"name": "Outdoor Temperature"
},
"program": {
"name": "Program"
},
"progress": {
"name": "Progress"
},
"realtime_power": {
"name": "Realtime Power"
},
"refrigerator_actual_temp": {
"name": "Refrigerator Actual Temperature"
},
"refrigerator_setting_temp": {
"name": "Refrigerator Setting Temperature"
},
"right_flex_zone_actual_temp": {
"name": "Right Flex Zone Actual Temperature"
},
"right_flex_zone_setting_temp": {
"name": "Right Flex Zone Setting Temperature"
},
"rinse_count": {
"name": "rinse count"
},
"rinse_level": {
"name": "rinse level"
},
"seat_temperature": {
"name": "Seat Temperature"
},
"soak_time": {
"name": "soak time"
},
"softener": {
"name": "softener"
},
"softwater": {
"name": "Softwater Level"
},
"status": {
"name": "Status"
},
"storage_remaining": {
"name": "Storage Remaining"
},
"tank": {
"name": "Tank"
},
"tank_actual_temperature": {
"name": "Tank Actual Temperature"
},
"target_temperature": {
"name": "Target Temperature"
},
"time_remaining": {
"name": "Time Remaining"
},
"top_compartment_remaining": {
"name": "Top Compartment Remaining"
},
"top_compartment_status": {
"name": "Top Compartment Status"
},
"top_compartment_temperature": {
"name": "Top Compartment Temperature"
},
"top_temperature": {
"name": "Top Temperature"
},
"total_energy_consumption": {
"name": "Total Energy Consumption"
},
"total_produced_energy": {
"name": "Total produced energy"
},
"tvoc": {
"name": "TVOC"
},
"wash_level": {
"name": "rinse count"
},
"wash_strength": {
"name": "wash strength"
},
"wash_time": {
"name": "wash time"
},
"water_consumption": {
"name": "Water Consumption"
},
"water_temperature": {
"name": "Water Temperature"
},
"water_level": {
"name": "Water Level"
},
"working_time": {
"name": "Working Time"
}
},
"switch": {
"anion": {
"name": "Anion"
},
"aux_heating": {
"name": "Aux Heating"
},
"boost_mode": {
"name": "Boost Mode"
},
"breezeless": {
"name": "Breezeless"
},
"comfort_mode": {
"name": "Comfort Mode"
},
"dhw_power": {
"name": "DHW Power"
},
"disinfect": {
"name": "Disinfect"
},
"dry": {
"name": "Dry"
},
"eco_mode": {
"name": "ECO Mode"
},
"fast_dhw": {
"name": "Fast DHW"
},
"foam_shield": {
"name": "Foam Shield"
},
"frost_protect": {
"name": "Frost Protect"
},
"heating_power": {
"name": "Heating Power"
},
"indirect_wind": {
"name": "Indirect Wind"
},
"light": {
"name": "Light"
},
"link_to_ac": {
"name": "Link to AC"
},
"main_light": {
"name": "Main Light"
},
"main_power": {
"name": "Main Power"
},
"natural_wind": {
"name": "Natural Wind"
},
"night_light": {
"name": "Night Light"
},
"oscillate": {
"name": "Oscillate"
},
"power": {
"name": "Power"
},
"powerful_purify": {
"name": "Powerful Purification"
},
"prompt_tone": {
"name": "Prompt Tone"
},
"screen_display": {
"name": "Screen Display"
},
"screen_display_alternate": {
"name": "Screen Display Alternate"
},
"sensor_light": {
"name": "Sensor Light"
},
"silent_mode": {
"name": "Silent Mode"
},
"sleep_mode": {
"name": "Sleep Mode"
},
"smart_eye": {
"name": "Smart Eye"
},
"smart_volume": {
"name": "Smart Volume"
},
"smelly_sensor": {
"name": "Smelly Sensor"
},
"standby": {
"name": "Standby"
},
"start": {
"name": "Start"
},
"storage": {
"name": "Storage"
},
"swing": {
"name": "swing"
},
"swing_horizontal": {
"name": "Swing Horizontal"
},
"swing_vertical": {
"name": "Swing Vertical"
},
"tbh": {
"name": "TBH"
},
"variable_heating": {
"name": "Variable Heating"
},
"ventilation": {
"name": "Ventilation"
},
"whole_tank_heating": {
"name": "Whole Tank Heating"
},
"zero_cold_pulse": {
"name": "Zero Cold Water (Pulse)"
},
"zero_cold_water": {
"name": "Zero Cold Water"
},
"zone1_curve": {
"name": "Zone1 Curve"
},
"zone1_power": {
"name": "Zone1 Power"
},
"zone2_curve": {
"name": "Zone2 Curve"
},
"zone2_power": {
"name": "Zone2 Power"
}
},
"water_heater": {
"domestic_hot_water": {
"name": "Domestic hot water"
},
"bathing": {
"name": "Bathing"
},
"heating": {
"name": "Heating"
}
}
},
"options": {
"step": {
"init": {
"data": {
"ip_address": "IP cím",
"refresh_interval": "Frissítési intervallum (0 azt jelenti, hogy nincs aktív frissítés)",
"sensors": "Extra érzékelők",
"switches": "Extra vezérlők",
"customize": "Személyre szabás"
},
"title": "Konfigurálás"
}
},
"abort": {
"account_option": "A fiók nem támogatja ezt a műveletet.\nKattintson az \"ESZKÖZ HOZZÁADÁSA\" gombra egy új eszköz hozzáadásához."
}
},
"services": {
"set_attribute": {
"name": "Attribútum beállítása",
"description": "Állítsa be az eszköz attribútumának értékét",
"fields": {
"device_id": {
"name": "Eszköz kód",
"description": "A készülék kódja (eszköz ID)"
},
"attribute": {
"name": "Attribútum",
"description": "A beállítandó attribútum neve"
},
"value": {
"name": "Érték",
"description": "A beállítandó attribútum értéke"
}
}
},
"send_command": {
"name": "Egyéni parancs",
"description": "Küldjön egy egyéni parancsot az eszköznek",
"fields": {
"device_id": {
"name": "Eszköz kód",
"description": "A készülék kódja (Eszköz ID)"
},
"cmd_type": {
"name": "Parancs típusa",
"description": "A parancs típusa, lehet 3 (lekérdezés) vagy 2 (beállítás)"
},
"cmd_body": {
"name": "Parancs törzse",
"description": "A parancs törzse (nem tartalmazza az MSmart protokoll fejlécét és a végén lévő ellenőrző összeget)"
}
}
}
}
}
@@ -0,0 +1,745 @@
{
"config": {
"error": {
"preset_account": "Failed to login with preset account, please report this issue",
"login_failed": "Не удалось авторизоваться, неверное имя аккаунта или пароль",
"no_devices": "Новые доступные устройства в локальной сети не найдены",
"device_exist": "Устройство уже настроено",
"config_incorrect": "Некорректная конфигурация",
"connect_error": "Не удается подключиться к устройству",
"invalid_token": "Некорректный формат Token или Key"
},
"step": {
"user": {
"data": {
"way": "Добавление устройства"
},
"description": "Выберите способ добавления устройств",
"title": "Добавить новое устройство"
},
"login": {
"data": {
"account": "Аккаунт",
"password": "Пароль"
},
"description": "Login and storage your Midea account only for getting the appliance info.\nYou can remove this configuration after all appliance configured.",
"title": "Login"
},
"discovery": {
"description": "Введите IP-адрес устройства или \"auto\", чтобы обнаружить устройства автоматически.\nВы также можете использовать IP-адрес для поиска в определенной сети, например, \"192.168.1.255\"",
"title": "Поиск",
"data": {
"ip_address": "IP-адрес"
}
},
"list": {
"description": "{table}",
"title": "Устройства"
},
"auto": {
"data": {
"device": "Устройства"
},
"description": "Выберите устройство для добавления",
"title": "Найдено новое устройство"
},
"manually": {
"data": {
"name": "Название (например, Кондиционер в гостиной)",
"device_id": "Идентификатор устройства",
"type": "Тип",
"ip_address": "IP-адрес",
"port": "Порт",
"model": "Модель",
"subtype": "Подтип",
"protocol": "Протокол",
"token": "Token",
"key": "Key"
},
"description": "Настройка устройства",
"title": "Новое устройство"
}
}
},
"entity": {
"binary_sensor": {
"bar_door": {
"name": "Bar Door"
},
"bar_door_overtime": {
"name": "Bar Door Overtime"
},
"bathing_working": {
"name": "Bathing Working Status"
},
"bottom_compartment_cooling": {
"name": "Bottom Compartment Cooling"
},
"bottom_compartment_door": {
"name": "Bottom Compartment Door"
},
"bottom_compartment_preheating": {
"name": "Bottom Compartment Preheating"
},
"burning_state": {
"name": "Burning State"
},
"cleaning_reminder": {
"name": "Требуется очистка"
},
"compressor_status": {
"name": "Статус компрессора"
},
"cooking": {
"name": "Приготовление"
},
"filter_change_reminder": {
"name": "Требуется замена фильтра"
},
"filter_cleaning_reminder": {
"name": "Требуется очистка фильтра"
},
"finished": {
"name": "Завершено"
},
"flex_zone_door": {
"name": "Flex Door"
},
"flex_zone_door_overtime": {
"name": "Flex Zone Door"
},
"freezer_door": {
"name": "Freezer Door"
},
"freezer_door_overtime": {
"name": "Freezer Door Overtime"
},
"full_dust": {
"name": "Загрязнение"
},
"heating": {
"name": "Нагрев"
},
"heating_working": {
"name": "Heating Working Status"
},
"keep_warm": {
"name": "Поддержание температуры"
},
"lid_status": {
"name": "Крышка"
},
"middle_compartment_cooling": {
"name": "Middle Compartment Cooling"
},
"middle_compartment_door": {
"name": "Middle Compartment Door"
},
"middle_compartment_preheating": {
"name": "Middle Compartment Preheating"
},
"oilcup_full": {
"name": "Oil-cup Full"
},
"protection": {
"name": "Защита"
},
"refrigerator_door": {
"name": "Refrigerator Door"
},
"refrigerator_door_overtime": {
"name": "Refrigerator Door Overtime"
},
"rinse_aid": {
"name": "Нехватка ополаскивателя"
},
"salt": {
"name": "Соль"
},
"seat_status": {
"name": "Сиденье"
},
"status_dhw": {
"name": "DHW status"
},
"status_heating": {
"name": "Подогрев"
},
"status_ibh": {
"name": "IBH status"
},
"status_tbh": {
"name": "TBH status"
},
"tank_ejected": {
"name": "Tank Ejected"
},
"tank_full": {
"name": "Tank status"
},
"top_compartment_cooling": {
"name": "Top Compartment Cooling"
},
"top_compartment_door": {
"name": "Top Compartment Door"
},
"top_compartment_preheating": {
"name": "Top Compartment Preheating"
},
"water_change_reminder": {
"name": "Water Change Reminder"
},
"water_shortage": {
"name": "Нехватка воды"
},
"with_pressure": {
"name": "With Pressure"
},
"zone1_room_temp_mode": {
"name": "Zone1 Room-temperature Mode"
},
"zone1_water_temp_mode": {
"name": "Zone1 Water-temperature Mode"
},
"zone2_room_temp_mode": {
"name": "Zone2 Room-temperature Mode"
},
"zone2_water_temp_mode": {
"name": "Zone2 Water-temperature Mode"
}
},
"climate": {
"climate_zone1": {
"name": "Zone1 Thermostat"
},
"climate_zone2": {
"name": "Zone2 Thermostat"
}
},
"fan": {
"fresh_air": {
"name": "Fresh Air"
}
},
"lock": {
"child_lock": {
"name": "Блокировка"
}
},
"number": {
"dry_level": {
"name": "Dry Level"
},
"heating_level": {
"name": "Heating Level"
},
"seat_temp_level": {
"name": "Seat Temperature Level"
},
"water_temp_level": {
"name": "Water Temperature Level"
}
},
"select": {
"detect_mode": {
"name": "Detect Mode"
},
"direction": {
"name": "Направление"
},
"fan_speed": {
"name": "Скорость"
},
"mode": {
"name": "Режим"
},
"oscillation_angle": {
"name": "Oscillation Angle"
},
"oscillation_mode": {
"name": "Oscillation Mode"
},
"screen_display": {
"name": "Screen Display"
},
"silent_level": {
"name": "Silent Level"
},
"tilting_angle": {
"name": "Угол поворота"
},
"water_level_set": {
"name": "Water Level Setting"
}
},
"sensor": {
"bathing_leaving_temperature": {
"name": "Bathing Leaving Water Temperature"
},
"bottom_compartment_remaining": {
"name": "Bottom Compartment Remaining"
},
"bottom_compartment_status": {
"name": "Bottom Compartment Status"
},
"bottom_compartment_temperature": {
"name": "Bottom Compartment Temperature"
},
"bottom_temperature": {
"name": "Bottom Temperature"
},
"bright": {
"name": "Bright Level"
},
"compressor_temperature": {
"name": "Compressor Temperature"
},
"condenser_temperature": {
"name": "Condenser Temperature"
},
"current_energy_consumption": {
"name": "Current Energy Consumption"
},
"current_temperature": {
"name": "Текущая температура"
},
"dehydration_speed": {
"name": "Dehydration speed"
},
"dehydration_time": {
"name": "Dehydration time"
},
"dehydration_time_value": {
"name": "Dehydration time value"
},
"wash_time_value": {
"name": "Wash time value"
},
"stains": {
"name": "Stains"
},
"dirty_degree": {
"name": "Dirty degree"
},
"detergent": {
"name": "Моющее средство"
},
"intensity": {
"name": "Интенсивность"
},
"dryness_level": {
"name": "Dryness Level"
},
"dry_temperature": {
"name": "Dry Temperature"
},
"door_warn": {
"name": "Door Warn"
},
"ai_switch": {
"name": "AI Switch"
},
"material": {
"name": "Материал"
},
"water_box": {
"name": "Water Box"
},
"energy_consumption": {
"name": "Энергопотребление"
},
"error_code": {
"name": "Код ошибки"
},
"fan_level": {
"name": "Fan level"
},
"filter_life": {
"name": "Состояние фильтра"
},
"filter1_days": {
"name": "Filter1 Available Days"
},
"filter1_life": {
"name": "Filter1 Life Level"
},
"filter2_days": {
"name": "Filter2 Available Days"
},
"filter2_life": {
"name": "Filter2 Life Level"
},
"filter3_days": {
"name": "Filter3 Available Days"
},
"filter3_life": {
"name": "Filter3 Life Level"
},
"flex_zone_actual_temp": {
"name": "Flex Zone Actual Temperature"
},
"flex_zone_setting_temp": {
"name": "Flex Zone Setting Temperature"
},
"freezer_actual_temp": {
"name": "Freezer Actual Temperature"
},
"freezer_setting_temp": {
"name": "Freezer Setting Temperature"
},
"hcho": {
"name": "Формальдегид"
},
"heating_leaving_temperature": {
"name": "Heating Leaving Water Temperature"
},
"heating_power": {
"name": "Мощность нагрева"
},
"heating_time_remaining": {
"name": "Heating Time Remaining"
},
"in_tds": {
"name": "In TDS"
},
"indoor_humidity": {
"name": "Indoor Humidity"
},
"indoor_temperature": {
"name": "Indoor Temperature"
},
"keep_warm_remaining": {
"name": "Keep Warm Remaining"
},
"keep_warm_time": {
"name": "Keep Warm Time"
},
"middle_compartment_remaining": {
"name": "Middle Compartment Remaining"
},
"middle_compartment_status": {
"name": "Middle Compartment Status"
},
"middle_compartment_temperature": {
"name": "Middle Compartment Temperature"
},
"mode": {
"name": "Режим"
},
"out_tds": {
"name": "Out TDS"
},
"outdoor_temperature": {
"name": "Outdoor Temperature"
},
"program": {
"name": "Программа"
},
"progress": {
"name": "Этап"
},
"realtime_power": {
"name": "Энергопотребление"
},
"refrigerator_actual_temp": {
"name": "Refrigerator Actual Temperature"
},
"refrigerator_setting_temp": {
"name": "Refrigerator Setting Temperature"
},
"right_flex_zone_actual_temp": {
"name": "Right Flex Zone Actual Temperature"
},
"right_flex_zone_setting_temp": {
"name": "Right Flex Zone Setting Temperature"
},
"rinse_count": {
"name": "Rinse count"
},
"rinse_level": {
"name": "Уровень ополаскивателя"
},
"seat_temperature": {
"name": "Температура сиденья"
},
"soak_time": {
"name": "Время замачивания"
},
"softener": {
"name": "Умягчитель"
},
"softwater": {
"name": "Уровень умягчителя"
},
"status": {
"name": "Статус"
},
"storage_remaining": {
"name": "Storage Remaining"
},
"tank": {
"name": "Tank"
},
"tank_actual_temperature": {
"name": "Tank Actual Temperature"
},
"target_temperature": {
"name": "Целевая температура"
},
"time_remaining": {
"name": "Время цикла"
},
"top_compartment_remaining": {
"name": "Top Compartment Remaining"
},
"top_compartment_status": {
"name": "Top Compartment Status"
},
"top_compartment_temperature": {
"name": "Top Compartment Temperature"
},
"top_temperature": {
"name": "Top Temperature"
},
"total_energy_consumption": {
"name": "Общее энергопотребление"
},
"total_produced_energy": {
"name": "Total produced energy"
},
"tvoc": {
"name": "TVOC"
},
"wash_strength": {
"name": "Wash strength"
},
"wash_time": {
"name": "Время мойки"
},
"water_consumption": {
"name": "Water Consumption"
},
"water_temperature": {
"name": "Температура воды"
},
"water_level": {
"name": "Уровень воды"
},
"working_time": {
"name": "Время работы"
}
},
"switch": {
"anion": {
"name": "Anion"
},
"aux_heating": {
"name": "Aux Heating"
},
"boost_mode": {
"name": "Boost Mode"
},
"breezeless": {
"name": "Breezeless"
},
"comfort_mode": {
"name": "Comfort Mode"
},
"dhw_power": {
"name": "DHW Power"
},
"disinfect": {
"name": "Дезинфекция"
},
"dry": {
"name": "Dry"
},
"eco_mode": {
"name": "Эко-режим"
},
"fast_dhw": {
"name": "Fast DHW"
},
"foam_shield": {
"name": "Foam Shield"
},
"frost_protect": {
"name": "Защита от замерзания"
},
"heating_power": {
"name": "Нагреватель"
},
"indirect_wind": {
"name": "Indirect Wind"
},
"light": {
"name": "Свет"
},
"link_to_ac": {
"name": "Link to AC"
},
"main_light": {
"name": "Main Light"
},
"main_power": {
"name": "Main Power"
},
"natural_wind": {
"name": "Natural Wind"
},
"night_light": {
"name": "Night Light"
},
"oscillate": {
"name": "Режим поворота"
},
"power": {
"name": "Питание"
},
"powerful_purify": {
"name": "Powerful Purification"
},
"prompt_tone": {
"name": "Prompt Tone"
},
"screen_display": {
"name": "Screen Display"
},
"screen_display_alternate": {
"name": "Screen Display Alternate"
},
"sensor_light": {
"name": "Sensor Light"
},
"silent_mode": {
"name": "Silent Mode"
},
"sleep_mode": {
"name": "Sleep Mode"
},
"smart_eye": {
"name": "Smart Eye"
},
"smart_volume": {
"name": "Smart Volume"
},
"smelly_sensor": {
"name": "Smelly Sensor"
},
"standby": {
"name": "Ожидание"
},
"start": {
"name": "Старт"
},
"storage": {
"name": "Storage"
},
"swing": {
"name": "Swing"
},
"swing_horizontal": {
"name": "Swing Horizontal"
},
"swing_vertical": {
"name": "Swing Vertical"
},
"tbh": {
"name": "TBH"
},
"variable_heating": {
"name": "Variable Heating"
},
"ventilation": {
"name": "Вентиляция"
},
"whole_tank_heating": {
"name": "Whole Tank Heating"
},
"zero_cold_pulse": {
"name": "Zero Cold Water (Pulse)"
},
"zero_cold_water": {
"name": "Zero Cold Water"
},
"zone1_curve": {
"name": "Zone1 Curve"
},
"zone1_power": {
"name": "Zone1 Power"
},
"zone2_curve": {
"name": "Zone2 Curve"
},
"zone2_power": {
"name": "Zone2 Power"
}
},
"water_heater": {
"domestic_hot_water": {
"name": "Domestic hot water"
},
"bathing": {
"name": "Bathing"
},
"heating": {
"name": "Heating"
}
}
},
"options": {
"step": {
"init": {
"data": {
"ip_address": "IP-адрес",
"refresh_interval": "Интервал обновления (0 не выполняет активное обновление)",
"sensors": "Дополнительные сенсоры",
"switches": "Дополнительные элементы управления",
"customize": "Настроить"
},
"title": "Конфигурация"
}
},
"abort": {
"account_option": "The account does not supports this operation.\nClick \"ADD DEVICE\" to add a new device."
}
},
"services": {
"set_attribute": {
"name": "Установить атрибут",
"description": "Установить значение атрибута устройства",
"fields": {
"device_id": {
"name": "Идентификатор устройства",
"description": "Код (Идентификатор) устройства"
},
"attribute": {
"name": "Атрибут",
"description": "Название атрибута для установки"
},
"value": {
"name": "Значение",
"description": "Значение атрибута для установки"
}
}
},
"send_command": {
"name": "Пользовательская команда",
"description": "Отправить пользовательскую команду на устройство",
"fields": {
"device_id": {
"name": "Идентификатор устройства",
"description": "Код (Идентификатор) устройства"
},
"cmd_type": {
"name": "Тип команды",
"description": "Тип команды, может быть 3 (запрос) или 2 (установка значения)"
},
"cmd_body": {
"name": "Тело команды",
"description": "Тело команды (без заголовков протокола MSmart и контрольной суммы в конце)"
}
}
}
}
}
@@ -0,0 +1,715 @@
{
"config": {
"error": {
"preset_account": "Failed to login with preset account, please report this issue",
"login_failed": "Failed to login, account or password was wrong",
"no_devices": "V sieti sa nenašli žiadne nové dostupné zariadenia",
"device_exist": "Spotrebič je už nakonfigurovaný",
"config_incorrect": "Konfigurácia je nesprávna",
"connect_error": "Nedá sa pripojiť k spotrebiču",
"invalid_token": "Token alebo Key v nesprávnom formáte"
},
"step": {
"user": {
"data": {
"way": "Pridávanie spotrebičov"
},
"description": "Vyberte spôsob pridávania zariadení",
"title": "Pridať nový spotrebič"
},
"login": {
"data": {
"account": "Account",
"password": "Password"
},
"description": "Login and storage your Midea account only for getting the appliance info.\nYou can remove this configuration after all appliance configured.",
"title": "Login"
},
"discovery": {
"description": "Zadajte IP adresu pre zariadenie, zadajte \"auto\" pre automatické vyhľadávanie.\nNa vyhľadávanie v rámci určitej siete môžete použiť aj IP adresu, napríklad \"192.168.1.255\".",
"title": "Hľadať",
"data": {
"ip_address": "IP adresa"
}
},
"list": {
"description": "{table}",
"title": "Spotrebiče"
},
"auto": {
"data": {
"device": "Spotrebiče"
},
"description": "Vyberte zariadenie, ktoré chcete pridať",
"title": "Našiel sa nový spotrebič"
},
"manually": {
"data": {
"name": "Názov (napr. AC obývačka)",
"device_id": "Kód spotrebiča",
"type": "Typ",
"ip_address": "IP adresa",
"port": "Port",
"model": "Model",
"subtype": "Podtyp",
"protocol": "Protokol",
"token": "Token",
"key": "Key"
},
"description": "Konfigurácia spotrebiča",
"title": "Nový spotrebič"
}
}
},
"entity": {
"binary_sensor": {
"bar_door": {
"name": "Bar Door"
},
"bar_door_overtime": {
"name": "Bar Door Overtime"
},
"bathing_working": {
"name": "Bathing Working Status"
},
"bottom_compartment_cooling": {
"name": "Bottom Compartment Cooling"
},
"bottom_compartment_door": {
"name": "Bottom Compartment Door"
},
"bottom_compartment_preheating": {
"name": "Bottom Compartment Preheating"
},
"burning_state": {
"name": "Burning State"
},
"cleaning_reminder": {
"name": "Cleaning Reminder"
},
"compressor_status": {
"name": "Compressor Status"
},
"cooking": {
"name": "Cooking"
},
"filter_change_reminder": {
"name": "Filter Change Reminder"
},
"filter_cleaning_reminder": {
"name": "Filter Cleaning Reminder"
},
"finished": {
"name": "Finished"
},
"flex_zone_door": {
"name": "Flex Door"
},
"flex_zone_door_overtime": {
"name": "Flex Zone Door"
},
"freezer_door": {
"name": "Freezer Door"
},
"freezer_door_overtime": {
"name": "Freezer Door Overtime"
},
"full_dust": {
"name": "Full of Dust"
},
"heating": {
"name": "Heating"
},
"heating_working": {
"name": "Heating Working Status"
},
"keep_warm": {
"name": "Keep Warm"
},
"lid_status": {
"name": "Lid Status"
},
"middle_compartment_cooling": {
"name": "Middle Compartment Cooling"
},
"middle_compartment_door": {
"name": "Middle Compartment Door"
},
"middle_compartment_preheating": {
"name": "Middle Compartment Preheating"
},
"oilcup_full": {
"name": "Oil-cup Full"
},
"protection": {
"name": "Protection"
},
"refrigerator_door": {
"name": "Refrigerator Door"
},
"refrigerator_door_overtime": {
"name": "Refrigerator Door Overtime"
},
"rinse_aid": {
"name": "Rinse Aid Shortage"
},
"salt": {
"name": "Salt Shortage"
},
"seat_status": {
"name": "Seat Status"
},
"status_dhw": {
"name": "DHW status"
},
"status_heating": {
"name": "Heating status"
},
"status_ibh": {
"name": "IBH status"
},
"status_tbh": {
"name": "TBH status"
},
"tank_ejected": {
"name": "Tank Ejected"
},
"tank_full": {
"name": "Tank status"
},
"top_compartment_cooling": {
"name": "Top Compartment Cooling"
},
"top_compartment_door": {
"name": "Top Compartment Door"
},
"top_compartment_preheating": {
"name": "Top Compartment Preheating"
},
"water_change_reminder": {
"name": "Water Change Reminder"
},
"water_shortage": {
"name": "Water Shortage"
},
"with_pressure": {
"name": "With Pressure"
},
"zone1_room_temp_mode": {
"name": "Zone1 Room-temperature Mode"
},
"zone1_water_temp_mode": {
"name": "Zone1 Water-temperature Mode"
},
"zone2_room_temp_mode": {
"name": "Zone2 Room-temperature Mode"
},
"zone2_water_temp_mode": {
"name": "Zone2 Water-temperature Mode"
}
},
"climate": {
"climate_zone1": {
"name": "Zone1 Thermostat"
},
"climate_zone2": {
"name": "Zone2 Thermostat"
}
},
"fan": {
"fresh_air": {
"name": "Fresh Air"
}
},
"lock": {
"child_lock": {
"name": "Child Lock"
}
},
"number": {
"dry_level": {
"name": "Dry Level"
},
"heating_level": {
"name": "Heating Level"
},
"seat_temp_level": {
"name": "Seat Temperature Level"
},
"water_temp_level": {
"name": "Water Temperature Level"
}
},
"select": {
"detect_mode": {
"name": "Detect Mode"
},
"direction": {
"name": "Direction"
},
"fan_speed": {
"name": "Fan Speed"
},
"mode": {
"name": "Mode"
},
"oscillation_angle": {
"name": "Oscillation Angle"
},
"oscillation_mode": {
"name": "Oscillation Mode"
},
"screen_display": {
"name": "Screen Display"
},
"silent_level": {
"name": "Silent Level"
},
"tilting_angle": {
"name": "Tilting Angle"
},
"water_level_set": {
"name": "Water Level Setting"
}
},
"sensor": {
"bathing_leaving_temperature": {
"name": "Bathing Leaving Water Temperature"
},
"bottom_compartment_remaining": {
"name": "Bottom Compartment Remaining"
},
"bottom_compartment_status": {
"name": "Bottom Compartment Status"
},
"bottom_compartment_temperature": {
"name": "Bottom Compartment Temperature"
},
"bottom_temperature": {
"name": "Bottom Temperature"
},
"bright": {
"name": "Bright Level"
},
"compressor_temperature": {
"name": "Compressor Temperature"
},
"condenser_temperature": {
"name": "Condenser Temperature"
},
"current_energy_consumption": {
"name": "Current Energy Consumption"
},
"current_temperature": {
"name": "Current Temperature"
},
"dehydration_speed": {
"name": "dehydration speed"
},
"dehydration_time": {
"name": "dehydration time"
},
"detergent": {
"name": "detergent"
},
"energy_consumption": {
"name": "Energy Consumption"
},
"error_code": {
"name": "Error Code"
},
"fan_level": {
"name": "Fan level"
},
"filter_life": {
"name": "Filter Life"
},
"filter1_days": {
"name": "Filter1 Available Days"
},
"filter1_life": {
"name": "Filter1 Life Level"
},
"filter2_days": {
"name": "Filter2 Available Days"
},
"filter2_life": {
"name": "Filter2 Life Level"
},
"filter3_days": {
"name": "Filter3 Available Days"
},
"filter3_life": {
"name": "Filter3 Life Level"
},
"flex_zone_actual_temp": {
"name": "Flex Zone Actual Temperature"
},
"flex_zone_setting_temp": {
"name": "Flex Zone Setting Temperature"
},
"freezer_actual_temp": {
"name": "Freezer Actual Temperature"
},
"freezer_setting_temp": {
"name": "Freezer Setting Temperature"
},
"hcho": {
"name": "Methanal"
},
"heating_leaving_temperature": {
"name": "Heating Leaving Water Temperature"
},
"heating_power": {
"name": "Heating Power"
},
"heating_time_remaining": {
"name": "Heating Time Remaining"
},
"in_tds": {
"name": "In TDS"
},
"indoor_humidity": {
"name": "Indoor Humidity"
},
"indoor_temperature": {
"name": "Indoor Temperature"
},
"keep_warm_remaining": {
"name": "Keep Warm Remaining"
},
"keep_warm_time": {
"name": "Keep Warm Time"
},
"middle_compartment_remaining": {
"name": "Middle Compartment Remaining"
},
"middle_compartment_status": {
"name": "Middle Compartment Status"
},
"middle_compartment_temperature": {
"name": "Middle Compartment Temperature"
},
"mode": {
"name": "Mode"
},
"out_tds": {
"name": "Out TDS"
},
"outdoor_temperature": {
"name": "Outdoor Temperature"
},
"program": {
"name": "Program"
},
"progress": {
"name": "Progress"
},
"realtime_power": {
"name": "Realtime Power"
},
"refrigerator_actual_temp": {
"name": "Refrigerator Actual Temperature"
},
"refrigerator_setting_temp": {
"name": "Refrigerator Setting Temperature"
},
"right_flex_zone_actual_temp": {
"name": "Right Flex Zone Actual Temperature"
},
"right_flex_zone_setting_temp": {
"name": "Right Flex Zone Setting Temperature"
},
"rinse_count": {
"name": "rinse count"
},
"rinse_level": {
"name": "rinse level"
},
"seat_temperature": {
"name": "Seat Temperature"
},
"soak_time": {
"name": "soak time"
},
"softener": {
"name": "softener"
},
"softwater": {
"name": "Softwater Level"
},
"status": {
"name": "Status"
},
"storage_remaining": {
"name": "Storage Remaining"
},
"tank": {
"name": "Tank"
},
"tank_actual_temperature": {
"name": "Tank Actual Temperature"
},
"target_temperature": {
"name": "Target Temperature"
},
"time_remaining": {
"name": "Time Remaining"
},
"top_compartment_remaining": {
"name": "Top Compartment Remaining"
},
"top_compartment_status": {
"name": "Top Compartment Status"
},
"top_compartment_temperature": {
"name": "Top Compartment Temperature"
},
"top_temperature": {
"name": "Top Temperature"
},
"total_energy_consumption": {
"name": "Total Energy Consumption"
},
"total_produced_energy": {
"name": "Total produced energy"
},
"tvoc": {
"name": "TVOC"
},
"wash_level": {
"name": "rinse count"
},
"wash_strength": {
"name": "wash strength"
},
"wash_time": {
"name": "wash time"
},
"water_consumption": {
"name": "Water Consumption"
},
"water_temperature": {
"name": "Water Temperature"
},
"water_level": {
"name": "Water Level"
},
"working_time": {
"name": "Working Time"
}
},
"switch": {
"anion": {
"name": "Anion"
},
"aux_heating": {
"name": "Aux Heating"
},
"boost_mode": {
"name": "Boost Mode"
},
"breezeless": {
"name": "Breezeless"
},
"comfort_mode": {
"name": "Comfort Mode"
},
"dhw_power": {
"name": "DHW Power"
},
"disinfect": {
"name": "Disinfect"
},
"dry": {
"name": "Dry"
},
"eco_mode": {
"name": "ECO Mode"
},
"fast_dhw": {
"name": "Fast DHW"
},
"foam_shield": {
"name": "Foam Shield"
},
"frost_protect": {
"name": "Frost Protect"
},
"heating_power": {
"name": "Heating Power"
},
"indirect_wind": {
"name": "Indirect Wind"
},
"light": {
"name": "Light"
},
"link_to_ac": {
"name": "Link to AC"
},
"main_light": {
"name": "Main Light"
},
"main_power": {
"name": "Main Power"
},
"natural_wind": {
"name": "Natural Wind"
},
"night_light": {
"name": "Night Light"
},
"oscillate": {
"name": "Oscillate"
},
"power": {
"name": "Power"
},
"powerful_purify": {
"name": "Powerful Purification"
},
"prompt_tone": {
"name": "Prompt Tone"
},
"screen_display": {
"name": "Screen Display"
},
"screen_display_alternate": {
"name": "Screen Display Alternate"
},
"sensor_light": {
"name": "Sensor Light"
},
"silent_mode": {
"name": "Silent Mode"
},
"sleep_mode": {
"name": "Sleep Mode"
},
"smart_eye": {
"name": "Smart Eye"
},
"smart_volume": {
"name": "Smart Volume"
},
"smelly_sensor": {
"name": "Smelly Sensor"
},
"standby": {
"name": "Standby"
},
"start": {
"name": "Start"
},
"storage": {
"name": "Storage"
},
"swing": {
"name": "swing"
},
"swing_horizontal": {
"name": "Swing Horizontal"
},
"swing_vertical": {
"name": "Swing Vertical"
},
"tbh": {
"name": "TBH"
},
"variable_heating": {
"name": "Variable Heating"
},
"ventilation": {
"name": "Ventilation"
},
"whole_tank_heating": {
"name": "Whole Tank Heating"
},
"zero_cold_pulse": {
"name": "Zero Cold Water (Pulse)"
},
"zero_cold_water": {
"name": "Zero Cold Water"
},
"zone1_curve": {
"name": "Zone1 Curve"
},
"zone1_power": {
"name": "Zone1 Power"
},
"zone2_curve": {
"name": "Zone2 Curve"
},
"zone2_power": {
"name": "Zone2 Power"
}
},
"water_heater": {
"domestic_hot_water": {
"name": "Domestic hot water"
},
"bathing": {
"name": "Bathing"
},
"heating": {
"name": "Heating"
}
}
},
"options": {
"step": {
"init": {
"data": {
"ip_address": "IP adresa",
"refresh_interval": "Refresh interval (0 means not refreshing actively)",
"sensors": "Extra Snímače",
"switches": "Extra Ovládacie prvky",
"customize": "Voliteľné"
},
"title": "Konfigurovať"
}
},
"abort": {
"account_option": "The account does not supports this operation.\nClick \"ADD DEVICE\" to add a new device."
}
},
"services": {
"set_attribute": {
"name": "Nastaviť atribút",
"description": "Nastavte hodnotu atribútu zariadenia",
"fields": {
"device_id": {
"name": "Kód spotrebiča",
"description": "Kód spotrebiča (ID zariadenia) spotrebiča"
},
"attribute": {
"name": "Atribút",
"description": "Názov atribútu, ktorý chcete nastaviť"
},
"value": {
"name": "Hodnota",
"description": "Hodnota atribútu, ktorú chcete nastaviť"
}
}
},
"send_command": {
"name": "Prispôsobiť príkaz",
"description": "Pošlite do zariadenia príkaz na prispôsobenie",
"fields": {
"device_id": {
"name": "Kód spotrebiča",
"description": "Kód spotrebiča (ID zariadenia) spotrebiča"
},
"cmd_type": {
"name": "typ príkazu",
"description": "Typ príkazu by mal byť 3 (dopyt) alebo 2 (nastaviť)"
},
"cmd_body": {
"name": "obsah príkazu",
"description": "Obsah príkazu, (nezahŕňa hlavičku protokolu MSmart a kontrolný súčet na konci)"
}
}
}
}
}
@@ -0,0 +1,802 @@
{
"config": {
"error": {
"preset_account": "预置账户登录失败,请报告此问题",
"login_failed": "登录失败, 用户名或密码错",
"no_devices": "未在网络上发现可用新设备",
"device_exist": "设备已经存在, 请添加其它设备",
"config_incorrect": "配置信息不正确, 请检查后重新输入",
"connect_error": "无法连接到指定设备",
"invalid_token": "Token或Key格式不正确"
},
"step": {
"user": {
"data": {
"way": "添加方式"
},
"description": "选择添加设备的方式",
"title": "添加新设备"
},
"login": {
"data": {
"account": "账号",
"password": "密码"
},
"description": "登录并保存你的美的账户,仅用于获取添加设备时设备信息\n添加设备完成后,你可以删除该配置",
"title": "登录"
},
"discovery": {
"description": "输入设备的IP地址, 输入\"auto\"自动搜索设备\n你也可以使用IP地址在指定网络中搜索, 比如`192.168.1.255`",
"title": "搜索",
"data": {
"ip_address": "IP地址"
}
},
"list": {
"description": "{table}",
"title": "设备"
},
"auto": {
"data": {
"device": "设备"
},
"description": "选择设备并添加",
"title": "发现新设备"
},
"manually": {
"data": {
"name": "名称(如客厅空调)",
"device_id": "设备编号",
"type": "设备类型",
"ip_address": "IP地址",
"port": "端口",
"model": "型号",
"subtype": "子型号",
"protocol": "协议版本",
"token": "Token",
"key": "Key"
},
"description": "配置设备参数",
"title": "新设备"
}
}
},
"entity": {
"binary_sensor": {
"bar_door": {
"name": "吧台门状态"
},
"bar_door_overtime": {
"name": "吧台门超时"
},
"bathing_working": {
"name": "淋浴工作状态"
},
"bottom_compartment_cooling": {
"name": "下层冷却"
},
"bottom_compartment_door": {
"name": "下层门状态"
},
"bottom_compartment_preheating": {
"name": "下层预热"
},
"burning_state": {
"name": "燃烧状态"
},
"cleaning_reminder": {
"name": "清洁提示"
},
"compressor_status": {
"name": "压缩机状态"
},
"cooking": {
"name": "烹饪中"
},
"filter_change_reminder": {
"name": "滤芯更换提醒"
},
"filter_cleaning_reminder": {
"name": "滤芯清洁提醒"
},
"finished": {
"name": "完成"
},
"flex_zone_door": {
"name": "变温区门状态"
},
"flex_zone_door_overtime": {
"name": "变温区门超时"
},
"freezer_door": {
"name": "冷冻室门状态"
},
"freezer_door_overtime": {
"name": "冷冻室门超时"
},
"full_dust": {
"name": "尘满"
},
"heating": {
"name": "加热"
},
"heating_working": {
"name": "取暖工作状态"
},
"keep_warm": {
"name": "保温"
},
"lid_status": {
"name": "盖子状态"
},
"middle_compartment_cooling": {
"name": "中层冷却"
},
"middle_compartment_door": {
"name": "中层门状态"
},
"middle_compartment_preheating": {
"name": "中层预热"
},
"oilcup_full": {
"name": "油杯满提示"
},
"protection": {
"name": "安全防护"
},
"refrigerator_door": {
"name": "冷藏室门状态"
},
"refrigerator_door_overtime": {
"name": "冷藏室门超时"
},
"rinse_aid": {
"name": "漂洗剂不足"
},
"salt": {
"name": "软水盐不足"
},
"seat_status": {
"name": "入座状态"
},
"status_dhw": {
"name": "DHW状态"
},
"status_heating": {
"name": "加热状态"
},
"status_ibh": {
"name": "IBH状态"
},
"status_tbh": {
"name": "TBH状态"
},
"tank_ejected": {
"name": "水箱弹出"
},
"tank_full": {
"name": "水箱状态"
},
"top_compartment_cooling": {
"name": "上层冷却"
},
"top_compartment_door": {
"name": "上层门状态"
},
"top_compartment_preheating": {
"name": "上层预热"
},
"water_change_reminder": {
"name": "换水提醒"
},
"water_shortage": {
"name": "缺水"
},
"with_pressure": {
"name": "带压中"
},
"zone1_room_temp_mode": {
"name": "区域1室温模式"
},
"zone1_water_temp_mode": {
"name": "区域1水温模式"
},
"zone2_room_temp_mode": {
"name": "区域2室温模式"
},
"zone2_water_temp_mode": {
"name": "区域2水温模式"
},
"microcrystal_fresh": {
"name": "微晶一周鲜"
},
"electronic_smell": {
"name": "净味杀菌"
}
},
"climate": {
"climate_zone1": {
"name": "区域1恒温器"
},
"climate_zone2": {
"name": "区域2恒温器"
},
"climate_key": {
"state_attributes": {
"fan_mode": {
"state": {
"silent": "静音",
"full": "全速"
}
}
}
}
},
"fan": {
"fresh_air": {
"name": "新风"
}
},
"lock": {
"child_lock": {
"name": "童锁"
}
},
"number": {
"dry_level": {
"name": "烘干档位"
},
"heating_level": {
"name": "加热档位"
},
"seat_temp_level": {
"name": "座温档位"
},
"water_temp_level": {
"name": "水温档位"
},
"fan_speed_percent": {
"name": "风速百分比"
}
},
"select": {
"detect_mode": {
"name": "检测模式"
},
"direction": {
"name": "摇摆方向"
},
"fan_speed": {
"name": "风速设定"
},
"mode": {
"name": "模式"
},
"oscillation_angle": {
"name": "水平摆头角度"
},
"oscillation_mode": {
"name": "水平摆头模式"
},
"screen_display": {
"name": "屏幕显示"
},
"silent_level": {
"name": "静音级别"
},
"tilting_angle": {
"name": "垂直摆头角度"
},
"water_level_set": {
"name": "水位设定"
},
"wind_lr_angle": {
"name": "水平出风",
"state": {
"off": "停止",
"left": "左方",
"left-mid": "左中",
"middle": "中部",
"right-mid": "右中",
"right": "右方"
}
},
"wind_ud_angle": {
"name": "垂直出风",
"state": {
"off": "停止",
"up": "上方",
"up-mid": "中上",
"middle": "中部",
"down-mid": "中下",
"down": "下方"
}
}
},
"sensor": {
"bathing_leaving_temperature": {
"name": "淋浴出水温度"
},
"bottom_compartment_remaining": {
"name": "下层剩余时间"
},
"bottom_compartment_status": {
"name": "下层状态"
},
"bottom_compartment_temperature": {
"name": "下层温度"
},
"bottom_temperature": {
"name": "底部温度"
},
"bright": {
"name": "亮碟剂档位"
},
"compressor_temperature": {
"name": "压缩机温度"
},
"condenser_temperature": {
"name": "冷凝器温度"
},
"current_energy_consumption": {
"name": "当前能耗"
},
"current_temperature": {
"name": "当前温度"
},
"dehydration_speed": {
"name": "脱水速度"
},
"dehydration_time": {
"name": "脱水时间档位"
},
"dehydration_time_value": {
"name": "脱水时间值"
},
"wash_time_value": {
"name": "洗涤时间值"
},
"stains": {
"name": "污渍"
},
"dirty_degree": {
"name": "脏度"
},
"intensity": {
"name": "强度"
},
"dryness_level": {
"name": "干燥档位"
},
"dry_temperature": {
"name": "干燥温度"
},
"door_warn": {
"name": "开门警告"
},
"ai_switch": {
"name": "AI开关"
},
"material": {
"name": "材质"
},
"water_box": {
"name": "水箱"
},
"energy_consumption": {
"name": "能耗"
},
"error_code": {
"name": "错误码"
},
"fan_level": {
"name": "当前风扇档位"
},
"filter_life": {
"name": "滤芯寿命"
},
"filter1_days": {
"name": "滤芯1可用天数"
},
"filter1_life": {
"name": "滤芯1剩余寿命"
},
"filter2_days": {
"name": "滤芯2可用天数"
},
"filter2_life": {
"name": "滤芯2剩余寿命"
},
"filter3_days": {
"name": "滤芯3可用天数"
},
"filter3_life": {
"name": "滤芯3剩余寿命"
},
"flex_zone_actual_temp": {
"name": "变温区实际温度"
},
"flex_zone_setting_temp": {
"name": "变温区设置温度"
},
"freezer_actual_temp": {
"name": "冷冻室实际温度"
},
"freezer_setting_temp": {
"name": "冷冻室设置温度"
},
"hcho": {
"name": "甲醛"
},
"heating_leaving_temperature": {
"name": "取暖出水温度"
},
"heating_power": {
"name": "加热功率"
},
"heating_time_remaining": {
"name": "加热剩余时间"
},
"in_tds": {
"name": "进水TDS"
},
"indoor_humidity": {
"name": "室内湿度"
},
"indoor_temperature": {
"name": "室内温度"
},
"keep_warm_remaining": {
"name": "保温剩余时间"
},
"keep_warm_time": {
"name": "保温时间"
},
"middle_compartment_remaining": {
"name": "中层剩余时间"
},
"middle_compartment_status": {
"name": "中层状态"
},
"middle_compartment_temperature": {
"name": "中层温度"
},
"mode": {
"name": "模式"
},
"out_tds": {
"name": "出水TDS"
},
"outdoor_temperature": {
"name": "室外温度"
},
"program": {
"name": "程序"
},
"progress": {
"name": "当前程序"
},
"realtime_power": {
"name": "实时功率"
},
"refrigerator_actual_temp": {
"name": "冷藏室实际温度"
},
"refrigerator_setting_temp": {
"name": "冷藏室设置温度"
},
"right_flex_zone_actual_temp": {
"name": "右变温区实际温度"
},
"right_flex_zone_setting_temp": {
"name": "右变温区设置温度"
},
"rinse_count": {
"name": "漂洗次数"
},
"rinse_level": {
"name": "漂洗档位"
},
"seat_temperature": {
"name": "当前座温"
},
"soak_time": {
"name": "浸泡时间"
},
"softener": {
"name": "柔顺剂"
},
"softwater": {
"name": "软水盐档位"
},
"status": {
"name": "状态"
},
"storage_remaining": {
"name": "保管剩余时间"
},
"tank": {
"name": "水箱水位"
},
"tank_actual_temperature": {
"name": "水箱实际温度"
},
"target_temperature": {
"name": "设定温度"
},
"time_remaining": {
"name": "剩余时间"
},
"top_compartment_remaining": {
"name": "上层剩余时间"
},
"top_compartment_status": {
"name": "上层状态"
},
"top_compartment_temperature": {
"name": "上层温度"
},
"top_temperature": {
"name": "顶部温度"
},
"total_energy_consumption": {
"name": "总能耗"
},
"total_produced_energy": {
"name": "总计产生能量"
},
"tvoc": {
"name": "TVOC"
},
"wash_level": {
"name": "洗涤档位"
},
"wash_strength": {
"name": "洗涤强度"
},
"wash_time": {
"name": "洗涤时间档位"
},
"detergent": {
"name": "洗涤剂"
},
"water_consumption": {
"name": "总耗水量"
},
"water_level": {
"name": "水位"
},
"water_temperature": {
"name": "水温"
},
"working_time": {
"name": "工作时间"
},
"humidity": {
"name": "湿度"
},
"variable_mode": {
"name": "变温区模式"
}
},
"switch": {
"anion": {
"name": "负离子开关"
},
"aux_heating": {
"name": "电辅热"
},
"boost_mode": {
"name": "强劲模式"
},
"breezeless": {
"name": "无风感"
},
"comfort_mode": {
"name": "舒省模式"
},
"cold_water_single": {
"name": "单次零冷水"
},
"cold_water_dot": {
"name": "点动零冷水"
},
"dhw_power": {
"name": "生活热水电源开关"
},
"disinfect": {
"name": "消毒"
},
"dry": {
"name": "干燥"
},
"eco_mode": {
"name": "ECO模式"
},
"fast_dhw": {
"name": "快速生活热水"
},
"foam_shield": {
"name": "泡沫盾"
},
"frost_protect": {
"name": "防霜冻"
},
"heating_power": {
"name": "加热",
"_comments": "E2 加热功率, E6 加热开关"
},
"indirect_wind": {
"name": "防直吹"
},
"light": {
"name": "灯光"
},
"link_to_ac": {
"name": "空调联动开关"
},
"main_light": {
"name": "主灯"
},
"main_power": {
"name": "主电源"
},
"natural_wind": {
"name": "自然风"
},
"night_light": {
"name": "夜灯"
},
"oscillate": {
"name": "摆头开关"
},
"power": {
"name": "电源开关"
},
"powerful_purify": {
"name": "强净开关"
},
"prompt_tone": {
"name": "提示音"
},
"screen_display": {
"name": "屏幕显示"
},
"screen_display_alternate": {
"name": "屏幕显示备用开关"
},
"sensor_light": {
"name": "感应夜灯"
},
"silent_mode": {
"name": "静音模式"
},
"sleep_mode": {
"name": "睡眠模式"
},
"smart_eye": {
"name": "智慧眼"
},
"smart_volume": {
"name": "智能变容"
},
"smelly_sensor": {
"name": "异味感应"
},
"standby": {
"name": "待机"
},
"start": {
"name": "启动"
},
"storage": {
"name": "保管开关"
},
"swing": {
"name": "摆风开关"
},
"swing_horizontal": {
"name": "水平摆风"
},
"swing_vertical": {
"name": "垂直摆风"
},
"tbh": {
"name": "TBH"
},
"variable_heating": {
"name": "变频加热"
},
"ventilation": {
"name": "换气"
},
"whole_tank_heating": {
"name": "全胆速热"
},
"zero_cold_pulse": {
"name": "零冷水(点动)"
},
"zero_cold_water": {
"name": "零冷水"
},
"zone1_curve": {
"name": "区域1曲线"
},
"zone1_power": {
"name": "区域1恒温器开关"
},
"zone2_curve": {
"name": "区域2曲线"
},
"zone2_power": {
"name": "区域2恒温器开关"
}
},
"water_heater": {
"domestic_hot_water": {
"name": "生活热水"
},
"bathing": {
"name": "淋浴"
},
"heating": {
"name": "加热"
}
}
},
"options": {
"step": {
"init": {
"data": {
"ip_address": "IP地址",
"refresh_interval": "刷新间隔(设0为不进行主动刷新)",
"sensors": "扩展传感器",
"switches": "扩展控制",
"customize": "自定义"
},
"title": "配置"
}
},
"abort": {
"account_option": "账户配置不支持此操作.\n点击\"添加设备\"完成添加新设备的操作"
}
},
"services": {
"set_attribute": {
"name": "设置属性",
"description": "设置设备的属性值",
"fields": {
"device_id": {
"name": "设备编码",
"description": "设备编码(Device ID)"
},
"attribute": {
"name": "属性",
"description": "要设置的属性名称"
},
"value": {
"name": "值",
"description": "要设置的属性值"
}
}
},
"send_command": {
"name": "自定义命令",
"description": "向设备发送一个自定义命令",
"fields": {
"device_id": {
"name": "设备编码",
"description": "设备编码(Device ID)"
},
"cmd_type": {
"name": "命令类型",
"description": "命令类型,可以为2(查询)或3(设置)"
},
"cmd_body": {
"name": "命令体",
"description": "命令的消息体(不包括前部的MSmart协议头及后部的校验码)"
}
}
}
}
}
@@ -0,0 +1,455 @@
"""Midea Water Heater entries."""
import functools as ft
import logging
from typing import Any, ClassVar, TypeAlias, cast
from homeassistant.components.water_heater import (
WaterHeaterEntity,
WaterHeaterEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_TEMPERATURE,
CONF_DEVICE_ID,
CONF_SWITCHES,
PRECISION_HALVES,
PRECISION_WHOLE,
STATE_OFF,
STATE_ON,
Platform,
UnitOfTemperature,
)
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 .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
_LOGGER = logging.getLogger(__name__)
E2_TEMPERATURE_MAX = 75
E2_TEMPERATURE_MIN = 30
E3_TEMPERATURE_MAX = 65
E3_TEMPERATURE_MIN = 35
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up water heater entries."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
devs: list[
MideaE2WaterHeater
| MideaE3WaterHeater
| MideaE6WaterHeater
| MideaC3WaterHeater
| MideaCDWaterHeater
] = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.WATER_HEATER and (
config.get("default") or entity_key in extra_switches
):
if device.device_type == DeviceType.E2:
devs.append(MideaE2WaterHeater(device, entity_key))
elif device.device_type == DeviceType.E3:
devs.append(MideaE3WaterHeater(device, entity_key))
elif device.device_type == DeviceType.E6:
devs.append(MideaE6WaterHeater(device, entity_key, config["use"]))
elif device.device_type == DeviceType.C3:
devs.append(MideaC3WaterHeater(device, entity_key))
elif device.device_type == DeviceType.CD:
devs.append(MideaCDWaterHeater(device, entity_key))
async_add_entities(devs)
MideaWaterHeaterDevice: TypeAlias = (
MideaE2Device | MideaE3Device | MideaC3Device | MideaE6Device | MideaCDDevice
)
class MideaWaterHeater(MideaEntity, WaterHeaterEntity):
"""Midea Water Heater Entries Base Class."""
_device: MideaWaterHeaterDevice
def __init__(self, device: MideaWaterHeaterDevice, entity_key: str) -> None:
"""Midea Water Heater entity init."""
super().__init__(device, entity_key)
self._operations: list[str] = []
@property
def supported_features(self) -> WaterHeaterEntityFeature:
"""Midea Water Heater supported features."""
return WaterHeaterEntityFeature.TARGET_TEMPERATURE
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Midea Water Heater extra state attributes."""
attrs: dict[str, Any] = self._device.attributes
if hasattr(self._device, "temperature_step"):
attrs["target_temperature_step"] = self._device.temperature_step
return attrs
@property
def min_temp(self) -> float:
"""Midea Water Heater min temperature."""
raise NotImplementedError
@property
def max_temp(self) -> float:
"""Midea Water Heater max temperature."""
raise NotImplementedError
@property
def precision(self) -> float:
"""Midea Water Heater precision."""
return float(PRECISION_WHOLE)
@property
def temperature_unit(self) -> UnitOfTemperature:
"""Midea Water Heater temperature unix."""
return UnitOfTemperature.CELSIUS
@property
def current_operation(self) -> str | None:
"""Midea Water Heater current operation."""
return cast(
"str",
(
self._device.get_attribute("mode")
if self._device.get_attribute("power")
else STATE_OFF
),
)
@property
def current_temperature(self) -> float:
"""Midea Water Heater current temperature."""
return cast("float", self._device.get_attribute("current_temperature"))
@property
def target_temperature(self) -> float:
"""Midea Water Heater target temperature."""
return cast("float", self._device.get_attribute("target_temperature"))
def set_temperature(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea Water Heater set temperature."""
if ATTR_TEMPERATURE not in kwargs:
return
# input target_temperature should be float
temperature = float(kwargs[ATTR_TEMPERATURE])
self._device.set_attribute("target_temperature", temperature)
def set_operation_mode(self, operation_mode: str) -> None:
"""Midea Water Heater set operation mode."""
self._device.set_attribute(attr="mode", value=operation_mode)
@property
def operation_list(self) -> list[str] | None:
"""Midea Water Heater operation list."""
if not hasattr(self._device, "preset_modes"):
return None
return cast("list", self._device.preset_modes)
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea Water Heater turn on."""
self._device.set_attribute(attr="power", value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea Water Heater turn off."""
self._device.set_attribute(attr="power", value=False)
async def async_turn_on(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea Water Heater async turn on."""
await self.hass.async_add_executor_job(ft.partial(self.turn_on, **kwargs))
async def async_turn_off(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea Water Heater async off."""
await self.hass.async_add_executor_job(ft.partial(self.turn_off, **kwargs))
def update_state(self, status: Any) -> None: # noqa: ANN401, ARG002
"""Midea Water Heater update state."""
if not self.hass:
_LOGGER.warning(
"Water update_state skipped for %s [%s]: HASS is None",
self.name,
type(self),
)
return
self.schedule_update_ha_state()
class MideaE2WaterHeater(MideaWaterHeater):
"""Midea E2 Water Heater Entries."""
_device: MideaE2Device
def __init__(self, device: MideaE2Device, entity_key: str) -> None:
"""Midea E2 Water Heater entity init."""
super().__init__(device, entity_key)
@property
def current_operation(self) -> str:
"""Midea E2 Water Heater current operation."""
return str(
STATE_ON if self._device.get_attribute(E2Attributes.power) else STATE_OFF,
)
@property
def min_temp(self) -> float:
"""Midea E2 Water Heater min temperature."""
return E2_TEMPERATURE_MIN
@property
def max_temp(self) -> float:
"""Midea E2 Water Heater max temperature."""
return E2_TEMPERATURE_MAX
@property
def supported_features(self) -> WaterHeaterEntityFeature:
"""Midea E2 Water Heater supported features."""
return (
WaterHeaterEntityFeature.TARGET_TEMPERATURE
| WaterHeaterEntityFeature.ON_OFF
)
class MideaE3WaterHeater(MideaWaterHeater):
"""Midea E3 Water Heater Entries."""
_device: MideaE3Device
def __init__(self, device: MideaE3Device, entity_key: str) -> None:
"""Midea E3 Water Heater entity init."""
super().__init__(device, entity_key)
@property
def min_temp(self) -> float:
"""Midea E3 Water Heater min temperature."""
return E3_TEMPERATURE_MIN
@property
def max_temp(self) -> float:
"""Midea E3 Water Heater max temperature."""
return E3_TEMPERATURE_MAX
@property
def precision(self) -> float:
"""Midea E3 Water Heater precision."""
return float(
PRECISION_HALVES if self._device.precision_halves else PRECISION_WHOLE,
)
@property
def current_operation(self) -> str:
"""Midea E3 Water Heater current operation."""
return str(
STATE_ON if self._device.get_attribute("power") else STATE_OFF,
)
class MideaC3WaterHeater(MideaWaterHeater):
"""Midea C3 Water Heater Entries."""
_device: MideaC3Device
def __init__(self, device: MideaC3Device, entity_key: str) -> None:
"""Midea C3 Water Heater entity init."""
super().__init__(device, entity_key)
@property
def current_operation(self) -> str:
"""Midea C3 Water Heater current operation."""
return str(
(
STATE_ON
if self._device.get_attribute(C3Attributes.dhw_power)
else STATE_OFF
),
)
@property
def current_temperature(self) -> float:
"""Midea C3 Water Heater current temperature."""
return cast(
"float",
self._device.get_attribute(C3Attributes.tank_actual_temperature),
)
@property
def target_temperature(self) -> float:
"""Midea C3 Water Heater target temperature."""
return cast("float", self._device.get_attribute(C3Attributes.dhw_target_temp))
def set_temperature(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea C3 Water Heater set temperature."""
if ATTR_TEMPERATURE not in kwargs:
return
temperature = float(kwargs[ATTR_TEMPERATURE])
self._device.set_attribute(C3Attributes.dhw_target_temp, temperature)
@property
def min_temp(self) -> float:
"""Midea C3 Water Heater min temperature."""
return cast("float", self._device.get_attribute(C3Attributes.dhw_temp_min))
@property
def max_temp(self) -> float:
"""Midea C3 Water Heater max temperature."""
return cast("float", self._device.get_attribute(C3Attributes.dhw_temp_max))
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea C3 Water Heater turn on."""
self._device.set_attribute(attr=C3Attributes.dhw_power, value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea C3 Water Heater turn off."""
self._device.set_attribute(attr=C3Attributes.dhw_power, value=False)
class MideaE6WaterHeater(MideaWaterHeater):
"""Midea E6 Water Heater Entries."""
_device: MideaE6Device
_powers: ClassVar[list[E6Attributes]] = [
E6Attributes.heating_power,
E6Attributes.main_power,
]
_current_temperatures: ClassVar[list[E6Attributes]] = [
E6Attributes.heating_leaving_temperature,
E6Attributes.bathing_leaving_temperature,
]
_target_temperatures: ClassVar[list[E6Attributes]] = [
E6Attributes.heating_temperature,
E6Attributes.bathing_temperature,
]
def __init__(self, device: MideaE6Device, entity_key: str, use: int) -> None:
"""Midea E6 Water Heater entity init."""
super().__init__(device, entity_key)
self._use = use
self._power_attr = MideaE6WaterHeater._powers[self._use]
self._current_temperature_attr = MideaE6WaterHeater._current_temperatures[
self._use
]
self._target_temperature_attr = MideaE6WaterHeater._target_temperatures[
self._use
]
@property
def current_operation(self) -> str:
"""Midea E6 Water Heater current operation."""
if self._use == 0: # for heating
return str(
(
STATE_ON
if self._device.get_attribute(E6Attributes.main_power)
and self._device.get_attribute(E6Attributes.heating_power)
else STATE_OFF
),
)
# for bathing
return str(
(
STATE_ON
if self._device.get_attribute(E6Attributes.main_power)
else STATE_OFF
),
)
@property
def current_temperature(self) -> float:
"""Midea E6 Water Heater current temperature."""
return cast("float", self._device.get_attribute(self._current_temperature_attr))
@property
def target_temperature(self) -> float:
"""Midea E6 Water Heater target temperature."""
return cast("float", self._device.get_attribute(self._target_temperature_attr))
def set_temperature(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea E6 Water Heater set temperature."""
if ATTR_TEMPERATURE not in kwargs:
return
temperature = float(kwargs[ATTR_TEMPERATURE])
self._device.set_attribute(self._target_temperature_attr, temperature)
@property
def min_temp(self) -> float:
"""Midea E6 Water Heater min temperature."""
min_temperature = cast(
"list[str]",
self._device.get_attribute(E6Attributes.min_temperature),
)
return cast(
"float",
min_temperature[self._use],
)
@property
def max_temp(self) -> float:
"""Midea E6 Water Heater max temperature."""
max_temperature = cast(
"list[str]",
self._device.get_attribute(E6Attributes.max_temperature),
)
return cast(
"float",
max_temperature[self._use],
)
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea E6 Water Heater turn on."""
self._device.set_attribute(attr=self._power_attr, value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea E6 Water Heater turn off."""
self._device.set_attribute(attr=self._power_attr, value=False)
class MideaCDWaterHeater(MideaWaterHeater):
"""Midea CD Water Heater Entries."""
_device: MideaCDDevice
def __init__(self, device: MideaCDDevice, entity_key: str) -> None:
"""Midea CD Water Heater entity init."""
super().__init__(device, entity_key)
@property
def supported_features(self) -> WaterHeaterEntityFeature:
"""Midea CD Water Heater supported features."""
return (
WaterHeaterEntityFeature.TARGET_TEMPERATURE
| WaterHeaterEntityFeature.OPERATION_MODE
)
@property
def min_temp(self) -> float:
"""Midea CD Water Heater min temperature."""
return cast("float", self._device.get_attribute(CDAttributes.min_temperature))
@property
def max_temp(self) -> float:
"""Midea CD Water Heater max temperature."""
return cast("float", self._device.get_attribute(CDAttributes.max_temperature))