From ff3b664a3f2f0d72f73d947fef54194494f396ec Mon Sep 17 00:00:00 2001 From: litinoveweedle <15144712+litinoveweedle@users.noreply.github.com> Date: Fri, 17 May 2024 21:26:57 +0200 Subject: [PATCH 1/2] python code reformat --- custom_components/smartir/__init__.py | 118 ++++--- custom_components/smartir/climate.py | 363 ++++++++++++++-------- custom_components/smartir/controller.py | 98 +++--- custom_components/smartir/fan.py | 177 ++++++----- custom_components/smartir/media_player.py | 183 ++++++----- 5 files changed, 556 insertions(+), 383 deletions(-) diff --git a/custom_components/smartir/__init__.py b/custom_components/smartir/__init__.py index 278b202b..4c26274c 100644 --- a/custom_components/smartir/__init__.py +++ b/custom_components/smartir/__init__.py @@ -11,36 +11,43 @@ import voluptuous as vol from aiohttp import ClientSession -from homeassistant.const import ( - ATTR_FRIENDLY_NAME, __version__ as current_ha_version) +from homeassistant.const import ATTR_FRIENDLY_NAME, __version__ as current_ha_version import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType _LOGGER = logging.getLogger(__name__) -DOMAIN = 'smartir' -VERSION = '1.17.9' +DOMAIN = "smartir" +VERSION = "1.17.9" MANIFEST_URL = ( "https://raw.githubusercontent.com/" "smartHomeHub/SmartIR/{}/" - "custom_components/smartir/manifest.json") + "custom_components/smartir/manifest.json" +) REMOTE_BASE_URL = ( "https://raw.githubusercontent.com/" "smartHomeHub/SmartIR/{}/" - "custom_components/smartir/") -COMPONENT_ABS_DIR = os.path.dirname( - os.path.abspath(__file__)) + "custom_components/smartir/" +) +COMPONENT_ABS_DIR = os.path.dirname(os.path.abspath(__file__)) + +CONF_CHECK_UPDATES = "check_updates" +CONF_UPDATE_BRANCH = "update_branch" + +CONFIG_SCHEMA = vol.Schema( + { + DOMAIN: vol.Schema( + { + vol.Optional(CONF_CHECK_UPDATES, default=True): cv.boolean, + vol.Optional(CONF_UPDATE_BRANCH, default="master"): vol.In( + ["master", "rc"] + ), + } + ) + }, + extra=vol.ALLOW_EXTRA, +) -CONF_CHECK_UPDATES = 'check_updates' -CONF_UPDATE_BRANCH = 'update_branch' - -CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Optional(CONF_CHECK_UPDATES, default=True): cv.boolean, - vol.Optional(CONF_UPDATE_BRANCH, default='master'): vol.In( - ['master', 'rc']) - }) -}, extra=vol.ALLOW_EXTRA) async def async_setup(hass, config): """Set up the SmartIR component.""" @@ -58,48 +65,57 @@ async def _check_updates(service): async def _update_component(service): await _update(hass, update_branch, True) - hass.services.async_register(DOMAIN, 'check_updates', _check_updates) - hass.services.async_register(DOMAIN, 'update_component', _update_component) + hass.services.async_register(DOMAIN, "check_updates", _check_updates) + hass.services.async_register(DOMAIN, "update_component", _update_component) if check_updates: await _update(hass, update_branch, False, False) return True + async def _update(hass, branch, do_update=False, notify_if_latest=True): try: async with aiohttp.ClientSession() as session: async with session.get(MANIFEST_URL.format(branch)) as response: if response.status == 200: - - data = await response.json(content_type='text/plain') - min_ha_version = data['homeassistant'] - last_version = data['updater']['version'] - release_notes = data['updater']['releaseNotes'] + + data = await response.json(content_type="text/plain") + min_ha_version = data["homeassistant"] + last_version = data["updater"]["version"] + release_notes = data["updater"]["releaseNotes"] if StrictVersion(last_version) <= StrictVersion(VERSION): if notify_if_latest: hass.components.persistent_notification.async_create( - "You're already using the latest version!", - title='SmartIR') + "You're already using the latest version!", + title="SmartIR", + ) return - if StrictVersion(current_ha_version) < StrictVersion(min_ha_version): + if StrictVersion(current_ha_version) < StrictVersion( + min_ha_version + ): hass.components.persistent_notification.async_create( "There is a new version of SmartIR integration, but it is **incompatible** " - "with your system. Please first update Home Assistant.", title='SmartIR') + "with your system. Please first update Home Assistant.", + title="SmartIR", + ) return if do_update is False: hass.components.persistent_notification.async_create( "A new version of SmartIR integration is available ({}). " "Call the ``smartir.update_component`` service to update " - "the integration. \n\n **Release notes:** \n{}" - .format(last_version, release_notes), title='SmartIR') + "the integration. \n\n **Release notes:** \n{}".format( + last_version, release_notes + ), + title="SmartIR", + ) return # Begin update - files = data['updater']['files'] + files = data["updater"]["files"] has_errors = False for file in files: @@ -110,33 +126,45 @@ async def _update(hass, branch, do_update=False, notify_if_latest=True): await Helper.downloader(source, dest) except Exception: has_errors = True - _LOGGER.error("Error updating %s. Please update the file manually.", file) + _LOGGER.error( + "Error updating %s. Please update the file manually.", + file, + ) if has_errors: hass.components.persistent_notification.async_create( "There was an error updating one or more files of SmartIR. " - "Please check the logs for more information.", title='SmartIR') + "Please check the logs for more information.", + title="SmartIR", + ) else: hass.components.persistent_notification.async_create( - "Successfully updated to {}. Please restart Home Assistant." - .format(last_version), title='SmartIR') + "Successfully updated to {}. Please restart Home Assistant.".format( + last_version + ), + title="SmartIR", + ) except Exception: - _LOGGER.error("An error occurred while checking for updates.") + _LOGGER.error("An error occurred while checking for updates.") + -class Helper(): +class Helper: @staticmethod async def downloader(source, dest): async with aiohttp.ClientSession() as session: async with session.get(source) as response: if response.status == 200: - async with aiofiles.open(dest, mode='wb') as f: + async with aiofiles.open(dest, mode="wb") as f: await f.write(await response.read()) else: raise Exception("File not found") @staticmethod def pronto2lirc(pronto): - codes = [int(binascii.hexlify(pronto[i:i+2]), 16) for i in range(0, len(pronto), 2)] + codes = [ + int(binascii.hexlify(pronto[i : i + 2]), 16) + for i in range(0, len(pronto), 2) + ] if codes[0]: raise ValueError("Pronto code should start with 0000") @@ -154,18 +182,18 @@ def lirc2broadlink(pulses): pulse = int(pulse * 269 / 8192) if pulse < 256: - array += bytearray(struct.pack('>B', pulse)) + array += bytearray(struct.pack(">B", pulse)) else: array += bytearray([0x00]) - array += bytearray(struct.pack('>H', pulse)) + array += bytearray(struct.pack(">H", pulse)) packet = bytearray([0x26, 0x00]) - packet += bytearray(struct.pack(' HVACAction | None: """Return the current running hvac operation if supported.""" @@ -294,28 +323,30 @@ def hvac_action(self) -> HVACAction | None: def extra_state_attributes(self): """Platform specific attributes.""" return { - 'last_on_operation': self._last_on_operation, - 'device_code': self._device_code, - 'manufacturer': self._manufacturer, - 'supported_models': self._supported_models, - 'supported_controller': self._supported_controller, - 'commands_encoding': self._commands_encoding + "last_on_operation": self._last_on_operation, + "device_code": self._device_code, + "manufacturer": self._manufacturer, + "supported_models": self._supported_models, + "supported_controller": self._supported_controller, + "commands_encoding": self._commands_encoding, } - + async def async_set_hvac_mode(self, hvac_mode): """Set operation mode.""" - await self.send_command(hvac_mode, self._fan_mode, self._swing_mode, self._target_temperature) + await self.send_command( + hvac_mode, self._fan_mode, self._swing_mode, self._target_temperature + ) async def async_set_temperature(self, **kwargs): """Set new target temperatures.""" - hvac_mode = kwargs.get(ATTR_HVAC_MODE) + hvac_mode = kwargs.get(ATTR_HVAC_MODE) temperature = kwargs.get(ATTR_TEMPERATURE) - + if temperature is None: return - + if temperature < self._min_temperature or temperature > self._max_temperature: - _LOGGER.warning('The temperature value is out of min/max range') + _LOGGER.warning("The temperature value is out of min/max range") return if self._precision == PRECISION_WHOLE: @@ -326,26 +357,32 @@ async def async_set_temperature(self, **kwargs): if self._hvac_mode == HVACMode.OFF: self._target_temperature = temperature else: - await self.send_command(self._hvac_mode, self._fan_mode, self._swing_mode, temperature) + await self.send_command( + self._hvac_mode, self._fan_mode, self._swing_mode, temperature + ) async def async_set_fan_mode(self, fan_mode): """Set fan mode.""" if self._hvac_mode == HVACMode.OFF: self._fan_mode = fan_mode else: - await self.send_command(self._hvac_mode, fan_mode, self._swing_mode, self._target_temperature) + await self.send_command( + self._hvac_mode, fan_mode, self._swing_mode, self._target_temperature + ) async def async_set_swing_mode(self, swing_mode): """Set swing mode.""" if self._hvac_mode == HVACMode.OFF: self._swing_mode = swing_mode else: - await self.send_command(self._hvac_mode, self._fan_mode, swing_mode, self._target_temperature) + await self.send_command( + self._hvac_mode, self._fan_mode, swing_mode, self._target_temperature + ) async def async_turn_off(self): """Turn off.""" await self.async_set_hvac_mode(HVACMode.OFF) - + async def async_turn_on(self): """Turn on.""" if self._last_on_operation is not None: @@ -356,58 +393,118 @@ async def async_turn_on(self): async def send_command(self, hvac_mode, fan_mode, swing_mode, temperature): async with self._temp_lock: try: - target_temperature = '{0:g}'.format(temperature) + target_temperature = "{0:g}".format(temperature) if hvac_mode == HVACMode.OFF: - if self._last_on_operation == HVACMode.COOL and isinstance(self._commands, dict) and 'off_cool' in self._commands.keys(): - await self._controller.send(self._commands['off_cool']) - elif self._last_on_operation == HVACMode.HEAT and isinstance(self._commands, dict) and 'off_heat' in self._commands.keys(): - await self._controller.send(self._commands['off_heat']) - elif self._last_on_operation == HVACMode.FAN_ONLY and isinstance(self._commands, dict) and 'off_fan' in self._commands.keys(): - await self._controller.send(self._commands['off_fan']) - elif isinstance(self._commands, dict) and 'off' in self._commands.keys(): - await self._controller.send(self._commands['off']) + if ( + self._last_on_operation == HVACMode.COOL + and isinstance(self._commands, dict) + and "off_cool" in self._commands.keys() + ): + await self._controller.send(self._commands["off_cool"]) + elif ( + self._last_on_operation == HVACMode.HEAT + and isinstance(self._commands, dict) + and "off_heat" in self._commands.keys() + ): + await self._controller.send(self._commands["off_heat"]) + elif ( + self._last_on_operation == HVACMode.FAN_ONLY + and isinstance(self._commands, dict) + and "off_fan" in self._commands.keys() + ): + await self._controller.send(self._commands["off_fan"]) + elif ( + isinstance(self._commands, dict) + and "off" in self._commands.keys() + ): + await self._controller.send(self._commands["off"]) else: - _LOGGER.error("Missing device IR code for any of off/off_cool/off_heat/off_fan operation mode.") + _LOGGER.error( + "Missing device IR code for any of off/off_cool/off_heat/off_fan operation mode." + ) return else: - if not(isinstance(self._commands, dict) and 'on' in self._commands.keys()): + if not ( + isinstance(self._commands, dict) + and "on" in self._commands.keys() + ): """if on code is not present, the on bit can be still set in the all operation/fan codes""" pass else: - await self._controller.send(self._commands['on']) + await self._controller.send(self._commands["on"]) await asyncio.sleep(self._delay) - if not (isinstance(self._commands, dict) and hvac_mode in self._commands.keys()): - _LOGGER.error("Missing device IR code for %s operation mode.", hvac_mode) + if not ( + isinstance(self._commands, dict) + and hvac_mode in self._commands.keys() + ): + _LOGGER.error( + "Missing device IR code for %s operation mode.", hvac_mode + ) return - elif not (isinstance(self._commands[hvac_mode], dict) and - fan_mode in self._commands[hvac_mode].keys()): - _LOGGER.error("Missing device IR code for %s fan mode.", fan_mode) + elif not ( + isinstance(self._commands[hvac_mode], dict) + and fan_mode in self._commands[hvac_mode].keys() + ): + _LOGGER.error( + "Missing device IR code for %s fan mode.", fan_mode + ) return elif self._support_swing == True: - if not (isinstance(self._commands[hvac_mode][fan_mode], dict) and - swing_mode in self._commands[hvac_mode][fan_mode].keys()): - _LOGGER.error("Missing device IR code for swing mode." + swing_mode) + if not ( + isinstance(self._commands[hvac_mode][fan_mode], dict) + and swing_mode in self._commands[hvac_mode][fan_mode].keys() + ): + _LOGGER.error( + "Missing device IR code for swing mode." + swing_mode + ) return elif hvac_mode == HVACMode.FAN_ONLY: - await self._controller.send(self._commands[hvac_mode][fan_mode][swing_mode]) - elif not (isinstance(self._commands[hvac_mode][fan_mode][swing_mode], dict) and - target_temperature in self._commands[hvac_mode][fan_mode][swing_mode].keys()): - _LOGGER.error("Missing device IR code %s target temperature.", target_temperature) + await self._controller.send( + self._commands[hvac_mode][fan_mode][swing_mode] + ) + elif not ( + isinstance( + self._commands[hvac_mode][fan_mode][swing_mode], dict + ) + and target_temperature + in self._commands[hvac_mode][fan_mode][swing_mode].keys() + ): + _LOGGER.error( + "Missing device IR code %s target temperature.", + target_temperature, + ) return else: - await self._controller.send(self._commands[hvac_mode][fan_mode][swing_mode][target_temperature]) + await self._controller.send( + self._commands[hvac_mode][fan_mode][swing_mode][ + target_temperature + ] + ) else: - if not isinstance(self._commands[hvac_mode][fan_mode], dict) and hvac_mode == HVACMode.FAN_ONLY: - await self._controller.send(self._commands[hvac_mode][fan_mode]) - elif not (isinstance(self._commands[hvac_mode][fan_mode], dict) and - target_temperature in self._commands[hvac_mode][fan_mode].keys()): - _LOGGER.error("Missing device IR code for %s target temperature.", target_temperature) + if ( + not isinstance(self._commands[hvac_mode][fan_mode], dict) + and hvac_mode == HVACMode.FAN_ONLY + ): + await self._controller.send( + self._commands[hvac_mode][fan_mode] + ) + elif not ( + isinstance(self._commands[hvac_mode][fan_mode], dict) + and target_temperature + in self._commands[hvac_mode][fan_mode].keys() + ): + _LOGGER.error( + "Missing device IR code for %s target temperature.", + target_temperature, + ) return else: - await self._controller.send(self._commands[hvac_mode][fan_mode][target_temperature]) - + await self._controller.send( + self._commands[hvac_mode][fan_mode][target_temperature] + ) + self._on_by_remote = False self._hvac_mode = hvac_mode self._fan_mode = fan_mode @@ -419,18 +516,22 @@ async def send_command(self, hvac_mode, fan_mode, swing_mode, temperature): except Exception as e: _LOGGER.exception(e) - - async def _async_temp_sensor_changed(self, event: Event[EventStateChangedData]) -> None: + + async def _async_temp_sensor_changed( + self, event: Event[EventStateChangedData] + ) -> None: """Handle temperature sensor changes.""" old_state = event.data["old_state"] new_state = event.data["new_state"] if new_state is None: return - + self._async_update_temp(new_state) await self._async_update_hvac_action() - async def _async_humidity_sensor_changed(self, event: Event[EventStateChangedData]) -> None: + async def _async_humidity_sensor_changed( + self, event: Event[EventStateChangedData] + ) -> None: """Handle humidity sensor changes.""" old_state = event.data["old_state"] new_state = event.data["new_state"] @@ -440,7 +541,9 @@ async def _async_humidity_sensor_changed(self, event: Event[EventStateChangedDat self._async_update_humidity(new_state) await self._async_update_hvac_action() - async def _async_power_sensor_changed(self, event: Event[EventStateChangedData]) -> None: + async def _async_power_sensor_changed( + self, event: Event[EventStateChangedData] + ) -> None: """Handle power sensor changes.""" old_state = event.data["old_state"] new_state = event.data["new_state"] @@ -452,7 +555,10 @@ async def _async_power_sensor_changed(self, event: Event[EventStateChangedData]) if new_state.state == STATE_ON and self._hvac_mode == HVACMode.OFF: self._on_by_remote = True - if self._power_sensor_restore_state == True and self._last_on_operation is not None: + if ( + self._power_sensor_restore_state == True + and self._last_on_operation is not None + ): self._hvac_mode = self._last_on_operation else: self._hvac_mode = STATE_ON @@ -468,17 +574,17 @@ async def _async_update_hvac_action(self): if not self._temperature_sensor: self.async_write_ha_state() return - + if self._hvac_mode == HVACMode.OFF: self._hvac_action = HVACAction.OFF elif ( - ( self._hvac_mode == HVACMode.HEAT or self._hvac_mode == HVACMode.HEAT_COOL ) + (self._hvac_mode == HVACMode.HEAT or self._hvac_mode == HVACMode.HEAT_COOL) and self._current_temperature is not None and self._current_temperature < self._target_temperature ): self._hvac_action = HVACAction.HEATING elif ( - ( self._hvac_mode == HVACMode.COOL or self._hvac_mode == HVACMode.HEAT_COOL ) + (self._hvac_mode == HVACMode.COOL or self._hvac_mode == HVACMode.HEAT_COOL) and self._current_temperature is not None and self._current_temperature > self._target_temperature ): @@ -490,7 +596,6 @@ async def _async_update_hvac_action(self): else: self._hvac_action = HVACAction.IDLE self.async_write_ha_state() - @callback def _async_update_temp(self, state): diff --git a/custom_components/smartir/controller.py b/custom_components/smartir/controller.py index 30758f62..984d9e56 100644 --- a/custom_components/smartir/controller.py +++ b/custom_components/smartir/controller.py @@ -10,16 +10,16 @@ _LOGGER = logging.getLogger(__name__) -BROADLINK_CONTROLLER = 'Broadlink' -XIAOMI_CONTROLLER = 'Xiaomi' -MQTT_CONTROLLER = 'MQTT' -LOOKIN_CONTROLLER = 'LOOKin' -ESPHOME_CONTROLLER = 'ESPHome' +BROADLINK_CONTROLLER = "Broadlink" +XIAOMI_CONTROLLER = "Xiaomi" +MQTT_CONTROLLER = "MQTT" +LOOKIN_CONTROLLER = "LOOKin" +ESPHOME_CONTROLLER = "ESPHome" -ENC_BASE64 = 'Base64' -ENC_HEX = 'Hex' -ENC_PRONTO = 'Pronto' -ENC_RAW = 'Raw' +ENC_BASE64 = "Base64" +ENC_HEX = "Hex" +ENC_PRONTO = "Pronto" +ENC_RAW = "Raw" BROADLINK_COMMANDS_ENCODING = [ENC_BASE64, ENC_HEX, ENC_PRONTO] XIAOMI_COMMANDS_ENCODING = [ENC_PRONTO, ENC_RAW] @@ -35,16 +35,19 @@ def get_controller(hass, controller, encoding, controller_data, delay): XIAOMI_CONTROLLER: XiaomiController, MQTT_CONTROLLER: MQTTController, LOOKIN_CONTROLLER: LookinController, - ESPHOME_CONTROLLER: ESPHomeController + ESPHOME_CONTROLLER: ESPHomeController, } try: - return controllers[controller](hass, controller, encoding, controller_data, delay) + return controllers[controller]( + hass, controller, encoding, controller_data, delay + ) except KeyError: raise Exception("The controller is not supported.") class AbstractController(ABC): """Representation of a controller.""" + def __init__(self, hass, controller, encoding, controller_data, delay): self.check_encoding(encoding) self.hass = hass @@ -70,46 +73,46 @@ class BroadlinkController(AbstractController): def check_encoding(self, encoding): """Check if the encoding is supported by the controller.""" if encoding not in BROADLINK_COMMANDS_ENCODING: - raise Exception("The encoding is not supported " - "by the Broadlink controller.") + raise Exception( + "The encoding is not supported " "by the Broadlink controller." + ) async def send(self, command): """Send a command.""" commands = [] - if not isinstance(command, list): + if not isinstance(command, list): command = [command] for _command in command: if self._encoding == ENC_HEX: try: _command = binascii.unhexlify(_command) - _command = b64encode(_command).decode('utf-8') + _command = b64encode(_command).decode("utf-8") except: - raise Exception("Error while converting " - "Hex to Base64 encoding") + raise Exception("Error while converting " "Hex to Base64 encoding") if self._encoding == ENC_PRONTO: try: - _command = _command.replace(' ', '') + _command = _command.replace(" ", "") _command = bytearray.fromhex(_command) _command = Helper.pronto2lirc(_command) _command = Helper.lirc2broadlink(_command) - _command = b64encode(_command).decode('utf-8') + _command = b64encode(_command).decode("utf-8") except: - raise Exception("Error while converting " - "Pronto to Base64 encoding") + raise Exception( + "Error while converting " "Pronto to Base64 encoding" + ) - commands.append('b64:' + _command) + commands.append("b64:" + _command) service_data = { ATTR_ENTITY_ID: self._controller_data, - 'command': commands, - 'delay_secs': self._delay + "command": commands, + "delay_secs": self._delay, } - await self.hass.services.async_call( - 'remote', 'send_command', service_data) + await self.hass.services.async_call("remote", "send_command", service_data) class XiaomiController(AbstractController): @@ -118,18 +121,18 @@ class XiaomiController(AbstractController): def check_encoding(self, encoding): """Check if the encoding is supported by the controller.""" if encoding not in XIAOMI_COMMANDS_ENCODING: - raise Exception("The encoding is not supported " - "by the Xiaomi controller.") + raise Exception( + "The encoding is not supported " "by the Xiaomi controller." + ) async def send(self, command): """Send a command.""" service_data = { ATTR_ENTITY_ID: self._controller_data, - 'command': self._encoding.lower() + ':' + command + "command": self._encoding.lower() + ":" + command, } - await self.hass.services.async_call( - 'remote', 'send_command', service_data) + await self.hass.services.async_call("remote", "send_command", service_data) class MQTTController(AbstractController): @@ -138,18 +141,13 @@ class MQTTController(AbstractController): def check_encoding(self, encoding): """Check if the encoding is supported by the controller.""" if encoding not in MQTT_COMMANDS_ENCODING: - raise Exception("The encoding is not supported " - "by the mqtt controller.") + raise Exception("The encoding is not supported " "by the mqtt controller.") async def send(self, command): """Send a command.""" - service_data = { - 'topic': self._controller_data, - 'payload': command - } + service_data = {"topic": self._controller_data, "payload": command} - await self.hass.services.async_call( - 'mqtt', 'publish', service_data) + await self.hass.services.async_call("mqtt", "publish", service_data) class LookinController(AbstractController): @@ -158,14 +156,14 @@ class LookinController(AbstractController): def check_encoding(self, encoding): """Check if the encoding is supported by the controller.""" if encoding not in LOOKIN_COMMANDS_ENCODING: - raise Exception("The encoding is not supported " - "by the LOOKin controller.") + raise Exception( + "The encoding is not supported " "by the LOOKin controller." + ) async def send(self, command): """Send a command.""" - encoding = self._encoding.lower().replace('pronto', 'prontohex') - url = f"http://{self._controller_data}/commands/ir/" \ - f"{encoding}/{command}" + encoding = self._encoding.lower().replace("pronto", "prontohex") + url = f"http://{self._controller_data}/commands/ir/" f"{encoding}/{command}" await self.hass.async_add_executor_job(requests.get, url) @@ -175,12 +173,14 @@ class ESPHomeController(AbstractController): def check_encoding(self, encoding): """Check if the encoding is supported by the controller.""" if encoding not in ESPHOME_COMMANDS_ENCODING: - raise Exception("The encoding is not supported " - "by the ESPHome controller.") - + raise Exception( + "The encoding is not supported " "by the ESPHome controller." + ) + async def send(self, command): """Send a command.""" - service_data = {'command': json.loads(command)} + service_data = {"command": json.loads(command)} await self.hass.services.async_call( - 'esphome', self._controller_data, service_data) \ No newline at end of file + "esphome", self._controller_data, service_data + ) diff --git a/custom_components/smartir/fan.py b/custom_components/smartir/fan.py index df386ace..be16f740 100644 --- a/custom_components/smartir/fan.py +++ b/custom_components/smartir/fan.py @@ -6,19 +6,23 @@ import voluptuous as vol from homeassistant.components.fan import ( - FanEntity, PLATFORM_SCHEMA, - DIRECTION_REVERSE, DIRECTION_FORWARD, - SUPPORT_SET_SPEED, SUPPORT_DIRECTION, SUPPORT_OSCILLATE, - ATTR_OSCILLATING) -from homeassistant.const import ( - CONF_NAME, STATE_OFF, STATE_ON, STATE_UNKNOWN) + FanEntity, + PLATFORM_SCHEMA, + DIRECTION_REVERSE, + DIRECTION_FORWARD, + SUPPORT_SET_SPEED, + SUPPORT_DIRECTION, + SUPPORT_OSCILLATE, + ATTR_OSCILLATING, +) +from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON, STATE_UNKNOWN from homeassistant.core import callback from homeassistant.helpers.event import async_track_state_change import homeassistant.helpers.config_validation as cv from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.util.percentage import ( ordered_list_item_to_percentage, - percentage_to_ordered_list_item + percentage_to_ordered_list_item, ) from . import COMPONENT_ABS_DIR, Helper from .controller import get_controller @@ -28,50 +32,59 @@ DEFAULT_NAME = "SmartIR Fan" DEFAULT_DELAY = 0.5 -CONF_UNIQUE_ID = 'unique_id' -CONF_DEVICE_CODE = 'device_code' +CONF_UNIQUE_ID = "unique_id" +CONF_DEVICE_CODE = "device_code" CONF_CONTROLLER_DATA = "controller_data" CONF_DELAY = "delay" -CONF_POWER_SENSOR = 'power_sensor' +CONF_POWER_SENSOR = "power_sensor" SPEED_OFF = "off" -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Required(CONF_DEVICE_CODE): cv.positive_int, - vol.Required(CONF_CONTROLLER_DATA): cv.string, - vol.Optional(CONF_DELAY, default=DEFAULT_DELAY): cv.string, - vol.Optional(CONF_POWER_SENSOR): cv.entity_id -}) +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Optional(CONF_UNIQUE_ID): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Required(CONF_DEVICE_CODE): cv.positive_int, + vol.Required(CONF_CONTROLLER_DATA): cv.string, + vol.Optional(CONF_DELAY, default=DEFAULT_DELAY): cv.string, + vol.Optional(CONF_POWER_SENSOR): cv.entity_id, + } +) + async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the IR Fan platform.""" device_code = config.get(CONF_DEVICE_CODE) - device_files_subdir = os.path.join('codes', 'fan') + device_files_subdir = os.path.join("codes", "fan") device_files_absdir = os.path.join(COMPONENT_ABS_DIR, device_files_subdir) if not os.path.isdir(device_files_absdir): os.makedirs(device_files_absdir) - device_json_filename = str(device_code) + '.json' + device_json_filename = str(device_code) + ".json" device_json_path = os.path.join(device_files_absdir, device_json_filename) if not os.path.exists(device_json_path): - _LOGGER.warning("Couldn't find the device Json file. The component will " \ - "try to download it from the GitHub repo.") + _LOGGER.warning( + "Couldn't find the device Json file. The component will " + "try to download it from the GitHub repo." + ) try: - codes_source = ("https://raw.githubusercontent.com/" - "smartHomeHub/SmartIR/master/" - "codes/fan/{}.json") + codes_source = ( + "https://raw.githubusercontent.com/" + "smartHomeHub/SmartIR/master/" + "codes/fan/{}.json" + ) await Helper.downloader(codes_source.format(device_code), device_json_path) except Exception: - _LOGGER.error("There was an error while downloading the device Json file. " \ - "Please check your internet connection or if the device code " \ - "exists on GitHub. If the problem still exists please " \ - "place the file manually in the proper directory.") + _LOGGER.error( + "There was an error while downloading the device Json file. " + "Please check your internet connection or if the device code " + "exists on GitHub. If the problem still exists please " + "place the file manually in the proper directory." + ) return with open(device_json_path) as j: @@ -81,9 +94,8 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= _LOGGER.error("The device JSON file is invalid") return - async_add_entities([SmartIRFan( - hass, config, device_data - )]) + async_add_entities([SmartIRFan(hass, config, device_data)]) + class SmartIRFan(FanEntity, RestoreEntity): def __init__(self, hass, config, device_data): @@ -95,63 +107,63 @@ def __init__(self, hass, config, device_data): self._delay = config.get(CONF_DELAY) self._power_sensor = config.get(CONF_POWER_SENSOR) - self._manufacturer = device_data['manufacturer'] - self._supported_models = device_data['supportedModels'] - self._supported_controller = device_data['supportedController'] - self._commands_encoding = device_data['commandsEncoding'] - self._speed_list = device_data['speed'] - self._commands = device_data['commands'] - + self._manufacturer = device_data["manufacturer"] + self._supported_models = device_data["supportedModels"] + self._supported_controller = device_data["supportedController"] + self._commands_encoding = device_data["commandsEncoding"] + self._speed_list = device_data["speed"] + self._commands = device_data["commands"] + self._speed = SPEED_OFF self._direction = None self._last_on_speed = None self._oscillating = None self._support_flags = SUPPORT_SET_SPEED - if (DIRECTION_REVERSE in self._commands and \ - DIRECTION_FORWARD in self._commands): + if DIRECTION_REVERSE in self._commands and DIRECTION_FORWARD in self._commands: self._direction = DIRECTION_REVERSE - self._support_flags = ( - self._support_flags | SUPPORT_DIRECTION) - if ('oscillate' in self._commands): + self._support_flags = self._support_flags | SUPPORT_DIRECTION + if "oscillate" in self._commands: self._oscillating = False - self._support_flags = ( - self._support_flags | SUPPORT_OSCILLATE) - + self._support_flags = self._support_flags | SUPPORT_OSCILLATE self._temp_lock = asyncio.Lock() self._on_by_remote = False - #Init the IR/RF controller + # Init the IR/RF controller self._controller = get_controller( self.hass, - self._supported_controller, + self._supported_controller, self._commands_encoding, self._controller_data, - self._delay) + self._delay, + ) async def async_added_to_hass(self): """Run when entity about to be added.""" await super().async_added_to_hass() - + last_state = await self.async_get_last_state() if last_state is not None: - if 'speed' in last_state.attributes: - self._speed = last_state.attributes['speed'] + if "speed" in last_state.attributes: + self._speed = last_state.attributes["speed"] - #If _direction has a value the direction controls appears - #in UI even if SUPPORT_DIRECTION is not provided in the flags - if ('direction' in last_state.attributes and \ - self._support_flags & SUPPORT_DIRECTION): - self._direction = last_state.attributes['direction'] + # If _direction has a value the direction controls appears + # in UI even if SUPPORT_DIRECTION is not provided in the flags + if ( + "direction" in last_state.attributes + and self._support_flags & SUPPORT_DIRECTION + ): + self._direction = last_state.attributes["direction"] - if 'last_on_speed' in last_state.attributes: - self._last_on_speed = last_state.attributes['last_on_speed'] + if "last_on_speed" in last_state.attributes: + self._last_on_speed = last_state.attributes["last_on_speed"] if self._power_sensor: - async_track_state_change(self.hass, self._power_sensor, - self._async_power_sensor_changed) + async_track_state_change( + self.hass, self._power_sensor, self._async_power_sensor_changed + ) @property def unique_id(self): @@ -166,15 +178,14 @@ def name(self): @property def state(self): """Return the current state.""" - if (self._on_by_remote or \ - self._speed != SPEED_OFF): + if self._on_by_remote or self._speed != SPEED_OFF: return STATE_ON return SPEED_OFF @property def percentage(self): """Return speed percentage of the fan.""" - if (self._speed == SPEED_OFF): + if self._speed == SPEED_OFF: return 0 return ordered_list_item_to_percentage(self._speed_list, self._speed) @@ -208,21 +219,20 @@ def supported_features(self): def extra_state_attributes(self): """Platform specific attributes.""" return { - 'last_on_speed': self._last_on_speed, - 'device_code': self._device_code, - 'manufacturer': self._manufacturer, - 'supported_models': self._supported_models, - 'supported_controller': self._supported_controller, - 'commands_encoding': self._commands_encoding, + "last_on_speed": self._last_on_speed, + "device_code": self._device_code, + "manufacturer": self._manufacturer, + "supported_models": self._supported_models, + "supported_controller": self._supported_controller, + "commands_encoding": self._commands_encoding, } async def async_set_percentage(self, percentage: int): """Set the desired speed for the fan.""" - if (percentage == 0): - self._speed = SPEED_OFF + if percentage == 0: + self._speed = SPEED_OFF else: - self._speed = percentage_to_ordered_list_item( - self._speed_list, percentage) + self._speed = percentage_to_ordered_list_item(self._speed_list, percentage) if not self._speed == SPEED_OFF: self._last_on_speed = self._speed @@ -246,11 +256,14 @@ async def async_set_direction(self, direction: str): self.async_write_ha_state() - async def async_turn_on(self, percentage: int = None, preset_mode: str = None, **kwargs): + async def async_turn_on( + self, percentage: int = None, preset_mode: str = None, **kwargs + ): """Turn on the fan.""" if percentage is None: percentage = ordered_list_item_to_percentage( - self._speed_list, self._last_on_speed or self._speed_list[0]) + self._speed_list, self._last_on_speed or self._speed_list[0] + ) await self.async_set_percentage(percentage) @@ -262,15 +275,15 @@ async def send_command(self): async with self._temp_lock: self._on_by_remote = False speed = self._speed - direction = self._direction or 'default' + direction = self._direction or "default" oscillating = self._oscillating if speed.lower() == SPEED_OFF: - command = self._commands['off'] + command = self._commands["off"] elif oscillating: - command = self._commands['oscillate'] + command = self._commands["oscillate"] else: - command = self._commands[direction][speed] + command = self._commands[direction][speed] try: await self._controller.send(command) @@ -294,4 +307,4 @@ async def _async_power_sensor_changed(self, entity_id, old_state, new_state): self._on_by_remote = False if self._speed != SPEED_OFF: self._speed = SPEED_OFF - self.async_write_ha_state() \ No newline at end of file + self.async_write_ha_state() diff --git a/custom_components/smartir/media_player.py b/custom_components/smartir/media_player.py index 278a8402..29cd2aa9 100644 --- a/custom_components/smartir/media_player.py +++ b/custom_components/smartir/media_player.py @@ -5,14 +5,19 @@ import voluptuous as vol -from homeassistant.components.media_player import ( - MediaPlayerEntity, PLATFORM_SCHEMA) +from homeassistant.components.media_player import MediaPlayerEntity, PLATFORM_SCHEMA from homeassistant.components.media_player.const import ( - SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_PREVIOUS_TRACK, - SUPPORT_NEXT_TRACK, SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_MUTE, - SUPPORT_PLAY_MEDIA, SUPPORT_SELECT_SOURCE, MEDIA_TYPE_CHANNEL) -from homeassistant.const import ( - CONF_NAME, STATE_OFF, STATE_ON, STATE_UNKNOWN) + SUPPORT_TURN_OFF, + SUPPORT_TURN_ON, + SUPPORT_PREVIOUS_TRACK, + SUPPORT_NEXT_TRACK, + SUPPORT_VOLUME_STEP, + SUPPORT_VOLUME_MUTE, + SUPPORT_PLAY_MEDIA, + SUPPORT_SELECT_SOURCE, + MEDIA_TYPE_CHANNEL, +) +from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON, STATE_UNKNOWN import homeassistant.helpers.config_validation as cv from homeassistant.helpers.restore_state import RestoreEntity from . import COMPONENT_ABS_DIR, Helper @@ -24,52 +29,61 @@ DEFAULT_DEVICE_CLASS = "tv" DEFAULT_DELAY = 0.5 -CONF_UNIQUE_ID = 'unique_id' -CONF_DEVICE_CODE = 'device_code' +CONF_UNIQUE_ID = "unique_id" +CONF_DEVICE_CODE = "device_code" CONF_CONTROLLER_DATA = "controller_data" CONF_DELAY = "delay" -CONF_POWER_SENSOR = 'power_sensor' -CONF_SOURCE_NAMES = 'source_names' -CONF_DEVICE_CLASS = 'device_class' - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Required(CONF_DEVICE_CODE): cv.positive_int, - vol.Required(CONF_CONTROLLER_DATA): cv.string, - vol.Optional(CONF_DELAY, default=DEFAULT_DELAY): cv.string, - vol.Optional(CONF_POWER_SENSOR): cv.entity_id, - vol.Optional(CONF_SOURCE_NAMES): dict, - vol.Optional(CONF_DEVICE_CLASS, default=DEFAULT_DEVICE_CLASS): cv.string -}) +CONF_POWER_SENSOR = "power_sensor" +CONF_SOURCE_NAMES = "source_names" +CONF_DEVICE_CLASS = "device_class" + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Optional(CONF_UNIQUE_ID): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Required(CONF_DEVICE_CODE): cv.positive_int, + vol.Required(CONF_CONTROLLER_DATA): cv.string, + vol.Optional(CONF_DELAY, default=DEFAULT_DELAY): cv.string, + vol.Optional(CONF_POWER_SENSOR): cv.entity_id, + vol.Optional(CONF_SOURCE_NAMES): dict, + vol.Optional(CONF_DEVICE_CLASS, default=DEFAULT_DEVICE_CLASS): cv.string, + } +) + async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the IR Media Player platform.""" device_code = config.get(CONF_DEVICE_CODE) - device_files_subdir = os.path.join('codes', 'media_player') + device_files_subdir = os.path.join("codes", "media_player") device_files_absdir = os.path.join(COMPONENT_ABS_DIR, device_files_subdir) if not os.path.isdir(device_files_absdir): os.makedirs(device_files_absdir) - device_json_filename = str(device_code) + '.json' + device_json_filename = str(device_code) + ".json" device_json_path = os.path.join(device_files_absdir, device_json_filename) if not os.path.exists(device_json_path): - _LOGGER.warning("Couldn't find the device Json file. The component will " \ - "try to download it from the GitHub repo.") + _LOGGER.warning( + "Couldn't find the device Json file. The component will " + "try to download it from the GitHub repo." + ) try: - codes_source = ("https://raw.githubusercontent.com/" - "smartHomeHub/SmartIR/master/" - "codes/media_player/{}.json") + codes_source = ( + "https://raw.githubusercontent.com/" + "smartHomeHub/SmartIR/master/" + "codes/media_player/{}.json" + ) await Helper.downloader(codes_source.format(device_code), device_json_path) except Exception: - _LOGGER.error("There was an error while downloading the device Json file. " \ - "Please check your internet connection or if the device code " \ - "exists on GitHub. If the problem still exists please " \ - "place the file manually in the proper directory.") + _LOGGER.error( + "There was an error while downloading the device Json file. " + "Please check your internet connection or if the device code " + "exists on GitHub. If the problem still exists please " + "place the file manually in the proper directory." + ) return with open(device_json_path) as j: @@ -79,9 +93,8 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= _LOGGER.error("The device JSON file is invalid") return - async_add_entities([SmartIRMediaPlayer( - hass, config, device_data - )]) + async_add_entities([SmartIRMediaPlayer(hass, config, device_data)]) + class SmartIRMediaPlayer(MediaPlayerEntity, RestoreEntity): def __init__(self, hass, config, device_data): @@ -93,11 +106,11 @@ def __init__(self, hass, config, device_data): self._delay = config.get(CONF_DELAY) self._power_sensor = config.get(CONF_POWER_SENSOR) - self._manufacturer = device_data['manufacturer'] - self._supported_models = device_data['supportedModels'] - self._supported_controller = device_data['supportedController'] - self._commands_encoding = device_data['commandsEncoding'] - self._commands = device_data['commands'] + self._manufacturer = device_data["manufacturer"] + self._supported_models = device_data["supportedModels"] + self._supported_controller = device_data["supportedController"] + self._commands_encoding = device_data["commandsEncoding"] + self._commands = device_data["commands"] self._state = STATE_OFF self._sources_list = [] @@ -106,49 +119,61 @@ def __init__(self, hass, config, device_data): self._device_class = config.get(CONF_DEVICE_CLASS) - #Supported features - if 'off' in self._commands and self._commands['off'] is not None: + # Supported features + if "off" in self._commands and self._commands["off"] is not None: self._support_flags = self._support_flags | SUPPORT_TURN_OFF - if 'on' in self._commands and self._commands['on'] is not None: + if "on" in self._commands and self._commands["on"] is not None: self._support_flags = self._support_flags | SUPPORT_TURN_ON - if 'previousChannel' in self._commands and self._commands['previousChannel'] is not None: + if ( + "previousChannel" in self._commands + and self._commands["previousChannel"] is not None + ): self._support_flags = self._support_flags | SUPPORT_PREVIOUS_TRACK - if 'nextChannel' in self._commands and self._commands['nextChannel'] is not None: + if ( + "nextChannel" in self._commands + and self._commands["nextChannel"] is not None + ): self._support_flags = self._support_flags | SUPPORT_NEXT_TRACK - if ('volumeDown' in self._commands and self._commands['volumeDown'] is not None) \ - or ('volumeUp' in self._commands and self._commands['volumeUp'] is not None): + if ( + "volumeDown" in self._commands and self._commands["volumeDown"] is not None + ) or ("volumeUp" in self._commands and self._commands["volumeUp"] is not None): self._support_flags = self._support_flags | SUPPORT_VOLUME_STEP - if 'mute' in self._commands and self._commands['mute'] is not None: + if "mute" in self._commands and self._commands["mute"] is not None: self._support_flags = self._support_flags | SUPPORT_VOLUME_MUTE - if 'sources' in self._commands and self._commands['sources'] is not None: - self._support_flags = self._support_flags | SUPPORT_SELECT_SOURCE | SUPPORT_PLAY_MEDIA + if "sources" in self._commands and self._commands["sources"] is not None: + self._support_flags = ( + self._support_flags | SUPPORT_SELECT_SOURCE | SUPPORT_PLAY_MEDIA + ) for source, new_name in config.get(CONF_SOURCE_NAMES, {}).items(): - if source in self._commands['sources']: + if source in self._commands["sources"]: if new_name is not None: - self._commands['sources'][new_name] = self._commands['sources'][source] + self._commands["sources"][new_name] = self._commands["sources"][ + source + ] - del self._commands['sources'][source] + del self._commands["sources"][source] - #Sources list - for key in self._commands['sources']: + # Sources list + for key in self._commands["sources"]: self._sources_list.append(key) self._temp_lock = asyncio.Lock() - #Init the IR/RF controller + # Init the IR/RF controller self._controller = get_controller( self.hass, - self._supported_controller, + self._supported_controller, self._commands_encoding, self._controller_data, - self._delay) + self._delay, + ) async def async_added_to_hass(self): """Run when entity about to be added.""" @@ -197,7 +222,7 @@ def media_content_type(self): @property def source_list(self): return self._sources_list - + @property def source(self): return self._source @@ -211,17 +236,17 @@ def supported_features(self): def extra_state_attributes(self): """Platform specific attributes.""" return { - 'device_code': self._device_code, - 'manufacturer': self._manufacturer, - 'supported_models': self._supported_models, - 'supported_controller': self._supported_controller, - 'commands_encoding': self._commands_encoding, + "device_code": self._device_code, + "manufacturer": self._manufacturer, + "supported_models": self._supported_models, + "supported_controller": self._supported_controller, + "commands_encoding": self._commands_encoding, } async def async_turn_off(self): """Turn the media player off.""" - await self.send_command(self._commands['off']) - + await self.send_command(self._commands["off"]) + if self._power_sensor is None: self._state = STATE_OFF self._source = None @@ -229,7 +254,7 @@ async def async_turn_off(self): async def async_turn_on(self): """Turn the media player off.""" - await self.send_command(self._commands['on']) + await self.send_command(self._commands["on"]) if self._power_sensor is None: self._state = STATE_ON @@ -237,33 +262,33 @@ async def async_turn_on(self): async def async_media_previous_track(self): """Send previous track command.""" - await self.send_command(self._commands['previousChannel']) + await self.send_command(self._commands["previousChannel"]) self.async_write_ha_state() async def async_media_next_track(self): """Send next track command.""" - await self.send_command(self._commands['nextChannel']) + await self.send_command(self._commands["nextChannel"]) self.async_write_ha_state() async def async_volume_down(self): """Turn volume down for media player.""" - await self.send_command(self._commands['volumeDown']) + await self.send_command(self._commands["volumeDown"]) self.async_write_ha_state() async def async_volume_up(self): """Turn volume up for media player.""" - await self.send_command(self._commands['volumeUp']) + await self.send_command(self._commands["volumeUp"]) self.async_write_ha_state() - + async def async_mute_volume(self, mute): """Mute the volume.""" - await self.send_command(self._commands['mute']) + await self.send_command(self._commands["mute"]) self.async_write_ha_state() async def async_select_source(self, source): """Select channel from source.""" self._source = source - await self.send_command(self._commands['sources'][source]) + await self.send_command(self._commands["sources"][source]) self.async_write_ha_state() async def async_play_media(self, media_type, media_id, **kwargs): @@ -280,7 +305,9 @@ async def async_play_media(self, media_type, media_id, **kwargs): self._source = "Channel {}".format(media_id) for digit in media_id: - await self.send_command(self._commands['sources']["Channel {}".format(digit)]) + await self.send_command( + self._commands["sources"]["Channel {}".format(digit)] + ) self.async_write_ha_state() async def send_command(self, command): @@ -289,7 +316,7 @@ async def send_command(self, command): await self._controller.send(command) except Exception as e: _LOGGER.exception(e) - + async def async_update(self): if self._power_sensor is None: return From d83922ca0ee05bb6442f4210395964923450448d Mon Sep 17 00:00:00 2001 From: litinoveweedle <15144712+litinoveweedle@users.noreply.github.com> Date: Fri, 17 May 2024 21:53:40 +0200 Subject: [PATCH 2/2] fixed unused hvac_mode in set_temperature --- custom_components/smartir/climate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/smartir/climate.py b/custom_components/smartir/climate.py index cbf988fe..fd795131 100644 --- a/custom_components/smartir/climate.py +++ b/custom_components/smartir/climate.py @@ -339,7 +339,7 @@ async def async_set_hvac_mode(self, hvac_mode): async def async_set_temperature(self, **kwargs): """Set new target temperatures.""" - hvac_mode = kwargs.get(ATTR_HVAC_MODE) + hvac_mode = kwargs.get(ATTR_HVAC_MODE, self._hvac_mode) temperature = kwargs.get(ATTR_TEMPERATURE) if temperature is None: @@ -358,7 +358,7 @@ async def async_set_temperature(self, **kwargs): self._target_temperature = temperature else: await self.send_command( - self._hvac_mode, self._fan_mode, self._swing_mode, temperature + hvac_mode, self._fan_mode, self._swing_mode, temperature ) async def async_set_fan_mode(self, fan_mode):