def report_lock_status(fixture_id, lock, status): lclient = MeteorClient('ws://127.0.0.1:3000/websocket', auto_reconnect=True) try: lclient.connect() except Exception as e: print (e) try: lclient.call('setLockStatus', [fixture_id, lock, status], callback_function) except Exception as e: print (e) try: lclient.close() except Exception as e: print (e)
def report_sequence_status(fixture_id, cavity, status, progress): lclient = MeteorClient('ws://127.0.0.1:3000/websocket', auto_reconnect=True) try: lclient.connect() except Exception as e: print (e) try: lclient.call('setCavityStatus', [fixture_id, cavity['id'], status, progress], callback_function) except Exception as e: print (e) try: lclient.close() except Exception as e: print (e)
class AoikRocketChatErrbot(ErrBot): """ Errbot backend for Rocket.Chat. The backend logs in as a Rocket.Chat user, receiving and sending messages. """ def __init__(self, config): """ Constructor. :param config: Errbot's config module. :return: None. """ # Call super method super(AoikRocketChatErrbot, self).__init__(config) # Get the backend's config object self._config_obj = getattr(self.bot_config, _CONFIG_OBJ_KEY, None) # Get logging level from env variable or config object log_level = orig_log_level = self._get_config( CONFIG_KEYS.BOT_LOG_LEVEL, None) # If not specified if log_level is None: # Get logging level from config module log_level = orig_log_level = getattr(self.bot_config, CONFIG_KEYS.BOT_LOG_LEVEL, None) # If not specified if log_level is None: # Use default log_level = logging.DEBUG # If the logging level is string, e.g. 'DEBUG'. # This means it may be an attribute name of the `logging` module. if isinstance(log_level, str): # Get attribute value from the `logging` module log_level = getattr(logging, log_level, None) # Error message error_msg = None # If the logging level is not int if not isinstance(log_level, int): # Get message error_msg = 'Config `BOT_LOG_LEVEL` value is invalid: {}'.format( repr(orig_log_level)) # Log message self._log_error(error_msg) # Raise error raise ValueError(error_msg) # Get logger self._logger = logging.getLogger('aoikrocketchaterrbot') # Set logging level self._logger.setLevel(log_level) # Get message msg = '# ----- Created logger -----\nBOT_LOG_LEVEL: {}'.format( log_level) # Log message self._logger.debug(msg) # Get rocket chat server URI self._server_uri = self._get_config(CONFIG_KEYS.SERVER_URI) # If server URI is not specified if self._server_uri is None: # Get message error_msg = 'Missing config `SERVER_URI`.' # Log message self._log_error(error_msg) # Raise error raise ValueError(error_msg) # Get login username self._login_username = self._get_config(CONFIG_KEYS.LOGIN_USERNAME) # If login username is not specified if self._login_username is None: # Get message error_msg = 'Missing config `LOGIN_USERNAME`.' # Log message self._log_error(error_msg) # Raise error raise ValueError(error_msg) # Get login password self._login_password = self._get_config(CONFIG_KEYS.LOGIN_PASSWORD) # If login password is not specified if self._login_password is None: # Get message error_msg = 'Missing config `LOGIN_PASSWORD`.' # Log message self._log_error(error_msg) # Raise error raise ValueError(error_msg) # If login password is not bytes if not isinstance(self._login_password, bytes): # Convert login password to bytes self._login_password = bytes(self._login_password, 'utf-8') # Create login user's identifier object. # # This attribute is required by superclass. # self.bot_identifier = self.build_identifier(self._login_username) # Event set when the the meteor client has done topic subscribing. # # When the event is set, the meteor client is in one of the two states: # - The topic subscribing has succeeded and the meteor client has # started handling messages. # - The topic subscribing has failed and the meteor client has been # closed. # # The rationale is that the loop at 65ZNO uses the meteor client's # `connected` attribute to decide whether continue, and the attribute # value is ready for use only after this event is set. self._subscribing_done_event = Event() # Event set when the meteor client calls the `closed` callback at # 3DMYH. # # The rationale is that the main thread code at 5W6XQ has to wait until # the meteor client is closed and the `closed` callback hooked at 7MOJX # is called. This ensures the cleanup is fully done. # self._meteor_closed_event = Event() @property def mode(self): """ Get mode name. :return: Mode name. """ # Return mode name return 'aoikrocketchaterrbot' def _log_debug(self, msg): """ Log debug message. :param msg: Message to log. :return: None. """ # Log the message self._logger.debug(msg) def __hash__(self): """Bots are now stored as a key in the bot so they need to be hashable.""" return id(self) def _log_error(self, msg): """ Log error message. :param msg: Message to log. :return: None. """ # Log the message self._logger.error(msg) def _get_config(self, key, default=None): """ Get config value from env variable or config object. Env variable takes precedence. :param key: Config key. :param default: Default value. :return: Config value. """ # Get env variable name env_var_name = _ENV_VAR_NAME_PREFIX + key # Get config value from env variable config_value = os.environ.get(env_var_name, None) # If not specified if config_value is None: # If not have config object if self._config_obj is None: # Use default config_value = default # If have config object else: # Get config value from config object config_value = getattr(self._config_obj, key, default) # Return config value return config_value def _get_bool_config(self, key, default=None): """ Get boolean config value from env variable or config object. Env variable takes precedence. :param key: Config key. :param default: Default value. :return: Config value. """ # Get config value config_value = self._get_config(key=key, default=default) # If config value is false. # This aims to handle False, 0, and None. if not config_value: # Return False return False # If config value is not false else: # Get config value's string in lower case config_value_str_lower = str(config_value).lower() # Consider '0', case-insensitive 'false' and 'no' as false, # otherwise as true. return config_value_str_lower not in ['0', 'false', 'no'] def _patch_meteor_client(self): """ Patch meteor client to fix an existing bug. :return: None. """ # Get whether need patch meteor client. Default is True. need_patch = self._get_bool_config(CONFIG_KEYS.PATCH_METEOR_CLIENT, True) # If need patch meteor client if need_patch: # Create `change_data` function def change_data(self, collection, id, fields, cleared): """ Callback called when data change occurred. :param self: CollectionData object. :param collection: Data collection key. :param id: Data item key. :param fields: Data fields changed. :param cleared: Data fields to be cleared. :return None. """ # If the data collection key not exists # # The original `change_data` function assumes it is existing, # but it is not in some cases. # if collection not in self.data: # Add data collection self.data[collection] = {} # If the data item key not exists. # # The original `change_data` function assumes it is existing, # but it is not in some cases. # if id not in self.data[collection]: # Add data item self.data[collection][id] = {} # For each data field changed for key, value in fields.items(): # Add to the data item self.data[collection][id][key] = value # For each data field to be cleared for key in cleared: # Delete from the data item del self.data[collection][id][key] # Store original `change_data`. # # pylint: disable=protected-access CollectionData._orig_change_data = CollectionData.change_data # pylint: enable=protected-access # Replace original `change_data` CollectionData.change_data = change_data def build_identifier(self, username): """ Create identifier object for given username. :param username: Rocket chat user name. :return: RocketChatUser instance. """ # Create identifier object return RocketChatUser(username) def serve_forever(self): """ Run the bot. Called by the Errbot framework. :return: None. """ # Log message self._log_debug('# ----- serve_forever -----') # Patch meteor client self._patch_meteor_client() # Get whether reconnect is enabled reconnect_enabled = self._get_bool_config( CONFIG_KEYS.RECONNECT_ENABLED, default=True, ) try: # Loop while True: try: # Run for once self.serve_once() # If have error except Exception: # Log message self._log_error(('# ----- `serve_once` failed with error' ' -----\n{}').format(format_exc())) # If reconnect is enabled if reconnect_enabled: # Get message msg = ('# ----- Sleep before reconnect -----\n' 'Interval: {:.2f}').format(self._reconnection_delay) # Log message self._log_debug(msg) # Sleep before reconnect self._delay_reconnect() # Log message self._log_debug('# ----- Wake up to reconnect -----') # Continue the loop continue # If reconnect is not enabled else: # Break the loop break # If have `KeyboardInterrupt` except KeyboardInterrupt: # Do not treat as error pass # Always do finally: # Call `shutdown` self.shutdown() # Log message self._log_debug('# ===== serve_forever =====') def serve_once(self): """ Run the bot until the connection is disconnected. :return: None. """ # Log message self._log_debug('# ----- serve_once -----') # Log message self._log_debug(('# ----- Create meteor client -----\n' 'SERVER_URI: {}').format(self._server_uri)) # Create meteor client self._meteor_client = MeteorClient( self._server_uri, # Disable the meteor client's auto reconnect. # Let `serve_forever` handle reconnect. auto_reconnect=False, ) # Log message self._log_debug('# ----- Hook meteor client callbacks -----') # 5DI82 # Hook meteor client `connected` callback self._meteor_client.on('connected', self._meteor_connected_callback) # 2RAYF # Hook meteor client `changed` callback self._meteor_client.on('changed', self._meteor_changed_callback) # 4XIZB # Hook meteor client `added` callback self._meteor_client.on('added', self._meteor_added_callback) # 2JEIK # Hook meteor client `removed` callback self._meteor_client.on('removed', self._meteor_removed_callback) # 32TF2 # Hook meteor client `failed` callback self._meteor_client.on('failed', self._meteor_failed_callback) # 5W6RX # Hook meteor client `reconnected` callback self._meteor_client.on('reconnected', self._meteor_reconnected_callback) # 7MOJX # Hook meteor client `closed` callback self._meteor_client.on('closed', self._meteor_closed_callback) # Clear the event self._subscribing_done_event.clear() # Clear the event self._meteor_closed_event.clear() # Log message self._log_debug('# ----- Connect to meteor server -----') try: # Connect to meteor server. # # If the connecting succeeds, the meteor client's thread will call # `self._meteor_connected_callback` hooked at 5DI82. The login, # topic subscribing, and message handling are run in that thread. # # The main thread merely waits until the meteor client is closed, # meanwhile it calls heartbeat function at interval if specified. # self._meteor_client.connect() # If have error except: # Log message self._log_debug('# ----- Connecting failed -----') # Log message self._log_debug('# ----- Unhook meteor client callbacks -----') # Remove meteor client callbacks self._meteor_client.remove_all_listeners() # Remove meteor client reference self._meteor_client = None # The two events below should not have been set if the connecting # failed. Just in case. # # Clear the event self._subscribing_done_event.clear() # Clear the event self._meteor_closed_event.clear() # Raise the error raise # Get whether heartbeat is enabled heartbeat_enabled = self._get_bool_config( CONFIG_KEYS.HEARTBEAT_ENABLED) try: # Wait until the topic subscribing is done in another thread at # 5MS7A self._subscribing_done_event.wait() # If heartbeat is enabled if heartbeat_enabled: # Get heartbeat function heartbeat_func = self._get_config(CONFIG_KEYS.HEARTBEAT_FUNC) # Assert the function is callable assert callable(heartbeat_func), repr(heartbeat_func) # Get heartbeat interval heartbeat_interval = self._get_config( CONFIG_KEYS.HEARTBEAT_INTERVAL, default=10, ) # Convert heartbeat interval to float heartbeat_interval = float(heartbeat_interval) # 65ZNO # Loop until the meteor client is disconnected while self._meteor_client.connected: # Send heartbeat heartbeat_func(self) # Sleep before sending next heartbeat time.sleep(heartbeat_interval) # 5W6XQ # Wait until the meteor client is closed and the `closed` callback # is called at 3DMYH self._meteor_closed_event.wait() # If have error except: # Close meteor client. # # This will cause `self._meteor_closed_callback` to be called, # which will set the `self._meteor_closed_event` below. # self._meteor_client.close() # See 5W6XQ self._meteor_closed_event.wait() # Raise the error raise # Always do finally: # Log message self._log_debug('# ----- Unhook meteor client callbacks -----') # Remove meteor client callbacks self._meteor_client.remove_all_listeners() # Remove meteor client reference. # # Do this before calling `callback_presence` below so that the # plugins will not be able to access the already closed client. # self._meteor_client = None # Log message self._log_debug('# ----- Call `callback_presence` -----') # Call `callback_presence` self.callback_presence( Presence(identifier=self.bot_identifier, status=OFFLINE)) # Log message self._log_debug('# ----- Call `disconnect_callback` -----') # Call `disconnect_callback` to unload plugins self.disconnect_callback() # Clear the event self._subscribing_done_event.clear() # Clear the event self._meteor_closed_event.clear() # Log message self._log_debug('# ===== serve_once =====') def _meteor_connected_callback(self): """ Callback called when the meteor client is connected. Hooked at 5DI82. :return: None. """ # Log message self._log_debug('# ----- _meteor_connected_callback -----') # Log message self._log_debug( '# ----- Log in to meteor server -----\nUser: {}'.format( self._login_username)) # Log in to meteor server self._meteor_client.login( user=self._login_username, password=self._login_password, # 2I0GP callback=self._meteor_login_callback, ) def _meteor_login_callback(self, error_info, success_info): """ Callback called when the meteor client has succeeded or failed login. Hooked at 2I0GP. :param error_info: Error info. :param success_info: Success info. :return: None. """ # Log message self._log_debug('# ----- _meteor_login_callback -----') # If have error info if error_info is not None: # Get message msg = 'Login failed:\n{}'.format(pformat(error_info, width=1)) # Log message self._log_debug(msg) # Close meteor client. # This will cause `_meteor_closed_callback` be called. self._meteor_client.close() # If not have error info else: # Get message msg = 'Login succeeded:\n{}'.format(pformat(success_info, width=1)) # Log message self._log_debug(msg) # Subscribe to message events self._meteor_client.subscribe( # Topic name name='stream-room-messages', params=[ # All messages from rooms the rocket chat user has joined '__my_messages__', False, ], # 6BKIR callback=self._meteor_subscribe_callback, ) def _meteor_subscribe_callback(self, error_info): """ Callback called when the meteor client has succeeded or failed \ subscribing. Hooked at 6BKIR. :param error_info: Error info. :return: None. """ # Log message self._log_debug('# ----- _meteor_subscribe_callback -----') # If have error info if error_info is not None: # Get message msg = 'Subscribing failed:\n{}'.format(pformat(error_info, width=1)) # Log message self._log_debug(msg) # Close meteor client. # This will cause `self._meteor_closed_callback` to be called. self._meteor_client.close() # If not have error info else: # Log message self._log_debug('Subscribing succeeded.') # Log message self._log_debug('# ----- Call `connect_callback` -----') # Call `connect_callback` to load plugins. # # This is called in meteor client's thread. # Plugins should not assume they are loaded from the main thread. # self.connect_callback() # Log message self._log_debug('# ----- Call `callback_presence` -----') # Call `callback_presence` self.callback_presence( Presence(identifier=self.bot_identifier, status=ONLINE)) # Log message self._log_debug( '# ----- Hook callback `_meteor_changed_callback` -----') # Reset reconnection count self.reset_reconnection_count() # 5MS7A # Set the topic subscribing is done self._subscribing_done_event.set() def _meteor_changed_callback(self, collection, id, fields, cleared): """ Callback called when the meteor client received message. Hooked at 2RAYF. :param collection: Data collection key. :param id: Data item key. :param fields: Data fields changed. :param cleared: Data fields to be cleared. :return: None. """ # If is message event if collection == 'stream-room-messages': # Get `args` value args = fields.get('args', None) # If `args` value is list if isinstance(args, list): # For each message info for msg_info in args: # Get message msg = msg_info.get('msg', None) # If have message if msg is not None: # Get sender info sender_info = msg_info['u'] # Get sender username sender_username = sender_info['username'] # If the sender is not the bot itself if sender_username != self._login_username: # Create sender's identifier object sender_identifier = self.build_identifier( sender_username) # Create extras info extras = { # 2QTGO 'msg_info': msg_info, } # Create received message object msg_obj = Message( body=msg, frm=sender_identifier, to=self.bot_identifier, extras=extras, ) # Log message self._log_debug( '# ----- Call `callback_message` -----') # Call `callback_message` to dispatch the message # to plugins self.callback_message(msg_obj) def _meteor_added_callback(self, collection, id, fields): """ Callback called when the meteor client emits `added` event. Hooked at 4XIZB. :param collection: Data collection key. :param id: Data item key. :param fields: Data fields. :return: None. """ # Log message self._log_debug('# ----- _meteor_added_callback -----') def _meteor_removed_callback(self, collection, id): """ Callback called when the meteor client emits `removed` event. Hooked at 2JEIK. :param collection: Data collection key. :param id: Data item key. :return: None. """ # Log message self._log_debug('# ----- _meteor_removed_callback -----') def _meteor_failed_callback(self): """ Callback called when the meteor client emits `failed` event. Hooked at 32TF2. :return: None. """ # Log message self._log_debug('# ----- _meteor_failed_callback -----') def _meteor_reconnected_callback(self): """ Callback called when the meteor client emits `reconnected` event. Hooked at 5W6RX. :return: None. """ # Log message self._log_debug('# ----- _meteor_reconnected_callback -----') def _meteor_closed_callback(self, code, reason): """ Callback called when the meteor client emits `closed` event. Hooked at 7MOJX. :param code: Close code. :param reason: Close reason. :return: None. """ # Log message self._log_debug( '# ----- _meteor_closed_callback -----\nCode: {}\nReason: {}'. format(code, reason)) # Set the topic subscribing is done self._subscribing_done_event.set() # 3DMYH # Set the meteor client's `closed` event is emitted self._meteor_closed_event.set() def build_reply(self, mess, text=None, private=False, threaded=False): """ Create reply message object. Used by `self.send_simple_reply`. :param mess: The original message object. :param text: Reply message text. :param private: Whether the reply message is private. :return: Message object. """ # Create reply message object reply = Message( body=text, frm=mess.to, to=mess.frm, extras={ # 5QXGV # Store the original message object 'orig_msg': mess }) # Return reply message object return reply def prefix_groupchat_reply(self, message, identifier): """ Add group chat prefix to the message. Used by `self.send` and `self.send_simple_reply`. :param message: Message object to send. :param identifier: The message receiver's identifier object. :return: None. """ # Do nothing def send_rocketchat_message(self, params): """ Send message to meteor server. :param params: RPC method `sendMessage`'s parameters. :return: None. """ # If argument `params` is not list if not isinstance(params, list): # Put it in a list params = [params] # Send message to meteor server self._meteor_client.call( method='sendMessage', params=params, ) def send_message(self, mess): """ Send message to meteor server. Used by `self.split_and_send_message`. `self.split_and_send_message` is used by `self.send` and `self.send_simple_reply`. :param mess: Message object to send. :return: None. """ # Call super method to dispatch to plugins super(AoikRocketChatErrbot, self).send_message(mess) # Get original message object. # # The key is set at 5QXGV and 3YRCT. # orig_msg = mess.extras['orig_msg'] # Get original message info. # # The key is set at 2QTGO # msg_info = orig_msg.extras['msg_info'] # Get room ID room_id = msg_info['rid'] # Send message to meteor server self.send_rocketchat_message(params={ 'rid': room_id, 'msg': mess.body, }) def send( self, identifier, text, in_reply_to=None, groupchat_nick_reply=False, ): """ Send message to meteor server. :param identifier: Receiver's identifier object. :param text: Message text to send. :param in_reply_to: Original message object. :param groupchat_nick_reply: Whether the message to send is group chat. `self.prefix_groupchat_reply` will be called to process the message if it is group chat. :return: None. """ # If the identifier object is not Identifier instance if not isinstance(identifier, Identifier): # Get message error_msg = ( 'Argument `identifier` is not Identifier instance: {}').format( repr(identifier)) # Raise error raise ValueError(error_msg) # If the original message is not given if in_reply_to is None: # Get message error_msg = 'Argument `in_reply_to` must be given.' # Raise error raise ValueError(error_msg) # Create message object msg_obj = Message( body=text, frm=in_reply_to.to, to=identifier, extras={ # 3YRCT # Store the original message object 'orig_msg': in_reply_to, }, ) # Get group chat prefix from config group_chat_prefix = self.bot_config.GROUPCHAT_NICK_PREFIXED # If the receiver is a room if isinstance(identifier, Room): # If have group chat prefix, # or the message is group chat. if group_chat_prefix or groupchat_nick_reply: # Call `prefix_groupchat_reply` to process the message self.prefix_groupchat_reply(msg_obj, in_reply_to.frm) # Send the message self.split_and_send_message(msg_obj) def query_room(self, room): """ Query room info. Not implemented. :param room: Room ID. :return: None. """ # Return None return None def rooms(self): """ Get room list. Not implemented. :return: Empty list. """ # Return empty list return [] def change_presence(self, status=ONLINE, message=''): """
logging.error ('No sensor defined in .sensors') exit() # Begin main application logging.info ('Setting meteor service') global client client = MeteorClient(host) logging.info ('Connecting to service') client.connect() client.on('connected', onConnected) client.on('reconnected', onConnected) try: logging.info ('Setting GPIO and start listening to sensors') GPIO.setmode(GPIO.BCM) for sensor in filter(getInterruptingOnly,sensorList): registerSensorCallback(sensor) while True: for sensor in filter(getTemperatureOnly, sensorList): readTemp(sensor) time.sleep(pollingTime) except KeyboardInterrupt: GPIO.cleanup() client.logout() client.close() logging.warning ('Exiting after cleanup!')