예제 #1
0
 def received_message(self, message):
     if not hasattr(self, 'metadata'):
         return
     allowed_types = [
         GatewayEvent.Types.OUTPUT_CHANGE,
         GatewayEvent.Types.THERMOSTAT_CHANGE,
         GatewayEvent.Types.THERMOSTAT_GROUP_CHANGE,
         GatewayEvent.Types.SHUTTER_CHANGE, GatewayEvent.Types.INPUT_CHANGE
     ]
     try:
         data = msgpack.loads(message.data)
         event = GatewayEvent.deserialize(data)
         if event.type == GatewayEvent.Types.ACTION:
             if event.data['action'] == 'set_subscription':
                 subscribed_types = [
                     stype for stype in event.data['types']
                     if stype in allowed_types
                 ]
                 cherrypy.engine.publish(
                     'update-events-receiver', self.metadata['client_id'],
                     {'subscribed_types': subscribed_types})
         elif event.type == GatewayEvent.Types.PING:
             self.send(msgpack.dumps(
                 GatewayEvent(event_type=GatewayEvent.Types.PONG,
                              data=None).serialize()),
                       binary=True)
     except Exception as ex:
         logger.exception('Error receiving message: %s', ex)
    def test_shutter_sync_state(self):
        master_controller = Mock()
        master_controller.load_shutters = lambda: []
        SetUpTestInjections(master_controller=master_controller,
                            maintenance_controller=Mock())
        controller = ShutterController()

        # Basic configuration
        controller.update_config(ShutterControllerTest.SHUTTER_CONFIG)
        self.assertEqual(len(controller._shutters), 4)

        events = []

        def on_change(gateway_event):
            events.append(gateway_event)

        self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.STATE, on_change)
        controller.start()
        self.pubsub._publish_all_events()
        self.assertEqual([GatewayEvent('SHUTTER_CHANGE', {'id': 0, 'status': {'state': 'STOPPED', 'position': None, 'last_change': 0.0}, 'location': {'room_id': None}}),
                          GatewayEvent('SHUTTER_CHANGE', {'id': 1, 'status': {'state': 'STOPPED', 'position': None, 'last_change': 0.0}, 'location': {'room_id': None}}),
                          GatewayEvent('SHUTTER_CHANGE', {'id': 2, 'status': {'state': 'STOPPED', 'position': None, 'last_change': 0.0}, 'location': {'room_id': None}}),
                          GatewayEvent('SHUTTER_CHANGE', {'id': 3, 'status': {'state': 'STOPPED', 'position': None, 'last_change': 0.0}, 'location': {'room_id': None}})], events)

        events = []
        fakesleep.reset(100)
        controller.report_shutter_position(0, 89, 'UP')
        self.pubsub._publish_all_events()
        self.assertEqual([GatewayEvent('SHUTTER_CHANGE', {'id': 0, 'status': {'state': 'GOING_UP', 'position': 89, 'last_change': 100.0}, 'location': {'room_id': None}})], events)
        controller.stop()
예제 #3
0
 def _handle_input_status(self, data, data_type='status'):
     event = GatewayEvent.deserialize(
         data) if data_type == 'event' else None
     for decorated_method in self._decorated_methods['input_status']:
         decorator_version = decorated_method.input_status.get('version', 1)
         if decorator_version not in PluginRuntime.SUPPORTED_DECORATOR_VERSIONS[
                 'input_status']:
             error = NotImplementedError(
                 'Version {} is not supported for input status decorators'.
                 format(decorator_version))
             self._writer.log_exception('input status', error)
         else:
             if decorator_version == 1 and data_type == 'status':
                 self._writer.with_catch('input status', decorated_method,
                                         [data])
             elif decorator_version == 2 and event is not None:
                 # get relevant event details
                 input_id = event.data['id']
                 status = event.data['status']
                 # Version 2 will send ALL input status changes AND in a dict format
                 self._writer.with_catch('input status', decorated_method,
                                         [{
                                             'input_id': input_id,
                                             'status': status
                                         }])
예제 #4
0
 def _publish_shutter_change(self, shutter_id, shutter_data, shutter_state):  # type: (int, ShutterDTO, Tuple[float, str]) -> None
     gateway_event = GatewayEvent(event_type=GatewayEvent.Types.SHUTTER_CHANGE,
                                  data={'id': shutter_id,
                                        'status': {'state': shutter_state[1].upper(),
                                                   'last_change': shutter_state[0]},
                                        'location': {'room_id': Toolbox.nonify(shutter_data.room, 255)}})
     self._pubsub.publish_gateway_event(PubSub.GatewayTopics.STATE, gateway_event)
예제 #5
0
 def _handle_power_event(self, master_event):
     # type: (MasterEvent) -> None
     if master_event.type == MasterEvent.Types.POWER_ADDRESS_EXIT:
         # TODO add controller / orm sync for power modules.
         gateway_event = GatewayEvent(GatewayEvent.Types.CONFIG_CHANGE,
                                      {'type': 'powermodule'})
         self._pubsub.publish_gateway_event(PubSub.GatewayTopics.CONFIG,
                                            gateway_event)
예제 #6
0
 def _handle_master_event(self, master_event):
     # type: (MasterEvent) -> None
     if master_event.type == MasterEvent.Types.INPUT_CHANGE:
         # TODO move to InputController
         gateway_event = GatewayEvent(
             event_type=GatewayEvent.Types.INPUT_CHANGE,
             data=master_event.data)
         self._pubsub.publish_gateway_event(PubSub.GatewayTopics.STATE,
                                            gateway_event)
예제 #7
0
    def _sync_orm(self):
        # type: () -> bool
        if self.SYNC_STRUCTURES is None:
            return False

        if self._sync_running:
            for structure in self.SYNC_STRUCTURES:
                orm_model = structure.orm_model
                logger.info('ORM sync ({0}): Already running'.format(
                    orm_model.__name__))
            return False
        self._sync_running = True

        try:
            for structure in self.SYNC_STRUCTURES:
                orm_model = structure.orm_model
                try:
                    name = structure.name
                    skip = structure.skip

                    start = time.time()
                    logger.info('ORM sync ({0})'.format(orm_model.__name__))

                    ids = []
                    for dto in getattr(self._master_controller,
                                       'load_{0}s'.format(name))():
                        if skip is not None and skip(dto):
                            continue
                        id_ = dto.id
                        ids.append(id_)
                        if not orm_model.select().where(
                                orm_model.number == id_).exists():
                            orm_model.create(number=id_)
                    orm_model.delete().where(
                        orm_model.number.not_in(ids)).execute()

                    duration = time.time() - start
                    logger.info(
                        'ORM sync ({0}): completed after {1:.1f}s'.format(
                            orm_model.__name__, duration))
                except CommunicationTimedOutException as ex:
                    logger.error('ORM sync ({0}): Failed: {1}'.format(
                        orm_model.__name__, ex))
                except Exception:
                    logger.exception('ORM sync ({0}): Failed'.format(
                        orm_model.__name__))

                if self._sync_dirty:
                    type_name = orm_model.__name__.lower()
                    gateway_event = GatewayEvent(
                        GatewayEvent.Types.CONFIG_CHANGE, {'type': type_name})
                    self._pubsub.publish_gateway_event(
                        PubSub.GatewayTopics.CONFIG, gateway_event)
            self._sync_dirty = False
        finally:
            self._sync_running = False
        return True
 def _handle_master_event(self, master_event):
     # type: (MasterEvent) -> None
     if master_event.type in [
             MasterEvent.Types.EEPROM_CHANGE,
             MasterEvent.Types.MODULE_DISCOVERY
     ]:
         self.invalidate_cache(THERMOSTATS)
         gateway_event = GatewayEvent(GatewayEvent.Types.CONFIG_CHANGE,
                                      {'type': 'thermostats'})
         self._pubsub.publish_gateway_event(PubSub.GatewayTopics.CONFIG,
                                            gateway_event)
예제 #9
0
 def _publish_state(self, state_dto):
     # type: (VentilationStatusDTO) -> None
     event_data = {
         'id': state_dto.id,
         'mode': state_dto.mode,
         'level': state_dto.level,
         'timer': state_dto.timer
     }
     gateway_event = GatewayEvent(GatewayEvent.Types.VENTILATION_CHANGE,
                                  event_data)
     self._pubsub.publish_gateway_event(PubSub.GatewayTopics.STATE,
                                        gateway_event)
예제 #10
0
    def test_events_sent_to_cloud(self):
        event_sender = EventSender()  # Don't start, trigger manually
        self.assertEqual(len(event_sender._queue), 0)
        self.assertFalse(event_sender._batch_send_events())

        select_mock = Mock()
        select_mock.dicts.return_value = [{
            'number': 1,
            'event_enabled': True
        }, {
            'number': 2,
            'event_enabled': False
        }]

        with patch.object(Input, 'select', return_value=select_mock):
            with patch.object(Config, 'get_entry', return_value=True):
                event_sender.enqueue_event(
                    GatewayEvent(GatewayEvent.Types.OUTPUT_CHANGE, {'id': 1}))
                event_sender.enqueue_event(
                    GatewayEvent(GatewayEvent.Types.THERMOSTAT_CHANGE,
                                 {'id': 1}))
                event_sender.enqueue_event(
                    GatewayEvent(GatewayEvent.Types.INPUT_CHANGE, {'id': 1}))
                event_sender.enqueue_event(
                    GatewayEvent(GatewayEvent.Types.INPUT_CHANGE, {'id': 2}))
            with patch.object(Config, 'get_entry', return_value=False):
                event_sender.enqueue_event(
                    GatewayEvent(GatewayEvent.Types.INPUT_CHANGE, {'id': 3}))

        self.assertEqual(3, len(event_sender._queue))
        self.assertTrue(event_sender._batch_send_events())
        self.assertEqual(0, len(event_sender._queue))
        events = self.sent_events.get('events', [])
        self.assertEqual(3, len(events))
        input_event = [
            event for event in events
            if event.type == GatewayEvent.Types.INPUT_CHANGE
        ][0]
        self.assertEqual(1, input_event.data['id'])
예제 #11
0
 def _thermostat_group_changed(self, status):
     # type: (Dict[str,Any]) -> None
     gateway_event = GatewayEvent(
         GatewayEvent.Types.THERMOSTAT_GROUP_CHANGE, {
             'id': 0,
             'status': {
                 'state': status['state'],
                 'mode': status['mode']
             },
             'location': {}
         })
     self._pubsub.publish_gateway_event(PubSub.GatewayTopics.STATE,
                                        gateway_event)
예제 #12
0
    def test_orm_sync(self):
        events = []

        def handle_event(gateway_event):
            events.append(gateway_event)

        self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.CONFIG, handle_event)

        input_dto = InputDTO(id=42)
        with mock.patch.object(self.master_controller, 'load_inputs', return_value=[input_dto]):
            self.controller.run_sync_orm()
            self.pubsub._publish_all_events()
            assert Input.select().where(Input.number == input_dto.id).count() == 1
            assert GatewayEvent(GatewayEvent.Types.CONFIG_CHANGE, {'type': 'input'}) in events
            assert len(events) == 1
예제 #13
0
    def test_config_event(self):
        events = []

        def handle_events(gateway_event):
            events.append(gateway_event)

        self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.CONFIG,
                                             handle_events)
        master_event = MasterEvent(MasterEvent.Types.POWER_ADDRESS_EXIT, {})
        self.pubsub.publish_master_event(PubSub.MasterTopics.POWER,
                                         master_event)
        self.pubsub._publish_all_events()

        assert GatewayEvent(GatewayEvent.Types.CONFIG_CHANGE,
                            {'type': 'powermodule'}) in events
        assert len(events) == 1
예제 #14
0
 def _thermostat_group_changed(self, thermostat_group):
     # type: (ThermostatGroup) -> None
     gateway_event = GatewayEvent(
         GatewayEvent.Types.THERMOSTAT_GROUP_CHANGE, {
             'id': 0,
             'status': {
                 'state':
                 'ON' if thermostat_group.on else 'OFF',
                 'mode':
                 'COOLING'
                 if thermostat_group.mode == 'cooling' else 'HEATING'
             },
             'location': {}
         })
     self._pubsub.publish_gateway_event(PubSub.GatewayTopics.STATE,
                                        gateway_event)
예제 #15
0
 def _handle_output_status(self, data, data_type='status'):
     event = GatewayEvent.deserialize(
         data) if data_type == 'event' else None
     for receiver in self._decorated_methods['output_status']:
         decorator_version = receiver.output_status.get('version', 1)
         if decorator_version not in PluginRuntime.SUPPORTED_DECORATOR_VERSIONS[
                 'output_status']:
             error = NotImplementedError(
                 'Version {} is not supported for output status decorators'.
                 format(decorator_version))
             self._writer.log_exception('output status', error)
         else:
             if decorator_version == 1 and data_type == 'status':
                 self._writer.with_catch('output status', receiver, [data])
             elif decorator_version == 2 and event:
                 self._writer.with_catch('output status', receiver,
                                         [event.data])
예제 #16
0
 def _publish_state(self, state_dto):
     # type: (VentilationStatusDTO) -> None
     # if the timer or remaining time is set, the other value will not be set,
     # so cache the previous value so it does not get lost
     state_dto = self._save_status_cache(state_dto)
     event_data = {
         'id': state_dto.id,
         'mode': state_dto.mode,
         'level': state_dto.level,
         'timer': state_dto.timer,
         'remaining_time': state_dto.remaining_time,
         'is_connected': state_dto.is_connected
     }
     gateway_event = GatewayEvent(GatewayEvent.Types.VENTILATION_CHANGE,
                                  event_data)
     self._pubsub.publish_gateway_event(PubSub.GatewayTopics.STATE,
                                        gateway_event)
예제 #17
0
    def test_ventilation_config_events(self):
        plugin = Plugin(id=2, name='dummy', version='0.0.1')
        with mock.patch.object(Plugin, 'get', return_value=plugin), \
                mock.patch.object(Ventilation, 'get_or_none',
                                  return_value=Ventilation(id=42,
                                                           source='plugin',
                                                           source_id=2,
                                                           external_id='device-000001',
                                                           name='foo',
                                                           amount_of_levels=4,
                                                           device_type='model-0',
                                                           device_vendor='example',
                                                           device_serial='device-000001',
                                                           plugin=plugin)), \
                mock.patch.object(Ventilation, 'save', side_effect=(0, 1)):

            events = []

            def callback(event):
                events.append(event)

            self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.CONFIG,
                                                 callback)

            ventilation_dto = VentilationDTO(id=42,
                                             external_id='device-000001',
                                             source=VentilationSourceDTO(
                                                 id=2,
                                                 name='dummy',
                                                 type='plugin'),
                                             name='foo',
                                             amount_of_levels=4,
                                             device_vendor='example',
                                             device_type='model-0',
                                             device_serial='device-000001')
            self.controller.save_ventilation(ventilation_dto)
            self.pubsub._publish_all_events()
            assert len(events) == 0, events  # No change

            ventilation_dto.name = 'bar'
            self.controller.save_ventilation(ventilation_dto)
            self.pubsub._publish_all_events()
            assert GatewayEvent(GatewayEvent.Types.CONFIG_CHANGE,
                                {'type': 'ventilation'}) in events
            assert len(events) == 1, events
예제 #18
0
 def _publish_output_change(self, output_dto):
     # type: (OutputDTO) -> None
     event_status = {
         'on': output_dto.state.status,
         'locked': output_dto.state.locked
     }
     if output_dto.module_type in ['d', 'D']:
         event_status['value'] = output_dto.state.dimmer
     event_data = {
         'id': output_dto.id,
         'status': event_status,
         'location': {
             'room_id': Toolbox.denonify(output_dto.room, 255)
         }
     }
     gateway_event = GatewayEvent(GatewayEvent.Types.OUTPUT_CHANGE,
                                  event_data)
     self._pubsub.publish_gateway_event(PubSub.GatewayTopics.STATE,
                                        gateway_event)
예제 #19
0
    def test_ventilation_change_events(self):
        plugin = Plugin(id=2, name='dummy', version='0.0.1')

        def get_ventilation(id):
            return Ventilation(id=id,
                               amount_of_levels=4,
                               source='plugin',
                               plugin=plugin)

        with mock.patch.object(Select, 'count', return_value=1), \
             mock.patch.object(Ventilation, 'get', side_effect=get_ventilation), \
             mock.patch.object(Ventilation, 'select',
                               return_value=[get_ventilation(42), get_ventilation(43)]):
            self.controller.set_status(
                VentilationStatusDTO(42, 'manual', level=0))
            self.controller.set_status(
                VentilationStatusDTO(43, 'manual', level=2, timer=60.0))
            self.pubsub._publish_all_events()

            events = []

            def callback(event):
                events.append(event)

            self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.STATE,
                                                 callback)

            self.controller.set_status(
                VentilationStatusDTO(42, 'manual', level=0))
            self.controller.set_status(
                VentilationStatusDTO(43, 'manual', level=2, timer=60.0))
            self.pubsub._publish_all_events()
            assert GatewayEvent(
                GatewayEvent.Types.VENTILATION_CHANGE, {
                    'id': 43,
                    'mode': 'manual',
                    'level': 2,
                    'timer': 60.0,
                    'remaining_time': None,
                    'is_connected': True
                }) in events
            assert len(events) == 1, events
예제 #20
0
 def _thermostat_changed(self, thermostat_number, active_preset,
                         current_setpoint, actual_temperature, percentages,
                         room):
     # type: (int, str, float, Optional[float], List[float], int) -> None
     location = {'room_id': room}
     gateway_event = GatewayEvent(
         GatewayEvent.Types.THERMOSTAT_CHANGE, {
             'id': thermostat_number,
             'status': {
                 'preset': active_preset,
                 'current_setpoint': current_setpoint,
                 'actual_temperature': actual_temperature,
                 'output_0':
                 percentages[0] if len(percentages) >= 1 else None,
                 'output_1':
                 percentages[1] if len(percentages) >= 2 else None
             },
             'location': location
         })
     self._pubsub.publish_gateway_event(PubSub.GatewayTopics.STATE,
                                        gateway_event)
예제 #21
0
 def _thermostat_changed(self, thermostat_id, status):
     # type: (int, Dict[str,Any]) -> None
     """ Executed by the Thermostat Status tracker when an output changed state """
     location = {
         'room_id':
         Toolbox.denonify(self._thermostats_config[thermostat_id].room, 255)
     }
     gateway_event = GatewayEvent(
         GatewayEvent.Types.THERMOSTAT_CHANGE, {
             'id': thermostat_id,
             'status': {
                 'preset': status['preset'],
                 'current_setpoint': status['current_setpoint'],
                 'actual_temperature': status['actual_temperature'],
                 'output_0': status['output_0'],
                 'output_1': status['output_1']
             },
             'location': location
         })
     self._pubsub.publish_gateway_event(PubSub.GatewayTopics.STATE,
                                        gateway_event)
예제 #22
0
    def test_thermostat_change_events(self):
        events = []

        def handle_event(gateway_event):
            events.append(gateway_event)

        self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.STATE,
                                             handle_event)

        with mock.patch.object(self.controller,
                               'invalidate_cache') as handle_event:
            status = {
                'act': None,
                'csetp': None,
                'setpoint': None,
                'output0': None,
                'output1': None
            }
            self.controller._thermostats_config = {1: ThermostatDTO(1)}
            self.controller._thermostat_status._report_change(1, status)
            self.pubsub._publish_all_events()
            event_data = {
                'id': 1,
                'status': {
                    'preset': 'AUTO',
                    'current_setpoint': None,
                    'actual_temperature': None,
                    'output_0': None,
                    'output_1': None
                },
                'location': {
                    'room_id': 255
                }
            }
            assert GatewayEvent(GatewayEvent.Types.THERMOSTAT_CHANGE,
                                event_data) in events
예제 #23
0
 def _handle_input_status(self, data):
     event = GatewayEvent.deserialize(data)
     # get relevant event details
     input_id = event.data['id']
     status = event.data['status']
     for decorated_method in self._decorated_methods['input_status']:
         decorator_version = decorated_method.input_status.get('version', 1)
         if decorator_version == 1:
             # Backwards compatibility: only send rising edges of the input (no input releases)
             if status:
                 self._writer.with_catch('input status', decorated_method,
                                         [(input_id, None)])
         elif decorator_version == 2:
             # Version 2 will send ALL input status changes AND in a dict format
             self._writer.with_catch('input status', decorated_method,
                                     [{
                                         'input_id': input_id,
                                         'status': status
                                     }])
         else:
             error = NotImplementedError(
                 'Version {} is not supported for input status decorators'.
                 format(decorator_version))
             self._writer.log_exception('input status', error)
예제 #24
0
 def _publish_config(self):
     # type: () -> None
     gateway_event = GatewayEvent(GatewayEvent.Types.CONFIG_CHANGE,
                                  {'type': 'ventilation'})
     self._pubsub.publish_gateway_event(PubSub.GatewayTopics.CONFIG,
                                        gateway_event)
예제 #25
0
    def test_get_shutter_decorators(self):
        """ Test getting shutter decorators on a plugin. """
        controller = None
        try:
            PluginControllerTest._create_plugin(
                'ShutterPlugin', """
from plugins.base import *

class ShutterPlugin(OMPluginBase):
    name = 'ShutterPlugin'
    version = '0.1.0'
    interfaces = [('webui', '1.0')]
        
    def __init__(self, webservice, logger):
        OMPluginBase.__init__(self, webservice, logger)
        self._shutter_data_v1 = None
        self._shutter_data_v1_detail = None
        self._shutter_data_v2 = None
        self._shutter_data_v3 = None
        
    @om_expose(auth=True)
    def html_index(self):
        return 'HTML'

    @om_expose(auth=False)
    def get_log(self):
        return {'shutter_data_v1': self._shutter_data_v1,
                'shutter_data_v1_detail': self._shutter_data_v1_detail,
                'shutter_data_v2': self._shutter_data_v2,
                'shutter_data_v3': self._shutter_data_v3}
                
    @shutter_status
    def shutter_v1(self, test_data):
        self._shutter_data_v1 = test_data
        
    @shutter_status
    def shutter_v1_detail(self, test_data, detail):
        self._shutter_data_v1_detail = (test_data, detail)
        
    @shutter_status(version=2)
    def shutter_v2(self, test_data, detail):
        self._shutter_data_v2 = (test_data, detail)
        
    @shutter_status(version=3)
    def shutter_v3(self, shutter_event):
        self._shutter_data_v3 = shutter_event
""")
            shutter_controller = Mock(ShutterController)
            shutter_status = [ShutterEnums.State.STOPPED]
            detail_for_shutter = {
                1: {
                    'state': ShutterEnums.State.STOPPED,
                    'actual_position': None,
                    'desired_position': None,
                    'last_change': 1596787761.147892
                }
            }
            shutter_controller.get_states = lambda: {
                'status': shutter_status,
                'detail': detail_for_shutter
            }
            controller = PluginControllerTest._get_controller(
                shutter_controller=shutter_controller)
            controller.start()

            shutter_event = GatewayEvent(
                event_type=GatewayEvent.Types.SHUTTER_CHANGE,
                data={'some_random_key': 'some_random_value'})
            controller.process_observer_event(shutter_event)

            keys = [
                'shutter_data_v1', 'shutter_data_v1_detail', 'shutter_data_v2',
                'shutter_data_v3'
            ]
            start = time.time()
            response = None
            while time.time() - start < 2:
                response = controller._request('ShutterPlugin', 'get_log')
                if all(response[key] is not None for key in keys):
                    break
                time.sleep(0.1)
            self.maxDiff = None
            self.assertEqual(response['shutter_data_v1'], shutter_status)
            self.assertEqual(response['shutter_data_v1_detail'],
                             [shutter_status, detail_for_shutter])
            self.assertEqual(response['shutter_data_v2'],
                             [shutter_status, detail_for_shutter])
            self.assertEqual(response['shutter_data_v3'], shutter_event.data)
        finally:
            if controller is not None:
                controller.stop()
            PluginControllerTest._destroy_plugin('ShutterPlugin')
예제 #26
0
    def test_output_master_change(self):
        events = []

        def on_change(gateway_event):
            events.append(gateway_event)

        self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.STATE,
                                             on_change)

        self.controller._cache.update_outputs(
            [OutputDTO(id=2),
             OutputDTO(id=40, module_type='D', room=3)])
        self.controller._handle_master_event(
            MasterEvent('OUTPUT_STATUS',
                        {'state': OutputStateDTO(id=2, status=False)}))
        self.controller._handle_master_event(
            MasterEvent(
                'OUTPUT_STATUS',
                {'state': OutputStateDTO(id=40, status=True, dimmer=100)}))
        self.pubsub._publish_all_events()

        events = []
        self.controller._handle_master_event(
            MasterEvent('OUTPUT_STATUS',
                        {'state': OutputStateDTO(id=2, status=True)}))
        self.controller._handle_master_event(
            MasterEvent('OUTPUT_STATUS',
                        {'state': OutputStateDTO(id=40, status=True)}))
        self.pubsub._publish_all_events()

        assert [
            GatewayEvent(
                'OUTPUT_CHANGE', {
                    'id': 2,
                    'status': {
                        'on': True,
                        'locked': False
                    },
                    'location': {
                        'room_id': 255
                    }
                })
        ] == events

        events = []
        self.controller._handle_master_event(
            MasterEvent('OUTPUT_STATUS',
                        {'state': OutputStateDTO(id=40, dimmer=50)}))
        self.pubsub._publish_all_events()
        assert [
            GatewayEvent(
                'OUTPUT_CHANGE', {
                    'id': 40,
                    'status': {
                        'on': True,
                        'value': 50,
                        'locked': False
                    },
                    'location': {
                        'room_id': 3
                    }
                })
        ] == events
예제 #27
0
 def _handle_thermostat_group_status(self, data):
     event = GatewayEvent.deserialize(data)
     for receiver in self._decorated_methods['thermostat_group_status']:
         self._writer.with_catch('thermostat group status', receiver,
                                 [event.data])
예제 #28
0
 def _handle_ventilation_status(self, data):
     event = GatewayEvent.deserialize(data)
     for receiver in self._decorated_methods['ventilation_status']:
         self._writer.with_catch('ventilation status', receiver,
                                 [event.data])
예제 #29
0
    def test_output_sync_change(self):
        events = []

        def on_change(gateway_event):
            events.append(gateway_event)

        self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.STATE,
                                             on_change)

        outputs = {2: OutputDTO(id=2), 40: OutputDTO(id=40, module_type='D')}
        select_mock = mock.Mock()
        select_mock.join_from.return_value = [
            Output(id=0, number=2),
            Output(id=1, number=40, room=Room(id=2, number=3))
        ]
        with mock.patch.object(Output, 'select', return_value=select_mock), \
             mock.patch.object(self.master_controller, 'load_output',
                               side_effect=lambda output_id: outputs.get(output_id)), \
             mock.patch.object(self.master_controller, 'load_output_status',
                               return_value=[OutputStateDTO(id=2, status=True),
                                             OutputStateDTO(id=40, status=True)]):
            self.controller._sync_state()
            self.pubsub._publish_all_events()
            assert [
                GatewayEvent(
                    'OUTPUT_CHANGE', {
                        'id': 2,
                        'status': {
                            'on': True,
                            'locked': False
                        },
                        'location': {
                            'room_id': 255
                        }
                    }),
                GatewayEvent(
                    'OUTPUT_CHANGE', {
                        'id': 40,
                        'status': {
                            'on': True,
                            'value': 0,
                            'locked': False
                        },
                        'location': {
                            'room_id': 3
                        }
                    })
            ] == events

        select_mock = mock.Mock()
        select_mock.join_from.return_value = [
            Output(id=0, number=2),
            Output(id=1, number=40, room=Room(id=2, number=3))
        ]
        with mock.patch.object(Output, 'select', return_value=select_mock), \
             mock.patch.object(self.master_controller, 'load_output',
                               side_effect=lambda output_id: outputs.get(output_id)), \
             mock.patch.object(self.master_controller, 'load_output_status',
                               return_value=[OutputStateDTO(id=2, status=True, dimmer=0),
                                             OutputStateDTO(id=40, status=True, dimmer=50)]):
            events = []
            self.controller._sync_state()
            self.pubsub._publish_all_events()
            assert [
                GatewayEvent(
                    'OUTPUT_CHANGE', {
                        'id': 2,
                        'status': {
                            'on': True,
                            'locked': False
                        },
                        'location': {
                            'room_id': 255
                        }
                    }),
                GatewayEvent(
                    'OUTPUT_CHANGE', {
                        'id': 40,
                        'status': {
                            'on': True,
                            'value': 50,
                            'locked': False
                        },
                        'location': {
                            'room_id': 3
                        }
                    })
            ] == events
예제 #30
0
    def test_get_special_methods(self):
        """ Test getting special methods on a plugin. """
        controller = None
        try:
            PluginControllerTest._create_plugin(
                'P1', """
import time
from plugins.base import *

class P1(OMPluginBase):
    name = 'P1'
    version = '0.1.0'
    interfaces = [('webui', '1.0')]

    def __init__(self, webservice, logger):
        OMPluginBase.__init__(self, webservice, logger)
        self._bg_running = False
        self._input_data = None
        self._input_data_version_2 = None
        self._output_data = None
        self._output_data_version_2 = None
        self._event_data = None

    @om_expose(auth=True)
    def html_index(self):
        return 'HTML'

    @om_expose(auth=False)
    def get_log(self):
        return {'bg_running': self._bg_running,
                'input_data': self._input_data,
                'input_data_version_2': self._input_data_version_2,
                'output_data': self._output_data,
                'output_data_version_2': self._output_data_version_2,
                'event_data': self._event_data}

    @input_status
    def input(self, input_status_inst):
        self._input_data = input_status_inst
        
    @input_status(version=2)
    def input_version_2(self, input_status_inst):
        self._input_data_version_2 = input_status_inst
        
    @output_status
    def output(self, output_status_inst):
        self._output_data = output_status_inst
        
    @output_status(version=2)
    def output_version_2(self, output_status_inst):
        self._output_data_version_2 = output_status_inst
        
    @receive_events
    def recv_events(self, code):
        self._event_data = code

    @background_task
    def run(self):
        while True:
            self._bg_running = True
            time.sleep(1)
""")

            output_controller = Mock(OutputController)
            output_controller.get_output_statuses = lambda: [
                OutputStateDTO(id=1, status=True, dimmer=5)
            ]
            controller = PluginControllerTest._get_controller(
                output_controller=output_controller)
            controller.start()

            response = controller._request('P1', 'html_index')
            self.assertEqual(response, 'HTML')

            rising_input_event = {
                'id': 1,
                'status': True,
                'location': {
                    'room_id': 1
                }
            }
            controller.process_observer_event(
                GatewayEvent(event_type=GatewayEvent.Types.INPUT_CHANGE,
                             data=rising_input_event))
            falling_input_event = {
                'id': 2,
                'status': False,
                'location': {
                    'room_id': 5
                }
            }
            controller.process_observer_event(
                GatewayEvent(event_type=GatewayEvent.Types.INPUT_CHANGE,
                             data=falling_input_event))
            output_event = {
                'id': 1,
                'status': {
                    'on': True,
                    'value': 5,
                    'locked': True
                },
                'location': {
                    'room_id': 5
                }
            }
            controller.process_observer_event(
                GatewayEvent(event_type=GatewayEvent.Types.OUTPUT_CHANGE,
                             data=output_event))
            controller.process_event(1)

            keys = [
                'input_data', 'input_data_version_2', 'output_data',
                'output_data_version_2', 'event_data'
            ]
            start = time.time()
            while time.time() - start < 2:
                response = controller._request('P1', 'get_log')
                if all(response[key] is not None for key in keys):
                    break
                time.sleep(0.1)
            self.assertEqual(response['bg_running'], True)
            self.assertEqual(
                response['input_data'],
                [1, None])  # only rising edges should be triggered
            self.assertEqual(response['output_data'], [[1, 5]])
            self.assertEqual(response['output_data_version_2'], output_event)
            self.assertEqual(response['event_data'], 1)
        finally:
            if controller is not None:
                controller.stop()
            PluginControllerTest._destroy_plugin('P1')