Skip to content

Commit

Permalink
Merge branch 'main' into advanced-options
Browse files Browse the repository at this point in the history
# Conflicts:
#	custom_components/xiaomi_home/config_flow.py
  • Loading branch information
JimmyKmi committed Dec 18, 2024
2 parents 72c5610 + 54e2637 commit b37a249
Show file tree
Hide file tree
Showing 17 changed files with 436 additions and 43 deletions.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion doc/CONTRIBUTING.md → CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down
162 changes: 153 additions & 9 deletions custom_components/xiaomi_home/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -344,31 +347,31 @@ async def async_set_fan_mode(self, fan_mode):
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:
Expand All @@ -377,7 +380,7 @@ def hvac_mode(self) -> Optional[HVACMode]:
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.
Expand All @@ -387,7 +390,7 @@ def fan_mode(self) -> Optional[str]:
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.
Expand Down Expand Up @@ -473,3 +476,144 @@ def __ac_state_changed(self, prop: MIoTSpecProperty, value: any) -> None:
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)
4 changes: 2 additions & 2 deletions custom_components/xiaomi_home/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,8 +602,8 @@ async def async_step_advanced_options(self, user_input=None):
last_step=True,
)

@ staticmethod
@ callback
@staticmethod
@callback
def async_get_options_flow(
config_entry: config_entries.ConfigEntry,
) -> config_entries.OptionsFlow:
Expand Down
4 changes: 2 additions & 2 deletions custom_components/xiaomi_home/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def color_temp_kelvin(self) -> Optional[int]:
"""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)
Expand All @@ -247,7 +247,7 @@ def rgb_color(self) -> Optional[tuple[int, int, int]]:
b = rgb & 0xFF
return r, g, b

@ property
@property
def effect(self) -> Optional[str]:
"""Return the current mode."""
return self.__get_mode_description(
Expand Down
2 changes: 1 addition & 1 deletion custom_components/xiaomi_home/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"cryptography",
"psutil"
],
"version": "v0.1.0",
"version": "v0.1.2",
"zeroconf": [
"_miot-central._tcp.local."
]
Expand Down
8 changes: 5 additions & 3 deletions custom_components/xiaomi_home/miot/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,13 @@
'zh-Hans': '简体中文',
'zh-Hant': '繁體中文',
'en': 'English',
'de': 'Deutsch',
'es': 'Español',
'ru': 'Русский',
'fr': 'Français',
'de': 'Deutsch',
'ja': '日本語'
'ja': '日本語',
'pt': 'Português',
'pt-BR': 'Português (Brasil)',
'ru': 'Русский',
}

DEFAULT_CTRL_MODE: str = 'auto'
Expand Down
Loading

0 comments on commit b37a249

Please sign in to comment.