From 55b914a5c686337ae47352e8eba623829fceab0b Mon Sep 17 00:00:00 2001 From: Rob Hofmann Date: Tue, 23 Jul 2024 22:10:23 +0200 Subject: [PATCH] Some fixes for None types, cleanup and extra checks (#198) * Update climate.py * Fixes, checks & removal of old stuff --- custom_components/gree/climate.py | 133 +++++++++++++++++++----------- 1 file changed, 85 insertions(+), 48 deletions(-) diff --git a/custom_components/gree/climate.py b/custom_components/gree/climate.py index 68b9a05..6bf7b56 100644 --- a/custom_components/gree/climate.py +++ b/custom_components/gree/climate.py @@ -1,15 +1,9 @@ #!/usr/bin/python # Do basic imports -import importlib.util import socket import base64 -import re -import sys -import asyncio import logging -import binascii -import os.path import voluptuous as vol import homeassistant.helpers.config_validation as cv @@ -23,24 +17,18 @@ from homeassistant.const import ( ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT, - CONF_CUSTOMIZE, CONF_HOST, CONF_MAC, CONF_NAME, CONF_PORT, CONF_TIMEOUT, - PRECISION_TENTHS, - PRECISION_WHOLE, STATE_OFF, STATE_ON, - STATE_UNKNOWN, - UnitOfTemperature + STATE_UNKNOWN ) from homeassistant.core import Event, EventStateChangedData, callback from homeassistant.helpers.event import async_track_state_change_event -from homeassistant.helpers.restore_state import RestoreEntity -from configparser import ConfigParser from Crypto.Cipher import AES try: import simplejson except ImportError: import json as simplejson @@ -186,7 +174,7 @@ def __init__(self, hass, name, ip_addr, port, mac_addr, timeout, target_temp_ste self._unit_of_measurement = '°C' self._hvac_modes = hvac_modes - self._hvac_mode = None + self._hvac_mode = HVACMode.OFF self._fan_modes = fan_modes self._fan_mode = None self._swing_modes = swing_modes @@ -292,33 +280,33 @@ def __init__(self, hass, name, ip_addr, port, mac_addr, timeout, target_temp_ste if light_sensor_entity_id: _LOGGER.info('Setting up light sensor entity: ' + str(light_sensor_entity_id)) - if self.hass.states.get(light_sensor_entity_id).state is STATE_ON: + if self.hass.states.get(light_sensor_entity_id) is not None and self.hass.states.get(light_sensor_entity_id).state is STATE_ON: self._enable_light_sensor = True - elif self.hass.states.get(light_sensor_entity_id).state is STATE_OFF: + else: self._enable_light_sensor = False async_track_state_change_event(hass, light_sensor_entity_id, self._async_light_sensor_entity_state_changed) else: - self._enable_light_sensor = None + self._enable_light_sensor = False if auto_light_entity_id: _LOGGER.info('Setting up auto light entity: ' + str(auto_light_entity_id)) - if self.hass.states.get(auto_light_entity_id).state is STATE_ON: + if self.hass.states.get(auto_light_entity_id) is not None and self.hass.states.get(auto_light_entity_id).state is STATE_ON: self._auto_light = True - elif self.hass.states.get(auto_light_entity_id).state is STATE_OFF: + else: self._auto_light = False async_track_state_change_event(hass, auto_light_entity_id, self._async_auto_light_entity_state_changed) else: - self._auto_light = None + self._auto_light = False if auto_xfan_entity_id: _LOGGER.info('Setting up auto xfan entity: ' + str(auto_xfan_entity_id)) - if self.hass.states.get(auto_xfan_entity_id).state is STATE_ON: + if self.hass.states.get(auto_xfan_entity_id) is not None and self.hass.states.get(auto_xfan_entity_id).state is STATE_ON: self._auto_xfan = True - elif self.hass.states.get(auto_xfan_entity_id).state is STATE_OFF: + else: self._auto_xfan = False async_track_state_change_event(hass, auto_xfan_entity_id, self._async_auto_xfan_entity_state_changed) else: - self._auto_xfan = None + self._auto_xfan = False # Pad helper method to help us get the right string for encrypting def Pad(self, s): @@ -330,6 +318,7 @@ def FetchResult(self, cipher, ip_addr, port, timeout, json): clientSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) clientSock.settimeout(timeout) clientSock.sendto(bytes(json, "utf-8"), (ip_addr, port)) + _LOGGER.info('3') data, addr = clientSock.recvfrom(64000) receivedJson = simplejson.loads(data) clientSock.close() @@ -754,9 +743,12 @@ async def _async_lights_entity_state_changed(self, event: Event[EventStateChange entity_id = event.data["entity_id"] old_state = event.data["old_state"] new_state = event.data["new_state"] - _LOGGER.info('lights_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('lights_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('lights_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return if new_state.state is self._current_lights: # do nothing if state change is triggered due to Sync with HVAC return @@ -778,9 +770,12 @@ async def _async_xfan_entity_state_changed(self, event: Event[EventStateChangedD entity_id = event.data["entity_id"] old_state = event.data["old_state"] new_state = event.data["new_state"] - _LOGGER.info('xfan_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('xfan_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('xfan_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return if new_state.state is self._current_xfan: # do nothing if state change is triggered due to Sync with HVAC return @@ -806,9 +801,12 @@ async def _async_health_entity_state_changed(self, event: Event[EventStateChange entity_id = event.data["entity_id"] old_state = event.data["old_state"] new_state = event.data["new_state"] - _LOGGER.info('health_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('health_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('health_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return if new_state.state is self._current_health: # do nothing if state change is triggered due to Sync with HVAC return @@ -830,12 +828,21 @@ async def _async_powersave_entity_state_changed(self, event: Event[EventStateCha entity_id = event.data["entity_id"] old_state = event.data["old_state"] new_state = event.data["new_state"] - _LOGGER.info('powersave_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('powersave_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('powersave_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return if new_state.state is self._current_powersave: # do nothing if state change is triggered due to Sync with HVAC return + if not hasattr(self, "_hvac_mode"): + _LOGGER.info('Cant set powersave in unknown mode') + return + if self._hvac_mode is None: + _LOGGER.info('Cant set powersave in unknown HVAC mode (self._hvac_mode is None)') + return if not self._hvac_mode in (HVACMode.COOL): # do nothing if not in cool mode _LOGGER.info('Cant set powersave in %s mode' % str(self._hvac_mode)) @@ -859,9 +866,12 @@ async def _async_sleep_entity_state_changed(self, event: Event[EventStateChanged entity_id = event.data["entity_id"] old_state = event.data["old_state"] new_state = event.data["new_state"] - _LOGGER.info('sleep_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('sleep_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('sleep_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return if new_state.state is self._current_sleep: # do nothing if state change is triggered due to Sync with HVAC return @@ -887,9 +897,12 @@ async def _async_eightdegheat_entity_state_changed(self, event: Event[EventState entity_id = event.data["entity_id"] old_state = event.data["old_state"] new_state = event.data["new_state"] - _LOGGER.info('eightdegheat_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('eightdegheat_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('eightdegheat_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return if new_state.state is self._current_eightdegheat: # do nothing if state change is triggered due to Sync with HVAC return @@ -915,9 +928,12 @@ def _async_air_entity_state_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('air_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('air_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('air_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return if new_state.state is self._current_air: # do nothing if state change is triggered due to Sync with HVAC return @@ -940,9 +956,12 @@ def _async_anti_direct_blow_entity_state_changed(self, event: Event[EventStateCh entity_id = event.data["entity_id"] old_state = event.data["old_state"] new_state = event.data["new_state"] - _LOGGER.info('anti_direct_blow_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('anti_direct_blow_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('anti_direct_blow_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return if new_state.state is self._current_anti_direct_blow: # do nothing if state change is triggered due to Sync with HVAC return @@ -964,9 +983,12 @@ def _async_light_sensor_entity_state_changed(self, event: Event[EventStateChange entity_id = event.data["entity_id"] old_state = event.data["old_state"] new_state = event.data["new_state"] - _LOGGER.info('light_sensor_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('light_sensor_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('light_sensor_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return if new_state.state is self._enable_light_sensor: # do nothing if state change is triggered due to Sync with HVAC return @@ -990,9 +1012,15 @@ def _async_auto_light_entity_state_changed(self, event: Event[EventStateChangedD entity_id = event.data["entity_id"] old_state = event.data["old_state"] new_state = event.data["new_state"] - _LOGGER.info('auto_light_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('auto_light_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('auto_light_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return + if not hasattr(self, "_auto_light"): + _LOGGER.info('auto_light_entity state changed | auto_light not (yet) initialized. Skipping.') + return if new_state.state is self._auto_light: # do nothing if state change is triggered due to Sync with HVAC return @@ -1018,9 +1046,15 @@ def _async_auto_xfan_entity_state_changed(self, event: Event[EventStateChangedDa entity_id = event.data["entity_id"] old_state = event.data["old_state"] new_state = event.data["new_state"] - _LOGGER.info('auto_xfan_entity state changed | ' + str(entity_id) + ' from ' + str(old_state.state) + ' to ' + str(new_state.state)) + _LOGGER.info('auto_xfan_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('auto_xfan_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return + if not hasattr(self, "_auto_xfan"): + _LOGGER.info('auto_xfan_entity state changed | auto_xfan not (yet) initialized. Skipping.') + return if new_state.state is self._auto_xfan: # do nothing if state change is triggered due to Sync with HVAC return @@ -1043,9 +1077,12 @@ def _async_target_temp_entity_state_changed(self, event: Event[EventStateChanged 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)) + _LOGGER.info('target_temp_entity state changed | ' + str(entity_id) + ' from ' + (str(old_state.state) if hasattr(old_state,'state') else "None") + ' to ' + str(new_state.state)) if new_state is None: return + if new_state.state is "off" and (old_state is None or old_state.state is None): + _LOGGER.info('target_temp_entity state changed to off, but old state is None. Ignoring to avoid beeps.') + return if int(float(new_state.state)) is self._target_temperature: # do nothing if state change is triggered due to Sync with HVAC return @@ -1155,7 +1192,7 @@ def swing_modes(self): @property def preset_mode(self): - if self._horizontal_swing: + if hasattr(self, "_horizontal_swing") and self._horizontal_swing: _LOGGER.info('preset_mode(): ' + str(self._preset_mode)) # get the current preset mode return self._preset_mode @@ -1188,8 +1225,8 @@ def fan_modes(self): @property def supported_features(self): - if self._horizontal_swing: - sf = SUPPORT_FLAGS | ClimateEntityFeature.PRESET_MODE + if hasattr(self, "_horizontal_swing") and self._horizontal_swing: + sf = SUPPORT_FLAGS | ClimateEntityFeature.PRESET_MODE else: sf = SUPPORT_FLAGS _LOGGER.info('supported_features(): ' + str(sf)) @@ -1249,17 +1286,17 @@ def set_hvac_mode(self, hvac_mode): c = {} if (hvac_mode == HVACMode.OFF): c.update({'Pow': 0}) - if self._auto_light: + if hasattr(self, "_auto_light") and self._auto_light: c.update({'Lig': 0}) - if self._has_light_sensor and self._enable_light_sensor: + if hasattr(self, "_has_light_sensor") and self._has_light_sensor and hasattr(self, "_enable_light_sensor") and self._enable_light_sensor: c.update({'LigSen': 1}) else: c.update({'Pow': 1, 'Mod': self.hvac_modes.index(hvac_mode)}) - if self._auto_light: + if hasattr(self, "_auto_light") and self._auto_light: c.update({'Lig': 1}) - if self._has_light_sensor and self._enable_light_sensor: + if hasattr(self, "_has_light_sensor") and self._has_light_sensor and hasattr(self, "_enable_light_sensor") and self._enable_light_sensor: c.update({'LigSen': 0}) - if self._auto_xfan: + if hasattr(self, "_auto_xfan") and self._auto_xfan: if (hvac_mode == HVACMode.COOL) or (hvac_mode == HVACMode.DRY): c.update({'Blo': 1}) self.SyncState(c) @@ -1269,9 +1306,9 @@ def turn_on(self): _LOGGER.info('turn_on(): ') # Turn on. c = {'Pow': 1} - if self._auto_light: + if hasattr(self, "_auto_light") and self._auto_light: c.update({'Lig': 1}) - if self._has_light_sensor and self._enable_light_sensor: + if hasattr(self, "_has_light_sensor") and self._has_light_sensor and hasattr(self, "_enable_light_sensor") and self._enable_light_sensor: c.update({'LigSen': 0}) self.SyncState(c) self.schedule_update_ha_state() @@ -1280,10 +1317,10 @@ def turn_off(self): _LOGGER.info('turn_off(): ') # Turn off. c = {'Pow': 0} - if self._auto_light: + if hasattr(self, "_auto_light") and self._auto_light: c.update({'Lig': 0}) - if self._has_light_sensor and self._enable_light_sensor: - c.update({'LigSen': 1}) + if hasattr(self, "_has_light_sensor") and self._has_light_sensor and hasattr(self, "_enable_light_sensor") and self._enable_light_sensor: + c.update({'LigSen': 1}) self.SyncState(c) self.schedule_update_ha_state()