Skip to content

Commit 6a996c4

Browse files
committed
Merge branch 'main' into add-hacs-to-readme
2 parents 9487355 + c1867e2 commit 6a996c4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+3326
-597
lines changed

.github/ISSUE_TEMPLATE/bug_report.yaml

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
name: Bug report / 报告问题
22
description: Create a report to help us improve. / 报告问题以帮助我们改进
3-
title: "[Bug]: "
4-
labels: ["bug"]
53
body:
64
- type: input
75
attributes:
8-
label: Describe the bug / 描述问题
6+
label: Describe the Bug / 描述问题
97
description: |
108
> A clear and concise description of what the bug is.
119
> 清晰且简明地描述问题。
@@ -14,7 +12,7 @@ body:
1412

1513
- type: textarea
1614
attributes:
17-
label: To Reproduce / 复现步骤
15+
label: How to Reproduce / 复现步骤
1816
description: |
1917
> If applicable, add screenshots to help explain your problem. You can attach images by clicking this area to highlight it and then dragging files in. Steps to reproduce the behavior:
2018
> 如有需要,可添加截图以帮助解释问题。点击此区域以高亮显示并拖动截图文件以上传。请详细描述复现步骤:
@@ -28,7 +26,7 @@ body:
2826

2927
- type: input
3028
attributes:
31-
label: Expected behavior / 预期结果
29+
label: Expected Behavior / 预期结果
3230
description: |
3331
> A clear and concise description of what you expected to happen.
3432
> 描述预期结果。
@@ -44,7 +42,7 @@ body:
4442
4543
- type: input
4644
attributes:
47-
label: Home Assistant Core version / Home Assistant Core 版本
45+
label: Home Assistant Core Version / Home Assistant Core 版本
4846
description: |
4947
> [Settings > About](https://my.home-assistant.io/redirect/info)
5048
> [设置 > 关于 Home Assistant](https://my.home-assistant.io/redirect/info)
@@ -54,7 +52,7 @@ body:
5452

5553
- type: input
5654
attributes:
57-
label: Home Assistant Operation System version / Home Assistant Operation System 版本
55+
label: Home Assistant Operation System Version / Home Assistant Operation System 版本
5856
description: |
5957
> [Settings > About](https://my.home-assistant.io/redirect/info)
6058
> [设置 > 关于 Home Assistant](https://my.home-assistant.io/redirect/info)
@@ -64,7 +62,7 @@ body:
6462

6563
- type: input
6664
attributes:
67-
label: Xiaomi Home integration version / 米家集成版本
65+
label: Xiaomi Home Integration Version / 米家集成版本
6866
description: |
6967
> [Settings > Devices & services > Configured > `Xiaomi Home`](https://my.home-assistant.io/redirect/integration/?domain=xiaomi_home)
7068
> [设置 > 设备与服务 > 已配置 > `Xiaomi Home`](https://my.home-assistant.io/redirect/integration/?domain=xiaomi_home)
@@ -74,4 +72,4 @@ body:
7472

7573
- type: textarea
7674
attributes:
77-
label: Additional context / 其他说明
75+
label: Additional Context / 其他说明

CHANGELOG.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# CHANGELOG
2+
3+
## v0.1.4b0
4+
### Added
5+
### Changed
6+
### Fixed
7+
- Fix miot cloud token refresh logic. [#307](https://github.com/XiaoMi/ha_xiaomi_home/pull/307)
8+
- Fix lan ctrl filter logic. [#303](https://github.com/XiaoMi/ha_xiaomi_home/pull/303)
9+
10+
## v0.1.3
11+
### Added
12+
### Changed
13+
- Remove default bug label. [#276](https://github.com/XiaoMi/ha_xiaomi_home/pull/276)
14+
- Improve multi-language translation actions. [#256](https://github.com/XiaoMi/ha_xiaomi_home/pull/256)
15+
- Use aiohttp instead of waiting for blocking calls. [#227](https://github.com/XiaoMi/ha_xiaomi_home/pull/227)
16+
- Language supports dt. [#237](https://github.com/XiaoMi/ha_xiaomi_home/pull/237)
17+
### Fixed
18+
- Fix local control error. [#271](https://github.com/XiaoMi/ha_xiaomi_home/pull/271)
19+
- Fix README_zh and miot_storage. [#270](https://github.com/XiaoMi/ha_xiaomi_home/pull/270)
20+
21+
## v0.1.2
22+
### Added
23+
- Support Xiaomi Heater devices. https://github.com/XiaoMi/ha_xiaomi_home/issues/124 https://github.com/XiaoMi/ha_xiaomi_home/issues/117
24+
- Language supports pt, pt-BR.
25+
### Changed
26+
- Adjust the minimum version of HASS core to 2024.4.4 and above versions.
27+
### Fixed
28+
29+
## v0.1.1
30+
### Added
31+
### Changed
32+
### Fixed
33+
- Fix humidifier trans rule. https://github.com/XiaoMi/ha_xiaomi_home/issues/59
34+
- Fix get homeinfo error. https://github.com/XiaoMi/ha_xiaomi_home/issues/22
35+
- Fix air-conditioner switch on. https://github.com/XiaoMi/ha_xiaomi_home/issues/37 https://github.com/XiaoMi/ha_xiaomi_home/issues/16
36+
- Fix invalid cover status. https://github.com/XiaoMi/ha_xiaomi_home/issues/11 https://github.com/XiaoMi/ha_xiaomi_home/issues/85
37+
- Water heater entity add STATE_OFF. https://github.com/XiaoMi/ha_xiaomi_home/issues/105 https://github.com/XiaoMi/ha_xiaomi_home/issues/17
38+
39+
## v0.1.0
40+
### Added
41+
- First version
42+
### Changed
43+
### Fixed

doc/CONTRIBUTING.md CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Contribution Guidelines
22

3-
[English](./CONTRIBUTING.md) | [简体中文](./CONTRIBUTING_zh.md)
3+
[English](./CONTRIBUTING.md) | [简体中文](./doc/CONTRIBUTING_zh.md)
44

55
Thank you for considering contributing to our project! We appreciate your efforts to make our project better.
66

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Xiaomi Home Integration is an integrated component of Home Assistant supported b
88

99
> Home Assistant version requirement:
1010
>
11-
> - Core $\geq$ 2024.11.0
11+
> - Core $\geq$ 2024.4.4
1212
> - Operating System $\geq$ 13.0
1313
1414
### Method 1: Git clone from GitHub
@@ -327,7 +327,7 @@ Device information service (urn:miot-spec-v2:service:device-information:00007801
327327

328328
## Multiple Language Support
329329

330-
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.
330+
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.
331331

332332
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.
333333

@@ -380,8 +380,8 @@ Example:
380380
## Documents
381381

382382
- [License](./LICENSE.md)
383-
- Contribution Guidelines: [English](./doc/CONTRIBUTING.md) | [简体中文](./doc/CONTRIBUTING_zh.md)
384-
- [ChangeLog](./doc/CHANGELOG.md)
383+
- Contribution Guidelines: [English](./CONTRIBUTING.md) | [简体中文](./doc/CONTRIBUTING_zh.md)
384+
- [ChangeLog](./CHANGELOG.md)
385385
- Development Documents: https://developers.home-assistant.io/docs/creating_component_index
386386

387387
## Directory Structure

custom_components/xiaomi_home/climate.py

+155-11
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"""
4848
from __future__ import annotations
4949
import logging
50-
from typing import Optional
50+
from typing import Any, Optional
5151

5252
from homeassistant.config_entries import ConfigEntry
5353
from homeassistant.core import HomeAssistant
@@ -82,9 +82,12 @@ async def async_setup_entry(
8282

8383
new_entities = []
8484
for miot_device in device_list:
85-
for data in miot_device.entity_list.get('climate', []):
85+
for data in miot_device.entity_list.get('air-conditioner', []):
8686
new_entities.append(
8787
AirConditioner(miot_device=miot_device, entity_data=data))
88+
for data in miot_device.entity_list.get('heater', []):
89+
new_entities.append(
90+
Heater(miot_device=miot_device, entity_data=data))
8891

8992
if new_entities:
9093
async_add_entities(new_entities)
@@ -115,7 +118,7 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
115118
def __init__(
116119
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
117120
) -> None:
118-
"""Initialize the Climate."""
121+
"""Initialize the Air conditioner."""
119122
super().__init__(miot_device=miot_device, entity_data=entity_data)
120123
self._attr_icon = 'mdi:air-conditioner'
121124
self._attr_supported_features = ClimateEntityFeature(0)
@@ -344,31 +347,31 @@ async def async_set_fan_mode(self, fan_mode):
344347
f'set climate prop.fan_mode failed, {fan_mode}, '
345348
f'{self.entity_id}')
346349

347-
@ property
350+
@property
348351
def target_temperature(self) -> Optional[float]:
349352
"""Return the target temperature."""
350353
return self.get_prop_value(
351354
prop=self._prop_target_temp) if self._prop_target_temp else None
352355

353-
@ property
356+
@property
354357
def target_humidity(self) -> Optional[int]:
355358
"""Return the target humidity."""
356359
return self.get_prop_value(
357360
prop=self._prop_target_humi) if self._prop_target_humi else None
358361

359-
@ property
362+
@property
360363
def current_temperature(self) -> Optional[float]:
361364
"""Return the current temperature."""
362365
return self.get_prop_value(
363366
prop=self._prop_env_temp) if self._prop_env_temp else None
364367

365-
@ property
368+
@property
366369
def current_humidity(self) -> Optional[int]:
367370
"""Return the current humidity."""
368371
return self.get_prop_value(
369372
prop=self._prop_env_humi) if self._prop_env_humi else None
370373

371-
@ property
374+
@property
372375
def hvac_mode(self) -> Optional[HVACMode]:
373376
"""Return the hvac mode. e.g., heat, cool mode."""
374377
if self.get_prop_value(prop=self._prop_on) is False:
@@ -377,7 +380,7 @@ def hvac_mode(self) -> Optional[HVACMode]:
377380
map_=self._hvac_mode_map,
378381
key=self.get_prop_value(prop=self._prop_mode))
379382

380-
@ property
383+
@property
381384
def fan_mode(self) -> Optional[str]:
382385
"""Return the fan mode.
383386
@@ -387,7 +390,7 @@ def fan_mode(self) -> Optional[str]:
387390
map_=self._fan_mode_map,
388391
key=self.get_prop_value(prop=self._prop_fan_level))
389392

390-
@ property
393+
@property
391394
def swing_mode(self) -> Optional[str]:
392395
"""Return the swing mode.
393396
@@ -412,7 +415,7 @@ def swing_mode(self) -> Optional[str]:
412415
return SWING_OFF
413416
return None
414417

415-
def __ac_state_changed(self, prop: MIoTSpecProperty, value: any) -> None:
418+
def __ac_state_changed(self, prop: MIoTSpecProperty, value: Any) -> None:
416419
del prop
417420
if not isinstance(value, str):
418421
_LOGGER.error(
@@ -473,3 +476,144 @@ def __ac_state_changed(self, prop: MIoTSpecProperty, value: any) -> None:
473476
self._value_ac_state.update(v_ac_state)
474477
_LOGGER.debug(
475478
'ac_state update, %s', self._value_ac_state)
479+
480+
481+
class Heater(MIoTServiceEntity, ClimateEntity):
482+
"""Heater entities for Xiaomi Home."""
483+
# service: heater
484+
_prop_on: Optional[MIoTSpecProperty]
485+
_prop_mode: Optional[MIoTSpecProperty]
486+
_prop_target_temp: Optional[MIoTSpecProperty]
487+
_prop_heat_level: Optional[MIoTSpecProperty]
488+
# service: environment
489+
_prop_env_temp: Optional[MIoTSpecProperty]
490+
_prop_env_humi: Optional[MIoTSpecProperty]
491+
492+
_heat_level_map: Optional[dict[int, str]]
493+
494+
def __init__(
495+
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
496+
) -> None:
497+
"""Initialize the Heater."""
498+
super().__init__(miot_device=miot_device, entity_data=entity_data)
499+
self._attr_icon = 'mdi:air-conditioner'
500+
self._attr_supported_features = ClimateEntityFeature(0)
501+
self._attr_preset_modes = []
502+
503+
self._prop_on = None
504+
self._prop_mode = None
505+
self._prop_target_temp = None
506+
self._prop_heat_level = None
507+
self._prop_env_temp = None
508+
self._prop_env_humi = None
509+
self._heat_level_map = None
510+
511+
# properties
512+
for prop in entity_data.props:
513+
if prop.name == 'on':
514+
self._attr_supported_features |= (
515+
ClimateEntityFeature.TURN_ON)
516+
self._attr_supported_features |= (
517+
ClimateEntityFeature.TURN_OFF)
518+
self._prop_on = prop
519+
elif prop.name == 'target-temperature':
520+
if not isinstance(prop.value_range, dict):
521+
_LOGGER.error(
522+
'invalid target-temperature value_range format, %s',
523+
self.entity_id)
524+
continue
525+
self._attr_min_temp = prop.value_range['min']
526+
self._attr_max_temp = prop.value_range['max']
527+
self._attr_target_temperature_step = prop.value_range['step']
528+
self._attr_temperature_unit = prop.external_unit
529+
self._attr_supported_features |= (
530+
ClimateEntityFeature.TARGET_TEMPERATURE)
531+
self._prop_target_temp = prop
532+
elif prop.name == 'heat-level':
533+
if (
534+
not isinstance(prop.value_list, list)
535+
or not prop.value_list
536+
):
537+
_LOGGER.error(
538+
'invalid heat-level value_list, %s', self.entity_id)
539+
continue
540+
self._heat_level_map = {
541+
item['value']: item['description']
542+
for item in prop.value_list}
543+
self._attr_preset_modes = list(self._heat_level_map.values())
544+
self._attr_supported_features |= (
545+
ClimateEntityFeature.PRESET_MODE)
546+
self._prop_heat_level = prop
547+
elif prop.name == 'temperature':
548+
self._prop_env_temp = prop
549+
elif prop.name == 'relative-humidity':
550+
self._prop_env_humi = prop
551+
552+
# hvac modes
553+
self._attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
554+
555+
async def async_turn_on(self) -> None:
556+
"""Turn the entity on."""
557+
await self.set_property_async(prop=self._prop_on, value=True)
558+
559+
async def async_turn_off(self) -> None:
560+
"""Turn the entity off."""
561+
await self.set_property_async(prop=self._prop_on, value=False)
562+
563+
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
564+
"""Set new target hvac mode."""
565+
await self.set_property_async(
566+
prop=self._prop_on, value=False
567+
if hvac_mode == HVACMode.OFF else True)
568+
569+
async def async_set_temperature(self, **kwargs):
570+
"""Set new target temperature."""
571+
if ATTR_TEMPERATURE in kwargs:
572+
temp = kwargs[ATTR_TEMPERATURE]
573+
if temp > self.max_temp:
574+
temp = self.max_temp
575+
elif temp < self.min_temp:
576+
temp = self.min_temp
577+
578+
await self.set_property_async(
579+
prop=self._prop_target_temp, value=temp)
580+
581+
async def async_set_preset_mode(self, preset_mode: str) -> None:
582+
"""Set the preset mode."""
583+
await self.set_property_async(
584+
self._prop_heat_level,
585+
value=self.get_map_value(
586+
map_=self._heat_level_map, description=preset_mode))
587+
588+
@property
589+
def target_temperature(self) -> Optional[float]:
590+
"""Return the target temperature."""
591+
return self.get_prop_value(
592+
prop=self._prop_target_temp) if self._prop_target_temp else None
593+
594+
@property
595+
def current_temperature(self) -> Optional[float]:
596+
"""Return the current temperature."""
597+
return self.get_prop_value(
598+
prop=self._prop_env_temp) if self._prop_env_temp else None
599+
600+
@property
601+
def current_humidity(self) -> Optional[int]:
602+
"""Return the current humidity."""
603+
return self.get_prop_value(
604+
prop=self._prop_env_humi) if self._prop_env_humi else None
605+
606+
@property
607+
def hvac_mode(self) -> Optional[HVACMode]:
608+
"""Return the hvac mode."""
609+
return (
610+
HVACMode.HEAT if self.get_prop_value(prop=self._prop_on)
611+
else HVACMode.OFF)
612+
613+
@property
614+
def preset_mode(self) -> Optional[str]:
615+
return (
616+
self.get_map_description(
617+
map_=self._heat_level_map,
618+
key=self.get_prop_value(prop=self._prop_heat_level))
619+
if self._prop_heat_level else None)

0 commit comments

Comments
 (0)