Пример #1
0
    def test_actions_press_release(self):
        orm = InputCoreMapperTest._dto_to_orm(
            action=240, basic_actions=[2, 1, 236, 0, 2, 2, 236, 255])
        self._validate_orm(orm,
                           in_use=True,
                           enable_press_and_release=True,
                           basic_action_press=True,
                           basic_action_release=True)
        self.assertEqual(BasicAction(action_type=19, action=0, device_nr=1),
                         orm.basic_action_press)
        self.assertEqual(BasicAction(action_type=19, action=0, device_nr=2),
                         orm.basic_action_release)

        dto = InputCoreMapperTest._orm_to_dto(
            input_link={
                'enable_1s_press': False,
                'enable_2s_press': False,
                'enable_double_press': False
            },
            basic_action_press=BasicAction(action_type=19,
                                           action=0,
                                           device_nr=1),
            basic_action_release=BasicAction(action_type=19,
                                             action=0,
                                             device_nr=2))
        self.assertEqual(240, dto.action)
        self.assertEqual([2, 1, 236, 0, 2, 2, 236, 255], dto.basic_actions)
Пример #2
0
    def save_global_led_feedback_configuration(
            global_feedbacks,
            activate=True):  # type: (List[GlobalFeedbackDTO], bool) -> None
        # Important assumption in the below code to make this strategy solvable: If any of the 4 feedbacks is
        # given, they all are assumed to be given.
        global_configuration = GlobalConfiguration()
        if global_configuration.groupaction_any_output_changed > 255:
            group_action = GroupActionController.get_unused_group_action()
            if group_action is None:
                raise ValueError(
                    'No GroupAction available to store LED feedback configuration'
                )
        else:
            group_action = GroupActionController.load_group_action(
                global_configuration.groupaction_any_output_changed)

        for global_feedback_dto in global_feedbacks:
            holds_data, holds_configuration = CANFeedbackController._available_led_data(
                global_feedback_dto)
            if not holds_data:
                continue  # No change required
            # First, delete everything related to this global_feedback_dto
            if global_feedback_dto.id in [0, 16]:
                action = 73
                extra_parametery_msb = 0 if global_feedback_dto.id == 16 else 1
            else:
                action = 71 if 0 < global_feedback_dto.id < 16 else 70
                extra_parametery_msb = global_feedback_dto.id - (
                    1 if global_feedback_dto.id < 16 else 17)
            for basic_action in group_action.actions[:]:
                if basic_action.action_type == 20 and basic_action.action == action:
                    if basic_action.extra_parameter_msb == extra_parametery_msb:
                        group_action.actions.remove(basic_action)
            # Then, add the relevant entries back again
            if holds_configuration:
                for field in CANFeedbackController.FIELDS:
                    feedback_led_dto = getattr(global_feedback_dto, field)
                    if field in global_feedback_dto.loaded_fields and feedback_led_dto.id is not None:
                        basic_action = BasicAction(
                            action_type=20,
                            action=action,
                            device_nr=feedback_led_dto.id)
                        basic_action.extra_parameter_msb = extra_parametery_msb
                        basic_action.extra_parameter_lsb = CANFeedbackController._blinking_string_to_byte(
                            feedback_led_dto.function)
                        group_action.actions.append(basic_action)
        # Save changes
        if group_action.actions:
            if group_action.name == '':
                group_action.name = 'Global feedback'
            global_configuration.groupaction_any_output_changed = group_action.id
        else:
            group_action.name = ''
            global_configuration.groupaction_any_output_changed = CANFeedbackController.WORD_MAX
        global_configuration.save(activate=False)
        GroupActionController.save_group_action(group_action,
                                                ['name', 'actions'],
                                                activate=False)
        if activate:
            MemoryActivator.activate()
Пример #3
0
 def classic_actions_to_core_input_configuration(action, basic_actions):
     # type: (Optional[int], List[int]) -> Dict[str, Any]
     data = {
         'input_link': {
             'output_id': 1023,
             'dimming_up': True,
             'enable_press_and_release': True,
             'enable_1s_press': True,
             'enable_2s_press': True,
             'not_used': True,
             'enable_double_press': True
         }
     }  # type: Dict[str, Any]
     if action is None or action == 255:
         return data
     data['input_link'].update({
         'dimming_up': False,
         'enable_press_and_release': False,
         'enable_1s_press': False,
         'enable_2s_press': False,
         'not_used': False,
         'enable_double_press': False
     })
     if action < 240:  # 240 means "execute the list of basic actions"
         data['input_link']['output_id'] = action
         return data
     action_types = set(basic_actions[i]
                        for i in range(0, len(basic_actions), 2))
     if 207 in action_types:
         if len(basic_actions) != 2:
             raise ValueError(
                 'Timing settings cannot be combined with other actions')
         data['input_link']['enable_2s_press'] = True
         data['basic_action_2s_press'] = BasicAction(
             action_type=19, action=0, device_nr=basic_actions[1])
         return data
     if action_types - {2, 236}:
         raise ValueError('Only executing GroupActions is supported')
     release_data = False
     release_action = None  # type: Optional[int]
     press_action = None  # type: Optional[int]
     for i in range(0, len(basic_actions), 2):
         action_type = basic_actions[i]
         action_number = basic_actions[i + 1]
         if action_type == 236:
             release_data = action_number == 0
         elif release_data:
             release_action = action_number
         else:
             press_action = action_number
     if press_action is not None:
         data['input_link']['enable_press_and_release'] = True
         data['basic_action_press'] = BasicAction(action_type=19,
                                                  action=0,
                                                  device_nr=press_action)
     if release_action is not None:
         data['input_link']['enable_press_and_release'] = True
         data['basic_action_release'] = BasicAction(
             action_type=19, action=0, device_nr=release_action)
     return data
Пример #4
0
 def save_output_led_feedback_configuration(output,
                                            output_dto,
                                            activate=True):
     # type: (OutputConfiguration, OutputDTO, bool) -> None
     holds_data, holds_configuration = CANFeedbackController._available_led_data(
         output_dto)
     if not holds_data:
         return  # No change required
     group_action_id = output.output_groupaction_follow
     group_action = None  # type: Optional[GroupAction]  # Needed for keeping Mypy happy...
     if group_action_id <= 255:
         group_action = GroupActionController.load_group_action(
             group_action_id)
         for basic_action in group_action.actions[:]:
             if basic_action.action_type == 20 and basic_action.action in [
                     50, 51
             ]:
                 group_action.actions.remove(basic_action)
     else:
         if not holds_configuration:
             return  # No GroupAction configured, and no configurion. Nothing to do.
         group_action = GroupActionController.get_unused_group_action()
         if group_action is None:
             raise ValueError(
                 'No GroupAction available to store LED feedback configuration'
             )
     for field in CANFeedbackController.FIELDS:
         feedback_led_dto = getattr(output_dto, field)
         if field in output_dto.loaded_fields and feedback_led_dto.id is not None:
             action = CANFeedbackController._inverted_string_to_action(
                 feedback_led_dto.function)
             basic_action = BasicAction(action_type=20,
                                        action=action,
                                        device_nr=feedback_led_dto.id)
             basic_action.extra_parameter_lsb = CANFeedbackController._blinking_string_to_byte(
                 feedback_led_dto.function)
             basic_action.extra_parameter_msb = CANFeedbackController._brightness_string_to_byte(
                 feedback_led_dto.function)
             group_action.actions.append(basic_action)
     if group_action.actions:
         if group_action.name == '':
             group_action.name = 'Output {0}'.format(output.id)
         output.output_groupaction_follow = group_action.id
     else:
         group_action.name = ''
         output.output_groupaction_follow = CANFeedbackController.WORD_MAX
     output.save(activate=False)
     GroupActionController.save_group_action(group_action,
                                             ['name', 'actions'],
                                             activate=False)
     if activate:
         MemoryActivator.activate()
Пример #5
0
 def test_mappings(self):
     normal_scenarios = [
         ([160, 5], [(0, 0, 5)]),  # Turn output 5 off
         ([161, 5], [(0, 1, 5)]),  # Turn output 5 on
         ([162, 5], [(0, 16, 5)]),  # Toggle output 5
         ([240, 0, 243, 5, 240, 255], [(100, 0), (100, 10, 5),
                                       (100, 255)]),  # If output 5 is on
         ([240, 0, 247, 5, 249, 10, 240,
           255], [(100, 0), (100, 19, 5, 10),
                  (100, 255)]),  # If temp sensor 5 > 10
         ([240, 0, 247, 37, 248, 10, 240,
           255], [(100, 0), (100, 23, 5, 10),
                  (100, 255)]),  # If hum sensor 5 == 10
         ([240, 0, 247, 69, 250, 10, 240,
           255], [(100, 0), (100, 27, 5, 10),
                  (100, 255)]),  # If brightness sensor 5 < 10
         ([240, 0, 247, 228, 249, 10, 240,
           255], [(100, 0), (100, 40, 10), (100, 255)]),  # If hour > 10
         ([240, 0, 247, 229, 248, 10, 240,
           255], [(100, 0), (100, 44, 10), (100, 255)]),  # If minutes == 10
         ([240, 0, 247, 230, 250, 3, 240, 255], [(100, 0), (100, 48, 3),
                                                 (100, 255)]),  # If day < 3
         ([171, 5], [(251, 0, 5, 0)]),  # Turn off all lights on floor 5
         ([172, 5], [(251, 0, 5, 1)]),  # Turn on all lights on floor 5
         ([173, 5], [(251, 0, 5, 2)]),  # Toggle all lights on floor 5
         ([171, 255], [(251, 0, 65535, 0)
                       ]),  # Turn off all lights (on all floors)
         ([172, 255], [(251, 0, 65535, 1)
                       ]),  # Turn on all lights (on all floors)
         ([173,
           255], [(251, 0, 65535, 2)]),  # Toggle all lights (on all floors)
         ([116, 5], [(1, 250, 5, 1)]),  # Disable input 5
         ([117, 5], [(1, 250, 5, 0)])
     ]  # Enable input 5
     incompatible_scenarios = [
         ([255, 255], [],
          (True, [])),  # Classic actions that are not supported on the Core
         (None, [(255, 255)], (False, []))
     ]  # Core actions that are not supported on the Classic
     for scenario in normal_scenarios + incompatible_scenarios:
         classic_scenario = scenario[0]
         core_scenario = scenario[1]
         if len(scenario) == 3:
             complete_expected, classic_expected = scenario[2]
         else:
             complete_expected = True
             classic_expected = classic_scenario
         core_expected = [BasicAction(*entry) for entry in core_scenario]
         if classic_scenario is not None:
             core_result = GroupActionMapper.classic_actions_to_core_actions(
                 classic_scenario)
             self.assertEqual(core_expected, core_result)
         complete_result, classic_result = GroupActionMapper.core_actions_to_classic_actions(
             core_expected)
         self.assertEqual(classic_expected, classic_result)
         self.assertEqual(complete_expected, complete_result)
Пример #6
0
 def _set_led(self, led, on, mode):
     # type: (str, bool, str) -> None
     if led not in FrontpanelCoreController.LED_TO_BA:
         return
     action = FrontpanelCoreController.LED_TO_BA[led]
     if mode not in FrontpanelCoreController.BLINKING_MAP:
         return
     state = self._led_drive_states.get(led)
     if state != (on, mode):
         extra_parameter = FrontpanelCoreController.BLINKING_MAP[mode]
         self._master_communicator.do_basic_action(
             BasicAction(action_type=210,
                         action=action,
                         device_nr=1 if on else 0,
                         extra_parameter=extra_parameter))
         self._led_drive_states[led] = on, mode
Пример #7
0
 def activate(self):  # type: () -> None
     with self._activate_lock:
         logger.info('MEMORY: Writing')
         write_cache, write_lock = self._get_write_cache()
         with write_lock:
             data_written = self._store_data(write_cache)
             self._clear_write_cache()
         if data_written:
             logger.info('MEMORY: Activating')
             self._self_activated = True
             self._activation_event.clear()
             self._core_communicator.do_basic_action(
                 BasicAction(action_type=200, action=1),
                 timeout=MemoryFile.ACTIVATE_TIMEOUT)
             self._activation_event.wait(timeout=60.0)
         else:
             logger.info('MEMORY: No activation requred')
Пример #8
0
 def data(self):
     if self.type == Event.Types.OUTPUT:
         data = {'output': self._device_nr}
         if self._action in [0, 1]:
             timer_type = self._data[1]  # type: int
             timer_value = self._word_decode(self._data[2:]) or 0  # type: int
             timer = Timer.event_timer_type_to_seconds(timer_type, timer_value)
             data.update({'type': Event.OutputEventTypes.STATUS,
                          'status': self._action == 1,
                          'dimmer_value': self._data[0],
                          'timer': timer})
         else:
             data.update({'type': Event.OutputEventTypes.LOCKING,
                          'locked': self._data[0] == 1})
         return data
     if self.type == Event.Types.INPUT:
         return {'input': self._device_nr,
                 'status': self._action == 1}
     if self.type == Event.Types.SENSOR:
         sensor_type = Event.SensorType.UNKNOWN
         sensor_value = None
         if self._action == 0:
             sensor_type = Event.SensorType.TEMPERATURE
             sensor_value = Temperature.system_value_to_temperature(self._data[1])
         elif self._action == 1:
             sensor_type = Event.SensorType.HUMIDITY
             sensor_value = Humidity.system_value_to_humidity(self._data[1])
         elif self._action == 2:
             sensor_type = Event.SensorType.BRIGHTNESS
             sensor_value = self._word_decode(self._data[0:2])
         return {'sensor': self._device_nr,
                 'type': sensor_type,
                 'value': sensor_value}
     if self.type == Event.Types.THERMOSTAT:
         origin_map = {0: Event.ThermostatOrigins.SLAVE,
                       1: Event.ThermostatOrigins.MASTER}
         return {'origin': origin_map.get(self._action, Event.ThermostatOrigins.UNKNOWN),
                 'thermostat': self._device_nr,
                 'mode': self._data[0],
                 'setpoint': self._data[1]}
     if self.type == Event.Types.BUTTON_PRESS:
         return {'button': self._device_nr,
                 'state': self._data[0]}
     if self.type == Event.Types.LED_BLINK:
         word_25 = self._device_nr
         word_50 = self._word_decode(self._data[0:2])
         word_75 = self._word_decode(self._data[2:4])
         leds = {}
         for i in range(16):
             if word_25 & (1 << i):
                 leds[i] = Event.LedFrequencies.BLINKING_25
             elif word_50 & (1 << i):
                 leds[i] = Event.LedFrequencies.BLINKING_50
             elif word_75 & (1 << i):
                 leds[i] = Event.LedFrequencies.BLINKING_75
             else:
                 leds[i] = Event.LedFrequencies.SOLID
         return {'chip': self._device_nr,
                 'leds': leds}
     if self.type == Event.Types.LED_ON:
         word_on = self._word_decode(self._data[0:2])
         leds = {}
         for i in range(16):
             leds[i] = Event.LedStates.ON if word_on & (1 << i) else Event.LedStates.OFF
         return {'chip': self._device_nr,
                 'leds': leds}
     if self.type == Event.Types.POWER:
         return {'bus': Event.Bus.RS485 if self._device_nr == 0 else Event.Bus.CAN,
                 'power': self._data[0 > 1]}
     if self.type == Event.Types.SYSTEM:
         type_map = {0: Event.SystemEventTypes.EEPROM_ACTIVATE,
                     1: Event.SystemEventTypes.ONBOARD_TEMP_CHANGED}
         event_type = type_map.get(self._action, Event.SystemEventTypes.UNKNOWN)
         event_data = {'type': event_type}
         if event_type == Event.SystemEventTypes.ONBOARD_TEMP_CHANGED:
             event_data['temperature'] = self._data[0]
         return event_data
     if self.type == Event.Types.UCAN:
         event_data = {'address': self._address_helper.decode(bytearray([self._device_nr & 0xFF]) + self._data[0:2])}
         if self._data[2] == 0 and self._data[3] == 0:
             event_data['type'] = Event.UCANEventTypes.POWER_OUT_ERROR
         elif self._data[2] == 0 and self._data[3] == 1:
             event_data['type'] = Event.UCANEventTypes.POWER_OUT_RESTORED
         elif self._data[2] == 0 and self._data[3] == 2:
             event_data['type'] = Event.UCANEventTypes.POWER_OUT_OFF
         elif self._data[2] == 2 and self._data[3] == 3:
             event_data['type'] = Event.UCANEventTypes.POWER_OUT_ON
         elif self._data[2] == 1:
             event_data.update({'type': Event.UCANEventTypes.I2C_ERROR,
                                'i2c_address': self._data[3]})
         else:
             event_data.update({'type': Event.UCANEventTypes.UNKNOWN,
                                'data': self._data[2:4]})
         return event_data
     if self.type == Event.Types.EXECUTED_BA:
         return {'basic_action': BasicAction(action_type=self._data[0],
                                             action=self._data[1],
                                             device_nr=self._device_nr,
                                             extra_parameter=self._word_decode(self._data[2:4]))}
     if self.type == Event.Types.EXECUTE_GATEWAY_API:
         return {'action': self._data[3],
                 'device_nr': self._device_nr,
                 'extra_parameter': self._word_decode(self._data[0:2])}
     return None
Пример #9
0
    def test_individual_feedback_leds(self):
        output = OutputConfiguration.deserialize({'id': 0})
        # Setup basic LED feedback
        output_dto = OutputDTO(
            id=0,
            can_led_1=FeedbackLedDTO(
                id=5, function=FeedbackLedDTO.Functions.ON_B16_NORMAL),
            can_led_3=FeedbackLedDTO(
                id=7, function=FeedbackLedDTO.Functions.MB_B8_INVERTED))

        # Save led feedback config
        CANFeedbackController.save_output_led_feedback_configuration(
            output, output_dto)

        # Validate correct data in created GA
        self.assertEqual(0, output.output_groupaction_follow)
        group_action = GroupActionController.load_group_action(0)
        self.assertEqual([
            BasicAction(
                action_type=20, action=50, device_nr=5, extra_parameter=65280),
            BasicAction(
                action_type=20, action=51, device_nr=7, extra_parameter=32514)
        ], group_action.actions)
        self.assertEqual('Output 0', group_action.name)

        # Alter GA
        extra_ba = BasicAction(action_type=123, action=123)  # Some random BA
        group_action.actions.append(extra_ba)
        group_action.name = 'Foobar'
        GroupActionController.save_group_action(group_action,
                                                ['name', 'actions'])

        # Validate loading data
        output_dto = OutputDTO(id=0)
        CANFeedbackController.load_output_led_feedback_configuration(
            output, output_dto)
        self.assertEqual(
            FeedbackLedDTO(id=5,
                           function=FeedbackLedDTO.Functions.ON_B16_NORMAL),
            output_dto.can_led_1)
        self.assertEqual(
            FeedbackLedDTO(id=7,
                           function=FeedbackLedDTO.Functions.MB_B8_INVERTED),
            output_dto.can_led_2)  # Moved to 2

        # Change led feedback config
        output_dto.can_led_2.function = FeedbackLedDTO.Functions.ON_B8_INVERTED
        CANFeedbackController.save_output_led_feedback_configuration(
            output, output_dto)

        # Validate stored led feedback data
        output_dto = OutputDTO(id=0)
        CANFeedbackController.load_output_led_feedback_configuration(
            output, output_dto)
        self.assertEqual(
            FeedbackLedDTO(id=5,
                           function=FeedbackLedDTO.Functions.ON_B16_NORMAL),
            output_dto.can_led_1)
        self.assertEqual(
            FeedbackLedDTO(id=7,
                           function=FeedbackLedDTO.Functions.ON_B8_INVERTED),
            output_dto.can_led_2)

        # Validate GA changes
        group_action = GroupActionController.load_group_action(0)
        self.assertEqual([extra_ba] + [
            BasicAction(
                action_type=20, action=50, device_nr=5, extra_parameter=65280),
            BasicAction(
                action_type=20, action=51, device_nr=7, extra_parameter=32512)
        ], group_action.actions)
        self.assertEqual('Foobar', group_action.name)
Пример #10
0
    def test_global_feedback_leds(self):
        global_configuration = GlobalConfiguration()

        # Verify base
        self.assertEqual(65535,
                         global_configuration.groupaction_any_output_changed)
        self.assertEqual(
            {}, CANFeedbackController.load_global_led_feedback_configuration())

        # Store feedback "0" (nr of lights == 0)
        global_feedback_0 = GlobalFeedbackDTO(
            id=0,
            can_led_1=FeedbackLedDTO(
                id=5, function=FeedbackLedDTO.Functions.ON_B16_NORMAL),
            can_led_3=FeedbackLedDTO(
                id=7, function=FeedbackLedDTO.Functions.ON_B8_INVERTED),
            can_led_4=FeedbackLedDTO(
                id=9, function=FeedbackLedDTO.Functions.FB_B8_NORMAL))
        CANFeedbackController.save_global_led_feedback_configuration(
            [global_feedback_0])

        #                                                                                                 +- 256 = MSB is 1 = lights
        # Validate                                                                                        |   +- 0 = Solid on, 1 = Fast blinking
        expected_basic_actions_0 = [
            BasicAction(action_type=20,
                        action=73,
                        device_nr=5,
                        extra_parameter=256 + 0),
            BasicAction(action_type=20,
                        action=73,
                        device_nr=7,
                        extra_parameter=256 + 0),
            BasicAction(action_type=20,
                        action=73,
                        device_nr=9,
                        extra_parameter=256 + 1)
        ]
        expected_global_feedback_0 = GlobalFeedbackDTO(
            id=0,
            can_led_1=FeedbackLedDTO(
                id=5, function=FeedbackLedDTO.Functions.ON_B16_NORMAL),
            can_led_2=FeedbackLedDTO(
                id=7, function=FeedbackLedDTO.Functions.ON_B16_NORMAL),
            can_led_3=FeedbackLedDTO(
                id=9, function=FeedbackLedDTO.Functions.FB_B16_NORMAL))
        global_configuration = GlobalConfiguration()
        group_action = GroupActionController.load_group_action(0)
        self.assertEqual(0,
                         global_configuration.groupaction_any_output_changed)
        self.assertEqual('Global feedback', group_action.name)
        self.assertEqual(expected_basic_actions_0, group_action.actions)
        self.assertEqual(
            {0: expected_global_feedback_0},
            CANFeedbackController.load_global_led_feedback_configuration())

        # Prepare feedback "3" (nr of lights > 2)
        global_feedback_3 = GlobalFeedbackDTO(
            id=3,
            can_led_1=FeedbackLedDTO(
                id=11, function=FeedbackLedDTO.Functions.ON_B16_NORMAL),
            can_led_3=FeedbackLedDTO(
                id=13, function=FeedbackLedDTO.Functions.FB_B8_INVERTED))
        expected_global_feedback_3 = GlobalFeedbackDTO(
            id=3,
            can_led_1=FeedbackLedDTO(
                id=11, function=FeedbackLedDTO.Functions.ON_B16_NORMAL),
            can_led_2=FeedbackLedDTO(
                id=13, function=FeedbackLedDTO.Functions.FB_B16_NORMAL))
        expected_basic_actions_3 = [
            BasicAction(action_type=20,
                        action=71,
                        device_nr=11,
                        extra_parameter=512 + 0),
            BasicAction(action_type=20,
                        action=71,
                        device_nr=13,
                        extra_parameter=512 + 1)
        ]
        #                                                                                                |   +- 0 = Solid on, 1 = Fast blinking
        #                                                                                                +- 512 = MSB is 2 = nr of lights

        # Store in various scenarios, all should yield the same response
        save_scenarios = [[global_feedback_3],
                          [global_feedback_0, global_feedback_3]]
        for save_scenario in save_scenarios:
            CANFeedbackController.save_global_led_feedback_configuration(
                save_scenario)

            global_configuration = GlobalConfiguration()
            group_action = GroupActionController.load_group_action(0)
            self.assertEqual(
                0, global_configuration.groupaction_any_output_changed)
            self.assertEqual(
                expected_basic_actions_0 + expected_basic_actions_3,
                group_action.actions)
            self.assertEqual(
                {
                    0: expected_global_feedback_0,
                    3: expected_global_feedback_3
                },
                CANFeedbackController.load_global_led_feedback_configuration())

        # Add extra BA that should not be removed by altering global feedback
        extra_basic_actions = [BasicAction(action_type=123, action=123)]
        group_action.actions += extra_basic_actions
        group_action.name = 'Foobar'
        GroupActionController.save_group_action(group_action,
                                                ['name', 'actions'])

        # Save without scenario (will re-save data, but should not alter)
        CANFeedbackController.save_global_led_feedback_configuration([])
        group_action = GroupActionController.load_group_action(0)
        self.assertEqual('Foobar', group_action.name)
        self.assertEqual(
            expected_basic_actions_0 + expected_basic_actions_3 +
            extra_basic_actions, group_action.actions)

        # Save full scenario (will remove feedback BAs and save them again at the end of the GA)
        CANFeedbackController.save_global_led_feedback_configuration(
            save_scenarios[1])
        group_action = GroupActionController.load_group_action(0)
        self.assertEqual('Foobar', group_action.name)
        self.assertEqual(
            extra_basic_actions + expected_basic_actions_0 +
            expected_basic_actions_3, group_action.actions)

        # Prepare feedbacks "16" (nr of outputs == 0) and "20" (nr of outputs > 3)
        global_feedback_16 = GlobalFeedbackDTO(
            id=16,
            can_led_1=FeedbackLedDTO(
                id=15, function=FeedbackLedDTO.Functions.ON_B16_NORMAL))
        global_feedback_20 = GlobalFeedbackDTO(
            id=20,
            can_led_1=FeedbackLedDTO(
                id=17, function=FeedbackLedDTO.Functions.ON_B16_NORMAL))
        expected_global_feedback_16 = GlobalFeedbackDTO(
            id=16,
            can_led_1=FeedbackLedDTO(
                id=15, function=FeedbackLedDTO.Functions.ON_B16_NORMAL))
        expected_global_feedback_20 = GlobalFeedbackDTO(
            id=20,
            can_led_1=FeedbackLedDTO(
                id=17, function=FeedbackLedDTO.Functions.ON_B16_NORMAL))
        expected_basic_actions_16 = [
            BasicAction(action_type=20,
                        action=73,
                        device_nr=15,
                        extra_parameter=0 + 0)
        ]  # 0 = MSB is 0 = outputs
        expected_basic_actions_20 = [
            BasicAction(action_type=20,
                        action=70,
                        device_nr=17,
                        extra_parameter=768 + 0)
        ]  # 768 = MSB is 3 = nr of outputs

        # Store
        CANFeedbackController.save_global_led_feedback_configuration([
            global_feedback_0, global_feedback_3, global_feedback_16,
            global_feedback_20
        ])

        # Validate
        global_configuration = GlobalConfiguration()
        group_action = GroupActionController.load_group_action(0)
        self.assertEqual(0,
                         global_configuration.groupaction_any_output_changed)
        self.assertEqual(
            extra_basic_actions + expected_basic_actions_0 +
            expected_basic_actions_3 + expected_basic_actions_16 +
            expected_basic_actions_20, group_action.actions)
        self.assertEqual(
            {
                0: expected_global_feedback_0,
                3: expected_global_feedback_3,
                16: expected_global_feedback_16,
                20: expected_global_feedback_20
            }, CANFeedbackController.load_global_led_feedback_configuration())

        # Remove 3
        empty_global_feedback_3 = GlobalFeedbackDTO(id=3,
                                                    can_led_1=None,
                                                    can_led_2=None,
                                                    can_led_3=None,
                                                    can_led_4=None)
        CANFeedbackController.save_global_led_feedback_configuration(
            [empty_global_feedback_3, global_feedback_20])

        # Validate
        global_configuration = GlobalConfiguration()
        group_action = GroupActionController.load_group_action(0)
        self.assertEqual(0,
                         global_configuration.groupaction_any_output_changed)
        self.assertEqual(
            extra_basic_actions + expected_basic_actions_0 +
            expected_basic_actions_16 + expected_basic_actions_20,
            group_action.actions)
        self.assertEqual(
            {
                0: expected_global_feedback_0,
                16: expected_global_feedback_16,
                20: expected_global_feedback_20
            }, CANFeedbackController.load_global_led_feedback_configuration())
Пример #11
0
 def classic_actions_to_core_actions(classic_actions):  # type: (List[int]) -> List[BasicAction]
     if len(classic_actions) % 2 != 0:
         raise ValueError('Classic actions must be a multiple of two')
     actions = []
     for i in range(0, len(classic_actions), 2):
         action_type = classic_actions[i]
         action_number = classic_actions[i + 1]
         #   0: Simple Action (Old instruction set, please do not use anymore)
         #   1: Simple Decision, ignore THEN/ELSE action, ignore previous decision (Old instruction set, please do not use anymore)
         if action_type == 2:
             #   2: Execute Group Action
             actions.append(BasicAction(action_type=19, action=0, device_nr=action_number))
         #   3: Put this Scheduled Action in the scheduled action queue
         #   4: Used by the system (to indicate in the queue which input has triggered the actions in the queue)
         #   7: Remove this Scheduled Action from the Scheduled action queue
         #   9: Simple Decision, perform THEN/ELSE action, ignore previous decision (Old instruction set, please do not use anymore)
         #  17: Simple Decision, ignore THEN/ELSE action, perform "OR" with previous decision (Old instruction set, please do not use anymore)
         #  25: Simple Decision, perform THEN/ELSE action, perform "OR" with previous decision (Old instruction set, please do not use anymore)
         #  49: Simple Decision, ignore THEN/ELSE action, perform "AND" with previous decision (Old instruction set, please do not use anymore)
         #  57: Simple Decision, perform THEN/ELSE action, perform "AND" with previous decision (Old instruction set, please do not use anymore)
         #  60: Will sent Event (API instruction EV) to the Beagle Bone Black and RTI RS232 port (when enabled) with Event Code x
         #  64: x=0 Put all Modules in lower power state (switch off leds except power and status led), x=1 Normal power state, x=2 normal power state for 2 minutes
         #  65: Flash led of output x
         #  66: Flash led of input x
         #  67: Flash led of sensor x
         elif action_type == 68:
             #  68: Press virtual input x
             actions.append(BasicAction(action_type=1, action=0, device_nr=action_number))
         elif action_type == 69:
             #  69: Release virtual input x
             actions.append(BasicAction(action_type=1, action=1, device_nr=action_number))
         #  70: Switch OFF 5V out off all temperature modules (switch ON 5V will automatically happen after 5 minutes)
         #  71: Switch ON 5V out off all temperature modules
         elif action_type == 72:
             #  72: Reset 12V out power on the gateway so all remote modules connected on BUS1 of the Gateway will get a power reset (Master will not respond during 5 seconds)
             actions.append(BasicAction(action_type=253, action=0, device_nr=0))
             actions.append(BasicAction(action_type=253, action=0, device_nr=1))
         #  73: Switch ON DALI group x, see DALI Installation Guide for more details
         #  74: Switch OFF DALI group x, see DALI Installation Guide for more details
         elif action_type == 75:
             #  75: Switch ON CAN power of all CAN controls (micro CAN's will receive power)
             actions.append(BasicAction(action_type=253, action=1, device_nr=1))
         elif action_type == 76:
             #  76: Switch OFF CAN power of all CAN controls (micro CAN's won't receive any power and will be switched off)
             actions.append(BasicAction(action_type=253, action=1, device_nr=0))
         #  79: This Basic Action will set the CleanTimerQueue setting. When Basic actions are added to the timer queue (for delayed action for example), the Master processor will check for the same Basic actions and remove the previous one. When x=0 -> Clean Timer Queue is disabled, when x<>0 -> Clean Timer Queue is enabled (standard setting) - see #Delaying Instructions and see BA235
         #  80 -> 91: Thermostat related
         elif action_type == 100:
             # 100: Roller/Shutter x up (only to be used in Large Installation mode, x<120)
             actions.append(BasicAction(action_type=10, action=1, device_nr=action_number))
         elif action_type == 101:
             # 101: Roller/Shutter x down (only to be used in Large Installation mode, x<120)
             actions.append(BasicAction(action_type=10, action=2, device_nr=action_number))
         elif action_type == 102:
             # 102: Roller/Shutter x stop (only to be used in Large Installation mode, x<120)
             actions.append(BasicAction(action_type=10, action=0, device_nr=action_number))
         elif action_type == 103:
             # 103: Roller/Shutter x up/stop/down/stop... (only to be used in Large Installation mode, x<120)
             actions.append(BasicAction(action_type=10, action=5, device_nr=action_number))
         # 104: All Roller/Shutters of group x up (only to be used in Large Installation mode, x<30)
         # 105: All Roller/Shutters of group x down (only to be used in Large Installation mode, x<30)
         # 106: All Roller/Shutters of group x stop (only to be used in Large Installation mode, x<30)
         # 107: All Roller/Shutters of group x up/stop/down/stop... (only to be used in Large Installation mode, x<30)
         elif action_type == 108:
             # 108: Roller/Shutter x up/stop/up/stop... (only to be used in Large Installation mode, x<120)
             actions.append(BasicAction(action_type=10, action=3, device_nr=action_number))
         elif action_type == 109:
             # 109: Roller/Shutter x down/stop/down/stop... (only to be used in Large Installation mode, x<120)
             actions.append(BasicAction(action_type=10, action=4, device_nr=action_number))
         # 110: All Roller/Shutters of group x up/stop/up/stop... (only to be used in Large Installation mode, x<30)
         # 111: All Roller/Shutters of group x down/stop/down/stop... (only to be used in Large Installation mode, x<30)
         # 112: The Timer of all Roller/Shutters will be disabled (x=0) or enabled (x=1)
         # 113: Enable/disable automatic Roller/Shutter Lock functionality for all Roller/Shutters: When x=0, the roller/shutters will work normally. When x>0, the Roller/shutters will be locked and the normal BA's to stop, up or down a shutter (or group) will be disabled. When a timer was activated to stop a Roller/shutter, even when the automatic Roller/shutter functionality is enabled, will still be executed.
         # 116: Disable input x (0-239)
         # 117: Enable input x (0-239)
         # 118: Reset Pulse Counters of all Modules
         # 120: Put free variable x (0-31) at zero
         # 121: Decrease free variable x (0-31) with 1
         # 122: Increase free variable x (0-31) with 1
         # 123: Decrease free variable x (0-31) with 2
         # 124: Increase free variable x (0-31) with 2
         # 125: Decrease free variable x (0-31) with 3
         # 126: Increase free variable x (0-31) with 3
         # 128 -> 143: Thermostat related
         # 144: Reserved (for Oled)
         # 145 -> 149: Thermostat related
         # 153: Light/Output x on with std timer and overrule/overwrite timer value when light is already switched on
         # 154: Increase light/output level of output/light x with 1 step until programmed Maximum (63) light level is achieved (x<240)
         # 155: Increase light/output level of output/light x with 2 steps until programmed Maximum (63) light level is achieved (x<240)
         # 156: Increase light/output level of output/light x with 3 steps until programmed Maximum (63) light level is achieved (x<240)
         # 157: Dim light/output x down with 1 step until programmed Minimum light level is achieved (x<240)
         # 158: Dim light/output x down with 2 steps until programmed Minimum light level is achieved (x<240)
         # 159: Dim light/output x down with 3 steps until programmed Minimum light level is achieved (x<240)
         elif action_type == 160:
             # 160: Light/Output x Off (x<240)
             actions.append(BasicAction(action_type=0, action=0, device_nr=action_number))
         elif action_type == 161:
             # 161: Light/Output x On (x<240, with standard timer setting, with last dimmer value)
             actions.append(BasicAction(action_type=0, action=1, device_nr=action_number))
         elif action_type == 162:
             # 162: Toggle light/Output x (x<240, with standard timer setting, with last dimmer value), see #Toggling Lights
             actions.append(BasicAction(action_type=0, action=16, device_nr=action_number))
         elif action_type == 163:
             # 163: All lights off (x=any value but <240)
             actions.append(BasicAction(action_type=0, action=255, device_nr=1))
         elif action_type == 164:
             # 164: All outputs including lights off (x=any value but <240)
             actions.append(BasicAction(action_type=0, action=255, device_nr=2))
         elif action_type == 165:
             # 165: Light/Output x On (x<240, with standard timer setting, at minimum dimmer value)
             actions.append(BasicAction(action_type=0, action=2, device_nr=action_number, extra_parameter=1))
         elif action_type == 166:
             # 166: Light/Output x On (x<240, with standard timer setting, at maximum dimmer value)
             actions.append(BasicAction(action_type=0, action=2, device_nr=action_number, extra_parameter=255))
         # 167: Light/Output x On (x<240, with standard timer setting, decrease dimmer value with 5)
         # 168: Light/Output x On (x<240, with standard timer setting, increase dimmer value with 5)
         elif action_type == 169:
             # 169: Set Dimmer value x at minimum (leaving the output at the current state)
             actions.append(BasicAction(action_type=0, action=9, device_nr=action_number, extra_parameter=1))
         elif action_type == 170:
             # 170: Set Dimmer value x at maximum (leaving the output at the current state)
             actions.append(BasicAction(action_type=0, action=9, device_nr=action_number, extra_parameter=255))
         # 171: All lights OFF of a certain floor level or group (x=floor level or group, x=0..254, when x=255 then all lights are selected)
         # 172: All lights ON of a certain floor level or group (x=floor level or group, x=0..254, when x=255 then all lights are selected)
         # 173: Toggle all lights of a certain floor or group (x=floor level or group, x=0..254, when x=255 then all lights are selected), see #Toggling a Floor
         # 174: Toggle Follow function ON (see #Toggling Lights), action number not used but must be < 240
         # 175: Toggle Follow function OFF (see #Toggling Lights), action number not used but must be < 240
         elif 176 <= action_type <= 184:
             # 176: Light/Output x On with dimmer at 10% (x<240, with standard timer setting)
             # 177: Light/Output x On with dimmer at 20% (x<240, with standard timer setting)
             # ...
             # 183: Light/Output x On with dimmer at 80% (x<240, with standard timer setting)
             # 184: Light/Output x On with dimmer at 90% (x<240, with standard timer setting)
             actions.append(BasicAction(action_type=0, action=2, device_nr=action_number,
                                        extra_parameter={176: 25, 177: 51, 178: 76, 179: 102, 180: 127, 181: 153, 182: 178, 183: 204, 184: 229}[action_type]))
         elif 185 <= action_type <= 194:
             # 185: Toggle/Output light x with dimmer at 10% (x<240, with standard timer setting)
             # 186: Toggle light/Output x with dimmer at 20% (x<240, with standard timer setting)
             # ...
             # 193: Toggle light/Output x with dimmer at 90% (x<240, with standard timer setting)
             # 194: Toggle light/Output x with dimmer at 100% (x<240, with standard timer setting)
             actions.append(BasicAction(action_type=0, action=17, device_nr=action_number,
                                        extra_parameter={185: 25, 186: 51, 187: 76, 188: 102, 189: 127, 190: 153, 191: 178, 192: 204, 193: 229, 194: 255}[action_type]))
         elif 195 <= action_type <= 200:
             # 195: Light/Output x on with timer at 2 min 30 and overrule timer value when light is already switched on (x<240, with last dimmer value) - see #Timers
             # 196: Light/Output x on with timer at 7 min 30 and overrule timer value when light is already switched on (x<240, with last dimmer value) - see #Timers
             # 197: Light/Output x on with timer at 15 min and overrule timer value when light is already switched on (x<240, with last dimmer value) - see #Timers
             # 198: Light/Output x on with timer at 25 min and overrule timer value when light is already switched on (x<240, with last dimmer value) - see #Timers
             # 199: Light/Output x on with timer at 37 min and overrule timer value when light is already switched on (x<240, with last dimmer value) - see #Timers
             # 200: Light/Output x on with timer at 52 min and overrule timer value when light is already switched on (x<240, with last dimmer value) - see #Timers
             actions.append(BasicAction(action_type=0, action=4, device_nr=action_number,
                                        extra_parameter={195: 150, 196: 450, 197: 900, 198: 1500, 199: 2220, 200: 3120}[action_type]))
         elif 201 <= action_type <= 206:
             # 201: Light/Output x on with timer at 2 min 30 but doesn't overrule timer value when light is already switched on (x<240, with last dimmer value)
             # 202: Light/Output x on with timer at 7 min 30 but doesn't overrule timer value when light is already switched on (x<240, with last dimmer value)
             # 203: Light/Output x on with timer at 15 min but doesn't overrule timer value when light is already switched on (x<240, with last dimmer value)
             # 204: Light/Output x on with timer at 25 min but doesn't overrule timer value when light is already switched on (x<240, with last dimmer value)
             # 205: Light/Output x on with timer at 37 min but doesn't overrule timer value when light is already switched on (x<240, with last dimmer value)
             # 206: Light/Output x on with timer at 52 min but doesn't overrule timer value when light is already switched on (x<240, with last dimmer value)
             actions.append(BasicAction(action_type=0, action=7, device_nr=action_number,
                                        extra_parameter={201: 150, 202: 450, 203: 900, 204: 1500, 205: 2220, 206: 3120}[action_type]))
         # 207: When current input is pressed for more than 2 seconds, execute group action x (See #Long Press)
         # 208: When current input is pressed for more than 3 seconds, execute group action x (See #Long Press)
         # 209: When current input is pressed for more than 4 seconds, execute group action x (See #Long Press)
         # 210: When current input is pressed for more than 5 seconds, execute group action x (See #Long Press)
         # 211: When current input is pressed for more than 6 seconds, execute group action x (See Note "Long Press")
         # 212: Switch CAN led x OFF (see #Important Remarks)
         # 213: Switch CAN led x ON (see #Important Remarks)
         # 214: Fast blinking of CAN led x (see #Important Remarks)
         # 215: Medium blinking of CAN led x (see #Important Remarks)
         # 216: Slow blinking of CAN led x (see #Important Remarks)
         # 217: Fade ON/OFF of CAN led x (see #Important Remarks)
         elif action_type in [218, 219]:
             # TODO: Tricky one, these are included in a single action on the Core so both parameters
             #       should be set at the same time. Idea: scan the whole sequence for the other one hoping
             #       that they are both set at the same GroupAction/Input
             # 218: Set minimum brightness of all CAN leds at value x (see #Important Remarks)
             # 219: Set maximum brightness of all CAN leds at value x (see #Important Remarks)
             raise ValueError('Cannot convert multi-instructions')
         # 235: Delay all next instructions with x seconds (x>0 and <249), x=255 -> All next instruction will be executed normally (see #Delaying Instructions and see BA79)
         # 236: Execute all next actions at button release (x=0), x=255 -> All next instructions will be executed normally (see #Additional Actions)
         # 237: Set the freely assigned validation bit x to 1 (x=0 to 255)
         # 238: Set the freely assigned validation bit x to 0 (x=0 to 255)
         # 239: Toggle the freely assigned validation bit x (x=0 to 255)
         elif action_type == 240:
             # 240: IF THEN ELSE ENDIF
             if action_number == 0:  # X=0 -> IF
                 actions.append(BasicAction(action_type=100, action=0))
             elif action_number == 1:  # X=1 -> AND
                 actions.append(BasicAction(action_type=100, action=90))
             elif action_number == 2:  # X=2 -> OR
                 actions.append(BasicAction(action_type=100, action=91))
             elif action_number == 10:  # X=10 -> THEN
                 actions.append(BasicAction(action_type=100, action=150))
             elif action_number == 20:  # X=20 -> ELSE
                 actions.append(BasicAction(action_type=100, action=200))
             elif action_number == 255:  # X=255 -> ENDIF
                 actions.append(BasicAction(action_type=100, action=255))
             else:  # X = 3 -> XOR, X=4 -> NAND, X=5 -> NOR, X=6 -> NXOR
                 # TODO: Implement once available
                 raise ValueError('Cannot convert operators: NAND, NOR, NXOR')
         elif action_type == 241:
             # 241: Check if input x is ON (To be used with IF THEN ELSE instruction)
             actions.append(BasicAction(action_type=100, action=12, device_nr=action_number))
         elif action_type == 242:
             # 242: Check if input x is OFF (To be used with IF THEN ELSE instruction)
             actions.append(BasicAction(action_type=100, action=13, device_nr=action_number))
         elif action_type == 243:
             # 243: Check if Light/Output x is ON (To be used with IF THEN ELSE instruction)
             actions.append(BasicAction(action_type=100, action=10, device_nr=action_number))
         elif action_type == 244:
             # 244: Check if Light/Output x is OFF (To be used with IF THEN ELSE instruction)
             actions.append(BasicAction(action_type=100, action=11, device_nr=action_number))
         elif action_type == 245:
             # 245: Check if Validation bit x is ON (To be used with IF THEN ELSE instruction)
             actions.append(BasicAction(action_type=100, action=14, device_nr=action_number))
         elif action_type == 246:
             # 246: Check if Validation bit x is OFF (To be used with IF THEN ELSE instruction)
             actions.append(BasicAction(action_type=100, action=15, device_nr=action_number))
         elif 247 <= action_type <= 250:
             # TODO: Need multi instruction parsing
             # 247: Check if temperature sensor 0-31 (x=0-31) or if humidity sensor 0-31 (x=32-63) or if light sensor 0-31 (x=64-95) or if temperature setpoint 0-23 (x=96-119) or if free variable 0-31 (x=128-159) or if time hour (x=228) or if time minute (x=229) or if day (x=230) is or if thermostat mode (x=235)  (always to be followed by action type 248 or 249 or 250) (see #Additional Input Values). All environmental parameters are written in System Value
             # 248: is equal to x (to be used always with action type 247) (see #Additional Input Values)
             # 249: is higher than x (to be used always with action type 247) (see #Additional Input Values)
             # 250: is lower than x (to be used always with action type 247) (see #Additional Input Values)
             raise ValueError('Cannot convert multi-instructions')
         elif action_type == 254:
             # 254: Reset the Master
             actions.append(BasicAction(action_type=254, action=0))
     return actions
Пример #12
0
    def test_save_allocations(self):
        """
        This test validates whether writing a GA will store its BAs in the appropriate location. This
        is checked on two ways:
        1. A free space map is generated that is used to validate where the free slots are located
        2. A human readable / visual overview is generated of all BA's action type values in the first 42 addresses
           Legend: X = Used by a few pre-defined GAs
                   n = Actual data, as every insert uses different action types, this can be used to verify
                       whether data is written correctly, as whether the old data is overwritten when needed.
                   _ = Slot that was never used
        Note: As an allocation table is used, the BA space is not cleared, only the reference is removed!
        """
        memory = {}
        for page in range(256, 381):
            memory[page] = bytearray([255] * 256)
        GroupActionTest._setup_master_communicator(memory)

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('__________________________________________', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({4200: [0]}, space_map)

        # Generate "pre-defined" GAs
        for group_action_id, address in {10: 0, 11: 2, 12: 5, 13: 8, 14: 14, 15: 25, 16: (41, 4199)}.items():
            start, end = (address[0], address[1]) if isinstance(address, tuple) else (address, address)
            GroupActionTest._write(memory[GroupActionTest.ADDRESS_START_PAGE], group_action_id * 4, self._word_helper.encode(start) + self._word_helper.encode(end))
            memory[GroupActionTest.ACTIONS_START_PAGE][start * 6] = 100 + group_action_id

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X_X__X__X_____X__________X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({1: [1],
                          2: [3, 6],
                          5: [9],
                          10: [15],
                          15: [26]}, space_map)

        # Store GA with 1 BA
        group_action_1 = GroupAction(id=1, actions=[BasicAction(1, 0)])
        GroupActionController.save_group_action(group_action_1, ['actions'])

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X1X__X__X_____X__________X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({2: [3, 6],
                          5: [9],
                          10: [15],
                          15: [26]}, space_map)

        # Store another GA with 1 BA
        group_action_2 = GroupAction(id=2, actions=[BasicAction(2, 0)])
        GroupActionController.save_group_action(group_action_2, ['actions'])

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X1X2_X__X_____X__________X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({1: [4],
                          2: [6],
                          5: [9],
                          10: [15],
                          15: [26]}, space_map)

        # GA is update dto two BAs
        group_action_2 = GroupAction(id=2, actions=[BasicAction(3, 0),
                                                    BasicAction(3, 0)])
        GroupActionController.save_group_action(group_action_2, ['actions'])

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X1X33X__X_____X__________X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({2: [6],
                          5: [9],
                          10: [15],
                          15: [26]}, space_map)

        # First GA is extended
        group_action_1 = GroupAction(id=1, actions=[BasicAction(4, 0),
                                                    BasicAction(4, 0)])
        GroupActionController.save_group_action(group_action_1, ['actions'])

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X1X33X44X_____X__________X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({1: [1],
                          5: [9],
                          10: [15],
                          15: [26]}, space_map)

        # Add large GA
        group_action_3 = GroupAction(id=3, actions=[BasicAction(5, 0), BasicAction(5, 0), BasicAction(5, 0),
                                                    BasicAction(5, 0), BasicAction(5, 0), BasicAction(5, 0)])
        GroupActionController.save_group_action(group_action_3, ['actions'])

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X1X33X44X_____X555555____X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({1: [1],
                          4: [21],
                          5: [9],
                          15: [26]}, space_map)

        # Large GA is reduced
        group_action_3 = GroupAction(id=3, actions=[BasicAction(6, 0), BasicAction(6, 0), BasicAction(6, 0)])
        GroupActionController.save_group_action(group_action_3, ['actions'])

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X1X33X44X666__X555555____X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({1: [1],
                          2: [12],
                          10: [15],
                          15: [26]}, space_map, 'Reduced GA should be moved')

        # Another GA is added with only one BA
        group_action_4 = GroupAction(id=4, actions=[BasicAction(7, 0)])
        GroupActionController.save_group_action(group_action_4, ['actions'])

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X7X33X44X666__X555555____X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({2: [12],
                          10: [15],
                          15: [26]}, space_map, 'Reduced GA should be moved')

        # Another large GA is added
        group_action_5 = GroupAction(id=5, actions=[BasicAction(8, 0), BasicAction(8, 0), BasicAction(8, 0),
                                                    BasicAction(8, 0)])
        GroupActionController.save_group_action(group_action_5, ['actions'])

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X7X33X44X666__X888855____X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({2: [12],
                          6: [19],
                          15: [26]}, space_map, 'Reduced GA should be moved')

        # Large GA is "deleted"
        group_action_5 = GroupAction(id=5, actions=[])
        GroupActionController.save_group_action(group_action_5, ['actions'])

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X7X33X44X666__X888855____X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({2: [12],
                          10: [15],
                          15: [26]}, space_map, 'Reduced GA should be moved')

        # A GA with too many BAs is added
        group_action_6 = GroupAction(id=6, actions=[BasicAction(8, 0)] * 16)
        with self.assertRaises(RuntimeError):
            GroupActionController.save_group_action(group_action_6, ['actions'])

        space_map = GroupActionController._free_address_space_map()
        self.assertEqual('X7X33X44X666__X888855____X_______________X', GroupActionTest._get_readable_ba_block(memory))
        #                 |    |    |    |    |    |    |    |    |
        #                 0    5    10   15   20   25   30   35   40
        self.assertEqual({2: [12],
                          10: [15],
                          15: [26]}, space_map, 'Memory is not changed')
Пример #13
0
    def test_list_group_actions(self):
        memory = {}
        for page in range(256, 381):
            memory[page] = bytearray([255] * 256)
        GroupActionTest._setup_master_communicator(memory)

        group_actions = GroupActionController.load_group_actions()
        self.assertEqual(256, len(group_actions), 'There should be 256 GAs')
        for i in range(256):
            self.assertFalse(group_actions[i].in_use, 'GA {0} should not be in use'.format(i))

        # Set valid start address
        GroupActionTest._write(memory[GroupActionTest.ADDRESS_START_PAGE], 0 * 4, self._word_helper.encode(0))

        group_actions = GroupActionController.load_group_actions()
        self.assertEqual(256, len(group_actions), 'There should still be 256 GAs')
        for i in range(256):
            self.assertFalse(group_actions[i].in_use, 'GA {0} should not be in use'.format(i))

        group_action = GroupActionController.load_group_action(0)
        self.assertEqual(group_actions[0], group_action, 'The GA is equal (same id, same name, same in_use state)')
        self.assertFalse(group_action.in_use, 'The GA is still not in use')

        # Add BA at start address and set a name
        basic_action_1 = BasicAction(0, 0)
        GroupActionTest._write(memory[GroupActionTest.ACTIONS_START_PAGE], 0 * 6, basic_action_1.encode())
        GroupActionTest._write(memory[GroupActionTest.GANAMES_START_PAGE], 0 * 16, GroupActionTest._encode_string('test'))

        group_action = GroupActionController.load_group_action(0)
        self.assertNotEqual(group_actions[0], group_action, 'The GA changed (name is set)')
        self.assertEqual('test', group_action.name)
        self.assertFalse(group_action.in_use, 'The GA is still not in use')

        # Write valid end address but remove BA
        GroupActionTest._write(memory[GroupActionTest.ADDRESS_START_PAGE], 0 * 4 + 2, self._word_helper.encode(0))
        GroupActionTest._write(memory[GroupActionTest.ACTIONS_START_PAGE], 0 * 6, [255, 255, 255, 255, 255, 255])

        group_action = GroupActionController.load_group_action(0)
        self.assertFalse(group_action.in_use, 'The GA is not in use yet (no BAs defined)')

        # Restore BA
        GroupActionTest._write(memory[GroupActionTest.ACTIONS_START_PAGE], 0 * 6, basic_action_1.encode())

        group_action = GroupActionController.load_group_action(0)
        self.assertTrue(group_action.in_use, 'The GA is now in use (has name and BA)')
        self.assertEqual(1, len(group_action.actions), 'There should be one GA')
        self.assertEqual(basic_action_1, group_action.actions[0], 'The expected BA should be configured')

        # Make the GA point to two BAs
        GroupActionTest._write(memory[GroupActionTest.ADDRESS_START_PAGE], 0 * 4 + 2, self._word_helper.encode(1))

        group_action = GroupActionController.load_group_action(0)
        self.assertTrue(group_action.in_use, 'The GA is still in use')
        self.assertEqual(1, len(group_action.actions), 'An empty BA should be excluded')
        self.assertEqual(basic_action_1, group_action.actions[0], 'The valid BA should still be included')

        # Write second BA
        basic_action_2 = BasicAction(0, 1)
        GroupActionTest._write(memory[GroupActionTest.ACTIONS_START_PAGE], 1 * 6, basic_action_2.encode())

        group_action = GroupActionController.load_group_action(0)
        self.assertTrue(group_action.in_use, 'The GA is still in use')
        self.assertEqual(2, len(group_action.actions), 'Both BAs should be included')
        self.assertEqual([basic_action_1, basic_action_2], group_action.actions, 'The valid BAs should be included')

        group_actions = GroupActionController.load_group_actions()
        self.assertEqual(256, len(group_actions), 'There should be 256 GAs')
        for i in range(1, 256):
            self.assertFalse(group_actions[i].in_use, 'GA {0} should not be in use'.format(i))
        self.assertEqual(group_action, group_actions[0], 'The list should correctly point to the first GA')

        # Set name of third GA, store BA and set addresses
        basic_action_3 = BasicAction(0, 2)
        GroupActionTest._write(memory[GroupActionTest.ACTIONS_START_PAGE], 2 * 6, basic_action_3.encode())
        GroupActionTest._write(memory[GroupActionTest.GANAMES_START_PAGE], 2 * 16, GroupActionTest._encode_string('three'))
        GroupActionTest._write(memory[GroupActionTest.ADDRESS_START_PAGE], 2 * 4, self._word_helper.encode(2))
        GroupActionTest._write(memory[GroupActionTest.ADDRESS_START_PAGE], 2 * 4 + 2, self._word_helper.encode(2))

        group_action_2 = GroupActionController.load_group_action(2)
        group_actions = GroupActionController.load_group_actions()
        self.assertEqual(256, len(group_actions), 'There should be 256 GAs')
        for i in range(0, 256):
            if i in [0, 2]:
                continue
            self.assertFalse(group_actions[i].in_use, 'GA {0} should not be in use'.format(i))
        self.assertEqual(group_action, group_actions[0], 'The list should correctly point to the first GA')
        self.assertEqual(group_action_2, group_actions[2], 'The list should correctly point to the first GA')
Пример #14
0
    def classic_actions_to_core_input_configuration(action, basic_actions):
        # type: (Optional[int], List[int]) -> Dict[str, Any]

        # Default data
        data = {
            'input_link': {
                'output_id': 1023,
                'dimming_up': True,
                'enable_press_and_release': True,
                'enable_1s_press': True,
                'enable_2s_press': True,
                'not_used': True,
                'enable_double_press': True
            },
            'basic_action_press': BasicAction.empty(),
            'basic_action_release': BasicAction.empty(),
            'basic_action_1s_press': BasicAction.empty(),
            'basic_action_2s_press': BasicAction.empty(),
            'basic_action_double_press': BasicAction.empty()
        }  # type: Dict[str, Any]

        # Disabled input
        if action is None or action == 255:
            return data

        # Change default data
        data['input_link'].update({
            'dimming_up': False,
            'enable_press_and_release': False,
            'enable_1s_press': False,
            'enable_2s_press': False,
            'not_used': False,
            'enable_double_press': False
        })

        # If theaction is < 240, it means that the input directly controls an output
        if action < 240:
            data['input_link']['output_id'] = action
            return data

        # If the action is 241 or 242
        if action in [241, 242]:
            data['input_link']['enable_press_and_release'] = True
            data['basic_action_press'] = BasicAction(
                action_type=0, action=255, device_nr=2 if action == 241 else 1)
            return data

        # Otherwise, it means that the input is supposed to execute a list of basic actions
        # but this is not supported anymore on the Core.
        # TODO: Convert any list of actions to one or more group actions and use these instead

        action_types = set(basic_actions[i]
                           for i in range(0, len(basic_actions), 2))

        # Delayed action(s)
        if 207 in action_types:
            if len(basic_actions) != 2:
                raise ValueError(
                    'Timing settings cannot be combined with other actions')
            data['input_link']['enable_2s_press'] = True
            data['basic_action_2s_press'] = BasicAction(
                action_type=19, action=0, device_nr=basic_actions[1])
            return data

        # Possible single on-press actions
        if action_types - {2, 236}:
            actions = GroupActionMapper.classic_actions_to_core_actions(
                basic_actions)
            if len(actions) != 1:
                raise ValueError(
                    'Only simple input configrations are supported')
            data['input_link']['enable_press_and_release'] = True
            data['basic_action_press'] = actions[0]
            return data

        # Press/release actions
        release_data = False
        release_action = None  # type: Optional[int]
        press_action = None  # type: Optional[int]
        for i in range(0, len(basic_actions), 2):
            action_type = basic_actions[i]
            action_number = basic_actions[i + 1]
            if action_type == 236:
                release_data = action_number == 0
            elif release_data:
                release_action = action_number
            else:
                press_action = action_number
        if press_action is not None:
            data['input_link']['enable_press_and_release'] = True
            data['basic_action_press'] = BasicAction(action_type=19,
                                                     action=0,
                                                     device_nr=press_action)
        if release_action is not None:
            data['input_link']['enable_press_and_release'] = True
            data['basic_action_release'] = BasicAction(
                action_type=19, action=0, device_nr=release_action)
        return data
Пример #15
0
    def decode(self, data):  # type: (bytearray) -> BasicAction
        from master.core.basic_action import BasicAction  # Prevent circular import

        return BasicAction.decode(data)
Пример #16
0
 def test_basicaction_field(self):
     self._test_field(MemoryBasicActionField, [[[], BasicAction(10, 10, 0, 0), bytearray([10, 10, 0, 0, 0, 0])],
                                               [[], BasicAction(10, 20, 256, 256), bytearray([10, 20, 1, 0, 1, 0])]])