Merge branch 'main' into advanced-options

# Conflicts:
#	custom_components/xiaomi_home/config_flow.py
This commit is contained in:
jimmykmi 2024-12-19 02:45:59 +08:00
commit b37a249c11
17 changed files with 437 additions and 44 deletions

25
CHANGELOG.md Normal file
View File

@ -0,0 +1,25 @@
# CHANGELOG
## v0.1.2
### Added
- Support Xiaomi Heater devices. https://github.com/XiaoMi/ha_xiaomi_home/issues/124 https://github.com/XiaoMi/ha_xiaomi_home/issues/117
- Language supports pt, pt-BR.
### Changed
- Adjust the minimum version of HASS core to 2024.4.4 and above versions.
### Fixed
## v0.1.1
### Added
### Changed
### Fixed
- Fix humidifier trans rule. https://github.com/XiaoMi/ha_xiaomi_home/issues/59
- Fix get homeinfo error. https://github.com/XiaoMi/ha_xiaomi_home/issues/22
- Fix air-conditioner switch on. https://github.com/XiaoMi/ha_xiaomi_home/issues/37 https://github.com/XiaoMi/ha_xiaomi_home/issues/16
- Fix invalid cover status. https://github.com/XiaoMi/ha_xiaomi_home/issues/11 https://github.com/XiaoMi/ha_xiaomi_home/issues/85
- Water heater entity add STATE_OFF. https://github.com/XiaoMi/ha_xiaomi_home/issues/105 https://github.com/XiaoMi/ha_xiaomi_home/issues/17
## v0.1.0
### Added
- First version
### Changed
### Fixed

View File

@ -1,6 +1,6 @@
# Contribution Guidelines
[English](./CONTRIBUTING.md) | [简体中文](./CONTRIBUTING_zh.md)
[English](./CONTRIBUTING.md) | [简体中文](./doc/CONTRIBUTING_zh.md)
Thank you for considering contributing to our project! We appreciate your efforts to make our project better.

View File

@ -323,7 +323,7 @@ Device information service (urn:miot-spec-v2:service:device-information:00007801
## Multiple Language Support
There are 8 languages available for selection in the config flow language option of Xiaomi Home, including Simplified Chinese, Traditional Chinese, English, Spanish, Russian, French, German, and Japanese. The config flow page in Simplified Chinese and English has been manually reviewed by the developer. Other languages are translated by machine translation. If you want to modify the words and sentences in the config flow page, you need to modify the json file of the certain language in `custom_components/xiaomi_home/translations/` directory.
There are 8 languages available for selection in the config flow language option of Xiaomi Home, including Simplified Chinese, Traditional Chinese, English, Spanish, Russian, French, German, and Japanese. The config flow page in Simplified Chinese and English has been manually reviewed by the developer. Other languages are translated by machine translation. If you want to modify the words and sentences in the config flow page, you need to modify the json file of the certain language in `custom_components/xiaomi_home/translations/` and `custom_components/xiaomi_home/miot/i18n/` directory.
When displaying Home Assistant entity name, Xiaomi Home downloads the multiple language file configured by the device vendor from MIoT Cloud, which contains translations for MIoT-Spec-V2 instances of the device. `multi_lang.json` is a locally maintained multiple language dictionary, which has a higher priority than the multiple language file obtained from the cloud and can be used to supplement or modify the multiple language translation of devices.
@ -376,8 +376,8 @@ Example:
## Documents
- [License](./LICENSE.md)
- Contribution Guidelines: [English](./doc/CONTRIBUTING.md) | [简体中文](./doc/CONTRIBUTING_zh.md)
- [ChangeLog](./doc/CHANGELOG.md)
- Contribution Guidelines: [English](./CONTRIBUTING.md) | [简体中文](./doc/CONTRIBUTING_zh.md)
- [ChangeLog](./CHANGELOG.md)
- Development Documents: https://developers.home-assistant.io/docs/creating_component_index
## Directory Structure

View File

@ -82,9 +82,12 @@ async def async_setup_entry(
new_entities = []
for miot_device in device_list:
for data in miot_device.entity_list.get('climate', []):
for data in miot_device.entity_list.get('air-conditioner', []):
new_entities.append(
AirConditioner(miot_device=miot_device, entity_data=data))
for data in miot_device.entity_list.get('heater', []):
new_entities.append(
Heater(miot_device=miot_device, entity_data=data))
if new_entities:
async_add_entities(new_entities)
@ -115,7 +118,7 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
def __init__(
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
) -> None:
"""Initialize the Climate."""
"""Initialize the Air conditioner."""
super().__init__(miot_device=miot_device, entity_data=entity_data)
self._attr_icon = 'mdi:air-conditioner'
self._attr_supported_features = ClimateEntityFeature(0)
@ -344,31 +347,31 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
f'set climate prop.fan_mode failed, {fan_mode}, '
f'{self.entity_id}')
@ property
@property
def target_temperature(self) -> Optional[float]:
"""Return the target temperature."""
return self.get_prop_value(
prop=self._prop_target_temp) if self._prop_target_temp else None
@ property
@property
def target_humidity(self) -> Optional[int]:
"""Return the target humidity."""
return self.get_prop_value(
prop=self._prop_target_humi) if self._prop_target_humi else None
@ property
@property
def current_temperature(self) -> Optional[float]:
"""Return the current temperature."""
return self.get_prop_value(
prop=self._prop_env_temp) if self._prop_env_temp else None
@ property
@property
def current_humidity(self) -> Optional[int]:
"""Return the current humidity."""
return self.get_prop_value(
prop=self._prop_env_humi) if self._prop_env_humi else None
@ property
@property
def hvac_mode(self) -> Optional[HVACMode]:
"""Return the hvac mode. e.g., heat, cool mode."""
if self.get_prop_value(prop=self._prop_on) is False:
@ -377,7 +380,7 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
map_=self._hvac_mode_map,
key=self.get_prop_value(prop=self._prop_mode))
@ property
@property
def fan_mode(self) -> Optional[str]:
"""Return the fan mode.
@ -387,7 +390,7 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
map_=self._fan_mode_map,
key=self.get_prop_value(prop=self._prop_fan_level))
@ property
@property
def swing_mode(self) -> Optional[str]:
"""Return the swing mode.
@ -473,3 +476,144 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
self._value_ac_state.update(v_ac_state)
_LOGGER.debug(
'ac_state update, %s', self._value_ac_state)
class Heater(MIoTServiceEntity, ClimateEntity):
"""Heater entities for Xiaomi Home."""
# service: heater
_prop_on: Optional[MIoTSpecProperty]
_prop_mode: Optional[MIoTSpecProperty]
_prop_target_temp: Optional[MIoTSpecProperty]
_prop_heat_level: Optional[MIoTSpecProperty]
# service: environment
_prop_env_temp: Optional[MIoTSpecProperty]
_prop_env_humi: Optional[MIoTSpecProperty]
_heat_level_map: Optional[dict[int, str]]
def __init__(
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
) -> None:
"""Initialize the Heater."""
super().__init__(miot_device=miot_device, entity_data=entity_data)
self._attr_icon = 'mdi:air-conditioner'
self._attr_supported_features = ClimateEntityFeature(0)
self._attr_preset_modes = []
self._prop_on = None
self._prop_mode = None
self._prop_target_temp = None
self._prop_heat_level = None
self._prop_env_temp = None
self._prop_env_humi = None
self._heat_level_map = None
# properties
for prop in entity_data.props:
if prop.name == 'on':
self._attr_supported_features |= (
ClimateEntityFeature.TURN_ON)
self._attr_supported_features |= (
ClimateEntityFeature.TURN_OFF)
self._prop_on = prop
elif prop.name == 'target-temperature':
if not isinstance(prop.value_range, dict):
_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_target_temperature_step = prop.value_range['step']
self._attr_temperature_unit = prop.external_unit
self._attr_supported_features |= (
ClimateEntityFeature.TARGET_TEMPERATURE)
self._prop_target_temp = prop
elif prop.name == 'heat-level':
if (
not isinstance(prop.value_list, list)
or not prop.value_list
):
_LOGGER.error(
'invalid heat-level value_list, %s', self.entity_id)
continue
self._heat_level_map = {
item['value']: item['description']
for item in prop.value_list}
self._attr_preset_modes = list(self._heat_level_map.values())
self._attr_supported_features |= (
ClimateEntityFeature.PRESET_MODE)
self._prop_heat_level = prop
elif prop.name == 'temperature':
self._prop_env_temp = prop
elif prop.name == 'relative-humidity':
self._prop_env_humi = prop
# hvac modes
self._attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
async def async_turn_on(self) -> None:
"""Turn the entity on."""
await self.set_property_async(prop=self._prop_on, value=True)
async def async_turn_off(self) -> None:
"""Turn the entity off."""
await self.set_property_async(prop=self._prop_on, value=False)
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""
await self.set_property_async(
prop=self._prop_on, value=False
if hvac_mode == HVACMode.OFF else True)
async def async_set_temperature(self, **kwargs):
"""Set new target temperature."""
if ATTR_TEMPERATURE in kwargs:
temp = kwargs[ATTR_TEMPERATURE]
if temp > self.max_temp:
temp = self.max_temp
elif temp < self.min_temp:
temp = self.min_temp
await self.set_property_async(
prop=self._prop_target_temp, value=temp)
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode."""
await self.set_property_async(
self._prop_heat_level,
value=self.get_map_value(
map_=self._heat_level_map, description=preset_mode))
@property
def target_temperature(self) -> Optional[float]:
"""Return the target temperature."""
return self.get_prop_value(
prop=self._prop_target_temp) if self._prop_target_temp else None
@property
def current_temperature(self) -> Optional[float]:
"""Return the current temperature."""
return self.get_prop_value(
prop=self._prop_env_temp) if self._prop_env_temp else None
@property
def current_humidity(self) -> Optional[int]:
"""Return the current humidity."""
return self.get_prop_value(
prop=self._prop_env_humi) if self._prop_env_humi else None
@property
def hvac_mode(self) -> Optional[HVACMode]:
"""Return the hvac mode."""
return (
HVACMode.HEAT if self.get_prop_value(prop=self._prop_on)
else HVACMode.OFF)
@property
def preset_mode(self) -> Optional[str]:
return (
self.get_map_description(
map_=self._heat_level_map,
key=self.get_prop_value(prop=self._prop_heat_level))
if self._prop_heat_level else None)

View File

@ -602,8 +602,8 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
last_step=True,
)
@ staticmethod
@ callback
@staticmethod
@callback
def async_get_options_flow(
config_entry: config_entries.ConfigEntry,
) -> config_entries.OptionsFlow:

View File

@ -236,7 +236,7 @@ class Light(MIoTServiceEntity, LightEntity):
"""Return the color temperature."""
return self.get_prop_value(prop=self._prop_color_temp)
@ property
@property
def rgb_color(self) -> Optional[tuple[int, int, int]]:
"""Return the rgb color value."""
rgb = self.get_prop_value(prop=self._prop_color)
@ -247,7 +247,7 @@ class Light(MIoTServiceEntity, LightEntity):
b = rgb & 0xFF
return r, g, b
@ property
@property
def effect(self) -> Optional[str]:
"""Return the current mode."""
return self.__get_mode_description(

View File

@ -25,7 +25,7 @@
"cryptography",
"psutil"
],
"version": "v0.1.0",
"version": "v0.1.2",
"zeroconf": [
"_miot-central._tcp.local."
]

View File

@ -110,11 +110,13 @@ INTEGRATION_LANGUAGES = {
'zh-Hans': '简体中文',
'zh-Hant': '繁體中文',
'en': 'English',
'es': 'Español',
'ru': 'Русский',
'fr': 'Français',
'de': 'Deutsch',
'ja': '日本語'
'es': 'Español',
'fr': 'Français',
'ja': '日本語',
'pt': 'Português',
'pt-BR': 'Português (Brasil)',
'ru': 'Русский',
}
DEFAULT_CTRL_MODE: str = 'auto'

View File

@ -0,0 +1,95 @@
{
"config": {
"other": {
"devices": "dispositivos",
"found_central_gateway": "encontrado o gateway central local"
},
"control_mode": {
"auto": "automático",
"cloud": "nuvem"
},
"room_name_rule": {
"none": "não sincronizado",
"home_room": "Nome da casa e nome do quarto (Xiaomi Home Quarto)",
"room": "Nome do quarto (Quarto)",
"home": "Nome da casa (Xiaomi Home)"
},
"option_status": {
"enable": "habilitado",
"disable": "desabilitado"
},
"lan_ctrl_config": {
"notice_net_dup": "\r\n**[Aviso]** Detectado múltiplas interfaces de rede que podem estar conectando à mesma rede, por favor, selecione a correta.",
"net_unavailable": "Interface indisponível"
}
},
"miot": {
"client": {
"invalid_oauth_info": "Informações de autenticação inválidas, a conexão com a nuvem estará indisponível. Vá para a página de integração do Xiaomi Home e clique em 'Opções' para reautenticar.",
"invalid_device_cache": "Informações de dispositivo no cache inválidas. Vá para a página de integração do Xiaomi Home e clique em 'Opções -> Atualizar lista de dispositivos' para atualizar as informações locais.",
"invalid_cert_info": "Certificado de usuário inválido. A conexão local do gateway central estará indisponível. Vá para a página de integração do Xiaomi Home e clique em 'Opções' para reautenticar.",
"device_cloud_error": "Erro ao obter informações do dispositivo da nuvem. Verifique a conexão da rede local.",
"xiaomi_home_error_title": "Erro de Integração do Xiaomi Home",
"xiaomi_home_error": "Erro detectado em **{nick_name}({uid}, {cloud_server})**. Vá para a página de opções para reconfigurar.\n\n**Erro**: \n{message}",
"device_list_changed_title": "Mudança na lista de dispositivos do Xiaomi Home",
"device_list_changed": "Detectado que as informações do dispositivo **{nick_name}({uid}, {cloud_server})** mudaram. Vá para a página de integração e clique em 'Opções -> Atualizar lista de dispositivos' para atualizar as informações locais.\n\nStatus atual da rede: {network_status}\n{message}\n",
"device_list_add": "\n**{count} dispositivos novos**: \n{message}",
"device_list_del": "\n**{count} dispositivos não disponíveis**: \n{message}",
"device_list_offline": "\n**{count} dispositivos offline**: \n{message}",
"network_status_online": "online",
"network_status_offline": "offline",
"device_exec_error": "Erro na execução"
}
},
"error": {
"common": {
"-10000": "Erro desconhecido",
"-10001": "Serviço indisponível",
"-10002": "Parâmetro inválido",
"-10003": "Recursos insuficientes",
"-10004": "Erro interno",
"-10005": "Permissões insuficientes",
"-10006": "Execução expirada",
"-10007": "Dispositivo offline ou inexistente",
"-10020": "OAuth2 não autorizado",
"-10030": "Token inválido (HTTP)",
"-10040": "Formato de mensagem inválido",
"-10050": "Certificado inválido",
"-704000000": "Erro desconhecido",
"-704010000": "Não autorizado (o dispositivo pode ter sido excluído)",
"-704014006": "Descrição do dispositivo não encontrada",
"-704030013": "Propriedade não pode ser lida",
"-704030023": "Propriedade não pode ser escrita",
"-704030033": "Propriedade não pode ser assinada",
"-704040002": "Serviço inexistente",
"-704040003": "Propriedade inexistente",
"-704040004": "Evento inexistente",
"-704040005": "Ação inexistente",
"-704040999": "Funcionalidade não lançada",
"-704042001": "Dispositivo inexistente",
"-704042011": "Dispositivo offline",
"-704053036": "Tempo de operação do dispositivo expirado",
"-704053100": "Dispositivo não pode executar esta operação no estado atual",
"-704083036": "Tempo de operação do dispositivo expirado",
"-704090001": "Dispositivo inexistente",
"-704220008": "ID inválido",
"-704220025": "Número de parâmetros de ação incompatível",
"-704220035": "Parâmetro de ação incorreto",
"-704220043": "Valor da propriedade incorreto",
"-704222034": "Valor de retorno de ação incorreto",
"-705004000": "Erro desconhecido",
"-705004501": "Erro desconhecido",
"-705201013": "Propriedade não pode ser lida",
"-705201015": "Erro na execução da ação",
"-705201023": "Propriedade não pode ser escrita",
"-705201033": "Propriedade não pode ser assinada",
"-706012000": "Erro desconhecido",
"-706012013": "Propriedade não pode ser lida",
"-706012015": "Erro na execução da ação",
"-706012023": "Propriedade não pode ser escrita",
"-706012033": "Propriedade não pode ser assinada",
"-706012043": "Valor da propriedade incorreto",
"-706014006": "Descrição do dispositivo não encontrada"
}
}
}

View File

@ -0,0 +1,95 @@
{
"config": {
"other": {
"devices": "dispositivos",
"found_central_gateway": ", encontrou a central de gateway local"
},
"control_mode": {
"auto": "Automático",
"cloud": "Nuvem"
},
"room_name_rule": {
"none": "Não sincronizar",
"home_room": "Nome da casa e Nome do quarto (Xiaomi Home Quarto)",
"room": "Nome do quarto (Quarto)",
"home": "Nome da casa (Xiaomi Home)"
},
"option_status": {
"enable": "Habilitar",
"disable": "Desabilitar"
},
"lan_ctrl_config": {
"notice_net_dup": "\r\n**[Aviso]** Detectado que várias interfaces podem estar conectadas à mesma rede, escolha com cuidado.",
"net_unavailable": "Interface indisponível"
}
},
"miot": {
"client": {
"invalid_oauth_info": "Informações de autenticação inválidas, a conexão na nuvem ficará indisponível. Por favor, acesse a página de integração do Xiaomi Home e clique em 'Opções' para autenticar novamente.",
"invalid_device_cache": "Erro no cache de informações do dispositivo. Por favor, acesse a página de integração do Xiaomi Home e clique em 'Opções -> Atualizar lista de dispositivos' para atualizar as informações locais.",
"invalid_cert_info": "Certificado de usuário inválido, a conexão com a central local ficará indisponível. Por favor, acesse a página de integração do Xiaomi Home e clique em 'Opções' para autenticar novamente.",
"device_cloud_error": "Erro ao obter informações do dispositivo na nuvem. Verifique a conexão de rede local.",
"xiaomi_home_error_title": "Erro de integração do Xiaomi Home",
"xiaomi_home_error": "Detectado erro em **{nick_name}({uid}, {cloud_server})**. Por favor, acesse a página de opções para reconfigurar.\n\n**Informação do erro**: \n{message}",
"device_list_changed_title": "Mudança na lista de dispositivos do Xiaomi Home",
"device_list_changed": "Detectada alteração nas informações do dispositivo de **{nick_name}({uid}, {cloud_server})**. Por favor, acesse a página de opções de integração e clique em 'Opções -> Atualizar lista de dispositivos' para atualizar as informações locais.\n\nStatus atual da rede: {network_status}\n{message}\n",
"device_list_add": "\n**{count} novos dispositivos**: \n{message}",
"device_list_del": "\n**{count} dispositivos indisponíveis**: \n{message}",
"device_list_offline": "\n**{count} dispositivos offline**: \n{message}",
"network_status_online": "Online",
"network_status_offline": "Offline",
"device_exec_error": "Erro de execução"
}
},
"error": {
"common": {
"-10000": "Erro desconhecido",
"-10001": "Serviço indisponível",
"-10002": "Parâmetro inválido",
"-10003": "Recursos insuficientes",
"-10004": "Erro interno",
"-10005": "Permissão negada",
"-10006": "Tempo limite de execução",
"-10007": "Dispositivo offline ou inexistente",
"-10020": "Não autorizado (OAuth2)",
"-10030": "Token inválido (HTTP)",
"-10040": "Formato de mensagem inválido",
"-10050": "Certificado inválido",
"-704000000": "Erro desconhecido",
"-704010000": "Não autorizado (o dispositivo pode ter sido removido)",
"-704014006": "Descrição do dispositivo não encontrada",
"-704030013": "Propriedade não legível",
"-704030023": "Propriedade não gravável",
"-704030033": "Propriedade não subscritível",
"-704040002": "Serviço inexistente",
"-704040003": "Propriedade inexistente",
"-704040004": "Evento inexistente",
"-704040005": "Ação inexistente",
"-704040999": "Funcionalidade não disponível",
"-704042001": "Dispositivo inexistente",
"-704042011": "Dispositivo offline",
"-704053036": "Tempo limite de operação do dispositivo",
"-704053100": "O dispositivo não pode executar esta operação no estado atual",
"-704083036": "Tempo limite de operação do dispositivo",
"-704090001": "Dispositivo inexistente",
"-704220008": "ID inválido",
"-704220025": "Número de parâmetros da ação não corresponde",
"-704220035": "Erro nos parâmetros da ação",
"-704220043": "Valor de propriedade inválido",
"-704222034": "Erro no valor de retorno da ação",
"-705004000": "Erro desconhecido",
"-705004501": "Erro desconhecido",
"-705201013": "Propriedade não legível",
"-705201015": "Erro na execução da ação",
"-705201023": "Propriedade não gravável",
"-705201033": "Propriedade não subscritível",
"-706012000": "Erro desconhecido",
"-706012013": "Propriedade não legível",
"-706012015": "Erro na execução da ação",
"-706012023": "Propriedade não gravável",
"-706012033": "Propriedade não subscritível",
"-706012043": "Valor de propriedade inválido",
"-706014006": "Descrição do dispositivo não encontrada"
}
}
}

View File

@ -1760,7 +1760,7 @@ class MIoTClient:
delay_sec, self.__show_devices_changed_notify)
@ staticmethod
@staticmethod
async def get_miot_instance_async(
hass: HomeAssistant, entry_id: str, entry_data: Optional[dict] = None,
persistent_notify: Optional[Callable[[str, str, str], None]] = None

View File

@ -54,8 +54,8 @@ from homeassistant.helpers.entity import Entity
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS,
@ -72,7 +72,6 @@ from homeassistant.const import (
UnitOfPower,
UnitOfVolume,
UnitOfVolumeFlowRate,
UnitOfConductivity
)
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.components.switch import SwitchDeviceClass
@ -505,7 +504,8 @@ class MIoTDevice:
prop_access.add('read')
if prop.writable:
prop_access.add('write')
if prop_access != (SPEC_PROP_TRANS_MAP['entities'][platform]['access']):
if prop_access != (SPEC_PROP_TRANS_MAP[
'entities'][platform]['access']):
return None
if prop.format_ not in SPEC_PROP_TRANS_MAP[
'entities'][platform]['format']:
@ -584,7 +584,8 @@ class MIoTDevice:
self.append_action(action=action)
def unit_convert(self, spec_unit: str) -> Optional[str]:
return {
"""Convert MIoT unit to Home Assistant unit."""
unit_map = {
'percentage': PERCENTAGE,
'weeks': UnitOfTime.WEEKS,
'days': UnitOfTime.DAYS,
@ -616,11 +617,21 @@ class MIoTDevice:
'm': UnitOfLength.METERS,
'km': UnitOfLength.KILOMETERS,
'm3/h': UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
'μS/cm': UnitOfConductivity.MICROSIEMENS_PER_CM,
'gram': UnitOfMass.GRAMS,
'dB': SIGNAL_STRENGTH_DECIBELS,
'kB': UnitOfInformation.KILOBYTES,
}.get(spec_unit, None)
}
# Handle UnitOfConductivity separately since
# it might not be available in all HA versions
try:
# pylint: disable=import-outside-toplevel
from homeassistant.const import UnitOfConductivity
unit_map['μS/cm'] = UnitOfConductivity.MICROSIEMENS_PER_CM
except ImportError:
unit_map['μS/cm'] = 'μS/cm'
return unit_map.get(spec_unit, None)
def icon_convert(self, spec_unit: str) -> Optional[str]:
if spec_unit in ['percentage']:
@ -1170,8 +1181,8 @@ class MIoTEventEntity(Entity):
handler=self.__on_device_state_changed)
# Sub value changed
self.miot_device.sub_event(
handler=self.__on_event_occurred, siid=self.service.iid,
eiid=self.spec.iid)
handler=self.__on_event_occurred,
siid=self.service.iid, eiid=self.spec.iid)
async def async_will_remove_from_hass(self) -> None:
self.miot_device.unsub_device_state(

View File

@ -564,11 +564,11 @@ class MIoTLan:
0, lambda: self._main_loop.create_task(
self.init_async()))
@ property
@property
def virtual_did(self) -> str:
return self._virtual_did
@ property
@property
def mev(self) -> MIoTEventLoop:
return self._mev

View File

@ -208,9 +208,32 @@ SPEC_DEVICE_TRANS_MAP: dict[str, dict | str] = {
}
}
},
'entity': 'climate'
'entity': 'air-conditioner'
},
'air-condition-outlet': 'air-conditioner'
'air-condition-outlet': 'air-conditioner',
'heater': {
'required': {
'heater': {
'required': {
'properties': {
'on': {'read', 'write'}
}
},
'optional': {
'properties': {'target-temperature', 'heat-level'}
},
}
},
'optional': {
'environment': {
'required': {},
'optional': {
'properties': {'temperature', 'relative-humidity'}
}
},
},
'entity': 'heater'
}
}
"""SPEC_SERVICE_TRANS_MAP

View File

@ -1,7 +0,0 @@
# CHANGELOG
## 0.1.0
### Added
- first version
### Changed
### Fixed

View File

@ -325,7 +325,7 @@ event instance name 下的值表示转换后实体所用的 `_attr_device_class`
## 多语言支持
米家集成配置选项中可选择的集成使用的语言有简体中文、繁体中文、英文、西班牙语、俄语、法语、德语、日语这八种语言。目前,米家集成配置页面的简体中文和英文已经过人工校审,其他语言由机器翻译。如果您希望修改配置页面的词句,则需要修改 `custom_components/xiaomi_home/translations/` 目录下相应语言的 json 文件。
米家集成配置选项中可选择的集成使用的语言有简体中文、繁体中文、英文、西班牙语、俄语、法语、德语、日语这八种语言。目前,米家集成配置页面的简体中文和英文已经过人工校审,其他语言由机器翻译。如果您希望修改配置页面的词句,则需要修改 `custom_components/xiaomi_home/translations/` 以及 `custom_components/xiaomi_home/miot/i18n/` 目录下相应语言的 json 文件。
在显示 Home Assistant 实体名称时,米家集成会从小米云下载设备厂商为设备配置的多语言文件,该文件包含设备 MIoT-Spec-V2 实例的多语言翻译。 `multi_lang.json` 是本地维护的多语言配置字典,其优先级高于从云端获取的多语言文件,可用于补充或修改设备的多语言翻译。

View File

@ -20,8 +20,13 @@ rm -rf "$config_path/custom_components/xiaomi_home"
script_path=$(dirname "$0")
# Change to the script path.
cd "$script_path"
# Copy the new version.
cp -r custom_components/xiaomi_home/ "$config_path/custom_components/"
if [ -d "$config_path/custom_components" ]; then
cp -r custom_components/xiaomi_home/ "$config_path/custom_components/"
else
cp -r custom_components/ "$config_path/custom_components/"
fi
# Done.
echo "Xiaomi Home installation is completed. Please restart Home Assistant."