Skip to content

Commit

Permalink
Merge branch 'XiaoMi:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
SusanPhevos authored Dec 18, 2024
2 parents b7fc534 + caec202 commit fa38293
Show file tree
Hide file tree
Showing 19 changed files with 746 additions and 48 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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
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 @@ -564,8 +564,8 @@ async def display_device_filter_form(self, reason: str):
last_step=False,
)

@ staticmethod
@ callback
@staticmethod
@callback
def async_get_options_flow(
config_entry: config_entries.ConfigEntry,
) -> config_entries.OptionsFlow:
Expand Down
15 changes: 9 additions & 6 deletions custom_components/xiaomi_home/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ class Cover(MIoTServiceEntity, CoverEntity):
_prop_motor_value_close: Optional[int]
_prop_motor_value_pause: Optional[int]
_prop_status: Optional[MIoTSpecProperty]
_prop_status_opening: Optional[bool]
_prop_status_closing: Optional[bool]
_prop_status_stop: Optional[bool]
_prop_status_opening: Optional[int]
_prop_status_closing: Optional[int]
_prop_status_stop: Optional[int]
_prop_current_position: Optional[MIoTSpecProperty]
_prop_target_position: Optional[MIoTSpecProperty]
_prop_position_value_min: Optional[int]
Expand All @@ -120,6 +120,9 @@ def __init__(
self._prop_motor_value_close = None
self._prop_motor_value_pause = None
self._prop_status = None
self._prop_status_opening = None
self._prop_status_closing = None
self._prop_status_stop = None
self._prop_current_position = None
self._prop_target_position = None
self._prop_position_value_min = None
Expand Down Expand Up @@ -159,11 +162,11 @@ def __init__(
'status value_list is None, %s', self.entity_id)
continue
for item in prop.value_list:
if item['name'].lower() in ['opening']:
if item['name'].lower() in ['opening', 'open']:
self._prop_status_opening = item['value']
elif item['name'].lower() in ['closing']:
elif item['name'].lower() in ['closing', 'close']:
self._prop_status_closing = item['value']
elif item['name'].lower() in ['stop']:
elif item['name'].lower() in ['stop', 'pause']:
self._prop_status_stop = item['value']
self._prop_status = prop
elif prop.name == 'current-position':
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 fa38293

Please sign in to comment.