Skip to content

Commit

Permalink
translation via miot-spec
Browse files Browse the repository at this point in the history
  • Loading branch information
al-one committed Oct 30, 2024
1 parent fbede48 commit e863c78
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 9 deletions.
3 changes: 3 additions & 0 deletions custom_components/xiaomi_miot/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ async def async_step_cloud(self, user_input=None):
vol.In(CLOUD_SERVERS),
vol.Required(CONF_CONN_MODE, default=user_input.get(CONF_CONN_MODE, 'auto')):
vol.In(CONN_MODES),
vol.Optional('trans_options', default=user_input.get('trans_options', False)): bool,
vol.Optional('filter_models', default=user_input.get('filter_models', False)): bool,
})
return self.async_show_form(
Expand Down Expand Up @@ -662,6 +663,7 @@ async def async_step_cloud(self, user_input=None):
vol.Required(CONF_CONN_MODE, default=user_input.get(CONF_CONN_MODE, DEFAULT_CONN_MODE)):
vol.In(CONN_MODES),
vol.Optional('renew_devices', default=user_input.get('renew_devices', False)): bool,
vol.Optional('trans_options', default=user_input.get('trans_options', False)): bool,
vol.Optional('disable_message', default=user_input.get('disable_message', False)): bool,
vol.Optional('disable_scene_history', default=user_input.get('disable_scene_history', False)): bool,
})
Expand Down Expand Up @@ -690,6 +692,7 @@ async def async_step_cloud_filter(self, user_input=None):
cfg = self.cloud.to_config() or {}
cfg.update({
CONF_CONN_MODE: prev_input.get(CONF_CONN_MODE),
'trans_options': prev_input.get('trans_options'),
'filter_models': prev_input.get('filter_models'),
'disable_message': prev_input.get('disable_message'),
'disable_scene_history': prev_input.get('disable_scene_history'),
Expand Down
3 changes: 2 additions & 1 deletion custom_components/xiaomi_miot/core/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,9 @@ async def get_spec(self) -> Optional[MiotSpec]:
dat = self.hass.data[DOMAIN].setdefault('miot_specs', {})
obj = dat.get(self.model)
if not obj:
trans_options = self.custom_config_bool('trans_options', self.entry.get_config('trans_options'))
urn = await self.get_urn()
obj = await MiotSpec.async_from_type(self.hass, urn)
obj = await MiotSpec.async_from_type(self.hass, urn, trans_options=trans_options)
dat[self.model] = obj
if obj:
self.spec = copy.copy(obj)
Expand Down
102 changes: 94 additions & 8 deletions custom_components/xiaomi_miot/core/miot_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import random
import time
import re
from functools import cached_property

from homeassistant.core import HomeAssistant
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
Expand All @@ -30,6 +32,7 @@
DOMAIN,
TRANSLATION_LANGUAGES,
)
from .utils import get_translation_langs

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -104,7 +107,7 @@ def name_by_type(typ):
def translation_keys(self):
return ['_globals']

@property
@cached_property
def translations(self):
dic = TRANSLATION_LANGUAGES
kls = self.translation_keys
Expand All @@ -115,7 +118,7 @@ def translations(self):
dic = {**dic, **d}
return dic

def get_translation(self, des):
def get_translation(self, des, viid=None):
dls = [
des.lower(),
des,
Expand All @@ -132,6 +135,11 @@ def get_translation(self, des):
continue
ret = ret[d]
return ret

fun = getattr(self, 'get_spec_translation', None)
ret = fun(viid=viid) if fun else None
if ret:
return ret
return des

@staticmethod
Expand All @@ -150,7 +158,10 @@ def __repr__(self):

# https://iot.mi.com/new/doc/design/spec/xiaoai
class MiotSpec(MiotSpecInstance):
def __init__(self, dat: dict):
def __init__(self, hass: HomeAssistant, dat: dict, translations=None, trans_options=None):
self.hass = hass
self.trans_options = trans_options
self.spec_translations = translations or {}
super().__init__(dat)
self.services = {}
self.services_count = {}
Expand Down Expand Up @@ -254,6 +265,18 @@ def generate_entity_id(self, entity, suffix=None, domain=None):
domain = DOMAIN
return f'{domain}.{eid}'

def get_spec_translation(self, siid, piid=None, aiid=None, viid=None):
if viid is not None and not self.trans_options:
return None
key = MiotSpec.spec_lang_key(siid, piid, aiid, viid)
langs = get_translation_langs(self.hass, self.spec_translations.keys())
for lang in langs:
dic = self.spec_translations.get(lang) or {}
val = dic.get(key)
if val is not None:
return val
return None

@staticmethod
async def async_from_model(hass, model, use_remote=False):
typ = await MiotSpec.async_get_model_type(hass, model, use_remote)
Expand Down Expand Up @@ -317,7 +340,7 @@ async def async_get_model_type(hass, model, use_remote=False):
return typ

@staticmethod
async def async_from_type(hass, typ):
async def async_from_type(hass, typ, trans_options=False):
if not typ:
return None
fnm = f'{DOMAIN}/{typ}.json'
Expand Down Expand Up @@ -354,7 +377,9 @@ async def async_from_type(hass, typ):
}
await store.async_save(dat)
_LOGGER.warning('Get miot-spec for %s failed: %s', typ, exc)
return MiotSpec(dat)

translations = await MiotSpec.async_get_langs(hass, typ)
return MiotSpec(hass, dat, translations, trans_options=trans_options)

@staticmethod
def unique_prop(siid, piid=None, aiid=None, eiid=None, valid=False):
Expand All @@ -375,6 +400,57 @@ def unique_prop(siid, piid=None, aiid=None, eiid=None, valid=False):
return None
return f'{typ}.{siid}.{iid}'

@staticmethod
def spec_lang_key(siid, piid=None, aiid=None, viid=None):
key = f'service:{siid:03}'
if aiid is not None:
return f'{key}:action:{aiid:03}'
if piid is not None:
key = f'{key}:property:{piid:03}'
if viid is not None:
key = f'{key}:valuelist:{viid:03}'
return key

@staticmethod
async def async_get_langs(hass, typ):
if not typ:
return None
fnm = f'{DOMAIN}/spec-langs/{typ}.json'
if platform.system() == 'Windows':
fnm = fnm.replace(':', '_')
store = Store(hass, 1, fnm)
try:
cached = await store.async_load() or {}
except (ValueError, HomeAssistantError):
await store.async_remove()
cached = {}
dat = cached
ptm = dat.pop('_updated_time', 0)
now = int(time.time())
ttl = 60
if dat.get('data'):
ttl = 86400 * random.randint(30, 50)
if dat and now - ptm > ttl:
dat = {}
if not dat.get('type'):
try:
url = f'/instance/v2/multiLanguage?urn={typ}'
dat = await MiotSpec.async_download_miot_spec(hass, url, tries=3)
dat['_updated_time'] = now
await store.async_save(dat)
except (TypeError, ValueError, BaseException) as exc:
if cached:
dat = cached
else:
dat = {
'type': typ or 'unknown',
'exception': f'{exc}',
'_updated_time': now,
}
await store.async_save(dat)
_LOGGER.warning('Get miot-spec langs for %s failed: %s', typ, exc)
return dat.get('data') or {}

@staticmethod
async def async_download_miot_spec(hass, path, tries=1, timeout=30):
session = async_get_clientsession(hass)
Expand Down Expand Up @@ -537,6 +613,9 @@ def unique_prop(self, **kwargs):
def generate_entity_id(self, entity, domain=None):
return self.spec.generate_entity_id(entity, self.desc_name, domain)

def get_spec_translation(self, piid=None, aiid=None, viid=None):
return self.spec.get_spec_translation(self.iid, piid=piid, aiid=aiid, viid=viid)

@property
def translation_keys(self):
return ['_globals', self.name]
Expand Down Expand Up @@ -633,6 +712,9 @@ def generate_entity_id(self, entity, domain=None):
eid = re.sub(r'_(\d(?:_|$))', r'\1', eid) # issue#153
return eid

def get_spec_translation(self, viid=None):
return self.service.get_spec_translation(piid=self.iid, viid=viid)

@property
def translation_keys(self):
return [
Expand Down Expand Up @@ -679,12 +761,13 @@ def list_value(self, des):
def list_description(self, val):
rls = []
for v in self.value_list:
des = self.get_translation(v.get('description'))
vid = v.get('value')
des = self.get_translation(v.get('description'), viid=vid)
if val is None:
if des == '':
des = v.get('value')
des = vid
rls.append(str(des))
elif val == v.get('value'):
elif val == vid:
return des
if rls and val is None:
return rls
Expand Down Expand Up @@ -977,6 +1060,9 @@ def out_results(self, out=None):
return dict(zip(kls, out))
return None

def get_spec_translation(self, viid=None):
return self.service.get_spec_translation(aiid=self.iid)

@property
def translation_keys(self):
return [
Expand Down
9 changes: 9 additions & 0 deletions custom_components/xiaomi_miot/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import voluptuous as vol
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity
from homeassistant.util import language as language_util
from homeassistant.util.dt import DEFAULT_TIME_ZONE, get_time_zone
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
Expand Down Expand Up @@ -174,6 +175,14 @@ def get_translations(*keys):
dic.update(tls)
return dic

def get_translation_langs(hass: HomeAssistant, langs=None):
lang = hass.config.language
if not langs:
return lang
if 'en' not in langs:
langs.append('en')
return language_util.matches(lang, langs)


def is_offline_exception(exc):
err = f'{exc}'
Expand Down
2 changes: 2 additions & 0 deletions custom_components/xiaomi_miot/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"captcha": "Captcha",
"server_country": "Server location of MiCloud",
"conn_mode": "Connection mode for device",
"trans_options": "Translation property value description",
"filter_models": "Filter devices via model/home/WiFi (Advanced)"
}
},
Expand Down Expand Up @@ -154,6 +155,7 @@
"server_country": "Server location of MiCloud",
"conn_mode": "Connection mode for device",
"renew_devices": "Force renew devices",
"trans_options": "Translation property value description",
"disable_message": "Disable Mihome notification sensor",
"disable_scene_history": "Disable Mihome scene history sensor"
}
Expand Down
2 changes: 2 additions & 0 deletions custom_components/xiaomi_miot/translations/zh-Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"captcha": "验证码",
"server_country": "小米服务器",
"conn_mode": "设备连接模式",
"trans_options": "翻译属性值描述",
"filter_models": "通过型号/家庭/WiFi筛选设备 (高级模式,新手勿选)"
}
},
Expand Down Expand Up @@ -93,6 +94,7 @@
"server_country": "小米服务器",
"conn_mode": "设备连接模式",
"renew_devices": "更新设备列表",
"trans_options": "翻译属性值描述",
"disable_message": "禁用米家APP通知消息实体",
"disable_scene_history": "禁用米家场景历史实体"
}
Expand Down

0 comments on commit e863c78

Please sign in to comment.