diff --git a/doc/user/source/installation/komplettanleitung/02_smarthomeng.rst b/doc/user/source/installation/komplettanleitung/02_smarthomeng.rst index e42ea6c53..3630f1e82 100644 --- a/doc/user/source/installation/komplettanleitung/02_smarthomeng.rst +++ b/doc/user/source/installation/komplettanleitung/02_smarthomeng.rst @@ -370,9 +370,6 @@ Die Koordinaten für einen Standort kann man z.B. auf http://www.mapcoordinates. # - mysql:pymysql # module_paths = /usr/local/python/lib # list of path-entries is possible - # Version 1.3: control type casting when assiging values to items - # assign_compatibility = latest # latest or compat_1.2 (compat_1.2 is default for shNG v1.3) - Es bietet sich an, die default-Datei nach smarthome.yaml zu kopieren und die Daten oben auf den eigenen Standort anzupassen. Alternativ kann diese Anpassung später über das Admin Interface durchgeführt werden. diff --git a/doc/user/source/referenz/items/funktionen.rst b/doc/user/source/referenz/items/funktionen.rst index 4fa157171..ef3a4ad2a 100644 --- a/doc/user/source/referenz/items/funktionen.rst +++ b/doc/user/source/referenz/items/funktionen.rst @@ -15,13 +15,10 @@ genutzt werden können. +--------------------------------+--------------------------------------------------------------------------------+ | **Funktion** | **Beschreibung** | +================================+================================================================================+ -| autotimer(time, value, compat) | Setzt einen Timer bei jedem Werte-Wechsel der Items. Angegeben wird die Zeit | +| autotimer(time, value) | Setzt einen Timer bei jedem Werte-Wechsel der Items. Angegeben wird die Zeit | | | (**time**) die vergehen soll, bis das Item auf den Wert (**value**) gesetzt | | | wird. Die Zeitangabe erfolgt in Sekunden. Eine Angabe der Dauer in Minuten | -| | ist wie in '10m' möglich. Die Bedeutung und Wirkungsweise von **compat** bitte | -| | auf der Seite | -| | :doc:`autotimer <./standard_attribute/autotimer>` | -| | nachlesen. | +| | ist wie in '10m' möglich. | +--------------------------------+--------------------------------------------------------------------------------+ | fade(end, step, delta, caller, | Blendet das Item mit der definierten Schrittweite (int oder float) und | | stop_fade, continue_fade, | timedelta (int oder float in Sekunden) auf einen angegebenen Wert auf oder | @@ -40,10 +37,8 @@ genutzt werden können. | return_parent() | Liefert den Item-Pfad des übergeordneten Items zurück. | | | Aufruf: sh.item.return_parent() | +--------------------------------+--------------------------------------------------------------------------------+ -| timer(time, value, compat) | Funktioniert wir **autotimer()**, ausser dass die Aktion nur einmal ausgeführt | -| | wird. Die Bedeutung und Wirkungsweise von **compat** bitte auf der Seite | -| | :doc:`autotimer <./standard_attribute/autotimer>` | -| | nachlesen. | +| timer(time, value) | Funktioniert wir **autotimer()**, ausser dass die Aktion nur einmal ausgeführt | +| | wird. | +--------------------------------+--------------------------------------------------------------------------------+ diff --git a/doc/user/source/referenz/items/standard_attribute/crontab.rst b/doc/user/source/referenz/items/standard_attribute/crontab.rst index 28ab7f107..f14849eef 100644 --- a/doc/user/source/referenz/items/standard_attribute/crontab.rst +++ b/doc/user/source/referenz/items/standard_attribute/crontab.rst @@ -100,6 +100,10 @@ Im Admin Interface können die einzelnen Parametersätze durch ``|`` getrennt we Durch Anhängen eines ``= value`` wird der entsprechende Wert ``value`` mitgesendet. Das Beispiel setzt den Wert des Items täglich um Mitternacht auf ``20``: +**Ab SmartHomeNG v1.11** werden die Konfigurationsmöglichkeiten erweitert: + +Für den **Wert** kann nun ein **eval** Ausdruck angegeben werden, der zur Laufzeit entsprechend neu evaluiert wird. +Dabei können auch Item Properties genutzt werden. .. code-block:: yaml @@ -107,6 +111,9 @@ Das Beispiel setzt den Wert des Items täglich um Mitternacht auf ``20``: - '0 0 * * = 20' - sunrise + crontab: '0 0 * * = sh.pfad.zum.item1() * 4 + sh.pfad.zum.item2.property.last_value' + + Möchte man einen Wert im Minutentakt aktualisieren, ist es notwendig den Ausdruck ``* * * *`` unter Anführungszeichen zu setzen. .. code-block:: yaml diff --git a/lib/item/helpers.py b/lib/item/helpers.py index 9ca4b5ded..7a617705a 100644 --- a/lib/item/helpers.py +++ b/lib/item/helpers.py @@ -138,21 +138,28 @@ def split_duration_value_string(value, ATTRIB_COMPAT_DEFAULT): components are: - time - value - - compat + - possibly compat (obsolete, kept for backward compatibility) - :param value: raw attribute string containing duration, value (and compatibility) + :param value: raw attribute string containing duration, value :return: three strings, representing time, value and compatibility attribute """ + compat = '' + if value.find(ATTRIBUTE_SEPARATOR) >= 0: time, __, attrvalue = value.partition(ATTRIBUTE_SEPARATOR) attrvalue, __, compat = attrvalue.partition(ATTRIBUTE_SEPARATOR) - elif value.find('=') >= 0: + elif value.find('=') >= 0 and value[value.find('='):value.find('=') + 2] != '==': time, __, attrvalue = value.partition('=') - attrvalue, __, compat = attrvalue.partition('=') + if attrvalue.find('=') >= 0 and (attrvalue.rfind('=') != attrvalue.rfind('==')) and (attrvalue.endswith('compat') or attrvalue.endswith('compat_1.2') or attrvalue.endswith('latest')): + attrvalue, __, compat = attrvalue.rpartition('=') else: time = value attrvalue = None - compat = '' + + # try to fix time if compat is (still) given: + time = time.strip() + if time.endswith('compat') or time.endswith('compat_1.2') or time.endswith('latest'): + time = time.removesuffix('compat').removesuffix('compat_1.2').removesuffix('latest').strip()[:-1] time = time.strip() if attrvalue is not None: @@ -164,6 +171,7 @@ def split_duration_value_string(value, ATTRIB_COMPAT_DEFAULT): # remove quotes, if present if value != '' and ((value[0] == "'" and value[-1] == "'") or (value[0] == '"' and value[-1] == '"')): value = value[1:-1] + return (time, attrvalue, compat) @@ -181,11 +189,11 @@ def join_duration_value_string(time, value, compat=''): """ result = str(time) if value != '' or compat != '': - result = result + ' =' + result = result + ' ' + ATTRIBUTE_SEPARATOR if value != '': result = result + ' ' + value if compat != '': - result = result + ' = ' + compat + result = result + ' ' + ATTRIBUTE_SEPARATOR + ' ' + compat return result @@ -204,7 +212,6 @@ def json_serialize(obj): return obj.isoformat() raise TypeError("Type not serializable") - def json_obj_hook(json_dict): """ helper method for json deserialization @@ -233,7 +240,6 @@ def cache_read(filename, tz, cformat=CACHE_FORMAT): return (dt, value) - def cache_write(filename, value, cformat=CACHE_FORMAT): try: if cformat == CACHE_PICKLE: diff --git a/lib/item/item.py b/lib/item/item.py index e817a4845..a1bd39418 100755 --- a/lib/item/item.py +++ b/lib/item/item.py @@ -1800,7 +1800,7 @@ def get_attr_time(self, attr: str) -> int | None: var = getattr(self, f'_{attr}_time') if var is None: - logger.debug(f'get_attr_time({attr}): item {self._path} has no member _{attr}_time. This is weird...') + logger.debug(f'get_attr_time({attr}): item {self._path} has no member _{attr}_time.') return if isinstance(var, int) or var is None: @@ -1833,17 +1833,22 @@ def get_attr_time(self, attr: str) -> int | None: except Exception as e: logger.warning(f'error on evaluation {attr} time "{var}" for item {self._path}: {e}') - def get_attr_value(self, attr: str): + def get_attr_value(self, attr: str, value=None): """ return attribute value, possibly recalculated at call time :param attr: attribute to calculate value for, e.g. cycle or autotimer :type attr: str """ - if attr not in ('cycle', 'autotimer'): + if attr not in ('cycle', 'autotimer', 'cron'): return - var = getattr(self, f'_{attr}_value') + # only for cron, the value is stored in the scheduler instead of in the item + # so we just use the given value to proceed (eval) + if value is not None: + var = value + else: + var = getattr(self, f'_{attr}_value') if var is None: return diff --git a/lib/scheduler.py b/lib/scheduler.py index 871cb0f2a..30a40dcf9 100644 --- a/lib/scheduler.py +++ b/lib/scheduler.py @@ -475,6 +475,8 @@ def add(self, name, obj, prio=3, cron=None, cycle=None, value=None, offset=None, _value = None else: _value = _value.strip() + if obj.__class__.__name__ == 'Item': + _value = obj.get_stringwithabsolutepathes(_value, 'sh.', '(') if desc.lower().startswith('init'): details = desc offset = 5 # default init offset @@ -560,7 +562,7 @@ def add(self, name, obj, prio=3, cron=None, cycle=None, value=None, offset=None, self._scheduler[name] = {'prio': prio, 'obj': obj, 'source': source, 'cron': cron, 'cycle': cycle, 'value': value, 'next': next, 'active': True} if next is None: self._next_time(name, offset) - except Exception as e: + except Exception: raise # logger.error(f"Exception: {e} while trying to add a new entry to scheduler") finally: @@ -781,12 +783,15 @@ def _task(self, name, obj, by, source, dest, value): if isinstance(value, dict) and value.get('caller') == 'Autotimer': src = 'autotimer' value = obj.get_attr_value(src) + elif isinstance(source, dict) and source.get('source', '') == 'cron': + src = 'cron' + if value is None: # re-set current item value. needs enforce_updates to work properly value = obj() else: # get current (static or evaluated) value from item itself - value = obj.get_attr_value(src) + value = obj.get_attr_value(src, value) # logger.debug(f'item {obj}: src = {src}, value = {value}') if src == 'cycle':