-
-
Notifications
You must be signed in to change notification settings - Fork 37.5k
Add support for LIVISI climate devices #86691
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
epenet
merged 14 commits into
home-assistant:dev
from
StefanIacobLivisi:livisi-climate-integration
Feb 27, 2023
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
f0720c3
Add support for LIVISI climate devices
StefanIacobLivisi db95f45
Remove the reauthentication logic
StefanIacobLivisi aa28a77
Add support for LIVISI climate devices
StefanIacobLivisi 3529015
Remove the reauthentication support
StefanIacobLivisi 8da1658
Code review follow-up
StefanIacobLivisi a3c5a60
Update homeassistant/components/livisi/manifest.json
StefanIacobLivisi 750f9fb
Update homeassistant/components/livisi/manifest.json
StefanIacobLivisi c89ed38
Code review follow-up
StefanIacobLivisi d76e5d7
Code Review Follow-up
StefanIacobLivisi 7650baa
Code Review Follow-up
StefanIacobLivisi 2a1a5e5
Code review follow-up
StefanIacobLivisi 820cc46
Code review follow-up
StefanIacobLivisi 2b4baca
Code review follow-up
StefanIacobLivisi 3b463f0
Code review follow-up
StefanIacobLivisi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,215 @@ | ||
| """Code to handle a Livisi Virtual Climate Control.""" | ||
|
StefanIacobLivisi marked this conversation as resolved.
Outdated
|
||
| from __future__ import annotations | ||
|
|
||
| from collections.abc import Mapping | ||
| from typing import Any | ||
|
|
||
| from aiolivisi.const import CAPABILITY_MAP | ||
|
|
||
| from homeassistant.components.climate import ( | ||
| ClimateEntity, | ||
| ClimateEntityFeature, | ||
| HVACMode, | ||
| ) | ||
| from homeassistant.config_entries import ConfigEntry | ||
| from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature | ||
| from homeassistant.core import HomeAssistant, callback | ||
| from homeassistant.exceptions import HomeAssistantError | ||
| from homeassistant.helpers.dispatcher import async_dispatcher_connect | ||
| from homeassistant.helpers.entity import DeviceInfo | ||
| from homeassistant.helpers.entity_platform import AddEntitiesCallback | ||
| from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||
|
|
||
| from .const import ( | ||
| DOMAIN, | ||
| LIVISI_REACHABILITY_CHANGE, | ||
| LIVISI_STATE_CHANGE, | ||
| LOGGER, | ||
| MAX_TEMPERATURE, | ||
| MIN_TEMPERATURE, | ||
| VRCC_DEVICE_TYPE, | ||
| ) | ||
| from .coordinator import LivisiDataUpdateCoordinator | ||
|
|
||
|
|
||
| async def async_setup_entry( | ||
| hass: HomeAssistant, | ||
| config_entry: ConfigEntry, | ||
| async_add_entities: AddEntitiesCallback, | ||
| ) -> None: | ||
| """Set up climate device.""" | ||
| coordinator: LivisiDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] | ||
|
|
||
| @callback | ||
| def handle_coordinator_update() -> None: | ||
| """Add climate device.""" | ||
| shc_devices: list[dict[str, Any]] = coordinator.data | ||
| entities: list[ClimateEntity] = [] | ||
| for device in shc_devices: | ||
| if ( | ||
| device["type"] == VRCC_DEVICE_TYPE | ||
| and device["id"] not in coordinator.devices | ||
| ): | ||
| livisi_climate: ClimateEntity = create_entity( | ||
| config_entry, device, coordinator | ||
| ) | ||
| LOGGER.debug("Include device type: %s", device.get("type")) | ||
| coordinator.devices.add(device["id"]) | ||
| entities.append(livisi_climate) | ||
| async_add_entities(entities) | ||
|
|
||
| config_entry.async_on_unload( | ||
| coordinator.async_add_listener(handle_coordinator_update) | ||
| ) | ||
|
|
||
|
|
||
| def create_entity( | ||
| config_entry: ConfigEntry, | ||
| device: dict[str, Any], | ||
| coordinator: LivisiDataUpdateCoordinator, | ||
| ) -> ClimateEntity: | ||
| """Create Climate Entity.""" | ||
| capabilities: Mapping[str, Any] = device[CAPABILITY_MAP] | ||
| room_id: str = device["location"] | ||
| room_name: str = coordinator.rooms[room_id] | ||
| livisi_climate = LivisiClimate( | ||
| config_entry, | ||
| coordinator, | ||
| unique_id=device["id"], | ||
| manufacturer=device["manufacturer"], | ||
| device_type=device["type"], | ||
| target_temperature_capability=capabilities["RoomSetpoint"], | ||
| temperature_capability=capabilities["RoomTemperature"], | ||
| humidity_capability=capabilities["RoomHumidity"], | ||
| room=room_name, | ||
| ) | ||
| return livisi_climate | ||
|
|
||
|
|
||
| class LivisiClimate(CoordinatorEntity[LivisiDataUpdateCoordinator], ClimateEntity): | ||
| """Represents the Livisi Climate.""" | ||
|
|
||
| _attr_hvac_modes = [HVACMode.HEAT] | ||
| _attr_hvac_mode = HVACMode.HEAT | ||
| _attr_temperature_unit = UnitOfTemperature.CELSIUS | ||
| _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE | ||
| _attr_target_temperature_high = MAX_TEMPERATURE | ||
| _attr_target_temperature_low = MIN_TEMPERATURE | ||
|
|
||
| def __init__( | ||
| self, | ||
| config_entry: ConfigEntry, | ||
| coordinator: LivisiDataUpdateCoordinator, | ||
| unique_id: str, | ||
| manufacturer: str, | ||
| device_type: str, | ||
| target_temperature_capability: str, | ||
| temperature_capability: str, | ||
| humidity_capability: str, | ||
| room: str, | ||
| ) -> None: | ||
| """Initialize the Livisi Climate.""" | ||
| self.config_entry = config_entry | ||
| self._attr_unique_id = unique_id | ||
| self._target_temperature_capability = target_temperature_capability | ||
| self._temperature_capability = temperature_capability | ||
| self._humidity_capability = humidity_capability | ||
| self.aio_livisi = coordinator.aiolivisi | ||
| self._attr_available = False | ||
| self._attr_device_info = DeviceInfo( | ||
| identifiers={(DOMAIN, unique_id)}, | ||
| manufacturer=manufacturer, | ||
| model=device_type, | ||
| name=room, | ||
| suggested_area=room, | ||
| via_device=(DOMAIN, config_entry.entry_id), | ||
| ) | ||
| super().__init__(coordinator) | ||
|
|
||
| async def async_set_temperature(self, **kwargs: Any) -> None: | ||
| """Set new target temperature.""" | ||
| response = await self.aio_livisi.async_vrcc_set_temperature( | ||
| self._target_temperature_capability, | ||
| kwargs.get(ATTR_TEMPERATURE), | ||
| self.coordinator.is_avatar, | ||
| ) | ||
| if response is None: | ||
| self._attr_available = False | ||
| raise HomeAssistantError(f"Failed to turn off {self._attr_name}") | ||
|
|
||
| def set_hvac_mode(self, hvac_mode: HVACMode) -> None: | ||
| """Do nothing as LIVISI devices do not support changing the hvac mode.""" | ||
|
StefanIacobLivisi marked this conversation as resolved.
|
||
| raise HomeAssistantError( | ||
| "This feature is not supported with the LIVISI climate devices" | ||
| ) | ||
|
|
||
| async def async_added_to_hass(self) -> None: | ||
| """Register callbacks.""" | ||
| target_temperature = await self.coordinator.async_get_vrcc_target_temperature( | ||
| self._target_temperature_capability | ||
| ) | ||
| temperature = await self.coordinator.async_get_vrcc_temperature( | ||
| self._temperature_capability | ||
| ) | ||
| humidity = await self.coordinator.async_get_vrcc_humidity( | ||
| self._humidity_capability | ||
| ) | ||
| if temperature is None: | ||
| self._attr_current_temperature = None | ||
| self._attr_available = False | ||
| else: | ||
| self._attr_target_temperature = target_temperature | ||
| self._attr_current_temperature = temperature | ||
| self._attr_current_humidity = humidity | ||
| self.async_on_remove( | ||
| async_dispatcher_connect( | ||
| self.hass, | ||
| f"{LIVISI_STATE_CHANGE}_{self._target_temperature_capability}", | ||
| self.update_target_temperature, | ||
| ) | ||
| ) | ||
| self.async_on_remove( | ||
| async_dispatcher_connect( | ||
| self.hass, | ||
| f"{LIVISI_STATE_CHANGE}_{self._temperature_capability}", | ||
| self.update_temperature, | ||
| ) | ||
| ) | ||
| self.async_on_remove( | ||
| async_dispatcher_connect( | ||
| self.hass, | ||
| f"{LIVISI_STATE_CHANGE}_{self._humidity_capability}", | ||
| self.update_humidity, | ||
| ) | ||
| ) | ||
| self.async_on_remove( | ||
| async_dispatcher_connect( | ||
| self.hass, | ||
| f"{LIVISI_REACHABILITY_CHANGE}_{self.unique_id}", | ||
| self.update_reachability, | ||
| ) | ||
| ) | ||
|
|
||
| @callback | ||
| def update_target_temperature(self, target_temperature: float) -> None: | ||
| """Update the target temperature of the climate device.""" | ||
| self._attr_target_temperature = target_temperature | ||
| self.async_write_ha_state() | ||
|
|
||
| @callback | ||
| def update_temperature(self, current_temperature: float) -> None: | ||
| """Update the current temperature of the climate device.""" | ||
| self._attr_current_temperature = current_temperature | ||
| self.async_write_ha_state() | ||
|
|
||
| @callback | ||
| def update_humidity(self, humidity: int) -> None: | ||
| """Update the humidity temperature of the climate device.""" | ||
| self._attr_current_humidity = humidity | ||
| self.async_write_ha_state() | ||
|
|
||
| @callback | ||
| def update_reachability(self, is_reachable: bool) -> None: | ||
| """Update the reachability of the climate device.""" | ||
| self._attr_available = is_reachable | ||
| self.async_write_ha_state() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.