Skip to content

Commit

Permalink
Add support for auto_light and auto_xfan (#157)
Browse files Browse the repository at this point in the history
* Update climate.py

Changed async_track_state_change to async_track_state_change_event according to https://developers.home-assistant.io/blog/2024/04/13/deprecate_async_track_state_change/

* Update climate.py

* Update climate.py

* Update climate.py

* Update climate.py

* Update climate.py

Added support for auto_light
Added support for auto_xfan

* Update climate.py

* Update climate.py

* Update README.md
  • Loading branch information
toughvj committed May 15, 2024
1 parent 4e2da92 commit 44ceb52
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 34 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ This component is added to HACS default repository list.
powersave: <OPTIONAL: input_boolean to switch AC powersave mode on/off. For example: input_boolean.first_ac_powersave>
eightdegheat: <OPTIONAL: input_boolean used to switch 8 degree heating on/off on your first AC>
air: <OPTIONAL: input_boolean used to switch air/scavenging on/off on your first AC>
target_temp: <OPTIONAL: input_number used to set the temperature of your first AC. This is usefull if you want to use dashboards with custom frontend components>
auto_xfan: <OPTIONAL: boolean (true/false); this feature will always turn on xFan in cool and dry mode to avoid mold & rust created from potential water buildup in the AC>
auto_light: <OPTIONAL: boolean (true/false); this feature will always turn light on when power on and turn light light off when power off automatically>

- platform: gree
name: Second AC
Expand Down
134 changes: 100 additions & 34 deletions custom_components/gree/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
CONF_AIR = 'air'
CONF_ENCRYPTION_KEY = 'encryption_key'
CONF_UID = 'uid'
CONF_AUTO_XFAN = 'auto_xfan'
CONF_AUTO_LIGHT = 'auto_light'
CONF_TARGET_TEMP = 'target_temp'

DEFAULT_PORT = 7000
DEFAULT_TIMEOUT = 10
Expand Down Expand Up @@ -96,7 +99,10 @@
vol.Optional(CONF_EIGHTDEGHEAT): cv.entity_id,
vol.Optional(CONF_AIR): cv.entity_id,
vol.Optional(CONF_ENCRYPTION_KEY): cv.string,
vol.Optional(CONF_UID): cv.positive_int
vol.Optional(CONF_UID): cv.positive_int,
vol.Optional(CONF_AUTO_XFAN): cv.boolean,
vol.Optional(CONF_AUTO_LIGHT): cv.boolean,
vol.Optional(CONF_TARGET_TEMP): cv.entity_id
})

async def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
Expand All @@ -116,21 +122,25 @@ async def async_setup_platform(hass, config, async_add_devices, discovery_info=N
sleep_entity_id = config.get(CONF_SLEEP)
eightdegheat_entity_id = config.get(CONF_EIGHTDEGHEAT)
air_entity_id = config.get(CONF_AIR)
target_temp_entity_id = config.get(CONF_TARGET_TEMP)
hvac_modes = HVAC_MODES
fan_modes = FAN_MODES
swing_modes = SWING_MODES
preset_modes = PRESET_MODES
encryption_key = config.get(CONF_ENCRYPTION_KEY)
uid = config.get(CONF_UID)
auto_xfan = config.get(CONF_AUTO_XFAN)
auto_light = config.get(CONF_AUTO_LIGHT)

_LOGGER.info('Adding Gree climate device to hass')

async_add_devices([
GreeClimate(hass, name, ip_addr, port, mac_addr, timeout, target_temp_step, temp_sensor_entity_id, lights_entity_id, xfan_entity_id, health_entity_id, powersave_entity_id, sleep_entity_id, eightdegheat_entity_id, air_entity_id, hvac_modes, fan_modes, swing_modes, preset_modes, encryption_key, uid)
GreeClimate(hass, name, ip_addr, port, mac_addr, timeout, target_temp_step, temp_sensor_entity_id, lights_entity_id, xfan_entity_id, health_entity_id, powersave_entity_id, sleep_entity_id, eightdegheat_entity_id, air_entity_id, target_temp_entity_id, hvac_modes, fan_modes, swing_modes, preset_modes, auto_xfan, auto_light, encryption_key, uid)
])

class GreeClimate(ClimateEntity):

def __init__(self, hass, name, ip_addr, port, mac_addr, timeout, target_temp_step, temp_sensor_entity_id, lights_entity_id, xfan_entity_id, health_entity_id, powersave_entity_id, sleep_entity_id, eightdegheat_entity_id, air_entity_id, hvac_modes, fan_modes, swing_modes, preset_modes, encryption_key=None, uid=None):
def __init__(self, hass, name, ip_addr, port, mac_addr, timeout, target_temp_step, temp_sensor_entity_id, lights_entity_id, xfan_entity_id, health_entity_id, powersave_entity_id, sleep_entity_id, eightdegheat_entity_id, air_entity_id, target_temp_entity_id, hvac_modes, fan_modes, swing_modes, preset_modes, auto_xfan, auto_light,encryption_key=None, uid=None):
_LOGGER.info('Initialize the GREE climate device')
self.hass = hass
self._name = name
Expand All @@ -152,6 +162,7 @@ def __init__(self, hass, name, ip_addr, port, mac_addr, timeout, target_temp_ste
self._sleep_entity_id = sleep_entity_id
self._eightdegheat_entity_id = eightdegheat_entity_id
self._air_entity_id = air_entity_id
self._target_temp_entity_id = target_temp_entity_id

self._hvac_mode = None
self._fan_mode = None
Expand Down Expand Up @@ -179,6 +190,9 @@ def __init__(self, hass, name, ip_addr, port, mac_addr, timeout, target_temp_ste
self._encryption_key = self.GetDeviceKey().encode("utf8")
_LOGGER.info('Fetched device encrytion key: %s' % str(self._encryption_key))

self._auto_xfan = auto_xfan
self._auto_light = auto_light

if uid:
self._uid = uid
else:
Expand All @@ -201,10 +215,11 @@ def __init__(self, hass, name, ip_addr, port, mac_addr, timeout, target_temp_ste
async_track_state_change_event(
hass, lights_entity_id, self._async_lights_entity_state_changed)

if xfan_entity_id:
_LOGGER.info('Setting up xfan entity: ' + str(xfan_entity_id))
async_track_state_change_event(
hass, xfan_entity_id, self._async_xfan_entity_state_changed)
if not self._auto_xfan:
if xfan_entity_id:
_LOGGER.info('Setting up xfan entity: ' + str(xfan_entity_id))
async_track_state_change_event(
hass, xfan_entity_id, self._async_xfan_entity_state_changed)

if health_entity_id:
_LOGGER.info('Setting up health entity: ' + str(health_entity_id))
Expand All @@ -230,6 +245,11 @@ def __init__(self, hass, name, ip_addr, port, mac_addr, timeout, target_temp_ste
_LOGGER.info('Setting up air entity: ' + str(air_entity_id))
async_track_state_change_event(
hass, air_entity_id, self._async_air_entity_state_changed)

if target_temp_entity_id:
_LOGGER.info('Setting up target temp entity: ' + str(target_temp_entity_id))
async_track_state_change_event(
hass, target_temp_entity_id, self._async_target_temp_entity_state_changed)

self._unique_id = 'climate.gree_' + mac_addr.decode('utf-8').lower()

Expand Down Expand Up @@ -307,6 +327,12 @@ def UpdateHATargetTemperature(self):
_LOGGER.info('HA target temp set according to HVAC state to 8℃ since 8℃ heating mode is active')
else:
self._target_temperature = self._acOptions['SetTem']
if self._target_temp_entity_id:
target_temp_state = self.hass.states.get(self._target_temp_entity_id)
if target_temp_state:
attr = target_temp_state.attributes
if self._target_temperature in range(MIN_TEMP, MAX_TEMP+1):
self.hass.states.async_set(self._target_temp_entity_id, float(self._target_temperature), attr)
_LOGGER.info('HA target temp set according to HVAC state to: ' + str(self._acOptions['SetTem']))

def UpdateHAOptions(self):
Expand All @@ -328,19 +354,20 @@ def UpdateHAOptions(self):
self.hass.states.async_set(self._lights_entity_id, self._current_lights, attr)
_LOGGER.info('HA lights option set according to HVAC state to: ' + str(self._current_lights))
# Sync current HVAC xfan option to HA
if (self._acOptions['Blo'] == 1):
self._current_xfan = STATE_ON
elif (self._acOptions['Blo'] == 0):
self._current_xfan = STATE_OFF
else:
self._current_xfan = STATE_UNKNOWN
if self._xfan_entity_id:
xfan_state = self.hass.states.get(self._xfan_entity_id)
if xfan_state:
attr = xfan_state.attributes
if self._current_xfan in (STATE_ON, STATE_OFF):
self.hass.states.async_set(self._xfan_entity_id, self._current_xfan, attr)
_LOGGER.info('HA xfan option set according to HVAC state to: ' + str(self._current_xfan))
if not self._auto_xfan:
if (self._acOptions['Blo'] == 1):
self._current_xfan = STATE_ON
elif (self._acOptions['Blo'] == 0):
self._current_xfan = STATE_OFF
else:
self._current_xfan = STATE_UNKNOWN
if self._xfan_entity_id:
xfan_state = self.hass.states.get(self._xfan_entity_id)
if xfan_state:
attr = xfan_state.attributes
if self._current_xfan in (STATE_ON, STATE_OFF):
self.hass.states.async_set(self._xfan_entity_id, self._current_xfan, attr)
_LOGGER.info('HA xfan option set according to HVAC state to: ' + str(self._current_xfan))
# Sync current HVAC health option to HA
if (self._acOptions['Health'] == 1):
self._current_health = STATE_ON
Expand Down Expand Up @@ -484,12 +511,13 @@ async def _async_temp_sensor_changed(self, event: Event[EventStateChangedData])
entity_id = event.data["entity_id"]
old_state = event.data["old_state"]
new_state = event.data["new_state"]
_LOGGER.info('temp_sensor state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state))
s = str(old_state.state) if hasattr(old_state,'state') else "None"
_LOGGER.info('temp_sensor state changed | ' + str(entity_id) + ' from ' + s + ' to ' + str(new_state.state))
# Handle temperature changes.
if new_state is None:
return
self._async_update_current_temp(new_state)
return self.async_update_ha_state()
return self.schedule_update_ha_state(True)

@callback
def _async_update_current_temp(self, state):
Expand Down Expand Up @@ -524,7 +552,7 @@ async def _async_lights_entity_state_changed(self, event: Event[EventStateChange
# do nothing if state change is triggered due to Sync with HVAC
return
self._async_update_current_lights(new_state)
return self.async_update_ha_state()
return self.schedule_update_ha_state(True)

@callback
def _async_update_current_lights(self, state):
Expand Down Expand Up @@ -552,7 +580,7 @@ async def _async_xfan_entity_state_changed(self, event: Event[EventStateChangedD
_LOGGER.info('Cant set xfan in %s mode' % str(self._hvac_mode))
return
self._async_update_current_xfan(new_state)
return self.async_update_ha_state()
return self.schedule_update_ha_state(True)

@callback
def _async_update_current_xfan(self, state):
Expand All @@ -576,7 +604,7 @@ async def _async_health_entity_state_changed(self, event: Event[EventStateChange
# do nothing if state change is triggered due to Sync with HVAC
return
self._async_update_current_health(new_state)
return self.async_update_ha_state()
return self.schedule_update_ha_state(True)

@callback
def _async_update_current_health(self, state):
Expand Down Expand Up @@ -604,7 +632,7 @@ async def _async_powersave_entity_state_changed(self, event: Event[EventStateCha
_LOGGER.info('Cant set powersave in %s mode' % str(self._hvac_mode))
return
self._async_update_current_powersave(new_state)
return self.async_update_ha_state()
return self.schedule_update_ha_state(True)

@callback
def _async_update_current_powersave(self, state):
Expand Down Expand Up @@ -633,7 +661,7 @@ async def _async_sleep_entity_state_changed(self, event: Event[EventStateChanged
_LOGGER.info('Cant set sleep in %s mode' % str(self._hvac_mode))
return
self._async_update_current_sleep(new_state)
return self.async_update_ha_state()
return self.schedule_update_ha_state(True)

@callback
def _async_update_current_sleep(self, state):
Expand Down Expand Up @@ -661,7 +689,7 @@ async def _async_eightdegheat_entity_state_changed(self, event: Event[EventState
_LOGGER.info('Cant set 8℃ heat in %s mode' % str(self._hvac_mode))
return
self._async_update_current_eightdegheat(new_state)
return self.async_update_ha_state()
return self.schedule_update_ha_state(True)

@callback
def _async_update_current_eightdegheat(self, state):
Expand All @@ -685,7 +713,7 @@ def _async_air_entity_state_changed(self, event: Event[EventStateChangedData]) -
# do nothing if state change is triggered due to Sync with HVAC
return
self._async_update_current_air(new_state)
return self.async_update_ha_state()
return self.schedule_update_ha_state(True)

@callback
def _async_update_current_air(self, state):
Expand All @@ -698,7 +726,27 @@ def _async_update_current_air(self, state):
return
_LOGGER.error('Unable to update from air_entity!')

def _async_target_temp_entity_state_changed(self, event: Event[EventStateChangedData]) -> None:
entity_id = event.data["entity_id"]
old_state = event.data["old_state"]
new_state = event.data["new_state"]
_LOGGER.info('target_temp_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state))
if new_state is None:
return
if int(float(new_state.state)) is self._target_temperature:
# do nothing if state change is triggered due to Sync with HVAC
return
self._async_update_current_target_temp(new_state)
return self.schedule_update_ha_state(True)

@callback
def _async_update_current_target_temp(self, state):
s = int(float(state.state))
_LOGGER.info('Updating HVAC with changed target_temp_entity state | ' + str(s))
if (s >= MIN_TEMP) and (s <= MAX_TEMP):
self.SyncState({'SetTem': s})
return
_LOGGER.error('Unable to update from target_temp_entity!')

@property
def should_poll(self):
Expand Down Expand Up @@ -859,22 +907,40 @@ def set_fan_mode(self, fan):
def set_hvac_mode(self, hvac_mode):
_LOGGER.info('set_hvac_mode(): ' + str(hvac_mode))
# Set new operation mode.
c = {}
if (hvac_mode == HVACMode.OFF):
self.SyncState({'Pow': 0})
c.update({'Pow': 0})
if self._auto_light:
self._is_running = False
c.update({'Lig': 0})
else:
self.SyncState({'Mod': self._hvac_modes.index(hvac_mode), 'Pow': 1})
c.update({'Pow': 1})
c.update({'Mod': self.hvac_modes.index(hvac_mode)})
if self._auto_light:
if (self._hvac_mode == HVACMode.OFF):
c.update({'Lig': 1})
if (hvac_mode == HVACMode.COOL) or (hvac_mode == HVACMode.DRY):
if self._auto_xfan:
c.update({'Blo': 1})
self.SyncState(c)
self.schedule_update_ha_state()

def turn_on(self):
_LOGGER.info('turn_on(): ')
# Turn on.
self.SyncState({'Pow': 1})
c = {'Pow': 1}
if self._auto_light:
c.update({'Lig': 1})
self.SyncState(c)
self.schedule_update_ha_state()

def turn_off(self):
_LOGGER.info('turn_off(): ')
# Turn on.
self.SyncState({'Pow': 0})
# Turn off.
c = {'Pow': 0}
if self._auto_light:
c.update({'Lig': 0})
self.SyncState(c)
self.schedule_update_ha_state()

async def async_added_to_hass(self):
Expand Down

0 comments on commit 44ceb52

Please sign in to comment.