diff --git a/README.md b/README.md index 4c0835e..8e98121 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ - Sensor `Bibliotheek.be` should become available with the number of items lent out. - sensor.bibliotheek_be_``_`` will be created for each user linked to the account - sensor.bibliotheek_be_bib_`` will be created for each library + - sensor.bibliotheek_be_warning will indicate if within how many days some items have to be returned at *any* library (this can be used of conditions, notifications, etc). ## Status Still some optimisations are planned, see [Issues](https://github.com/myTselection/bibliotheek_be/issues) section in GitHub. @@ -157,3 +158,46 @@ content: >- {% endfor %} title: Gebruikers ``` + +### Example with conditional check for warnings: + +## Extra binary sensor based on personal perference on number of days to limit the warning +Example provided with sensor that will turn on if items have to be returned within 7 days. The alert sensor will be turned on if items have to be returned within 7 days and no extension is possible. +`configuration.yaml`: +``` +binary_sensor: + - platform: template + sensors: + bibliotheek_warning_7d: + friendly_name: Bibliotheek Warning 7d + value_template: > + {{states('sensor.bibliotheek_be_warning')|int <= 7}} + - platform: template + sensors: + bibliotheek_alert_7d: + friendly_name: Bibliotheek Alert 7d + value_template: > + {{states('sensor.bibliotheek_be_warning')|int <= 7 and state_attr('sensor.bibliotheek_be_warning','some_not_extendable') == True}} +``` +Base on these sensors, a automation can be build for notifications or below conditional card can be defined: + +``` +- type: conditional +conditions: + - entity: binary_sensor.bibliotheek_warning_7d + state: 'on' + - entity: binary_sensor.bibliotheek_alert_7d + state: 'off' +card: + type: markdown + content: ⏰Boeken verlengen deze week ! +- type: conditional +conditions: + - entity: binary_sensor.bibliotheek_warning_7d + state: 'on' + - entity: binary_sensor.bibliotheek_alert_7d + state: 'on' +card: + type: markdown + content: ⏰Boeken binnen brengen deze week ! +``` \ No newline at end of file diff --git a/custom_components/bibliotheek_be/manifest.json b/custom_components/bibliotheek_be/manifest.json index 3dfd659..75421dd 100644 --- a/custom_components/bibliotheek_be/manifest.json +++ b/custom_components/bibliotheek_be/manifest.json @@ -7,7 +7,7 @@ "iot_class": "cloud_polling", "name": "Bibliotheek.be", "requirements": ["bs4", "html5lib"], - "version": "0.4.0", + "version": "0.5.0", "integration_type": "hub", "issue_tracker": "https://github.com/myTselection/bibliotheek_be/issues" } diff --git a/custom_components/bibliotheek_be/sensor.py b/custom_components/bibliotheek_be/sensor.py index 9941e9a..20adc6a 100644 --- a/custom_components/bibliotheek_be/sensor.py +++ b/custom_components/bibliotheek_be/sensor.py @@ -70,6 +70,9 @@ async def dry_setup(hass, config_entry, async_add_devices): _LOGGER.debug(f"{NAME} Init sensor for date {libraryName}") sensors.append(sensorDate) + sensorLibrariesWarning = ComponentLibrariesWarningSensor(componentData, hass) + sensors.append(sensorLibrariesWarning) + async_add_devices(sensors) #TODO: sensor per library (total items loand from library), attribute: number of each type lended @@ -265,6 +268,7 @@ def __init__(self, data, hass, libraryName, loanTypes): self._last_update = None self._lowest_till_date = None self._days_left = None + self._some_not_extendable = False self._loandetails = [] self._num_loans = 0 self._num_total_loans = 0 @@ -283,6 +287,7 @@ async def async_update(self): self._loantypes= {key: 0 for key in self._loantypes} self._num_loans = 0 self._num_total_loans = 0 + self._some_not_extendable = False for user_id, loan_data in self._data._loandetails.items(): _LOGGER.debug(f"library loop {user_id} {self._libraryName}") @@ -298,9 +303,13 @@ async def async_update(self): self._days_left = loan_item.get('days_remaining') self._lowest_till_date = loan_item.get('loan_till') self._num_loans = 1 + if loan_item.get('extend_loan_id') == '': + self._some_not_extendable = True if self._days_left == loan_item.get('days_remaining'): _LOGGER.debug(f"library_name_loop same days {library_name_loop} {loan_item}") self._num_loans += 1 + if loan_item.get('extend_loan_id') == '': + self._some_not_extendable = True async def async_will_remove_from_hass(self): @@ -333,6 +342,7 @@ def extra_state_attributes(self) -> dict: "last update": self._last_update, "libraryName": self._libraryName, "days_left": self._days_left, + "some_not_extendable": self._some_not_extendable, "lowest_till_date": self._lowest_till_date, "num_loans": self._num_loans, "num_total_loans": self._num_total_loans, @@ -341,7 +351,115 @@ def extra_state_attributes(self) -> dict: } attributes.update(self._loantypes) return attributes - _num_total_loans + + @property + def device_info(self) -> dict: + """I can't remember why this was needed :D""" + return { + "identifiers": {(DOMAIN, self.unique_id)}, + "name": self.name, + "manufacturer": DOMAIN, + } + + @property + def unit(self) -> int: + """Unit""" + return int + + @property + def unit_of_measurement(self) -> str: + """Return the unit of measurement this sensor expresses itself in.""" + return "days" + + @property + def friendly_name(self) -> str: + return self.name + + +class ComponentLibrariesWarningSensor(Entity): + def __init__(self, data, hass): + self._data = data + self._hass = hass + self._last_update = None + self._lowest_till_date = None + self._days_left = None + self._some_not_extendable = False + self._num_loans = 0 + self._num_total_loans = 0 + self._library_name = "" + + @property + def state(self): + """Return the state of the sensor.""" + return self._days_left + + async def async_update(self): + await self._data.update() + self._last_update = self._data._lastupdate; + self._num_loans = 0 + self._num_total_loans = 0 + self._some_not_extendable = False + self._library_name = "" + + for user_id, loan_data in self._data._loandetails.items(): + _LOGGER.debug(f"library warning loop {user_id}") + for loan_id, loan_item in loan_data.items(): + library_name_loop = loan_item.get('library') + _LOGGER.debug(f"library_name_loop {library_name_loop}") + self._num_total_loans += 1 + if loan_item.get('days_remaining') and ((self._days_left is None) or (self._days_left > loan_item.get('days_remaining'))): + _LOGGER.debug(f"library_name_loop less days {library_name_loop} {loan_item}") + self._days_left = loan_item.get('days_remaining') + self._lowest_till_date = loan_item.get('loan_till') + self._num_loans = 1 + self._library_name = f"{loan_item.get('library')}" + if loan_item.get('extend_loan_id') == '': + self._some_not_extendable = True + if self._days_left == loan_item.get('days_remaining'): + _LOGGER.debug(f"library_name_loop same days {library_name_loop} {loan_item}") + self._num_loans += 1 + if loan_item.get('library') and loan_item.get('library') not in self._library_name: + self._library_name += f", {loan_item.get('library')}" + if loan_item.get('extend_loan_id') == '': + self._some_not_extendable = True + + + async def async_will_remove_from_hass(self): + """Clean up after entity before removal.""" + _LOGGER.info("async_will_remove_from_hass " + NAME) + self._data.clear_session() + + + @property + def icon(self) -> str: + """Shows the correct icon for container.""" + return "mdi:bookshelf" + + @property + def unique_id(self) -> str: + """Return the name of the sensor.""" + return ( + f"{NAME} warning" + ) + + @property + def name(self) -> str: + return self.unique_id + + @property + def extra_state_attributes(self) -> dict: + """Return the state attributes.""" + attributes = { + ATTR_ATTRIBUTION: NAME, + "last update": self._last_update, + "days_left": self._days_left, + "some_not_extendable": self._some_not_extendable, + "lowest_till_date": self._lowest_till_date, + "num_loans": self._num_loans, + "num_total_loans": self._num_total_loans, + "library_name": self._library_name + } + return attributes @property def device_info(self) -> dict: