diff --git a/.devcontainer/configuration.yaml b/.devcontainer/configuration.yaml index 2a9b2cf..d77f90c 100644 --- a/.devcontainer/configuration.yaml +++ b/.devcontainer/configuration.yaml @@ -3,7 +3,7 @@ default_config: logger: default: info logs: - custom_components.midea_dehumidifier_lan: debug + custom_components.midea_dehumidifier_lan: info midea_beautiful: info # To debug uncomment the line below (doc: https://www.home-assistant.io/integrations/debugpy/) diff --git a/README.md b/README.md index ef86445..f3b591b 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Once the integration is installed, you can add it to the Home Assistant by going ### Configuring 1. Add `Midea Air Appliances (LAN)` integration via UI. -2. Enter Midea cloud username and password. Those are the same used in NetHome Plus mobile application. +2. Enter Midea cloud username and password and select mobile application you use. 3. The integration will discover appliance on local network(s). 4. If an appliance is not automatically discovered, but is registered to the cloud account, user is prompted to enter IPv4 address of the appliance. 5. If you want to use integration with air conditioner unit(s), please select the checkbox on `Advanced settings` page. @@ -39,7 +39,8 @@ Once the integration is installed, you can add it to the Home Assistant by going * If you encounter issues after upgrading, uninstall the integration, restart Home Assistant and re-install it. * Some of sensors and switches are disabled by default. You need to enable them manually. See tables below for more information. * Temperature sensor on dehumidifier is often under-reporting real ambient temperature. This may be due to sensor proximity to cooling pipes of the humidifier, algorithm, or electronics error. The under-reporting depends on the active mode, and stronger modes may result in larger offset from real temperature. -* Some Midea appliances, built in 2021 and later, use Tuya based patform and this integration will not work with them. In some cases those appliances have have same model names as old ones. +* Some Midea appliances, built in 2021 and later, use Tuya based patform and this integration will not work with them. In some cases those appliances have have same model names as old ones. +* When migrating from version 0.6 or 0.7 to 0.8, integration may fail. Please remove and re-install integration. ## Supported appliances @@ -109,8 +110,8 @@ On entry page, paste following content: ```yaml service: logger.set_level data: - custom_components.midea_dehumidifier_lan: debug - midea_beautiful: debug + custom_components.midea_dehumidifier_lan: DEBUG + midea_beautiful: DEBUG ``` It is possible to activate debug logging on Home Assistent start. To do this, open Home Assistant's `configuration.yaml` file on your machine, and add following to `logger` configuration: diff --git a/assets/advanced-options-ac.png b/assets/advanced-options-ac.png index 0e77ccc..3f5d890 100644 Binary files a/assets/advanced-options-ac.png and b/assets/advanced-options-ac.png differ diff --git a/assets/advanced-options-debug.png b/assets/advanced-options-debug.png index f07a706..8e8bc78 100644 Binary files a/assets/advanced-options-debug.png and b/assets/advanced-options-debug.png differ diff --git a/assets/advanced-options.png b/assets/advanced-options.png index cdc3e64..18d6205 100644 Binary files a/assets/advanced-options.png and b/assets/advanced-options.png differ diff --git a/custom_components/midea_dehumidifier_lan/__init__.py b/custom_components/midea_dehumidifier_lan/__init__.py index 64a7814..c030c34 100644 --- a/custom_components/midea_dehumidifier_lan/__init__.py +++ b/custom_components/midea_dehumidifier_lan/__init__.py @@ -29,14 +29,13 @@ from midea_beautiful.cloud import MideaCloud from midea_beautiful.exceptions import MideaError from midea_beautiful.lan import LanDevice -from midea_beautiful.midea import DEFAULT_APP_ID, DEFAULT_APPKEY +from midea_beautiful.midea import SUPPORTED_APPS, DEFAULT_APP_ID, DEFAULT_APPKEY from custom_components.midea_dehumidifier_lan.const import ( - CONF_APPID, - CONF_APPKEY, + CONF_MOBILE_APP, CONF_TOKEN_KEY, CONF_USE_CLOUD_OBSOLETE, - CURRENT_CONFIG_VERSION, + DEFAULT_APP, DEFAULT_TTL, DISCOVERY_CLOUD, DISCOVERY_IGNORE, @@ -45,13 +44,15 @@ DOMAIN, LOCAL_BROADCAST, NAME, + CURRENT_CONFIG_VERSION, + OBSOLETE_CONF_APPID, + OBSOLETE_CONF_APPKEY, PLATFORMS, UNKNOWN_IP, ) from custom_components.midea_dehumidifier_lan.hub import Hub from custom_components.midea_dehumidifier_lan.util import MideaClient, address_ok - _LOGGER = logging.getLogger(__name__) @@ -127,15 +128,28 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> if not old_broadcast: old_broadcast = [LOCAL_BROADCAST] new_conf = { - CONF_APPKEY: old_conf.get(CONF_APPKEY), - CONF_APPID: old_conf.get(CONF_APPID), + CONF_MOBILE_APP: old_conf.get(CONF_MOBILE_APP), CONF_BROADCAST_ADDRESS: old_broadcast, CONF_USERNAME: old_conf.get(CONF_USERNAME), CONF_PASSWORD: old_conf.get(CONF_PASSWORD), } - if not new_conf.get(CONF_APPID) or not new_conf.get(CONF_APPKEY): - new_conf[CONF_APPKEY] = DEFAULT_APPKEY - new_conf[CONF_APPID] = DEFAULT_APP_ID + if not old_conf.get(OBSOLETE_CONF_APPID) or not old_conf.get( + OBSOLETE_CONF_APPKEY + ): + new_conf[CONF_MOBILE_APP] = DEFAULT_APP + else: + appkey = old_conf.get(OBSOLETE_CONF_APPKEY, DEFAULT_APPKEY) + if appkey: + for appname, appconf in SUPPORTED_APPS.items(): + if appconf["appkey"] == appkey: + new_conf[CONF_MOBILE_APP] = appname + break + else: + appid = old_conf.get(OBSOLETE_CONF_APPID, DEFAULT_APP_ID) + for appname, appconf in SUPPORTED_APPS.items(): + if appconf["appid"] == appid: + new_conf[CONF_MOBILE_APP] = appname + break new_devices = [] new_conf[CONF_DEVICES] = new_devices diff --git a/custom_components/midea_dehumidifier_lan/config_flow.py b/custom_components/midea_dehumidifier_lan/config_flow.py index 254c6a6..4130440 100644 --- a/custom_components/midea_dehumidifier_lan/config_flow.py +++ b/custom_components/midea_dehumidifier_lan/config_flow.py @@ -43,22 +43,18 @@ from midea_beautiful.lan import LanDevice from midea_beautiful.midea import ( APPLIANCE_TYPE_DEHUMIDIFIER, - DEFAULT_APP_ID, - DEFAULT_APPKEY, SUPPORTED_APPS, ) from custom_components.midea_dehumidifier_lan import Hub from custom_components.midea_dehumidifier_lan.const import ( NAME, + CURRENT_CONFIG_VERSION, SUPPORTED_APPLIANCES, CONF_ADVANCED_SETTINGS, - CONF_APPID, - CONF_APPKEY, CONF_DEBUG, CONF_MOBILE_APP, CONF_TOKEN_KEY, - CURRENT_CONFIG_VERSION, DEFAULT_APP, DEFAULT_DISCOVERY_MODE, DEFAULT_PASSWORD, @@ -118,8 +114,7 @@ def _appliance_schema( # pylint: disable=too-many-arguments def _advanced_settings_schema( username: str = "", password: str = "", - appkey: str = "", - appid: int = None, + app: str = DEFAULT_APP, broadcast_address: str = "", appliances: list[str] = None, debug: bool = False, @@ -129,8 +124,7 @@ def _advanced_settings_schema( { vol.Required(CONF_USERNAME, default=username): cv.string, vol.Required(CONF_PASSWORD, default=password): cv.string, - vol.Required(CONF_APPKEY, default=appkey): cv.string, - vol.Required(CONF_APPID, default=appid): cv.positive_int, + vol.Optional(CONF_MOBILE_APP, default=app): vol.In(SUPPORTED_APPS.keys()), vol.Optional(CONF_BROADCAST_ADDRESS, default=broadcast_address): cv.string, vol.Required( CONF_SCAN_INTERVAL, @@ -150,15 +144,11 @@ def _advanced_settings_schema( def _reauth_schema( username: str, password: str, - appkey: str, - appid: int, ) -> vol.Schema: return vol.Schema( { vol.Required(CONF_USERNAME, default=username): cv.string, vol.Required(CONF_PASSWORD, default=password): cv.string, - vol.Required(CONF_APPKEY, default=appkey): cv.string, - vol.Required(CONF_APPID, default=appid): cv.positive_int, } ) @@ -310,7 +300,6 @@ async def _async_add_entry(self: _MideaFlow) -> FlowResult: # Remove not used elements self.conf.pop(CONF_ADVANCED_SETTINGS, None) - self.conf.pop(CONF_MOBILE_APP, None) if self.config_entry: _LOGGER.debug("Updating configuration data %s", RedactedConf(self.conf)) self.hass.config_entries.async_update_entry( @@ -526,16 +515,14 @@ async def _validate_discovery_phase( if self.advanced_settings: assert self.conf is not None - self.conf[CONF_APPID] = user_input[CONF_APPID] - self.conf[CONF_APPKEY] = user_input[CONF_APPKEY] + self.conf[CONF_MOBILE_APP] = user_input.get(CONF_MOBILE_APP, DEFAULT_APP) self.conf[CONF_INCLUDE] = user_input[CONF_INCLUDE] self.conf[CONF_SCAN_INTERVAL] = user_input[CONF_SCAN_INTERVAL] self.conf[CONF_DEBUG] = user_input[CONF_DEBUG] self.conf[CONF_BROADCAST_ADDRESS] = _get_broadcast_addresses(user_input) else: - app = user_input.get(CONF_MOBILE_APP, DEFAULT_APP) - self.conf |= SUPPORTED_APPS.get(app, SUPPORTED_APPS[DEFAULT_APP]) + self.conf[CONF_MOBILE_APP] = user_input.get(CONF_MOBILE_APP, DEFAULT_APP) if user_input.get(CONF_ADVANCED_SETTINGS): return await self.async_step_advanced_settings() @@ -614,8 +601,7 @@ async def async_step_advanced_settings( password = user_input.get( CONF_PASSWORD, self.conf.get(CONF_PASSWORD, DEFAULT_PASSWORD) ) - appkey = user_input.get(CONF_APPKEY, DEFAULT_APPKEY) - appid = user_input.get(CONF_APPID, DEFAULT_APP_ID) + app = user_input.get(CONF_MOBILE_APP, DEFAULT_APP) broadcast_addresses = user_input.get( CONF_BROADCAST_ADDRESS, ",".join(self.conf.get(CONF_BROADCAST_ADDRESS, [])) ) @@ -625,8 +611,7 @@ async def async_step_advanced_settings( data_schema=_advanced_settings_schema( username=username, password=password, - appkey=appkey, - appid=appid, + app=app, broadcast_address=broadcast_addresses, ), description_placeholders=self._placeholders(), @@ -661,14 +646,12 @@ async def async_step_reauth_confirm( self.errors.clear() password = "" username = self.conf.get(CONF_USERNAME, "") - appkey = self.conf.get(CONF_APPKEY, DEFAULT_APPKEY) - appid = self.conf.get(CONF_APPID, DEFAULT_APP_ID) + app = self.conf.get(CONF_MOBILE_APP, DEFAULT_APP) if user_input is not None: extra_conf = { CONF_USERNAME: user_input.get(CONF_USERNAME, ""), CONF_PASSWORD: user_input.get(CONF_PASSWORD, ""), - CONF_APPKEY: user_input.get(CONF_APPKEY, appkey), - CONF_APPID: user_input.get(CONF_APPID, appid), + CONF_MOBILE_APP: user_input.get(CONF_MOBILE_APP, app), } try: await self.hass.async_add_executor_job( @@ -679,8 +662,7 @@ async def async_step_reauth_confirm( else: self.conf[CONF_USERNAME] = username self.conf[CONF_PASSWORD] = password - self.conf[CONF_APPKEY] = appkey - self.conf[CONF_APPID] = appid + self.conf[CONF_MOBILE_APP] = app return await self._async_add_entry() return self.async_show_form( @@ -688,8 +670,6 @@ async def async_step_reauth_confirm( data_schema=_reauth_schema( username=username, password=password, - appkey=appkey, - appid=appid, ), description_placeholders=self._placeholders(), errors=self.errors, diff --git a/custom_components/midea_dehumidifier_lan/const.py b/custom_components/midea_dehumidifier_lan/const.py index f09f687..d5bd651 100644 --- a/custom_components/midea_dehumidifier_lan/const.py +++ b/custom_components/midea_dehumidifier_lan/const.py @@ -8,7 +8,7 @@ from midea_beautiful.midea import ( APPLIANCE_TYPE_AIRCON, APPLIANCE_TYPE_DEHUMIDIFIER, - SUPPORTED_APPS, + DEFAULT_APP as DEFAULT_APP_FROM_LIB, ) __version__ = "0.7.0" @@ -23,8 +23,8 @@ ISSUE_URL: Final = "https://github.com/nbogojevic/homeassistant-midea-air-appliances-lan/issues/new/choose" # noqa: E501 CONF_ADVANCED_SETTINGS: Final = "advanced_settings" -CONF_APPID: Final = "appid" -CONF_APPKEY: Final = "appkey" +OBSOLETE_CONF_APPID: Final = "appid" +OBSOLETE_CONF_APPKEY: Final = "appkey" CONF_DEBUG: Final = "debug" CONF_MOBILE_APP: Final = "mobile_app" CONF_TOKEN_KEY: Final = "token_key" @@ -36,7 +36,7 @@ MAX_TARGET_TEMPERATURE: Final = 32 MIN_TARGET_TEMPERATURE: Final = 16 -CURRENT_CONFIG_VERSION: Final = 2 +CURRENT_CONFIG_VERSION: Final = 3 # Wait half a second between successive refresh calls APPLIANCE_REFRESH_COOLDOWN: Final = 0.5 @@ -72,7 +72,7 @@ DISCOVERY_BATCH_SIZE: Final = 64 -DEFAULT_APP: Final = next(app for app in SUPPORTED_APPS) +DEFAULT_APP: Final = DEFAULT_APP_FROM_LIB DEFAULT_USERNAME: Final = "" DEFAULT_PASSWORD: Final = "" diff --git a/custom_components/midea_dehumidifier_lan/manifest.json b/custom_components/midea_dehumidifier_lan/manifest.json index f0fdc58..54e6344 100644 --- a/custom_components/midea_dehumidifier_lan/manifest.json +++ b/custom_components/midea_dehumidifier_lan/manifest.json @@ -9,7 +9,7 @@ "@nbogojevic" ], "requirements": [ - "midea-beautiful-air==0.9.0" + "midea-beautiful-air==0.9.7" ], "after_dependencies": [ "network", diff --git a/custom_components/midea_dehumidifier_lan/translations/en.json b/custom_components/midea_dehumidifier_lan/translations/en.json index 2ff0896..7272971 100644 --- a/custom_components/midea_dehumidifier_lan/translations/en.json +++ b/custom_components/midea_dehumidifier_lan/translations/en.json @@ -22,7 +22,7 @@ "scan_interval": "Network scan interval", "debug": "Advanced debug mode" }, - "description": "You can provide specific Midea mobile app key and app id if the default ones don't work.\n\nYou can also specify network address (e.g. 192.0.2.2) or range (e.g. 192.0.2.4/24) to search for specific appliance(s) if regular discovery doesn't work.", + "description": "You can specify network address (e.g. 192.0.2.2) or range (e.g. 192.0.2.4/24) to search for specific appliance(s) if regular discovery doesn't work.", "title": "Advanced settings" }, "unreachable_appliance": { @@ -64,7 +64,7 @@ "abort": { "single_instance_allowed": "Already defined a Midea app account. Only a single account is supported for Midea Air Appliances (LAN).", "reauth_successful": "Re-authentication was successful", - "no_configured_devices": "Nothing to configure" + "no_configured_devices": "There are no devices to configure" } }, "options": { diff --git a/custom_components/midea_dehumidifier_lan/util.py b/custom_components/midea_dehumidifier_lan/util.py index 94343e1..d78209e 100644 --- a/custom_components/midea_dehumidifier_lan/util.py +++ b/custom_components/midea_dehumidifier_lan/util.py @@ -30,8 +30,7 @@ from custom_components.midea_dehumidifier_lan.const import ( _ALWAYS_CREATE, - CONF_APPID, - CONF_APPKEY, + CONF_MOBILE_APP, CONF_TOKEN_KEY, UNKNOWN_IP, ) @@ -193,8 +192,7 @@ def connect_to_cloud(self, conf: dict[str, Any]) -> MideaCloud: return midea_beautiful_api.connect_to_cloud( account=conf[CONF_USERNAME], password=conf[CONF_PASSWORD], - appkey=conf[CONF_APPKEY], - appid=conf[CONF_APPID], + appname=conf[CONF_MOBILE_APP], ) def appliance_state( # pylint: disable=too-many-arguments,no-self-use diff --git a/info.md b/info.md index b041e60..648c7c2 100644 --- a/info.md +++ b/info.md @@ -12,6 +12,10 @@ ## Breaking Changes - Unique id of entities changed. Using serial number now instead of cloud API id. Old entites are updated with new unique id. - Removed sleep switch. +{% if (version_installed.split(".")[1] | int) < 8 %} +- Removed application key from advanced settings page. Only application name is used for configuration. +- When migrating from version 0.6 or 0.7 new configuration may fail. Please remove and re-install integration. +{% endif %} ## Major changes - Added support for air conditioners (**beta**)