def _load_scenes(self): """ Load defined scenes with learned values from ../scene directory :return: """ self._scenes = {} self._learned_values = {} self._scenes_dir = self._sh._scenes_dir if not os.path.isdir(self._scenes_dir): logger.warning(translate("Directory '{scenes_dir}' not found. Ignoring scenes.", {'scenes_dir': self._scenes_dir})) return self._learned_values = {} # for item in smarthome.return_items(): for item in self.items.return_items(): if item.type() == 'scene': self.scene_file = os.path.join(self._scenes_dir, item.id()) scene_file_yaml = yaml.yaml_load(self.scene_file+'.yaml', ordered=False, ignore_notfound=True) if scene_file_yaml is not None: # Reading yaml file with scene definition for state in scene_file_yaml: actions = scene_file_yaml[state].get('actions', None) if actions is not None: if isinstance(actions, dict): actions = [ actions ] if isinstance( actions, list ): for action in actions: if isinstance(action, dict): self._add_scene_entry(item, str(state), action.get('item', ''), str(action.get('value', '')), action.get('learn', ''), scene_file_yaml[state].get('name', '')) else: logger.warning(translate("Scene {scene}, state {state}: action '{action}' is not a dict", {'scene': item, 'state': state, 'action': action})) else: logger.warning(translate("Scene {scene}, state {state}: actions are not a list", {'scene': item, 'state': state})) self._load_learned_values(str(item.id())) else: # Trying to read conf file with scene definition scene_conf_file = self.scene_file + '.conf' try: with open(scene_conf_file, 'r', encoding='UTF-8') as f: reader = csv.reader(f, delimiter=' ') for row in reader: if row == []: # ignore empty lines continue if row[0][0] == '#': # ignore comments continue self._add_scene_entry(item, row[0], row[1], row[2]) except Exception as e: logger.warning(translate("Problem reading scene file {file}: No .yaml or .conf file found with this name", {'file': self.scene_file})) continue item.add_method_trigger(self._trigger) return
def import_user_module(m): """ Import a module with userfunctions :param m: name of module to import from <shng_base_dir>/functions :return: True, if import was successful """ modulename = _uf_subdir + '.' + m import importlib try: exec(f"globals()['{m}']=importlib.import_module('{modulename}')") except Exception as e: _logger.error( translate("Error importing userfunctions from '{module}': {error}", { 'module': m, 'error': e })) return False else: global _uf_version _uf_version = '?.?.?' try: exec(f"globals()['_uf_version'] = {m}._VERSION") except: exec(f"{m}._VERSION = _uf_version") global _uf_description _uf_description = '?' try: exec(f"globals()['_uf_description'] = {m}._DESCRIPTION") except: exec(f"{m}._DESCRIPTION = _uf_description") _logger.notice( translate( "Imported userfunctions from '{mmodule}' v{version} - {description}", { 'module': m, 'version': _uf_version, 'description': _uf_description })) return True
def reload_scenes(self): """ Reload defined scenes with learned values from ../scene directory :return: """ self._load_scenes() logger.notice(translate("Reloaded all scenes")) return True
def get_scene_action_name(self, scenename, action): """ Returns the name of a scene-action """ action = str(action) try: return self._scenes[scenename][action][0][2] except: logger.warning(translate("get_scene_action_name: " + "unable to get self._scenes['{scenename}']['{action}'][0][2] <- {res}", {'scenename': scenename, 'action': action, 'res': self._scenes[scenename][action][0]})) return ''
def reload(userlib): import importlib if userlib in _user_modules: try: exec(f"importlib.reload({userlib})") except Exception as e: if str(e) == f"name '{userlib}' is not defined": _logger.warning( translate( "Error reloading userfunctions Modul '{module}': Module is not loaded, trying to newly import userfunctions '{module}' instead", {'module': userlib})) if import_user_module(userlib): return True else: return False else: _logger.error( translate( "Error reloading userfunctions '{module}': {error} - old version of '{module}' is still active", { 'module': userlib, 'error': e })) return False else: _logger.notice( translate("Reloaded userfunctions '{module}'", {'module': userlib})) return True else: if import_user_module(userlib): #_logger.notice(translate("Reload: Loaded new userfunctions '{module}'", {'module': userlib})) return True else: _logger.error( translate("Reload: Userfunctions '{module}' do not exist", {'module': userlib})) return False
def reload_all(): if _user_modules == []: _logger.warning( translate('No userfunctions are loaded, nothing to reload')) return False else: result = True for lib in _user_modules: if not reload(lib): result = False return result
def _trigger(self, item, caller, source, dest): """ Trigger a scene """ if not item.id() in self._scenes: return if str(item()&127) in self._scenes[item.id()]: state = item() if Utils.is_int(state): state = int(state) else: logger.error(translate("Invalid state '{state}' for scene {scene}", {'state': state, 'scene': item.id()})) return if (state >= 0) and (state < 64): # set state self._trigger_setstate(item, state, caller, source, dest) elif (state >= 128) and (state < 128+64): # learn state self._trigger_learnstate(item, state&127, caller, source, dest) else: logger.error(translate("Invalid state '{state}' for scene {scene}", {'state': state, 'scene': item.id()}))
def _add_scene_entry(self, item, state, ditemname, value, learn=False, name=''): """ Adds a single assignement entry to the loaded scenes :param item: item defing the scene (type: scene) :param row: list of: state number, item to assign to, value to assign to item :param name: name of the scene state :type item: item object :type row: list (with 3 entries) :type name: str """ logger.debug("_add_scene_entry: item = {}, state = {}, ditemname = {}, value = {}, learn = {}, name = {}".format(item.id(), state, ditemname, value, learn, name)) value = item.get_stringwithabsolutepathes(value, 'sh.', '(', 'scene') # ditem = self._sh.return_item(item.get_absolutepath(ditemname, attribute='scene')) ditem = self.items.return_item(item.get_absolutepath(ditemname, attribute='scene')) if learn: rvalue = self._eval(value) if str(rvalue) != value: logger.warning(translate("_add_scene_entry - " + "Learn set to 'False', because '{rvalue}' != '{value}'", {'rvalue': rvalue, 'value': value})) learn = False if ditem is None: ditem = self.logics.return_logic(ditemname) if ditem is None: logger.warning(translate("Could not find item or logic '{ditemname}' specified in {file}", {'ditemname': ditemname, 'file': self.scene_file})) return if item.id() in self._scenes: if state in self._scenes[item.id()]: self._scenes[item.id()][state].append([ditem, value, name, learn]) else: self._scenes[item.id()][state] = [[ditem, value, name, learn]] else: self._scenes[item.id()] = {state: [[ditem, value, name, learn]]} return
def __init__(self, smarthome): self._sh = smarthome global _scenes_instance if _scenes_instance is not None: import inspect curframe = inspect.currentframe() calframe = inspect.getouterframes(curframe, 2) logger.critical(translate("A second 'scenes' object has been created. There should only be ONE instance of class 'Scenes'!!! Called from: {frame1} ({frame2})", {'frame1': calframe[1][1], 'frame2': calframe[1][3]})) _scenes_instance = self self.items = Items.get_instance() self.logics = Logics.get_instance() self._load_scenes() return
def _eval(self, value): """ Evaluate a scene value :param value: value expression to evaluate :type value: str :return: evaluated value or None :rtype: type of evaluated expression or None """ sh = self._sh shtime = Shtime.get_instance() items = Items.get_instance() import math import lib.userfunctions as uf try: rvalue = eval(value) except Exception as e: logger.warning(" - " + translate("Problem evaluating: {value} - {exception}", {'value': value, 'exception': e})) return value return rvalue