def configure_mode_settings(self, config): """Processes this mode's configuration settings from a config dictionary. """ if not ('priority' in config and type(config['priority']) is int): config['priority'] = 0 if 'start_events' in config: config['start_events'] = Config.string_to_list( config['start_events']) else: config['start_events'] = list() if 'stop_events' in config: config['stop_events'] = Config.string_to_list( config['stop_events']) else: config['stop_events'] = list() # register mode start events if 'start_events' in config: for event in config['start_events']: self.machine.events.add_handler(event, self.start) self.config['mode'] = config
def configure_led(self, config): if not self.rgb_connection: self.log.critical("A request was made to configure a FAST LED, " "but no connection to an LED processor is " "available") sys.exit() if not self.flag_led_tick_registered: self.machine.events.add_handler('timer_tick', self.update_leds) self.flag_led_tick_registered = True # if the LED number is in <channel> - <led> format, convert it to a # FAST hardware number if '-' in config['number_str']: num = config['number_str'].split('-') config['number'] = int((num[0] * 64) + num[1]) self.config['config_number_format'] = 'int' else: config['number'] = str(config['number']) if self.config['config_number_format'] == 'int': config['number'] = Config.int_to_hex_string(config['number']) else: config['number'] = Config.normalize_hex_string(config['number']) this_fast_led = FASTDirectLED(config['number']) self.fast_leds.add(this_fast_led) return this_fast_led
def __init__(self, slide, machine, dmd_object=None, x=None, y=None, h_pos=None, v_pos=None, layer=0, **kwargs): super(VirtualDMD, self).__init__(slide, x, y, h_pos, v_pos, layer) if not dmd_object: self.dmd_object = machine.display.displays['dmd'] else: self.dmd_object = dmd_object self.config = kwargs self.name = 'VirtualDMD' if self.dmd_object.depth == 8: if 'pixel_color' not in kwargs: self.config['pixel_color'] = 'ff5500' if 'dark_color' not in self.config: self.config['dark_color'] = '221100' if 'pixel_spacing' not in self.config: self.config['pixel_spacing'] = 2 # convert hex colors to list of ints self.config['pixel_color'] = Config.hexstring_to_list( self.config['pixel_color']) self.config['dark_color'] = Config.hexstring_to_list( self.config['dark_color']) # This needs to match the source DMD or it could get weird self.config['shades'] = self.dmd_object.config['shades'] self.palette = mpf.media_controller.display_modules.dmd.create_palette( bright_color=self.config['pixel_color'], dark_color=self.config['dark_color'], steps=self.config['shades']) if ('width' in self.config and 'height' not in self.config): self.config['height'] = self.config['width'] / 4 elif ('height' in self.config and 'width' not in self.config): self.config['width'] = self.config['height'] * 4 elif ('width' not in self.config and 'height' not in self.config): self.config['width'] = 512 self.config['height'] = 128 # Create a Pygame surface for the on screen DMD self.element_surface = pygame.Surface((self.config['width'], self.config['height']), depth=self.dmd_object.depth) if self.dmd_object.depth == 8: self.element_surface.set_palette(self.palette) self.layer = layer self.set_position(x, y, h_pos, v_pos)
def __init__(self, machine, name, player, config): self.machine = machine self.name = name self.player = player self.handler_keys = set() self.enabled = False config_spec = ''' enable_events: list|None disable_events: list|None reset_events: list|None restart_on_complete: boolean|False disable_on_complete: boolean|True ''' self.config = Config.process_config(config_spec=config_spec, source=config) if 'events_when_complete' not in config: self.config['events_when_complete'] = ([ 'logicblock_' + self.name + '_complete' ]) else: self.config['events_when_complete'] = Config.string_to_list( config['events_when_complete']) if 'reset_each_ball' in config and config['reset_each_ball']: if 'ball_starting' not in self.config['reset_events']: self.config['reset_events'].append('ball_starting')
def __init__(self, machine, name, player, config): self.machine = machine self.name = name self.player = player self.handler_keys = set() self.enabled = False config_spec = ''' enable_events: list|None disable_events: list|None reset_events: list|None restart_events: list|None restart_on_complete: boolean|False disable_on_complete: boolean|True ''' self.config = Config.process_config(config_spec=config_spec, source=config) if 'events_when_complete' not in config: self.config['events_when_complete'] = ([ 'logicblock_' + self.name + '_complete']) else: self.config['events_when_complete'] = Config.string_to_list( config['events_when_complete']) if 'reset_each_ball' in config and config['reset_each_ball']: if 'ball_starting' not in self.config['reset_events']: self.config['reset_events'].append('ball_starting')
def receive_sa(self, msg): self.log.debug("Received SA: %s", msg) hw_states = dict() num_local, local_states, num_nw, nw_states = msg.split(',') for offset, byte in enumerate(bytearray.fromhex(nw_states)): for i in range(8): num = Config.int_to_hex_string((offset * 8) + i) if byte & (2**i): hw_states[(num, 1)] = 1 else: hw_states[(num, 1)] = 0 for offset, byte in enumerate(bytearray.fromhex(local_states)): for i in range(8): num = Config.int_to_hex_string((offset * 8) + i) if byte & (2**i): hw_states[(num, 0)] = 1 else: hw_states[(num, 0)] = 0 self.hw_switch_data = hw_states
def __init__(self, machine, name, player, config): self.machine = machine self.name = name self.player = player self.handler_keys = set() self.enabled = False config_spec = """ enable_events: list|None disable_events: list|None reset_events: list|None restart_on_complete: boolean|False disable_on_complete: boolean|True """ self.config = Config.process_config(config_spec=config_spec, source=config) if "events_when_complete" not in config: self.config["events_when_complete"] = ["logicblock_" + self.name + "_complete"] else: self.config["events_when_complete"] = Config.string_to_list(config["events_when_complete"]) if "reset_each_ball" in config and config["reset_each_ball"]: if "ball_starting" not in self.config["reset_events"]: self.config["reset_events"].append("ball_starting")
def _load_mode(self, mode_string): """Loads a mode, reads in its config, and creates the Mode object. Args: mode: String name of the mode you're loading. This is the name of the mode's folder in your game's machine_files/modes folder. """ self.log.debug('Processing mode: %s', mode_string) config = dict() # find the folder for this mode: mode_path = os.path.join( self.machine.machine_path, self.machine.config['media_controller']['paths']['modes'], mode_string) if not os.path.exists(mode_path): mode_path = os.path.abspath( os.path.join( 'mpf', self.machine.config['media_controller']['paths']['modes'], mode_string)) # Is there an MPF default config for this mode? If so, load it first mpf_mode_config = os.path.join( 'mpf', self.machine.config['media_controller']['paths']['modes'], mode_string, 'config', mode_string + '.yaml') if os.path.isfile(mpf_mode_config): config = Config.load_config_file(mpf_mode_config) # Now figure out if there's a machine-specific config for this mode, and # if so, merge it into the config mode_config_folder = os.path.join( self.machine.machine_path, self.machine.config['media_controller']['paths']['modes'], mode_string, 'config') found_file = False for path, _, files in os.walk(mode_config_folder): for file in files: file_root, file_ext = os.path.splitext(file) if file_root == mode_string: config = Util.dict_merge( config, Config.load_config_file(os.path.join(path, file))) found_file = True break if found_file: break return Mode(self.machine, config, mode_string, mode_path)
def __init__(self, machine, name, config, collection=None): self.log = logging.getLogger("Switch." + name) super(Switch, self).__init__(machine, name, config, collection, platform_section="switches") self.machine = machine self.name = name self.config = config self.deactivation_events = list() self.activation_events = list() self.state = 0 """ The logical state of a switch. 1 = active, 0 = inactive. This takes into consideration the NC or NO settings for the switch.""" self.hw_state = 0 """ The physical hardware state of the switch. 1 = active, 0 = inactive. This is what the actual hardware is reporting and does not consider whether a switch is NC or NO.""" # todo read these in and/or change to dict self.type = "NO" """ Specifies whether the switch is normally open ('NO', default) or normally closed ('NC').""" if "type" in config and config["type"].upper() == "NC": self.type = "NC" if "debounce" not in config: config["debounce"] = True if "activation_events" in config: self.activation_events = Config.string_to_lowercase_list(config["activation_events"]) if "deactivation_events" in config: self.deactivation_events = Config.string_to_lowercase_list(config["deactivation_events"]) # We save out number_str since the platform driver will convert the # number into a hardware number, but we need the original number for # some things later. self.config["number_str"] = str(config["number"]).upper() self.last_changed = None self.hw_timestamp = None self.log.debug("Creating '%s' with config: %s", name, config) self.hw_switch, self.number, self.hw_state = self.platform.configure_switch(config) self.log.debug("Current hardware state of switch '%s': %s", self.name, self.hw_state) # If we're using physical hardware, set the initial logical switch # state based on the hw_state if self.machine.physical_hw: if self.type == "NC": self.state = self.hw_state ^ 1 else: self.state = self.hw_state
def _initialize_switches(self): self.update_switches_from_hw() for switch in self.machine.switches: # Populate self.switches self.set_state(switch.name, switch.state, reset_time=True) # Populate self.registered_switches self.registered_switches[switch.name + '-0'] = list() self.registered_switches[switch.name + '-1'] = list() if self.machine.config['mpf']['auto_create_switch_events']: switch.activation_events.add( self.machine.config['mpf']['switch_event_active'].replace( '%', switch.name)) switch.deactivation_events.add( self.machine.config['mpf'][ 'switch_event_inactive'].replace( '%', switch.name)) if 'activation_events' in switch.config: for event in Config.string_to_lowercase_list( switch.config['activation_events']): if "|" in event: ev_name, ev_time = event.split("|") self.add_switch_handler( switch_name=switch.name, callback=self.machine.events.post, state=1, ms=Timing.string_to_ms(ev_time), callback_kwargs={'event': ev_name} ) else: switch.activation_events.add(event) if 'deactivation_events' in switch.config: for event in Config.string_to_lowercase_list( switch.config['deactivation_events']): if "|" in event: ev_name, ev_time = event.split("|") self.add_switch_handler( switch_name=switch.name, callback=self.machine.events.post, state=0, ms=Timing.string_to_ms(ev_time), callback_kwargs={'event': ev_name} ) else: switch.deactivation_events.add(event)
def _load_mode(self, mode_string): """Loads a mode, reads in its config, and creates the Mode object. Args: mode: String name of the mode you're loading. This is the name of the mode's folder in your game's machine_files/modes folder. """ self.log.debug('Processing mode: %s', mode_string) config = dict() # find the folder for this mode: mode_path = os.path.join(self.machine.machine_path, self.machine.config['media_controller']['paths']['modes'], mode_string) if not os.path.exists(mode_path): mode_path = os.path.abspath(os.path.join('mpf', self.machine.config['media_controller']['paths']['modes'], mode_string)) # Is there an MPF default config for this mode? If so, load it first mpf_mode_config = os.path.join( 'mpf', self.machine.config['media_controller']['paths']['modes'], mode_string, 'config', mode_string + '.yaml') if os.path.isfile(mpf_mode_config): config = Config.load_config_file(mpf_mode_config) # Now figure out if there's a machine-specific config for this mode, and # if so, merge it into the config mode_config_folder = os.path.join(self.machine.machine_path, self.machine.config['media_controller']['paths']['modes'], mode_string, 'config') found_file = False for path, _, files in os.walk(mode_config_folder): for file in files: file_root, file_ext = os.path.splitext(file) if file_root == mode_string: config = Util.dict_merge(config, Config.load_config_file(os.path.join(path, file))) found_file = True break if found_file: break return Mode(self.machine, config, mode_string, mode_path)
def _load_config(self): # creates the main config dictionary from the YAML machine config files. self.config = dict() # load the MPF config & machine defaults self.config = Config.load_config_yaml( config=self.config, yaml_file=self.options['mpfconfigfile']) # Find the machine_files location. If it starts with a forward or # backward slash, then we assume it's from the mpf root. Otherwise we # assume it's from the subfolder location specified in the # mpfconfigfile location if (self.options['machinepath'].startswith('/') or self.options['machinepath'].startswith('\\')): machine_path = self.options['machinepath'] else: machine_path = os.path.join( self.config['mpf']['paths']['machine_files'], self.options['machinepath']) self.machine_path = os.path.abspath(machine_path) # Add the machine folder to our path so we can import modules from it sys.path.append(self.machine_path) self.log.info("Machine folder: %s", machine_path) # Now find the config file location. Same as machine_file with the # slash uses to specify an absolute path if (self.options['configfile'].startswith('/') or self.options['configfile'].startswith('\\')): config_file = self.options['configfile'] else: if not self.options['configfile'].endswith('.yaml'): self.options['configfile'] += '.yaml' config_file = os.path.join(machine_path, self.config['mpf']['paths']['config'], self.options['configfile']) self.log.info("Base machine config file: %s", config_file) # Load the machine-specific config self.config = Config.load_config_yaml(config=self.config, yaml_file=config_file)
def _load_config(self): # creates the main config dictionary from the YAML machine config files. self.config = dict() # load the MPF config & machine defaults self.config = Config.load_config_yaml(config=self.config, yaml_file=self.options['mpfconfigfile']) # Find the machine_files location. If it starts with a forward or # backward slash, then we assume it's from the mpf root. Otherwise we # assume it's from the subfolder location specified in the # mpfconfigfile location if (self.options['machinepath'].startswith('/') or self.options['machinepath'].startswith('\\')): machine_path = self.options['machinepath'] else: machine_path = os.path.join(self.config['mpf']['paths'] ['machine_files'], self.options['machinepath']) self.machine_path = os.path.abspath(machine_path) # Add the machine folder to our path so we can import modules from it sys.path.append(self.machine_path) self.log.info("Machine folder: %s", machine_path) # Now find the config file location. Same as machine_file with the # slash uses to specify an absolute path if (self.options['configfile'].startswith('/') or self.options['configfile'].startswith('\\')): config_file = self.options['configfile'] else: if not self.options['configfile'].endswith('.yaml'): self.options['configfile'] += '.yaml' config_file = os.path.join(machine_path, self.config['mpf']['paths']['config'], self.options['configfile']) self.log.info("Base machine config file: %s", config_file) # Load the machine-specific config self.config = Config.load_config_yaml(config=self.config, yaml_file=config_file)
def __init__(self, machine, name, config, receive_queue): self.log = logging.getLogger('BCPClient.' + name) self.log.info('Setting up BCP Client...') self.machine = machine self.name = name self.receive_queue = receive_queue config_spec = ''' host: string port: int|5050 connection_attempts: int|-1 require_connection: boolean|False ''' self.config = Config.process_config(config_spec, config) self.sending_queue = Queue() self.receive_thread = None self.sending_thread = None self.socket = None self.connection_attempts = 0 self.attempt_socket_connection = True self.send_goodbye = True self.bcp_commands = { 'hello': self.receive_hello, 'goodbye': self.receive_goodbye, } self.setup_client_socket()
def enable(self): """Enables the shot.""" super(StandardShot, self).enable() for switch in Config.string_to_list(self.config['switch']): self.machine.switch_controller.add_switch_handler( switch, self._switch_handler, return_info=True)
def __init__(self, machine, name, config, collection=None, validate=True): self.shots = list() # list of strings for shot in Config.string_to_list(config['shots']): self.shots.append(machine.shots[shot]) # If this device is setup in a machine-wide config, make sure it has # a default enable event. # TODO add a mode parameter to the device constructor and do the logic # there. if not machine.modes: if 'enable_events' not in config: config['enable_events'] = 'ball_starting' if 'disable_events' not in config: config['disable_events'] = 'ball_ended' if 'reset_events' not in config: config['reset_events'] = 'ball_ended' if 'profile' in config: for shot in self.shots: shot.update_enable_table(profile=config['profile'], mode=None) super(ShotGroup, self).__init__(machine, name, config, collection, validate=validate) self.rotation_enabled = True if self.debug: self._enable_related_device_debugging()
def disable(self): """Disables the shot.""" super(StandardShot, self).disable() for switch in Config.string_to_list(self.config['switch']): self.machine.switch_controller.remove_switch_handler( switch, self._switch_handler)
def __init__(self, machine, name, config, priority): """SequenceShot is where you need certain switches to be hit in the right order, possibly within a time limit. Subclass of `Shot` Args: machine: The MachineController object name: String name of this shot. config: Dictionary that holds the configuration for this shot. """ super(SequenceShot, self).__init__(machine, name, config, priority) self.delay = DelayManager() self.progress_index = 0 """Tracks how far along through this sequence the current shot is.""" # convert our switches config to a list if 'switches' in self.config: self.config['switches'] = \ Config.string_to_list(self.config['switches']) # convert our timout to ms if 'time' in self.config: self.config['time'] = Timing.string_to_ms(self.config['time']) else: self.config['time'] = 0 self.active_delay = False self.enable()
def initialize_hw_states(self): """Reads and processes the hardware states of the physical switches. We can't do this in __init__() because we need the switch controller to be setup first before we set up the hw switches. This method is called via an event handler which listens for `init_phase_2`. """ start_active = list() if not self.machine.physical_hw: try: start_active = Config.string_to_lowercase_list( self.machine.config['virtual platform start active switches']) except KeyError: pass self.log.debug("Syncing the logical and physical switch states.") for switch in self.machine.switches: if switch.name in start_active: switch.state = 1 self.set_state(switch.name, switch.state, reset_time=True)
def set_brightness_compensation(self, value): """Sets the brightness compensation for this LED. args: value: Str or list (of 1-to-3 items) of the new brightness compensation value to set. List items are floats. 1.0 is standard full brightness. 0.0 is off. 2.0 is 200% brightness (which only comes into play if the LED is not at full brightness). If the value is a string, it's converted to a list, broken by commas. The brightness compensation list is three items long, one for each RGB element. If the LED has less than three elements, additional values are ignored. If the value list is only one item, that value is used for all three elements. If the value list is two items, a value of 1.0 is used for the third item. """ if type(value) is not list: value = Config.string_to_list(value) value = [float(x) for x in value] if len(value) == 1: value.extend([value[0], value[0]]) elif len(value) == 2: value.append(1.0) self.config["brightness_compensation"] = value
def __init__(self, machine, config): super(FadeCandyOPClient, self).__init__(machine, config) self.log = logging.getLogger('FadeCandyClient') self.update_every_tick = True self.gamma = self.machine.config['ledsettings']['gamma'] self.whitepoint = Config.string_to_list( self.machine.config['ledsettings']['whitepoint']) self.whitepoint[0] = float(self.whitepoint[0]) self.whitepoint[1] = float(self.whitepoint[1]) self.whitepoint[2] = float(self.whitepoint[2]) self.linear_slope = ( self.machine.config['ledsettings']['linear_slope']) self.linear_cutoff = ( self.machine.config['ledsettings']['linear_cutoff']) self.keyframe_interpolation = ( self.machine.config['ledsettings']['keyframe_interpolation']) self.dithering = self.machine.config['ledsettings']['dithering'] if not self.dithering: self.disable_dithering() if not self.keyframe_interpolation: self.update_every_tick = False self.set_global_color_correction() self.write_firmware_options()
def _load_mode(self, mode_string): """Loads a mode, reads in its config, and creates the Mode object. Args: mode: String name of the mode you're loading. This is the name of the mode's folder in your game's machine_files/modes folder. """ self.log.info('Processing mode: %s', mode_string) mode_path = os.path.join(self.machine.machine_path, self.machine.config['mediacontroller']['paths']['modes'], mode_string) mode_config_file = os.path.join(self.machine.machine_path, self.machine.config['mediacontroller']['paths']['modes'], mode_string, 'config', mode_string + '.yaml') config = Config.load_config_yaml(yaml_file=mode_config_file) if 'code' in config['mode']: import_str = ('modes.' + mode_string + '.code.' + config['mode']['code'].split('.')[0]) i = __import__(import_str, fromlist=['']) mode_object = getattr(i, config['mode']['code'].split('.')[1])( self.machine, config, mode_string, mode_path) else: mode_object = Mode(self.machine, config, mode_string, mode_path) return mode_object
def _load_plugins(self): for plugin in Config.string_to_list(self.config['mpf']['plugins']): self.log.info("Loading '%s' plugin", plugin) i = __import__('mpf.plugins.' + plugin, fromlist=['']) self.plugins.append(i.plugin_class(self))
def __init__(self, machine): self.log = logging.getLogger('switch_player') if 'switch_player' not in machine.config: machine.log.debug('"switch_player:" section not found in ' 'machine configuration, so the Switch Player' 'plugin will not be used.') return self.machine = machine self.delay = DelayManager() self.current_step = 0 config_spec = ''' start_event: string|machine_reset_phase_3 start_delay: secs|0 ''' self.config = Config.process_config( config_spec, self.machine.config['switch_player']) self.machine.events.add_handler(self.config['start_event'], self._start_event_callback) self.step_list = self.config['steps'] self.start_delay = self.config['start_delay']
def __init__(self, machine): self.log = logging.getLogger('switchplayer') if 'switchplayer' not in machine.config: machine.log.debug('"switchplayer:" section not found in ' 'machine configuration, so the Switch Player' 'plugin will not be used.') return self.machine = machine self.delay = DelayManager() self.current_step = 0 config_spec = ''' start_event: string|machine_reset_phase_3 start_delay: secs|0 ''' self.config = Config.process_config(config_spec, self.machine.config['switchplayer']) self.machine.events.add_handler(self.config['start_event'], self._start_event_callback) self.step_list = self.config['steps'] self.start_delay = self.config['start_delay']
def _initialize_switches(self): # Set "start active" switches start_active = list() if not self.machine.physical_hw: try: start_active = Config.string_to_lowercase_list( self.machine. config['virtual platform start active switches']) except KeyError: pass for switch in self.machine.switches: # Populate self.switches if switch.name in start_active: switch.state = 1 # set state based on physical state self.set_state(switch.name, switch.state, reset_time=True) # Populate self.registered_switches self.registered_switches[switch.name + '-0'] = list() self.registered_switches[switch.name + '-1'] = list()
def __init__(self, machine, name, config, collection, member_collection=None, device_str=None): self.device_str = 'drop_targets' self.log = logging.getLogger('DropTargetBank.' + name) super(DropTargetBank, self).__init__(machine, name, config, collection, machine.drop_targets, self.device_str) # set config defaults if 'reset_events' not in self.config: self.config['reset_events'] = None if 'reset_coils' in self.config: self.config['reset_coils'] = Config.string_to_list( self.config['reset_coils']) # can't read the switches until the switch controller is set up self.machine.events.add_handler('init_phase_1', self.update_count)
def __init__(self, machine, name, config, receive_queue): self.log = logging.getLogger('BCPClient.' + name) self.log.info('Setting up BCP Client...') self.machine = machine self.name = name self.receive_queue = receive_queue config_spec = ''' host: string port: int|5050 connection_attempts: int|-1 require_connection: boolean|False ''' self.config = Config.process_config(config_spec, config) self.sending_queue = Queue() self.receive_thread = None self.sending_thread = None self.socket = None self.connection_attempts = 0 self.attempt_socket_connection = True self.send_goodbye = True self.bcp_commands = {'hello': self.receive_hello, 'goodbye': self.receive_goodbye, } self.setup_client_socket()
def _load_mode(self, mode_string): """Loads a mode, reads in its config, and creates the Mode object. Args: mode: String name of the mode you're loading. This is the name of the mode's folder in your game's machine_files/modes folder. """ self.log.info('Processing mode: %s', mode_string) mode_path = os.path.join( self.machine.machine_path, self.machine.config['mediacontroller']['paths']['modes'], mode_string) mode_config_file = os.path.join( self.machine.machine_path, self.machine.config['mediacontroller']['paths']['modes'], mode_string, 'config', mode_string + '.yaml') config = Config.load_config_yaml(yaml_file=mode_config_file) if 'code' in config['mode']: import_str = ('modes.' + mode_string + '.code.' + config['mode']['code'].split('.')[0]) i = __import__(import_str, fromlist=['']) mode_object = getattr(i, config['mode']['code'].split('.')[1])( self.machine, config, mode_string, mode_path) else: mode_object = Mode(self.machine, config, mode_string, mode_path) return mode_object
def __init__(self, machine): self.log = logging.getLogger("switch_player") if "switch_player" not in machine.config: machine.log.debug( '"switch_player:" section not found in ' "machine configuration, so the Switch Player" "plugin will not be used." ) return self.machine = machine self.delay = DelayManager() self.current_step = 0 config_spec = """ start_event: string|machine_reset_phase_3 start_delay: secs|0 """ self.config = Config.process_config(config_spec, self.machine.config["switch_player"]) self.machine.events.add_handler(self.config["start_event"], self._start_event_callback) self.step_list = self.config["steps"] self.start_delay = self.config["start_delay"]
def _load_plugins(self): for plugin in Config.string_to_list( self.config['mpf']['plugins']): self.log.info("Loading '%s' plugin", plugin) i = __import__('mpf.plugins.' + plugin, fromlist=['']) self.plugins.append(i.plugin_class(self))
def _initialize(self): # Initializes the auditor. We do this separate from __init__() since # we need everything else to be setup first. config = """ save_events: list|ball_ended audit: list|None events: list|None player: list|None num_player_top_records: int|10 """ self.config = Config.process_config(config, self.machine.config["auditor"]) self.filename = os.path.join(self.machine.machine_path, self.machine.config["mpf"]["paths"]["audits"]) # todo add option for abs path outside of machine root self.current_audits = self.load_from_disk(self.filename) self.make_sure_path_exists(os.path.dirname(self.filename)) if not self.current_audits: self.current_audits = dict() # Make sure we have all the sections we need in our audit dict if "switches" not in self.current_audits: self.current_audits["switches"] = dict() if "events" not in self.current_audits: self.current_audits["events"] = dict() if "player" not in self.current_audits: self.current_audits["player"] = dict() # Make sure we have all the switches in our audit dict for switch in self.machine.switches: if switch.name not in self.current_audits["switches"]: self.current_audits["switches"][switch.name] = 0 # Make sure we have all the player stuff in our audit dict if "player" in self.config["audit"]: for item in self.config["player"]: if item not in self.current_audits["player"]: self.current_audits["player"][item] = dict() self.current_audits["player"][item]["top"] = list() self.current_audits["player"][item]["average"] = 0 self.current_audits["player"][item]["total"] = 0 # Register for the events the auditor needs to do its job self.machine.events.add_handler("game_starting", self.enable) self.machine.events.add_handler("game_ended", self.disable) if "player" in self.config["audit"]: self.machine.events.add_handler("game_ending", self.audit_player) # Enable the shots monitor Shot.monitor_enabled = True self.machine.register_monitor("shots", self.audit_shot)
def configure_matrixlight(self, config): if not self.net_connection: self.log.critical("A request was made to configure a FAST matrix " "light, but no connection to a NET processor is " "available") sys.exit() if self.machine_type == 'wpc': # translate number to FAST light num config['number'] = self.wpc_light_map.get( config['number_str'].upper()) elif self.config['config_number_format'] == 'int': config['number'] = Config.int_to_hex_string(config['number']) else: config['number'] = Config.normalize_hex_string(config['number']) return (FASTMatrixLight(config['number'], self.net_connection.send), config['number'])
def register_mpfmc_trigger_events(self, config, **kwargs): """Scans an MPF config file and creates trigger events for the config settings that need them. Args: config: An MPF config dictionary (can be the machine-wide or a mode- specific one). **kwargs: Not used. Included to catch any additional kwargs that may be associted with this method being registered as an event handler. """ self.log.debug("Registering Trigger Events") try: for event in config['show_player'].keys(): self.create_trigger_event(event) except KeyError: pass try: for event in config['slide_player'].keys(): self.create_trigger_event(event) except KeyError: pass try: for event in config['event_player'].keys(): self.create_trigger_event(event) except KeyError: pass try: for k, v in config['sound_player'].iteritems(): if 'start_events' in v: for event in Config.string_to_list(v['start_events']): self.create_trigger_event(event) if 'stop_events' in v: for event in Config.string_to_list(v['stop_events']): self.create_trigger_event(event) except KeyError: pass
def _configure(self): self.config = self.machine.config["languages"] self.machine.language = self self.languages = Config.string_to_lowercase_list(self.machine.config["languages"]) # Set the default language to the first entry in the list self.set_language(self.languages[0]) self.default_language = self.languages[0] self.find_text = re.compile("(\(.*?\))")
def _configure(self): self.config = self.machine.config['languages'] self.machine.language = self self.languages = Config.string_to_list( self.machine.config['languages']) # Set the default language to the first entry in the list self.set_language(self.languages[0]) self.default_language = self.languages[0] self.find_text = re.compile('(\(.*?\))')
def configure_driver(self, config, device_type='coil'): if not self.net_connection: self.log.critical("A request was made to configure a FAST driver, " "but no connection to a NET processor is " "available") sys.exit() # If we have WPC driver boards, look up the driver number if self.machine_type == 'wpc': config['number'] = self.wpc_driver_map.get( config['number_str'].upper()) if ('connection' in config and config['connection'].lower() == 'network'): config['connection'] = 1 else: config['connection'] = 0 # local driver (default for WPC) # If we have fast driver boards, we need to make sure we have hex strs elif self.machine_type == 'fast': if self.config['config_number_format'] == 'int': config['number'] = Config.int_to_hex_string(config['number']) else: config['number'] = Config.normalize_hex_string(config['number']) # Now figure out the connection type if ('connection' in config and config['connection'].lower() == 'local'): config['connection'] = 0 else: config['connection'] = 1 # network driver (default for FAST) else: self.log.critical("Invalid machine type: {0{}}".format( self.machine_type)) sys.exit() return (FASTDriver(config, self.net_connection.send), (config['number'], config['connection']))
def _load(self, callback, show_actions=None): self.show_actions = list() self.asset_manager.log.debug("Loading Show %s", self.file_name) if not show_actions: show_actions = self.load_show_from_disk() for step_num in range(len(show_actions)): step_actions = dict() step_actions['tocks'] = show_actions[step_num]['tocks'] # look for empty steps. If we find them we'll just add their tock # time to the previous step. if len(show_actions[step_num]) == 1: # 1 because it still has tocks show_actions[-1]['tocks'] += step_actions['tocks'] continue # Events # make sure events is a list of strings if ('events' in show_actions[step_num] and show_actions[step_num]['events']): event_list = (Config.string_to_list( show_actions[step_num]['events'])) step_actions['events'] = event_list # SlidePlayer if ('display' in show_actions[step_num] and show_actions[step_num]['display']): step_actions['display'] = ( self.machine.display.slidebuilder.preprocess_settings( show_actions[step_num]['display'])) self.show_actions.append(step_actions) # count how many total locations are in the show. We need this later # so we can know when we're at the end of a show self.total_locations = len(self.show_actions) self.loaded = True if callback: callback() self._asset_loaded()
def _load_machine_config(self): for num, config_file in enumerate(self.options['configfile']): if not (config_file.startswith('/') or config_file.startswith('\\')): config_file = os.path.join(self.machine_path, self.config['mpf']['paths']['config'], config_file) self.log.info("Machine config file #%s: %s", num+1, config_file) self.config = Util.dict_merge(self.config, Config.load_config_file(config_file))
def _load_machine_config(self): for num, config_file in enumerate(self.options['configfile']): if not (config_file.startswith('/') or config_file.startswith('\\')): config_file = os.path.join(self.machine_path, self.config['media_controller']['paths']['config'], config_file) self.log.info("Machine config file #%s: %s", num+1, config_file) self.config = Util.dict_merge(self.config, Config.load_config_file(config_file))
def __init__(self, machine): if 'osc' not in machine.config: machine.log.debug('"OSC:" section not found in the machine ' 'configuration, so the OSC plugin will not ' 'be used.') return if not import_success: machine.log.warning('OSC plugin requires PyOSC which does not ' 'appear to be installed. No prob, but FYI ' 'that the OSC will not be available.') return self.log = logging.getLogger('osc') self.machine = machine config_spec = ''' client_port: int|8000 debug_messages: boolean|False ''' self.config = Config.process_config(config_spec, self.machine.config['osc']) if self.config['machine_ip'].upper() == 'AUTO': self.config['machine_ip'] = socket.gethostbyname( socket.gethostname()) if 'client_updates' in self.config: self.config['client_updates'] = self.config['client_updates'].split( ' ') else: self.config['client_updates'] = None self.OSC_clients = dict() self.client_needs_sync = False self.client_last_update_time = None self.last_loop_time = 1 self.client_mode = 'name' self.clients_to_delete = list() self.clients_to_add = list() # If this machine uses WPC driver boards then we can drive devices by # if self.machine.config['hardware']['driverboards'][0:3] == 'wpc': self.wpc = True else: self.wpc = False # register for events self.machine.events.add_handler('init_phase_4', self.start)
def __init__(self, machine): if 'osc' not in machine.config: machine.log.debug('"OSC:" section not found in the machine ' 'configuration, so the OSC plugin will not ' 'be used.') return if not import_success: machine.log.warning('OSC plugin requires PyOSC which does not ' 'appear to be installed. No prob, but FYI ' 'that the OSC will not be available.') return self.log = logging.getLogger('osc') self.machine = machine config_spec = ''' client_port: int|8000 debug_messages: boolean|False ''' self.config = Config.process_config(config_spec, self.machine.config['osc']) if self.config['machine_ip'].upper() == 'AUTO': self.config['machine_ip'] = socket.gethostbyname( socket.gethostname()) if 'client_updates' in self.config: self.config['client_updates'] = self.config[ 'client_updates'].split(' ') else: self.config['client_updates'] = None self.OSC_clients = dict() self.client_needs_sync = False self.client_last_update_time = None self.last_loop_time = 1 self.client_mode = 'name' self.clients_to_delete = list() self.clients_to_add = list() # If this machine uses WPC driver boards then we can drive devices by # if self.machine.config['hardware']['driverboards'][0:3] == 'wpc': self.wpc = True else: self.wpc = False # register for events self.machine.events.add_handler('init_phase_4', self.start)
def register_mpfmc_trigger_events(self, config, **kwargs): """Scans an MPF config file and creates trigger events for the config settings that need them. Args: config: An MPF config dictionary (can be the machine-wide or a mode- specific one). **kwargs: Not used. Included to catch any additional kwargs that may be associted with this method being registered as an event handler. """ self.log.debug("Registering Trigger Events") try: for event in config['showplayer'].keys(): self.create_trigger_event(event) except KeyError: pass try: for event in config['slideplayer'].keys(): self.create_trigger_event(event) except KeyError: pass try: for k, v in config['soundplayer'].iteritems(): if 'start_events' in v: for event in Config.string_to_list(v['start_events']): self.create_trigger_event(event) if 'stop_events' in v: for event in Config.string_to_list(v['stop_events']): self.create_trigger_event(event) except KeyError: pass
def _event_config_to_dict(self, config): # processes the enable, disable, and reset events from the config file return_dict = dict() if type(config) is dict: return config elif type(config) is str: config = Config.string_to_list(config) # 'if' instead of 'elif' to pick up just-converted str if type(config) is list: for event in config: return_dict[event] = 0 return return_dict
def __init__(self, machine, name, player, config): self.log = logging.getLogger('Sequence.' + name) self.log.debug("Creating Sequence LogicBlock") super(Sequence, self).__init__(machine, name, player, config) config_spec = ''' events: list_of_lists ''' self.config = Config.process_config(config_spec=config_spec, source=self.config) if 'player_variable' not in config: self.config['player_variable'] = self.name + '_step' self.player[self.config['player_variable']] = 0
def __init__(self, machine): super(HardwarePlatform, self).__init__(machine) self.log = logging.getLogger('SmartMatrix') self.log.info("Configuring SmartMatrix hardware interface.") self.dmd_frame = bytearray() self.queue = None config_spec = ''' port: string use_separate_thread: boolean|True ''' self.config = Config.process_config( config_spec=config_spec, source=self.machine.config['smartmatrix'])
def __init__(self, machine, name, player, config): self.log = logging.getLogger('Accrual.' + name) self.log.debug("Creating Accrual LogicBlock") super(Accrual, self).__init__(machine, name, player, config) config_spec = ''' events: list_of_lists ''' self.config = Config.process_config(config_spec=config_spec, source=self.config) if 'player_variable' not in config: self.config['player_variable'] = self.name + '_status' # populate status list self.player[self.config['player_variable']] = ( [False] * len(self.config['events']))
def __init__(self, options): self.options = options self.log = logging.getLogger('machinewizard') self.log.info("MPF Wizard v%s", version.__version__) self.log.debug("Init Options: {}".format(self.options)) self.verify_system_info() self.done = False self.machine_path = None # Path to this machine's folder root FileManager.init() self.mpfconfig = dict() self.mpfconfig = Config.load_config_file(self.options['mpfconfigfile']) self.config_files = dict() #self.config = Config.load_config_file(self.options['mpfconfigfile']) self._set_machine_path() self._load_config_from_files() self.log.info('machine config loaded')
def __init__(self, machine, name, player, config): self.log = logging.getLogger('Counter.' + name) self.log.debug("Creating Counter LogicBlock") super(Counter, self).__init__(machine, name, player, config) self.delay = DelayManager() self.ignore_hits = False self.hit_value = -1 config_spec = ''' count_events: list|None count_complete_value: int|0 multiple_hit_window: ms|0 count_interval: int|1 direction: string|up starting_count: int|0 ''' self.config = Config.process_config(config_spec=config_spec, source=self.config) if 'event_when_hit' not in self.config: self.config['event_when_hit'] = ('counter_' + self.name + '_hit') if 'player_variable' not in self.config: self.config['player_variable'] = self.name + '_count' self.hit_value = self.config['count_interval'] if self.config['direction'] == 'down' and self.hit_value > 0: self.hit_value *= -1 elif self.config['direction'] == 'up' and self.hit_value < 0: self.hit_value *= -1 self.player[self.config['player_variable']] = ( self.config['starting_count'])
def load(self, filename, verify_version=True, halt_on_error=False): """Loads a YAML file from disk. Args: filename: The file to load. verify_version: Boolean which specifies whether this method should verify whether this file's config_version is compatible with this version of MPF. Default is True. halt_on_error: Boolean which controls what happens if the file can't be loaded. (Not found, invalid format, etc. If True, MPF will raise an error and exit. If False, an empty config dictionary will be returned. Returns: A dictionary of the settings from this YAML file. """ if verify_version and not Config.check_config_file_version(filename): raise Exception( "Config file version mismatch: {}".format(filename)) try: self.log.debug("Loading configuration file: %s", filename) with open(filename, 'r') as f: config = Util.keys_to_lower(yaml.load(f)) except yaml.YAMLError, exc: if hasattr(exc, 'problem_mark'): mark = exc.problem_mark self.log.critical( "Error found in config file %s. Line %s, " "Position %s", filename, mark.line + 1, mark.column + 1) if halt_on_error: sys.exit() else: config = dict()
def __init__(self, machine, name, config, collection=None): self.log = logging.getLogger('BallDevice.' + name) super(BallDevice, self).__init__(machine, name, config, collection) self.delay = DelayManager() # set config defaults if 'exit_count_delay' not in self.config: self.config['exit_count_delay'] = ".5s" # todo make optional if 'entrance_count_delay' not in self.config: self.config['entrance_count_delay'] = "0.5s" if 'eject_coil' not in self.config: self.config['eject_coil'] = None if 'eject_switch' not in self.config: self.config['eject_switch'] = None if 'entrance_switch' not in self.config: self.config['entrance_switch'] = None if 'jam_switch' not in self.config: self.config['jam_switch'] = None if 'eject_coil_hold_times' not in self.config: self.config['eject_coil_hold_times'] = list() if 'confirm_eject_type' not in self.config: self.config['confirm_eject_type'] = 'count' # todo make optional? if 'eject_targets' not in self.config: self.config['eject_targets'] = ['playfield'] else: self.config['eject_targets'] = Config.string_to_list( self.config['eject_targets']) if 'eject_timeouts' not in self.config: self.config['eject_timeouts'] = list() else: self.config['eject_timeouts'] = Config.string_to_list( self.config['eject_timeouts']) if 'confirm_eject_switch' not in self.config: self.config['confirm_eject_switch'] = None if 'confirm_eject_event' not in self.config: self.config['confirm_eject_event'] = None if 'balls_per_eject' not in self.config: self.config['balls_per_eject'] = 1 if 'max_eject_attempts' not in self.config: self.config['max_eject_attempts'] = 0 if 'ball_switches' in self.config: self.config['ball_switches'] = Config.string_to_list( self.config['ball_switches']) else: self.config['ball_switches'] = [] if 'ball_capacity' not in self.config: self.config['ball_capacity'] = len(self.config['ball_switches']) if 'debug' not in self.config: self.config['debug'] = False # initialize variables self.balls = 0 # Number of balls currently contained (held) in this device.. self.eject_queue = deque() # Queue of the list of eject targets (ball devices) for the balls this # device is trying to eject. self.num_eject_attempts = 0 # Counter of how many attempts to eject the current ball this device # has tried. Eventually it will give up. # todo log attemps more than one? self.eject_in_progress_target = None # The ball device this device is currently trying to eject to self.num_balls_requested = 0 # The number of balls this device is in the process of trying to get. self.num_jam_switch_count = 0 # How many times the jam switch has been activated since the last # successful eject. self.machine.events.add_handler('machine_reset_phase_1', self._initialize) self.num_balls_ejecting = 0 # The number of balls that are currently in the process of being # ejected. This is either 0, 1, or whatever the balls was # for devices that eject all their balls at once. self.flag_confirm_eject_via_count = False # Notifies the count_balls() method that it should confirm an eject if # it finds a ball missing. We need this to be a standalone variable # since sometimes other eject methods will have to "fall back" on count #-based confirmations. self.valid = False self.need_first_time_count = True # Now configure the device self.configure()
def __init__(self, machine, name, config=None, collection=-1, platform_section=None): self.machine = machine self.name = name.lower() self.tags = list() self.label = None self.debug_logging = False self.config = defaultdict(lambda: None, config) if config: self.config.update(config) if 'tags' in config: self.tags = Config.string_to_list(config['tags']) if 'label' in config: self.label = config['label'] # todo change to multi lang # todo more pythonic way, like self.label = blah if blah? if 'debug' in config and config['debug']: self.debug_logging = True self.log.info("Enabling debug logging for this device") if platform_section: if self.machine.physical_hw: if 'platform' not in config: if self.machine.config['hardware'][ platform_section] != 'default': self.platform = (self.machine.hardware_platforms[ self.machine.config['hardware'] [platform_section]]) else: self.platform = self.machine.default_platform else: self.platform = (self.machine.hardware_platforms[ config['platform']]) else: self.platform = self.machine.default_platform # set event handlers to enable, disable, and reset this device # note that not all devices will use all of these methods # these lists of events can be strings or dicts if 'enable_events' in self.config: self.config['enable_events'] = self._event_config_to_dict( self.config['enable_events']) else: self.config['enable_events'] = dict() for event, delay in self.config['enable_events'].iteritems(): self._create_events(ev_name=event, ev_type='enable', delay=delay, callback=self.enable) if 'disable_events' in self.config: self.config['disable_events'] = self._event_config_to_dict( self.config['disable_events']) else: self.config['disable_events'] = dict() for event, delay in self.config['disable_events'].iteritems(): self._create_events(ev_name=event, ev_type='disable', delay=delay, callback=self.disable) if 'reset_events' in self.config: self.config['reset_events'] = self._event_config_to_dict( self.config['reset_events']) else: self.config['reset_events'] = dict() for event, delay in self.config['reset_events'].iteritems(): self._create_events(ev_name=event, ev_type='reset', delay=delay, callback=self.reset) # Add this instance to the collection for this type of device if collection != -1: # Have to use -1 here instead of None to catch an empty collection collection[name] = self
def _load(self, callback, show_actions=None): self.show_actions = list() self.asset_manager.log.debug("Loading Show %s", self.file_name) if not show_actions: show_actions = self.load_show_from_disk() for step_num in range(len(show_actions)): step_actions = dict() step_actions['tocks'] = show_actions[step_num]['tocks'] # look for empty steps. If we find them we'll just add their tock # time to the previous step. if len(show_actions[step_num] ) == 1: # 1 because it still has tocks show_actions[-1]['tocks'] += step_actions['tocks'] continue # Events # make sure events is a list of strings if ('events' in show_actions[step_num] and show_actions[step_num]['events']): event_list = (Config.string_to_list( show_actions[step_num]['events'])) step_actions['events'] = event_list # SlidePlayer if ('display' in show_actions[step_num] and show_actions[step_num]['display']): step_actions['display'] = ( self.machine.display.slidebuilder.preprocess_settings( show_actions[step_num]['display'])) # Sounds if ('sounds' in show_actions[step_num] and show_actions[step_num]['sounds']): # make sure we have a list of dicts if type(show_actions[step_num]['sounds']) is dict: show_actions[step_num]['sounds'] = ([ show_actions[step_num]['sounds'] ]) for entry in show_actions[step_num]['sounds']: try: entry['sound'] = self.machine.sounds[entry['sound']] except KeyError: self.asset_manager.log.critical( "Invalid sound '%s' " "found in show. ", entry['sound']) raise step_actions['sounds'] = show_actions[step_num]['sounds'] self.show_actions.append(step_actions) # count how many total locations are in the show. We need this later # so we can know when we're at the end of a show self.total_locations = len(self.show_actions) self.loaded = True if callback: callback() self._asset_loaded()
def _load_mpf_config(self): self.config = Config.load_config_file(self.options['mpfconfigfile'])
def __init__(self, machine): super(HardwarePlatform, self).__init__(machine) self.log = logging.getLogger('FAST') self.log.debug("Configuring FAST hardware.") if not serial_imported: self.log.error('Could not import "pySerial". This is required for ' 'the FAST platform interface') sys.exit() # ---------------------------------------------------------------------- # Platform-specific hardware features. WARNING: Do not edit these. They # are based on what the FAST hardware can and cannot do. self.features['max_pulse'] = 255 # todo self.features['hw_timer'] = False self.features['hw_rule_coil_delay'] = True # todo self.features['variable_recycle_time'] = True # todo self.features['variable_debounce_time'] = True # todo self.features['hw_enable_auto_disable'] = True # Make the platform features available to everyone self.machine.config['platform'] = self.features # ---------------------------------------------------------------------- self.hw_rules = dict() self.dmd_connection = None self.net_connection = None self.rgb_connection = None self.fast_nodes = list() self.connection_threads = set() self.receive_queue = Queue.Queue() self.fast_leds = set() self.flag_led_tick_registered = False self.flag_switch_registered = False config_spec = ''' ports: list baud: int|921600 config_number_format: string|hex watchdog: ms|1000 default_debounce_open: ms|30 default_debounce_close: ms|30 debug: boolean|False ''' self.config = Config.process_config(config_spec=config_spec, source=self.machine.config['fast']) self.watchdog_command = 'WD:' + str(hex(self.config['watchdog']))[2:] self._connect_to_hardware() if 'config_number_format' not in self.machine.config['fast']: self.machine.config['fast']['config_number_format'] = 'int' self.machine_type = ( self.machine.config['hardware']['driverboards'].lower()) if self.machine_type == 'wpc': self.log.debug("Configuring the FAST Controller for WPC driver " "boards") elif self.machine_type == 'fast': self.log.debug( "Configuring FAST Controller for FAST driver boards.") self.wpc_switch_map = { # autogenerated, so not in order. Sorry :( 'SF8': '67', 'SF6': '65', 'SF7': '66', 'SF4': '63', 'SF5': '64', 'SF2': '61', 'SF3': '62', 'SF1': '60', 'S57': '26', 'S56': '25', 'S55': '24', 'S54': '23', 'S53': '22', 'S52': '21', 'S51': '20', 'S58': '27', 'S44': '1B', 'S45': '1C', 'S46': '1D', 'S47': '1E', 'S41': '18', 'S42': '19', 'S43': '1A', 'S48': '1F', 'S78': '37', 'S71': '30', 'S73': '32', 'S72': '31', 'S75': '34', 'S74': '33', 'S77': '36', 'S76': '35', 'S68': '2F', 'S66': '2d', 'S67': '2E', 'S64': '2B', 'S65': '2C', 'S62': '29', 'S63': '2A', 'S61': '28', 'S18': '07', 'S13': '02', 'S12': '01', 'S11': '00', 'S17': '06', 'S16': '05', 'S15': '04', 'S14': '03', 'S93': '42', 'S92': '41', 'S91': '40', 'S97': '46', 'S96': '45', 'S95': '44', 'S94': '43', 'S98': '47', 'S81': '38', 'S82': '39', 'S83': '3A', 'S84': '3B', 'S85': '3C', 'S86': '3D', 'S87': '3E', 'S88': '3F', 'SD4': '53', 'SD5': '54', 'SD6': '55', 'SD7': '56', 'SD1': '50', 'SD2': '51', 'SD3': '52', 'SD8': '57', 'S38': '17', 'S35': '14', 'S34': '13', 'S37': '16', 'S36': '15', 'S31': '10', 'S33': '12', 'S32': '11', 'S22': '09', 'S23': '0A', 'S21': '08', 'S26': '0D', 'S27': '0E', 'S24': '0B', 'S25': '0C', 'S28': '0F', 'DIP8': '5F', 'DIP1': '58', 'DIP3': '5A', 'DIP2': '59', 'DIP5': '5C', 'DIP4': '5B', 'DIP7': '5E', 'DIP6': '5D', } self.wpc_light_map = { 'L11': '00', 'L12': '01', 'L13': '02', 'L14': '03', 'L15': '04', 'L16': '05', 'L17': '06', 'L18': '07', 'L21': '08', 'L22': '09', 'L23': '0A', 'L24': '0B', 'L25': '0C', 'L26': '0D', 'L27': '0E', 'L28': '0F', 'L31': '10', 'L32': '11', 'L33': '12', 'L34': '13', 'L35': '14', 'L36': '15', 'L37': '16', 'L38': '17', 'L41': '18', 'L42': '19', 'L43': '1A', 'L44': '1B', 'L45': '1C', 'L46': '1D', 'L47': '1E', 'L48': '1F', 'L51': '20', 'L52': '21', 'L53': '22', 'L54': '23', 'L55': '24', 'L56': '25', 'L57': '26', 'L58': '27', 'L61': '28', 'L62': '29', 'L63': '2A', 'L64': '2B', 'L65': '2C', 'L66': '2D', 'L67': '2E', 'L68': '2F', 'L71': '30', 'L72': '31', 'L73': '32', 'L74': '33', 'L75': '34', 'L76': '35', 'L77': '36', 'L78': '37', 'L81': '38', 'L82': '39', 'L83': '3A', 'L84': '3B', 'L85': '3C', 'L86': '3D', 'L87': '3E', 'L88': '3F', } self.wpc_driver_map = { 'C01': '00', 'C02': '01', 'C03': '02', 'C04': '03', 'C05': '04', 'C06': '05', 'C07': '06', 'C08': '07', 'C09': '08', 'C10': '09', 'C11': '0A', 'C12': '0B', 'C13': '0C', 'C14': '0D', 'C15': '0E', 'C16': '0F', 'C17': '10', 'C18': '11', 'C19': '12', 'C20': '13', 'C21': '14', 'C22': '15', 'C23': '16', 'C24': '17', 'C25': '18', 'C26': '19', 'C27': '1A', 'C28': '1B', 'C29': '1C', 'C30': '1D', 'C31': '1E', 'C32': '1F', 'C33': '24', 'C34': '25', 'C35': '26', 'C36': '27', 'FLRM': '20', 'FLRH': '21', 'FLLM': '22', 'FLLH': '23', 'FURM': '24', 'FURH': '25', 'FULM': '26', 'FULH': '27', 'C37': '28', 'C38': '29', 'C39': '2A', 'C40': '2B', 'C41': '2C', 'C42': '2D', 'C43': '2E', 'C44': '2F', } self.wpc_gi_map = { 'G01': '00', 'G02': '01', 'G03': '02', 'G04': '03', 'G05': '04', 'G06': '05', 'G07': '06', 'G08': '07', } self.pwm8_to_hex_string = { 0: '00', 1: '01', 2: '88', 3: '92', 4: 'AA', 5: 'BA', 6: 'EE', 7: 'FE', 8: 'FF' } self.pwm8_to_int = { 0: 0, 1: 1, 2: 136, 3: 146, 4: 170, 5: 186, 6: 238, 7: 254, 8: 255 } # todo verify this list self.fast_commands = { 'ID': self.receive_id, # processor ID 'WX': self.receive_wx, # watchdog 'NN': self.receive_nn, # node id list 'NI': self.receive_ni, # node ID 'RX': self.receive_rx, # RGB cmd received 'DX': self.receive_dx, # DMD cmd received 'SX': self.receive_sx, # sw config received 'LX': self.receive_lx, # lamp cmd received 'PX': self.receive_px, # segment cmd received 'SA': self.receive_sa, # all switch states '/N': self.receive_nw_open, # nw switch open '-N': self.receive_nw_closed, # nw switch closed '/L': self.receive_local_open, # local sw open '-L': self.receive_local_closed, # local sw close 'WD': self.receive_wd, # watchdog }