Skip to content

Commit

Permalink
Merge pull request #60 from RobertD502/hvac_mode_restructure
Browse files Browse the repository at this point in the history
Hvac mode restructure
  • Loading branch information
RobertD502 authored Jul 1, 2023
2 parents dd978b1 + 57de644 commit ac67023
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 154 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,9 @@ Each IR HVAC unit has the following entities:
| Entity | Entity Type | Additional Comments |
|-------------| --- |---------------------|
| `HVAC unit` | `Climate` | SEE NOTE BELOW |
| `HVAC power` | `Switch` | Only available if structure mode is set to manual |

> To fully control your unit, the associated Flair structure needs to be in `Manual Mode`.
>
> If your structure is set to `Auto Mode`: you will only be able to control `Fan speed` and `Swing` (if available for your unit). In addition, mini split set points are controlled by rooms if a Flair structure is set to `Auto Mode`. Changing the temperature of this climate entity will result in changing the room set point when in `Auto Mode`. You also cannot change the mode as this is controlled at the Structure level when in auto mode.
> If your structure is set to `Auto Mode`: you will only be able to control `Fan speed` and `Swing` (if available for your unit). In addition, mini split temperature set point is controlled by rooms if a Flair structure is set to `Auto Mode`. Changing the temperature of this climate entity will result in changing the room set point when in `Auto Mode`. You also cannot change the HVAC mode as this is controlled at the Structure level when in auto mode.
>
> If your structure is set to `Manual Mode`: You can only change the temp, mode, fan speed, and swing when your unit is powered on. If your structure is in manual mode, you can turn your mini split on/off by utilizing the `HVAC power` switch entity.
> If your structure is set to `Manual Mode`: Setting the HVAC mode to `Off` will turn your HVAC unit off. In order to turn the unit on, set the HVAC mode to your desired HVAC mode (Heat, Cool, Fan Only, etc).
117 changes: 91 additions & 26 deletions custom_components/flair/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util.unit_system import METRIC_SYSTEM
Expand Down Expand Up @@ -559,6 +560,22 @@ def available_fan_speeds(self) -> list[str]:
fan_speeds.append(key)
return fan_speeds

@property
def fan_only_fan_speeds(self) -> list[str]:
"""Returns available fan speeds when in fan only hvac mode."""

mode = "FAN"
fan_speeds = []

if "ON" in self.hvac_data.attributes['constraints']['ON'][mode].keys():
for key in self.hvac_data.attributes['constraints']['ON'][mode]['ON'].keys():
fan_speeds.append(key)
return fan_speeds
else:
for key in self.hvac_data.attributes['constraints']['ON'][mode]['OFF'].keys():
fan_speeds.append(key)
return fan_speeds

@property
def swing_available(self) -> bool:
"""Determine if swing mode is available."""
Expand All @@ -580,33 +597,33 @@ def hvac_mode(self) -> HVACMode:

mode = self.hvac_data.attributes['mode']

# Always revert to Off if a manual HVAC is powered off
if (not self.is_on) and (self.structure_mode == 'manual'):
return HVACMode.OFF

if mode in HVAC_CURRENT_MODE_MAP:
return HVAC_CURRENT_MODE_MAP[mode]

@property
def hvac_modes(self) -> list[str]:
"""Return the Supported Modes.
Can't change modes when structure mode is manual & off
or when in auto mode regardless of power state. So, we
need to only return the last mode it was in.
"""
"""Return the Supported Modes."""

current_mode = self.hvac_data.attributes['mode']

if (self.structure_mode == 'manual') and (not self.is_on):
supported_modes = []
if current_mode in HVAC_CURRENT_MODE_MAP:
supported_modes.append(HVAC_CURRENT_MODE_MAP[current_mode])
return supported_modes
elif self.structure_mode =='auto':
# Can't change modes when structure mode is
# auto regardless of power state. So, we
# need to only return the last mode it was in.
if self.structure_mode =='auto':
supported_modes = []
if current_mode in HVAC_CURRENT_MODE_MAP:
supported_modes.append(HVAC_CURRENT_MODE_MAP[current_mode])
return supported_modes
else:
modes = self.available_hvac_modes
supported_modes = []
# Always make Off mode avaiilable for manually controlled units
if self.structure_mode == 'manual':
supported_modes.append(HVACMode.OFF)
for mode in modes:
if mode in HVAC_AVAILABLE_MODES_MAP:
supported_modes.append(HVAC_AVAILABLE_MODES_MAP[mode])
Expand Down Expand Up @@ -790,7 +807,7 @@ async def async_set_temperature(self, **kwargs) -> None:

if self.structure_mode == 'manual':
if not self.is_on:
LOGGER.error(f'Temperature for {self.hvac_data.attributes["name"]} can only be set when it is powered on.')
raise HomeAssistantError(f'Temperature for {self.hvac_data.attributes["name"]} can only be set when it is powered on.')
else:
auto_mode = False
type_id = self.hvac_data.id
Expand All @@ -802,19 +819,62 @@ async def async_set_temperature(self, **kwargs) -> None:
await self.coordinator.async_request_refresh()

async def async_set_hvac_mode(self, hvac_mode) -> None:
"""Set new target hvac mode.
Setting the targert temperature can only be done
when structure state is manual and HVAC unit is on.
"""

mode = HASS_HVAC_MODE_TO_FLAIR.get(hvac_mode)
attributes = self.set_attributes('hvac_mode', mode, False)
"""Set new target hvac mode."""

if hvac_mode == HVACMode.DRY:
await self.async_set_fan_mode(FAN_AUTO)
await self.coordinator.client.update('hvac-units', self.hvac_data.id, attributes=attributes, relationships={})
self.hvac_data.attributes['mode'] = mode
# Power off manual HVAC unit
if hvac_mode == HVACMode.OFF:
power_attributes = {"power": "Off"}
await self.coordinator.client.update('hvac-units', self.hvac_data.id, attributes=power_attributes, relationships={})
self.hvac_data.attributes['power'] = 'Off'
elif self.structure_mode == 'manual':
# Turn the HVAC unit on before sending desired mode
if not self.is_on:
power_attributes = {"power": "On"}
await self.coordinator.client.update('hvac-units', self.hvac_data.id, attributes=power_attributes, relationships={})
self.hvac_data.attributes['power'] = 'On'

mode = HASS_HVAC_MODE_TO_FLAIR.get(hvac_mode)

if hvac_mode == HVACMode.DRY:
# When switching from Fan Only mode to Dry Mode,
# the fan speed needs to be set to Auto along with Dry Mode.
# Otherwise, Flair API will complain that Auto mode isn't available in
# Fan Only mode.
# Note: There is currently a bug in the API where it takes multiple
# tries until the validation error doesn't get raised.
if self.hvac_mode == HVACMode.FAN_ONLY:
flair_speed = HASS_HVAC_FAN_SPEED_TO_FLAIR.get(FAN_AUTO)
attributes = self.set_attributes('hvac_mode-fan_speed', mode, False, flair_speed)
await self.coordinator.client.update('hvac-units', self.hvac_data.id, attributes=attributes, relationships={})
self.hvac_data.attributes['mode'] = mode
self.hvac_data.attributes['fan-speed'] = flair_speed
self.async_write_ha_state()
await self.coordinator.async_request_refresh()
return None
else:
await self.async_set_fan_mode(FAN_AUTO)
if hvac_mode == HVACMode.FAN_ONLY:
# If fan speed is Auto before switching to Fan Only mode,
# we have to set a valid fan speed since Auto isn't available
# in Fan Only mode.
# Note: There is currently a bug in the API where it takes multiple
# tries until the validation error doesn't get raised.
if self.fan_mode == FAN_AUTO:
valid_fan_speed = HVAC_AVAILABLE_FAN_SPEEDS[self.fan_only_fan_speeds[0]]
flair_speed = HASS_HVAC_FAN_SPEED_TO_FLAIR.get(valid_fan_speed)
attributes = self.set_attributes('hvac_mode-fan_speed', mode, False, flair_speed)
await self.coordinator.client.update('hvac-units', self.hvac_data.id, attributes=attributes, relationships={})
self.hvac_data.attributes['mode'] = mode
self.hvac_data.attributes['fan-speed'] = flair_speed
self.async_write_ha_state()
await self.coordinator.async_request_refresh()
return None
# For all other cases just send the hvac_mode
attributes = self.set_attributes('hvac_mode', mode, False)
await self.coordinator.client.update('hvac-units', self.hvac_data.id, attributes=attributes, relationships={})
self.hvac_data.attributes['mode'] = mode
else:
return None
self.async_write_ha_state()
await self.coordinator.async_request_refresh()

Expand Down Expand Up @@ -868,7 +928,7 @@ async def async_set_swing_mode(self, swing_mode) -> None:
await self.coordinator.async_request_refresh()

@staticmethod
def set_attributes(setting: str, value: Any, auto_mode: bool) -> dict[str, Any]:
def set_attributes(setting: str, value: Any, auto_mode: bool, extra_val: str = None) -> dict[str, Any]:
"""Create attributes dictionary for client update method."""

attributes: dict[str, Any] = {}
Expand Down Expand Up @@ -903,5 +963,10 @@ def set_attributes(setting: str, value: Any, auto_mode: bool) -> dict[str, Any]:
attributes ={
"swing": value,
}
if setting == 'hvac_mode-fan_speed':
attributes = {
"mode": value,
"fan-speed": extra_val
}

return attributes
2 changes: 1 addition & 1 deletion custom_components/flair/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/RobertD502/home-assistant-flair/issues",
"requirements": ["flairaio==0.1.3"],
"version": "0.1.8.3"
"version": "0.1.9"
}
125 changes: 1 addition & 124 deletions custom_components/flair/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from typing import Any

from flairaio.model import HVACUnit, Puck, Structure
from flairaio.model import Puck, Structure

from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
Expand Down Expand Up @@ -38,20 +38,6 @@ async def async_setup_entry(
PuckLock(coordinator, structure_id, puck_id),
))

# HVAC Units
if structure_data.hvac_units:
for hvac_id, hvac_data in structure_data.hvac_units.items():
try:
structure_data.hvac_units[hvac_id].attributes['constraints']['temperature-scale']
except KeyError:
unit_name = structure_data.hvac_units[hvac_id].attributes['name']
LOGGER.error(f'''Flair HVAC Unit {unit_name} does not have a temperature scale.
Contact Flair customer support to get this fixed.''')
continue
switches.extend((
HVACPower(coordinator, structure_id, hvac_id),
))

async_add_entities(switches)


Expand Down Expand Up @@ -228,112 +214,3 @@ async def async_turn_off(self, **kwargs) -> None:
self.puck_data.attributes['locked'] = False
self.async_write_ha_state()
await self.coordinator.async_request_refresh()


class HVACPower(CoordinatorEntity, SwitchEntity):
"""Representation of HVAC Unit Power."""

def __init__(self, coordinator, structure_id, hvac_id):
super().__init__(coordinator)
self.hvac_id = hvac_id
self.structure_id = structure_id

@property
def hvac_data(self) -> HVACUnit:
"""Handle coordinator HVAC unit data."""

return self.coordinator.data.structures[self.structure_id].hvac_units[self.hvac_id]

@property
def structure_data(self) -> Structure:
"""Handle coordinator structure data."""

return self.coordinator.data.structures[self.structure_id]

@property
def puck_data(self) -> Puck:
"""Handle coordinator puck data."""

puck_id = self.hvac_data.relationships['puck']['data']['id']
return self.coordinator.data.structures[self.structure_id].pucks[puck_id]

@property
def structure_mode(self) -> str:
"""Return structure mode of associated structure."""

return self.structure_data.attributes['mode']

@property
def device_info(self) -> dict[str, Any]:
"""Return device registry information for this entity."""

return {
"identifiers": {(DOMAIN, self.hvac_data.id)},
"name": self.hvac_data.attributes['name'],
"manufacturer": self.hvac_data.attributes['make-name'],
"model": "HVAC Unit",
"configuration_url": "https://my.flair.co/",
}

@property
def unique_id(self) -> str:
"""Sets unique ID for this entity."""

return str(self.hvac_data.id) + '_hvac_power'

@property
def name(self) -> str:
"""Return name of the entity."""

return "HVAC power"

@property
def has_entity_name(self) -> bool:
"""Indicate that entity has name defined."""

return True

@property
def icon(self) -> str:
"""Set hvac icon."""

return 'mdi:hvac'

@property
def is_on(self) -> bool:
"""Return if HVAC unit is on."""

return self.hvac_data.attributes['power'] == 'On'

@property
def available(self) -> bool:
"""Determine if puck is active.
Return true if puck is active and
structure is set to manual mode.
"""

puck_inactive = self.puck_data.attributes['inactive']

if (puck_inactive == False) and (self.structure_mode == 'manual'):
return True
else:
return False

async def async_turn_on(self, **kwargs) -> None:
"""Turn on HVAC unit."""

attributes = {"power": "On"}
await self.coordinator.client.update('hvac-units', self.hvac_data.id, attributes=attributes, relationships={})
self.hvac_data.attributes['power'] = 'On'
self.async_write_ha_state()
await self.coordinator.async_request_refresh()

async def async_turn_off(self, **kwargs) -> None:
"""Turn off HVAC unit."""

attributes = {"power": "Off"}
await self.coordinator.client.update('hvac-units', self.hvac_data.id, attributes=attributes, relationships={})
self.hvac_data.attributes['power'] = 'Off'
self.async_write_ha_state()
await self.coordinator.async_request_refresh()

0 comments on commit ac67023

Please sign in to comment.