diff --git a/.gitignore b/.gitignore index bc08e4ed4..6a46b1f3c 100755 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,10 @@ ehthumbs.db Thumbs.db # don't upload private plugins -/priv_* +/priv_*/ + +# don't upload plugins loaded from develop to a release installation +/*_dev/ # Pycharm settings /.idea diff --git a/byd_bat/__init__.py b/byd_bat/__init__.py index 22544d63b..91594310c 100644 --- a/byd_bat/__init__.py +++ b/byd_bat/__init__.py @@ -35,6 +35,8 @@ # V0.0.3 230819 - Code mit pycodestyle kontrolliert/angepasst # - Anpassungen durch 'check_plugin' # +# V0.0.4 230904 - Bilder JPG in PNG konvertiert fuer user_doc.rst +# # ----------------------------------------------------------------------- # # Als Basis fuer die Implementierung wurde die folgende Quelle verwendet: @@ -176,7 +178,7 @@ class properties and methods (class variables and class functions) are already available! """ - PLUGIN_VERSION = '0.0.3' + PLUGIN_VERSION = '0.0.4' def __init__(self,sh): """ diff --git a/byd_bat/plugin.yaml b/byd_bat/plugin.yaml index bf5ee1608..305458573 100644 --- a/byd_bat/plugin.yaml +++ b/byd_bat/plugin.yaml @@ -12,7 +12,7 @@ plugin: # documentation: https://github.com/smarthomeNG/smarthome/wiki/CLI-Plugin # url of documentation (wiki) page support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1886748-support-thread-f%C3%BCr-das-byd-batterie-plugin - version: 0.0.3 # Plugin version (must match the version specified in __init__.py) + version: 0.0.4 # Plugin version (must match the version specified in __init__.py) sh_minversion: 1.9 # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin diff --git a/byd_bat/user_doc.rst b/byd_bat/user_doc.rst index 95bea0503..706d6b300 100644 --- a/byd_bat/user_doc.rst +++ b/byd_bat/user_doc.rst @@ -33,12 +33,12 @@ Anforderungen Der BYD Energiespeicher muss mit dem LAN verbunden sind. Die IP-Adresse des BYD wird über DHCP zugewiesen und muss ermittelt werden. Diese IP-Adresse muss in der Plugin-Konfiguration gespeichert werden. Notwendige Software -~~~~~~~~~~~~~~~~~~~ +------------------- * matplotlib Unterstützte Geräte -~~~~~~~~~~~~~~~~~~~ +------------------- Folgende Typen werden unterstützt: @@ -53,27 +53,27 @@ Konfiguration ============= plugin.yaml -~~~~~~~~~~~ +----------- -Bitte die Dokumentation lesen, die aus den Metadaten der plugin.yaml erzeugt wurde. +Zu den Informationen, welche Parameter in der ../etc/plugin.yaml konfiguriert werden können bzw. müssen, bitte die Dokumentation :doc:`Dokumentation ` lesen, die aus den Metadaten der plugin.yaml erzeugt wurde. items.yaml -~~~~~~~~~~ +---------- -Bitte die Dokumentation lesen, die aus den Metadaten der plugin.yaml erzeugt wurde. +Zu den Informationen, welche Attribute in der Item Konfiguration verwendet werden können bzw. müssen, bitte die Dokumentation :doc:`Dokumentation ` lesen, die aus den Metadaten der plugin.yaml erzeugt wurde. logic.yaml -~~~~~~~~~~ +---------- -Bitte die Dokumentation lesen, die aus den Metadaten der plugin.yaml erzeugt wurde. +Zu den Informationen, welche Konfigurationsmöglichkeiten für Logiken bestehen, bitte die Dokumentation :doc:`Dokumentation ` lesen, die aus den Metadaten der plugin.yaml erzeugt wurde. Funktionen -~~~~~~~~~~ +---------- -Bitte die Dokumentation lesen, die aus den Metadaten der plugin.yaml erzeugt wurde. +Zu den Informationen, welche Funktionen das Plugin bereitstellt (z.B. zur Nutzung in Logiken), bitte die Dokumentation :doc:`Dokumentation ` lesen, die aus den Metadaten der plugin.yaml erzeugt wurde. Web Interface ============= @@ -87,20 +87,20 @@ Oben rechts werden die wichtigsten Daten zum BYD Energiespeicher angezeigt. Im Tab "BYD Home" sind die Grunddaten des Energiespeichers dargestellt: -.. image:: assets/home.JPG +.. image:: assets/home.png :class: screenshot Im Tab "BYD Diagnose" werden Diagnosedaten angezeigt: -.. image:: assets/diag.JPG +.. image:: assets/diag.png :class: screenshot Im Tab "BYD Spannungen" werden die Spannungen der Module als Heatmap angezeigt: -.. image:: assets/volt.JPG +.. image:: assets/volt.png :class: screenshot Im Tab "BYD Temperaturen" werden die Temperaturen der Module als Heatmap angezeigt: -.. image:: assets/temp.JPG +.. image:: assets/temp.png :class: screenshot diff --git a/byd_bat/webif/static/img/diag.JPG b/byd_bat/webif/static/img/diag.JPG deleted file mode 100644 index 9c12b92db..000000000 Binary files a/byd_bat/webif/static/img/diag.JPG and /dev/null differ diff --git a/byd_bat/webif/static/img/diag.png b/byd_bat/webif/static/img/diag.png new file mode 100644 index 000000000..829475694 Binary files /dev/null and b/byd_bat/webif/static/img/diag.png differ diff --git a/byd_bat/webif/static/img/home.JPG b/byd_bat/webif/static/img/home.JPG deleted file mode 100644 index b8522d1a4..000000000 Binary files a/byd_bat/webif/static/img/home.JPG and /dev/null differ diff --git a/byd_bat/webif/static/img/home.png b/byd_bat/webif/static/img/home.png new file mode 100644 index 000000000..fb56b7b97 Binary files /dev/null and b/byd_bat/webif/static/img/home.png differ diff --git a/byd_bat/webif/static/img/temp.JPG b/byd_bat/webif/static/img/temp.JPG deleted file mode 100644 index 6c3c7afcd..000000000 Binary files a/byd_bat/webif/static/img/temp.JPG and /dev/null differ diff --git a/byd_bat/webif/static/img/temp.png b/byd_bat/webif/static/img/temp.png new file mode 100644 index 000000000..84c97db70 Binary files /dev/null and b/byd_bat/webif/static/img/temp.png differ diff --git a/byd_bat/webif/static/img/volt.JPG b/byd_bat/webif/static/img/volt.JPG deleted file mode 100644 index 05ce0edc4..000000000 Binary files a/byd_bat/webif/static/img/volt.JPG and /dev/null differ diff --git a/byd_bat/webif/static/img/volt.png b/byd_bat/webif/static/img/volt.png new file mode 100644 index 000000000..7677dd5f3 Binary files /dev/null and b/byd_bat/webif/static/img/volt.png differ diff --git a/garminconnect/requirements.txt b/garminconnect/requirements.txt index f13dc3ee8..2b83de542 100755 --- a/garminconnect/requirements.txt +++ b/garminconnect/requirements.txt @@ -1 +1,2 @@ -garminconnect>=0.1.28 \ No newline at end of file +garminconnect>=0.1.28,<0.2.0;python_version<='3.9' +garminconnect>=0.1.28;python_version>='3.10' diff --git a/mailsend/__init__.py b/mailsend/__init__.py index a63f61377..fde79ebc0 100755 --- a/mailsend/__init__.py +++ b/mailsend/__init__.py @@ -78,11 +78,15 @@ def __call__(self, to, sub, msg, caller=None, source=None): pass self.logger.debug("email was sent") - def extended(self, to, sub, msg, sender_name: str, img_list: list=[], attachments: list=[]): + def extended(self, to, sub, msg, sender_name: str, img_list: list=None, attachments: list=None, caller=None, source=None): + if img_list is None: + img_list = [] + if attachments is None: + attachments = [] try: smtp = self._connect() except Exception as e: - self.logger.warning("Could not connect to {0}: {1}".format(self._host, e)) + self.logger.warning(f"Could not connect to {self._host}: {e}") return try: sender_name = Header(sender_name, 'utf-8').encode() diff --git a/odlinfo/README.md b/odlinfo/README.md index dc141221f..27dbd7432 100755 --- a/odlinfo/README.md +++ b/odlinfo/README.md @@ -81,6 +81,7 @@ odlinfo: class_name: ODLInfo class_path: plugins.odlinfo cycle: 3600 + verify: True ``` ### items.yaml diff --git a/odlinfo/__init__.py b/odlinfo/__init__.py index 31d395275..0801ee492 100755 --- a/odlinfo/__init__.py +++ b/odlinfo/__init__.py @@ -41,7 +41,7 @@ import cherrypy class ODLInfo(SmartPlugin): - PLUGIN_VERSION = "1.5.2" + PLUGIN_VERSION = "1.5.3" _base_url = 'https://www.imis.bfs.de/ogc/opendata/ows' def __init__(self, sh, *args, **kwargs): @@ -55,6 +55,7 @@ def __init__(self, sh, *args, **kwargs): if not self.init_webinterface(WebInterface): self._init_complete = False self._cycle = self.get_parameter_value('cycle') + self._verify = self.get_parameter_value('verify') self._stations = [] self._items = {} self._update_timestamp = None @@ -88,7 +89,7 @@ def _get_stations(self): """ try: parameters = "service=WFS&version=1.1.0&request=GetFeature&typeName=opendata:odlinfo_odl_1h_latest&outputFormat=application/json&sortBy=plz" - response = self._session.get(self._build_url(parameters)) + response = self._session.get(self._build_url(parameters), verify=self._verify) self._update_timestamp = self.shtime.now() except Exception as e: diff --git a/odlinfo/plugin.yaml b/odlinfo/plugin.yaml index 3eef0af6d..1f73e7852 100755 --- a/odlinfo/plugin.yaml +++ b/odlinfo/plugin.yaml @@ -12,7 +12,7 @@ plugin: documentation: https://smarthomeng.de/user/plugins_doc/config/odlinfo.html support: https://knx-user-forum.de/node/986480 - version: 1.5.2 # Plugin version + version: 1.5.3 # Plugin version sh_minversion: 1.6 # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance @@ -27,6 +27,12 @@ parameters: description: de: '(optional) Zeit zwischen zwei Updateläufen. Default ist 3600 Sekunden.' en: '(optional) Time period between two update cycles. Default is 3600 seconds.' + verify: + type: bool + default: True + description: + de: 'Schaltet die Prüfung der Zertifikate des odlinfo Endpunkts ab.' + en: 'Removes certificate validation of the odlinfo endpoint.' item_attributes: # Definition of item attributes defined by this plugin diff --git a/shelly/__init__.py b/shelly/__init__.py index 9d5ab5555..4916f7a92 100755 --- a/shelly/__init__.py +++ b/shelly/__init__.py @@ -39,7 +39,7 @@ class Shelly(MqttPlugin): the update functions for the items """ - PLUGIN_VERSION = '1.8.0' + PLUGIN_VERSION = '1.8.1' def __init__(self, sh): @@ -361,7 +361,7 @@ def update_Gen1_from_item(self, item, config_data): # Support methods for Gen1 and Gen2 devices # ---------------------------------------------------------------------------------------------- - def get_shelly_device_from_item(self, item): + def get_shelly_device_from_item(self, item) -> dict: """ Get the shelly device data for a device specified by an item object @@ -1078,6 +1078,8 @@ def handle_gen1_info(self, shelly_id, topic, payload): elif property == 'sensor': self.update_items_from_status(shelly_id, 'sensor', 'state', sub_property['state'], 'info') + elif property == 'charger': + self.update_items_from_status(shelly_id, 'sensor', property, sub_property) elif property == 'bat': self.update_items_from_status(shelly_id, 'sensor', 'battery', sub_property['value'], 'info') self.update_items_from_status(shelly_id, 'sensor', 'voltage', sub_property['voltage'], 'info') @@ -1092,8 +1094,7 @@ def handle_gen1_info(self, shelly_id, topic, payload): self.shelly_devices[shelly_id]['rssi'] = sub_property.get('rssi', '') else: - self.log_unhandled_status(shelly_id, property, sub_property, topic=topic, payload=payload, - position='*1') + self.log_unhandled_status(shelly_id, property, sub_property, topic=topic, payload=payload, position='*1') return @@ -1113,8 +1114,10 @@ def handle_gen1_status(self, shelly_id: str, property, topic, payload, group=Non property_mapping = {'temperature': 'temp', 'temperature_f': 'temp_f'} if property.startswith('command'): pass - elif property == 'loaderror': + elif property in ['loaderror']: pass + elif property == 'charger': + self.update_items_from_status(shelly_id, 'sensor', property, payload) elif property == 'online': self.update_items_from_status(shelly_id, '', property, payload) elif property in ['temperature', 'temperature_f', 'overtemperature', 'overpower', 'input']: @@ -1127,7 +1130,9 @@ def handle_gen1_status(self, shelly_id: str, property, topic, payload, group=Non self.log_unhandled_status(shelly_id, property, payload, topic=topic, payload=payload, position='*1.1') elif group.startswith('switch:'): - if property in ['output', 'power', 'energy']: + if property == 'command': # ignore commands sent to the shelly device + pass + elif property in ['output', 'power', 'energy']: self.update_items_from_status(shelly_id, group, property, payload) self.logger.dbghigh(f"handle_gen1_status: {shelly_id} {group} - {property}={payload} - (mapping={group + '-' + property})") else: diff --git a/shelly/plugin.yaml b/shelly/plugin.yaml index 33c670f78..ba3d8be5c 100755 --- a/shelly/plugin.yaml +++ b/shelly/plugin.yaml @@ -12,7 +12,7 @@ plugin: # documentation: http://smarthomeng.de/user/plugins/mqtt2/user_doc.html support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1451853-support-thread-für-das-shelly-plugin - version: 1.8.0 # Plugin version + version: 1.8.1 # Plugin version sh_minversion: 1.9.5.6 # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance diff --git a/shelly/user_doc.rst b/shelly/user_doc.rst index a5902ddbc..867a46f13 100755 --- a/shelly/user_doc.rst +++ b/shelly/user_doc.rst @@ -21,7 +21,7 @@ die Dokumentation des jeweiligen Devices zu Rate ziehen. Konfiguration ============= -Zurzeit werden eine Reihe von Shelly Devices mit Gen1 API im **Backward-Compatibility Mode** unterstützt. Dabei handelt +Zurzeit werden eine Reihe von Shelly Devices mit Gen1 API im **Backward-Co^mpatibility Mode** unterstützt. Dabei handelt es sich um die Devices, die bereits in der v1.2.0 des Plugins unterstützt wurden. Diese Devices werden konfiguriert, wie es bis zur v1.2.0 des shally Plugins üblich war. diff --git a/uzsu/__init__.py b/uzsu/__init__.py index 7c4ae7a00..f752f29d8 100755 --- a/uzsu/__init__.py +++ b/uzsu/__init__.py @@ -104,7 +104,7 @@ class UZSU(SmartPlugin): ALLOW_MULTIINSTANCE = False - PLUGIN_VERSION = "1.6.5" # item buffer for all uzsu enabled items + PLUGIN_VERSION = "1.6.6" # item buffer for all uzsu enabled items def __init__(self, smarthome): """ @@ -137,7 +137,7 @@ def run(self): self.logger.debug("run method called") self.alive = True self.scheduler_add('uzsu_sunupdate', self._update_all_suns, - value={'caller': 'scheduler'}, cron=self._suncalculation_cron) + value={'caller': 'Scheduler:UZSU'}, cron=self._suncalculation_cron) self.logger.info("Adding sun update schedule for midnight") for item in self._items: @@ -180,6 +180,7 @@ def stop(self): Stop method for the plugin """ self.logger.debug("stop method called") + self.scheduler_remove('uzsu_sunupdate') for item in self._items: try: self.scheduler_remove('{}'.format(item.property.path)) @@ -195,7 +196,7 @@ def _update_all_suns(self, caller=None): :type caller: str """ for item in self._items: - success = self._update_sun(item) + success = self._update_sun(item, caller="update_all_suns") if success: self.logger.debug('Updating sun info for item {}. Caller: {}'.format(item, caller)) self._update_item(item, 'UZSU Plugin', 'update_all_suns') @@ -223,8 +224,8 @@ def _update_sun(self, item, caller=None): item, caller, self._items[item]['sunrise'], self._items[item]['sunset'])) success = True except Exception as e: - success = False - self.logger.debug("Not updated sun entries for item {}. Error {}".format(item, e)) + success = "Not updated sun entries for item {}. Error {}".format(item, e) + self.logger.debug(success) return success def _update_suncalc(self, item, entry, entryindex, entryvalue): @@ -355,7 +356,8 @@ def _logics_planned(self, item=None): if self._planned.get(item) not in [None, {}, 'notinit'] and self._items[item].get('active') is True: self.logger.info("Item '{}' is going to be set to {} at {}".format( item, self._planned[item]['value'], self._planned[item]['next'])) - self._webdata['items'][item.id()].update({'planned': {'value': self._planned[item]['value'], 'time': self._planned[item]['next']}}) + self._webdata['items'][item.id()].update({'planned': {'value': self._planned[item]['value'], + 'time': self._planned[item]['next']}}) return self._planned[item] elif self._planned.get(item) == 'notinit' and self._items[item].get('active') is True: self.logger.info("Item '{}' is active but not fully initialized yet.".format(item)) @@ -524,26 +526,24 @@ def _update_item(self, item, caller="", comment=""): success = self._get_sun4week(item, caller="_update_item") if success: self.logger.debug('Updated weekly sun info for item {}' - ' caller : {} comment : {}'.format(item, caller, comment)) + ' caller: {} comment: {}'.format(item, caller, comment)) else: self.logger.debug('Issues with updating weekly sun info' - ' for item {} caller : {} comment : {}'.format(item, caller, comment)) - success = False + ' for item {} caller: {} comment: {}'.format(item, caller, comment)) success = self._series_calculate(item, caller, comment) - if success: + if success is True: self.logger.debug('Updated seriesCalculated for item {}' - ' caller : {} comment : {}'.format(item, caller, comment)) + ' caller: {} comment: {}'.format(item, caller, comment)) else: self.logger.debug('Issues with updating seriesCalculated' - ' for item {} caller : {} comment : {}'.format(item, caller, comment)) - success = False + ' for item {} caller: {} comment: {}, issue: {}'.format(item, caller, comment, success)) success = self._update_sun(item, caller="_update_item") - if success: + if success is True: self.logger.debug('Updated sunset/rise calculations for item {}' - ' caller : {} comment : {}'.format(item, caller, comment)) + ' caller: {} comment: {}'.format(item, caller, comment)) else: self.logger.debug('Issues with updating sunset/rise calculations' - ' for item {} caller : {} comment : {}'.format(item, caller, comment)) + ' for item {} caller: {} comment: {}, issue: {}'.format(item, caller, comment, success)) item(self._items[item], caller, comment) self._webdata['items'][item.id()].update({'interpolation': self._items[item].get('interpolation')}) self._webdata['items'][item.id()].update({'active': str(self._items[item].get('active'))}) @@ -562,13 +562,13 @@ def _schedule(self, item, caller=None): This function schedules an item: First the item is removed from the scheduler. If the item is active then the list is searched for the nearest next execution time. No matter if active or not the calculation for the execution time is triggered. - :param item: item to be updated towards the plugin + :param item: item to be updated towards the plugin. :param caller: if given it represents the callers name. If the caller is set - to "dry_run" the evaluation of sun entries takes place but no scheduler will be set + to "dry_run" the evaluation of sun entries takes place but no scheduler will be set. """ if caller != "dry_run": self.scheduler_remove('{}'.format(item.property.path)) - _caller = "scheduler" + _caller = "Scheduler:UZSU" self.logger.debug('Schedule Item {}, Trigger: {}, Changed by: {}'.format( item, caller, item.changed_by())) else: @@ -717,7 +717,7 @@ def _schedule(self, item, caller=None): self._webdata['items'][item.id()].update({'planned': {'value': _value, 'time': _next.strftime('%d.%m.%Y %H:%M')}}) self._update_count['done'] = self._update_count.get('done') + 1 self.scheduler_add('{}'.format(item.property.path), self._set, - value={'item': item, 'value': _value}, next=_next) + value={'item': item, 'value': _value, 'caller': 'Scheduler'}, next=_next) if self._update_count.get('done') == self._update_count.get('todo'): self.scheduler_trigger('uzsu_sunupdate', by='UZSU Plugin') self._update_count = {'done': 0, 'todo': 0} @@ -736,7 +736,7 @@ def _set(self, item=None, value=None, caller=None): _uzsuitem, _itemvalue = self._get_dependant(item) _uzsuitem(value, 'UZSU Plugin', 'set') self._webdata['items'][item.id()].update({'depend': {'item': _uzsuitem.id(), 'value': str(_itemvalue)}}) - if not caller: + if not caller or caller == "Scheduler": self._schedule(item, caller='set') def _get_time(self, entry, timescan, item=None, entryindex=None, caller=None): @@ -767,6 +767,7 @@ def _get_time(self, entry, timescan, item=None, entryindex=None, caller=None): if 'time' not in entry: return None, None value = entry['value'] + next = None active = True if caller == "dry_run" else entry['active'] today = datetime.today() tomorrow = today + timedelta(days=1) @@ -793,7 +794,7 @@ def _get_time(self, entry, timescan, item=None, entryindex=None, caller=None): rrule = rrulestr(entry['rrule'], dtstart=datetime.combine( weekbefore, self._sun(datetime.combine(weekbefore.date(), datetime.min.time()).replace(tzinfo=self._timezone), - time, timescan).time())) + time, timescan).time())) self.logger.debug("Looking for {} sun-related time. Found rrule: {}".format( timescan, str(rrule).replace('\n', ';'))) else: @@ -809,7 +810,7 @@ def _get_time(self, entry, timescan, item=None, entryindex=None, caller=None): sleep(0.01) next = self._sun(datetime.combine(dt.date(), datetime.min.time()).replace(tzinfo=self._timezone), - time, timescan) + time, timescan) self.logger.debug("Result parsing time (rrule) {}: {}".format(time, next)) if entryindex is not None and timescan == 'next': self._update_suncalc(item, entry, entryindex, next.strftime("%H:%M")) @@ -855,12 +856,12 @@ def _get_time(self, entry, timescan, item=None, entryindex=None, caller=None): self.logger.debug("Looking for {} series-related time. Found rrule: {} with start-time . {}".format( timescan, entry['rrule'].replace('\n', ';'), entry['series']['timeSeriesMin'])) - cond_today = next.date() == today.date() - cond_yesterday = next.date() - timedelta(days=1) == yesterday.date() - cond_tomorrow = next.date() == tomorrow.date() - cond_next = next > datetime.now(self._timezone) - cond_previous_today = next - timedelta(seconds=1) < datetime.now(self._timezone) - cond_previous_yesterday = next - timedelta(days=1) < datetime.now(self._timezone) + cond_today = False if next is None else next.date() == today.date() + cond_yesterday = False if next is None else next.date() - timedelta(days=1) == yesterday.date() + cond_tomorrow = False if next is None else next.date() == tomorrow.date() + cond_next = False if next is None else next > datetime.now(self._timezone) + cond_previous_today = False if next is None else next - timedelta(seconds=1) < datetime.now(self._timezone) + cond_previous_yesterday = False if next is None else next - timedelta(days=1) < datetime.now(self._timezone) if next and cond_today and cond_next: self._itpl[item][next.timestamp() * 1000.0] = value self.logger.debug("Return next today: {}, value {}".format(next, value)) @@ -893,7 +894,8 @@ def _series_calculate(self, item, caller=None, source=None): """ self.logger.debug("Series Calculate method for item {} called by {}. Source: {}".format(item, caller, source)) if not self._items[item].get('list'): - return + issue = "No list entry in UZSU dict for item {}".format(item) + return issue try: mydays = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'] for i, mydict in enumerate(self._items[item]['list']): @@ -906,32 +908,36 @@ def _series_calculate(self, item, caller=None, source=None): try: ##################### seriesbegin, seriesend, daycount, mydict = self._fix_empty_values(mydict) - intervall = mydict['series'].get('timeSeriesIntervall', None) + interval = mydict['series'].get('timeSeriesIntervall', None) seriesstart = seriesbegin + endtime = None - if intervall is None or intervall == "": - self.logger.warning("Could not calculate serie for item {}" - " - because intervall is None - {}".format(item, mydict)) - return + if interval is None or interval == "": + issue = "Could not calculate serie for item {}"\ + " - because interval is None - {}".format(item, mydict) + self.logger.warning(issue) + return issue if (daycount == '' or daycount is None) and seriesend is None: - self.logger.warning("Could not calculate series" - " because timeSeriesCount is NONE and TimeSeriesMax is NONE") - return + issue = "Could not calculate series because "\ + "timeSeriesCount is NONE and TimeSeriesMax is NONE" + self.logger.warning(issue) + return issue - intervall = int(intervall.split(":")[0]) * 60 + int(mydict['series']['timeSeriesIntervall'].split(":")[1]) + interval = int(interval.split(":")[0]) * 60 + int(mydict['series']['timeSeriesIntervall'].split(":")[1]) - if intervall == 0: - self.logger.warning("Could not calculate serie because intervall is ZERO - {}".format(mydict)) - return + if interval == 0: + issue = "Could not calculate serie because interval is ZERO - {}".format(mydict) + self.logger.warning(issue) + return issue if daycount is not None and daycount != '': - if int(daycount) * intervall >= 1440: + if int(daycount) * interval >= 1440: org_daycount = daycount - daycount = int(1439 / intervall) + daycount = int(1439 / interval) self.logger.warning("Cut your SerieCount to {} -" - " because intervall {} x SerieCount {}" - " is more than 24h".format(daycount, intervall, org_daycount)) + " because interval {} x SerieCount {}" + " is more than 24h".format(daycount, interval, org_daycount)) if 'sun' not in mydict['series']['timeSeriesMin']: starttime = datetime.strptime(mydict['series']['timeSeriesMin'], "%H:%M") @@ -944,7 +950,7 @@ def _series_calculate(self, item, caller=None, source=None): # calculate End of Serie by Count if seriesend is None: endtime = starttime - endtime += timedelta(minutes=intervall * int(daycount)) + endtime += timedelta(minutes=interval * int(daycount)) if seriesend is not None and 'sun' in seriesend: mytime = self._sun(datetime.now().replace(hour=0, minute=0, second=0).astimezone(self._timezone), @@ -954,7 +960,7 @@ def _series_calculate(self, item, caller=None, source=None): elif seriesend is not None and 'sun' not in seriesend: endtime = datetime.strptime(seriesend, "%H:%M") - if seriesend is None: + if seriesend is None and endtime: seriesend = str(endtime.time())[:5] if endtime <= starttime: @@ -964,13 +970,13 @@ def _series_calculate(self, item, caller=None, source=None): original_daycount = daycount if daycount is None: - daycount = int(timediff.total_seconds() / 60 / intervall) + daycount = int(timediff.total_seconds() / 60 / interval) else: - new_daycount = int(timediff.total_seconds() / 60 / intervall) + new_daycount = int(timediff.total_seconds() / 60 / interval) if int(daycount) > new_daycount: - self.logger.warning("Cut your SerieCount to {} - because intervall {}" + self.logger.warning("Cut your SerieCount to {} - because interval {}" " x SerieCount {} is not possible between {} and {}".format( - new_daycount, intervall, daycount, starttime, endtime)) + new_daycount, interval, daycount, starttime, endtime)) daycount = new_daycount ##################### @@ -981,13 +987,13 @@ def _series_calculate(self, item, caller=None, source=None): str(starttime.minute)).time())) mynewlist = [] - intervall = int(mydict['series']['timeSeriesIntervall'].split(":")[0])*60 + \ + interval = int(mydict['series']['timeSeriesIntervall'].split(":")[0])*60 + \ int(mydict['series']['timeSeriesIntervall'].split(":")[1]) exceptions = 0 for day in list(rrule): if not mydays[day.weekday()] in mydict['rrule']: continue - myrulenext = "FREQ=MINUTELY;COUNT={};INTERVAL={}".format(daycount, intervall) + myrulenext = "FREQ=MINUTELY;COUNT={};INTERVAL={}".format(daycount, interval) if 'sun' not in mydict['series']['timeSeriesMin']: starttime = datetime.strptime(mydict['series']['timeSeriesMin'], "%H:%M") @@ -1029,13 +1035,14 @@ def _series_calculate(self, item, caller=None, source=None): mytpl = {'seriesMin': str(seriestarttime.time())[:5]} if original_daycount is not None: mytpl['seriesMax'] = str((seriestarttime + - timedelta(minutes=intervall * count)).time())[:5] + timedelta(minutes=interval * count)).time())[:5] else: mytpl['seriesMax'] = "{:02d}".format(endtime.hour) + ":" + \ "{:02d}".format(endtime.minute) mytpl['seriesDay'] = actday mytpl['maxCountCalculated'] = count if exceptions == 0 else 0 - self.logger.debug("Mytpl: {}, count {}, daycount {}, interval {}".format(mytpl, count, daycount, intervall)) + self.logger.debug("Mytpl: {}, count {}, " + "daycount {}, interval {}".format(mytpl, count, daycount, interval)) mynewlist.append(mytpl) count = 0 seriestarttime = None @@ -1051,14 +1058,14 @@ def _series_calculate(self, item, caller=None, source=None): if seriestarttime is not None: mytpl = {'seriesMin': str(seriestarttime.time())[:5]} if original_daycount is not None: - mytpl['seriesMax'] = str((seriestarttime + timedelta(minutes=intervall * count)).time())[:5] + mytpl['seriesMax'] = str((seriestarttime + timedelta(minutes=interval * count)).time())[:5] else: mytpl['seriesMax'] = "{:02d}".format(endtime.hour) + ":" + "{:02d}".format(endtime.minute) mytpl['maxCountCalculated'] = count if exceptions == 0 else 0 mytpl['seriesDay'] = actday self.logger.debug("Mytpl for last time of day: {}," " count {} daycount {}," - " interval {}".format(mytpl, count, original_daycount, intervall)) + " interval {}".format(mytpl, count, original_daycount, interval)) mynewlist.append(mytpl) if mynewlist: @@ -1125,15 +1132,15 @@ def _series_get_time(self, mydict, timescan=''): returnvalue = None seriesbegin, seriesend, daycount, mydict = self._fix_empty_values(mydict) - intervall = mydict['series'].get('timeSeriesIntervall', None) + interval = mydict['series'].get('timeSeriesIntervall', None) seriesstart = seriesbegin - if intervall is not None and intervall != "": - intervall = int(intervall.split(":")[0])*60 + int(mydict['series']['timeSeriesIntervall'].split(":")[1]) + if interval is not None and interval != "": + interval = int(interval.split(":")[0])*60 + int(mydict['series']['timeSeriesIntervall'].split(":")[1]) else: return returnvalue - if intervall == 0: - self.logger.warning("Could not calculate serie because intervall is ZERO - {}".format(mydict)) + if interval == 0: + self.logger.warning("Could not calculate serie because interval is ZERO - {}".format(mydict)) return returnvalue if 'sun' not in mydict['series']['timeSeriesMin']: @@ -1155,30 +1162,29 @@ def _series_get_time(self, mydict, timescan=''): if endtime < starttime: endtime += timedelta(days=1) timediff = endtime - starttime - daycount = int(timediff.total_seconds() / 60 / intervall) + daycount = int(timediff.total_seconds() / 60 / interval) else: if seriesend is None: endtime = starttime - endtime += timedelta(minutes=intervall * int(daycount)) + endtime += timedelta(minutes=interval * int(daycount)) timediff = endtime - starttime - daycount = int(timediff.total_seconds() / 60 / intervall) + daycount = int(timediff.total_seconds() / 60 / interval) else: endtime = datetime.strptime(seriesend, "%H:%M") timediff = endtime - starttime if daycount is not None and daycount != '': - if seriesend is None and (int(daycount) * intervall >= 1440): + if seriesend is None and (int(daycount) * interval >= 1440): org_count = daycount - count = int(1439 / intervall) - self.logger.warning("Cut your SerieCount to {} - because intervall {}" - " x SerieCount {} is more than 24h".format(count, intervall, org_count)) + count = int(1439 / interval) + self.logger.warning("Cut your SerieCount to {} - because interval {}" + " x SerieCount {} is more than 24h".format(count, interval, org_count)) else: - new_daycount = int(timediff.total_seconds() / 60 / intervall) - #self.logger.debug("new daycount: {}, seriesend {}".format(new_daycount, seriesend)) + new_daycount = int(timediff.total_seconds() / 60 / interval) if int(daycount) > new_daycount: - self.logger.warning("Cut your SerieCount to {} - because intervall {}" + self.logger.warning("Cut your SerieCount to {} - because interval {}" " x SerieCount {} is not possible between {} and {}".format( - new_daycount, intervall, daycount, datetime.strftime(starttime, "%H:%M"), + new_daycount, interval, daycount, datetime.strftime(starttime, "%H:%M"), datetime.strftime(endtime, "%H:%M"))) daycount = new_daycount mylist = OrderedDict() @@ -1191,7 +1197,7 @@ def _series_get_time(self, mydict, timescan=''): timestamp = day mylist[timestamp] = 'x' while mycount < daycount: - timestamp = timestamp + timedelta(minutes=intervall) + timestamp = timestamp + timedelta(minutes=interval) mylist[timestamp] = 'x' mycount += 1 diff --git a/uzsu/plugin.yaml b/uzsu/plugin.yaml index 29bc5e70e..5a0096583 100755 --- a/uzsu/plugin.yaml +++ b/uzsu/plugin.yaml @@ -46,7 +46,7 @@ plugin: keywords: scheduler uzsu trigger series support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1364692-supportthread-für-uzsu-plugin - version: 1.6.5 # Plugin version + version: 1.6.6 # Plugin version sh_minversion: 1.6 # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance