From debf4db961ff711634eaf48bd208e80f9159643f Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 12 Jan 2024 09:06:53 +0000 Subject: [PATCH] Quantity (#469) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Migrate to a separate type and quantity * Streamline check * Added attribute translations * Update readme * Version bump * Update device: Temperature_and_humidity_sensor_ZG_227Z by TuYa (#427) Co-authored-by: 4D4M-Github <4D4M-Github@users.noreply.github.com> * Apply automatic changes * Update device: SML004 by Signify_Netherlands_B_V (#429) Co-authored-by: KevinBowdler * Apply automatic changes * Update new_device_request.yml * Update device: ROM001 by Signify_Netherlands_B_V (#431) Co-authored-by: KevinBowdler * Apply automatic changes * Update library.json Remove duplicate * Apply automatic changes * Update device: Smart_Valve by Netatmo (#433) Co-authored-by: hawkcapl * Apply automatic changes * Update device: Smart_button_IH_K663 by TuYa (#435) Co-authored-by: stijnb1234 * Apply automatic changes * Update device: ZBT_CCTSwitch_D0001 by LDS (#438) Co-authored-by: m4ha7m4 * Apply automatic changes * Update device: 3450_L by CentraLite (#440) Co-authored-by: m4ha7m4 * Apply automatic changes * Update device: _TZ3000_qja6nq5z by TS004F (#442) Co-authored-by: m4ha7m4 * Apply automatic changes * Added Polish translation (#436) * Update translation for quantity * Update device: A001082 by LK (#446) Co-authored-by: m4ha7m4 * Apply automatic changes * Update device: BE468ZP by Allegion (#448) Co-authored-by: m4ha7m4 * Apply automatic changes * Update device: ZG8101 by Vision_Security (#450) Co-authored-by: m4ha7m4 * Apply automatic changes * Update device: ZD2105 by Vision_Security (#452) Co-authored-by: m4ha7m4 * Apply automatic changes * Update device: DMS01 by Elexa_Consumer_Products_Inc (#454) Co-authored-by: m4ha7m4 * Apply automatic changes * Update device: SPZB0001 by Eurotronic (#456) Co-authored-by: o0shojo0o * Apply automatic changes * Update device: U_BOLT_PRO_ZWAVE by Ultraloq (#458) Co-authored-by: m4ha7m4 * Apply automatic changes * Update device: TS0601_thermostat by TuYa (#460) Co-authored-by: o0shojo0o * Apply automatic changes * Update device: NAS_AB06B2 by Neo (#462) Co-authored-by: emilianogetino * Apply automatic changes * add missing battery info (#463) * Apply automatic changes * Update device: Water_sensor_3315_S by SmartThings (#466) Co-authored-by: ajaymdesai * Apply automatic changes * Update device: CP1500PFCLCDa by CPS (#468) Co-authored-by: ajaymdesai * Apply automatic changes * Lint fix --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: 4D4M-Github <4D4M-Github@users.noreply.github.com> Co-authored-by: andrew-codechimp Co-authored-by: KevinBowdler Co-authored-by: hawkcapl Co-authored-by: stijnb1234 Co-authored-by: m4ha7m4 Co-authored-by: pswid <78219494+pswid@users.noreply.github.com> Co-authored-by: o0shojo0o Co-authored-by: emilianogetino Co-authored-by: Jean-François Auger Co-authored-by: ajaymdesai --- README.md | 4 +- custom_components/battery_notes/__init__.py | 45 ++++++++++++++++++- .../battery_notes/config_flow.py | 34 +++++++++++++- custom_components/battery_notes/const.py | 1 + custom_components/battery_notes/manifest.json | 2 +- custom_components/battery_notes/sensor.py | 28 ++++++------ .../battery_notes/translations/da.json | 19 +++++--- .../battery_notes/translations/de.json | 19 +++++--- .../battery_notes/translations/en.json | 19 +++++--- .../battery_notes/translations/hu.json | 19 +++++--- .../battery_notes/translations/pl.json | 19 +++++--- .../battery_notes/translations/sk.json | 19 +++++--- 12 files changed, 171 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 38a1127ac..7b2828713 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,10 @@ Track both the battery type and also when the battery was replaced. Platform | Name | Description -- | -- | -- -`sensor` | Battery Type | Show battery type. +`sensor` | Battery Type | Show battery type with attributes for separation of type & quantity. `sensor` | Battery last replaced | Date & Time the battery was last replaced. `button` | Battery replaced | Update Battery last replaced to now. -`service` | Set battery replaced | Update Battery last replaced. +`service` | Set battery replaced | Update Battery last replaced, optionally set a date other than now. ## Installation diff --git a/custom_components/battery_notes/__init__.py b/custom_components/battery_notes/__init__.py index 3020877c1..03612ba7f 100644 --- a/custom_components/battery_notes/__init__.py +++ b/custom_components/battery_notes/__init__.py @@ -10,6 +10,7 @@ import homeassistant.helpers.config_validation as cv import voluptuous as vol +import re from awesomeversion.awesomeversion import AwesomeVersion from homeassistant.config_entries import ConfigEntry @@ -19,6 +20,8 @@ from homeassistant.helpers import device_registry as dr from homeassistant.util import dt as dt_util +from .config_flow import CONFIG_VERSION + from .discovery import DiscoveryManager from .library_updater import ( LibraryUpdater, @@ -42,7 +45,9 @@ DATA_COORDINATOR, ATTR_REMOVE, ATTR_DEVICE_ID, - ATTR_DATE_TIME_REPLACED + ATTR_DATE_TIME_REPLACED, + CONF_BATTERY_TYPE, + CONF_BATTERY_QUANTITY, ) MIN_HA_VERSION = "2023.7" @@ -137,6 +142,44 @@ async def async_remove_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> _LOGGER.debug("Removed Device %s", device_id) +async def async_migrate_entry(hass, config_entry: ConfigEntry): + """Migrate old config.""" + new_version = CONFIG_VERSION + + if config_entry.version == 1: + # Version 1 had a single config for qty & type, split them + _LOGGER.debug("Migrating config entry from version %s", config_entry.version) + + matches: re.Match = re.search( + r"^(\d+)(?=x)(?:x\s)(\w+$)|([\s\S]+)", config_entry.data[CONF_BATTERY_TYPE] + ) + if matches: + _qty = matches.group(1) if matches.group(1) is not None else "1" + _type = ( + matches.group(2) if matches.group(2) is not None else matches.group(3) + ) + else: + _qty = 1 + _type = config_entry.data[CONF_BATTERY_TYPE] + + new_data = {**config_entry.data} + new_data[CONF_BATTERY_TYPE] = _type + new_data[CONF_BATTERY_QUANTITY] = _qty + + config_entry.version = new_version + + hass.config_entries.async_update_entry( + config_entry, title=config_entry.title, data=new_data + ) + + _LOGGER.info( + "Entry %s successfully migrated to version %s.", + config_entry.entry_id, + new_version, + ) + + return True + @callback async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None: """Update options.""" diff --git a/custom_components/battery_notes/config_flow.py b/custom_components/battery_notes/config_flow.py index 879ebef62..b0ddf3576 100644 --- a/custom_components/battery_notes/config_flow.py +++ b/custom_components/battery_notes/config_flow.py @@ -29,6 +29,7 @@ from .const import ( DOMAIN, CONF_BATTERY_TYPE, + CONF_BATTERY_QUANTITY, CONF_DEVICE_NAME, CONF_MANUFACTURER, CONF_MODEL, @@ -39,6 +40,8 @@ _LOGGER = logging.getLogger(__name__) +CONFIG_VERSION = 2 + DEVICE_SCHEMA_ALL = vol.Schema( { vol.Required(CONF_DEVICE_ID): selector.DeviceSelector( @@ -76,7 +79,7 @@ class BatteryNotesFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for BatteryNotes.""" - VERSION = 1 + VERSION = CONFIG_VERSION data: dict @@ -133,6 +136,9 @@ async def async_step_user( library = Library.factory(self.hass) + # Set defaults if not found in library + self.data[CONF_BATTERY_QUANTITY] = 1 + device_battery_details = await library.get_device_battery_details( device_entry.manufacturer, device_entry.model ) @@ -146,7 +152,11 @@ async def async_step_user( ) self.data[ CONF_BATTERY_TYPE - ] = device_battery_details.battery_type_and_quantity + ] = device_battery_details.battery_type + + self.data[ + CONF_BATTERY_QUANTITY + ] = device_battery_details.battery_quantity return await self.async_step_battery() @@ -172,6 +182,7 @@ async def async_step_battery(self, user_input: dict[str, Any] | None = None): errors: dict[str, str] = {} if user_input is not None: self.data[CONF_BATTERY_TYPE] = user_input[CONF_BATTERY_TYPE] + self.data[CONF_BATTERY_QUANTITY] = int(user_input[CONF_BATTERY_QUANTITY]) device_id = self.data[CONF_DEVICE_ID] unique_id = f"bn_{device_id}" @@ -204,6 +215,16 @@ async def async_step_battery(self, user_input: dict[str, Any] | None = None): type=selector.TextSelectorType.TEXT ), ), + vol.Required( + CONF_BATTERY_QUANTITY, + default=int(self.data.get(CONF_BATTERY_QUANTITY)) + ): selector.NumberSelector( + selector.NumberSelectorConfig( + min=1, + max=100, + mode=selector.NumberSelectorMode.BOX + ), + ), } ), errors=errors, @@ -220,6 +241,7 @@ def __init__(self, config_entry: ConfigEntry) -> None: self.source_device_id: str = self.current_config.get(CONF_DEVICE_ID) # type: ignore self.name: str = self.current_config.get(CONF_NAME) self.battery_type: str = self.current_config.get(CONF_BATTERY_TYPE) + self.battery_quantity: int = self.current_config.get(CONF_BATTERY_QUANTITY) async def async_step_init( self, @@ -231,6 +253,7 @@ async def async_step_init( schema = self.build_options_schema() if user_input is not None: + user_input[CONF_BATTERY_QUANTITY] = int(user_input[CONF_BATTERY_QUANTITY]) errors = await self.save_options(user_input, schema) if not errors: return self.async_create_entry(title="", data={}) @@ -289,6 +312,13 @@ def build_options_schema(self) -> vol.Schema: vol.Required(CONF_BATTERY_TYPE): selector.TextSelector( selector.TextSelectorConfig(type=selector.TextSelectorType.TEXT), ), + vol.Required(CONF_BATTERY_QUANTITY): selector.NumberSelector( + selector.NumberSelectorConfig( + min=1, + max=100, + mode=selector.NumberSelectorMode.BOX + ), + ), } ) diff --git a/custom_components/battery_notes/const.py b/custom_components/battery_notes/const.py index 940b3c373..c3b88b586 100644 --- a/custom_components/battery_notes/const.py +++ b/custom_components/battery_notes/const.py @@ -25,6 +25,7 @@ DOMAIN_CONFIG = "config" CONF_BATTERY_TYPE = "battery_type" +CONF_BATTERY_QUANTITY = "battery_quantity" CONF_SENSORS = "sensors" CONF_ENABLE_AUTODISCOVERY = "enable_autodiscovery" CONF_USER_LIBRARY = "user_library" diff --git a/custom_components/battery_notes/manifest.json b/custom_components/battery_notes/manifest.json index ce89be050..0e8d7c8de 100644 --- a/custom_components/battery_notes/manifest.json +++ b/custom_components/battery_notes/manifest.json @@ -9,5 +9,5 @@ "integration_type": "device", "iot_class": "calculated", "issue_tracker": "https://github.com/andrew-codechimp/ha-battery-notes/issues", - "version": "1.3.5" + "version": "1.4.1" } \ No newline at end of file diff --git a/custom_components/battery_notes/sensor.py b/custom_components/battery_notes/sensor.py index cd3515342..3a0d948be 100644 --- a/custom_components/battery_notes/sensor.py +++ b/custom_components/battery_notes/sensor.py @@ -4,7 +4,6 @@ from datetime import datetime from dataclasses import dataclass import voluptuous as vol -import re from homeassistant.components.sensor import ( PLATFORM_SCHEMA, @@ -40,6 +39,7 @@ DOMAIN, PLATFORMS, CONF_BATTERY_TYPE, + CONF_BATTERY_QUANTITY, DATA_COORDINATOR, LAST_REPLACED, DOMAIN_CONFIG, @@ -70,6 +70,7 @@ class BatteryNotesSensorEntityDescription( vol.Optional(CONF_NAME): cv.string, vol.Required(CONF_DEVICE_ID): cv.string, vol.Required(CONF_BATTERY_TYPE): cv.string, + vol.Required(CONF_BATTERY_QUANTITY): cv.positive_int, } ) @@ -96,6 +97,10 @@ async def async_setup_entry( device_id = config_entry.data.get(CONF_DEVICE_ID) battery_type = config_entry.data.get(CONF_BATTERY_TYPE) + try: + battery_quantity = int(config_entry.data.get(CONF_BATTERY_QUANTITY)) + except ValueError: + battery_quantity = 1 async def async_registry_updated(event: Event) -> None: """Handle entity registry update.""" @@ -165,6 +170,7 @@ async def async_registry_updated(event: Event) -> None: device_id, f"{config_entry.entry_id}{type_sensor_entity_description.unique_id_suffix}", battery_type, + battery_quantity, ), BatteryNotesLastReplacedSensor( hass, @@ -201,6 +207,7 @@ def __init__( device_id: str, unique_id: str, battery_type: str | None = None, + battery_quantity: str | None = None, ) -> None: """Initialize the sensor.""" super().__init__() @@ -221,6 +228,7 @@ def __init__( self.entity_id = f"sensor.{device.name}_{description.key}" self._battery_type = battery_type + self._battery_quantity = battery_quantity async def async_added_to_hass(self) -> None: """Handle added to Hass.""" @@ -244,27 +252,17 @@ async def async_added_to_hass(self) -> None: def native_value(self) -> str: """Return the native value of the sensor.""" + if self._battery_quantity and int(self._battery_quantity) > 1: + return str(self._battery_quantity) + "x " + self._battery_type return self._battery_type @property def extra_state_attributes(self) -> dict[str, str] | None: """Return the state attributes of the battery type.""" - matches: re.Match = re.search( - r"^(\d+)(?=x)(?:x\s)(\w+$)|([\s\S]+)", self._battery_type - ) - if matches: - _qty = matches.group(1) if matches.group(1) is not None else "1" - _type = ( - matches.group(2) if matches.group(2) is not None else matches.group(3) - ) - else: - _qty = 1 - _type = self._battery_type - attrs = { - ATTR_BATTERY_QUANTITY: _qty, - ATTR_BATTERY_TYPE: _type, + ATTR_BATTERY_QUANTITY: self._battery_quantity, + ATTR_BATTERY_TYPE: self._battery_type, } super_attrs = super().extra_state_attributes diff --git a/custom_components/battery_notes/translations/da.json b/custom_components/battery_notes/translations/da.json index cb9791515..60267c6f1 100644 --- a/custom_components/battery_notes/translations/da.json +++ b/custom_components/battery_notes/translations/da.json @@ -13,10 +13,8 @@ }, "battery": { "data": { - "battery_type": "Batteri type" - }, - "data_description": { - "battery_type": "Tilføj eventuelt batterimængden, hvis mere end 1 f.eks. 2x AAA" + "battery_type": "Batteri type", + "battery_quantity": "Antal batterier" } } }, @@ -33,7 +31,8 @@ "description": "Hvis du har brug for hjælp til konfigurationen, så kig her: https://github.com/andrew-codechimp/ha-battery-notes", "data": { "name": "Navn", - "battery_type": "Batteri type" + "battery_type": "Batteri type", + "battery_quantity": "Antal batterier" }, "data_description": { "name": "Hvis du lader det være tomt, hentes navnet fra kildeenheden" @@ -52,7 +51,15 @@ }, "sensor": { "battery_type": { - "name": "Batteri type" + "name": "Batteri type", + "state_attributes": { + "battery_type": { + "name": "Batteri type" + }, + "battery_quantity": { + "name": "Antal batterier" + } + } }, "battery_last_replaced": { "name": "Batteri sidst skiftet" diff --git a/custom_components/battery_notes/translations/de.json b/custom_components/battery_notes/translations/de.json index c39defbef..390c317dc 100644 --- a/custom_components/battery_notes/translations/de.json +++ b/custom_components/battery_notes/translations/de.json @@ -13,10 +13,8 @@ }, "battery": { "data": { - "battery_type": "Batterieart" - }, - "data_description": { - "battery_type": "Optional: Anzahl der Batterien eingeben, wenn es mehr als eine Batterie ist, z. B. 2x AAA" + "battery_type": "Batterieart", + "battery_quantity": "Batteriemenge" } } }, @@ -33,7 +31,8 @@ "description": "Hilfe zur Konfiguration findest du unter: https://github.com/andrew-codechimp/ha-battery-notes", "data": { "name": "Name", - "battery_type": "Batterieart" + "battery_type": "Batterieart", + "battery_quantity": "Batteriemenge" }, "data_description": { "name": "Wenn du nichts eingibst, wird der Name vom Quellgerät übernommen" @@ -52,7 +51,15 @@ }, "sensor": { "battery_type": { - "name": "Batterieart" + "name": "Batterieart", + "state_attributes": { + "battery_type": { + "name": "Batterieart" + }, + "battery_quantity": { + "name": "Batteriemenge" + } + } }, "battery_last_replaced": { "name": "Batterie zuletzt ersetzt" diff --git a/custom_components/battery_notes/translations/en.json b/custom_components/battery_notes/translations/en.json index eea3d0e20..d3d719357 100644 --- a/custom_components/battery_notes/translations/en.json +++ b/custom_components/battery_notes/translations/en.json @@ -13,10 +13,8 @@ }, "battery": { "data": { - "battery_type": "Battery type" - }, - "data_description": { - "battery_type": "Optionally add the battery quantity if more than 1 eg. 2x AAA" + "battery_type": "Battery type", + "battery_quantity": "Battery quantity" } } }, @@ -33,7 +31,8 @@ "description": "If you need help with the configuration have a look here: https://github.com/andrew-codechimp/ha-battery-notes", "data": { "name": "Name", - "battery_type": "Battery type" + "battery_type": "Battery type", + "battery_quantity": "Battery quantity" }, "data_description": { "name": "Leaving blank will take the name from the source device" @@ -52,7 +51,15 @@ }, "sensor": { "battery_type": { - "name": "Battery type" + "name": "Battery type", + "state_attributes": { + "battery_type": { + "name": "Battery type" + }, + "battery_quantity": { + "name": "Battery quantity" + } + } }, "battery_last_replaced": { "name": "Battery last replaced" diff --git a/custom_components/battery_notes/translations/hu.json b/custom_components/battery_notes/translations/hu.json index d30b54029..a1c772643 100644 --- a/custom_components/battery_notes/translations/hu.json +++ b/custom_components/battery_notes/translations/hu.json @@ -13,10 +13,8 @@ }, "battery": { "data": { - "battery_type": "Elem típus" - }, - "data_description": { - "battery_type": "Opcionálisan add meg a mennyiséget, ha több, mint 1, pl. 2x AAA" + "battery_type": "Elem típus", + "battery_quantity": "Az akkumulátor mennyisége" } } }, @@ -33,7 +31,8 @@ "description": "Ha segítségre van szükséged a konfigurációhoz: https://github.com/andrew-codechimp/ha-battery-notes", "data": { "name": "Név", - "battery_type": "Elem típus" + "battery_type": "Elem típus", + "battery_quantity": "Az akkumulátor mennyisége" }, "data_description": { "name": "Üresen hagyva a forrás eszköz neve lesz használva" @@ -52,7 +51,15 @@ }, "sensor": { "battery_type": { - "name": "Elem típus" + "name": "Elem típus", + "state_attributes": { + "battery_type": { + "name": "Elem típus" + }, + "battery_quantity": { + "name": "Az akkumulátor mennyisége" + } + } }, "battery_last_replaced": { "name": "Utolsó elemcsere" diff --git a/custom_components/battery_notes/translations/pl.json b/custom_components/battery_notes/translations/pl.json index b0584c91f..e0588955e 100644 --- a/custom_components/battery_notes/translations/pl.json +++ b/custom_components/battery_notes/translations/pl.json @@ -13,10 +13,8 @@ }, "battery": { "data": { - "battery_type": "Typ baterii" - }, - "data_description": { - "battery_type": "Ocjonalnie, wprowadź też ilość baterii jeśli jest więcej niż 1 szt., np. 2x AAA" + "battery_type": "Typ baterii", + "battery_quantity": "Liczba baterii" } } }, @@ -33,7 +31,8 @@ "description": "Jeśli potrzebujesz pomocy w konfiguracji, zajrzyj tutaj: https://github.com/andrew-codechimp/ha-battery-notes", "data": { "name": "Nazwa", - "battery_type": "Typ baterii" + "battery_type": "Typ baterii", + "battery_quantity": "Liczba baterii" }, "data_description": { "name": "Pozostawienie pustego pola spowoduje pobranie nazwy z urządzenia źródłowego" @@ -52,7 +51,15 @@ }, "sensor": { "battery_type": { - "name": "Typ baterii" + "name": "Typ baterii", + "state_attributes": { + "battery_type": { + "name": "Typ baterii" + }, + "battery_quantity": { + "name": "Liczba baterii" + } + } }, "battery_last_replaced": { "name": "Ostatnia wymiana baterii" diff --git a/custom_components/battery_notes/translations/sk.json b/custom_components/battery_notes/translations/sk.json index 7674aeceb..43a75b78d 100644 --- a/custom_components/battery_notes/translations/sk.json +++ b/custom_components/battery_notes/translations/sk.json @@ -13,10 +13,8 @@ }, "battery": { "data": { - "battery_type": "Typ batérie" - }, - "data_description": { - "battery_type": "Voliteľne pridajte množstvo batérie, ak je viac ako 1 napr. 2x AAA" + "battery_type": "Typ batérie", + "battery_quantity": "Množstvo batérie" } } }, @@ -33,7 +31,8 @@ "description": "Ak potrebujete pomoc s konfiguráciou, pozrite sa sem: https://github.com/andrew-codechimp/ha-battery-notes", "data": { "name": "Názov", - "battery_type": "Typ batérie" + "battery_type": "Typ batérie", + "battery_quantity": "Množstvo batérie" }, "data_description": { "name": "Ak ponecháte prázdne, názov sa prevezme zo zdrojového zariadenia" @@ -52,7 +51,15 @@ }, "sensor": { "battery_type": { - "name": "Typ batérie" + "name": "Typ batérie", + "state_attributes": { + "battery_type": { + "name": "Typ batérie" + }, + "battery_quantity": { + "name": "Množstvo batérie" + } + } }, "battery_last_replaced": { "name": "Batéria naposledy vymenená"