Compare commits

...

4 Commits

Author SHA1 Message Date
Li Shuzhen
1b11b2bc40
Merge 84e79c3435 into 6557b22a52 2025-01-10 09:26:33 +08:00
Paul Shawn
6557b22a52
fix: fix multi ha instance login (#560)
* fix: fix multi ha instance login

* fix: fix option flow oauth
2025-01-10 09:19:24 +08:00
LiShuzhen
84e79c3435 fix: add water heater on_off feature 2025-01-07 11:45:43 +08:00
LiShuzhen
56e9f1d32e fix: water heater STATE_ON 2025-01-07 10:44:57 +08:00
15 changed files with 43 additions and 31 deletions

View File

@ -68,6 +68,7 @@ from homeassistant.components.webhook import (
)
from homeassistant.core import callback
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.helpers.instance_id import async_get
import homeassistant.helpers.config_validation as cv
from .miot.const import (
@ -247,6 +248,13 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if user_input:
self._cloud_server = user_input.get(
'cloud_server', self._cloud_server)
# Gen instance uuid
ha_uuid = await async_get(self.hass)
if not ha_uuid:
raise AbortFlow(reason='ha_uuid_get_failed')
self._uuid = hashlib.sha256(
f'{ha_uuid}.{self._virtual_did}.{self._cloud_server}'.encode(
'utf-8')).hexdigest()[:32]
self._integration_language = user_input.get(
'integration_language', DEFAULT_INTEGRATION_LANGUAGE)
self._miot_i18n = MIoTI18n(
@ -415,9 +423,11 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
miot_oauth = MIoTOauthClient(
client_id=OAUTH2_CLIENT_ID,
redirect_url=self._oauth_redirect_url_full,
cloud_server=self._cloud_server
)
state = str(secrets.randbits(64))
cloud_server=self._cloud_server,
uuid=self._uuid,
loop=self._main_loop)
state = hashlib.sha1(
f'd=ha.{self._uuid}'.encode('utf-8')).hexdigest()
self.hass.data[DOMAIN][self._virtual_did]['oauth_state'] = state
self._cc_oauth_auth_url = miot_oauth.gen_auth_url(
redirect_url=self._oauth_redirect_url_full, state=state)
@ -498,11 +508,6 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
client_id=OAUTH2_CLIENT_ID,
access_token=auth_info['access_token'])
self._auth_info = auth_info
# Gen uuid
self._uuid = hashlib.sha256(
f'{self._virtual_did}.{auth_info["access_token"]}'.encode(
'utf-8')
).hexdigest()[:32]
try:
self._nick_name = (
await self._miot_http.get_user_info_async() or {}
@ -1145,7 +1150,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
async def async_step_oauth(self, user_input=None):
try:
if self._cc_task_oauth is None:
state = str(secrets.randbits(64))
state = hashlib.sha1(
f'd=ha.{self._entry_data["uuid"]}'.encode('utf-8')
).hexdigest()
self.hass.data[DOMAIN][self._virtual_did]['oauth_state'] = state
self._miot_oauth.set_redirect_url(
redirect_url=self._oauth_redirect_url_full)

View File

@ -257,6 +257,7 @@ class MIoTClient:
client_id=OAUTH2_CLIENT_ID,
redirect_url=self._entry_data['oauth_redirect_url'],
cloud_server=self._cloud_server,
uuid=self._entry_data["uuid"],
loop=self._main_loop)
# MIoT http client instance
self._http = MIoTHttpClient(

View File

@ -75,10 +75,11 @@ class MIoTOauthClient:
_oauth_host: str
_client_id: int
_redirect_url: str
_device_id: str
def __init__(
self, client_id: str, redirect_url: str, cloud_server: str,
loop: Optional[asyncio.AbstractEventLoop] = None
uuid: str, loop: Optional[asyncio.AbstractEventLoop] = None
) -> None:
self._main_loop = loop or asyncio.get_running_loop()
if client_id is None or client_id.strip() == '':
@ -87,6 +88,8 @@ class MIoTOauthClient:
raise MIoTOauthError('invalid redirect_url')
if not cloud_server:
raise MIoTOauthError('invalid cloud_server')
if not uuid:
raise MIoTOauthError('invalid uuid')
self._client_id = int(client_id)
self._redirect_url = redirect_url
@ -94,6 +97,7 @@ class MIoTOauthClient:
self._oauth_host = DEFAULT_OAUTH2_API_HOST
else:
self._oauth_host = f'{cloud_server}.{DEFAULT_OAUTH2_API_HOST}'
self._device_id = f'ha.{uuid}'
self._session = aiohttp.ClientSession(loop=self._main_loop)
async def deinit_async(self) -> None:
@ -132,6 +136,7 @@ class MIoTOauthClient:
'redirect_uri': redirect_url or self._redirect_url,
'client_id': self._client_id,
'response_type': 'code',
'device_id': self._device_id
}
if state:
params['state'] = state
@ -191,6 +196,7 @@ class MIoTOauthClient:
'client_id': self._client_id,
'redirect_uri': self._redirect_url,
'code': code,
'device_id': self._device_id
})
async def refresh_access_token_async(self, refresh_token: str) -> dict:

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "Xiaomi MQTT Broker-Adresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration."
},
"abort": {
"ha_uuid_get_failed": "Fehler beim Abrufen der Home Assistant-UUID.",
"network_connect_error": "Konfiguration fehlgeschlagen. Netzwerkverbindung fehlgeschlagen. Überprüfen Sie die Netzwerkkonfiguration des Geräts.",
"already_configured": "Dieser Benutzer hat die Konfiguration bereits abgeschlossen. Gehen Sie zur Integrationsseite und klicken Sie auf die Schaltfläche \"Konfiguration\", um die Konfiguration zu ändern.",
"invalid_auth_info": "Authentifizierungsinformationen sind abgelaufen. Gehen Sie zur Integrationsseite und klicken Sie auf die Schaltfläche \"Konfiguration\", um die Authentifizierung erneut durchzuführen.",

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "Unable to reach Xiaomi MQTT Broker address, please check network configuration."
},
"abort": {
"ha_uuid_get_failed": "Failed to get Home Assistant UUID.",
"network_connect_error": "Configuration failed. The network connection is abnormal. Please check the equipment network configuration.",
"already_configured": "Configuration for this user is already completed. Please go to the integration page and click the CONFIGURE button for modifications.",
"invalid_auth_info": "Authentication information has expired. Please go to the integration page and click the CONFIGURE button to re-authenticate.",

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "No se puede acceder a la dirección del Broker MQTT de Xiaomi, por favor verifique la configuración de la red."
},
"abort": {
"ha_uuid_get_failed": "Error al obtener el UUID de Home Assistant.",
"network_connect_error": "La configuración ha fallado. Existe un problema con la conexión de red, verifique la configuración de red del dispositivo.",
"already_configured": "Esta cuenta ya ha finalizado la configuración. Ingrese a la página de integración y haga clic en el botón \"Configurar\" para modificar la configuración.",
"invalid_auth_info": "La información de autorización ha caducado. Ingrese a la página de integración y haga clic en el botón \"Configurar\" para volver a autenticarse.",

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "Impossible d'atteindre l'adresse du Broker MQTT de Xiaomi, veuillez vérifier la configuration réseau."
},
"abort": {
"ha_uuid_get_failed": "Échec de l'obtention de l'UUID de Home Assistant.",
"network_connect_error": "La configuration a échoué. Erreur de connexion réseau. Veuillez vérifier la configuration du réseau de l'appareil.",
"already_configured": "Cet utilisateur a déjà terminé la configuration. Veuillez accéder à la page d'intégration et cliquer sur le bouton \"Configurer\" pour modifier la configuration.",
"invalid_auth_info": "Les informations d'authentification ont expiré. Veuillez accéder à la page d'intégration et cliquer sur le bouton \"Configurer\" pour vous authentifier à nouveau.",

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "Xiaomi MQTT ブローカーアドレスにアクセスできません。ネットワーク設定を確認してください。"
},
"abort": {
"ha_uuid_get_failed": "Home Assistant インスタンスIDを取得できませんでした。",
"network_connect_error": "設定に失敗しました。ネットワーク接続に異常があります。デバイスのネットワーク設定を確認してください。",
"already_configured": "このユーザーはすでに設定が完了しています。統合ページにアクセスして、「設定」ボタンをクリックして設定を変更してください。",
"invalid_auth_info": "認証情報が期限切れになりました。統合ページにアクセスして、「設定」ボタンをクリックして再度認証してください。",

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "Kan Xiaomi MQTT Broker-adres niet bereiken, controleer de netwerkconfiguratie."
},
"abort": {
"ha_uuid_get_failed": "Mislukt bij het ophalen van Home Assistant UUID.",
"network_connect_error": "Configuratie mislukt. De netwerkverbinding is abnormaal. Controleer de netwerkinstellingen van de apparatuur.",
"already_configured": "Configuratie voor deze gebruiker is al voltooid. Ga naar de integratiepagina en klik op de CONFIGUREER-knop om wijzigingen aan te brengen.",
"invalid_auth_info": "Authenticatie-informatie is verlopen. Ga naar de integratiepagina en klik op de CONFIGUREER-knop om opnieuw te authentiseren.",

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "Não é possível acessar o endereço do Broker MQTT da Xiaomi, verifique a configuração da rede."
},
"abort": {
"ha_uuid_get_failed": "Falha ao obter o UUID do Home Assistant.",
"network_connect_error": "Configuração falhou. A conexão de rede está anormal. Verifique a configuração de rede do equipamento.",
"already_configured": "A configuração para este usuário já foi concluída. Vá para a página de integrações e clique no botão CONFIGURAR para modificações.",
"invalid_auth_info": "As informações de autenticação expiraram. Vá para a página de integrações e clique em CONFIGURAR para reautenticar.",

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "Não é possível acessar o endereço do Broker MQTT da Xiaomi, verifique a configuração da rede."
},
"abort": {
"ha_uuid_get_failed": "Não foi possível obter o UUID do Home Assistant.",
"network_connect_error": "A configuração falhou. A ligação de rede é anormal. Verifique a configuração de rede do equipamento.",
"already_configured": "A configuração para este utilizador já foi concluída. Vá à página de integrações e clique em CONFIGURAR para efetuar alterações.",
"invalid_auth_info": "A informação de autenticação expirou. Vá à página de integrações e clique em CONFIGURAR para reautenticar.",

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "Не удается подключиться к адресу MQTT брокера Xiaomi, проверьте настройки сети."
},
"abort": {
"ha_uuid_get_failed": "Не удалось получить UUID Home Assistant.",
"network_connect_error": "Ошибка настройки. Сетевое подключение недоступно. Проверьте настройки сети устройства.",
"already_configured": "Этот пользователь уже настроен. Перейдите на страницу интеграции и нажмите кнопку «Настроить», чтобы изменить настройки.",
"invalid_auth_info": "Информация об авторизации истекла. Перейдите на страницу интеграции и нажмите кнопку «Настроить», чтобы переавторизоваться.",

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "无法访问小米 MQTT Broker 地址,请检查网络配置。"
},
"abort": {
"ha_uuid_get_failed": "获取 Home Assistant UUID 失败。",
"network_connect_error": "配置失败。网络连接异常,请检查设备网络配置。",
"already_configured": "该用户已配置完成。请进入集成页面,点击“配置”按钮修改配置。",
"invalid_auth_info": "认证信息已过期。请进入集成页面,点击“配置”按钮重新认证。",

View File

@ -90,6 +90,7 @@
"unreachable_mqtt_broker": "無法訪問小米 MQTT Broker 地址,請檢查網絡配置。"
},
"abort": {
"ha_uuid_get_failed": "獲取 Home Assistant UUID 失敗。",
"network_connect_error": "配置失敗。網絡連接異常,請檢查設備網絡配置。",
"already_configured": "該用戶已配置完成。請進入集成頁面,點擊“配置”按鈕修改配置。",
"invalid_auth_info": "認證信息已過期。請進入集成頁面,點擊“配置”按鈕重新認證。",

View File

@ -93,7 +93,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
_prop_target_temp: Optional[MIoTSpecProperty]
_prop_mode: Optional[MIoTSpecProperty]
_mode_list: Optional[dict[Any, Any]]
_mode_map: Optional[dict[int, str]]
def __init__(
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
@ -113,6 +113,8 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
# on
if prop.name == 'on':
self._prop_on = prop
self._attr_supported_features |= (
WaterHeaterEntityFeature.ON_OFF)
# temperature
if prop.name == 'temperature':
if isinstance(prop.value_range, dict):
@ -145,10 +147,10 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
_LOGGER.error(
'mode value_list is None, %s', self.entity_id)
continue
self._mode_list = {
self._mode_map = {
item['value']: item['description']
for item in prop.value_list}
self._attr_operation_list = list(self._mode_list.values())
self._attr_operation_list = list(self._mode_map.values())
self._attr_supported_features |= (
WaterHeaterEntityFeature.OPERATION_MODE)
self._prop_mode = prop
@ -184,7 +186,8 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
prop=self._prop_on, value=True, update=False)
await self.set_property_async(
prop=self._prop_mode,
value=self.__get_mode_value(description=operation_mode))
value=self.get_map_value(
map_=self._mode_map, description=operation_mode))
async def async_turn_away_mode_on(self) -> None:
"""Set the water heater to away mode."""
@ -207,20 +210,4 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
return STATE_OFF
if not self._prop_mode and self.get_prop_value(prop=self._prop_on):
return STATE_ON
return self.__get_mode_description(
key=self.get_prop_value(prop=self._prop_mode))
def __get_mode_description(self, key: int) -> Optional[str]:
"""Convert mode value to description."""
if self._mode_list is None:
return None
return self._mode_list.get(key, None)
def __get_mode_value(self, description: str) -> Optional[int]:
"""Convert mode description to value."""
if self._mode_list is None:
return None
for key, value in self._mode_list.items():
if value == description:
return key
return None
return self._mode_map[self.get_prop_value(prop=self._prop_mode)]