def rollback(self): with self.__rlock: try: return self.__con.rollback() except Exception as e: Logger.log_error("Database.rollback ex:", e) traceback.print_exc()
def initialize(self): currentVersion = self.get_user_version() for updateToVersion, (__runOnce, __run) in enumerate( zip(self.__runOnce, self.__run), 1): # First version = 1, not 0. if updateToVersion > currentVersion: # Performs run once function call on later versions only. try: __runOnce("/".join( (Database.RESOURCE_PATH, str(updateToVersion)))) except Exception as e: Logger.log_error("Error on updating to version", updateToVersion, "ex:", e) raise Exception( "Failed to initialize database: error on run-once function for version " + str(updateToVersion)) # Executes all custom functions try: __run() except Exception as e: Logger.log_error("Error on version", updateToVersion, "function execution, ex:", e) raise Exception( "Failed to initialize database: error on run-everytime function for version " + str(updateToVersion)) else: del (self.__runOnce) del (self.__run) self.set_user_version(updateToVersion)
def process_method_list(methodList): #=================================================================== # Basic type validation #=================================================================== if not isinstance(methodList, list): Logger.log_error( "RuleService.set_rule: 'condition' and 'execution' must be type of list." ) Logger.log_debug("type:", type(methodList), "value:", methodList) raise AutomationException( 11704, "List is required for both 'condition' and 'execution'") #=================================================================== # Check allowed size, raise error if exceeded. #=================================================================== methodListLen = len(methodList) if methodListLen > AppConstants.MAX_METHOD_SIZE: Logger.log_error( "RuleService.set_rule: 'condition' and 'execution' cannot have more than", AppConstants.MAX_METHOD_SIZE, "items respectively.") raise AutomationException(11705, "Only a maximum of " + \ str(AppConstants.MAX_METHOD_SIZE) + \ " items is allowed for each 'condition' and 'execution' - given size " + \ str(methodListLen), lambda text: str(AppConstants.MAX_METHOD_SIZE).join(text.split(":max_item_size:"))) #=================================================================== # Check if all kbxMethodIds are valid and all kbxMethodParams are list #=================================================================== idValidator = NumberValidator(isRequired=True, decimalPoint=False) if not all([ idValidator.is_valid(eachMethod["kbxMethodId"]) and isinstance(eachMethod["kbxMethodParams"], list) for eachMethod in methodList ]): raise AutomationException( 11704, "'condition' and 'execution' have incorrect data structure." ) #=================================================================== # Check if all kbxParamName and kbxParamCurrentValue exists #=================================================================== paramNameValidator = StringValidator(isRequired=True) for eachMethod in methodList: methodArgs = eachMethod["kbxMethodParams"] for methodArg in methodArgs: if not paramNameValidator.is_valid( methodArg[AppConstants.ARG_NAME]): raise AutomationException( 11704, "'condition' and 'execution' have invalid params structure" ) if not AppConstants.ARG_CURRENT_VALUE in methodArg: methodArg[AppConstants.ARG_CURRENT_VALUE] = None return methodList
def __date_time_range(self, request): try: dtVal = request.get_value( TimerModule._PARAM_DT_RANGE.get_kbx_param_name()) startDateTime = dtVal.get_start_date_time() endDateTime = dtVal.get_end_date_time() execTime = request.get_arg(AppConstants.KEY_CONDITION_TIMESTAMP) execTime = int(execTime) if execTime > endDateTime: # Allow a time delta of 59 seconds response = bool(abs(execTime - endDateTime) <= 59) else: response = bool(startDateTime <= execTime <= endDateTime) if not response: raise AutomationException(11800) self.send_response({}, request.requestId) except AutomationException as ae: Logger.log_error( "TimerModule date_time range failed on comparison:", str(ae)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message()) except Exception as e: Logger.log_error( "TimerModule date_time range failed on comparison (unexpected):", str(e)) ae = AutomationException(11099, str(e)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message())
def delete_favorited_scene(self, sceneId): ''' sceneId - must be a favorited scene. ''' with self.__fav_lock: try: favSort = self.__sceneController.get_favsort_of(sceneId) except Exception as e: raise AutomationException(11092, "prevSceneId does not belongs to any scene - " + \ "prevSceneId provided: " + str(sceneId) + ", error: " + str(e)) else: if favSort is None: raise AutomationException(13000, "sceneId does not belongs to any favorited scene - " + \ "sceneId provided: " + str(sceneId)) # This method raise error and rollback automatically if failed, or commit once succeed. try: self.__sceneController.delete_favorited_scene(sceneId) self.__sceneController.commit() except Exception as e: self.__sceneController.rollback() Logger.log_error( "SceneService.delete_favorited_scene failed to delete favorited scene, ex:", str(e)) raise AutomationException(13002, "unexpected error: " + str(e)) #Broadcast event self.__broadcast_message__scene_updated(sceneId) self.__broadcast_message__favorited_scene_deleted(sceneId)
def __update_scene(scene): try: # Fire scene update start event sceneId = scene["sceneId"] sceneName = scene["sceneName"] # Add methods to subscribe list methodIds = [ kbxMethod["kbxMethodId"] for kbxMethod in scene["execution"] ] self.__methodController.add(methodIds) # Update "scene" base table self.__sceneController.update(scene) self.__sceneController.commit() except Exception as e: self.__sceneController.rollback() self.__broadcast_message__scene_update_failed( sceneId, sceneName) Logger.log_error("SceneService __update_scene failed:", e, "-- rolledback") else: # Broadcast message: completed updating a scene self.__broadcast_message__scene_updated(sceneId)
def __date_time_range(self, request): try: dtVal = request.get_value(TimerModule._PARAM_DT_RANGE.get_kbx_param_name()) startDateTime = dtVal.get_start_date_time() endDateTime = dtVal.get_end_date_time() execTime = request.get_arg(AppConstants.KEY_CONDITION_TIMESTAMP) execTime = int(execTime) if execTime > endDateTime: # Allow a time delta of 59 seconds response = bool(abs(execTime - endDateTime) <= 59) else: response = bool(startDateTime <= execTime <= endDateTime) if not response: raise AutomationException(11800) self.send_response({}, request.requestId) except AutomationException as ae: Logger.log_error("TimerModule date_time range failed on comparison:", str(ae)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message()) except Exception as e: Logger.log_error("TimerModule date_time range failed on comparison (unexpected):", str(e)) ae = AutomationException(11099, str(e)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message())
def start(self): ''' Initialize all services required by automation app. ''' MethodController.instance() # Must listen to groups and methods added event before bootstrap. # Bootstrap try: bs = BootstrapService() Logger.log_info("Bootstrap update kbx method ...") bs.update_kbx_method() Logger.log_info("Bootstrap update kbx group ...") bs.update_kbx_group() Logger.log_info("Bootstrap register trigger schedulers ...") bs.register_trigger_schedulers() Logger.log_info("Bootstrap register timer module schedulers ...") bs.register_timer_module_schedulers() except Exception as e: bs.rollback() Logger.log_error("AutomationModuleWrapper.start bootstrap ex:", e) traceback.print_exc() raise e else: bs.commit() Logger.log_info("Bootstrap process completed!") self.__apiService = APIService() self.__ruleService = RuleService() self.__sceneService = SceneService() self.__serService = SceneExecutionResultService.instance()
def __day_of_week(self, request): try: dows = request.get_value( TimerModule._PARAM_DOW.get_kbx_param_name()) execTime = request.get_arg(AppConstants.KEY_CONDITION_TIMESTAMP) execTime = datetime.datetime.fromtimestamp(execTime) execDow = execTime.isoweekday() % 7 response = bool(execDow in dows) if not response: raise AutomationException(11800) self.send_response({}, request.requestId) except AutomationException as ae: Logger.log_error("TimerModule day_of_week failed on comparison:", str(ae)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message()) except Exception as e: Logger.log_error( "TimerModule day_of_week failed on comparison (unexpected):", str(e)) ae = AutomationException(11099, str(e)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message())
def rollback(self): with self.__rlock: try: return self.__con.rollback() except Exception as e: Logger.log_error("Database.rollback ex:", e) traceback.print_exc()
def delete_favorited_scene(self, sceneId): ''' sceneId - must be a favorited scene. ''' with self.__fav_lock: try: favSort = self.__sceneController.get_favsort_of(sceneId) except Exception as e: raise AutomationException(11092, "prevSceneId does not belongs to any scene - " + \ "prevSceneId provided: " + str(sceneId) + ", error: " + str(e)) else: if favSort is None: raise AutomationException(13000, "sceneId does not belongs to any favorited scene - " + \ "sceneId provided: " + str(sceneId)) # This method raise error and rollback automatically if failed, or commit once succeed. try: self.__sceneController.delete_favorited_scene(sceneId) self.__sceneController.commit() except Exception as e: self.__sceneController.rollback() Logger.log_error("SceneService.delete_favorited_scene failed to delete favorited scene, ex:", str(e)) raise AutomationException(13002, "unexpected error: " + str(e)) #Broadcast event self.__broadcast_message__scene_updated(sceneId) self.__broadcast_message__favorited_scene_deleted(sceneId)
def __update_rule(rule): try: # Fire rule update start event ruleId = rule["ruleId"] # Add methods to subscribe list methodIds = [kbxMethod["kbxMethodId"] for kbxMethod in rule["condition"] + rule["execution"]] self.__methodController.add(methodIds) # Update "rule" base table self.__ruleController.update(rule) self.__ruleController.commit() except Exception as e: self.__ruleController.rollback() self.__broadcast_message__rule_update_failed(ruleId, ruleName) Logger.log_error("RuleService __update_rule failed:", e, "-- rolledback") else: self.__triggerController.register_listener(ruleId, rule["trigger"]) # Process for Timer Module TimerModule.delete_scheduler(ruleId) timerModuleHandlers = {TimerModule.METHOD_ID_DATE_TIME_RANGE:TimerModule.handle_date_time_range, TimerModule.METHOD_ID_DAY_OF_WEEK:TimerModule.handle_dow, TimerModule.METHOD_ID_TIME_RANGE:TimerModule.handle_time_range} for kbxMethod in rule["condition"]: kbxMethodId = kbxMethod["kbxMethodId"] timerModuleHandler = timerModuleHandlers.get(kbxMethodId, None) if timerModuleHandler is not None: timerModuleHandler(ruleId, kbxMethod["kbxMethodParams"]) # Broadcast message: completed updating a rule self.__broadcast_message__rule_updated(ruleId)
def commit(self): with self.__rlock: try: return self.__con.commit() except Exception as e: Logger.log_error("Database.commit ex:", e) traceback.print_exc()
def commit(self): with self.__rlock: try: return self.__con.commit() except Exception as e: Logger.log_error("Database.commit ex:", e) traceback.print_exc()
def list_groups(self, language, section, parentId=None): try: def sort_group_list(value): name = value.get("kbxGroupLabel") if name is None: return "" else: return str(name).lower() unknownGroupDict = {} unknownGroupId = self.__get_group_ids() parentId = AppConstants.GROUP_ID_AUTOMATION if parentId is None else parentId result = SharedMethod.list_shared_method_groups(kbxGroupParentId=parentId, kbxMethodTag=section, enableTagCount=True, language=language) groupList = result["groupList"] # Level 1 groups which contain of SERVICES and LOCATIONS. if parentId == AppConstants.GROUP_ID_AUTOMATION: services = deque() groups = deque() for groupDict in groupList: kbxGroupId = groupDict["kbxGroupId"] # Add indicator for UI groupDict["kbxGroupHasChild"] = True # Reordering if kbxGroupId == AppConstants.GROUP_ID_NOTIFICATION: groupDict["kbxGroupDesc"] = KBXLang("group_notification_" + section) services.appendleft(groupDict) elif kbxGroupId == AppConstants.GROUP_ID_SERVICE: groupDict["kbxGroupDesc"] = KBXLang("group_service_" + section) services.append(groupDict) elif kbxGroupId == unknownGroupId: #append the group dict unknownGroupDict = groupDict else: groups.append(groupDict) groups = sorted(groups, key=sort_group_list) if len(unknownGroupDict) > 0: groups.append(unknownGroupDict) services.extend(groups) groupList = services parentGroup = None # Level 2 groups which are DEVICES or SERVICES. else: parentGroup = SharedMethod.get_shared_method_group_by_id(kbxGroupId=parentId, language=language) return parentGroup, groupList except Exception as e: Logger.log_error("APIService.list_groups ex:", e) raise AutomationException(11601, "Unexpected error - " + str(e))
def __on_shared_method_updated(self, eventObject): ''' Triggered based on event broadcasted by system app. ''' try: kbxMethod = json.loads(eventObject["eventData"]) # eventData = kbxMethod self.update(kbxMethod) except Exception as e: Logger.log_error("MethodController.__on_shared_method_updated ex:", e) traceback.print_exc()
def enable_rule(self, ruleId, enabled): self.__check_rule_process_status(ruleId) try: self.__ruleController.enable(ruleId, enabled) self.__ruleController.commit() except Exception as e: self.__ruleController.rollback() Logger.log_error("RuleService enable_rule ex:", e, "-- rolled back") else: self.__broadcast_message__rule_updated(ruleId)
def enable_rule(self, ruleId, enabled): self.__check_rule_process_status(ruleId) try: self.__ruleController.enable(ruleId, enabled) self.__ruleController.commit() except Exception as e: self.__ruleController.rollback() Logger.log_error("RuleService enable_rule ex:", e, "-- rolled back") else: self.__broadcast_message__rule_updated(ruleId)
def __on_shared_method_group_updated(self, eventObject): ''' Triggered based on event broadcasted by system app. ''' try: eventData = json.loads(eventObject["eventData"]) self.update(eventData) # eventData = kbxGroup except Exception as e: Logger.log_error( "GroupController.__on_shared_method_group_updated ex:", e) traceback.print_exc()
def __broadcast_message__scene_updated(self, sceneId): try: scene = self.__sceneController.get_summary(sceneId) except Exception as e: Logger.log_error("SceneService.__broadcast_message__scene_updated get_summary ex:", e) scene = None eventTag = AppConstants.EVENT_SCENE_UPDATED eventData = {"sceneId":sceneId, "newSceneSummary":scene} self.__broadcast_message(eventTag, eventData) Logger.log_info("Scene Updated:", scene["sceneName"])
def __on_shared_method_deleted(self, eventObject): ''' Triggered based on event broadcasted by system app. ''' try: eventData = json.loads(eventObject["eventData"]) kbxMethodId = eventData["kbxMethodId"] self.delete(kbxMethodId) except Exception as e: Logger.log_error("MethodController.__on_shared_method_deleted ex:", e) traceback.print_exc()
def __add_scene_execution_result(self, serUrl, serStartTime, serEndTime, sceneName, sceneProtected, sceneId, execution, language): try: SceneExecutionResultService.instance().add_scene_execution_result(serUrl=serUrl, serStartTime=serStartTime, serEndTime=serEndTime, sceneName=sceneName, sceneProtected=sceneProtected, sceneId=sceneId, execution=execution, language=language) except Exception as e: Logger.log_error("SceneService.__add_scene_execution_result ex:", e)
def set_favorited_scene(self, sceneId, prevSceneId=None): ''' sceneId - can be either favorited/non-favorited scene, but must be a valid scene id. prevSceneId - must be another favorited scene or error is raised. Both updating and executing doesn't blocks a scene from adding to/removing from favorited list. ''' with self.__fav_lock: # Validation for sceneId. if not self.__sceneController.has(sceneId): raise AutomationException( 11092, "sceneId does not belongs to any scene - sceneId provided: " + str(sceneId)) # Because UI display in reversed order, hence their prevSceneId == our nextSceneId nextSceneId = prevSceneId or None # Get favSort before sceneId of nextSceneId if nextSceneId is None: maxFavSort = self.__sceneController.get_largest_favsort_num( ) # If len of favorited list is 0, maxFavSort = 0 # favSort stores number to be assigned to sceneId. favSort = maxFavSort + 1 else: try: # favSort stores number to be assigned to sceneId. # current favSort of prevSceneId will becomes favSort of sceneId. favSort = self.__sceneController.get_favsort_of( nextSceneId) except Exception as e: raise AutomationException(11092, "prevSceneId does not belongs to any scene - " + \ "prevSceneId provided: " + str(nextSceneId) + ", error: " + str(e)) else: if favSort is None: raise AutomationException(13000, "prevSceneId does not belongs to any favorited scene - " + \ "prevSceneId provided: " + str(nextSceneId) + ", error: " + str(e)) # Update favSort of the scene try: self.__sceneController.update_favorited_scene(sceneId, favSort) self.__sceneController.commit() except Exception as e: self.__sceneController.rollback() Logger.log_error( "SceneService.set_favorited_scene failed to update favorited scene, ex:", str(e)) raise AutomationException(13001, "unexpected error: " + str(e)) # Broadcast events self.__broadcast_message__scene_updated(sceneId) self.__broadcast_message__favorited_scene_added( sceneId, prevSceneId)
def delete_rule(self, ruleId): self.__check_rule_process_status(ruleId) try: self.__ruleController.delete(ruleId) self.__ruleController.commit() except Exception as e: self.__ruleController.rollback() Logger.log_error("RuleService delete_rule ex:", e, "-- rolled back") else: self.__broadcast_message__rule_deleted(ruleId) self.__triggerController.unregister_listener(ruleId) TimerModule.delete_scheduler(ruleId)
def __broadcast_message__rule_updated(self, ruleId): try: rule = self.__ruleController.get_summary(ruleId) except Exception as e: Logger.log_error("RuleService.__broadcast_message__rule_updated get_summary ex:", e) return eventTag = AppConstants.EVENT_RULE_UPDATED eventData = rule self.__broadcast_message(eventTag, eventData) Logger.log_info("Rule Updated:", rule["ruleName"])
def delete_rule(self, ruleId): self.__check_rule_process_status(ruleId) try: self.__ruleController.delete(ruleId) self.__ruleController.commit() except Exception as e: self.__ruleController.rollback() Logger.log_error("RuleService delete_rule ex:", e, "-- rolled back") else: self.__broadcast_message__rule_deleted(ruleId) self.__triggerController.unregister_listener(ruleId) TimerModule.delete_scheduler(ruleId)
def __on_shared_method_group_deleted(self, eventObject): ''' Triggered based on event broadcasted by system app. ''' try: eventData = json.loads(eventObject["eventData"]) kbxGroupId = eventData["kbxGroupId"] self.delete(kbxGroupId) except Exception as e: Logger.log_error("GroupController.__on_shared_method_group_deleted ex:", e) traceback.print_exc()
def __broadcast_message__scene_updated(self, sceneId): try: scene = self.__sceneController.get_summary(sceneId) except Exception as e: Logger.log_error( "SceneService.__broadcast_message__scene_updated get_summary ex:", e) scene = None eventTag = AppConstants.EVENT_SCENE_UPDATED eventData = {"sceneId": sceneId, "newSceneSummary": scene} self.__broadcast_message(eventTag, eventData) Logger.log_info("Scene Updated:", scene["sceneName"])
def __broadcast_message__rule_updated(self, ruleId): try: rule = self.__ruleController.get_summary(ruleId) except Exception as e: Logger.log_error( "RuleService.__broadcast_message__rule_updated get_summary ex:", e) return eventTag = AppConstants.EVENT_RULE_UPDATED eventData = rule self.__broadcast_message(eventTag, eventData) Logger.log_info("Rule Updated:", rule["ruleName"])
def __parse_section(self, section): ''' Convert if/then into correlated automation tags. ''' section = str(section).lower() mapDict = {AppConstants.SECTION_CONDITION:AppConstants.TAG_CONDITION, AppConstants.SECTION_EXECUTION:AppConstants.TAG_ACTION} try: return mapDict[section] except: Logger.log_error("__parse_section ex: given value(%s), type(%s), allowed values(%s)" % (str(section), str(type(section)), str(mapDict.keys()))) raise AutomationException(11097, "section param must be either value from " + str(mapDict.keys())) # invalid parameter
def __add_scene_execution_result(self, serUrl, serStartTime, serEndTime, sceneName, sceneProtected, sceneId, execution, language): try: SceneExecutionResultService.instance().add_scene_execution_result( serUrl=serUrl, serStartTime=serStartTime, serEndTime=serEndTime, sceneName=sceneName, sceneProtected=sceneProtected, sceneId=sceneId, execution=execution, language=language) except Exception as e: Logger.log_error("SceneService.__add_scene_execution_result ex:", e)
def delete_scene(self, sceneId): self.__verify_scene_updated(sceneId) try: favSort = self.__sceneController.get_favsort_of(sceneId) # To determine should favorited_Scene_deleted broadcasted self.__sceneController.delete(sceneId) self.__sceneController.commit() except Exception as e: self.__sceneController.rollback() traceback.print_exc() Logger.log_error("SceneService delete_scene ex:", e, "-- rolled back") raise AutomationException(11906, "Unable to delete scene, problem - " + str(e)) else: self.__broadcast_message__scene_deleted(sceneId) if favSort is not None: self.__broadcast_message__favorited_scene_deleted(sceneId)
def __update_rule(rule): try: # Fire rule update start event ruleId = rule["ruleId"] # Add methods to subscribe list methodIds = [ kbxMethod["kbxMethodId"] for kbxMethod in rule["condition"] + rule["execution"] ] self.__methodController.add(methodIds) # Update "rule" base table self.__ruleController.update(rule) self.__ruleController.commit() except Exception as e: self.__ruleController.rollback() self.__broadcast_message__rule_update_failed(ruleId, ruleName) Logger.log_error("RuleService __update_rule failed:", e, "-- rolledback") else: self.__triggerController.register_listener( ruleId, rule["trigger"]) # Process for Timer Module TimerModule.delete_scheduler(ruleId) timerModuleHandlers = { TimerModule.METHOD_ID_DATE_TIME_RANGE: TimerModule.handle_date_time_range, TimerModule.METHOD_ID_DAY_OF_WEEK: TimerModule.handle_dow, TimerModule.METHOD_ID_TIME_RANGE: TimerModule.handle_time_range } for kbxMethod in rule["condition"]: kbxMethodId = kbxMethod["kbxMethodId"] timerModuleHandler = timerModuleHandlers.get( kbxMethodId, None) if timerModuleHandler is not None: timerModuleHandler(ruleId, kbxMethod["kbxMethodParams"]) # Broadcast message: completed updating a rule self.__broadcast_message__rule_updated(ruleId)
def execute(self, stmt, bindings=None): ''' Execute only the statement without any return value. ''' with self.__rlock: cursor = self.__con.cursor() try: if bindings is None: cursor.execute(str(stmt)) else: cursor.execute(str(stmt), tuple(bindings)) except Exception as e: Logger.log_error("Database.execute ex:", e) traceback.print_exc() raise e finally: cursor.close()
def __get_default_group_ids(self): try: systemId = SharedMethod.get_system_id() results = SharedMethod.list_shared_method_groups(kbxGroupAppId=[systemId], kbxGroupName=["automation_app", "notification", "service"]) groups = {group.get("kbxGroupName", "_"):group.get("kbxGroupId") for group in results["groupList"]} AppConstants.GROUP_ID_AUTOMATION = groups["automation_app"] AppConstants.GROUP_ID_NOTIFICATION = groups["notification"] AppConstants.GROUP_ID_SERVICE = groups["service"] except Exception as e: Logger.log_error("AutomationApp on_system_connected retrying __get_default_group_ids, ex:", e) traceback.print_exc() raise e
def insert(self, stmt, bindings): ''' The only different from "execute" is that this function returns lastrowid. ''' with self.__rlock: cursor = self.__con.cursor() try: cursor.execute(stmt, bindings) lastrowid = cursor.lastrowid return lastrowid except Exception as e: Logger.log_error("Database.insert ex:", e) Logger.log_debug("Statement:", stmt, "bindings:", bindings) traceback.print_exc() raise e finally: cursor.close()
def insert(self, stmt, bindings): ''' The only different from "execute" is that this function returns lastrowid. ''' with self.__rlock: cursor = self.__con.cursor() try: cursor.execute(stmt, bindings) lastrowid = cursor.lastrowid return lastrowid except Exception as e: Logger.log_error("Database.insert ex:", e) Logger.log_debug("Statement:", stmt, "bindings:", bindings) traceback.print_exc() raise e finally: cursor.close()
def execute(self, stmt, bindings=None): ''' Execute only the statement without any return value. ''' with self.__rlock: cursor = self.__con.cursor() try: if bindings is None: cursor.execute(str(stmt)) else: cursor.execute(str(stmt), tuple(bindings)) except Exception as e: Logger.log_error("Database.execute ex:", e) traceback.print_exc() raise e finally: cursor.close()
def set_favorited_scene(self, sceneId, prevSceneId=None): ''' sceneId - can be either favorited/non-favorited scene, but must be a valid scene id. prevSceneId - must be another favorited scene or error is raised. Both updating and executing doesn't blocks a scene from adding to/removing from favorited list. ''' with self.__fav_lock: # Validation for sceneId. if not self.__sceneController.has(sceneId): raise AutomationException(11092, "sceneId does not belongs to any scene - sceneId provided: " + str(sceneId)) # Because UI display in reversed order, hence their prevSceneId == our nextSceneId nextSceneId = prevSceneId or None # Get favSort before sceneId of nextSceneId if nextSceneId is None: maxFavSort = self.__sceneController.get_largest_favsort_num() # If len of favorited list is 0, maxFavSort = 0 # favSort stores number to be assigned to sceneId. favSort = maxFavSort + 1 else: try: # favSort stores number to be assigned to sceneId. # current favSort of prevSceneId will becomes favSort of sceneId. favSort = self.__sceneController.get_favsort_of(nextSceneId) except Exception as e: raise AutomationException(11092, "prevSceneId does not belongs to any scene - " + \ "prevSceneId provided: " + str(nextSceneId) + ", error: " + str(e)) else: if favSort is None: raise AutomationException(13000, "prevSceneId does not belongs to any favorited scene - " + \ "prevSceneId provided: " + str(nextSceneId) + ", error: " + str(e)) # Update favSort of the scene try: self.__sceneController.update_favorited_scene(sceneId, favSort) self.__sceneController.commit() except Exception as e: self.__sceneController.rollback() Logger.log_error("SceneService.set_favorited_scene failed to update favorited scene, ex:", str(e)) raise AutomationException(13001, "unexpected error: " + str(e)) # Broadcast events self.__broadcast_message__scene_updated(sceneId) self.__broadcast_message__favorited_scene_added(sceneId, prevSceneId)
def __time_range(self, request): try: try: tVal = request.get_value( TimerModule._PARAM_TIME_RANGE.get_kbx_param_name()) startTime = tVal.get_start_time() endTime = tVal.get_end_time() except: # Backward compatible to daily_task kbxTime = KBXTime(kbxParamName="time") startTime = endTime = kbxTime.cast(request.get_arg("time")) execTime = request.get_arg(AppConstants.KEY_CONDITION_TIMESTAMP) execTime = datetime.datetime.fromtimestamp(execTime) execTime = (execTime.hour * 3600) + (execTime.minute * 60) + (execTime.second) if endTime < startTime: checkRange = [(startTime, 86400), (0, endTime + 59)] else: checkRange = [(startTime, endTime + 59)] for startVal, endVal in checkRange: if startVal <= execTime <= endVal: response = True break else: response = False if not response: raise AutomationException(11800) self.send_response({}, request.requestId) except AutomationException as ae: Logger.log_error("TimerModule time range failed on comparison:", str(ae)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message()) except Exception as e: Logger.log_error( "TimerModule time range failed on comparison (unexpected):", str(e)) ae = AutomationException(11099, str(e)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message())
def process_method_list(methodList): #=================================================================== # Basic type validation #=================================================================== if not isinstance(methodList, list): Logger.log_error("RuleService.set_rule: 'condition' and 'execution' must be type of list.") Logger.log_debug("type:", type(methodList), "value:", methodList) raise AutomationException(11704, "List is required for both 'condition' and 'execution'") #=================================================================== # Check allowed size, raise error if exceeded. #=================================================================== methodListLen = len(methodList) if methodListLen > AppConstants.MAX_METHOD_SIZE: Logger.log_error("RuleService.set_rule: 'condition' and 'execution' cannot have more than", AppConstants.MAX_METHOD_SIZE, "items respectively.") raise AutomationException(11705, "Only a maximum of " + \ str(AppConstants.MAX_METHOD_SIZE) + \ " items is allowed for each 'condition' and 'execution' - given size " + \ str(methodListLen), lambda text: str(AppConstants.MAX_METHOD_SIZE).join(text.split(":max_item_size:"))) #=================================================================== # Check if all kbxMethodIds are valid and all kbxMethodParams are list #=================================================================== idValidator = NumberValidator(isRequired=True, decimalPoint=False) if not all([idValidator.is_valid(eachMethod["kbxMethodId"]) and isinstance(eachMethod["kbxMethodParams"], list) for eachMethod in methodList]): raise AutomationException(11704, "'condition' and 'execution' have incorrect data structure.") #=================================================================== # Check if all kbxParamName and kbxParamCurrentValue exists #=================================================================== paramNameValidator = StringValidator(isRequired=True) for eachMethod in methodList: methodArgs = eachMethod["kbxMethodParams"] for methodArg in methodArgs: if not paramNameValidator.is_valid(methodArg[AppConstants.ARG_NAME]): raise AutomationException(11704, "'condition' and 'execution' have invalid params structure") if not AppConstants.ARG_CURRENT_VALUE in methodArg: methodArg[AppConstants.ARG_CURRENT_VALUE] = None return methodList
def execute_and_fetch_all(self, stmt, bindings=None): ''' Returns a list of sqlite.Row instances. The "list" if empty if no result was fetched. ''' with self.__rlock: cursor = self.__con.cursor() try: if bindings is None: cursor.execute(str(stmt)) else: cursor.execute(str(stmt), tuple(bindings)) return cursor.fetchall() except Exception as e: Logger.log_error("Database.execute_and_fetch_all ex:", e) traceback.print_exc() raise e finally: cursor.close()
def execute_and_fetch_one(self, stmt, bindings=None): ''' Returns an sqlite.Row instance. Return value is None if the statement fetch no result. ''' with self.__rlock: cursor = self.__con.cursor() try: if bindings is None: cursor.execute(str(stmt)) else: cursor.execute(str(stmt), tuple(bindings)) return cursor.fetchone() except Exception as e: Logger.log_error("Database.execute_and_fetch_all ex:", e) traceback.print_exc() raise e finally: cursor.close()
def execute_and_fetch_one(self, stmt, bindings=None): ''' Returns an sqlite.Row instance. Return value is None if the statement fetch no result. ''' with self.__rlock: cursor = self.__con.cursor() try: if bindings is None: cursor.execute(str(stmt)) else: cursor.execute(str(stmt), tuple(bindings)) return cursor.fetchone() except Exception as e: Logger.log_error("Database.execute_and_fetch_all ex:", e) traceback.print_exc() raise e finally: cursor.close()
def delete_scene(self, sceneId): self.__verify_scene_updated(sceneId) try: favSort = self.__sceneController.get_favsort_of( sceneId ) # To determine should favorited_Scene_deleted broadcasted self.__sceneController.delete(sceneId) self.__sceneController.commit() except Exception as e: self.__sceneController.rollback() traceback.print_exc() Logger.log_error("SceneService delete_scene ex:", e, "-- rolled back") raise AutomationException( 11906, "Unable to delete scene, problem - " + str(e)) else: self.__broadcast_message__scene_deleted(sceneId) if favSort is not None: self.__broadcast_message__favorited_scene_deleted(sceneId)
def execute_and_fetch_all(self, stmt, bindings=None): ''' Returns a list of sqlite.Row instances. The "list" if empty if no result was fetched. ''' with self.__rlock: cursor = self.__con.cursor() try: if bindings is None: cursor.execute(str(stmt)) else: cursor.execute(str(stmt), tuple(bindings)) return cursor.fetchall() except Exception as e: Logger.log_error("Database.execute_and_fetch_all ex:", e) traceback.print_exc() raise e finally: cursor.close()
def __retry_seri_implementation(self, serId, serisWithError, seriIndexes): try: # Update statuses of all items to be retried to "busy". self.__serController.update_seri_status(serId=serId, seriIndexes=seriIndexes, seriStatus="busy", seriError=None) # Broadcast event. self.__broadcast_message__seri_retry_started(serId, seriIndexes=seriIndexes) # Retry process starts here. for seri in serisWithError: try: kbxMethodId = seri["kbxMethodId"] if kbxMethodId != -291: # Skips all -291 and declare as "ok" immediately. params = {kbxMethodParam["kbxParamName"]:kbxMethodParam["kbxParamCurrentValue"] for kbxMethodParam in seri["kbxMethodParams"]} result = SharedMethod.call_by_method_id(kbxMethodId, **params) seriError = str(result) else: seriError = None seriStatus = "ok" except Exception as e: seriStatus = "error" seriError = str(e) finally: seriIndex = seri["seriIndex"] if not self.__serLock.is_deleted(serId): # Update statuses and broadcast events only if ser is not deleted. self.__serController.update_seri_status(serId=serId, seriIndexes=[seriIndex], seriStatus=seriStatus, seriError=seriError) self.__broadcast_message__seri_retry_completed(serId, seriIndex, seriStatus, seriError) except Exception as e: ''' THIS PORTION SHOULD NEVER RUN. (it's bug if this portion is executed) ''' Logger.log_error("SceneExecutionResultService.retry_scene_execution_result_item ex:", e) finally: self.__serLock.unlock(serId)
def __update_scene(scene): try: # Fire scene update start event sceneId = scene["sceneId"] sceneName = scene["sceneName"] # Add methods to subscribe list methodIds = [kbxMethod["kbxMethodId"] for kbxMethod in scene["execution"]] self.__methodController.add(methodIds) # Update "scene" base table self.__sceneController.update(scene) self.__sceneController.commit() except Exception as e: self.__sceneController.rollback() self.__broadcast_message__scene_update_failed(sceneId, sceneName) Logger.log_error("SceneService __update_scene failed:", e, "-- rolledback") else: # Broadcast message: completed updating a scene self.__broadcast_message__scene_updated(sceneId)
def __day_of_week(self, request): try: dows = request.get_value(TimerModule._PARAM_DOW.get_kbx_param_name()) execTime = request.get_arg(AppConstants.KEY_CONDITION_TIMESTAMP) execTime = datetime.datetime.fromtimestamp(execTime) execDow = execTime.isoweekday() % 7 response = bool(execDow in dows) if not response: raise AutomationException(11800) self.send_response({}, request.requestId) except AutomationException as ae: Logger.log_error("TimerModule day_of_week failed on comparison:", str(ae)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message()) except Exception as e: Logger.log_error("TimerModule day_of_week failed on comparison (unexpected):", str(e)) ae = AutomationException(11099, str(e)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message())
def __time_range(self, request): try: try: tVal = request.get_value(TimerModule._PARAM_TIME_RANGE.get_kbx_param_name()) startTime = tVal.get_start_time() endTime = tVal.get_end_time() except: # Backward compatible to daily_task kbxTime = KBXTime(kbxParamName="time") startTime = endTime = kbxTime.cast(request.get_arg("time")) execTime = request.get_arg(AppConstants.KEY_CONDITION_TIMESTAMP) execTime = datetime.datetime.fromtimestamp(execTime) execTime = (execTime.hour * 3600) + (execTime.minute * 60) + (execTime.second) if endTime < startTime: checkRange = [(startTime, 86400), (0, endTime + 59)] else: checkRange = [(startTime, endTime + 59)] for startVal, endVal in checkRange: if startVal <= execTime <= endVal: response = True break else: response = False if not response: raise AutomationException(11800) self.send_response({}, request.requestId) except AutomationException as ae: Logger.log_error("TimerModule time range failed on comparison:", str(ae)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message()) except Exception as e: Logger.log_error("TimerModule time range failed on comparison (unexpected):", str(e)) ae = AutomationException(11099, str(e)) self.send_response({}, request.requestId, ae.get_error_code(), ae.get_error_message())
def __get_default_group_ids(self): try: systemId = SharedMethod.get_system_id() results = SharedMethod.list_shared_method_groups( kbxGroupAppId=[systemId], kbxGroupName=["automation_app", "notification", "service"]) groups = { group.get("kbxGroupName", "_"): group.get("kbxGroupId") for group in results["groupList"] } AppConstants.GROUP_ID_AUTOMATION = groups["automation_app"] AppConstants.GROUP_ID_NOTIFICATION = groups["notification"] AppConstants.GROUP_ID_SERVICE = groups["service"] except Exception as e: Logger.log_error( "AutomationApp on_system_connected retrying __get_default_group_ids, ex:", e) traceback.print_exc() raise e
def initialize(self): currentVersion = self.get_user_version() for updateToVersion, (__runOnce, __run) in enumerate(zip(self.__runOnce, self.__run), 1): # First version = 1, not 0. if updateToVersion > currentVersion: # Performs run once function call on later versions only. try: __runOnce("/".join((Database.RESOURCE_PATH, str(updateToVersion)))) except Exception as e: Logger.log_error("Error on updating to version", updateToVersion, "ex:", e) raise Exception("Failed to initialize database: error on run-once function for version " + str(updateToVersion)) # Executes all custom functions try: __run() except Exception as e: Logger.log_error("Error on version", updateToVersion, "function execution, ex:", e) raise Exception("Failed to initialize database: error on run-everytime function for version " + str(updateToVersion)) else: del(self.__runOnce) del(self.__run) self.set_user_version(updateToVersion)
def list_methods(self, language, section, groupId): try: result = SharedMethod.list_shared_methods(kbxGroupId=groupId, kbxMethodTag=section, kbxMethodStatus=[SharedMethod.METHOD_STATUS_ACTIVE, SharedMethod.METHOD_STATUS_INACTIVE], language=language) methodList = result["methodList"] #=================================================================== # Get group information #=================================================================== groupDict = SharedMethod.get_shared_method_group_by_id(kbxGroupId=groupId, language=language) # Append "kbxMethodHasEvent" indicator. for kbxMethod in methodList: kbxMethodEvent = kbxMethod.get("kbxMethodEvent") kbxMethodIdentifier = kbxMethod.get("kbxMethodIdentifier") kbxMethod["kbxMethodHasEvent"] = not Util.is_empty(kbxMethodEvent) and not Util.is_empty(kbxMethodIdentifier) return methodList, groupDict except Exception as e: Logger.log_error("APIService.list_methods ex:", e) raise AutomationException(11601, "Unexpected error - " + str(e))
def __execute_scene_implementation(self, sceneId, execution, serObj, language): ''' ** Call execute_scene; DO NOT call this function directly. ''' Logger.log_info("execute scene id:", sceneId) def execution_func(sceneThreadEvent, kbxMethodId, seri, **kwargs): try: if kbxMethodId == -291: # Delay Timer delayInSec = kwargs["delayInSec"] sceneThreadEvent.wait(delayInSec) seri["seriError"] = None else: # Execute method result = SharedMethod.call(**kwargs) seri["seriError"] = str(result) seri["seriStatus"] = "ok" except Exception as e: seri["seriStatus"] = "error" seri["seriError"] = str(e) Logger.log_debug("Execution failed, method:", kwargs["kbxMethodName"]) finally: sceneThreadEvent.set() try: # Record for debugging purpose serStartTime = time.time() #=================================================================== # Prepare to execute execution methods #=================================================================== sceneExecLock = self.__sceneExecLocks.get(sceneId) sceneThreadEvent = sceneExecLock.get_thread_event() seris = deque() methodExecTime = int(time.time()) isLoopCompleted = True for row in execution: kbxMethodId = row["kbxMethodId"] kbxMethodName = row["kbxMethodName"] kbxGroupId = row["kbxGroupId"] kbxMethodStatus = row["kbxMethodStatus"] methodParamsWithCurrentValues = row["kbxMethodParams"] seri = {"kbxMethodId":kbxMethodId, "kbxGroupId":kbxGroupId, "kbxMethodName":kbxMethodName, "kbxMethodParams":methodParamsWithCurrentValues} seris.append(seri) # Check is stop if sceneExecLock.is_stop(): if not isLoopCompleted: serEndTime = time.time() isLoopCompleted = False # === Execution interrupted === continue # Check is method not removed elif kbxMethodStatus not in (SharedMethod.METHOD_STATUS_ACTIVE, SharedMethod.METHOD_STATUS_INACTIVE): seri["seriStatus"] = "error" seri["seriError"] = "method is removed" continue kwargs = {methodParam[AppConstants.ARG_NAME]: methodParam[AppConstants.ARG_CURRENT_VALUE] for methodParam in methodParamsWithCurrentValues} if AppInfo.REQUEST_KEY_LANGUAGE not in kwargs: kwargs[AppInfo.REQUEST_KEY_LANGUAGE] = AppInfo.DEFAULT_API_LANGUAGE kwargs["kbxMethodName"] = kbxMethodName kwargs["kbxGroupId"] = kbxGroupId kwargs["kbxModuleName"] = row["kbxModuleName"] kwargs["kbxMethodAppId"] = row["kbxMethodAppId"] kwargs[AppConstants.KEY_ACTION_TIMESTAMP] = methodExecTime #=========================================================== # Execute method #=========================================================== sceneThreadEvent.clear() execThread = threading.Thread(target=execution_func, args=[sceneThreadEvent, row["kbxMethodId"], seri], kwargs=kwargs) execThread.daemon = False execThread.start() sceneThreadEvent.wait() # Event will be set by "stop_scene" or SharedMethod.call returns. # Check is stop if sceneExecLock.is_stop(): if not isLoopCompleted: serEndTime = time.time() isLoopCompleted = False # === Execution interrupted === continue if isLoopCompleted: # Record for debugging purpose serEndTime = time.time() # === Execution completed === except Exception as e: # This is unexpected error. Execution will not be recorded by Scene Execution Result. Logger.log_error("SceneService.__execute_scene_implementation ex:", e) # === Execution completed with errors === else: if serObj is not None: # Log to sceneExecutionResultService self.__sceneExecutionResultThreadPool.submit(self.__add_scene_execution_result, serUrl=serObj["serUrl"], serStartTime=serStartTime, serEndTime=serEndTime, sceneName=serObj["sceneName"], sceneProtected=serObj["sceneProtected"], sceneId=sceneId, execution=seris, language=language) finally: del(self.__sceneExecLocks[sceneId]) # Delete exec lock sceneExecLock.release()
def __execute_scene_implementation(self, sceneId, execution, serObj, language): ''' ** Call execute_scene; DO NOT call this function directly. ''' Logger.log_info("execute scene id:", sceneId) def execution_func(sceneThreadEvent, kbxMethodId, seri, **kwargs): try: if kbxMethodId == -291: # Delay Timer delayInSec = kwargs["delayInSec"] sceneThreadEvent.wait(delayInSec) seri["seriError"] = None else: # Execute method result = SharedMethod.call(**kwargs) seri["seriError"] = str(result) seri["seriStatus"] = "ok" except Exception as e: seri["seriStatus"] = "error" seri["seriError"] = str(e) Logger.log_debug("Execution failed, method:", kwargs["kbxMethodName"]) finally: sceneThreadEvent.set() try: # Record for debugging purpose serStartTime = time.time() #=================================================================== # Prepare to execute execution methods #=================================================================== sceneExecLock = self.__sceneExecLocks.get(sceneId) sceneThreadEvent = sceneExecLock.get_thread_event() seris = deque() methodExecTime = int(time.time()) isLoopCompleted = True for row in execution: kbxMethodId = row["kbxMethodId"] kbxMethodName = row["kbxMethodName"] kbxGroupId = row["kbxGroupId"] kbxMethodStatus = row["kbxMethodStatus"] methodParamsWithCurrentValues = row["kbxMethodParams"] seri = { "kbxMethodId": kbxMethodId, "kbxGroupId": kbxGroupId, "kbxMethodName": kbxMethodName, "kbxMethodParams": methodParamsWithCurrentValues } seris.append(seri) # Check is stop if sceneExecLock.is_stop(): if not isLoopCompleted: serEndTime = time.time() isLoopCompleted = False # === Execution interrupted === continue # Check is method not removed elif kbxMethodStatus not in ( SharedMethod.METHOD_STATUS_ACTIVE, SharedMethod.METHOD_STATUS_INACTIVE): seri["seriStatus"] = "error" seri["seriError"] = "method is removed" continue kwargs = { methodParam[AppConstants.ARG_NAME]: methodParam[AppConstants.ARG_CURRENT_VALUE] for methodParam in methodParamsWithCurrentValues } if AppInfo.REQUEST_KEY_LANGUAGE not in kwargs: kwargs[AppInfo. REQUEST_KEY_LANGUAGE] = AppInfo.DEFAULT_API_LANGUAGE kwargs["kbxMethodName"] = kbxMethodName kwargs["kbxGroupId"] = kbxGroupId kwargs["kbxModuleName"] = row["kbxModuleName"] kwargs["kbxMethodAppId"] = row["kbxMethodAppId"] kwargs[AppConstants.KEY_ACTION_TIMESTAMP] = methodExecTime #=========================================================== # Execute method #=========================================================== sceneThreadEvent.clear() execThread = threading.Thread( target=execution_func, args=[sceneThreadEvent, row["kbxMethodId"], seri], kwargs=kwargs) execThread.daemon = False execThread.start() sceneThreadEvent.wait( ) # Event will be set by "stop_scene" or SharedMethod.call returns. # Check is stop if sceneExecLock.is_stop(): if not isLoopCompleted: serEndTime = time.time() isLoopCompleted = False # === Execution interrupted === continue if isLoopCompleted: # Record for debugging purpose serEndTime = time.time() # === Execution completed === except Exception as e: # This is unexpected error. Execution will not be recorded by Scene Execution Result. Logger.log_error("SceneService.__execute_scene_implementation ex:", e) # === Execution completed with errors === else: if serObj is not None: # Log to sceneExecutionResultService self.__sceneExecutionResultThreadPool.submit( self.__add_scene_execution_result, serUrl=serObj["serUrl"], serStartTime=serStartTime, serEndTime=serEndTime, sceneName=serObj["sceneName"], sceneProtected=serObj["sceneProtected"], sceneId=sceneId, execution=seris, language=language) finally: del (self.__sceneExecLocks[sceneId]) # Delete exec lock sceneExecLock.release()