diff --git a/bin/shngversion.py b/bin/shngversion.py index ec76a29054..67ff12d297 100644 --- a/bin/shngversion.py +++ b/bin/shngversion.py @@ -107,7 +107,8 @@ # Update auf 1.10.0 wg. Release # Update auf 1.10.0.1 wg. Kennzeichnung des Repo Stands als "nach dem v1.10.0 Release" # Update auf 1.10.0.2 wg. smartplugin: Added support for the use of asyncio in plugins -shNG_version = '1.10.0.2' +# Update auf 1.10.0.3 wg. smartplugin: Added support for suspend/resume and functions for pause_item +shNG_version = '1.10.0.3' shNG_branch = 'develop' shNG_releasedate = '29. Januar 2024' # Muss beim Release für den master branch auf das Release Datum gesetzt werden diff --git a/lib/logic.py b/lib/logic.py index 4706bd82e1..1179237c05 100644 --- a/lib/logic.py +++ b/lib/logic.py @@ -97,8 +97,8 @@ def __init__(self, smarthome, userlogicconf, envlogicconf): self._userlogicconf = userlogicconf self._env_dir = smarthome._env_dir self._envlogicconf = envlogicconf - self._etc_dir = smarthome._etc_dir - self._logic_dir = smarthome._logic_dir + self._etc_dir = smarthome.get_etcdir() + self._logic_dir = smarthome.get_logicsdir() self._workers = [] self._logics = {} #self._bytecode = {} @@ -897,11 +897,12 @@ def _count_filename_uses(self, conf, filename): Count the number of logics (sections) that reference this filename """ count = 0 - for name in conf: - section = conf.get(name, None) - fn = section.get('filename', None) - if fn is not None and fn.lower() == filename.lower(): - count += 1 + if conf: + for name in conf: + section = conf.get(name, None) + fn = section.get('filename', None) + if fn is not None and fn.lower() == filename.lower(): + count += 1 return count @@ -909,7 +910,6 @@ def filename_used_count(self, filename): # load /etc/logic.yaml conf_filename = os.path.join(self._get_etc_dir(), 'logic') conf = shyaml.yaml_load_roundtrip(conf_filename) - count = self._count_filename_uses(conf, filename) return count diff --git a/lib/model/smartplugin.py b/lib/model/smartplugin.py index c471391ed3..d44a45e5c6 100644 --- a/lib/model/smartplugin.py +++ b/lib/model/smartplugin.py @@ -1076,7 +1076,7 @@ def scheduler_get_all(self): def scheduler_remove_all(self): """ This method removes all schedulers added by the plugin """ - for sched in self._schedulers: + for sched in self._schedulers.copy(): try: self.scheduler_remove(sched) except Exception: diff --git a/lib/shpypi.py b/lib/shpypi.py index 1fbb4a1168..5af97ce999 100644 --- a/lib/shpypi.py +++ b/lib/shpypi.py @@ -1022,13 +1022,62 @@ def _split_requirement_to_min_max(self, requirement): reqs = requirement.split(',') for req in reqs: operator, version = self._split_operator(req) - if operator in ['>=', '==', '>']: + if operator in ['>=', '==']: result['min'] = version - if operator in ['<', '<=', '==']: + if operator in ['<=', '==']: result['max'] = version + if operator == '>': + result['min'] = self._get_bounding_version(version, below=True) + if operator == '<': + result['max'] = self._get_bounding_version(version, above=True) return result + def _get_bounding_version(self, version: str, below: bool = False, above: bool = False) -> str: + """ + calculate bounding version number to store requirements like + version < 2.0 --> bounding version will be 1.999 + version < 2.2.0 --> bounding version will be 2.1.999 + + If below == above, version is returned unchanged + + :param version: version number + :param below: True means bounding version below given version + :param above: True means bounding version above given version + :type version: str + :type below: bool + :type above: bool + + :return: modified version number + :rtype: str + """ + + def _to_num(vl: list) -> int: + """ convert version number in list form to 1000-based decimal """ + vi = int(vl[0]) + for d in vl[1:]: + vi = vi * 1000 + vi = vi + int(d) + return vi + + def _to_str(vi: int) -> str: + """ convert 1000-based decimal to reduced string """ + vl = [] + vs = str(vi) + while vs: + vl = [str(int(vs[-3:]))] + vl + vs = vs[:-3] + return ".".join(vl) + + if above == below: + return version + vers_org = self._version_to_list(version) + vers_num = _to_num(vers_org) + if above: + vers_num += 1 + if below: + vers_num -= 1 + return _to_str(vers_num) def _compare_versions(self, vers1, vers2, operator): diff --git a/lib/smarthome.py b/lib/smarthome.py index 204e8db963..c407c4fb9f 100644 --- a/lib/smarthome.py +++ b/lib/smarthome.py @@ -58,7 +58,7 @@ import logging import logging.handlers import logging.config -import platform # TODO: remove? unused +# import platform # TODO: remove? unused import shutil import signal @@ -66,10 +66,10 @@ import threading import time import traceback -try: - import psutil # TODO: remove? unused -except ImportError: - pass +# try: +# import psutil # TODO: remove? unused +# except ImportError: +# pass BASE = os.path.sep.join(os.path.realpath(__file__).split(os.path.sep)[:-2]) PIDFILE = os.path.join(BASE, 'var', 'run', 'smarthome.pid') @@ -153,31 +153,37 @@ def initialize_dir_vars(self): self._base_dir = BASE self.base_dir = self._base_dir # **base_dir** is deprecated. Use method get_basedir() instead. - for external modules using that var (backend, ...?) - self._extern_conf_dir = BASE - - self._etc_dir = os.path.join(self._base_dir, 'etc') + self._etc_dir = os.path.join(self._extern_conf_dir, 'etc') - # decide where to look for config files + # self._conf_dir contains the base dir for config folders if self._config_etc: self._conf_dir = self._etc_dir else: - self._conf_dir = self._base_dir + self._conf_dir = self._extern_conf_dir + # shng system dirs self._var_dir = os.path.join(self._base_dir, 'var') self._lib_dir = os.path.join(self._base_dir, 'lib') self._plugins_dir = os.path.join(self._base_dir, 'plugins') - self._structs_dir = os.path.join(self._conf_dir, 'structs') - self._env_dir = os.path.join(self._lib_dir, 'env' + os.path.sep) + # env and var dirs + self._env_dir = os.path.join(self._lib_dir, 'env' + os.path.sep) self._env_logic_conf_basename = os.path.join(self._env_dir, 'logic') + self._cache_dir = os.path.join(self._var_dir, 'cache' + os.path.sep) + + # user config dirs self._items_dir = os.path.join(self._conf_dir, 'items' + os.path.sep) - self._logic_conf_basename = os.path.join(self._conf_dir, 'logic') + self._structs_dir = os.path.join(self._conf_dir, 'structs') self._logic_dir = os.path.join(self._conf_dir, 'logics' + os.path.sep) - self._cache_dir = os.path.join(self._var_dir, 'cache' + os.path.sep) - self._log_conf_basename = os.path.join(self._etc_dir, 'logging') + self._functions_dir = os.path.join(self._conf_dir, 'functions' + os.path.sep) + self._scenes_dir = os.path.join(self._conf_dir, 'scenes' + os.path.sep) + # system config files + self._smarthome_conf_basename = os.path.join(self._etc_dir, 'smarthome') + self._log_conf_basename = os.path.join(self._etc_dir, 'logging') self._module_conf_basename = os.path.join(self._etc_dir, 'module') self._plugin_conf_basename = os.path.join(self._etc_dir, 'plugin') + self._logic_conf_basename = os.path.join(self._etc_dir, 'logic') def create_directories(self): @@ -187,8 +193,8 @@ def create_directories(self): os.makedirs(self._structs_dir, mode=0o775, exist_ok=True) os.makedirs(self._var_dir, mode=0o775, exist_ok=True) - os.makedirs(os.path.join(self._var_dir, 'backup'), mode=0o775, exist_ok=True) os.makedirs(self._cache_dir, mode=0o775, exist_ok=True) + os.makedirs(os.path.join(self._var_dir, 'backup'), mode=0o775, exist_ok=True) os.makedirs(os.path.join(self._var_dir, 'db'), mode=0o775, exist_ok=True) os.makedirs(os.path.join(self._var_dir, 'log'), mode=0o775, exist_ok=True) os.makedirs(os.path.join(self._var_dir, 'run'), mode=0o775, exist_ok=True) @@ -215,6 +221,9 @@ def __init__(self, MODE, extern_conf_dir='', config_etc=False): self._mode = MODE self._config_etc = config_etc + self._extern_conf_dir = BASE + if extern_conf_dir != '': + self._extern_conf_dir = extern_conf_dir self.initialize_vars() self.initialize_dir_vars() @@ -227,9 +236,6 @@ def __init__(self, MODE, extern_conf_dir='', config_etc=False): self.PYTHON_VERSION = lib.utils.get_python_version() self.python_bin = sys.executable - if extern_conf_dir != '': - self._extern_conf_dir = extern_conf_dir - # get systeminfo closs self.systeminfo = Systeminfo @@ -245,25 +251,6 @@ def __init__(self, MODE, extern_conf_dir='', config_etc=False): # self.version = shngversion.get_shng_version() self.connections = None - # reinitialize dir vars with path to extern configuration directory - self._etc_dir = os.path.join(self._extern_conf_dir, 'etc') - - # decide where to look for config files - if self._config_etc: - self._conf_dir = self._etc_dir - else: - self._conf_dir = self._extern_conf_dir - - self._items_dir = os.path.join(self._conf_dir, 'items' + os.path.sep) - self._functions_dir = os.path.join(self._conf_dir, 'functions' + os.path.sep) - self._logic_dir = os.path.join(self._conf_dir, 'logics' + os.path.sep) - self._scenes_dir = os.path.join(self._conf_dir, 'scenes' + os.path.sep) - self._smarthome_conf_basename = os.path.join(self._etc_dir, 'smarthome') - self._logic_conf_basename = os.path.join(self._etc_dir, 'logic') - self._module_conf_basename = os.path.join(self._etc_dir, 'module') - self._plugin_conf_basename = os.path.join(self._etc_dir, 'plugin') - self._log_conf_basename = os.path.join(self._etc_dir, 'logging') - self._pidfile = PIDFILE # check config files @@ -548,6 +535,24 @@ def get_structsdir(self) -> str: return self._structs_dir + def get_logicsdir(self) -> str: + """ + Function to return the logics config directory + + :return: Config directory as an absolute path + """ + return self._logic_dir + + + def get_functionsdir(self) -> str: + """ + Function to return the userfunctions config directory + + :return: Config directory as an absolute path + """ + return self._functions_dir + + def get_vardir(self): """ Function to return the var directory used by SmartHomeNG diff --git a/lib/userfunctions.py b/lib/userfunctions.py index 3bc9883bd9..6f410083e9 100644 --- a/lib/userfunctions.py +++ b/lib/userfunctions.py @@ -91,7 +91,10 @@ def init_lib(shng_base_dir=None, sh=None): else: base_dir = os.getcwd() - _func_dir = os.path.join(base_dir, _uf_subdir) + if _sh: + _func_dir = _sh.get_functionsdir() + else: + _func_dir = os.path.join(base_dir, _uf_subdir) user_modules = [] if os.path.isdir(_func_dir): diff --git a/modules/admin/api_config.py b/modules/admin/api_config.py index 1d00fa5ede..099694a071 100644 --- a/modules/admin/api_config.py +++ b/modules/admin/api_config.py @@ -42,7 +42,7 @@ def __init__(self, module): self.base_dir = self._sh.get_basedir() self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) - self.etc_dir = self._sh._etc_dir + self.etc_dir = self._sh.get_etcdir() self.modules_dir = os.path.join(self.base_dir, 'modules') self.core_conf = shyaml.yaml_load(os.path.join(self.modules_dir, 'core', 'module.yaml')) diff --git a/modules/admin/api_files.py b/modules/admin/api_files.py index b1759238ab..255aa5b4a1 100644 --- a/modules/admin/api_files.py +++ b/modules/admin/api_files.py @@ -51,11 +51,11 @@ def __init__(self, module): self.base_dir = self._sh.get_basedir() self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) - self.etc_dir = self._sh._etc_dir + self.etc_dir = self._sh.get_etcdir() self.items_dir = self._sh._items_dir - self.functions_dir = self._sh._functions_dir + self.functions_dir = self._sh.get_functionsdir() self.scenes_dir = self._sh._scenes_dir - self.logics_dir = self._sh._logic_dir + self.logics_dir = self._sh.get_logicsdir() self.extern_conf_dir = self._sh._extern_conf_dir self.modules_dir = os.path.join(self.base_dir, 'modules') return diff --git a/modules/admin/api_logics.py b/modules/admin/api_logics.py index 78cb5ca835..1db6703428 100644 --- a/modules/admin/api_logics.py +++ b/modules/admin/api_logics.py @@ -49,7 +49,7 @@ def __init__(self, module): self.etc_dir = self._sh._etc_dir - self.logics_dir = os.path.join(self.base_dir, 'logics') + self.logics_dir = self._sh.get_logicsdir() self.logics = Logics.get_instance() self.logger.info("__init__ self.logics = {}".format(self.logics)) self.plugins = Plugins.get_instance() diff --git a/tests/mock/core.py b/tests/mock/core.py index dd9dedf94c..967d56c345 100644 --- a/tests/mock/core.py +++ b/tests/mock/core.py @@ -215,6 +215,24 @@ def get_structsdir(self): """ return self._structs_dir + + def get_logicsdir(self) -> str: + """ + Function to return the logics config directory + + :return: Config directory as an absolute path + """ + return self._logic_dir + + + def get_functionsdir(self) -> str: + """ + Function to return the userfunctions config directory + + :return: Config directory as an absolute path + """ + return self._functions_dir + def get_vardir(self): """ Function to return the var directory used by SmartHomeNG