import os import sys import yaml class Config: def __init__(self): # Load initial config file if len(sys.argv) > 1: config_file = sys.argv[1] else: config_file = os.path.join(os.path.dirname(__file__), "ptv.yaml") self.load_config(config_file) # Load the profile explicitly at the end of initiailization so that # profile settings take precedence over global config settings self.load_config(os.path.join(self.profile, "profile.yaml")) def load_config(self, filename): with open(filename, "r") as infile: self._image_closed = None self._image_open = None self._image_blink_closed = None self._image_blink_open = None for k, v in yaml.load(infile, Loader=yaml.Loader).items(): # This ugly thing checks if a config parameter named in the # config file maps to a class property. If not, we throw an # error and exit because the config file is broken. # FIXME: Find a better way to do this if not isinstance(getattr(type(self), k, None), property): print(f"Invalid config paramter: {k}") sys.exit(1) setattr(self, k, v) @property def profile(self): try: return self._profile except AttributeError: print("profile is a required parameter") sys.exit(1) @profile.setter def profile(self, profile): # Check filename is a string if not isinstance(profile, str): print("Config paramter profile must be a valid filename") sys.exit(1) # Look for file in working dir and script dir targets = [os.path.join(os.path.dirname(__file__), profile), profile] for target in targets: if os.path.exists(target): if not os.path.exists(os.path.join(target, "profile.yaml")): print(f"Profile path {profile} does not contain a profile.yaml file") sys.exit(1) self._profile = target self.load_config(os.path.join(target, "profile.yaml")) return # Can't find the profile file, fail print(f"Cannot find file {profile}") sys.exit(1) @property def image_closed(self): try: return self._image_closed except AttributeError: print("image_closed is a required paramter") sys.exit(1) @image_closed.setter def image_closed(self, filename): # Check filename is a string if not isinstance(filename, str): print("Config parameter image_closed must be a valid filename") # Look for the file in profile dir, script dir, and working dir targets = [os.path.join(self.profile, filename), os.path.join(os.path.dirname(__file__), filename), filename] for target in targets: if os.path.exists(target): self._image_closed = target return # Can't find the image, fail print(f"Cannot find file {filename}") sys.exit(1) @property def image_open(self): try: return self._image_open except AttributeError: return None @image_open.setter def image_open(self, filename): # Check filename is a string if not isinstance(filename, str): print("Config parameter image_open must be a valid filename") # Look for the file in profile dir, script dir, and working dir targets = [os.path.join(self.profile, filename), os.path.join(os.path.dirname(__file__), filename), filename] for target in targets: if os.path.exists(target): self._image_open = target return # Can't find the image, fail print(f"Cannot find file {filename}") sys.exit(1) @property def image_blink_open(self): try: return self._image_blink_open except AttributeError: return None @image_blink_open.setter def image_blink_open(self, filename): # Check filename is a string if not isinstance(filename, str): print("Config parameter image_blink_open must be a valid filename") # Look for the file in profile dir, script dir, and working dir targets = [os.path.join(self.profile, filename), os.path.join(os.path.dirname(__file__), filename), filename] for target in targets: if os.path.exists(target): self._image_blink_open = target return # Can't find the image, fail print(f"Cannot find file {filename}") sys.exit(1) @property def image_blink_closed(self): try: return self._image_blink_closed except AttributeError: return None @image_blink_closed.setter def image_blink_closed(self, filename): # Check filename is a string if not isinstance(filename, str): print("Config parameter image_blink_closed must be a valid filename") # Look for the file in profile dir, script dir, and working dir targets = [os.path.join(self.profile, filename), os.path.join(os.path.dirname(__file__), filename), filename] for target in targets: if os.path.exists(target): self._image_blink_closed = target return # Can't find the image, fail print(f"Cannot find file {filename}") sys.exit(1) @property def bg_color(self): try: return self._bg_color except AttributeError: return (0, 0, 0) @bg_color.setter def bg_color(self, color): if not isinstance(color, (list, tuple)) or len(color) != 3: print("Config parameter bg_color must be a three-item list") sys.exit(1) self._bg_color = color @property def audio_checks(self): try: return self._audio_checks except AttributeError: return 60 @audio_checks.setter def audio_checks(self, checks): if not isinstance(checks, int): print("Config parameter audio_checks must be an integer") sys.exit(1) self._audio_checks = checks @property def threshold(self): try: return self._threshold except AttributeError: return 6000 @threshold.setter def threshold(self, threshold): if not isinstance(threshold, int): print("Config parameter threshold must be an integer") sys.exit(1) self._threshold = threshold @property def smoothing(self): try: return self._smoothing except AttributeError: return 3 @smoothing.setter def smoothing(self, smoothing): if not isinstance(smoothing, int): print("Config paramter smoothing must be an integer") sys.exit(1) self._smoothing = smoothing @property def mic_rate(self): try: return self._mic_rate except AttributeError: return 44100 @mic_rate.setter def mic_rate(self, rate): if not isinstance(rate, int): print("Config paramter mic_rate must be an integer") sys.exit(1) self._mic_rate = rate @property def mic_stereo(self): try: return self._mic_stereo except AttributeError: return True @mic_stereo.setter def mic_stereo(self, is_stereo): if not isinstance(is_stereo, bool): print("Config paramter mic_stereo must be a boolean") sys.exit(1) self._mic_stereo = True @property def blink_frames(self): try: return self._blink_frames except AttributeError: return 0 @blink_frames.setter def blink_frames(self, frames): if not isinstance(frames, int): print("Config parameter blink_frames must be an integer") sys.exit(1) self._blink_frames = frames @property def blink_chance(self): try: return self._blink_chance except AttributeError: return 0 @blink_chance.setter def blink_chance(self, chance): if not isinstance(chance, float) and not isinstance(chance, int): print("Config parameter blink_chance must be an integer or float") sys.exit(1) self._blink_chance = chance @property def shake_delay(self): try: return self._shake_delay except AttributeError: return 0 @shake_delay.setter def shake_delay(self, delay): if not isinstance(delay, int): print("Config parameter shake_delay must be an integer") sys.exit(1) self._shake_delay = delay @property def shake_intensity(self): try: return self._shake_intensity except AttributeError: return 0 @shake_intensity.setter def shake_intensity(self, intensity): if not isinstance(intensity, int): print("Config parameter shake_intensity must be an integer") sys.exit(1) self._shake_intensity = intensity