async def startSim(): global client, stack # change entries to modify test beds = [ ("W1", "1"), ("W1", "2"), ("W1", "3"), ("W1", "4"), ] async with AsyncExitStack() as stack: # Connect to the MQTT broker client = Client(BROKER_ADDRESS) await stack.enter_async_context(client) tasks = set() stack.push_async_callback(cancel_tasks, tasks) outbound_topics = [ "+/+/patientDetails", "+/+/HR", "+/+/spO2", "+/+/diaBP", "+/+/sysBP", "+/+/ppg", "+/+/ecg", ] for ot in outbound_topics: manager = client.filtered_messages(ot) messages = await stack.enter_async_context(manager) template = f'Outbound -- [topic="{{}}"] {{}}' task = asyncio.create_task(log_messages(messages, template)) tasks.add(task) inbound_topics = ["+/+/sendDetails"] for it in inbound_topics: manager = client.filtered_messages(it) messages = await stack.enter_async_context(manager) template = f'Inbound -- [topic="{{}}"] {{}}' task = asyncio.create_task(log_messages(messages, template)) tasks.add(task) # Messages that doesn't match a filter will get logged here messages = await stack.enter_async_context( client.unfiltered_messages()) task = asyncio.create_task( log_messages(messages, f'Other -- [topic="{{}}"] {{}}')) tasks.add(task) await client.subscribe('#') # subscribe to all messages for bed in beds: tasks.add(asyncio.create_task(onboardPatient(bed, client))) tasks.add(asyncio.create_task(startHRProducer(bed, client))) tasks.add(asyncio.create_task(startBPProducer(bed, client))) tasks.add(asyncio.create_task(startSpO2Producer(bed, client))) tasks.add(asyncio.create_task(startECGProducer(bed, client))) await asyncio.gather(*tasks)
async def connect_to_mqtt_server(config): async with AsyncExitStack() as stack: # Keep track of the asyncio tasks that we create, so that # we can cancel them on exit tasks = set() stack.push_async_callback(cancel_tasks, tasks) if config.has_section('MQTT'): # Connect to the MQTT broker # client = Client("10.0.1.20", username="******", password="******") client = Client(config.get('MQTT', 'broker_address'), username=config.get('MQTT', 'usr'), password=config.get('MQTT', 'pswd')) await stack.enter_async_context(client) # Create angle fsm fsm = AngleFSM(config) # Messages that doesn't match a filter will get logged here messages = await stack.enter_async_context( client.unfiltered_messages()) task = asyncio.create_task( log_messages(messages, "[unfiltered] {}")) tasks.add(task) # Subscribe to chairState await client.subscribe("sensors/chairState") manager = client.filtered_messages('sensors/chairState') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_sensors_chair_state(client, messages, fsm)) tasks.add(task) # Subscribe to config notification settings changes await client.subscribe("goal/update_data") manager = client.filtered_messages('goal/update_data') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_goal_update_data(client, messages, fsm)) tasks.add(task) # Start periodic publish of chair state task = asyncio.create_task(publish_angle_fsm(client, fsm)) tasks.add(task) # Wait for everything to complete (or fail due to, e.g., network errors) await asyncio.gather(*tasks)
async def advanced_example(): # We 💛 context managers. Let's create a stack to help # us manage them. async with AsyncExitStack() as stack: # Keep track of the asyncio tasks that we create, so that # we can cancel them on exit tasks = set() stack.push_async_callback(cancel_tasks, tasks) tls_ctx = ssl.create_default_context( cafile="/srv/security/iot-hub-ca.pem") tls_ctx.load_cert_chain( "/srv/security/device-home-office-crt.pem", keyfile="/srv/security/device-home-office-key.pem", ) # Connect to the MQTT broker client = Client( "iot.fr-par.scw.cloud", port=8883, client_id="50ce1f12-ef04-4e25-806c-3c51aa3f044a", tls_context=tls_ctx, ) await stack.enter_async_context(client) # You can create any number of topic filters topic_filters = ( "floors/+/humidity", "floors/rooftop/#" # 👉 Try to add more filters! ) for topic_filter in topic_filters: # Log all messages that matches the filter manager = client.filtered_messages(topic_filter) messages = await stack.enter_async_context(manager) template = f'[topic_filter="{topic_filter}"] {{}}' task = asyncio.create_task(log_messages(messages, template)) tasks.add(task) # Messages that doesn't match a filter will get logged here messages = await stack.enter_async_context( client.unfiltered_messages()) task = asyncio.create_task(log_messages(messages, "[unfiltered] {}")) tasks.add(task) # Subscribe to topic(s) # 🤔 Note that we subscribe *after* starting the message # loggers. Otherwise, we may miss retained messages. await client.subscribe("floors/#") # Publish a random value to each of these topics topics = ( "floors/basement/humidity", "floors/rooftop/humidity", "floors/rooftop/illuminance", # 👉 Try to add more topics! ) task = asyncio.create_task(post_to_topics(client, topics)) tasks.add(task) # Wait for everything to complete (or fail due to, e.g., network # errors) await asyncio.gather(*tasks)
async def listen(self): async with AsyncExitStack() as stack: # Track tasks tasks = set() stack.push_async_callback(cancel_tasks, tasks) # Connect to the MQTT broker client = Client(self.host, self.port, username=self.username, password=self.password) await stack.enter_async_context(client) logging.info(f'MQTT client connected') # Add tasks for each data source handler for ds in self.data_sources: # Get handlers from data source ds_listeners = ds.listeners() # Iterate through data source listeners and convert to # 'prime' listeners for each topic for listener in ds_listeners: topic = listener.topic funcs = listener.handlers if topic in self.topics: # Add these handlers to existing top level topic handler logging.debug( f'Adding handlers for existing prime Listener: {topic}' ) ext_topic = self.topics[topic] ext_topic.handlers.extend(funcs) else: # Add this instance as a new top level handler logging.debug( f'Creating new prime Listener for topic: {topic}') self.topics[topic] = MQTTListener(topic, funcs) # Add handlers for each topic as a filtered topic for topic, listener in self.topics.items(): manager = client.filtered_messages(topic) messages = await stack.enter_async_context(manager) task = asyncio.create_task(self.parse_messages(messages)) tasks.add(task) # Subscribe to all topics # Assume QoS 0 for now all_topics = [(t, 0) for t in self.topics.keys()] logging.info(f'Subscribing to MQTT {len(all_topics)} topic(s)') logging.debug(f'Topics: {all_topics}') try: await client.subscribe(all_topics) except ValueError as err: logging.error(f'MQTT Subscribe error: {err}') # Gather all tasks await asyncio.gather(*tasks) logging.info(f'Listening for MQTT updates')
async def handler(mqtt_hostname, mqtt_port, logger, context, topics, router): # defining an AsyncExitStack as stack to use with the asynccontextmanager async with AsyncExitStack() as stack: #set up a set of tasks the asyncio shall work through tasks = set() #defining what the asynccontextmanager shall do upon exit. stack.push_async_callback(cancel_tasks, tasks) #using the clientID as both clientID and base topic clientID = topics["clientID"] #TODO: Fix the asyncio_MQTT library with tuples to pass the certificates. #initializing a MQTT client LOG.info("Connect to MQTT-broker") client = Client(mqtt_hostname, port=mqtt_port, logger=logger, tls_context=context, client_id=clientID) router = teltonikaRUT9x(router["hostname"], router["username"], router["password"]) position = ColumbusV800() #putting the client into the asynccontextmanager await stack.enter_async_context(client) #Loop through all topics that we shall subscribe to topic_filters = topics["subscribe"] for topic_filter in topic_filters: #Use the filtered_messages to make an async generator manager = client.filtered_messages(topic_filter) #put the generator into the asynccontextmanager messages = await stack.enter_async_context(manager) template = f'[topic_filter="{topic_filter}"] {{}}' #set up a task task = asyncio.create_task(log_messages(messages, template)) tasks.add(task) #Make a list of topics with Qos 1 to feed into the client.subscribe subscribe_topics = [] for i in topic_filters: subscribe_topics.append((i, 1)) await client.subscribe(subscribe_topics) #create a task that sends the position on the MQTT with the clientID as a basis for topic task = asyncio.create_task( sendPosition(client, position, 0.2, clientID)) tasks.add(task) #add the signallogging to the async tasks task = asyncio.create_task(logSignal(router, 1)) tasks.add(task) # Wait for everything to complete (or fail due to, e.g., network # errors) await asyncio.gather(*tasks)
async def advanced_example(): # We 💛 context managers. Let's create a stack to help # us manage them. async with AsyncExitStack() as stack: # Keep track of the asyncio tasks that we create, so that # we can cancel them on exit tasks = set() stack.push_async_callback(cancel_tasks, tasks) # Connect to the MQTT broker client = Client(broker) await stack.enter_async_context(client) # You can create any number of topic filters topic_filters = ( "Battle/#/Trainer1/", "Battle/#/Trainer2/" # 👉 Try to add more filters! ) for topic_filter in topic_filters: # Log all messages that matches the filter manager = client.filtered_messages(topic_filter) messages = await stack.enter_async_context(manager) template = f'[topic_filter="{topic_filter}"] {{}}' task = asyncio.create_task(log_messages(messages, template)) tasks.add(task) # Messages that doesn't match a filter will get logged here messages = await stack.enter_async_context( client.unfiltered_messages()) task = asyncio.create_task(log_messages(messages, "[unfiltered] {}")) tasks.add(task) # Subscribe to topic(s) # 🤔 Note that we subscribe *after* starting the message # loggers. Otherwise, we may miss retained messages. await client.subscribe("Battle/#") # Publish a random value to each of these topics topics = ( "Battle/BattleLog", "Battle/Trainer1/", "Battle/Trainer2/", # 👉 Try to add more topics! ) task = asyncio.create_task(post_to_topics(client, topics)) tasks.add(task) # Wait for everything to complete (or fail due to, e.g., network # errors) await asyncio.gather(*tasks)
async def mqtt_listen(mq: MqttClient, q: asyncio.Queue, topic=MQTT_TOPIC_CMND): await mq.subscribe(topic) async with mq.filtered_messages(topic) as messages: async for message in messages: topic = message.topic data = message.payload.decode() _log.debug(f"MQTT incoming [{topic}]: {data}") msg = parse_mqtt_msg(topic, data) if not msg: continue try: cmnd = process_mqtt_msg(topic, msg) except Exception: cmnd = None _log.exception(f"while processing message [{topic}]: {msg}") if not cmnd: continue await q.put(cmnd)
async def handle_messages(self,client:Client): """Hier stop je logica in die inkomende berichten leest en vervolgens jouw knx blind aanstuurt. """ topic = f"{self._topic}/#" _LOGGER.debug(f"Start watching: {self._topic}") async with client.filtered_messages(topic) as messages: async for message in messages: data = json.loads(message.payload.decode("utf-8")) _LOGGER.debug("topic: %s data: %s",message.topic,data) if message.topic == f"{self._topic}/pos_ang": _LOGGER.debug("Angle position topic found.") position = data["pos"] angle=data["ang"] await self.handle_position_angle(position,angle) else: _LOGGER.error("Incoming message with unknown topic: %s",message.topic) await self.handle_unknown_message_type(**data) _LOGGER.debug("Finished.")
async def bed_loop(ble): async with AsyncExitStack() as stack: # Keep track of the asyncio tasks that we create, so that # we can cancel them on exit tasks = set() stack.push_async_callback(cancel_tasks, tasks) # Connect to the MQTT broker client = Client( MQTT_SERVER, port=MQTT_SERVER_PORT, username=MQTT_USERNAME, password=MQTT_PASSWORD, ) await stack.enter_async_context(client) # Set up the topic filter manager = client.filtered_messages(MQTT_TOPIC) messages = await stack.enter_async_context(manager) task = asyncio.create_task(bed_command(ble, messages)) tasks.add(task) # Subscribe to topic(s) await client.subscribe(MQTT_TOPIC) # let everyone know we are online if DEBUG: print("Going online") await client.publish(MQTT_CHECKIN_TOPIC, MQTT_ONLINE_PAYLOAD, qos=1) # let everyone know we are still alive task = asyncio.create_task( check_in(client, MQTT_CHECKIN_TOPIC, MQTT_CHECKIN_PAYLOAD) ) tasks.add(task) # Wait for everything to complete (or fail due to, e.g., network # errors) await asyncio.gather(*tasks)
class MQTTProxy(Base): def __init__(self, hub, server, prefix="fakesilvia/", debug_mappings=False): super().__init__(hub) self.prefix = prefix self.client = Client(server) self.client._client.will_set(self.write_topic('LWT'), 'Offline', retain=True) self.last_send = {} self.mappings = { topics.TOPIC_AVERAGE_BOILER_TEMPERATURE: Mapping('avgtemp', formatter=Mapping.FloatFormatter, throttle=3), topics.TOPIC_CURRENT_GROUP_TEMPERATURE: Mapping('group_temp', formatter=Mapping.FloatFormatter, throttle=3), topics.TOPIC_PID_AVERAGE_VALUE: Mapping('avgpid', formatter=Mapping.FloatFormatter, throttle=3), topics.TOPIC_SCALE_WEIGHT: Mapping('scale_weight', formatter=Mapping.FloatFormatter, throttle=1), topics.TOPIC_SCALE_CONNECTED: Mapping('scale_is_connected'), topics.TOPIC_CURRENT_BREW_TIME_UPDATE: Mapping('current_brew_time', formatter=Mapping.FloatFormatter), topics.TOPIC_LAST_BREW_DURATION: Mapping('shot_time', formatter=Mapping.FloatFormatter), topics.TOPIC_STEAM_MODE: Mapping('steam_mode', mode=Mapping.MODE_READWRITE), topics.TOPIC_CONNECT_TO_SCALE: Mapping('keep_scale_connected', mode=Mapping.MODE_READWRITE), topics.TOPIC_ENABLE_WEIGHTED_SHOT: Mapping('brew_to_weight', mode=Mapping.MODE_READWRITE), topics.TOPIC_TARGET_WEIGHT: Mapping('target_weight', formatter=Mapping.FloatFormatter, mode=Mapping.MODE_READWRITE), topics.TOPIC_TARGET_RATIO: Mapping('target_ratio', formatter=Mapping.FloatFormatter, mode=Mapping.MODE_READWRITE), topics.TOPIC_USE_PREINFUSION: Mapping('use_preinfusion', mode=Mapping.MODE_READWRITE), topics.TOPIC_PREINFUSION_TIME: Mapping('preinfusion_time', formatter=Mapping.FloatFormatter, mode=Mapping.MODE_READWRITE), topics.TOPIC_DWELL_TIME: Mapping('dwell_time', formatter=Mapping.FloatFormatter, mode=Mapping.MODE_READWRITE), topics.TOPIC_SET_POINT: Mapping('settemp', mode=Mapping.MODE_READWRITE, formatter=Mapping.FloatFormatter), topics.TOPIC_HE_ENABLED: Mapping('is_awake', mode=Mapping.MODE_READWRITE), topics.TOPIC_STEAM_TEMPERATURE_SET_POINT: Mapping('steam_set_point', mode=Mapping.MODE_READWRITE, formatter=Mapping.FloatFormatter), topics.TOPIC_STEAM_TEMPERATURE_DELTA: Mapping('steam_delta', mode=Mapping.MODE_READWRITE, formatter=Mapping.FloatFormatter), topics.TOPIC_PUMP_PIDVAL_FEED_FORWARD: Mapping('pump_feed_forward', mode=Mapping.MODE_READWRITE, formatter=Mapping.FloatFormatter), topics.TOPIC_OLED_SAVER: Mapping('oled_saver', mode=Mapping.MODE_READWRITE, formatter=Mapping.BoolFormatter), topics.TOPIC_CAPTURE_DOSE: Mapping('capture_dose', mode=Mapping.MODE_WRITEONLY, formatter=Mapping.BoolFormatter), topics.TOPIC_SOLENOID_OPEN: Mapping('solenoid', mode=Mapping.MODE_READWRITE), topics.TOPIC_CAPTURE_DOSE_AND_SET_TARGET_WEIGHT: Mapping('capture_dose_set_weight', mode=Mapping.MODE_WRITEONLY, formatter=Mapping.BoolFormatter), } if debug_mappings: self.mappings[topics.TOPIC_COFFEE_BUTTON] = Mapping( "coffee_button", mode=Mapping.MODE_READWRITE) self.mappings[topics.TOPIC_WATER_BUTTON] = Mapping( "water_button", mode=Mapping.MODE_READWRITE) self.mappings[topics.TOPIC_STEAM_BUTTON] = Mapping( "steam_button", mode=Mapping.MODE_READWRITE) async def run_async(self): async with AsyncExitStack() as stack: # Keep track of the asyncio tasks that we create, so that # we can cancel them on exit tasks = set() stack.push_async_callback(self.cancel_tasks, tasks) # Connect to the MQTT broker await stack.enter_async_context(self.client) await self.client.publish(self.write_topic('LWT'), "Online", retain=True) for key in self.mappings: mapping = self.mappings[key] if mapping.mode == Mapping.MODE_READONLY: continue manager = self.client.filtered_messages( self.read_topic(mapping.key)) messages = await stack.enter_async_context(manager) task = asyncio.create_task( self.read_values(messages, key, mapping)) tasks.add(task) await self.client.subscribe(self.read_topic(mapping.key)) pub_task = asyncio.create_task(self.publish_values()) heartbeat_task = asyncio.create_task(self.publish_heartbeat()) tasks.add(pub_task) tasks.add(heartbeat_task) await asyncio.gather(*tasks) async def cancel_tasks(self, tasks): for task in tasks: task.cancel() try: await task except asyncio.CancelledError: pass async def read_values(self, messages, key, mapping): async for message in messages: self.hub.publish( key, mapping.formatter.from_mqtt(message.payload.decode())) async def publish_heartbeat(self): while True: await self.client.publish(self.write_topic('heartbeat'), time.time()) await asyncio.sleep(10) async def publish_values(self): with PubSub.Listener(self.hub) as queue: while True: key, value = await queue.get() try: mapping = self.mappings[key] except KeyError: continue if mapping.mode == Mapping.MODE_WRITEONLY: continue if mapping.throttle: try: last_send = self.last_send[mapping.key] except KeyError: last_send = 0 if last_send + mapping.throttle > time.time(): continue await self.client.publish(self.write_topic(mapping.key), mapping.formatter.to_mqtt(value)) self.last_send[mapping.key] = time.time() def write_topic(self, key): return "{}{}".format(self.prefix, key) def read_topic(self, key): return "{}{}/set".format(self.prefix, key) def futures(self, loop): return [self.run_async()]
async def connect_to_mqtt_server(config): async with AsyncExitStack() as stack: # Keep track of the asyncio tasks that we create, so that # we can cancel them on exit tasks = set() stack.push_async_callback(cancel_tasks, tasks) if config.has_section('MQTT'): # Connect to the MQTT broker # client = Client("10.0.1.20", username="******", password="******") client = Client(config.get('MQTT', 'broker_address'), username=config.get('MQTT', 'usr'), password=config.get('MQTT', 'pswd')) await stack.enter_async_context(client) # Create PCA9536 driver pca = pca9536() # Create Alarm State state = AlarmState(config) # Messages that doesn't match a filter will get logged here messages = await stack.enter_async_context( client.unfiltered_messages()) task = asyncio.create_task( log_messages(messages, "[unfiltered] {}")) tasks.add(task) # sensors/alarm/enabled await client.subscribe("sensors/alarm/enabled") manager = client.filtered_messages('sensors/alarm/enabled') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_alarm_enabled(client, messages, pca, state)) tasks.add(task) # sensors/alarm/alternatingLedBlink await client.subscribe("sensors/alarm/alternatingLedBlink") manager = client.filtered_messages( 'sensors/alarm/alternatingLedBlink') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_alarm_alternating_led_blink(client, messages, pca, state)) tasks.add(task) # sensors/alarm/motorOn await client.subscribe("sensors/alarm/motorOn") manager = client.filtered_messages('sensors/alarm/motorOn') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_alarm_motor_on(client, messages, pca, state)) tasks.add(task) # sensors/alarm/redLedOn await client.subscribe("sensors/alarm/redLedOn") manager = client.filtered_messages('sensors/alarm/redLedOn') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_alarm_red_led_on(client, messages, pca, state)) tasks.add(task) # sensors/alarm/redLedBlink await client.subscribe("sensors/alarm/redLedBlink") manager = client.filtered_messages('sensors/alarm/redLedBlink') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_alarm_red_led_blink(client, messages, pca, state)) tasks.add(task) # sensors/alarm/greenLedOn await client.subscribe("sensors/alarm/greenLedOn") manager = client.filtered_messages('sensors/alarm/greenLedOn') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_alarm_green_led_on(client, messages, pca, state)) tasks.add(task) # sensors/alarm/greenLedBlink await client.subscribe("sensors/alarm/greenLedBlink") manager = client.filtered_messages('sensors/alarm/greenLedBlink') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_alarm_green_led_blink(client, messages, pca, state)) tasks.add(task) # Start periodic publish of alarm state task = asyncio.create_task(alarm_loop(client, pca, state)) tasks.add(task) # Wait for everything to complete (or fail due to, e.g., network errors) await asyncio.gather(*tasks)
class SatelliteController: def __init__(self, cbpi): self.cbpi = cbpi self.client = None async def init(self): asyncio.create_task(self.init_client(self.cbpi)) async def publish(self, topic, message): print("MQTT ON") await self.client.publish(topic, message, qos=1) async def handle_message(self, messages): async for message in messages: print("FILTERED", message.payload.decode()) async def handle_unfilterd_message(self, messages): async for message in messages: print("UNFILTERED", message.payload.decode()) async def init_client(self, cbpi): async def log_messages(messages, template): async for message in messages: print(template.format(message.payload.decode())) async def cancel_tasks(tasks): for task in tasks: if task.done(): continue task.cancel() try: await task except asyncio.CancelledError: pass async with AsyncExitStack() as stack: tasks = set() stack.push_async_callback(cancel_tasks, tasks) self.client = Client("localhost", will=Will(topic="cbpi/diconnect", payload="CBPi Server Disconnected")) await stack.enter_async_context(self.client) topic_filters = ("cbpi/sensor/#", "cbpi/actor/#") for topic_filter in topic_filters: # Log all messages that matches the filter manager = self.client.filtered_messages(topic_filter) messages = await stack.enter_async_context(manager) task = asyncio.create_task(self.handle_message(messages)) tasks.add(task) messages = await stack.enter_async_context( self.client.unfiltered_messages()) task = asyncio.create_task(self.handle_unfilterd_message(messages)) tasks.add(task) await self.client.subscribe("cbpi/#") await asyncio.gather(*tasks)
class SatelliteController: def __init__(self, cbpi): self.cbpi = cbpi self.logger = logging.getLogger(__name__) self.host = cbpi.static_config.get("mqtt_host", "localhost") self.port = cbpi.static_config.get("mqtt_port", 1883) self.username = cbpi.static_config.get("mqtt_username", None) self.password = cbpi.static_config.get("mqtt_password", None) self.client = None self.topic_filters = [ ("cbpi/actor/+/on", self._actor_on), ("cbpi/actor/+/off", self._actor_off) ] self.tasks = set() async def init(self): asyncio.create_task(self.init_client(self.cbpi)) async def publish(self, topic, message, retain=False): if self.client is not None and self.client._connected: try: await self.client.publish(topic, message, qos=1, retain=retain) except: self.logger.warning("Faild to push data via mqtt") async def _actor_on(self, messages): async for message in messages: try: topic_key = message.topic.split("/") await self.cbpi.actor.on(topic_key[2]) except: self.logger.warning("Faild to process actor on via mqtt") async def _actor_off(self, messages): async for message in messages: try: topic_key = message.topic.split("/") await self.cbpi.actor.off(topic_key[2]) except: self.logger.warning("Faild to process actor off via mqtt") def subcribe(self, topic, method): task = asyncio.create_task(self._subcribe(topic, method)) return task async def _subcribe(self, topic, method): while True: try: if self.client._connected.done(): async with self.client.filtered_messages(topic) as messages: await self.client.subscribe(topic) async for message in messages: await method(message.payload.decode()) except asyncio.CancelledError as e: # Cancel self.logger.warning( "Sub CancelledError Exception: {}".format(e)) return except MqttError as e: self.logger.error("Sub MQTT Exception: {}".format(e)) except Exception as e: self.logger.error("Sub Exception: {}".format(e)) # wait before try to resubscribe await asyncio.sleep(5) async def init_client(self, cbpi): async def cancel_tasks(tasks): for task in tasks: if task.done(): continue task.cancel() try: await task except asyncio.CancelledError: pass while True: try: async with AsyncExitStack() as stack: self.tasks = set() stack.push_async_callback(cancel_tasks, self.tasks) self.client = Client(self.host, port=self.port, username=self.username, password=self.password, will=Will(topic="cbpi/diconnect", payload="CBPi Server Disconnected")) await stack.enter_async_context(self.client) for topic_filter in self.topic_filters: topic = topic_filter[0] method = topic_filter[1] manager = self.client.filtered_messages(topic) messages = await stack.enter_async_context(manager) task = asyncio.create_task(method(messages)) self.tasks.add(task) for topic_filter in self.topic_filters: topic = topic_filter[0] await self.client.subscribe(topic) self.logger.info("MQTT Connected to {}:{}".format(self.host, self.port)) await asyncio.gather(*self.tasks) except MqttError as e: self.logger.error("MQTT Exception: {}".format(e)) except Exception as e: self.logger.error("MQTT General Exception: {}".format(e)) await asyncio.sleep(5)
class MQTT(BasePlugin): def __init__(self, daemon, cfg): super(MQTT, self).__init__(daemon, cfg) daemon.knx_read_cbs.append(self.process_knx) daemon.value_direct_cbs.append(self.process_direct) self.poll_interval = "poll_interval" in cfg and cfg["poll_interval"] or 10 self._mqtt_tasks = None self._mqtt_client = None self.status_pending_for_groups = [] async def mqtt_loop(self): while True: try: await self.mqtt_stack() except MqttError as error: log.warning("MqttError: {error}. Reconnecting in {self.poll_interval} seconds.") finally: await asyncio.sleep(self.poll_interval) async def mqtt_stack(self): async with AsyncExitStack() as stack: self._mqtt_tasks = set() stack.push_async_callback(self.cancel_tasks, self._mqtt_tasks) username = self.cfg["user"] or None password = self.cfg["pass"] or None self._mqtt_client = Client(self.cfg["host"],port=self.cfg["port"],username=username,password=password) await stack.enter_async_context(self._mqtt_client) topics = [] for o in self.obj_list: topic = o["topic"] topics.append(topic) manager = self._mqtt_client.filtered_messages(topic) messages = await stack.enter_async_context(manager) task = asyncio.create_task(self.mqtt_handle(messages, o)) self._mqtt_tasks.add(task) for topic in topics: await self._mqtt_client.subscribe(topic) await asyncio.gather(*self._mqtt_tasks) async def mqtt_handle(self, messages, obj): group_value_dict = {} async for message in messages: payload = message.payload.decode() try: jsonobj = loads(payload) except ValueError: jsonobj = None value = str(payload) objects = self._get_objects_by_topic(message.topic) for o in objects: knx_group = o["knx_group"] if jsonobj and "valmap" in o: for key, valdict in o["valmap"].items(): if key in jsonobj: prop = jsonobj[key] if type(valdict) == dict and prop in valdict: value = valdict[prop] else: value = str(prop) else: value = None continue prev_val = o["value"] if value == None: pass elif prev_val != value: o["value"] = value group_value_dict[knx_group] = value log.debug(f"{self.device_name} mqtt topic={message.topic} payload={payload} knx_group={knx_group} updated value {prev_val}=>{value}") if group_value_dict: await self.d.set_group_value_dict(group_value_dict) else: log.debug(f"{self.device_name} mqtt topic={message.topic} payload={payload} knx_group={knx_group} value={value} unchanged, ignored") if knx_group in self.status_pending_for_groups: self.status_pending_for_groups.remove(knx_group) async def cancel_tasks(self): for task in self._mqtt_tasks: if task.done(): continue task.cancel() try: await task except asyncio.CancelledError: pass async def process_direct(self, knx_group, knx_val): debug_msg = f"{self.device_name} process_direct({knx_group}={knx_val})" await self._write_mqtt(knx_group, knx_val, debug_msg) async def process_knx(self, cmd): try: knx_group, knx_val = cmd.strip().split("=") try: o = self.get_obj_by_knxgrp(knx_group) if o["enabled"]: debug_msg = f"{self.device_name} process_knx({knx_group}={knx_val})" await self._write_mqtt(knx_group, knx_val, debug_msg) except StopIteration: pass return True except Exception as e: log.warning("f{self.device_name} couldn't parse KNX command {cmd} ({e}!") return False async def _write_mqtt(self, knx_group, knx_val, debug_msg): if not self._mqtt_client: return objects = [] request_status = False for item in self.obj_list: if item["knx_group"] == knx_group: objects.append(item) request_status = "request_status" in item for o in objects: if "publish_topic" in o: topic = o["publish_topic"] prev_val = o["value"] if "valmap" in o: payload = o["valmap"][knx_val] else: payload = knx_val log.info(f"{debug_msg} topic {topic} updating {prev_val}=>{knx_val} ({payload})") try: await self._mqtt_client.publish(topic, payload, qos=1, retain=True) o["value"] = knx_val except MqttCodeError as error: log.error(f"{debug_msg} MqttCodeError {error} on topic {topic}") if objects and request_status and "status_object" in self.cfg and self._mqtt_client and not knx_group in self.status_pending_for_groups: so = self.cfg["status_object"] delay = so.get("delay", 10.0) topic = so["topic"] payload = so["payload"] await asyncio.sleep(delay) try: await self._mqtt_client.publish(topic, payload, qos=1, retain=True) log.debug(f"{debug_msg} requested status topic {topic} payload=>{payload}") self.status_pending_for_groups.append(knx_group) except MqttCodeError as error: log.error(f"{debug_msg} MqttCodeError {error} on topic {topic}") def _get_objects_by_topic(self, topic): objects = [] for item in self.obj_list: if item["topic"] == topic: objects.append(item) return objects def _run(self): loop_task = self.d.loop.create_task(self.mqtt_loop()) return [loop_task]
async def connect_to_mqtt_server(config): async with AsyncExitStack() as stack: # Keep track of the asyncio tasks that we create, so that # we can cancel them on exit tasks = set() stack.push_async_callback(cancel_tasks, tasks) if config.has_section('MQTT'): # Connect to the MQTT broker # client = Client("10.0.1.20", username="******", password="******") client = Client(config.get('MQTT', 'broker_address'), username=config.get('MQTT', 'usr'), password=config.get('MQTT', 'pswd')) await stack.enter_async_context(client) # Create chair state chair_state = ChairState() # Select topic filters # You can create any number of topic filters topic_filters = ( "sensors/#", # TODO add more filters ) # Log all messages # for topic_filter in topic_filters: # # Log all messages that matches the filter # manager = client.filtered_messages(topic_filter) # messages = await stack.enter_async_context(manager) # template = f'[topic_filter="{topic_filter}"] {{}}' # task = asyncio.create_task(log_messages(messages, template)) # tasks.add(task) # Messages that doesn't match a filter will get logged here messages = await stack.enter_async_context( client.unfiltered_messages()) task = asyncio.create_task( log_messages(messages, "[unfiltered] {}")) tasks.add(task) # Subscribe to pressure sensors await client.subscribe("sensors/pressure") manager = client.filtered_messages('sensors/pressure') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_sensors_pressure(client, messages, chair_state)) tasks.add(task) # Subscribe to angle sensors await client.subscribe("sensors/angle") manager = client.filtered_messages('sensors/angle') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_sensors_angle(client, messages, chair_state)) tasks.add(task) # Subscribe to travel sensors await client.subscribe("sensors/travel") manager = client.filtered_messages('sensors/travel') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_sensors_travel(client, messages, chair_state)) tasks.add(task) # Subscribe to alarm sensors await client.subscribe("sensors/alarm/state") manager = client.filtered_messages('sensors/alarm/state') messages = await stack.enter_async_context(manager) task = asyncio.create_task( handle_sensors_alarm(client, messages, chair_state)) tasks.add(task) # Start periodic publish of chair state task = asyncio.create_task(publish_chair_state( client, chair_state)) tasks.add(task) # Wait for everything to complete (or fail due to, e.g., network errors) await asyncio.gather(*tasks)