From 7fa436511ecc80a72487686af60a163fa6281a3d Mon Sep 17 00:00:00 2001 From: umu <46300268+xZetsubou@users.noreply.github.com> Date: Sun, 13 Aug 2023 14:45:52 +0300 Subject: [PATCH] Reformat - Bump HA to 2023.05 - Bump Python worksflow 3.10 - Reformat all codes --- .github/workflows/tox.yaml | 2 +- custom_components/localtuya/__init__.py | 17 +++-- custom_components/localtuya/button.py | 11 +++- custom_components/localtuya/climate.py | 4 +- custom_components/localtuya/config_flow.py | 75 ++++++++++++++++------ custom_components/localtuya/const.py | 13 ++-- custom_components/localtuya/cover.py | 61 ++++++++++-------- custom_components/localtuya/light.py | 15 +++-- requirements_test.txt | 2 +- 9 files changed, 128 insertions(+), 72 deletions(-) diff --git a/.github/workflows/tox.yaml b/.github/workflows/tox.yaml index a7910f4ce..082994cc0 100644 --- a/.github/workflows/tox.yaml +++ b/.github/workflows/tox.yaml @@ -16,7 +16,7 @@ jobs: platform: - ubuntu-latest python-version: - - 3.9 + - '3.10' steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index 0332f52ca..0db95b6bf 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -220,21 +220,20 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry): hass.config_entries.async_update_entry(stored_entries[0], data=new_data) await hass.config_entries.async_remove(config_entry.entry_id) if config_entry.version == 2: - """ Switch config flow to selectors convert DP IDs from int to str `require HA 2022.4 ` """ + # Switch config flow to selectors convert DP IDs from int to str require HA 2022.4. _LOGGER.debug("Migrating config entry from version %s", config_entry.version) if config_entry.entry_id == stored_entries[0].entry_id: new_data = stored_entries[0].data.copy() for device in new_data[CONF_DEVICES]: i = 0 - if new_data[CONF_DEVICES][device][CONF_ENTITIES] is not None: - for _ent in new_data[CONF_DEVICES][device][CONF_ENTITIES]: - ent_items = {} - for k, v in _ent.items(): - ent_items[k] = str(v) if type(v) == type(1) else v - new_data[CONF_DEVICES][device][CONF_ENTITIES][i].update(ent_items) - i = i + 1 + for _ent in new_data[CONF_DEVICES][device][CONF_ENTITIES]: + ent_items = {} + for k, v in _ent.items(): + ent_items[k] = str(v) if isinstance(v, int) else v + new_data[CONF_DEVICES][device][CONF_ENTITIES][i].update(ent_items) + i = i + 1 config_entry.version = new_version - hass.config_entries.async_update_entry(config_entry, data=new_data) + hass.config_entries.async_update_entry(config_entry, data=new_data) _LOGGER.info( "Entry %s successfully migrated to version %s.", diff --git a/custom_components/localtuya/button.py b/custom_components/localtuya/button.py index 3f855f317..00f90b39c 100644 --- a/custom_components/localtuya/button.py +++ b/custom_components/localtuya/button.py @@ -23,14 +23,21 @@ def flow_schema(dps): class LocaltuyaButton(LocalTuyaEntity, ButtonEntity): """Representation of a Tuya button.""" - def __init__(self,device,config_entry,buttonid,**kwargs,): + def __init__( + self, + device, + config_entry, + buttonid, + **kwargs, + ): """Initialize the Tuya button.""" super().__init__(device, config_entry, buttonid, _LOGGER, **kwargs) self._state = None _LOGGER.debug("Initialized button [%s]", self.name) async def async_press(self): - """Press the button""" + """Press the button.""" await self._device.set_dp(True, self._dp_id) + async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaButton, flow_schema) diff --git a/custom_components/localtuya/climate.py b/custom_components/localtuya/climate.py index 6a80b1e10..029d74519 100644 --- a/custom_components/localtuya/climate.py +++ b/custom_components/localtuya/climate.py @@ -133,7 +133,9 @@ def flow_schema(dps): vol.Optional(CONF_HVAC_MODE_DP): _col_to_select(dps, is_dps=True), vol.Optional(CONF_HVAC_MODE_SET): _col_to_select(list(HVAC_MODE_SETS.keys())), vol.Optional(CONF_HVAC_ACTION_DP): _col_to_select(dps, is_dps=True), - vol.Optional(CONF_HVAC_ACTION_SET): _col_to_select(list(HVAC_ACTION_SETS.keys())), + vol.Optional(CONF_HVAC_ACTION_SET): _col_to_select( + list(HVAC_ACTION_SETS.keys()) + ), vol.Optional(CONF_ECO_DP): _col_to_select(dps, is_dps=True), vol.Optional(CONF_ECO_VALUE): str, vol.Optional(CONF_PRESET_DP): _col_to_select(dps, is_dps=True), diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index af2faa6c0..5b9f88daf 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -6,10 +6,16 @@ import homeassistant.helpers.config_validation as cv -import homeassistant.helpers.entity_registry as er + +# import homeassistant.helpers.entity_registry as er # Disabled it because no need to delete registry. import voluptuous as vol from homeassistant import config_entries, core, exceptions -from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig, SelectSelectorMode, SelectOptionDict +from homeassistant.helpers.selector import ( + SelectSelector, + SelectSelectorConfig, + SelectSelectorMode, + SelectOptionDict, +) from homeassistant.const import ( CONF_CLIENT_ID, CONF_CLIENT_SECRET, @@ -59,20 +65,33 @@ _LOGGER = logging.getLogger(__name__) -def _col_to_select(opt_list: dict, multi_select = False, is_dps = False): + +def _col_to_select(opt_list: dict, multi_select=False, is_dps=False): """Convert collections to SelectSelectorConfig.""" if type(opt_list) == dict: - return SelectSelector(SelectSelectorConfig( - options=[SelectOptionDict(value=str(k), label=l) for l, k in opt_list.items()], - mode=SelectSelectorMode.DROPDOWN, - )) + return SelectSelector( + SelectSelectorConfig( + options=[ + SelectOptionDict(value=str(v), label=k) for k, v in opt_list.items() + ], + mode=SelectSelectorMode.DROPDOWN, + ) + ) elif type(opt_list) == list: - # value used the same method as func available_dps_string, no spaces values. - return SelectSelector(SelectSelectorConfig( - options=[SelectOptionDict(value=str(l).split(' ')[0] if is_dps == True else str(l) - , label=str(l)) for l in opt_list], - mode=SelectSelectorMode.DROPDOWN, multiple = True if multi_select == True else False, - )) + # value used the same method as func available_dps_string, no spaces values. + return SelectSelector( + SelectSelectorConfig( + options=[ + SelectOptionDict( + value=str(kv).split(" ")[0] if is_dps else str(kv), + label=str(kv), + ) + for kv in opt_list + ], + mode=SelectSelectorMode.DROPDOWN, + multiple=True if multi_select else False, + ) + ) ENTRIES_VERSION = 3 @@ -81,7 +100,7 @@ def _col_to_select(opt_list: dict, multi_select = False, is_dps = False): NO_ADDITIONAL_ENTITIES = "no_additional_entities" SELECTED_DEVICE = "selected_device" -CUSTOM_DEVICE = {"Add custom device" : "..."} +CUSTOM_DEVICE = {"Add custom device": "..."} CONF_ACTIONS = { CONF_ADD_DEVICE: "Add a new device", @@ -97,7 +116,9 @@ def _col_to_select(opt_list: dict, multi_select = False, is_dps = False): CLOUD_SETUP_SCHEMA = vol.Schema( { - vol.Required(CONF_REGION, default="eu"): _col_to_select(["eu", "us", "cn", "in"]), + vol.Required(CONF_REGION, default="eu"): _col_to_select( + ["eu", "us", "cn", "in"] + ), vol.Optional(CONF_CLIENT_ID): cv.string, vol.Optional(CONF_CLIENT_SECRET): cv.string, vol.Optional(CONF_USER_ID): cv.string, @@ -127,6 +148,7 @@ def _col_to_select(opt_list: dict, multi_select = False, is_dps = False): {vol.Required(PLATFORM_TO_ADD, default="switch"): _col_to_select(PLATFORMS)} ) + def devices_schema(discovered_devices, cloud_devices_list, add_custom_device=True): """Create schema for devices step.""" devices = {} @@ -145,7 +167,13 @@ def devices_schema(discovered_devices, cloud_devices_list, add_custom_device=Tru # for ent in entries # } # ) - return vol.Schema({vol.Required(SELECTED_DEVICE, default=list(devices.values())[0]): _col_to_select(devices)}) + return vol.Schema( + { + vol.Required( + SELECTED_DEVICE, default=list(devices.values())[0] + ): _col_to_select(devices) + } + ) def options_schema(entities): @@ -167,7 +195,8 @@ def options_schema(entities): vol.Optional(CONF_RESET_DPIDS): cv.string, vol.Required( CONF_ENTITIES, description={"suggested_value": entity_names} - ): cv.multi_select(entity_names),# _col_to_select(entity_names, multi_select=True) + ): cv.multi_select(entity_names), + # _col_to_select(entity_names, multi_select=True) vol.Required(CONF_ENABLE_ADD_ENTITIES, default=False): bool, } ) @@ -219,20 +248,24 @@ def platform_schema(platform, dps_strings, allow_id=True, yaml=False): if allow_id: schema[vol.Required(CONF_ID)] = _col_to_select(dps_strings, is_dps=True) schema[vol.Required(CONF_FRIENDLY_NAME)] = str - schema[vol.Required(CONF_CATEGORY_ENTITY, default=str(default_category(platform)))] = _col_to_select(ENTITY_CATEGORY) + schema[ + vol.Required(CONF_CATEGORY_ENTITY, default=str(default_category(platform))) + ] = _col_to_select(ENTITY_CATEGORY) return vol.Schema(schema).extend(flow_schema(platform, dps_strings)) + def default_category(_platform): - """Auto Select default category depends on the platform""" + """Auto Select default category depends on the platform.""" if any(_platform in i for i in DEFAULT_CATEGORIES["CONTROL"]): return str(None) elif any(_platform in i for i in DEFAULT_CATEGORIES["CONFIG"]): return EntityCategory.CONFIG elif any(_platform in i for i in DEFAULT_CATEGORIES["DIAGNOSTIC"]): - return EntityCategory.DIAGNOSTIC + return EntityCategory.DIAGNOSTIC else: return str(None) + def flow_schema(platform, dps_strings): """Return flow schema for a specific platform.""" integration_module = ".".join(__name__.split(".")[:-1]) @@ -791,7 +824,7 @@ async def async_step_configure_entity(self, user_input=None): # finished editing device. Let's store the new config entry.... dev_id = self.device_data[CONF_DEVICE_ID] new_data = self.config_entry.data.copy() - entry_id = self.config_entry.entry_id + # entry_id = self.config_entry.entry_id # removing entities from registry (they will be recreated) # ent_reg = er.async_get(self.hass) # reg_entities = { diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 5ec0577db..2619162d5 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -20,7 +20,6 @@ "switch", "vacuum", ] - TUYA_DEVICES = "tuya_devices" ATTR_CURRENT = "current" @@ -142,14 +141,14 @@ # Categories ENTITY_CATEGORY = { - "Controls" : None, - "Configuration" : EntityCategory.CONFIG, - "Diagnostic" : EntityCategory.DIAGNOSTIC + "Controls": None, + "Configuration": EntityCategory.CONFIG, + "Diagnostic": EntityCategory.DIAGNOSTIC, } # Default Categories DEFAULT_CATEGORIES = { - "CONTROL" : ['switch', 'climate','fan','vacuum','light'], - "CONFIG" : ['select','number','button'], - "DIAGNOSTIC" : ['sensor','binary_sensor'] + "CONTROL": ["switch", "climate", "fan", "vacuum", "light"], + "CONFIG": ["select", "number", "button"], + "DIAGNOSTIC": ["sensor", "binary_sensor"], } diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 0b95e09ed..de3ab081d 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -12,7 +12,7 @@ CoverEntity, ) from .config_flow import _col_to_select -from .common import LocalTuyaEntity, async_setup_entry +from .common import LocalTuyaEntity, async_setup_entry from .const import ( CONF_COMMANDS_SET, CONF_CURRENT_POSITION_DP, @@ -35,31 +35,35 @@ COVER_COMMANDS = { - "ON, OFF and Stop" : "on_off_stop", - 'Open, Close and Stop' : "open_close_stop", - 'fz,zz and Stop' : "fz_zz_stop", - '1,2 and 3' : "1_2_3", - '0,1 and 2' : '0_1_2', + "ON, OFF and Stop": "on_off_stop", + "Open, Close and Stop": "open_close_stop", + "fz,zz and Stop": "fz_zz_stop", + "1,2 and 3": "1_2_3", + "0,1 and 2": "0_1_2", } COVER_MODES = { - "None": 'none', - "Position": 'position', - "Timed": 'timed', + "None": "none", + "Position": "position", + "Timed": "timed", } COVER_TIMEOUT_TOLERANCE = 3.0 -DEFAULT_COMMANDS_SET = list(COVER_COMMANDS.values())[0] -DEFAULT_POSITIONING_MODE = list(COVER_MODES.values())[0] +DEF_CMD_SET = list(COVER_COMMANDS.values())[0] +DEF_POS_MODE = list(COVER_MODES.values())[0] DEFAULT_SPAN_TIME = 25.0 def flow_schema(dps): """Return schema used in config flow.""" return { - vol.Optional(CONF_COMMANDS_SET, default=DEFAULT_COMMANDS_SET): _col_to_select(COVER_COMMANDS), - vol.Optional(CONF_POSITIONING_MODE, default=DEFAULT_POSITIONING_MODE): _col_to_select(COVER_MODES), + vol.Optional(CONF_COMMANDS_SET, default=DEF_CMD_SET): _col_to_select( + COVER_COMMANDS + ), + vol.Optional(CONF_POSITIONING_MODE, default=DEF_POS_MODE): _col_to_select( + COVER_MODES + ), vol.Optional(CONF_CURRENT_POSITION_DP): _col_to_select(dps, is_dps=True), vol.Optional(CONF_SET_POSITION_DP): _col_to_select(dps, is_dps=True), vol.Optional(CONF_POSITION_INVERTED, default=False): bool, @@ -75,7 +79,7 @@ class LocaltuyaCover(LocalTuyaEntity, CoverEntity): def __init__(self, device, config_entry, switchid, **kwargs): """Initialize a new LocaltuyaCover.""" super().__init__(device, config_entry, switchid, _LOGGER, **kwargs) - commands_set = DEFAULT_COMMANDS_SET + commands_set = DEF_CMD_SET if self.has_config(CONF_COMMANDS_SET): commands_set = self._config[CONF_COMMANDS_SET] self._open_cmd = commands_set.split("_")[0] @@ -85,15 +89,16 @@ def __init__(self, device, config_entry, switchid, **kwargs): self._state = self._stop_cmd self._previous_state = self._state self._current_cover_position = 0 - - self._current_state_action = STATE_STOPPED # Default. + self._current_state_action = STATE_STOPPED # Default. self._set_new_position = int | None _LOGGER.debug("Initialized cover [%s]", self.name) @property def supported_features(self): """Flag supported features.""" - supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP + supported_features = ( + CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP + ) if self._config[CONF_POSITIONING_MODE] != COVER_MODES["None"]: supported_features = supported_features | CoverEntityFeature.SET_POSITION return supported_features @@ -104,17 +109,21 @@ def _current_state(self) -> str: state = self._current_state_action curr_pos = self.current_cover_position # Reset STATE when cover is fully closed or fully opened. - if (state == STATE_CLOSING and curr_pos == 0) or (state == STATE_OPENING and curr_pos == 100): + if (state == STATE_CLOSING and curr_pos == 0) or ( + state == STATE_OPENING and curr_pos == 100 + ): self._current_state_action = STATE_STOPPED # in case cover moving by set position cmd. - if (self._current_state_action == STATE_SET_CLOSING - or self._current_state_action == STATE_SET_OPENING): + if ( + self._current_state_action == STATE_SET_CLOSING + or self._current_state_action == STATE_SET_OPENING + ): set_pos = self._set_new_position # Reset state whenn cover reached the position. - if curr_pos - set_pos < 5 and curr_pos - set_pos >= -5 : - self._current_state_action = STATE_STOPPED + if curr_pos - set_pos < 5 and curr_pos - set_pos >= -5: + self._current_state_action = STATE_STOPPED return self._current_state_action - + @property def current_cover_position(self): """Return current cover position in percent.""" @@ -175,7 +184,7 @@ async def async_set_cover_position(self, **kwargs): ) # Give it a moment, to make sure hass updated current pos. await asyncio.sleep(0.1) - self.update_state(STATE_SET_CMD,converted_position) + self.update_state(STATE_SET_CMD, converted_position) async def async_stop_after_timeout(self, delay_sec): """Stop the cover if timeout (max movement span) occurred.""" @@ -196,7 +205,6 @@ async def async_open_cover(self, **kwargs): ) self.update_state(STATE_OPENING) - async def async_close_cover(self, **kwargs): """Close cover.""" self.debug("Launching command %s to cover ", self._close_cmd) @@ -271,7 +279,7 @@ def status_updated(self): self._last_state = self._state def update_state(self, action, position=None): - """ update cover current states """ + """Update cover current states.""" state = self._current_state_action # using Commands. if position is None: @@ -292,4 +300,5 @@ def update_state(self, action, position=None): # Write state data. self.async_write_ha_state() + async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaCover, flow_schema) diff --git a/custom_components/localtuya/light.py b/custom_components/localtuya/light.py index 7c019a795..8aa409d32 100644 --- a/custom_components/localtuya/light.py +++ b/custom_components/localtuya/light.py @@ -172,10 +172,17 @@ def __init__( self._effect_list = [] self._scenes = None if self.has_config(CONF_SCENE): - if (self.has_config(CONF_SCENE_VALUES) and - self.has_config(CONF_SCENE_VALUES_FRIENDLY)): - values_list = [value.strip() for value in self._config.get(CONF_SCENE_VALUES).split(";")] - friendly_values_list = [value.strip() for value in self._config.get(CONF_SCENE_VALUES_FRIENDLY).split(";")] + if self.has_config(CONF_SCENE_VALUES) and self.has_config( + CONF_SCENE_VALUES_FRIENDLY + ): + values_list = [ + value.strip() + for value in self._config.get(CONF_SCENE_VALUES).split(";") + ] + friendly_values_list = [ + value.strip() + for value in self._config.get(CONF_SCENE_VALUES_FRIENDLY).split(";") + ] self._scenes = dict(zip(friendly_values_list, values_list)) elif self._config.get(CONF_SCENE) < 20: self._scenes = SCENE_LIST_RGBW_255 diff --git a/requirements_test.txt b/requirements_test.txt index 8cfbf7c59..d67e17e88 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -5,4 +5,4 @@ mypy==0.901 pydocstyle==6.1.1 pylint==2.8.2 pylint-strict-informational==0.1 -homeassistant==2021.12.10 +homeassistant==2023.05.0