def test_event_context_update(event_setup, monkeypatch): """ Test update command """ syscfg = event_setup() syscfg.get('device').config.set('value', 'oldval') syscfg.get('device').config.save() event = make_event('device', 'update', { 'value': 'newval', 'task_id': 123123 }) with mgr.EventContext(syscfg) as context: monkeypatch.setattr( context, 'confirm_update', lambda *args: { 'task_id': args[0], 'response': args[1] }) result = context.manage(event) devconf = syscfg.get('device').config devconf.data = {} # clean up config test_conf = devconf.load() # reread from file assert result.get('response') == 'ACK' assert result.get('task_id') == 123123 assert test_conf.get('value') == 'newval'
def run(self): """start application device module""" self.logger.info('application starting') self.logger.debug( f'{self} starting with device config: \n {self.config}') reload_event = make_event('device', 'reload') self.q_int.put(reload_event)
def run(self): """ Running lock self.gpio_setup() must be performed before run """ self.on_start() self.logger.info('running lock...') start_event = make_event('device', 'reload') self.q_int.put(start_event) self.running = True self.keypad_thread.start() while self.running: time.sleep(DEFAULT_SLEEP / 5) # main routine self.manage_sound() # Это должно быть сверху, потому что иначе неправильно работает игровой фидбек от замка в blocked статусе if not self.keypad_data_queue.empty(): # reading serial from keypads. data = self.keypad_data_queue.get() self.parse_data(data) # blocked rules all if self.config.get('blocked') or self.check_timer("main", int(time.time())): self.set_closed() continue # sync state - opening if self.closed and not self.config.get('closed'): self.open() # sync state - closing if not self.closed and self.config.get('closed'): self.close() else: return self.stop()
def run(self): print('application is starting...') self.logger.info(f'tester starting as: {self.sysconf}') self.running = True event = make_event('device', 'reload') self.q_int.put(event) initial_config = self.config.data #q_ext = self.sysconf.get('q_ext') while self.running: # event = q_ext.get() # if event: # print(event) # q_ext.put(event) #self.emulate_activity() if self.web: form_data = self.flask_queue.get() try: res = json.loads(form_data.get("datahold")) except json.decoder.JSONDecodeError as e: res = form_data.get("datahold") if form_data.get("type") == "SUP": self.state_update(res) else: self.send_message(res) new_config = self.config.data if new_config != initial_config: print(f"{new_config}") initial_config = new_config time.sleep(1)
def mqtt_to_internal(self, mqtt_event: Event, internal_command: str): datahold = mqtt_event.data.get('datahold') if not datahold: raise Exception( f'empty datahold in {mqtt_event.data.get("command").upper()} packet: {mqtt_event}' ) event = make_event('device', internal_command, datahold) self.q_int.put(event)
def test_device_input_send_msg(get_device, default_config): device, devcfg, syscfg = get_device event = device.send_message(default_config('dev')) test_event = make_event('device', 'info', default_config('dev')) assert event.type == test_event.type, 'bad event type' assert event.cmd == test_event.cmd, 'bad event command' assert event.data == test_event.data, 'bad event data'
def test_event_context_reload(event_setup, default_config): """ Test reload command """ syscfg = event_setup() dev_conf = syscfg.get('device').config pre_conf = dev_conf.load() changed = dev_conf.update({'value': 'updated'}) event = make_event('device', 'reload') with mgr.EventContext(syscfg) as context: result = context.manage(event) assert changed.get('value') == 'updated', 'config not updated on the fly' assert result == pre_conf, 'config was not reloaded'
def test_event_context_info_send(event_setup, monkeypatch, default_config): """ Test send command """ syscfg = event_setup() _dict = {'new_value': 'new'} event = make_event('device', 'info', _dict) with mgr.EventContext(syscfg) as context: monkeypatch.setattr(context, 'send_message', lambda x: x) result = context.manage(event) test_conf = syscfg.get('device').config.load() assert test_conf.get('new_value') is None, 'config saved when should not' assert result == _dict, 'bad data sent'
def test_event_context_input(event_setup, monkeypatch, default_config, payload): """ Test device state_update (input event) """ syscfg = event_setup() event = make_event('device', 'input', payload) with mgr.EventContext(syscfg) as context: # not sending config anywhere, just calling device.config.save monkeypatch.setattr(context, 'send_config', lambda x: x) result = context.manage(event) post_conf = {**default_config('dev'), **payload} assert result == payload, 'config not sent' assert syscfg.get('device').config.load() == post_conf, 'config not saved'
def test_device_input_new(get_device, default_config, payload): device, devcfg, syscfg = get_device event = device.state_update({**default_config('dev'), **payload}) payload_and_uid = {**payload, **{'uid': syscfg.get('uid')}} test_event = make_event('device', 'input', payload_and_uid) device.save(payload) assert device.config.data == { **devcfg.data, **payload }, 'user input not saved' assert event.type == test_event.type, 'bad event type' assert event.cmd == test_event.cmd, 'bad event command' assert event.data == test_event.data, 'bad event data'
def test_router_event_device(get_router, monkeypatch, get_from_queue, event_data): router, syscfg, devcfg = get_router test_queue = Queue() monkeypatch.setattr(EventContext, 'manage', lambda x, y: test_queue.put(y)) router.start() event = make_event('device', 'test', event_data) syscfg.get('q_int').put(event) result = list(get_from_queue(test_queue)) assert result, 'event not managed' assert not len(result) > 1, 'too many events' assert result[0].type == 'device' assert result[0].cmd == 'test' assert result[0].data == event_data
def test_router_exit_by_event(get_router, request, get_from_queue): router, syscfg, devcfg = get_router router.start() def _fin(): router.running = False router.join(.1) request.addfinalizer(_fin) event = make_event('exit') syscfg.data['q_int'].put(event) expected_event = list(get_from_queue(syscfg.get('q_ext'))) assert expected_event, 'cannot get event from external queue' assert not len(expected_event) > 1, 'too many events' assert expected_event[0].type == 'exit', 'external message not sent' assert router.running is False, 'router was not stopped'
def connect_client(self): exit_message = make_event('device', 'exit') tries = 0 while not self.is_connected: if self.running is False: return self.client.loop() # manual loop while not connected sleep_time = self.default_timeout # default sleep time tries += 1 self.logger.info( f':: trying MQTT broker: {tries}, next try after {sleep_time}s' ) try: self.client.connect(host=self.broker_ip, port=self.broker_port, keepalive=60) except ValueError: _errm = f"check system config, client misconfigured.\n"\ f"broker_ip: {self.broker_ip} broker_port: {self.broker_port}" self.client.loop_stop() self.q_int.put(exit_message) except (ConnectionRefusedError, OSError): sleep_time = 30 _errm = f'mqtt broker not available, waiting {sleep_time}s' self.logger.error(_errm) except MQTTAuthError: self.logger.exception('auth error. check system config ') self.q_int.put(exit_message) except MQTTProtocolError: self.logger.exception('protocol error. report immediately') self.q_int.put(exit_message) except Exception: self.logger.exception('exception occured') self.q_int.put(exit_message) time.sleep(sleep_time) _connm = ':: connected to MQTT broker at ' \ '{broker_ip}:{broker_port} ' \ 'as {client._client_id}'.format(**self.__dict__) self.logger.info(_connm) self.client.loop_start()
def test_router_event_mqtt(get_router, monkeypatch, get_from_queue): router, syscfg, devcfg = get_router test_queue = Queue() monkeypatch.setattr(EventContext, 'manage_mqtt', lambda x, y: test_queue.put(y)) router.start() kinda_mqtt_parsed_message = { 'datahold': { 'data': 'test' }, 'command': 'PING' } event = make_event('mqtt', 'test', kinda_mqtt_parsed_message) syscfg.get('q_int').put(event) result = list(get_from_queue(test_queue)) assert result, 'event not managed' assert not len(result) > 1, 'too many events' assert result[0].data.get('command') == 'PING' assert result[0].data.get('datahold') == {'data': 'test'}
def on_connect(self, client: mqtt.Client, userdata: Any, flags, rc: int): """On connect to broker TODO: type annotation for userdata """ if 6 > rc > 0: # connection failed raise auth_exc.get(str(rc)) self.is_connected = True try: for c in self.sub: self.client.subscribe(c) self.subscriptions_info = "subscribed to " + ','.join(self.sub) except Exception: raise # request config on connect request_config_event = make_event("device", "cup") self.q_int.put(request_config_event)
def state_update(self, data: dict): """Update device configuration from user actions When new data from user actions received, check current config and if changed, send new event to inner event queue for local config change. """ if not isinstance(data, dict): self.logger.error('message type not dict: {}\n{}'.format( type(data), data)) return delta = {} for key in data: # make diff old_value = self.config.get(key, None) if data[key] != old_value: delta[key] = data[key] if delta: delta['uid'] = self.uid event = make_event('device', 'input', delta) self.q_int.put(event) return event
def on_message(self, client: mqtt.Client, userdata, msg): """Message from MQTT broker received receive message as (str, b'{}'), return dict TODO: type annotations for userdata, msg """ self.logger.debug(f'RECEIVE: {msg.topic} {msg.payload}') try: full_topic = msg.topic.split('/') if 2 > len(full_topic) > 3: raise Exception(f'unsupported topic format: {full_topic}') payload = json.loads(msg.payload.decode('utf-8')) data = dict(topic=full_topic[0], uid=full_topic[1] if len(full_topic) == 3 else None, command=full_topic[-1], task_id=payload.get('task_id'), timestamp=int(payload.get('timestamp')), datahold=payload.get('datahold')) event = make_event('mqtt', 'new', data) self.q_int.put(event) except BaseException: self.logger.exception('while receiving message')
def test_event_context_send_config_from_event_with_data( event_setup, monkeypatch, default_config, key, expected): """ Test send config to server with filtered fields """ in_queue = list() syscfg = event_setup(dev_config=base_config) internal_event = make_event("device", "sup", [ key, ]) with mgr.EventContext(syscfg) as context: monkeypatch.setattr(context.q_ext, 'put', lambda x: in_queue.append(x)) context.manage(internal_event) while not in_queue: time.sleep(.1) else: message = MockMessage(in_queue[-1]) packet_topic = '/'.join((context.topic, syscfg.get('uid'), 'SUP')) assert message.topic == packet_topic, 'wrong message topic' assert message.decoded.get( 'timestamp') == 0, f'wrong device timestamp {message.decoded}' assert message.decoded['datahold'].get( key ) == expected, f'{key}, {expected} > bad data send: {message.decoded}'
def stop(self): """stop application device module""" self.logger.info('device is stopping...') self.logger.debug(f"stopping device {self}") end_event = make_event("exit") self.q_int.put(end_event)
def enqueue(self, record): data = {"msg": record.msg, "lvl": record.levelname} event = make_event('device', 'send', data) self.queue.put(event)
def send_message(self, data: dict): """Send message to server""" event = make_event('device', 'info', data) self.q_int.put(event) return event