class ThermostatControllerMasterTest(unittest.TestCase): @classmethod def setUpClass(cls): SetTestMode() def setUp(self): self.pubsub = PubSub() SetUpTestInjections( pubsub=self.pubsub, master_controller=mock.Mock(MasterClassicController), output_controller=mock.Mock(OutputController)) self.controller = ThermostatControllerMaster() 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 def test_eeprom_events(self): master_event = MasterEvent(MasterEvent.Types.EEPROM_CHANGE, {}) with mock.patch.object(self.controller, 'invalidate_cache') as handle_event: self.pubsub.publish_master_event(PubSub.MasterTopics.EEPROM, master_event) self.pubsub._publish_all_events() handle_event.assert_called()
class PowerControllerTest(unittest.TestCase): @classmethod def setUpClass(cls): SetTestMode() def setUp(self): self.pubsub = PubSub() SetUpTestInjections(pubsub=self.pubsub) self.power_communicator = mock.Mock() SetUpTestInjections(power_communicator=self.power_communicator, power_store=mock.Mock()) self.controller = PowerController() def test_get_module_current(self): with mock.patch.object(self.power_communicator, 'do_command') as cmd: self.controller.get_module_current({ 'version': POWER_MODULE, 'address': '11.0' }) assert cmd.call_args_list == [ mock.call( '11.0', PowerCommand('G', 'CUR', '', '8f', module_type=bytearray(b'E'))) ] def test_get_module_frequency(self): with mock.patch.object(self.power_communicator, 'do_command') as cmd: self.controller.get_module_frequency({ 'version': POWER_MODULE, 'address': '11.0' }) assert cmd.call_args_list == [ mock.call( '11.0', PowerCommand('G', 'FRE', '', 'f', module_type=bytearray(b'E'))) ] def test_get_module_power(self): with mock.patch.object(self.power_communicator, 'do_command') as cmd: self.controller.get_module_power({ 'version': POWER_MODULE, 'address': '11.0' }) assert cmd.call_args_list == [ mock.call( '11.0', PowerCommand('G', 'POW', '', '8f', module_type=bytearray(b'E'))) ] def test_get_module_voltage(self): with mock.patch.object(self.power_communicator, 'do_command') as cmd: self.controller.get_module_voltage({ 'version': POWER_MODULE, 'address': '11.0' }) assert cmd.call_args_list == [ mock.call( '11.0', PowerCommand('G', 'VOL', '', 'f', module_type=bytearray(b'E'))) ] def test_get_module_day_energy(self): with mock.patch.object(self.power_communicator, 'do_command') as cmd: self.controller.get_module_day_energy({ 'version': POWER_MODULE, 'address': '11.0' }) assert cmd.call_args_list == [ mock.call( '11.0', PowerCommand('G', 'EDA', '', '8L', module_type=bytearray(b'E'))) ] def test_get_module_night_energy(self): with mock.patch.object(self.power_communicator, 'do_command') as cmd: self.controller.get_module_night_energy({ 'version': POWER_MODULE, 'address': '11.0' }) assert cmd.call_args_list == [ mock.call( '11.0', PowerCommand('G', 'ENI', '', '8L', module_type=bytearray(b'E'))) ] 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
class MasterCoreControllerTest(unittest.TestCase): """ Tests for MasterCoreController. """ @classmethod def setUpClass(cls): SetTestMode() def setUp(self): self.memory = {} self.return_data = {} def _do_command(command, fields, timeout=None): _ = timeout instruction = ''.join(str(chr(c)) for c in command.instruction) if instruction == 'MR': page = fields['page'] start = fields['start'] length = fields['length'] return {'data': self.memory.get(page, bytearray([255] * 256))[start:start + length]} elif instruction == 'MW': page = fields['page'] start = fields['start'] page_data = self.memory.setdefault(page, bytearray([255] * 256)) for index, data_byte in enumerate(fields['data']): page_data[start + index] = data_byte elif instruction in self.return_data: return self.return_data[instruction] else: raise AssertionError('unexpected instruction: {0}'.format(instruction)) def _do_basic_action(action_type, action, device_nr=0, extra_parameter=0, timeout=2, log=True): _ = device_nr, extra_parameter, timeout, log if action_type == 200 and action == 1: # Send EEPROM_ACTIVATE event eeprom_file._handle_event({'type': 254, 'action': 0, 'device_nr': 0, 'data': 0}) self.communicator = mock.Mock(CoreCommunicator) self.communicator.do_command = _do_command self.communicator.do_basic_action = _do_basic_action self.pubsub = PubSub() SetUpTestInjections(master_communicator=self.communicator, pubsub=self.pubsub) eeprom_file = MemoryFile(MemoryTypes.EEPROM) eeprom_file._cache = self.memory SetUpTestInjections(memory_files={MemoryTypes.EEPROM: eeprom_file, MemoryTypes.FRAM: MemoryFile(MemoryTypes.FRAM)}, ucan_communicator=UCANCommunicator(), slave_communicator=SlaveCommunicator()) self.controller = MasterCoreController() # For testing purposes, remove read-only flag from certain properties for field_name in ['device_type', 'address', 'firmware_version']: for model_type in [OutputModuleConfiguration, InputModuleConfiguration, SensorModuleConfiguration]: if hasattr(model_type, '_{0}'.format(field_name)): getattr(model_type, '_{0}'.format(field_name))._read_only = False else: getattr(model_type, field_name)._read_only = False def test_master_output_event(self): events = [] def _on_event(master_event): events.append(master_event) self.pubsub.subscribe_master_events(PubSub.MasterTopics.OUTPUT, _on_event) events = [] self.controller._handle_event({'type': 0, 'device_nr': 0, 'action': 0, 'data': bytearray([255, 0, 0, 0])}) self.controller._handle_event({'type': 0, 'device_nr': 2, 'action': 1, 'data': bytearray([100, 2, 0xff, 0xfe])}) self.pubsub._publish_all_events() self.assertEqual([MasterEvent(MasterEvent.Types.OUTPUT_STATUS, {'id': 0, 'status': False, 'dimmer': 255, 'ctimer': 0}), MasterEvent(MasterEvent.Types.OUTPUT_STATUS, {'id': 2, 'status': True, 'dimmer': 100, 'ctimer': 65534})], events) def test_master_shutter_event(self): events = [] def _on_event(master_event): events.append(master_event) self.pubsub.subscribe_master_events(PubSub.MasterTopics.SHUTTER, _on_event) self.controller._output_states = {0: OutputStateDTO(id=0, status=False), 10: OutputStateDTO(id=10, status=False), 11: OutputStateDTO(id=11, status=False)} self.controller._output_shutter_map = {10: 1, 11: 1} self.controller._shutter_status = {1: (False, False)} self.pubsub._publish_all_events() with mock.patch.object(gateway.hal.master_controller_core, 'ShutterConfiguration', side_effect=get_core_shutter_dummy): events = [] self.controller._handle_event({'type': 0, 'device_nr': 10, 'action': 0, 'data': [None, 0, 0, 0]}) self.controller._handle_event({'type': 0, 'device_nr': 11, 'action': 0, 'data': [None, 0, 0, 0]}) self.pubsub._publish_all_events() assert [] == events events = [] self.controller._handle_event({'type': 0, 'device_nr': 10, 'action': 1, 'data': [None, 0, 0, 0]}) self.pubsub._publish_all_events() assert [MasterEvent('SHUTTER_CHANGE', {'id': 1, 'status': 'going_up', 'location': {'room_id': 255}})] == events events = [] self.controller._handle_event({'type': 0, 'device_nr': 11, 'action': 1, 'data': [None, 0, 0, 0]}) self.pubsub._publish_all_events() assert [MasterEvent('SHUTTER_CHANGE', {'id': 1, 'status': 'stopped', 'location': {'room_id': 255}})] == events events = [] self.controller._handle_event({'type': 0, 'device_nr': 10, 'action': 0, 'data': [None, 0, 0, 0]}) self.pubsub._publish_all_events() assert [MasterEvent('SHUTTER_CHANGE', {'id': 1, 'status': 'going_down', 'location': {'room_id': 255}})] == events events = [] self.controller._handle_event({'type': 0, 'device_nr': 11, 'action': 0, 'data': [None, 0, 0, 0]}) self.pubsub._publish_all_events() assert [MasterEvent('SHUTTER_CHANGE', {'id': 1, 'status': 'stopped', 'location': {'room_id': 255}})] == events def test_master_shutter_refresh(self): events = [] def _on_event(master_event): events.append(master_event) self.pubsub.subscribe_master_events(PubSub.MasterTopics.SHUTTER, _on_event) output_status = [{'device_nr': 0, 'status': False, 'dimmer': 0}, {'device_nr': 1, 'status': False, 'dimmer': 0}, {'device_nr': 10, 'status': False, 'dimmer': 0}, {'device_nr': 11, 'status': False, 'dimmer': 0}] with mock.patch.object(gateway.hal.master_controller_core, 'ShutterConfiguration', side_effect=get_core_shutter_dummy), \ mock.patch.object(self.controller, 'load_output_status', return_value=output_status): events = [] self.controller._refresh_shutter_states() self.pubsub._publish_all_events() assert [MasterEvent('SHUTTER_CHANGE', {'id': 1, 'status': 'stopped', 'location': {'room_id': 255}})] == events output_status = [{'device_nr': 0, 'status': False, 'dimmer': 0}, {'device_nr': 1, 'status': True, 'dimmer': 0}, {'device_nr': 10, 'status': True, 'dimmer': 0}, {'device_nr': 11, 'status': False, 'dimmer': 0}] with mock.patch.object(gateway.hal.master_controller_core, 'ShutterConfiguration', side_effect=get_core_shutter_dummy), \ mock.patch.object(self.controller, 'load_output_status', return_value=output_status): events = [] self.controller._refresh_shutter_states() self.pubsub._publish_all_events() assert [MasterEvent('SHUTTER_CHANGE', {'id': 1, 'status': 'going_up', 'location': {'room_id': 255}})] == events output_status = [{'device_nr': 0, 'status': False, 'dimmer': 0}, {'device_nr': 1, 'status': True, 'dimmer': 0}, {'device_nr': 10, 'status': False, 'dimmer': 0}, {'device_nr': 11, 'status': True, 'dimmer': 0}] with mock.patch.object(gateway.hal.master_controller_core, 'ShutterConfiguration', side_effect=get_core_shutter_dummy), \ mock.patch.object(self.controller, 'load_output_status', return_value=output_status): events = [] self.controller._refresh_shutter_states() self.pubsub._publish_all_events() assert [MasterEvent('SHUTTER_CHANGE', {'id': 1, 'status': 'going_down', 'location': {'room_id': 255}})] == events def test_input_module_type(self): with mock.patch.object(gateway.hal.master_controller_core, 'InputConfiguration', return_value=get_core_input_dummy(1)): data = self.controller.get_input_module_type(1) self.assertEqual('I', data) def test_load_input(self): data = self.controller.load_input(1) self.assertEqual(data.id, 1) def test_load_inputs(self): input_modules = list(map(get_core_input_dummy, range(1, 17))) self.return_data['GC'] = {'input': 2} with mock.patch.object(gateway.hal.master_controller_core, 'InputConfiguration', side_effect=input_modules): inputs = self.controller.load_inputs() self.assertEqual([x.id for x in inputs], list(range(1, 17))) def test_save_inputs(self): data = [(InputDTO(id=1, name='foo', module_type='I'), ['id', 'name', 'module_type']), (InputDTO(id=2, name='bar', module_type='I'), ['id', 'name', 'module_type'])] input_mock = mock.Mock(InputConfiguration) with mock.patch.object(InputConfiguration, 'deserialize', return_value=input_mock) as deserialize, \ mock.patch.object(input_mock, 'save', return_value=None) as save: self.controller.save_inputs(data) self.assertIn(mock.call({'id': 1, 'name': 'foo'}), deserialize.call_args_list) self.assertIn(mock.call({'id': 2, 'name': 'bar'}), deserialize.call_args_list) save.assert_called_with() def test_inputs_with_status(self): from gateway.hal.master_controller_core import MasterInputState with mock.patch.object(MasterInputState, 'get_inputs', return_value=[]) as get: self.controller.get_inputs_with_status() get.assert_called_with() def test_recent_inputs(self): from gateway.hal.master_controller_core import MasterInputState with mock.patch.object(MasterInputState, 'get_recent', return_value=[]) as get: self.controller.get_recent_inputs() get.assert_called_with() def test_event_consumer(self): with mock.patch.object(gateway.hal.master_controller_core, 'BackgroundConsumer', return_value=None) as new_consumer: controller = MasterCoreController() expected_call = mock.call(CoreAPI.event_information(), 0, mock.ANY) self.assertIn(expected_call, new_consumer.call_args_list) def test_subscribe_input_events(self): consumer_list = [] def new_consumer(*args): consumer = BackgroundConsumer(*args) consumer_list.append(consumer) return consumer subscriber = mock.Mock() with mock.patch.object(gateway.hal.master_controller_core, 'BackgroundConsumer', side_effect=new_consumer) as new_consumer: controller = MasterCoreController() self.pubsub.subscribe_master_events(PubSub.MasterTopics.INPUT, subscriber.callback) new_consumer.assert_called() event_data = {'type': 1, 'action': 1, 'device_nr': 2, 'data': {}} with mock.patch.object(Queue, 'get', return_value=event_data): consumer_list[0].deliver() self.pubsub._publish_all_events() expected_event = MasterEvent.deserialize({'type': 'INPUT_CHANGE', 'data': {'id': 2, 'status': True, 'location': {'room_id': 255}}}) subscriber.callback.assert_called_with(expected_event) def test_get_modules(self): from master.core.memory_models import ( InputModuleConfiguration, OutputModuleConfiguration, SensorModuleConfiguration, GlobalConfiguration ) global_configuration = GlobalConfiguration() global_configuration.number_of_output_modules = 5 global_configuration.number_of_input_modules = 4 global_configuration.number_of_sensor_modules = 2 global_configuration.number_of_can_control_modules = 2 global_configuration.save() for module_id, module_class, device_type, address in [(0, InputModuleConfiguration, 'I', '{0}.123.123.123'.format(ord('I'))), (1, InputModuleConfiguration, 'i', '{0}.123.123.123'.format(ord('i'))), (2, InputModuleConfiguration, 'i', '{0}.000.000.000'.format(ord('i'))), (3, InputModuleConfiguration, 'b', '{0}.123.132.123'.format(ord('b'))), (0, OutputModuleConfiguration, 'o', '{0}.000.000.000'.format(ord('o'))), (1, OutputModuleConfiguration, 'o', '{0}.000.000.001'.format(ord('o'))), (2, OutputModuleConfiguration, 'o', '{0}.000.000.002'.format(ord('o'))), (3, OutputModuleConfiguration, 'o', '{0}.123.123.123'.format(ord('o'))), (4, OutputModuleConfiguration, 'O', '{0}.123.123.123'.format(ord('O'))), (0, SensorModuleConfiguration, 's', '{0}.123.123.123'.format(ord('s'))), (1, SensorModuleConfiguration, 'T', '{0}.123.123.123'.format(ord('T')))]: instance = module_class(module_id) instance.device_type = device_type instance.address = address instance.save() self.assertEqual({'can_inputs': ['I', 'T', 'C', 'E'], 'inputs': ['I', 'i', 'J', 'T'], 'outputs': ['P', 'P', 'P', 'o', 'O'], 'shutters': []}, self.controller.get_modules()) def test_master_eeprom_event(self): master_event = MasterEvent(MasterEvent.Types.EEPROM_CHANGE, {}) self.controller._output_last_updated = 1603178386.0 self.pubsub.publish_master_event(PubSub.MasterTopics.EEPROM, master_event) self.pubsub._publish_all_events() assert self.controller._output_last_updated == 0