-
-
-
- '''
+ if _template == '':
+ await asyncio.get_running_loop().run_in_executor(
+ None, _load_page_template)
+ web_page = _template.replace('TITLE_PLACEHOLDER', title)
+ web_page = web_page.replace('CONTENT_PLACEHOLDER', content)
+ web_page = web_page.replace('BUTTON_PLACEHOLDER', button)
+ web_page = web_page.replace(
+ 'STATUS_PLACEHOLDER', 'true' if success else 'false')
+ return web_page
diff --git a/custom_components/xiaomi_home/notify.py b/custom_components/xiaomi_home/notify.py
index ba0844a..5cf3fd8 100644
--- a/custom_components/xiaomi_home/notify.py
+++ b/custom_components/xiaomi_home/notify.py
@@ -90,7 +90,7 @@ class Notify(MIoTActionEntity, NotifyEntity):
super().__init__(miot_device=miot_device, spec=spec)
self._attr_extra_state_attributes = {}
action_in: str = ', '.join([
- f'{prop.description_trans}({prop.format_})'
+ f'{prop.description_trans}({prop.format_.__name__})'
for prop in self.spec.in_])
self._attr_extra_state_attributes['action params'] = f'[{action_in}]'
@@ -122,24 +122,24 @@ class Notify(MIoTActionEntity, NotifyEntity):
return
in_value: list[dict] = []
for index, prop in enumerate(self.spec.in_):
- if prop.format_ == 'str':
+ if prop.format_ == str:
if isinstance(in_list[index], (bool, int, float, str)):
in_value.append(
{'piid': prop.iid, 'value': str(in_list[index])})
continue
- elif prop.format_ == 'bool':
+ elif prop.format_ == bool:
if isinstance(in_list[index], (bool, int)):
# yes, no, on, off, true, false and other bool types
# will also be parsed as 0 and 1 of int.
in_value.append(
{'piid': prop.iid, 'value': bool(in_list[index])})
continue
- elif prop.format_ == 'float':
+ elif prop.format_ == float:
if isinstance(in_list[index], (int, float)):
in_value.append(
{'piid': prop.iid, 'value': in_list[index]})
continue
- elif prop.format_ == 'int':
+ elif prop.format_ == int:
if isinstance(in_list[index], int):
in_value.append(
{'piid': prop.iid, 'value': in_list[index]})
diff --git a/custom_components/xiaomi_home/number.py b/custom_components/xiaomi_home/number.py
index 53bc09c..29bd6b7 100644
--- a/custom_components/xiaomi_home/number.py
+++ b/custom_components/xiaomi_home/number.py
@@ -92,9 +92,9 @@ class Number(MIoTPropertyEntity, NumberEntity):
self._attr_icon = self.spec.icon
# Set value range
if self._value_range:
- self._attr_native_min_value = self._value_range['min']
- self._attr_native_max_value = self._value_range['max']
- self._attr_native_step = self._value_range['step']
+ self._attr_native_min_value = self._value_range.min_
+ self._attr_native_max_value = self._value_range.max_
+ self._attr_native_step = self._value_range.step
@property
def native_value(self) -> Optional[float]:
diff --git a/custom_components/xiaomi_home/select.py b/custom_components/xiaomi_home/select.py
index 4c9bad3..21b5e78 100644
--- a/custom_components/xiaomi_home/select.py
+++ b/custom_components/xiaomi_home/select.py
@@ -82,7 +82,8 @@ class Select(MIoTPropertyEntity, SelectEntity):
def __init__(self, miot_device: MIoTDevice, spec: MIoTSpecProperty) -> None:
"""Initialize the Select."""
super().__init__(miot_device=miot_device, spec=spec)
- self._attr_options = list(self._value_list.values())
+ if self._value_list:
+ self._attr_options = self._value_list.descriptions
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
diff --git a/custom_components/xiaomi_home/sensor.py b/custom_components/xiaomi_home/sensor.py
index 39b3bdb..88b4bac 100644
--- a/custom_components/xiaomi_home/sensor.py
+++ b/custom_components/xiaomi_home/sensor.py
@@ -76,6 +76,12 @@ async def async_setup_entry(
for prop in miot_device.prop_list.get('sensor', []):
new_entities.append(Sensor(miot_device=miot_device, spec=prop))
+ if miot_device.miot_client.display_binary_text:
+ for prop in miot_device.prop_list.get('binary_sensor', []):
+ if not prop.value_list:
+ continue
+ new_entities.append(Sensor(miot_device=miot_device, spec=prop))
+
if new_entities:
async_add_entities(new_entities)
@@ -91,7 +97,7 @@ class Sensor(MIoTPropertyEntity, SensorEntity):
self._attr_device_class = SensorDeviceClass.ENUM
self._attr_icon = 'mdi:message-text'
self._attr_native_unit_of_measurement = None
- self._attr_options = list(self._value_list.values())
+ self._attr_options = self._value_list.descriptions
else:
self._attr_device_class = spec.device_class
if spec.external_unit:
@@ -100,29 +106,29 @@ class Sensor(MIoTPropertyEntity, SensorEntity):
# device_class is not empty but unit is empty.
# Set the default unit according to device_class.
unit_sets = DEVICE_CLASS_UNITS.get(
- self._attr_device_class, None)
+ self._attr_device_class, None) # type: ignore
self._attr_native_unit_of_measurement = list(
unit_sets)[0] if unit_sets else None
+ # Set state_class
+ if spec.state_class:
+ self._attr_state_class = spec.state_class
# Set icon
if spec.icon:
self._attr_icon = spec.icon
- # Set state_class
- if spec.state_class:
- self._attr_state_class = spec.state_class
@property
def native_value(self) -> Any:
"""Return the current value of the sensor."""
if self._value_range and isinstance(self._value, (int, float)):
if (
- self._value < self._value_range['min']
- or self._value > self._value_range['max']
+ self._value < self._value_range.min_
+ or self._value > self._value_range.max_
):
_LOGGER.info(
'%s, data exception, out of range, %s, %s',
self.entity_id, self._value, self._value_range)
if self._value_list:
- return self._value_list.get(self._value, None)
+ return self.get_vlist_description(self._value)
if isinstance(self._value, str):
return self._value[:255]
return self._value
diff --git a/custom_components/xiaomi_home/text.py b/custom_components/xiaomi_home/text.py
index 8a6b9ae..ff6ac3e 100644
--- a/custom_components/xiaomi_home/text.py
+++ b/custom_components/xiaomi_home/text.py
@@ -76,9 +76,10 @@ async def async_setup_entry(
for prop in miot_device.prop_list.get('text', []):
new_entities.append(Text(miot_device=miot_device, spec=prop))
- for action in miot_device.action_list.get('action_text', []):
- new_entities.append(ActionText(
- miot_device=miot_device, spec=action))
+ if miot_device.miot_client.action_debug:
+ for action in miot_device.action_list.get('notify', []):
+ new_entities.append(ActionText(
+ miot_device=miot_device, spec=action))
if new_entities:
async_add_entities(new_entities)
@@ -111,11 +112,9 @@ class ActionText(MIoTActionEntity, TextEntity):
self._attr_extra_state_attributes = {}
self._attr_native_value = ''
action_in: str = ', '.join([
- f'{prop.description_trans}({prop.format_})'
+ f'{prop.description_trans}({prop.format_.__name__})'
for prop in self.spec.in_])
self._attr_extra_state_attributes['action params'] = f'[{action_in}]'
- # For action debug
- self.action_platform = 'action_text'
async def async_set_value(self, value: str) -> None:
if not value:
@@ -141,24 +140,24 @@ class ActionText(MIoTActionEntity, TextEntity):
f'invalid action params, {value}')
in_value: list[dict] = []
for index, prop in enumerate(self.spec.in_):
- if prop.format_ == 'str':
+ if prop.format_ == str:
if isinstance(in_list[index], (bool, int, float, str)):
in_value.append(
{'piid': prop.iid, 'value': str(in_list[index])})
continue
- elif prop.format_ == 'bool':
+ elif prop.format_ == bool:
if isinstance(in_list[index], (bool, int)):
# yes, no, on, off, true, false and other bool types
# will also be parsed as 0 and 1 of int.
in_value.append(
{'piid': prop.iid, 'value': bool(in_list[index])})
continue
- elif prop.format_ == 'float':
+ elif prop.format_ == float:
if isinstance(in_list[index], (int, float)):
in_value.append(
{'piid': prop.iid, 'value': in_list[index]})
continue
- elif prop.format_ == 'int':
+ elif prop.format_ == int:
if isinstance(in_list[index], int):
in_value.append(
{'piid': prop.iid, 'value': in_list[index]})
diff --git a/custom_components/xiaomi_home/translations/de.json b/custom_components/xiaomi_home/translations/de.json
index 68a0373..15f5c0f 100644
--- a/custom_components/xiaomi_home/translations/de.json
+++ b/custom_components/xiaomi_home/translations/de.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "Erweiterte Einstellungen",
- "description": "## Gebrauchsanweisung\r\n### Wenn Sie die Bedeutung der folgenden Optionen nicht genau kennen, belassen Sie sie bitte auf den Standardeinstellungen.\r\n### Geräte filtern\r\nUnterstützt das Filtern von Geräten nach Raumnamen und Gerätetypen sowie das Filtern nach Gerätedimensionen.\r\n\r\n### Steuerungsmodus\r\n- Automatisch: Wenn ein verfügbarer Xiaomi-Hub im lokalen Netzwerk vorhanden ist, priorisiert Home Assistant das Senden von Steuerbefehlen über den Hub, um eine lokale Steuerung zu ermöglichen. Wenn kein Hub vorhanden ist, wird versucht, Steuerbefehle über das Xiaomi-OT-Protokoll zu senden. Nur wenn diese Bedingungen für die lokale Steuerung nicht erfüllt sind, werden die Befehle über die Cloud gesendet.\r\n- Cloud: Steuerbefehle werden ausschließlich über die Cloud gesendet.\r\n### Action-Debug-Modus\r\nFür Methoden, die von MIoT-Spec-V2-Geräten definiert werden, wird neben der Benachrichtigungsentität auch eine Texteingabe-Entität erstellt, mit der Sie während des Debuggens Steuerbefehle an das Gerät senden können.\r\n### Nicht standardmäßige Entitäten ausblenden\r\nBlendet Entitäten aus, die von nicht-standardmäßigen MIoT-Spec-V2-Instanzen generiert werden und deren Name mit „*“ beginnt.\r\n### Gerätestatusänderungen anzeigen\r\nDetaillierte Anzeige von Gerätestatusänderungen, es werden nur die ausgewählten Benachrichtigungen angezeigt.",
+ "description": "## Gebrauchsanweisung\r\n### Wenn Sie die Bedeutung der folgenden Optionen nicht genau kennen, belassen Sie sie bitte auf den Standardeinstellungen.\r\n### Geräte filtern\r\nUnterstützt das Filtern von Geräten nach Raumnamen und Gerätetypen sowie das Filtern nach Gerätedimensionen.\r\n\r\n### Steuerungsmodus\r\n- Automatisch: Wenn ein verfügbarer Xiaomi-Hub im lokalen Netzwerk vorhanden ist, priorisiert Home Assistant das Senden von Steuerbefehlen über den Hub, um eine lokale Steuerung zu ermöglichen. Wenn kein Hub vorhanden ist, wird versucht, Steuerbefehle über das Xiaomi-OT-Protokoll zu senden. Nur wenn diese Bedingungen für die lokale Steuerung nicht erfüllt sind, werden die Befehle über die Cloud gesendet.\r\n- Cloud: Steuerbefehle werden ausschließlich über die Cloud gesendet.\r\n### Action-Debug-Modus\r\nFür Methoden, die von MIoT-Spec-V2-Geräten definiert werden, wird neben der Benachrichtigungsentität auch eine Texteingabe-Entität erstellt, mit der Sie während des Debuggens Steuerbefehle an das Gerät senden können.\r\n### Nicht standardmäßige Entitäten ausblenden\r\nBlendet Entitäten aus, die von nicht-standardmäßigen MIoT-Spec-V2-Instanzen generiert werden und deren Name mit „*“ beginnt.\r\n### Binärsensor-Anzeigemodus\r\nZeigt Binärsensoren in Xiaomi Home als Textsensor-Entität oder Binärsensor-Entität an。\r\n### Gerätestatusänderungen anzeigen\r\nDetaillierte Anzeige von Gerätestatusänderungen, es werden nur die ausgewählten Benachrichtigungen angezeigt.",
"data": {
"devices_filter": "Geräte filtern",
"ctrl_mode": "Steuerungsmodus",
"action_debug": "Action-Debug-Modus",
"hide_non_standard_entities": "Nicht standardmäßige Entitäten ausblenden",
+ "display_binary_mode": "Binärsensor-Anzeigemodus",
"display_devices_changed_notify": "Gerätestatusänderungen anzeigen"
}
},
@@ -90,6 +91,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.",
@@ -118,6 +120,7 @@
"update_devices": "Geräteliste aktualisieren",
"action_debug": "Action-Debug-Modus",
"hide_non_standard_entities": "Verstecke Nicht-Standard-Entitäten",
+ "display_binary_mode": "Binärsensor-Anzeigemodus",
"display_devices_changed_notify": "Gerätestatusänderungen anzeigen",
"update_trans_rules": "Entitätskonvertierungsregeln aktualisieren",
"update_lan_ctrl_config": "LAN-Steuerungskonfiguration aktualisieren",
diff --git a/custom_components/xiaomi_home/translations/en.json b/custom_components/xiaomi_home/translations/en.json
index 2244730..7832fd1 100644
--- a/custom_components/xiaomi_home/translations/en.json
+++ b/custom_components/xiaomi_home/translations/en.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "Advanced Settings",
- "description": "## Introduction\r\n### Unless you are very clear about the meaning of the following options, please keep the default settings.\r\n### Filter Devices\r\nSupports filtering devices by room name and device type, and also supports device dimension filtering.\r\n### Control Mode\r\n- Auto: When there is an available Xiaomi central hub gateway in the local area network, Home Assistant will prioritize sending device control commands through the central hub gateway to achieve local control. If there is no central hub gateway in the local area network, it will attempt to send control commands through Xiaomi OT protocol to achieve local control. Only when the above local control conditions are not met, the device control commands will be sent through the cloud.\r\n- Cloud: All control commands are sent through the cloud.\r\n### Action Debug Mode\r\nFor the methods defined by the device MIoT-Spec-V2, in addition to generating notification entities, a text input box entity will also be generated. You can use it to send control commands to the device during debugging.\r\n### Hide Non-Standard Generated Entities\r\nHide entities generated by non-standard MIoT-Spec-V2 instances with names starting with \"*\".\r\n### Display Device Status Change Notifications\r\nDisplay detailed device status change notifications, only showing the selected notifications.",
+ "description": "## Introduction\r\n### Unless you are very clear about the meaning of the following options, please keep the default settings.\r\n### Filter Devices\r\nSupports filtering devices by room name and device type, and also supports device dimension filtering.\r\n### Control Mode\r\n- Auto: When there is an available Xiaomi central hub gateway in the local area network, Home Assistant will prioritize sending device control commands through the central hub gateway to achieve local control. If there is no central hub gateway in the local area network, it will attempt to send control commands through Xiaomi OT protocol to achieve local control. Only when the above local control conditions are not met, the device control commands will be sent through the cloud.\r\n- Cloud: All control commands are sent through the cloud.\r\n### Action Debug Mode\r\nFor the methods defined by the device MIoT-Spec-V2, in addition to generating notification entities, a text input box entity will also be generated. You can use it to send control commands to the device during debugging.\r\n### Hide Non-Standard Generated Entities\r\nHide entities generated by non-standard MIoT-Spec-V2 instances with names starting with \"*\".\r\n### Binary Sensor Display Mode\r\nDisplay binary sensors in Xiaomi Home as text sensor entity or binary sensor entity。\r\n### Display Device Status Change Notifications\r\nDisplay detailed device status change notifications, only showing the selected notifications.",
"data": {
"devices_filter": "Filter Devices",
"ctrl_mode": "Control Mode",
"action_debug": "Action Debug Mode",
"hide_non_standard_entities": "Hide Non-Standard Generated Entities",
+ "display_binary_mode": "Binary Sensor Display Mode",
"display_devices_changed_notify": "Display Device Status Change Notifications"
}
},
@@ -90,6 +91,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.",
@@ -118,6 +120,7 @@
"update_devices": "Update device list",
"action_debug": "Debug mode for action",
"hide_non_standard_entities": "Hide non-standard created entities",
+ "display_binary_mode": "Binary Sensor Display Mode",
"display_devices_changed_notify": "Display device status change notifications",
"update_trans_rules": "Update entity conversion rules",
"update_lan_ctrl_config": "Update LAN control configuration",
diff --git a/custom_components/xiaomi_home/translations/es.json b/custom_components/xiaomi_home/translations/es.json
index 2942567..eb52c74 100644
--- a/custom_components/xiaomi_home/translations/es.json
+++ b/custom_components/xiaomi_home/translations/es.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "Opciones Avanzadas",
- "description": "## Introducción\r\n### A menos que entienda claramente el significado de las siguientes opciones, manténgalas en su configuración predeterminada.\r\n### Filtrar dispositivos\r\nAdmite la filtración de dispositivos por nombre de habitación y tipo de dispositivo, y también admite la filtración por familia.\r\n### Modo de Control\r\n- Automático: Cuando hay una puerta de enlace central de Xiaomi disponible en la red local, Home Assistant enviará comandos de control de dispositivos a través de la puerta de enlace central para lograr la función de control local. Cuando no hay una puerta de enlace central en la red local, intentará enviar comandos de control a través del protocolo OT de Xiaomi para lograr la función de control local. Solo cuando no se cumplan las condiciones de control local anteriores, los comandos de control de dispositivos se enviarán a través de la nube.\r\n- Nube: Los comandos de control solo se envían a través de la nube.\r\n### Modo de Depuración de Acciones\r\nPara los métodos definidos por el dispositivo MIoT-Spec-V2, además de generar una entidad de notificación, también se generará una entidad de cuadro de texto que se puede utilizar para enviar comandos de control al dispositivo durante la depuración.\r\n### Ocultar Entidades Generadas No Estándar\r\nOcultar entidades generadas por instancias MIoT-Spec-V2 no estándar que comienzan con \"*\".\r\n### Mostrar notificaciones de cambio de estado del dispositivo\r\nMostrar notificaciones detalladas de cambio de estado del dispositivo, mostrando solo las notificaciones seleccionadas.",
+ "description": "## Introducción\r\n### A menos que entienda claramente el significado de las siguientes opciones, manténgalas en su configuración predeterminada.\r\n### Filtrar dispositivos\r\nAdmite la filtración de dispositivos por nombre de habitación y tipo de dispositivo, y también admite la filtración por familia.\r\n### Modo de Control\r\n- Automático: Cuando hay una puerta de enlace central de Xiaomi disponible en la red local, Home Assistant enviará comandos de control de dispositivos a través de la puerta de enlace central para lograr la función de control local. Cuando no hay una puerta de enlace central en la red local, intentará enviar comandos de control a través del protocolo OT de Xiaomi para lograr la función de control local. Solo cuando no se cumplan las condiciones de control local anteriores, los comandos de control de dispositivos se enviarán a través de la nube.\r\n- Nube: Los comandos de control solo se envían a través de la nube.\r\n### Modo de Depuración de Acciones\r\nPara los métodos definidos por el dispositivo MIoT-Spec-V2, además de generar una entidad de notificación, también se generará una entidad de cuadro de texto que se puede utilizar para enviar comandos de control al dispositivo durante la depuración.\r\n### Ocultar Entidades Generadas No Estándar\r\nOcultar entidades generadas por instancias MIoT-Spec-V2 no estándar que comienzan con \"*\".\r\n### Modo de visualización del sensor binario\r\nMuestra los sensores binarios en Xiaomi Home como entidad de sensor de texto o entidad de sensor binario。\r\n### Mostrar notificaciones de cambio de estado del dispositivo\r\nMostrar notificaciones detalladas de cambio de estado del dispositivo, mostrando solo las notificaciones seleccionadas.",
"data": {
"devices_filter": "Filtrar Dispositivos",
"ctrl_mode": "Modo de Control",
"action_debug": "Modo de Depuración de Acciones",
"hide_non_standard_entities": "Ocultar Entidades Generadas No Estándar",
+ "display_binary_mode": "Modo de visualización del sensor binario",
"display_devices_changed_notify": "Mostrar notificaciones de cambio de estado del dispositivo"
}
},
@@ -90,6 +91,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.",
@@ -118,6 +120,7 @@
"update_devices": "Actualizar lista de dispositivos",
"action_debug": "Modo de depuración de Action",
"hide_non_standard_entities": "Ocultar entidades generadas no estándar",
+ "display_binary_mode": "Modo de visualización del sensor binario",
"display_devices_changed_notify": "Mostrar notificaciones de cambio de estado del dispositivo",
"update_trans_rules": "Actualizar reglas de conversión de entidad",
"update_lan_ctrl_config": "Actualizar configuración de control LAN",
diff --git a/custom_components/xiaomi_home/translations/fr.json b/custom_components/xiaomi_home/translations/fr.json
index fa1b84d..07c2245 100644
--- a/custom_components/xiaomi_home/translations/fr.json
+++ b/custom_components/xiaomi_home/translations/fr.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "Paramètres Avancés",
- "description": "## Introduction\r\n### Sauf si vous comprenez très bien la signification des options suivantes, veuillez les laisser par défaut.\r\n### Filtrer les appareils\r\nPrend en charge le filtrage des appareils en fonction du nom de la pièce et du type d'appareil, ainsi que le filtrage basé sur les appareils.\r\n### Mode de Contrôle\r\n- Automatique : Lorsqu'une passerelle Xiaomi est disponible dans le réseau local, Home Assistant enverra les commandes de contrôle des appareils via la passerelle pour permettre le contrôle local. Si aucune passerelle n'est disponible dans le réseau local, Home Assistant essaiera d'envoyer les commandes de contrôle des appareils via le protocole OT Xiaomi pour permettre le contrôle local. Seules si les conditions de contrôle local ci-dessus ne sont pas remplies, les commandes de contrôle des appareils seront envoyées via le cloud.\r\n- Cloud : Les commandes de contrôle des appareils sont envoyées uniquement via le cloud.\r\n### Mode de Débogage d’Actions\r\nPour les méthodes définies par les appareils MIoT-Spec-V2, en plus de générer une entité de notification, une entité de champ de texte sera également générée pour vous permettre d'envoyer des commandes de contrôle aux appareils lors du débogage.\r\n### Masquer les Entités Non Standard\r\nMasquer les entités générées par des instances MIoT-Spec-V2 non standard et commençant par \"*\".\r\n### Afficher les notifications de changement d'état de l'appareil\r\nAfficher les notifications détaillées de changement d'état de l'appareil, en affichant uniquement les notifications sélectionnées.",
+ "description": "## Introduction\r\n### Sauf si vous comprenez très bien la signification des options suivantes, veuillez les laisser par défaut.\r\n### Filtrer les appareils\r\nPrend en charge le filtrage des appareils en fonction du nom de la pièce et du type d'appareil, ainsi que le filtrage basé sur les appareils.\r\n### Mode de Contrôle\r\n- Automatique : Lorsqu'une passerelle Xiaomi est disponible dans le réseau local, Home Assistant enverra les commandes de contrôle des appareils via la passerelle pour permettre le contrôle local. Si aucune passerelle n'est disponible dans le réseau local, Home Assistant essaiera d'envoyer les commandes de contrôle des appareils via le protocole OT Xiaomi pour permettre le contrôle local. Seules si les conditions de contrôle local ci-dessus ne sont pas remplies, les commandes de contrôle des appareils seront envoyées via le cloud.\r\n- Cloud : Les commandes de contrôle des appareils sont envoyées uniquement via le cloud.\r\n### Mode de Débogage d’Actions\r\nPour les méthodes définies par les appareils MIoT-Spec-V2, en plus de générer une entité de notification, une entité de champ de texte sera également générée pour vous permettre d'envoyer des commandes de contrôle aux appareils lors du débogage.\r\n### Masquer les Entités Non Standard\r\nMasquer les entités générées par des instances MIoT-Spec-V2 non standard et commençant par \"*\".\r\n### Mode d'affichage du capteur binaire\r\nAffiche les capteurs binaires dans Xiaomi Home comme entité de capteur de texte ou entité de capteur binaire。\r\n### Afficher les notifications de changement d'état de l'appareil\r\nAfficher les notifications détaillées de changement d'état de l'appareil, en affichant uniquement les notifications sélectionnées.",
"data": {
"devices_filter": "Filtrer les Appareils",
"ctrl_mode": "Mode de Contrôle",
"action_debug": "Mode de Débogage d’Actions",
"hide_non_standard_entities": "Masquer les Entités Non Standard",
+ "display_binary_mode": "Mode d'affichage du capteur binaire",
"display_devices_changed_notify": "Afficher les notifications de changement d'état de l'appareil"
}
},
@@ -90,6 +91,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.",
@@ -118,6 +120,7 @@
"update_devices": "Mettre à jour la liste des appareils",
"action_debug": "Mode de débogage d'action",
"hide_non_standard_entities": "Masquer les entités générées non standard",
+ "display_binary_mode": "Mode d'affichage du capteur binaire",
"display_devices_changed_notify": "Afficher les notifications de changement d'état de l'appareil",
"update_trans_rules": "Mettre à jour les règles de conversion d'entités",
"update_lan_ctrl_config": "Mettre à jour la configuration de contrôle LAN",
diff --git a/custom_components/xiaomi_home/translations/it.json b/custom_components/xiaomi_home/translations/it.json
new file mode 100644
index 0000000..b384103
--- /dev/null
+++ b/custom_components/xiaomi_home/translations/it.json
@@ -0,0 +1,224 @@
+{
+ "config": {
+ "flow_title": "Integrazione Xiaomi Home",
+ "step": {
+ "eula": {
+ "title": "Avviso sui Rischi",
+ "description": "1. Le informazioni del tuo utente Xiaomi e le informazioni del dispositivo saranno memorizzate nel sistema Home Assistant. **Xiaomi non può garantire la sicurezza del meccanismo di archiviazione di Home Assistant**. Sei responsabile per prevenire il furto delle tue informazioni.\r\n2. Questa integrazione è mantenuta dalla comunità open-source. Potrebbero esserci problemi di stabilità o altri problemi. In caso di problemi o bug con questa integrazione, **dovresti cercare aiuto dalla comunità open-source piuttosto che contattare il servizio clienti Xiaomi**.\r\n3. È necessaria una certa abilità tecnica per mantenere il tuo ambiente operativo locale. L'integrazione non è user-friendly per i principianti.\r\n4. Si prega di leggere il file README prima di iniziare.\n\n5. Per garantire un uso stabile dell'integrazione e prevenire l'abuso dell'interfaccia, **questa integrazione può essere utilizzata solo in Home Assistant. Per i dettagli, consulta il LICENSE**.",
+ "data": {
+ "eula": "Sono consapevole dei rischi sopra indicati e sono disposto ad assumermi volontariamente qualsiasi rischio associato all'uso dell'integrazione."
+ }
+ },
+ "auth_config": {
+ "title": "Configurazione di base",
+ "description": "### Regione di Login\r\nSeleziona la regione del tuo account Xiaomi. Puoi trovarla nell'APP Xiaomi Home > Profilo (nel menu in basso) > Impostazioni aggiuntive > Informazioni su Xiaomi Home.\r\n### Lingua\r\nSeleziona la lingua dei nomi dei dispositivi e delle entità. Alcune frasi senza traduzione verranno visualizzate in inglese.\r\n### URL di reindirizzamento OAuth2\r\nL'indirizzo di reindirizzamento dell'autenticazione OAuth2 è **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant deve trovarsi nella stessa rete locale del terminale operativo corrente (ad esempio, il computer personale) e il terminale operativo deve poter accedere alla home page di Home Assistant tramite questo indirizzo. Altrimenti, l'autenticazione del login potrebbe fallire.\r\n### Nota\r\n- Per gli utenti con centinaia o più dispositivi Mi Home, l'aggiunta iniziale dell'integrazione richiederà del tempo. Si prega di essere pazienti.\r\n- Se Home Assistant è in esecuzione in un ambiente Docker, assicurarsi che la modalità di rete Docker sia impostata su host, altrimenti la funzionalità di controllo locale potrebbe non funzionare correttamente.\r\n- La funzionalità di controllo locale dell'integrazione ha alcune dipendenze. Si prega di leggere attentamente il README.",
+ "data": {
+ "cloud_server": "Regione di Login",
+ "integration_language": "Lingua",
+ "oauth_redirect_url": "URL di reindirizzamento OAuth2",
+ "network_detect_config": "Configurazione di rete integrata"
+ }
+ },
+ "network_detect_config": {
+ "title": "Configurazione di Rete Integrata",
+ "description": "## Introduzione all'uso\r\n### Indirizzo di Rilevamento della Rete\r\nUtilizzato per verificare se la rete funziona correttamente. Se non impostato, verrà utilizzato l'indirizzo di default del sistema. Se il controllo dell'indirizzo predefinito fallisce, puoi provare a inserire un indirizzo personalizzato.\r\n- Puoi inserire più indirizzi di rilevamento, separati da virgole, come `8.8.8.8,https://www.bing.com`\r\n- Se è un indirizzo IP, il rilevamento verrà eseguito tramite ping. Se è un indirizzo HTTP(s), il rilevamento verrà eseguito tramite richiesta HTTP GET.\r\n- Se desideri ripristinare l'indirizzo di rilevamento predefinito del sistema, inserisci una virgola `,` e fai clic su 'Avanti'.\r\n- **Questa configurazione è globale e le modifiche influenzeranno altre istanze di integrazione. Si prega di modificare con cautela.**\r\n### Controlla le Dipendenze di Rete\r\nControlla una per una le seguenti dipendenze di rete per vedere se sono accessibili. Se gli indirizzi correlati non sono accessibili, causerà problemi di integrazione.\r\n- Indirizzo di Autenticazione OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Indirizzo API HTTP di Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Indirizzo API SPEC di Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Indirizzo del Broker MQTT di Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
+ "data": {
+ "network_detect_addr": "Indirizzo di Rilevamento della Rete",
+ "check_network_deps": "Controlla le Dipendenze di Rete"
+ }
+ },
+ "oauth_error": {
+ "title": "Errore di Login",
+ "description": "Clicca AVANTI per riprovare."
+ },
+ "homes_select": {
+ "title": "Seleziona Famiglia e Dispositivo",
+ "description": "## Introduzione\r\n### Importa la Famiglia del Dispositivo\r\nL'integrazione aggiungerà i dispositivi dalla famiglia selezionata.\r\n### Modalità di Sincronizzazione del Nome della Stanza\r\nQuando si sincronizzano i dispositivi dall'app Mi Home a Home Assistant, la denominazione dell'area in Home Assistant seguirà le regole indicate di seguito. Si noti che il processo di sincronizzazione non modificherà le impostazioni di famiglia e stanza nell'app Mi Home.\r\n- Non sincronizzare: Il dispositivo non verrà aggiunto a nessuna area.\r\n- Altre opzioni: L'area a cui viene aggiunto il dispositivo verrà denominata come la famiglia o il nome della stanza nell'app Mi Home.\r\n### Impostazioni Avanzate\r\nMostra le impostazioni avanzate per modificare le opzioni di configurazione professionale dell'integrazione.\r\n\r\n \r\n### {nick_name} Ciao! Seleziona la famiglia a cui desideri aggiungere il dispositivo.",
+ "data": {
+ "home_infos": "Importa la Famiglia del Dispositivo",
+ "area_name_rule": "Modalità di Sincronizzazione del Nome della Stanza",
+ "advanced_options": "Impostazioni Avanzate"
+ }
+ },
+ "advanced_options": {
+ "title": "Impostazioni Avanzate",
+ "description": "## Introduzione\r\n### A meno che tu non abbia chiaro il significato delle seguenti opzioni, si prega di mantenere le impostazioni predefinite.\r\n### Filtra Dispositivi\r\nSupporta il filtraggio dei dispositivi per nome della stanza e tipo di dispositivo, e supporta anche il filtraggio delle dimensioni del dispositivo.\r\n### Modalità di Controllo\r\n- Automatico: Quando è disponibile un gateway hub centrale Xiaomi nella rete locale, Home Assistant darà priorità all'invio dei comandi di controllo dei dispositivi tramite il gateway hub centrale per ottenere il controllo locale. Se non è presente un gateway hub centrale nella rete locale, tenterà di inviare comandi di controllo tramite il protocollo OT di Xiaomi per ottenere il controllo locale. Solo quando le condizioni di controllo locale sopra indicate non sono soddisfatte, i comandi di controllo del dispositivo verranno inviati tramite il cloud.\r\n- Cloud: Tutti i comandi di controllo vengono inviati tramite il cloud.\r\n### Modalità di Debug delle Azioni\r\nPer i metodi definiti dal dispositivo MIoT-Spec-V2, oltre a generare entità di notifica, verrà generata anche un'entità di casella di input di testo. È possibile utilizzarla per inviare comandi di controllo al dispositivo durante il debug.\r\n### Nascondi Entità Generate Non Standard\r\nNasconde le entità generate da istanze non standard MIoT-Spec-V2 con nomi che iniziano con \"*\".\r\n### Modalità di visualizzazione del sensore binario\r\nVisualizza i sensori binari in Mi Home come entità del sensore di testo o entità del sensore binario。\r\n### Mostra Notifiche di Cambio di Stato del Dispositivo\r\nMostra notifiche dettagliate sui cambiamenti di stato del dispositivo, mostrando solo le notifiche selezionate.",
+ "data": {
+ "devices_filter": "Filtra Dispositivi",
+ "ctrl_mode": "Modalità di Controllo",
+ "action_debug": "Modalità di Debug delle Azioni",
+ "hide_non_standard_entities": "Nascondi Entità Generate Non Standard",
+ "display_binary_mode": "Modalità di visualizzazione del sensore binario",
+ "display_devices_changed_notify": "Mostra Notifiche di Cambio di Stato del Dispositivo"
+ }
+ },
+ "devices_filter": {
+ "title": "Filtra Dispositivi",
+ "description": "## Istruzioni per l'uso\r\nSupporta il filtraggio dei dispositivi per nome della stanza, tipo di accesso al dispositivo e modello del dispositivo, e supporta anche il filtraggio delle dimensioni del dispositivo. La logica di filtraggio è la seguente:\r\n- Prima, secondo la logica statistica, ottieni l'unione o l'intersezione di tutti gli elementi inclusi, poi ottieni l'intersezione o l'unione degli elementi esclusi e infine sottrai il [risultato riassuntivo incluso] dal [risultato riassuntivo escluso] per ottenere il [risultato del filtro].\r\n- Se non vengono selezionati elementi inclusi, significa che tutti sono inclusi.\r\n### Modalità di Filtro\r\n- Escludi: Rimuovi gli elementi indesiderati.\r\n- Includi: Includi gli elementi desiderati.\r\n### Logica Statistica\r\n- Logica AND: Prendi l'intersezione di tutti gli elementi nella stessa modalità.\r\n- Logica OR: Prendi l'unione di tutti gli elementi nella stessa modalità.\r\n\r\nPuoi anche andare alla pagina [Configurazione > Aggiorna Elenco Dispositivi] dell'elemento di integrazione e controllare [Filtra Dispositivi] per rifiltrare.",
+ "data": {
+ "room_filter_mode": "Filtra Stanze della Famiglia",
+ "room_list": "Stanze della Famiglia",
+ "type_filter_mode": "Filtra Tipo di Connessione del Dispositivo",
+ "type_list": "Tipo di Connessione del Dispositivo",
+ "model_filter_mode": "Filtra Modello del Dispositivo",
+ "model_list": "Modello del Dispositivo",
+ "devices_filter_mode": "Filtra Dispositivi",
+ "device_list": "Elenco Dispositivi",
+ "statistics_logic": "Logica Statistica"
+ }
+ }
+ },
+ "progress": {
+ "oauth": "### {link_left}Clicca qui per accedere{link_right}\r\n(Verrai reindirizzato automaticamente alla pagina successiva dopo un accesso riuscito)"
+ },
+ "error": {
+ "eula_not_agree": "Si prega di leggere l'avviso sui rischi.",
+ "get_token_error": "Impossibile recuperare le informazioni di autorizzazione per il login (token OAuth).",
+ "get_homeinfo_error": "Impossibile recuperare le informazioni della casa.",
+ "mdns_discovery_error": "Eccezione del servizio di scoperta dei dispositivi locali.",
+ "get_cert_error": "Impossibile recuperare il certificato del gateway centrale.",
+ "no_family_selected": "Nessuna casa selezionata.",
+ "no_devices": "La casa selezionata non ha dispositivi. Si prega di scegliere una casa che contiene dispositivi e continuare.",
+ "no_filter_devices": "I dispositivi filtrati sono vuoti. Si prega di selezionare criteri di filtro validi e continuare.",
+ "no_central_device": "[Modalità Gateway Hub Centrale] richiede un gateway hub centrale Xiaomi disponibile nella rete locale in cui esiste Home Assistant. Si prega di verificare se la casa selezionata soddisfa il requisito.",
+ "invalid_network_addr": "Rilevato indirizzo IP o indirizzo HTTP non valido, si prega di inserire un indirizzo valido.",
+ "invalid_ip_addr": "Rilevato indirizzo IP non raggiungibile, si prega di inserire un indirizzo IP valido.",
+ "invalid_http_addr": "Rilevato indirizzo HTTP non raggiungibile, si prega di inserire un indirizzo HTTP valido.",
+ "invalid_default_addr": "Indirizzo di rilevamento della rete predefinito non raggiungibile, si prega di verificare la configurazione della rete o utilizzare un indirizzo di rilevamento della rete personalizzato.",
+ "unreachable_oauth2_host": "Impossibile raggiungere l'indirizzo di autenticazione OAuth2, si prega di verificare la configurazione della rete.",
+ "unreachable_http_host": "Impossibile raggiungere l'indirizzo API HTTP di Xiaomi, si prega di verificare la configurazione della rete.",
+ "unreachable_spec_host": "Impossibile raggiungere l'indirizzo API SPEC di Xiaomi, si prega di verificare la configurazione della rete.",
+ "unreachable_mqtt_broker": "Impossibile raggiungere l'indirizzo del broker MQTT di Xiaomi, si prega di verificare la configurazione della rete."
+ },
+ "abort": {
+ "ha_uuid_get_failed": "Impossibile ottenere l'UUID di Home Assistant.",
+ "network_connect_error": "Configurazione fallita. La connessione di rete è anomala. Si prega di controllare la configurazione della rete del dispositivo.",
+ "already_configured": "La configurazione per questo utente è già completata. Si prega di andare alla pagina dell'integrazione e cliccare sul pulsante CONFIGURA per le modifiche.",
+ "invalid_auth_info": "Le informazioni di autenticazione sono scadute. Si prega di andare alla pagina dell'integrazione e cliccare sul pulsante CONFIGURA per ri-autenticarsi.",
+ "config_flow_error": "Errore di configurazione dell'integrazione: {error}."
+ }
+ },
+ "options": {
+ "step": {
+ "auth_config": {
+ "title": "Configurazione dell'Autenticazione",
+ "description": "Le informazioni di autenticazione locale sono scadute. Si prega di riavviare il processo di autenticazione.\r\n### Regione di Login Corrente: {cloud_server}\r\n### URL di reindirizzamento OAuth2\r\nL'indirizzo di reindirizzamento dell'autenticazione OAuth2 è **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant deve trovarsi nella stessa rete locale del terminale operativo corrente (ad esempio, il computer personale) e il terminale operativo deve poter accedere alla home page di Home Assistant tramite questo indirizzo. Altrimenti, l'autenticazione del login potrebbe fallire.",
+ "data": {
+ "oauth_redirect_url": "URL di reindirizzamento OAuth2"
+ }
+ },
+ "oauth_error": {
+ "title": "Si è verificato un errore durante il login.",
+ "description": "Clicca AVANTI per riprovare."
+ },
+ "config_options": {
+ "title": "Opzioni di Configurazione",
+ "description": "### Ciao, {nick_name}\r\n\r\nID Xiaomi: {uid}\r\nRegione di Login Corrente: {cloud_server}\r\n\r\nSeleziona le opzioni che desideri configurare, poi clicca AVANTI.",
+ "data": {
+ "integration_language": "Lingua dell'Integrazione",
+ "update_user_info": "Aggiorna le informazioni dell'utente",
+ "update_devices": "Aggiorna l'elenco dei dispositivi",
+ "action_debug": "Modalità debug per azione",
+ "hide_non_standard_entities": "Nascondi entità create non standard",
+ "display_binary_mode": "Modalità di visualizzazione del sensore binario",
+ "display_devices_changed_notify": "Mostra notifiche di cambio stato del dispositivo",
+ "update_trans_rules": "Aggiorna le regole di conversione delle entità",
+ "update_lan_ctrl_config": "Aggiorna configurazione del controllo LAN",
+ "network_detect_config": "Configurazione di Rete Integrata"
+ }
+ },
+ "update_user_info": {
+ "title": "Aggiorna il Nickname dell'Utente",
+ "description": "Ciao {nick_name}, puoi modificare il tuo nickname personalizzato qui sotto.",
+ "data": {
+ "nick_name": "Nickname"
+ }
+ },
+ "homes_select": {
+ "title": "Seleziona Nuovamente Casa e Dispositivi",
+ "description": "## Istruzioni per l'uso\r\n### Importa dispositivi da casa\r\nL'integrazione aggiungerà dispositivi dalle case selezionate.\r\n### Filtra Dispositivi\r\nSupporta il filtraggio dei dispositivi per nome della stanza, tipo di accesso al dispositivo e modello del dispositivo, e supporta anche il filtraggio delle dimensioni del dispositivo. **{local_count}** dispositivi sono stati filtrati.\r\n### Modalità di Controllo\r\n- Automatico: Quando è disponibile un gateway hub centrale Xiaomi nella rete locale, Home Assistant darà priorità all'invio dei comandi di controllo dei dispositivi tramite il gateway hub centrale per ottenere il controllo locale. Se non è presente un gateway hub centrale nella rete locale, tenterà di inviare comandi di controllo tramite la funzione di controllo LAN di Xiaomi. Solo quando le condizioni di controllo locale sopra indicate non sono soddisfatte, i comandi di controllo del dispositivo verranno inviati tramite il cloud.\r\n- Cloud: Tutti i comandi di controllo vengono inviati tramite il cloud.",
+ "data": {
+ "home_infos": "Importa dispositivi da casa",
+ "devices_filter": "Filtra dispositivi",
+ "ctrl_mode": "Modalità di controllo"
+ }
+ },
+ "devices_filter": {
+ "title": "Filtra Dispositivi",
+ "description": "## Istruzioni per l'uso\r\nSupporta il filtraggio dei dispositivi per nome della stanza, tipo di accesso al dispositivo e modello del dispositivo, e supporta anche il filtraggio delle dimensioni del dispositivo. La logica di filtraggio è la seguente:\r\n- Prima, secondo la logica statistica, ottieni l'unione o l'intersezione di tutti gli elementi inclusi, poi ottieni l'intersezione o l'unione degli elementi esclusi e infine sottrai il [risultato riassuntivo incluso] dal [risultato riassuntivo escluso] per ottenere il [risultato del filtro].\r\n- Se non vengono selezionati elementi inclusi, significa che tutti sono inclusi.\r\n### Modalità di Filtro\r\n- Escludi: Rimuovi gli elementi indesiderati.\r\n- Includi: Includi gli elementi desiderati.\r\n### Logica Statistica\r\n- Logica AND: Prendi l'intersezione di tutti gli elementi nella stessa modalità.\r\n- Logica OR: Prendi l'unione di tutti gli elementi nella stessa modalità.\r\n\r\nPuoi anche andare alla pagina [Configurazione > Aggiorna Elenco Dispositivi] dell'elemento di integrazione e controllare [Filtra Dispositivi] per rifiltrare.",
+ "data": {
+ "room_filter_mode": "Filtra Stanze della Famiglia",
+ "room_list": "Stanze della Famiglia",
+ "type_filter_mode": "Filtra Tipo di Connessione del Dispositivo",
+ "type_list": "Tipo di Connessione del Dispositivo",
+ "model_filter_mode": "Filtra Modello del Dispositivo",
+ "model_list": "Modello del Dispositivo",
+ "devices_filter_mode": "Filtra Dispositivi",
+ "device_list": "Elenco Dispositivi",
+ "statistics_logic": "Logica Statistica"
+ }
+ },
+ "update_trans_rules": {
+ "title": "Aggiorna le Regole di Trasformazione delle Entità",
+ "description": "## Istruzioni per l'uso\r\n- Aggiorna le informazioni delle entità dei dispositivi nell'istanza dell'integrazione corrente, incluse la configurazione multilingue MIoT-Spec-V2, la traduzione booleana e il filtro dei modelli.\r\n- **Avviso**: Questa è una configurazione globale e aggiornerà la cache locale. Influenzando tutte le istanze di integrazione.\r\n- Questa operazione richiederà del tempo, si prega di essere pazienti. Seleziona \"Conferma Aggiornamento\" e clicca \"Avanti\" per iniziare l'aggiornamento di **{urn_count}** regole, altrimenti salta l'aggiornamento.",
+ "data": {
+ "confirm": "Conferma l'aggiornamento"
+ }
+ },
+ "update_lan_ctrl_config": {
+ "title": "Aggiorna configurazione del controllo LAN",
+ "description": "## Istruzioni per l'uso\r\nAggiorna le configurazioni per la funzione di controllo LAN di Xiaomi. Quando il cloud e il gateway centrale non possono controllare i dispositivi, l'integrazione tenterà di controllare i dispositivi tramite la LAN. Se nessuna scheda di rete è selezionata, la funzione di controllo LAN non avrà effetto.\r\n- Solo i dispositivi compatibili con MIoT-Spec-V2 nella LAN sono supportati. Alcuni dispositivi prodotti prima del 2020 potrebbero non supportare il controllo LAN o l'abbonamento LAN.\r\n- Seleziona la/le scheda/e di rete nella stessa rete dei dispositivi da controllare. È possibile selezionare più schede di rete. Se Home Assistant ha due o più connessioni alla rete locale a causa della selezione multipla delle schede di rete, si consiglia di selezionare quella con la migliore connessione di rete, altrimenti potrebbe avere un effetto negativo sui dispositivi.\r\n- Se ci sono dispositivi terminali (altoparlanti Xiaomi con schermo, telefono cellulare, ecc.) nella LAN che supportano il controllo locale, abilitare l'abbonamento LAN potrebbe causare anomalie nell'automazione locale e nei dispositivi.\r\n- **Avviso**: Questa è una configurazione globale. Influenzando tutte le istanze di integrazione. Usala con cautela.\r\n{notice_net_dup}",
+ "data": {
+ "net_interfaces": "Si prega di selezionare la scheda di rete da utilizzare",
+ "enable_subscribe": "Abilita Sottoscrizione LAN"
+ }
+ },
+ "network_detect_config": {
+ "title": "Configurazione di Rete Integrata",
+ "description": "## Introduzione all'uso\r\n### Indirizzo di Rilevamento della Rete\r\nUtilizzato per verificare se la rete funziona correttamente. Se non impostato, verrà utilizzato l'indirizzo di default del sistema. Se il controllo dell'indirizzo predefinito fallisce, puoi provare a inserire un indirizzo personalizzato.\r\n- Puoi inserire più indirizzi di rilevamento, separati da virgole, come `8.8.8.8,https://www.bing.com`\r\n- Se è un indirizzo IP, il rilevamento verrà eseguito tramite ping. Se è un indirizzo HTTP(s), il rilevamento verrà eseguito tramite richiesta HTTP GET.\r\n- Se desideri ripristinare l'indirizzo di rilevamento predefinito del sistema, inserisci una virgola `,` e fai clic su 'Avanti'.\r\n- **Questa configurazione è globale e le modifiche influenzeranno altre istanze di integrazione. Si prega di modificare con cautela.**\r\n### Controlla le Dipendenze di Rete\r\nControlla una per una le seguenti dipendenze di rete per vedere se sono accessibili. Se gli indirizzi correlati non sono accessibili, causerà problemi di integrazione.\r\n- Indirizzo di Autenticazione OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Indirizzo API HTTP di Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Indirizzo API SPEC di Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Indirizzo del Broker MQTT di Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
+ "data": {
+ "network_detect_addr": "Indirizzo di Rilevamento della Rete",
+ "check_network_deps": "Controlla le Dipendenze di Rete"
+ }
+ },
+ "config_confirm": {
+ "title": "Conferma Configurazione",
+ "description": "Ciao **{nick_name}**, si prega di confermare le informazioni di configurazione più recenti e poi fare clic su INVIA.\r\nL'integrazione verrà ricaricata utilizzando la configurazione aggiornata.\r\n\r\nLingua dell'Integrazione: \t{lang_new}\r\nSoprannome: \t{nick_name_new}\r\nModalità di debug per azione: \t{action_debug}\r\nNascondi entità create non standard: \t{hide_non_standard_entities}\r\nMostra notifiche di cambio stato del dispositivo:\t{display_devices_changed_notify}\r\nCambiamenti del Dispositivo: \tAggiungi **{devices_add}** dispositivi, Rimuovi **{devices_remove}** dispositivi\r\nCambiamenti delle regole di trasformazione: \tCi sono un totale di **{trans_rules_count}** regole, e aggiornate **{trans_rules_count_success}** regole",
+ "data": {
+ "confirm": "Conferma la modifica"
+ }
+ }
+ },
+ "progress": {
+ "oauth": "### {link_left}Clicca qui per riaccedere{link_right}"
+ },
+ "error": {
+ "not_auth": "Non autenticato. Si prega di fare clic sul link di autenticazione per autenticare l'identità dell'utente.",
+ "get_token_error": "Impossibile recuperare le informazioni di autorizzazione all'accesso (token OAuth).",
+ "get_homeinfo_error": "Impossibile recuperare le informazioni sulla casa.",
+ "get_cert_error": "Impossibile recuperare il certificato del gateway hub centrale.",
+ "no_devices": "Non ci sono dispositivi nella casa selezionata. Si prega di selezionare una casa con dispositivi e continuare.",
+ "no_filter_devices": "I dispositivi filtrati sono vuoti. Si prega di selezionare criteri di filtro validi e continuare.",
+ "no_family_selected": "Nessuna casa selezionata.",
+ "no_central_device": "[Modalità Gateway Hub Centrale] richiede un gateway hub centrale Xiaomi disponibile nella rete locale in cui esiste Home Assistant. Si prega di verificare se la casa selezionata soddisfa il requisito.",
+ "mdns_discovery_error": "Eccezione nel servizio di rilevamento dei dispositivi locali.",
+ "update_config_error": "Impossibile aggiornare le informazioni di configurazione.",
+ "not_confirm": "Le modifiche non sono confermate. Si prega di confermare la modifica prima di inviare.",
+ "invalid_network_addr": "Rilevato indirizzo IP o indirizzo HTTP non valido, si prega di inserire un indirizzo valido.",
+ "invalid_ip_addr": "Rilevato indirizzo IP non raggiungibile, si prega di inserire un indirizzo IP valido.",
+ "invalid_http_addr": "Rilevato indirizzo HTTP non raggiungibile, si prega di inserire un indirizzo HTTP valido.",
+ "invalid_default_addr": "Indirizzo di rilevamento della rete predefinito non raggiungibile, si prega di verificare la configurazione della rete o utilizzare un indirizzo di rilevamento della rete personalizzato.",
+ "unreachable_oauth2_host": "Impossibile raggiungere l'indirizzo di autenticazione OAuth2, si prega di verificare la configurazione della rete.",
+ "unreachable_http_host": "Impossibile raggiungere l'indirizzo API HTTP di Xiaomi, si prega di verificare la configurazione della rete.",
+ "unreachable_spec_host": "Impossibile raggiungere l'indirizzo API SPEC di Xiaomi, si prega di verificare la configurazione della rete.",
+ "unreachable_mqtt_broker": "Impossibile raggiungere l'indirizzo del broker MQTT di Xiaomi, si prega di verificare la configurazione della rete."
+ },
+ "abort": {
+ "network_connect_error": "Configurazione fallita. La connessione di rete è anomala. Si prega di controllare la configurazione della rete del dispositivo.",
+ "options_flow_error": "Errore di riconfigurazione dell'integrazione: {error}",
+ "re_add": "Si prega di riaggiungere l'integrazione. Messaggio di errore: {error}",
+ "storage_error": "Eccezione del modulo di archiviazione dell'integrazione. Si prega di riprovare o riaggiungere l'integrazione: {error}",
+ "inconsistent_account": "Le informazioni dell'account sono incoerenti."
+ }
+ }
+}
\ No newline at end of file
diff --git a/custom_components/xiaomi_home/translations/ja.json b/custom_components/xiaomi_home/translations/ja.json
index d63201b..2dda890 100644
--- a/custom_components/xiaomi_home/translations/ja.json
+++ b/custom_components/xiaomi_home/translations/ja.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "高度な設定オプション",
- "description": "## 紹介\r\n### 以下のオプションの意味がよくわからない場合は、デフォルトのままにしてください。\r\n### デバイスのフィルタリング\r\n部屋名とデバイスタイプでデバイスをフィルタリングすることができます。デバイスの次元でフィルタリングすることもできます。\r\n### コントロールモード\r\n- 自動:ローカルネットワーク内に利用可能なXiaomi中央ゲートウェイがある場合、Home Assistantはデバイス制御命令を送信するために優先的に中央ゲートウェイを使用します。ローカルネットワークに中央ゲートウェイがない場合、Xiaomi OTプロトコルを使用してデバイス制御命令を送信し、ローカル制御機能を実現します。上記のローカル制御条件が満たされない場合のみ、デバイス制御命令はクラウドを介して送信されます。\r\n- クラウド:制御命令はクラウドを介してのみ送信されます。\r\n### Actionデバッグモード\r\nデバイスが定義するMIoT-Spec-V2のメソッドに対して、通知エンティティを生成するだけでなく、デバイスに制御命令を送信するためのテキスト入力ボックスエンティティも生成されます。デバッグ時にデバイスに制御命令を送信するために使用できます。\r\n### 非標準生成エンティティを隠す\r\n「*」で始まる名前の非標準MIoT-Spec-V2インスタンスによって生成されたエンティティを非表示にします。\r\n### デバイスの状態変化通知を表示\r\nデバイスの状態変化通知を詳細に表示し、選択された通知のみを表示します。",
+ "description": "## 紹介\r\n### 以下のオプションの意味がよくわからない場合は、デフォルトのままにしてください。\r\n### デバイスのフィルタリング\r\n部屋名とデバイスタイプでデバイスをフィルタリングすることができます。デバイスの次元でフィルタリングすることもできます。\r\n### コントロールモード\r\n- 自動:ローカルネットワーク内に利用可能なXiaomi中央ゲートウェイがある場合、Home Assistantはデバイス制御命令を送信するために優先的に中央ゲートウェイを使用します。ローカルネットワークに中央ゲートウェイがない場合、Xiaomi OTプロトコルを使用してデバイス制御命令を送信し、ローカル制御機能を実現します。上記のローカル制御条件が満たされない場合のみ、デバイス制御命令はクラウドを介して送信されます。\r\n- クラウド:制御命令はクラウドを介してのみ送信されます。\r\n### Actionデバッグモード\r\nデバイスが定義するMIoT-Spec-V2のメソッドに対して、通知エンティティを生成するだけでなく、デバイスに制御命令を送信するためのテキスト入力ボックスエンティティも生成されます。デバッグ時にデバイスに制御命令を送信するために使用できます。\r\n### 非標準生成エンティティを隠す\r\n「*」で始まる名前の非標準MIoT-Spec-V2インスタンスによって生成されたエンティティを非表示にします。\r\n### バイナリセンサー表示モード\r\nXiaomi Homeのバイナリセンサーをテキストセンサーエンティティまたはバイナリセンサーエンティティとして表示します。\r\n### デバイスの状態変化通知を表示\r\nデバイスの状態変化通知を詳細に表示し、選択された通知のみを表示します。",
"data": {
"devices_filter": "デバイスをフィルタリング",
"ctrl_mode": "コントロールモード",
"action_debug": "Actionデバッグモード",
"hide_non_standard_entities": "非標準生成エンティティを隠す",
+ "display_binary_mode": "バイナリセンサー表示モード",
"display_devices_changed_notify": "デバイスの状態変化通知を表示"
}
},
@@ -90,6 +91,7 @@
"unreachable_mqtt_broker": "Xiaomi MQTT ブローカーアドレスにアクセスできません。ネットワーク設定を確認してください。"
},
"abort": {
+ "ha_uuid_get_failed": "Home Assistant インスタンスIDを取得できませんでした。",
"network_connect_error": "設定に失敗しました。ネットワーク接続に異常があります。デバイスのネットワーク設定を確認してください。",
"already_configured": "このユーザーはすでに設定が完了しています。統合ページにアクセスして、「設定」ボタンをクリックして設定を変更してください。",
"invalid_auth_info": "認証情報が期限切れになりました。統合ページにアクセスして、「設定」ボタンをクリックして再度認証してください。",
@@ -118,6 +120,7 @@
"update_devices": "デバイスリストを更新する",
"action_debug": "Action デバッグモード",
"hide_non_standard_entities": "非標準生成エンティティを非表示にする",
+ "display_binary_mode": "バイナリセンサー表示モード",
"display_devices_changed_notify": "デバイスの状態変化通知を表示",
"update_trans_rules": "エンティティ変換ルールを更新する",
"update_lan_ctrl_config": "LAN制御構成を更新する",
diff --git a/custom_components/xiaomi_home/translations/nl.json b/custom_components/xiaomi_home/translations/nl.json
index 6c4c436..c9d8e40 100644
--- a/custom_components/xiaomi_home/translations/nl.json
+++ b/custom_components/xiaomi_home/translations/nl.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "Geavanceerde Instellingen",
- "description": "## Inleiding\r\n### Tenzij u zeer goed op de hoogte bent van de betekenis van de volgende opties, houdt u de standaardinstellingen.\r\n### Apparaten filteren\r\nOndersteunt het filteren van apparaten op basis van kamer- en apparaattypen, en ondersteunt ook apparaatdimensiefiltering.\r\n### Besturingsmodus\r\n- Automatisch: Wanneer er een beschikbare Xiaomi centrale hubgateway in het lokale netwerk is, zal Home Assistant eerst apparaatbesturingsinstructies via de centrale hubgateway verzenden om lokale controlefunctionaliteit te bereiken. Als er geen centrale hub in het lokale netwerk is, zal het proberen om besturingsinstructies via het Xiaomi OT-protocol te verzenden om lokale controlefunctionaliteit te bereiken. Alleen als de bovenstaande lokale controlevoorwaarden niet worden vervuld, worden apparaatbesturingsinstructies via de cloud verzonden.\r\n- Cloud: Besturingsinstructies worden alleen via de cloud verzonden.\r\n### Actie-debugmodus\r\nVoor methoden die zijn gedefinieerd in de MIoT-Spec-V2 van het apparaat, wordt naast het genereren van een meldingsentiteit ook een tekstinvoerveldentiteit gegenereerd. U kunt dit gebruiken om besturingsinstructies naar het apparaat te sturen tijdens het debuggen.\r\n### Niet-standaard entiteiten verbergen\r\nVerberg entiteiten die zijn gegenereerd door niet-standaard MIoT-Spec-V2-instanties die beginnen met \"*\".\r\n### Apparaatstatuswijzigingen weergeven\r\nGedetailleerde apparaatstatuswijzigingen weergeven, alleen de geselecteerde meldingen weergeven.",
+ "description": "## Inleiding\r\n### Tenzij u zeer goed op de hoogte bent van de betekenis van de volgende opties, houdt u de standaardinstellingen.\r\n### Apparaten filteren\r\nOndersteunt het filteren van apparaten op basis van kamer- en apparaattypen, en ondersteunt ook apparaatdimensiefiltering.\r\n### Besturingsmodus\r\n- Automatisch: Wanneer er een beschikbare Xiaomi centrale hubgateway in het lokale netwerk is, zal Home Assistant eerst apparaatbesturingsinstructies via de centrale hubgateway verzenden om lokale controlefunctionaliteit te bereiken. Als er geen centrale hub in het lokale netwerk is, zal het proberen om besturingsinstructies via het Xiaomi OT-protocol te verzenden om lokale controlefunctionaliteit te bereiken. Alleen als de bovenstaande lokale controlevoorwaarden niet worden vervuld, worden apparaatbesturingsinstructies via de cloud verzonden.\r\n- Cloud: Besturingsinstructies worden alleen via de cloud verzonden.\r\n### Actie-debugmodus\r\nVoor methoden die zijn gedefinieerd in de MIoT-Spec-V2 van het apparaat, wordt naast het genereren van een meldingsentiteit ook een tekstinvoerveldentiteit gegenereerd. U kunt dit gebruiken om besturingsinstructies naar het apparaat te sturen tijdens het debuggen.\r\n### Niet-standaard entiteiten verbergen\r\nVerberg entiteiten die zijn gegenereerd door niet-standaard MIoT-Spec-V2-instanties die beginnen met \"*\".\r\n### Binaire sensorweergavemodus\r\nToont binaire sensoren in Xiaomi Home als tekstsensor-entiteit of binairesensor-entiteit。\r\n### Apparaatstatuswijzigingen weergeven\r\nGedetailleerde apparaatstatuswijzigingen weergeven, alleen de geselecteerde meldingen weergeven.",
"data": {
"devices_filter": "Apparaten filteren",
"ctrl_mode": "Besturingsmodus",
"action_debug": "Actie-debugmodus",
"hide_non_standard_entities": "Niet-standaard entiteiten verbergen",
+ "display_binary_mode": "Binaire sensorweergavemodus",
"display_devices_changed_notify": "Apparaatstatuswijzigingen weergeven"
}
},
@@ -90,6 +91,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.",
@@ -118,6 +120,7 @@
"update_devices": "Werk apparatenlijst bij",
"action_debug": "Debugmodus voor actie",
"hide_non_standard_entities": "Verberg niet-standaard gemaakte entiteiten",
+ "display_binary_mode": "Binaire sensorweergavemodus",
"display_devices_changed_notify": "Apparaatstatuswijzigingen weergeven",
"update_trans_rules": "Werk entiteitsconversieregels bij",
"update_lan_ctrl_config": "Werk LAN controleconfiguratie bij",
diff --git a/custom_components/xiaomi_home/translations/pt-BR.json b/custom_components/xiaomi_home/translations/pt-BR.json
index 0c453b5..12286d5 100644
--- a/custom_components/xiaomi_home/translations/pt-BR.json
+++ b/custom_components/xiaomi_home/translations/pt-BR.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "Configurações Avançadas",
- "description": "## Introdução\r\n### A menos que você entenda claramente o significado das opções a seguir, mantenha as configurações padrão.\r\n### Filtrar Dispositivos\r\nSuporte para filtrar dispositivos por nome da sala e tipo de dispositivo, bem como filtragem por família.\r\n### Modo de Controle\r\n- Automático: Quando um gateway central Xiaomi disponível na rede local está disponível, o Home Assistant enviará comandos de controle de dispositivo através do gateway central para realizar a função de controle local. Quando não há gateway central na rede local, ele tentará enviar comandos de controle através do protocolo OT da Xiaomi para realizar a função de controle local. Somente quando as condições de controle local acima não forem atendidas, os comandos de controle do dispositivo serão enviados através da nuvem.\r\n- Nuvem: Os comandos de controle são enviados apenas através da nuvem.\r\n### Modo de Depuração de Ações\r\nPara métodos definidos pelo MIoT-Spec-V2 do dispositivo, além de gerar uma entidade de notificação, também será gerada uma entidade de caixa de texto para você enviar comandos de controle ao dispositivo durante a depuração.\r\n### Ocultar Entidades Geradas Não Padrão\r\nOcultar entidades geradas por instâncias MIoT-Spec-V2 não padrão que começam com \"*\".\r\n### Exibir notificações de mudança de status do dispositivo\r\nExibir notificações detalhadas de mudança de status do dispositivo, mostrando apenas as notificações selecionadas.",
+ "description": "## Introdução\r\n### A menos que você entenda claramente o significado das opções a seguir, mantenha as configurações padrão.\r\n### Filtrar Dispositivos\r\nSuporte para filtrar dispositivos por nome da sala e tipo de dispositivo, bem como filtragem por família.\r\n### Modo de Controle\r\n- Automático: Quando um gateway central Xiaomi disponível na rede local está disponível, o Home Assistant enviará comandos de controle de dispositivo através do gateway central para realizar a função de controle local. Quando não há gateway central na rede local, ele tentará enviar comandos de controle através do protocolo OT da Xiaomi para realizar a função de controle local. Somente quando as condições de controle local acima não forem atendidas, os comandos de controle do dispositivo serão enviados através da nuvem.\r\n- Nuvem: Os comandos de controle são enviados apenas através da nuvem.\r\n### Modo de Depuração de Ações\r\nPara métodos definidos pelo MIoT-Spec-V2 do dispositivo, além de gerar uma entidade de notificação, também será gerada uma entidade de caixa de texto para você enviar comandos de controle ao dispositivo durante a depuração.\r\n### Ocultar Entidades Geradas Não Padrão\r\nOcultar entidades geradas por instâncias MIoT-Spec-V2 não padrão que começam com \"*\".\r\n### Modo de exibição do sensor binário\r\nExibe sensores binários no Xiaomi Home como entidade de sensor de texto ou entidade de sensor binário。\r\n### Exibir notificações de mudança de status do dispositivo\r\nExibir notificações detalhadas de mudança de status do dispositivo, mostrando apenas as notificações selecionadas.",
"data": {
"devices_filter": "Filtrar Dispositivos",
"ctrl_mode": "Modo de Controle",
"action_debug": "Modo de Depuração de Ações",
"hide_non_standard_entities": "Ocultar Entidades Geradas Não Padrão",
+ "display_binary_mode": "Modo de exibição do sensor binário",
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo"
}
},
@@ -90,6 +91,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.",
@@ -118,6 +120,7 @@
"update_devices": "Atualizar lista de dispositivos",
"action_debug": "Modo de depuração para ação",
"hide_non_standard_entities": "Ocultar entidades não padrão criadas",
+ "display_binary_mode": "Modo de exibição do sensor binário",
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo",
"update_trans_rules": "Atualizar regras de conversão de entidades",
"update_lan_ctrl_config": "Atualizar configuração de controle LAN",
diff --git a/custom_components/xiaomi_home/translations/pt.json b/custom_components/xiaomi_home/translations/pt.json
index 787ddcd..2287585 100644
--- a/custom_components/xiaomi_home/translations/pt.json
+++ b/custom_components/xiaomi_home/translations/pt.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "Opções Avançadas",
- "description": "## Introdução\r\n### A menos que você entenda claramente o significado das opções abaixo, mantenha as configurações padrão.\r\n### Filtrar Dispositivos\r\nSuporte para filtrar dispositivos por nome da sala e tipo de dispositivo, bem como filtragem por família.\r\n### Modo de Controle\r\n- Automático: Quando um gateway central Xiaomi está disponível na rede local, o Home Assistant enviará comandos de controlo de dispositivos através do gateway central para realizar o controlo local. Quando não há gateway central na rede local, tentará enviar comandos de controlo através do protocolo Xiaomi OT para realizar o controlo local. Apenas quando as condições de controlo local acima não são atendidas, os comandos de controlo de dispositivos serão enviados através da nuvem.\r\n- Nuvem: Os comandos de controlo são enviados apenas através da nuvem.\r\n### Modo de Depuração de Ações\r\nPara métodos definidos pelo MIoT-Spec-V2, além de gerar uma entidade de notificação, também será gerada uma entidade de caixa de texto para depuração de controlo de dispositivos.\r\n### Ocultar Entidades Geradas Não Padrão\r\nOcultar entidades geradas por instâncias MIoT-Spec-V2 não padrão, cujos nomes começam com \"*\".\r\n### Exibir notificações de mudança de status do dispositivo\r\nExibir notificações detalhadas de mudança de status do dispositivo, mostrando apenas as notificações selecionadas.",
+ "description": "## Introdução\r\n### A menos que você entenda claramente o significado das opções abaixo, mantenha as configurações padrão.\r\n### Filtrar Dispositivos\r\nSuporte para filtrar dispositivos por nome da sala e tipo de dispositivo, bem como filtragem por família.\r\n### Modo de Controle\r\n- Automático: Quando um gateway central Xiaomi está disponível na rede local, o Home Assistant enviará comandos de controlo de dispositivos através do gateway central para realizar o controlo local. Quando não há gateway central na rede local, tentará enviar comandos de controlo através do protocolo Xiaomi OT para realizar o controlo local. Apenas quando as condições de controlo local acima não são atendidas, os comandos de controlo de dispositivos serão enviados através da nuvem.\r\n- Nuvem: Os comandos de controlo são enviados apenas através da nuvem.\r\n### Modo de Depuração de Ações\r\nPara métodos definidos pelo MIoT-Spec-V2, além de gerar uma entidade de notificação, também será gerada uma entidade de caixa de texto para depuração de controlo de dispositivos.\r\n### Ocultar Entidades Geradas Não Padrão\r\nOcultar entidades geradas por instâncias MIoT-Spec-V2 não padrão, cujos nomes começam com \"*\".\r\n### Modo de exibição do sensor binário\r\nExibe sensores binários no Xiaomi Home como entidade de sensor de texto ou entidade de sensor binário。\r\n### Exibir notificações de mudança de status do dispositivo\r\nExibir notificações detalhadas de mudança de status do dispositivo, mostrando apenas as notificações selecionadas.",
"data": {
"devices_filter": "Filtrar Dispositivos",
"ctrl_mode": "Modo de Controlo",
"action_debug": "Modo de Depuração de Ações",
"hide_non_standard_entities": "Ocultar Entidades Geradas Não Padrão",
+ "display_binary_mode": "Modo de exibição do sensor binário",
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo"
}
},
@@ -90,6 +91,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.",
@@ -118,6 +120,7 @@
"update_devices": "Atualizar lista de dispositivos",
"action_debug": "Modo de depuração de ação",
"hide_non_standard_entities": "Ocultar entidades não padrão",
+ "display_binary_mode": "Modo de exibição do sensor binário",
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo",
"update_trans_rules": "Atualizar regras de conversão de entidades",
"update_lan_ctrl_config": "Atualizar configuração de controlo LAN",
diff --git a/custom_components/xiaomi_home/translations/ru.json b/custom_components/xiaomi_home/translations/ru.json
index 7e06055..fba3edc 100644
--- a/custom_components/xiaomi_home/translations/ru.json
+++ b/custom_components/xiaomi_home/translations/ru.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "Расширенные настройки",
- "description": "## Введение\r\n### Если вы не очень хорошо понимаете значение следующих параметров, оставьте их по умолчанию.\r\n### Фильтрация устройств\r\nПоддерживает фильтрацию устройств по названию комнаты и типу устройства, а также фильтрацию по уровню устройства.\r\n### Режим управления\r\n- Автоматически: при наличии доступного центрального шлюза Xiaomi в локальной сети Home Assistant Home Assistant будет отправлять команды управления устройствами через центральный шлюз для локального управления. Если центрального шлюза нет в локальной сети, Home Assistant попытается отправить команды управления устройствами через протокол OT Xiaomi для локального управления. Только если вышеуказанные условия локального управления не выполняются, команды управления устройствами будут отправляться через облако.\r\n- Облако: команды управления отправляются только через облако.\r\n### Режим отладки действий\r\nДля методов, определенных устройством MIoT-Spec-V2, помимо создания уведомления, будет создана сущность текстового поля, которую вы можете использовать для отправки команд управления устройством во время отладки.\r\n### Скрыть нестандартные сущности\r\nСкрыть сущности, созданные нестандартными экземплярами MIoT-Spec-V2, имена которых начинаются с «*».\r\n### Отображать уведомления о изменении состояния устройства\r\nОтображать подробные уведомления о изменении состояния устройства, показывая только выбранные уведомления.",
+ "description": "## Введение\r\n### Если вы не очень хорошо понимаете значение следующих параметров, оставьте их по умолчанию.\r\n### Фильтрация устройств\r\nПоддерживает фильтрацию устройств по названию комнаты и типу устройства, а также фильтрацию по уровню устройства.\r\n### Режим управления\r\n- Автоматически: при наличии доступного центрального шлюза Xiaomi в локальной сети Home Assistant Home Assistant будет отправлять команды управления устройствами через центральный шлюз для локального управления. Если центрального шлюза нет в локальной сети, Home Assistant попытается отправить команды управления устройствами через протокол OT Xiaomi для локального управления. Только если вышеуказанные условия локального управления не выполняются, команды управления устройствами будут отправляться через облако.\r\n- Облако: команды управления отправляются только через облако.\r\n### Режим отладки действий\r\nДля методов, определенных устройством MIoT-Spec-V2, помимо создания уведомления, будет создана сущность текстового поля, которую вы можете использовать для отправки команд управления устройством во время отладки.\r\n### Скрыть нестандартные сущности\r\nСкрыть сущности, созданные нестандартными экземплярами MIoT-Spec-V2, имена которых начинаются с «*».\r\n### Режим отображения бинарного датчика\r\nОтображает бинарные датчики в Xiaomi Home как сущность текстового датчика или сущность бинарного датчика。\r\n### Отображать уведомления о изменении состояния устройства\r\nОтображать подробные уведомления о изменении состояния устройства, показывая только выбранные уведомления.",
"data": {
"devices_filter": "Фильтрация устройств",
"ctrl_mode": "Режим управления",
"action_debug": "Режим отладки действий",
"hide_non_standard_entities": "Скрыть нестандартные сущности",
+ "display_binary_mode": "Режим отображения бинарного датчика",
"display_devices_changed_notify": "Отображать уведомления о изменении состояния устройства"
}
},
@@ -90,6 +91,7 @@
"unreachable_mqtt_broker": "Не удается подключиться к адресу MQTT брокера Xiaomi, проверьте настройки сети."
},
"abort": {
+ "ha_uuid_get_failed": "Не удалось получить UUID Home Assistant.",
"network_connect_error": "Ошибка настройки. Сетевое подключение недоступно. Проверьте настройки сети устройства.",
"already_configured": "Этот пользователь уже настроен. Перейдите на страницу интеграции и нажмите кнопку «Настроить», чтобы изменить настройки.",
"invalid_auth_info": "Информация об авторизации истекла. Перейдите на страницу интеграции и нажмите кнопку «Настроить», чтобы переавторизоваться.",
@@ -118,6 +120,7 @@
"update_devices": "Обновить список устройств",
"action_debug": "Режим отладки Action",
"hide_non_standard_entities": "Скрыть нестандартные сущности",
+ "display_binary_mode": "Режим отображения бинарного датчика",
"display_devices_changed_notify": "Отображать уведомления о изменении состояния устройства",
"update_trans_rules": "Обновить правила преобразования сущностей",
"update_lan_ctrl_config": "Обновить конфигурацию управления LAN",
diff --git a/custom_components/xiaomi_home/translations/zh-Hans.json b/custom_components/xiaomi_home/translations/zh-Hans.json
index 1b6a138..67c134c 100644
--- a/custom_components/xiaomi_home/translations/zh-Hans.json
+++ b/custom_components/xiaomi_home/translations/zh-Hans.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "高级设置选项",
- "description": "## 使用介绍\r\n### 除非您非常清楚下列选项的含义,否则请保持默认。\r\n### 筛选设备\r\n支持按照家庭房间名称、设备接入类型、设备型号筛选设备,同时也支持设备维度筛选。\r\n### 控制模式\r\n- 自动:本地局域网内存在可用的小米中枢网关时, Home Assistant 会优先通过中枢网关发送设备控制指令,以实现本地化控制功能。本地局域网不存在中枢时,会尝试通过小米OT协议发送控制指令,以实现本地化控制功能。只有当上述本地化控制条件不满足时,设备控制指令才会通过云端发送。\r\n- 云端:控制指令仅通过云端发送。\r\n### Action 调试模式\r\n对于设备 MIoT-Spec-V2 定义的方法,在生成通知实体之外,还会生成一个文本输入框实体,您可以在调试时用它向设备发送控制指令。\r\n### 隐藏非标准生成实体\r\n隐藏名称以“*”开头的非标准 MIoT-Spec-V2 实例生成的实体。\r\n### 显示设备状态变化通知\r\n细化显示设备状态变化通知,只显示勾选的通知消息。",
+ "description": "## 使用介绍\r\n### 除非您非常清楚下列选项的含义,否则请保持默认。\r\n### 筛选设备\r\n支持按照家庭房间名称、设备接入类型、设备型号筛选设备,同时也支持设备维度筛选。\r\n### 控制模式\r\n- 自动:本地局域网内存在可用的小米中枢网关时, Home Assistant 会优先通过中枢网关发送设备控制指令,以实现本地化控制功能。本地局域网不存在中枢时,会尝试通过小米OT协议发送控制指令,以实现本地化控制功能。只有当上述本地化控制条件不满足时,设备控制指令才会通过云端发送。\r\n- 云端:控制指令仅通过云端发送。\r\n### Action 调试模式\r\n对于设备 MIoT-Spec-V2 定义的方法,在生成通知实体之外,还会生成一个文本输入框实体,您可以在调试时用它向设备发送控制指令。\r\n### 隐藏非标准生成实体\r\n隐藏名称以“*”开头的非标准 MIoT-Spec-V2 实例生成的实体。\r\n### 二进制传感器显示模式\r\n将米家中的二进制传感器显示为文本传感器实体或者二进制传感器实体。\r\n### 显示设备状态变化通知\r\n细化显示设备状态变化通知,只显示勾选的通知消息。",
"data": {
"devices_filter": "筛选设备",
"ctrl_mode": "控制模式",
"action_debug": "Action 调试模式",
"hide_non_standard_entities": "隐藏非标准生成实体",
+ "display_binary_mode": "二进制传感器显示模式",
"display_devices_changed_notify": "显示设备状态变化通知"
}
},
@@ -90,6 +91,7 @@
"unreachable_mqtt_broker": "无法访问小米 MQTT Broker 地址,请检查网络配置。"
},
"abort": {
+ "ha_uuid_get_failed": "获取 Home Assistant UUID 失败。",
"network_connect_error": "配置失败。网络连接异常,请检查设备网络配置。",
"already_configured": "该用户已配置完成。请进入集成页面,点击“配置”按钮修改配置。",
"invalid_auth_info": "认证信息已过期。请进入集成页面,点击“配置”按钮重新认证。",
@@ -118,6 +120,7 @@
"update_devices": "更新设备列表",
"action_debug": "Action 调试模式",
"hide_non_standard_entities": "隐藏非标准生成实体",
+ "display_binary_mode": "二进制传感器显示模式",
"display_devices_changed_notify": "显示设备状态变化通知",
"update_trans_rules": "更新实体转换规则",
"update_lan_ctrl_config": "更新局域网控制配置",
diff --git a/custom_components/xiaomi_home/translations/zh-Hant.json b/custom_components/xiaomi_home/translations/zh-Hant.json
index 7fcfb67..68cc982 100644
--- a/custom_components/xiaomi_home/translations/zh-Hant.json
+++ b/custom_components/xiaomi_home/translations/zh-Hant.json
@@ -42,12 +42,13 @@
},
"advanced_options": {
"title": "高級設置選項",
- "description": "## 使用介紹\r\n### 除非您非常清楚下列選項的含義,否則請保持默認。\r\n### 篩選設備\r\n支持按照房間名稱和設備類型篩選設備,同時也支持設備維度篩選。\r\n### 控制模式\r\n- 自動:本地局域網內存在可用的小米中樞網關時, Home Assistant 會優先通過中樞網關發送設備控制指令,以實現本地化控制功能。本地局域網不存在中樞時,會嘗試通過小米OT協議發送控制指令,以實現本地化控制功能。只有當上述本地化控制條件不滿足時,設備控制指令才會通過雲端發送。\r\n- 雲端:控制指令僅通過雲端發送。\r\n### Action 調試模式\r\n對於設備 MIoT-Spec-V2 定義的方法,在生成通知實體之外,還會生成一個文本輸入框實體,您可以在調試時用它向設備發送控制指令。\r\n### 隱藏非標準生成實體\r\n隱藏名稱以“*”開頭的非標準 MIoT-Spec-V2 實例生成的實體。\r\n### 顯示設備狀態變化通知\r\n細化顯示設備狀態變化通知,只顯示勾選的通知消息。",
+ "description": "## 使用介紹\r\n### 除非您非常清楚下列選項的含義,否則請保持默認。\r\n### 篩選設備\r\n支持按照房間名稱和設備類型篩選設備,同時也支持設備維度篩選。\r\n### 控制模式\r\n- 自動:本地局域網內存在可用的小米中樞網關時, Home Assistant 會優先通過中樞網關發送設備控制指令,以實現本地化控制功能。本地局域網不存在中樞時,會嘗試通過小米OT協議發送控制指令,以實現本地化控制功能。只有當上述本地化控制條件不滿足時,設備控制指令才會通過雲端發送。\r\n- 雲端:控制指令僅通過雲端發送。\r\n### Action 調試模式\r\n對於設備 MIoT-Spec-V2 定義的方法,在生成通知實體之外,還會生成一個文本輸入框實體,您可以在調試時用它向設備發送控制指令。\r\n### 隱藏非標準生成實體\r\n隱藏名稱以“*”開頭的非標準 MIoT-Spec-V2 實例生成的實體。\r\n### 二進制傳感器顯示模式\r\n將米家中的二進制傳感器顯示為文本傳感器實體或者二進制傳感器實體。\r\n### 顯示設備狀態變化通知\r\n細化顯示設備狀態變化通知,只顯示勾選的通知消息。",
"data": {
"devices_filter": "篩選設備",
"ctrl_mode": "控制模式",
"action_debug": "Action 調試模式",
"hide_non_standard_entities": "隱藏非標準生成實體",
+ "display_binary_mode": "二進制傳感器顯示模式",
"display_devices_changed_notify": "顯示設備狀態變化通知"
}
},
@@ -90,6 +91,7 @@
"unreachable_mqtt_broker": "無法訪問小米 MQTT Broker 地址,請檢查網絡配置。"
},
"abort": {
+ "ha_uuid_get_failed": "獲取 Home Assistant UUID 失敗。",
"network_connect_error": "配置失敗。網絡連接異常,請檢查設備網絡配置。",
"already_configured": "該用戶已配置完成。請進入集成頁面,點擊“配置”按鈕修改配置。",
"invalid_auth_info": "認證信息已過期。請進入集成頁面,點擊“配置”按鈕重新認證。",
@@ -118,6 +120,7 @@
"update_devices": "更新設備列表",
"action_debug": "Action 調試模式",
"hide_non_standard_entities": "隱藏非標準生成實體",
+ "display_binary_mode": "二進制傳感器顯示模式",
"display_devices_changed_notify": "顯示設備狀態變化通知",
"update_trans_rules": "更新實體轉換規則",
"update_lan_ctrl_config": "更新局域網控制配置",
diff --git a/custom_components/xiaomi_home/vacuum.py b/custom_components/xiaomi_home/vacuum.py
index fda2d5a..232e676 100644
--- a/custom_components/xiaomi_home/vacuum.py
+++ b/custom_components/xiaomi_home/vacuum.py
@@ -120,28 +120,18 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
# properties
for prop in entity_data.props:
if prop.name == 'status':
- if (
- not isinstance(prop.value_list, list)
- or not prop.value_list
- ):
+ if not prop.value_list:
_LOGGER.error(
'invalid status value_list, %s', self.entity_id)
continue
- self._status_map = {
- item['value']: item['description']
- for item in prop.value_list}
+ self._status_map = prop.value_list.to_map()
self._prop_status = prop
elif prop.name == 'fan-level':
- if (
- not isinstance(prop.value_list, list)
- or not prop.value_list
- ):
+ if not prop.value_list:
_LOGGER.error(
'invalid fan-level value_list, %s', self.entity_id)
continue
- self._fan_level_map = {
- item['value']: item['description']
- for item in prop.value_list}
+ self._fan_level_map = prop.value_list.to_map()
self._attr_fan_speed_list = list(self._fan_level_map.values())
self._attr_supported_features |= VacuumEntityFeature.FAN_SPEED
self._prop_fan_level = prop
@@ -202,7 +192,7 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
@property
def state(self) -> Optional[str]:
"""Return the current state of the vacuum cleaner."""
- return self.get_map_description(
+ return self.get_map_value(
map_=self._status_map,
key=self.get_prop_value(prop=self._prop_status))
@@ -214,6 +204,6 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
@property
def fan_speed(self) -> Optional[str]:
"""Return the current fan speed of the vacuum cleaner."""
- return self.get_map_description(
+ return self.get_map_value(
map_=self._fan_level_map,
key=self.get_prop_value(prop=self._prop_fan_level))
diff --git a/custom_components/xiaomi_home/water_heater.py b/custom_components/xiaomi_home/water_heater.py
index aa7fe67..aba6093 100644
--- a/custom_components/xiaomi_home/water_heater.py
+++ b/custom_components/xiaomi_home/water_heater.py
@@ -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[Any, Any]]
def __init__(
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
@@ -106,7 +106,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
self._prop_temp = None
self._prop_target_temp = None
self._prop_mode = None
- self._mode_list = None
+ self._mode_map = None
# properties
for prop in entity_data.props:
@@ -115,7 +115,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
self._prop_on = prop
# temperature
if prop.name == 'temperature':
- if isinstance(prop.value_range, dict):
+ if prop.value_range:
if (
self._attr_temperature_unit is None
and prop.external_unit
@@ -128,9 +128,14 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
self.entity_id)
# target-temperature
if prop.name == 'target-temperature':
- self._attr_min_temp = prop.value_range['min']
- self._attr_max_temp = prop.value_range['max']
- self._attr_precision = prop.value_range['step']
+ if not prop.value_range:
+ _LOGGER.error(
+ 'invalid target-temperature value_range format, %s',
+ self.entity_id)
+ continue
+ self._attr_min_temp = prop.value_range.min_
+ self._attr_max_temp = prop.value_range.max_
+ self._attr_precision = prop.value_range.step
if self._attr_temperature_unit is None and prop.external_unit:
self._attr_temperature_unit = prop.external_unit
self._attr_supported_features |= (
@@ -138,17 +143,12 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
self._prop_target_temp = prop
# mode
if prop.name == 'mode':
- if (
- not isinstance(prop.value_list, list)
- or not prop.value_list
- ):
+ if not prop.value_list:
_LOGGER.error(
'mode value_list is None, %s', self.entity_id)
continue
- self._mode_list = {
- item['value']: item['description']
- for item in prop.value_list}
- self._attr_operation_list = list(self._mode_list.values())
+ self._mode_map = prop.value_list.to_map()
+ self._attr_operation_list = list(self._mode_map.values())
self._attr_supported_features |= (
WaterHeaterEntityFeature.OPERATION_MODE)
self._prop_mode = prop
@@ -184,7 +184,9 @@ 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_key(
+ map_=self._mode_map,
+ value=operation_mode))
async def async_turn_away_mode_on(self) -> None:
"""Set the water heater to away mode."""
@@ -207,20 +209,6 @@ 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(
+ return self.get_map_value(
+ map_=self._mode_map,
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
diff --git a/test/check_rule_format.py b/test/check_rule_format.py
index 3c20afa..18ce034 100644
--- a/test/check_rule_format.py
+++ b/test/check_rule_format.py
@@ -1,11 +1,14 @@
# -*- coding: utf-8 -*-
"""Test rule format."""
import json
+import logging
from os import listdir, path
from typing import Optional
import pytest
import yaml
+_LOGGER = logging.getLogger(__name__)
+
ROOT_PATH: str = path.dirname(path.abspath(__file__))
TRANS_RELATIVE_PATH: str = path.join(
ROOT_PATH, '../custom_components/xiaomi_home/translations')
@@ -13,13 +16,10 @@ MIOT_I18N_RELATIVE_PATH: str = path.join(
ROOT_PATH, '../custom_components/xiaomi_home/miot/i18n')
SPEC_BOOL_TRANS_FILE = path.join(
ROOT_PATH,
- '../custom_components/xiaomi_home/miot/specs/bool_trans.json')
-SPEC_MULTI_LANG_FILE = path.join(
- ROOT_PATH,
- '../custom_components/xiaomi_home/miot/specs/multi_lang.json')
+ '../custom_components/xiaomi_home/miot/specs/bool_trans.yaml')
SPEC_FILTER_FILE = path.join(
ROOT_PATH,
- '../custom_components/xiaomi_home/miot/specs/spec_filter.json')
+ '../custom_components/xiaomi_home/miot/specs/spec_filter.yaml')
def load_json_file(file_path: str) -> Optional[dict]:
@@ -27,10 +27,10 @@ def load_json_file(file_path: str) -> Optional[dict]:
with open(file_path, 'r', encoding='utf-8') as file:
return json.load(file)
except FileNotFoundError:
- print(file_path, 'is not found.')
+ _LOGGER.info('%s is not found.', file_path,)
return None
except json.JSONDecodeError:
- print(file_path, 'is not a valid JSON file.')
+ _LOGGER.info('%s is not a valid JSON file.', file_path)
return None
@@ -44,13 +44,19 @@ def load_yaml_file(file_path: str) -> Optional[dict]:
with open(file_path, 'r', encoding='utf-8') as file:
return yaml.safe_load(file)
except FileNotFoundError:
- print(file_path, 'is not found.')
+ _LOGGER.info('%s is not found.', file_path)
return None
except yaml.YAMLError:
- print(file_path, 'is not a valid YAML file.')
+ _LOGGER.info('%s, is not a valid YAML file.', file_path)
return None
+def save_yaml_file(file_path: str, data: dict) -> None:
+ with open(file_path, 'w', encoding='utf-8') as file:
+ yaml.safe_dump(
+ data, file, default_flow_style=False, allow_unicode=True, indent=2)
+
+
def dict_str_str(d: dict) -> bool:
"""restricted format: dict[str, str]"""
if not isinstance(d, dict):
@@ -116,61 +122,59 @@ def bool_trans(d: dict) -> bool:
return False
default_trans: dict = d['translate'].pop('default')
if not default_trans:
- print('default trans is empty')
+ _LOGGER.info('default trans is empty')
return False
default_keys: set[str] = set(default_trans.keys())
for key, trans in d['translate'].items():
trans_keys: set[str] = set(trans.keys())
if set(trans.keys()) != default_keys:
- print('bool trans inconsistent', key, default_keys, trans_keys)
+ _LOGGER.info(
+ 'bool trans inconsistent, %s, %s, %s',
+ key, default_keys, trans_keys)
return False
return True
def compare_dict_structure(dict1: dict, dict2: dict) -> bool:
if not isinstance(dict1, dict) or not isinstance(dict2, dict):
- print('invalid type')
+ _LOGGER.info('invalid type')
return False
if dict1.keys() != dict2.keys():
- print('inconsistent key values, ', dict1.keys(), dict2.keys())
+ _LOGGER.info(
+ 'inconsistent key values, %s, %s', dict1.keys(), dict2.keys())
return False
for key in dict1:
if isinstance(dict1[key], dict) and isinstance(dict2[key], dict):
if not compare_dict_structure(dict1[key], dict2[key]):
- print('inconsistent key values, dict, ', key)
+ _LOGGER.info(
+ 'inconsistent key values, dict, %s', key)
return False
elif isinstance(dict1[key], list) and isinstance(dict2[key], list):
if not all(
isinstance(i, type(j))
for i, j in zip(dict1[key], dict2[key])):
- print('inconsistent key values, list, ', key)
+ _LOGGER.info(
+ 'inconsistent key values, list, %s', key)
return False
elif not isinstance(dict1[key], type(dict2[key])):
- print('inconsistent key values, type, ', key)
+ _LOGGER.info(
+ 'inconsistent key values, type, %s', key)
return False
return True
def sort_bool_trans(file_path: str):
- trans_data: dict = load_json_file(file_path=file_path)
+ trans_data = load_yaml_file(file_path=file_path)
+ assert isinstance(trans_data, dict), f'{file_path} format error'
trans_data['data'] = dict(sorted(trans_data['data'].items()))
for key, trans in trans_data['translate'].items():
trans_data['translate'][key] = dict(sorted(trans.items()))
return trans_data
-def sort_multi_lang(file_path: str):
- multi_lang: dict = load_json_file(file_path=file_path)
- multi_lang = dict(sorted(multi_lang.items()))
- for urn, trans in multi_lang.items():
- multi_lang[urn] = dict(sorted(trans.items()))
- for lang, spec in multi_lang[urn].items():
- multi_lang[urn][lang] = dict(sorted(spec.items()))
- return multi_lang
-
-
def sort_spec_filter(file_path: str):
- filter_data: dict = load_json_file(file_path=file_path)
+ filter_data = load_yaml_file(file_path=file_path)
+ assert isinstance(filter_data, dict), f'{file_path} format error'
filter_data = dict(sorted(filter_data.items()))
for urn, spec in filter_data.items():
filter_data[urn] = dict(sorted(spec.items()))
@@ -179,30 +183,26 @@ def sort_spec_filter(file_path: str):
@pytest.mark.github
def test_bool_trans():
- data: dict = load_json_file(SPEC_BOOL_TRANS_FILE)
+ data = load_yaml_file(SPEC_BOOL_TRANS_FILE)
+ assert isinstance(data, dict)
assert data, f'load {SPEC_BOOL_TRANS_FILE} failed'
assert bool_trans(data), f'{SPEC_BOOL_TRANS_FILE} format error'
@pytest.mark.github
def test_spec_filter():
- data: dict = load_json_file(SPEC_FILTER_FILE)
+ data = load_yaml_file(SPEC_FILTER_FILE)
+ assert isinstance(data, dict)
assert data, f'load {SPEC_FILTER_FILE} failed'
assert spec_filter(data), f'{SPEC_FILTER_FILE} format error'
-@pytest.mark.github
-def test_multi_lang():
- data: dict = load_json_file(SPEC_MULTI_LANG_FILE)
- assert data, f'load {SPEC_MULTI_LANG_FILE} failed'
- assert nested_3_dict_str_str(data), f'{SPEC_MULTI_LANG_FILE} format error'
-
-
@pytest.mark.github
def test_miot_i18n():
for file_name in listdir(MIOT_I18N_RELATIVE_PATH):
file_path: str = path.join(MIOT_I18N_RELATIVE_PATH, file_name)
- data: dict = load_json_file(file_path)
+ data = load_json_file(file_path)
+ assert isinstance(data, dict)
assert data, f'load {file_path} failed'
assert nested_3_dict_str_str(data), f'{file_path} format error'
@@ -211,7 +211,8 @@ def test_miot_i18n():
def test_translations():
for file_name in listdir(TRANS_RELATIVE_PATH):
file_path: str = path.join(TRANS_RELATIVE_PATH, file_name)
- data: dict = load_json_file(file_path)
+ data = load_json_file(file_path)
+ assert isinstance(data, dict)
assert data, f'load {file_path} failed'
assert dict_str_dict(data), f'{file_path} format error'
@@ -228,27 +229,30 @@ def test_miot_lang_integrity():
i18n_names: set[str] = set(listdir(MIOT_I18N_RELATIVE_PATH))
assert len(i18n_names) == len(translations_names)
assert i18n_names == translations_names
- bool_trans_data: set[str] = load_json_file(SPEC_BOOL_TRANS_FILE)
+ bool_trans_data = load_yaml_file(SPEC_BOOL_TRANS_FILE)
+ assert isinstance(bool_trans_data, dict)
bool_trans_names: set[str] = set(
bool_trans_data['translate']['default'].keys())
assert len(bool_trans_names) == len(translations_names)
# Check translation files structure
- default_dict: dict = load_json_file(
+ default_dict = load_json_file(
path.join(TRANS_RELATIVE_PATH, integration_lang_list[0]))
for name in list(integration_lang_list)[1:]:
- compare_dict: dict = load_json_file(
+ compare_dict = load_json_file(
path.join(TRANS_RELATIVE_PATH, name))
if not compare_dict_structure(default_dict, compare_dict):
- print('compare_dict_structure failed /translations, ', name)
+ _LOGGER.info(
+ 'compare_dict_structure failed /translations, %s', name)
assert False
# Check i18n files structure
default_dict = load_json_file(
path.join(MIOT_I18N_RELATIVE_PATH, integration_lang_list[0]))
for name in list(integration_lang_list)[1:]:
- compare_dict: dict = load_json_file(
+ compare_dict = load_json_file(
path.join(MIOT_I18N_RELATIVE_PATH, name))
if not compare_dict_structure(default_dict, compare_dict):
- print('compare_dict_structure failed /miot/i18n, ', name)
+ _LOGGER.info(
+ 'compare_dict_structure failed /miot/i18n, %s', name)
assert False
@@ -261,19 +265,13 @@ def test_miot_data_sort():
'INTEGRATION_LANGUAGES not sorted, correct order\r\n'
f'{list(sort_langs.keys())}')
assert json.dumps(
- load_json_file(file_path=SPEC_BOOL_TRANS_FILE)) == json.dumps(
+ load_yaml_file(file_path=SPEC_BOOL_TRANS_FILE)) == json.dumps(
sort_bool_trans(file_path=SPEC_BOOL_TRANS_FILE)), (
f'{SPEC_BOOL_TRANS_FILE} not sorted, goto project root path'
' and run the following command sorting, ',
'pytest -s -v -m update ./test/check_rule_format.py')
assert json.dumps(
- load_json_file(file_path=SPEC_MULTI_LANG_FILE)) == json.dumps(
- sort_multi_lang(file_path=SPEC_MULTI_LANG_FILE)), (
- f'{SPEC_MULTI_LANG_FILE} not sorted, goto project root path'
- ' and run the following command sorting, ',
- 'pytest -s -v -m update ./test/check_rule_format.py')
- assert json.dumps(
- load_json_file(file_path=SPEC_FILTER_FILE)) == json.dumps(
+ load_yaml_file(file_path=SPEC_FILTER_FILE)) == json.dumps(
sort_spec_filter(file_path=SPEC_FILTER_FILE)), (
f'{SPEC_FILTER_FILE} not sorted, goto project root path'
' and run the following command sorting, ',
@@ -283,11 +281,8 @@ def test_miot_data_sort():
@pytest.mark.update
def test_sort_spec_data():
sort_data: dict = sort_bool_trans(file_path=SPEC_BOOL_TRANS_FILE)
- save_json_file(file_path=SPEC_BOOL_TRANS_FILE, data=sort_data)
- print(SPEC_BOOL_TRANS_FILE, 'formatted.')
- sort_data = sort_multi_lang(file_path=SPEC_MULTI_LANG_FILE)
- save_json_file(file_path=SPEC_MULTI_LANG_FILE, data=sort_data)
- print(SPEC_MULTI_LANG_FILE, 'formatted.')
+ save_yaml_file(file_path=SPEC_BOOL_TRANS_FILE, data=sort_data)
+ _LOGGER.info('%s formatted.', SPEC_BOOL_TRANS_FILE)
sort_data = sort_spec_filter(file_path=SPEC_FILTER_FILE)
- save_json_file(file_path=SPEC_FILTER_FILE, data=sort_data)
- print(SPEC_FILTER_FILE, 'formatted.')
+ save_yaml_file(file_path=SPEC_FILTER_FILE, data=sort_data)
+ _LOGGER.info('%s formatted.', SPEC_FILTER_FILE)
diff --git a/test/conftest.py b/test/conftest.py
index 9263402..9e9160a 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -1,16 +1,37 @@
# -*- coding: utf-8 -*-
"""Pytest fixtures."""
+import logging
+import random
import shutil
import pytest
from os import path, makedirs
+from uuid import uuid4
TEST_ROOT_PATH: str = path.dirname(path.abspath(__file__))
TEST_FILES_PATH: str = path.join(TEST_ROOT_PATH, 'miot')
TEST_CACHE_PATH: str = path.join(TEST_ROOT_PATH, 'test_cache')
+TEST_OAUTH2_REDIRECT_URL: str = 'http://homeassistant.local:8123'
TEST_LANG: str = 'zh-Hans'
TEST_UID: str = '123456789'
TEST_CLOUD_SERVER: str = 'cn'
+DOMAIN_CLOUD_CACHE: str = 'cloud_cache'
+
+_LOGGER = logging.getLogger(__name__)
+
+
+@pytest.fixture(scope='session', autouse=True)
+def set_logger():
+ logger = logging.getLogger()
+ logger.setLevel(logging.INFO)
+ console_handler = logging.StreamHandler()
+ console_handler.setLevel(logging.INFO)
+ formatter = logging.Formatter(
+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+ console_handler.setFormatter(formatter)
+ logger.addHandler(console_handler)
+ _LOGGER.info('set logger, %s', logger)
+
@pytest.fixture(scope='session', autouse=True)
def load_py_file():
@@ -20,10 +41,10 @@ def load_py_file():
'const.py',
'miot_cloud.py',
'miot_error.py',
- 'miot_ev.py',
'miot_i18n.py',
'miot_lan.py',
'miot_mdns.py',
+ 'miot_mips.py',
'miot_network.py',
'miot_spec.py',
'miot_storage.py']
@@ -35,31 +56,35 @@ def load_py_file():
TEST_ROOT_PATH, '../custom_components/xiaomi_home/miot',
file_name),
path.join(TEST_FILES_PATH, file_name))
- print('\nloaded test py files, ', file_list)
+ _LOGGER.info('\nloaded test py files, %s', file_list)
# Copy spec files to test folder
shutil.copytree(
src=path.join(
TEST_ROOT_PATH, '../custom_components/xiaomi_home/miot/specs'),
dst=path.join(TEST_FILES_PATH, 'specs'),
dirs_exist_ok=True)
- print('loaded spec test folder, specs')
+ _LOGGER.info('loaded spec test folder, specs')
# Copy lan files to test folder
shutil.copytree(
src=path.join(
TEST_ROOT_PATH, '../custom_components/xiaomi_home/miot/lan'),
dst=path.join(TEST_FILES_PATH, 'lan'),
dirs_exist_ok=True)
- print('loaded lan test folder, lan')
+ _LOGGER.info('loaded lan test folder, lan')
# Copy i18n files to test folder
shutil.copytree(
src=path.join(
TEST_ROOT_PATH, '../custom_components/xiaomi_home/miot/i18n'),
dst=path.join(TEST_FILES_PATH, 'i18n'),
dirs_exist_ok=True)
- print('loaded i18n test folder, i18n')
+ _LOGGER.info('loaded i18n test folder, i18n')
yield
+ # NOTICE: All test files and data (tokens, device information, etc.) will
+ # be deleted after the test is completed. For some test cases that
+ # require caching data, you can comment out the following code.
+
if path.exists(TEST_FILES_PATH):
shutil.rmtree(TEST_FILES_PATH)
print('\nremoved test files, ', TEST_FILES_PATH)
@@ -80,6 +105,11 @@ def test_cache_path() -> str:
return TEST_CACHE_PATH
+@pytest.fixture(scope='session')
+def test_oauth2_redirect_url() -> str:
+ return TEST_OAUTH2_REDIRECT_URL
+
+
@pytest.fixture(scope='session')
def test_lang() -> str:
return TEST_LANG
@@ -90,6 +120,53 @@ def test_uid() -> str:
return TEST_UID
+@pytest.fixture(scope='session')
+def test_random_did() -> str:
+ # Gen random did
+ return str(random.getrandbits(64))
+
+
+@pytest.fixture(scope='session')
+def test_uuid() -> str:
+ # Gen uuid
+ return uuid4().hex
+
+
@pytest.fixture(scope='session')
def test_cloud_server() -> str:
return TEST_CLOUD_SERVER
+
+
+@pytest.fixture(scope='session')
+def test_domain_cloud_cache() -> str:
+ return DOMAIN_CLOUD_CACHE
+
+
+@pytest.fixture(scope='session')
+def test_name_oauth2_info() -> str:
+ return f'{TEST_CLOUD_SERVER}_oauth2_info'
+
+
+@pytest.fixture(scope='session')
+def test_name_uid() -> str:
+ return f'{TEST_CLOUD_SERVER}_uid'
+
+
+@pytest.fixture(scope='session')
+def test_name_uuid() -> str:
+ return f'{TEST_CLOUD_SERVER}_uuid'
+
+
+@pytest.fixture(scope='session')
+def test_name_rd_did() -> str:
+ return f'{TEST_CLOUD_SERVER}_rd_did'
+
+
+@pytest.fixture(scope='session')
+def test_name_homes() -> str:
+ return f'{TEST_CLOUD_SERVER}_homes'
+
+
+@pytest.fixture(scope='session')
+def test_name_devices() -> str:
+ return f'{TEST_CLOUD_SERVER}_devices'
diff --git a/test/test_cloud.py b/test/test_cloud.py
new file mode 100755
index 0000000..f1c74b9
--- /dev/null
+++ b/test/test_cloud.py
@@ -0,0 +1,587 @@
+# -*- coding: utf-8 -*-
+"""Unit test for miot_cloud.py."""
+import asyncio
+import logging
+import time
+import webbrowser
+import pytest
+
+# pylint: disable=import-outside-toplevel, unused-argument
+_LOGGER = logging.getLogger(__name__)
+
+
+@pytest.mark.asyncio
+@pytest.mark.dependency()
+async def test_miot_oauth_async(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_oauth2_redirect_url: str,
+ test_uuid: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_uuid: str
+) -> dict:
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTOauthClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ local_uuid = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_uuid, type_=str)
+ uuid = str(local_uuid or test_uuid)
+ _LOGGER.info('uuid: %s', uuid)
+ miot_oauth = MIoTOauthClient(
+ client_id=OAUTH2_CLIENT_ID,
+ redirect_url=test_oauth2_redirect_url,
+ cloud_server=test_cloud_server,
+ uuid=uuid)
+
+ oauth_info = None
+ load_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ if (
+ isinstance(load_info, dict)
+ and 'access_token' in load_info
+ and 'expires_ts' in load_info
+ and load_info['expires_ts'] > int(time.time())
+ ):
+ _LOGGER.info('load oauth info, %s', load_info)
+ oauth_info = load_info
+ if oauth_info is None:
+ # gen oauth url
+ auth_url: str = miot_oauth.gen_auth_url()
+ assert isinstance(auth_url, str)
+ _LOGGER.info('auth url: %s', auth_url)
+ # get code
+ webbrowser.open(auth_url)
+ code: str = input('input code: ')
+ assert code is not None
+ # get access_token
+ res_obj = await miot_oauth.get_access_token_async(code=code)
+ assert res_obj is not None
+ oauth_info = res_obj
+ _LOGGER.info('get_access_token result: %s', res_obj)
+ rc = await miot_storage.save_async(
+ test_domain_cloud_cache, test_name_oauth2_info, oauth_info)
+ assert rc
+ _LOGGER.info('save oauth info')
+ rc = await miot_storage.save_async(
+ test_domain_cloud_cache, test_name_uuid, uuid)
+ assert rc
+ _LOGGER.info('save uuid')
+
+ access_token = oauth_info.get('access_token', None)
+ assert isinstance(access_token, str)
+ _LOGGER.info('access_token: %s', access_token)
+ refresh_token = oauth_info.get('refresh_token', None)
+ assert isinstance(refresh_token, str)
+ _LOGGER.info('refresh_token: %s', refresh_token)
+
+ await miot_oauth.deinit_async()
+ return oauth_info
+
+
+@pytest.mark.asyncio
+@pytest.mark.dependency(on=['test_miot_oauth_async'])
+async def test_miot_oauth_refresh_token(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_oauth2_redirect_url: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_uuid: str
+):
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTOauthClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ uuid = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_uuid, type_=str)
+ assert isinstance(uuid, str)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict)
+ assert 'access_token' in oauth_info
+ assert 'refresh_token' in oauth_info
+ assert 'expires_ts' in oauth_info
+ remaining_time = oauth_info['expires_ts'] - int(time.time())
+ _LOGGER.info('token remaining valid time: %ss', remaining_time)
+ # Refresh token
+ miot_oauth = MIoTOauthClient(
+ client_id=OAUTH2_CLIENT_ID,
+ redirect_url=test_oauth2_redirect_url,
+ cloud_server=test_cloud_server,
+ uuid=uuid)
+ refresh_token = oauth_info.get('refresh_token', None)
+ assert refresh_token
+ update_info = await miot_oauth.refresh_access_token_async(
+ refresh_token=refresh_token)
+ assert update_info
+ assert 'access_token' in update_info
+ assert 'refresh_token' in update_info
+ assert 'expires_ts' in update_info
+ remaining_time = update_info['expires_ts'] - int(time.time())
+ assert remaining_time > 0
+ _LOGGER.info('refresh token, remaining valid time: %ss', remaining_time)
+ # Save oauth2 info
+ rc = await miot_storage.save_async(
+ test_domain_cloud_cache, test_name_oauth2_info, update_info)
+ assert rc
+ _LOGGER.info('refresh token success, %s', update_info)
+
+ await miot_oauth.deinit_async()
+
+
+@pytest.mark.asyncio
+@pytest.mark.dependency()
+async def test_miot_cloud_get_nickname_async(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str
+):
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTHttpClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
+ access_token=oauth_info['access_token'])
+
+ # Get nickname
+ user_info = await miot_http.get_user_info_async()
+ assert isinstance(user_info, dict) and 'miliaoNick' in user_info
+ nickname = user_info['miliaoNick']
+ _LOGGER.info('your nickname: %s', nickname)
+
+ await miot_http.deinit_async()
+
+
+@pytest.mark.asyncio
+@pytest.mark.dependency()
+async def test_miot_cloud_get_uid_async(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_uid: str
+):
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTHttpClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
+ access_token=oauth_info['access_token'])
+
+ uid = await miot_http.get_uid_async()
+ assert isinstance(uid, str)
+ _LOGGER.info('your uid: %s', uid)
+ # Save uid
+ rc = await miot_storage.save_async(
+ domain=test_domain_cloud_cache, name=test_name_uid, data=uid)
+ assert rc
+
+ await miot_http.deinit_async()
+
+
+@pytest.mark.asyncio
+@pytest.mark.dependency()
+async def test_miot_cloud_get_homeinfos_async(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_uid: str
+):
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTHttpClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
+ access_token=oauth_info['access_token'])
+
+ # Get homeinfos
+ homeinfos = await miot_http.get_homeinfos_async()
+ assert isinstance(homeinfos, dict)
+ assert 'uid' in homeinfos and isinstance(homeinfos['uid'], str)
+ assert 'home_list' in homeinfos and isinstance(
+ homeinfos['home_list'], dict)
+ assert 'share_home_list' in homeinfos and isinstance(
+ homeinfos['share_home_list'], dict)
+ # Get uid
+ uid = homeinfos.get('uid', '')
+ # Compare uid with uid in storage
+ uid2 = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
+ assert uid == uid2
+ _LOGGER.info('your uid: %s', uid)
+ # Get homes
+ home_list = homeinfos.get('home_list', {})
+ _LOGGER.info('your home_list: ,%s', home_list)
+ # Get share homes
+ share_home_list = homeinfos.get('share_home_list', {})
+ _LOGGER.info('your share_home_list: %s', share_home_list)
+
+ await miot_http.deinit_async()
+
+
+@pytest.mark.asyncio
+@pytest.mark.dependency()
+async def test_miot_cloud_get_devices_async(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_uid: str,
+ test_name_homes: str,
+ test_name_devices: str
+):
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTHttpClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
+ access_token=oauth_info['access_token'])
+
+ # Get devices
+ devices = await miot_http.get_devices_async()
+ assert isinstance(devices, dict)
+ assert 'uid' in devices and isinstance(devices['uid'], str)
+ assert 'homes' in devices and isinstance(devices['homes'], dict)
+ assert 'devices' in devices and isinstance(devices['devices'], dict)
+ # Compare uid with uid in storage
+ uid = devices.get('uid', '')
+ uid2 = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
+ assert uid == uid2
+ _LOGGER.info('your uid: %s', uid)
+ # Get homes
+ homes = devices['homes']
+ _LOGGER.info('your homes: %s', homes)
+ # Get devices
+ devices = devices['devices']
+ _LOGGER.info('your devices count: %s', len(devices))
+ # Storage homes and devices
+ rc = await miot_storage.save_async(
+ domain=test_domain_cloud_cache, name=test_name_homes, data=homes)
+ assert rc
+ rc = await miot_storage.save_async(
+ domain=test_domain_cloud_cache, name=test_name_devices, data=devices)
+ assert rc
+
+ await miot_http.deinit_async()
+
+
+@pytest.mark.asyncio
+@pytest.mark.dependency()
+async def test_miot_cloud_get_devices_with_dids_async(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_devices: str
+):
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTHttpClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
+ access_token=oauth_info['access_token'])
+
+ # Load devices
+ local_devices = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
+ assert isinstance(local_devices, dict)
+ did_list = list(local_devices.keys())
+ assert len(did_list) > 0
+ # Get device with dids
+ test_list = did_list[:6]
+ devices_info = await miot_http.get_devices_with_dids_async(
+ dids=test_list)
+ assert isinstance(devices_info, dict)
+ _LOGGER.info('test did list, %s, %s', len(test_list), test_list)
+ _LOGGER.info(
+ 'test result: %s, %s', len(devices_info), list(devices_info.keys()))
+
+ await miot_http.deinit_async()
+
+
+@pytest.mark.asyncio
+async def test_miot_cloud_get_cert(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_random_did: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_uid: str,
+ test_name_rd_did: str
+):
+ """
+ NOTICE: Currently, only certificate acquisition in the CN region is
+ supported.
+ """
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTHttpClient
+ from miot.miot_storage import MIoTCert, MIoTStorage
+
+ if test_cloud_server.lower() != 'cn':
+ _LOGGER.info('only support CN region')
+ return
+
+ miot_storage = MIoTStorage(test_cache_path)
+ uid = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
+ assert isinstance(uid, str)
+ _LOGGER.info('your uid: %s', uid)
+ random_did = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_rd_did, type_=str)
+ if not random_did:
+ random_did = test_random_did
+ rc = await miot_storage.save_async(
+ domain=test_domain_cloud_cache, name=test_name_rd_did,
+ data=random_did)
+ assert rc
+ assert isinstance(random_did, str)
+ _LOGGER.info('your random_did: %s', random_did)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict)
+ assert 'access_token' in oauth_info
+ access_token = oauth_info['access_token']
+
+ # Get certificates
+ miot_cert = MIoTCert(storage=miot_storage, uid=uid, cloud_server='CN')
+ assert await miot_cert.verify_ca_cert_async(), 'invalid ca cert'
+ remaining_time: int = await miot_cert.user_cert_remaining_time_async()
+ if remaining_time > 0:
+ _LOGGER.info(
+ 'user cert is valid, remaining time, %ss', remaining_time)
+ _LOGGER.info((
+ 'if you want to obtain it again, please delete the '
+ 'key, csr, and cert files in %s.'), test_cache_path)
+ return
+
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server,
+ client_id=OAUTH2_CLIENT_ID,
+ access_token=access_token)
+
+ user_key = miot_cert.gen_user_key()
+ assert isinstance(user_key, str)
+ _LOGGER.info('user_key str, %s', user_key)
+ user_csr = miot_cert.gen_user_csr(user_key=user_key, did=random_did)
+ assert isinstance(user_csr, str)
+ _LOGGER.info('user_csr str, %s', user_csr)
+ cert_str = await miot_http.get_central_cert_async(csr=user_csr)
+ assert isinstance(cert_str, str)
+ _LOGGER.info('user_cert str, %s', cert_str)
+ rc = await miot_cert.update_user_key_async(key=user_key)
+ assert rc
+ rc = await miot_cert.update_user_cert_async(cert=cert_str)
+ assert rc
+ # verify user certificates
+ remaining_time = await miot_cert.user_cert_remaining_time_async(
+ cert_data=cert_str.encode('utf-8'), did=random_did)
+ assert remaining_time > 0
+ _LOGGER.info('user cert remaining time, %ss', remaining_time)
+
+ await miot_http.deinit_async()
+
+
+@pytest.mark.asyncio
+@pytest.mark.dependency()
+async def test_miot_cloud_get_prop_async(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_devices: str
+):
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTHttpClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
+ access_token=oauth_info['access_token'])
+
+ # Load devices
+ local_devices = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
+ assert isinstance(local_devices, dict)
+ did_list = list(local_devices.keys())
+ assert len(did_list) > 0
+ # Get prop
+ test_list = did_list[:6]
+ for did in test_list:
+ prop_value = await miot_http.get_prop_async(did=did, siid=2, piid=1)
+ device_name = local_devices[did]['name']
+ _LOGGER.info('%s(%s), prop.2.1: %s', device_name, did, prop_value)
+
+ await miot_http.deinit_async()
+
+
+@pytest.mark.asyncio
+@pytest.mark.dependency()
+async def test_miot_cloud_get_props_async(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_devices: str
+):
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTHttpClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
+ access_token=oauth_info['access_token'])
+
+ # Load devices
+ local_devices = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
+ assert isinstance(local_devices, dict)
+ did_list = list(local_devices.keys())
+ assert len(did_list) > 0
+ # Get props
+ test_list = did_list[:6]
+ prop_values = await miot_http.get_props_async(params=[
+ {'did': did, 'siid': 2, 'piid': 1} for did in test_list])
+
+ _LOGGER.info('test did list, %s, %s', len(test_list), test_list)
+ _LOGGER.info('test result, %s, %s', len(prop_values), prop_values)
+
+ await miot_http.deinit_async()
+
+
+@pytest.mark.skip(reason='skip danger operation')
+@pytest.mark.asyncio
+@pytest.mark.dependency()
+async def test_miot_cloud_set_prop_async(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_devices: str
+):
+ """
+ WARNING: This test case will control the actual device and is not enabled
+ by default. You can uncomment @pytest.mark.skip to enable it.
+ """
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTHttpClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
+ access_token=oauth_info['access_token'])
+
+ # Load devices
+ local_devices = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
+ assert isinstance(local_devices, dict)
+ assert len(local_devices) > 0
+ # Set prop
+ # Find central hub gateway, control its indicator light switch
+ # You can replace it with the device you want to control.
+ test_did = ''
+ for did, dev in local_devices.items():
+ if dev['model'] == 'xiaomi.gateway.hub1':
+ test_did = did
+ break
+ assert test_did != '', 'no central hub gateway found'
+ result = await miot_http.set_prop_async(params=[{
+ 'did': test_did, 'siid': 3, 'piid': 1, 'value': False}])
+ _LOGGER.info('test did, %s, prop.3.1=False -> %s', test_did, result)
+ await asyncio.sleep(1)
+ result = await miot_http.set_prop_async(params=[{
+ 'did': test_did, 'siid': 3, 'piid': 1, 'value': True}])
+ _LOGGER.info('test did, %s, prop.3.1=True -> %s', test_did, result)
+
+ await miot_http.deinit_async()
+
+
+@pytest.mark.skip(reason='skip danger operation')
+@pytest.mark.asyncio
+@pytest.mark.dependency()
+async def test_miot_cloud_action_async(
+ test_cache_path: str,
+ test_cloud_server: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_devices: str
+):
+ """
+ WARNING: This test case will control the actual device and is not enabled
+ by default. You can uncomment @pytest.mark.skip to enable it.
+ """
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_cloud import MIoTHttpClient
+ from miot.miot_storage import MIoTStorage
+
+ miot_storage = MIoTStorage(test_cache_path)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
+ access_token=oauth_info['access_token'])
+
+ # Load devices
+ local_devices = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
+ assert isinstance(local_devices, dict)
+ assert len(local_devices) > 0
+ # Action
+ # Find central hub gateway, trigger its virtual events
+ # You can replace it with the device you want to control.
+ test_did = ''
+ for did, dev in local_devices.items():
+ if dev['model'] == 'xiaomi.gateway.hub1':
+ test_did = did
+ break
+ assert test_did != '', 'no central hub gateway found'
+ result = await miot_http.action_async(
+ did=test_did, siid=4, aiid=1,
+ in_list=[{'piid': 1, 'value': 'hello world.'}])
+ _LOGGER.info('test did, %s, action.4.1 -> %s', test_did, result)
+
+ await miot_http.deinit_async()
diff --git a/test/test_common.py b/test/test_common.py
index a6d68bc..18a4736 100644
--- a/test/test_common.py
+++ b/test/test_common.py
@@ -18,7 +18,7 @@ def test_miot_matcher():
if not matcher.get(topic=f'test/+/{l2}'):
matcher[f'test/+/{l2}'] = f'test/+/{l2}'
# Match
- match_result: list[(str, dict)] = list(matcher.iter_all_nodes())
+ match_result: list[str] = list(matcher.iter_all_nodes())
assert len(match_result) == 120
match_result: list[str] = list(matcher.iter_match(topic='test/1/1'))
assert len(match_result) == 3
diff --git a/test/test_ev.py b/test/test_ev.py
deleted file mode 100644
index 6353fe8..0000000
--- a/test/test_ev.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Unit test for miot_ev.py."""
-import os
-import pytest
-
-# pylint: disable=import-outside-toplevel, disable=unused-argument
-
-
-@pytest.mark.github
-def test_mev_timer_and_fd():
- from miot.miot_ev import MIoTEventLoop, TimeoutHandle
-
- mev = MIoTEventLoop()
- assert mev
- event_fd: os.eventfd = os.eventfd(0, os.O_NONBLOCK)
- assert event_fd
- timer4: TimeoutHandle = None
-
- def event_handler(event_fd):
- value: int = os.eventfd_read(event_fd)
- if value == 1:
- mev.clear_timeout(timer4)
- print('cancel timer4')
- elif value == 2:
- print('event write twice in a row')
- elif value == 3:
- mev.set_read_handler(event_fd, None, None)
- os.close(event_fd)
- event_fd = None
- print('close event fd')
-
- def timer1_handler(event_fd):
- os.eventfd_write(event_fd, 1)
-
- def timer2_handler(event_fd):
- os.eventfd_write(event_fd, 1)
- os.eventfd_write(event_fd, 1)
-
- def timer3_handler(event_fd):
- os.eventfd_write(event_fd, 3)
-
- def timer4_handler(event_fd):
- raise ValueError('unreachable code')
-
- mev.set_read_handler(
- event_fd, event_handler, event_fd)
-
- mev.set_timeout(500, timer1_handler, event_fd)
- mev.set_timeout(1000, timer2_handler, event_fd)
- mev.set_timeout(1500, timer3_handler, event_fd)
- timer4 = mev.set_timeout(2000, timer4_handler, event_fd)
-
- mev.loop_forever()
- # Loop will exit when there are no timers or fd handlers.
- mev.loop_stop()
diff --git a/test/test_lan.py b/test/test_lan.py
index a6051c0..a2861cc 100755
--- a/test/test_lan.py
+++ b/test/test_lan.py
@@ -1,11 +1,14 @@
# -*- coding: utf-8 -*-
"""Unit test for miot_lan.py."""
+import logging
from typing import Any
import pytest
import asyncio
from zeroconf import IPVersion
from zeroconf.asyncio import AsyncZeroconf
+_LOGGER = logging.getLogger(__name__)
+
# pylint: disable=import-outside-toplevel, unused-argument
@@ -67,7 +70,7 @@ async def test_lan_async(test_devices: dict):
miot_network = MIoTNetwork()
await miot_network.init_async()
- print('miot_network, ', miot_network.network_info)
+ _LOGGER.info('miot_network, %s', miot_network.network_info)
mips_service = MipsService(
aiozc=AsyncZeroconf(ip_version=IPVersion.V4Only))
await mips_service.init_async()
@@ -81,7 +84,7 @@ async def test_lan_async(test_devices: dict):
await miot_lan.vote_for_lan_ctrl_async(key='test', vote=True)
async def device_state_change(did: str, state: dict, ctx: Any):
- print('device state change, ', did, state)
+ _LOGGER.info('device state change, %s, %s', did, state)
if did != test_did:
return
if (
@@ -91,10 +94,10 @@ async def test_lan_async(test_devices: dict):
# Test sub prop
miot_lan.sub_prop(
did=did, siid=3, piid=1, handler=lambda msg, ctx:
- print(f'sub prop.3.1 msg, {did}={msg}'))
+ _LOGGER.info('sub prop.3.1 msg, %s=%s', did, msg))
miot_lan.sub_prop(
did=did, handler=lambda msg, ctx:
- print(f'sub all device msg, {did}={msg}'))
+ _LOGGER.info('sub all device msg, %s=%s', did, msg))
evt_push_available.set()
else:
# miot_lan.unsub_prop(did=did, siid=3, piid=1)
@@ -102,7 +105,7 @@ async def test_lan_async(test_devices: dict):
evt_push_unavailable.set()
async def lan_state_change(state: bool):
- print('lan state change, ', state)
+ _LOGGER.info('lan state change, %s', state)
if not state:
return
miot_lan.update_devices(devices={
diff --git a/test/test_mdns.py b/test/test_mdns.py
index ddf6a10..a0e148a 100755
--- a/test/test_mdns.py
+++ b/test/test_mdns.py
@@ -1,28 +1,35 @@
# -*- coding: utf-8 -*-
"""Unit test for miot_mdns.py."""
+import asyncio
+import logging
import pytest
from zeroconf import IPVersion
from zeroconf.asyncio import AsyncZeroconf
+_LOGGER = logging.getLogger(__name__)
+
# pylint: disable=import-outside-toplevel, unused-argument
@pytest.mark.asyncio
async def test_service_loop_async():
- from miot.miot_mdns import MipsService, MipsServiceData, MipsServiceState
+ from miot.miot_mdns import MipsService, MipsServiceState
async def on_service_state_change(
- group_id: str, state: MipsServiceState, data: MipsServiceData):
- print(
+ group_id: str, state: MipsServiceState, data: dict):
+ _LOGGER.info(
'on_service_state_change, %s, %s, %s', group_id, state, data)
async with AsyncZeroconf(ip_version=IPVersion.V4Only) as aiozc:
mips_service = MipsService(aiozc)
mips_service.sub_service_change('test', '*', on_service_state_change)
await mips_service.init_async()
+ # Wait for service to discover
+ await asyncio.sleep(3)
services_detail = mips_service.get_services()
- print('get all service, ', services_detail.keys())
+ _LOGGER.info('get all service, %s', list(services_detail.keys()))
for name, data in services_detail.items():
- print(
- '\tinfo, ', name, data['did'], data['addresses'], data['port'])
+ _LOGGER.info(
+ '\tinfo, %s, %s, %s, %s',
+ name, data['did'], data['addresses'], data['port'])
await mips_service.deinit_async()
diff --git a/test/test_mips.py b/test/test_mips.py
new file mode 100644
index 0000000..d808f22
--- /dev/null
+++ b/test/test_mips.py
@@ -0,0 +1,264 @@
+# -*- coding: utf-8 -*-
+"""Unit test for miot_mips.py.
+NOTICE: When running this test case, you need to run test_cloud.py first to
+obtain the token and certificate information, and at the same time avoid data
+deletion.
+"""
+import ipaddress
+from typing import Any, Tuple
+import pytest
+import asyncio
+import logging
+
+_LOGGER = logging.getLogger(__name__)
+
+
+# pylint: disable = import-outside-toplevel, unused-argument
+
+@pytest.mark.parametrize('central_info', [
+ ('', 'Gateway did', 'Gateway ip', 8883),
+])
+@pytest.mark.asyncio
+async def test_mips_local_async(
+ test_cache_path: str,
+ test_domain_cloud_cache: str,
+ test_name_uid: str,
+ test_name_rd_did: str,
+ central_info: Tuple[str, str, str, int]
+):
+ """
+ NOTICE:
+ - Mips local is used to connect to the central gateway and is only
+ supported in the Chinese mainland region.
+ - Before running this test case, you need to run test_mdns.py first to
+ obtain the group_id, did, ip, and port of the hub, and then fill in this
+ information in the parametrize. you can enter multiple central connection
+ information items for separate tests.
+ - This test case requires running test_cloud.py first to obtain the
+ central connection certificate.
+ - This test case will control the indicator light switch of the central
+ gateway.
+ """
+ from miot.miot_storage import MIoTStorage, MIoTCert
+ from miot.miot_mips import MipsLocalClient
+
+ central_group_id: str = central_info[0]
+ assert isinstance(central_group_id, str)
+ central_did: str = central_info[1]
+ assert central_did.isdigit()
+ central_ip: str = central_info[2]
+ assert ipaddress.ip_address(central_ip)
+ central_port: int = central_info[3]
+ assert isinstance(central_port, int)
+
+ miot_storage = MIoTStorage(test_cache_path)
+ uid = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
+ assert isinstance(uid, str)
+ random_did = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_rd_did, type_=str)
+ assert isinstance(random_did, str)
+ miot_cert = MIoTCert(storage=miot_storage, uid=uid, cloud_server='CN')
+ assert miot_cert.ca_file
+ assert miot_cert.cert_file
+ assert miot_cert.key_file
+ _LOGGER.info(
+ 'cert info, %s, %s, %s', miot_cert.ca_file, miot_cert.cert_file,
+ miot_cert.key_file)
+
+ mips_local = MipsLocalClient(
+ did=random_did,
+ host=central_ip,
+ group_id=central_group_id,
+ ca_file=miot_cert.ca_file,
+ cert_file=miot_cert.cert_file,
+ key_file=miot_cert.key_file,
+ port=central_port,
+ home_name='mips local test')
+ mips_local.enable_logger(logger=_LOGGER)
+ mips_local.enable_mqtt_logger(logger=_LOGGER)
+
+ async def on_mips_state_changed_async(key: str, state: bool):
+ _LOGGER.info('on mips state changed, %s, %s', key, state)
+
+ async def on_dev_list_changed_async(
+ mips: MipsLocalClient, did_list: list[str]
+ ):
+ _LOGGER.info('dev list changed, %s', did_list)
+
+ def on_prop_changed(payload: dict, ctx: Any):
+ _LOGGER.info('prop changed, %s=%s', ctx, payload)
+
+ def on_event_occurred(payload: dict, ctx: Any):
+ _LOGGER.info('event occurred, %s=%s', ctx, payload)
+
+ # Reg mips state
+ mips_local.sub_mips_state(
+ key='mips_local', handler=on_mips_state_changed_async)
+ mips_local.on_dev_list_changed = on_dev_list_changed_async
+ # Connect
+ await mips_local.connect_async()
+ await asyncio.sleep(0.5)
+ # Get device list
+ device_list = await mips_local.get_dev_list_async()
+ assert isinstance(device_list, dict)
+ _LOGGER.info(
+ 'get_dev_list, %d, %s', len(device_list), list(device_list.keys()))
+ # Sub Prop
+ mips_local.sub_prop(
+ did=central_did, handler=on_prop_changed,
+ handler_ctx=f'{central_did}.*')
+ # Sub Event
+ mips_local.sub_event(
+ did=central_did, handler=on_event_occurred,
+ handler_ctx=f'{central_did}.*')
+ # Get/set prop
+ test_siid = 3
+ test_piid = 1
+ # mips_local.sub_prop(
+ # did=central_did, siid=test_siid, piid=test_piid,
+ # handler=on_prop_changed,
+ # handler_ctx=f'{central_did}.{test_siid}.{test_piid}')
+ result1 = await mips_local.get_prop_async(
+ did=central_did, siid=test_siid, piid=test_piid)
+ assert isinstance(result1, bool)
+ _LOGGER.info('get prop.%s.%s, value=%s', test_siid, test_piid, result1)
+ result2 = await mips_local.set_prop_async(
+ did=central_did, siid=test_siid, piid=test_piid, value=not result1)
+ _LOGGER.info(
+ 'set prop.%s.%s=%s, result=%s',
+ test_siid, test_piid, not result1, result2)
+ assert isinstance(result2, dict)
+ result3 = await mips_local.get_prop_async(
+ did=central_did, siid=test_siid, piid=test_piid)
+ assert isinstance(result3, bool)
+ _LOGGER.info('get prop.%s.%s, value=%s', test_siid, test_piid, result3)
+ # Action
+ test_siid = 4
+ test_aiid = 1
+ in_list = [{'piid': 1, 'value': 'hello world.'}]
+ result4 = await mips_local.action_async(
+ did=central_did, siid=test_siid, aiid=test_aiid,
+ in_list=in_list)
+ assert isinstance(result4, dict)
+ _LOGGER.info(
+ 'action.%s.%s=%s, result=%s', test_siid, test_piid, in_list, result4)
+ # Disconnect
+ await mips_local.disconnect_async()
+ await mips_local.deinit_async()
+
+
+@pytest.mark.asyncio
+async def test_mips_cloud_async(
+ test_cache_path: str,
+ test_name_uuid: str,
+ test_cloud_server: str,
+ test_domain_cloud_cache: str,
+ test_name_oauth2_info: str,
+ test_name_devices: str
+):
+ """
+ NOTICE:
+ - This test case requires running test_cloud.py first to obtain the
+ central connection certificate.
+ - This test case will control the indicator light switch of the central
+ gateway.
+ """
+ from miot.const import OAUTH2_CLIENT_ID
+ from miot.miot_storage import MIoTStorage
+ from miot.miot_mips import MipsCloudClient
+ from miot.miot_cloud import MIoTHttpClient
+
+ miot_storage = MIoTStorage(test_cache_path)
+ uuid = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_uuid, type_=str)
+ assert isinstance(uuid, str)
+ oauth_info = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
+ assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
+ access_token = oauth_info['access_token']
+ _LOGGER.info('connect info, %s, %s', uuid, access_token)
+ mips_cloud = MipsCloudClient(
+ uuid=uuid,
+ cloud_server=test_cloud_server,
+ app_id=OAUTH2_CLIENT_ID,
+ token=access_token)
+ mips_cloud.enable_logger(logger=_LOGGER)
+ mips_cloud.enable_mqtt_logger(logger=_LOGGER)
+ miot_http = MIoTHttpClient(
+ cloud_server=test_cloud_server,
+ client_id=OAUTH2_CLIENT_ID,
+ access_token=access_token)
+
+ async def on_mips_state_changed_async(key: str, state: bool):
+ _LOGGER.info('on mips state changed, %s, %s', key, state)
+
+ def on_prop_changed(payload: dict, ctx: Any):
+ _LOGGER.info('prop changed, %s=%s', ctx, payload)
+
+ def on_event_occurred(payload: dict, ctx: Any):
+ _LOGGER.info('event occurred, %s=%s', ctx, payload)
+
+ await mips_cloud.connect_async()
+ await asyncio.sleep(0.5)
+
+ # Sub mips state
+ mips_cloud.sub_mips_state(
+ key='mips_cloud', handler=on_mips_state_changed_async)
+ # Load devices
+ local_devices = await miot_storage.load_async(
+ domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
+ assert isinstance(local_devices, dict)
+ central_did = ''
+ for did, info in local_devices.items():
+ if info['model'] != 'xiaomi.gateway.hub1':
+ continue
+ central_did = did
+ break
+ if central_did:
+ # Sub Prop
+ mips_cloud.sub_prop(
+ did=central_did, handler=on_prop_changed,
+ handler_ctx=f'{central_did}.*')
+ # Sub Event
+ mips_cloud.sub_event(
+ did=central_did, handler=on_event_occurred,
+ handler_ctx=f'{central_did}.*')
+ # Get/set prop
+ test_siid = 3
+ test_piid = 1
+ # mips_cloud.sub_prop(
+ # did=central_did, siid=test_siid, piid=test_piid,
+ # handler=on_prop_changed,
+ # handler_ctx=f'{central_did}.{test_siid}.{test_piid}')
+ result1 = await miot_http.get_prop_async(
+ did=central_did, siid=test_siid, piid=test_piid)
+ assert isinstance(result1, bool)
+ _LOGGER.info('get prop.%s.%s, value=%s', test_siid, test_piid, result1)
+ result2 = await miot_http.set_prop_async(params=[{
+ 'did': central_did, 'siid': test_siid, 'piid': test_piid,
+ 'value': not result1}])
+ _LOGGER.info(
+ 'set prop.%s.%s=%s, result=%s',
+ test_siid, test_piid, not result1, result2)
+ assert isinstance(result2, list)
+ result3 = await miot_http.get_prop_async(
+ did=central_did, siid=test_siid, piid=test_piid)
+ assert isinstance(result3, bool)
+ _LOGGER.info('get prop.%s.%s, value=%s', test_siid, test_piid, result3)
+ # Action
+ test_siid = 4
+ test_aiid = 1
+ in_list = [{'piid': 1, 'value': 'hello world.'}]
+ result4 = await miot_http.action_async(
+ did=central_did, siid=test_siid, aiid=test_aiid,
+ in_list=in_list)
+ assert isinstance(result4, dict)
+ _LOGGER.info(
+ 'action.%s.%s=%s, result=%s',
+ test_siid, test_piid, in_list, result4)
+ await asyncio.sleep(1)
+ # Disconnect
+ await mips_cloud.disconnect_async()
+ await mips_cloud.deinit_async()
+ await miot_http.deinit_async()
diff --git a/test/test_network.py b/test/test_network.py
index aa81a4e..f59ddb2 100755
--- a/test/test_network.py
+++ b/test/test_network.py
@@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
"""Unit test for miot_network.py."""
+import logging
import pytest
import asyncio
+_LOGGER = logging.getLogger(__name__)
+
# pylint: disable=import-outside-toplevel, unused-argument
@@ -12,16 +15,16 @@ async def test_network_monitor_loop_async():
miot_net = MIoTNetwork()
async def on_network_status_changed(status: bool):
- print(f'on_network_status_changed, {status}')
+ _LOGGER.info('on_network_status_changed, %s', status)
miot_net.sub_network_status(key='test', handler=on_network_status_changed)
async def on_network_info_changed(
status: InterfaceStatus, info: NetworkInfo):
- print(f'on_network_info_changed, {status}, {info}')
+ _LOGGER.info('on_network_info_changed, %s, %s', status, info)
miot_net.sub_network_info(key='test', handler=on_network_info_changed)
- await miot_net.init_async(3)
+ await miot_net.init_async()
await asyncio.sleep(3)
- print(f'net status: {miot_net.network_status}')
- print(f'net info: {miot_net.network_info}')
+ _LOGGER.info('net status: %s', miot_net.network_status)
+ _LOGGER.info('net info: %s', miot_net.network_info)
await miot_net.deinit_async()
diff --git a/test/test_spec.py b/test/test_spec.py
index 57ccbb6..248e9d8 100755
--- a/test/test_spec.py
+++ b/test/test_spec.py
@@ -1,11 +1,14 @@
# -*- coding: utf-8 -*-
"""Unit test for miot_spec.py."""
import json
+import logging
import random
import time
from urllib.request import Request, urlopen
import pytest
+_LOGGER = logging.getLogger(__name__)
+
# pylint: disable=import-outside-toplevel, unused-argument
@@ -79,10 +82,10 @@ async def test_spec_random_parse_async(test_cache_path, test_lang):
storage = MIoTStorage(test_cache_path)
spec_parser = MIoTSpecParser(lang=test_lang, storage=storage)
await spec_parser.init_async()
- start_ts: int = time.time()*1000
+ start_ts = time.time()*1000
for index in test_urn_index:
urn: str = test_urns[int(index)]
result = await spec_parser.parse(urn=urn, skip_cache=True)
assert result is not None
- end_ts: int = time.time()*1000
- print(f'takes time, {test_count}, {end_ts-start_ts}')
+ end_ts = time.time()*1000
+ _LOGGER.info('takes time, %s, %s', test_count, end_ts-start_ts)
diff --git a/test/test_storage.py b/test/test_storage.py
index 76ec510..ace0c53 100755
--- a/test/test_storage.py
+++ b/test/test_storage.py
@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
"""Unit test for miot_storage.py."""
import asyncio
+import logging
from os import path
import pytest
+_LOGGER = logging.getLogger(__name__)
+
# pylint: disable=import-outside-toplevel, unused-argument
@@ -101,7 +104,7 @@ async def test_multi_task_load_async(test_cache_path):
for _ in range(task_count):
task_list.append(asyncio.create_task(storage.load_async(
domain=test_domain, name=name, type_=dict)))
- print(f'\ntask count, {len(task_list)}')
+ _LOGGER.info('task count, %s', len(task_list))
result: list = await asyncio.gather(*task_list)
assert None not in result
@@ -178,28 +181,28 @@ async def test_user_config_async(
config=config_update, replace=True)
assert (config_replace := await storage.load_user_config_async(
uid=test_uid, cloud_server=test_cloud_server)) == config_update
- print('replace result, ', config_replace)
+ _LOGGER.info('replace result, %s', config_replace)
# Test query
query_keys = list(config_base.keys())
- print('query keys, ', query_keys)
+ _LOGGER.info('query keys, %s', query_keys)
query_result = await storage.load_user_config_async(
uid=test_uid, cloud_server=test_cloud_server, keys=query_keys)
- print('query result 1, ', query_result)
+ _LOGGER.info('query result 1, %s', query_result)
assert await storage.update_user_config_async(
uid=test_uid, cloud_server=test_cloud_server,
config=config_base, replace=True)
query_result = await storage.load_user_config_async(
uid=test_uid, cloud_server=test_cloud_server, keys=query_keys)
- print('query result 2, ', query_result)
+ _LOGGER.info('query result 2, %s', query_result)
query_result = await storage.load_user_config_async(
uid=test_uid, cloud_server=test_cloud_server)
- print('query result all, ', query_result)
+ _LOGGER.info('query result all, %s', query_result)
# Remove config
assert await storage.update_user_config_async(
uid=test_uid, cloud_server=test_cloud_server, config=None)
query_result = await storage.load_user_config_async(
uid=test_uid, cloud_server=test_cloud_server)
- print('remove result, ', query_result)
+ _LOGGER.info('remove result, %s', query_result)
# Remove domain
assert await storage.remove_domain_async(domain='miot_config')