def test_load_ventilation(self): with mock.patch.object(Ventilation, 'get', return_value=Ventilation( id=42, source='plugin', external_id='device-000001', name='foo', amount_of_levels=4, device_vendor='example', device_type='model-0', device_serial='device-000001', plugin=Plugin(id=2, name='dummy', version='0.0.1'))): ventilation_dto = self.controller.load_ventilation(42) assert 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')
def test_update_existing_ventilation(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)) as get_or_none, \ mock.patch.object(Ventilation, 'save', return_value=1) as save: 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) get_or_none.assert_called_with(id=42, source='plugin', plugin=plugin, external_id='device-000001') save.assert_called()
def dto_to_orm(ventilation_dto, fields): # type: (VentilationDTO, List[str]) -> Ventilation lookup_kwargs = {} # type: Dict[str,Any] if ventilation_dto.id: lookup_kwargs.update({'id': ventilation_dto.id}) if ventilation_dto.source.is_plugin: plugin = Plugin.get(name=ventilation_dto.source.name) lookup_kwargs.update({ 'plugin': plugin, 'source': ventilation_dto.source.type, 'external_id': ventilation_dto.external_id }) ventilation = Ventilation.get_or_none(**lookup_kwargs) if ventilation is None: ventilation = Ventilation(**lookup_kwargs) if 'name' in fields: ventilation.name = ventilation_dto.name if 'amount_of_levels' in fields: ventilation.amount_of_levels = ventilation_dto.amount_of_levels if 'device' in fields: ventilation.device_vendor = ventilation_dto.device_vendor ventilation.device_type = ventilation_dto.device_type if ventilation_dto.device_serial: ventilation.device_serial = ventilation_dto.device_serial return ventilation
def test_set_invalid_level(self): plugin = Plugin(id=2, name='dummy', version='0.0.1') with mock.patch.object(Select, 'count', return_value=1), \ mock.patch.object(Ventilation, 'get', return_value=Ventilation(id=42, amount_of_levels=4, souurce='plugin', plugin=plugin)): self.assertRaises(ValueError, self.controller.set_level, 42, 5) self.assertRaises(ValueError, self.controller.set_level, 42, -1)
def _update_orm(name, version): # type: (str, str) -> None try: plugin, _ = Plugin.get_or_create(name=name, defaults={'version': version}) if plugin.version != version: plugin.version = version plugin.save() except Exception as ex: logger.error('Could not store Plugin version: {0}'.format(ex))
def test_ventilation_controller_inactive_status(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(43)]): events = [] def callback(event): events.append(event) self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.STATE, callback) self.controller.set_status( VentilationStatusDTO(43, 'manual', level=2, timer=60.0, last_seen=(time.time() - 600))) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.controller._check_connected_timeout() self.pubsub._publish_all_events() self.assertEqual(2, len(events)) self.assertEqual(1, len(self.controller._status)) # Check that the last event that has been send is Null last_event = events[-1] self.assertEqual(None, last_event.data['mode']) self.assertEqual(None, last_event.data['level']) self.controller.set_status( VentilationStatusDTO(43, 'manual', level=2, timer=60.0)) self.pubsub._publish_all_events() self.assertEqual(3, len(events)) self.assertEqual(1, len(self.controller._status)) self.controller._check_connected_timeout() self.pubsub._publish_all_events() # Now there would no timeout occur self.assertEqual(3, len(events)) self.assertEqual(1, len(self.controller._status))
def remove_plugin(self, name): """ Remove a plugin, this removes the plugin package and configuration. It also calls the remove function on the plugin to cleanup other files written by the plugin. """ from shutil import rmtree plugin = self._get_plugin(name) # Check if the plugin in installed if plugin is None: Plugin.delete().where(Plugin.name == name).execute() raise Exception('Plugin \'{0}\' is not installed.'.format(name)) # Execute the on_remove callbacks try: plugin.remove_callback() except Exception as exception: logger.error('Exception while removing plugin \'{0}\': {1}'.format(name, exception)) # Stop the plugin process self._destroy_plugin_runner(name) self._update_dependencies() # Remove the plugin package plugin_path = '{0}/{1}'.format(self._plugins_path, name) try: rmtree(plugin_path) except Exception as exception: raise Exception('Error while removing package for plugin \'{0}\': {1}'.format(name, exception)) # Remove the plugin configuration conf_file = '{0}/pi_{1}.conf'.format(self._plugin_config_path, name) if os.path.exists(conf_file): os.remove(conf_file) # Finally remove database entry. Plugin.delete().where(Plugin.name == name).execute() return {'msg': 'Plugin successfully removed'}
def get_plugins(self): # type: () -> List[PluginRunner] """ Get a list of all installed plugins. """ plugins = [] for plugin_orm in list(Plugin.select()): plugin = self._runners.get(plugin_orm.name) if plugin: plugins.append(plugin) else: logger.warning('missing runner for plugin {}'.format(plugin_orm.name)) return plugins
def test_update_plugin(self): """ Validates whether a plugin can be updated """ test_1_md5, test_1_data = PluginControllerTest._create_plugin_package( 'Test', """ from plugins.base import * class Test(OMPluginBase): name = 'Test' version = '0.0.1' interfaces = [] """) test_2_md5, test_2_data = PluginControllerTest._create_plugin_package( 'Test', """ from plugins.base import * class Test(OMPluginBase): name = 'Test' version = '0.0.2' interfaces = [] """) controller = PluginControllerTest._get_controller() controller.start() # Install first version result = controller.install_plugin(test_1_md5, test_1_data) self.assertEqual(result, 'Plugin successfully installed') controller.start_plugin('Test') self.assertEqual([r.name for r in controller.get_plugins()], ['Test']) plugin = Plugin.get(name='Test') self.assertEqual('0.0.1', plugin.version) # Update to version 2 result = controller.install_plugin(test_2_md5, test_2_data) self.assertEqual(result, 'Plugin successfully installed') self.assertEqual([r.name for r in controller.get_plugins()], ['Test']) plugin = Plugin.get(name='Test') self.assertEqual('0.0.2', plugin.version)
def test_set_level(self): plugin = Plugin(id=2, name='dummy', version='0.0.1') with mock.patch.object(Select, 'count', return_value=1), \ mock.patch.object(Ventilation, 'get', side_effect=[Ventilation(id=42, amount_of_levels=4, source='plugin', plugin=plugin), Ventilation(id=43, amount_of_levels=4, source='plugin', plugin=plugin)]), \ mock.patch.object(Ventilation, 'select', return_value=[Ventilation(id=42, amount_of_levels=4, source='plugin', plugin=plugin), Ventilation(id=43, amount_of_levels=4, source='plugin', plugin=plugin)]): self.controller.set_level(42, 0) self.controller.set_level(43, 2, timer=60.0) status = self.controller.get_status() assert {'manual'} == set(x.mode for x in status) assert {42, 43} == set(x.id for x in status) assert {0, 2} == set(x.level for x in status) assert {None, 60.0} == set(x.timer for x in status)
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
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
def test_get_one_plugin(self): """ Test getting one plugin in the plugins package. """ controller = None try: PluginControllerTest._create_plugin( 'P1', """ from plugins.base import * class P1(OMPluginBase): name = 'P1' version = '1.0.0' interfaces = [] """) controller = PluginControllerTest._get_controller() controller.start() plugin_list = controller.get_plugins() self.assertEqual(1, len(plugin_list)) self.assertEqual('P1', plugin_list[0].name) plugin = Plugin.get(name='P1') self.assertEqual('1.0.0', plugin.version) finally: if controller is not None: controller.stop() PluginControllerTest._destroy_plugin('P1')
def test_ventilation_plugin_anti_ping_pong(self): # This test will see if the gateway will not keep sending events back and forth when an event change happens # This can be caused by ping-ponging back and forth between the plugin and gateway when sending updates 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(Ventilation, 'get', side_effect=get_ventilation), \ mock.patch.object(Ventilation, 'select', return_value=[get_ventilation(43)]): events = [] def callback(event, self=self): events.append(event) if len(events) > 20: self.fail( 'There should never be more than 20 events due to ventilation in this test' ) # resend the same event to mock the plugin who will send an event back with the same status # id, mode, level=None, timer=None, remaining_time=None, last_seen=None status_dto = VentilationStatusDTO( id=event.data['id'], mode=event.data['mode'], level=event.data['level'], remaining_time=event.data['timer'], ) self.controller.set_status(status_dto) self.pubsub._publish_all_events() self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.STATE, callback) # event that ventilation box is running in automatic mode self.controller.set_status( VentilationStatusDTO(43, 'automatic', level=1, timer=None, remaining_time=None, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) # Clear all current events events = [] # event that timer has been started self.controller.set_status( VentilationStatusDTO(43, 'manual', level=1, timer=30, remaining_time=None, last_seen=time.time())) self.pubsub._publish_all_events() self.assertGreaterEqual(10, len(events)) # event that timer is running self.controller.set_status( VentilationStatusDTO(43, 'manual', level=1, timer=None, remaining_time=15, last_seen=time.time())) self.pubsub._publish_all_events() self.assertGreaterEqual(10, len(events)) # event that timer is done self.controller.set_status( VentilationStatusDTO(43, 'automatic', level=1, timer=None, remaining_time=None, last_seen=time.time())) self.pubsub._publish_all_events() self.assertGreaterEqual(10, len(events))
def test_ventilation_timer_expire_automatic(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(43)]): events = [] def callback(event): events.append(event) self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.STATE, callback) # event that ventilation box is running in automatic mode self.controller.set_status( VentilationStatusDTO(43, 'automatic', level=1, timer=None, remaining_time=None, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.assertEqual( None, events[-1].data['remaining_time']) # no timers running self.assertEqual(None, events[-1].data['timer']) # no timers running # Clear all current events events = [] # event that timer has been started self.controller.set_status( VentilationStatusDTO(43, 'automatic', level=1, timer=30, remaining_time=None, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.assertEqual( None, events[-1].data['remaining_time'] ) # There has not been an update from the ventilation box or plugin self.assertEqual(30, events[-1].data['timer']) for i in range(30, 0, -1): # Clear all current events events = [] # event from ventilation plugin mode = 'automatic' if i % 2 == 0 else 'manual' self.controller.set_status( VentilationStatusDTO(43, mode, level=1, timer=None, remaining_time=i, last_seen=time.time())) self.pubsub._publish_all_events() print(events) self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.assertEqual(i, events[-1].data['remaining_time']) self.assertEqual(30, events[-1].data['timer'] ) # this value should be kept in cache # Clear all current events events = [] # event from ventilation plugin -> Timer has expired, and has switched to automatic mode self.controller.set_status( VentilationStatusDTO(43, 'automatic', level=1, timer=None, remaining_time=None, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.assertEqual(None, events[-1].data['remaining_time']) self.assertEqual( None, events[-1].data['timer'] ) # this value should now be cleared when timer has done
def test_ventilation_timer_expire_manual(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(43)]): events = [] def callback(event): events.append(event) self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.STATE, callback) # first timer is running self.controller.set_status( VentilationStatusDTO(43, 'manual', level=2, timer=60.0, remaining_time=5.0, last_seen=(time.time() - 10))) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) # This should not trigger an event self.controller._check_connected_timeout() # This should trigger an update event. self.controller._periodic_event_update() self.pubsub._publish_all_events() self.assertEqual(2, len(events)) self.assertEqual(1, len(self.controller._status)) # Clear all current events events = [] # event that timer has been done self.controller.set_status( VentilationStatusDTO(43, 'automatic', level=1, timer=None, remaining_time=None, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.assertEqual(None, events[-1].data['remaining_time']) self.assertEqual(None, events[-1].data['timer']) # Clear all current events events = [] # event that timer has been started self.controller.set_status( VentilationStatusDTO(43, 'automatic', level=1, timer=30, remaining_time=None, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.assertEqual(None, events[-1].data['remaining_time']) self.assertEqual(30, events[-1].data['timer']) # Clear all current events events = [] # event from ventilation plugin self.controller.set_status( VentilationStatusDTO(43, 'manual', level=1, timer=None, remaining_time=29, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.assertEqual(29, events[-1].data['remaining_time']) self.assertEqual( 30, events[-1].data['timer']) # this value should be kept in cache # Clear all current events events = [] # event from ventilation plugin self.controller.set_status( VentilationStatusDTO(43, 'automatic', level=1, timer=None, remaining_time=15, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.assertEqual(15, events[-1].data['remaining_time'] ) # this value should update from the event self.assertEqual( 30, events[-1].data['timer']) # this value should be kept in cache # Clear all current events events = [] # event from ventilation plugin (same value) self.controller.set_status( VentilationStatusDTO(43, 'automatic', level=1, timer=None, remaining_time=15, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(0, len(events)) self.assertEqual(1, len(self.controller._status)) # event from ventilation plugin self.controller.set_status( VentilationStatusDTO(43, 'automatic', level=1, timer=None, remaining_time=14, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.assertEqual(14, events[-1].data['remaining_time'] ) # this value should update from the event self.assertEqual( 30, events[-1].data['timer']) # this value should be kept in cache # Clear all current events events = [] # event from ventilation plugin -> Timer has expired, but is still in manual mode self.controller.set_status( VentilationStatusDTO(43, 'manual', level=1, timer=None, remaining_time=None, last_seen=time.time())) self.pubsub._publish_all_events() self.assertEqual(1, len(events)) self.assertEqual(1, len(self.controller._status)) self.assertEqual(None, events[-1].data['remaining_time']) self.assertEqual( None, events[-1].data['timer'] ) # this value should now be cleared when timer has done