def __init__(self): self._light_dict = {} self._group_dict = {} self._location_dict = {} self._light_names = SortedList() self._num_successful_discovers = 0 self._num_failed_discovers = 0
def _update_memberships(light, current_set_name, set_dict): LightSet._remove_memberships(light, set_dict) if current_set_name not in set_dict: set_dict[current_set_name] = SortedList(light.name) else: set_dict[current_set_name].add(light.name)
class LightSet(i_controller.LightSet): """ The lights are stored in _light_dict, keyed on light name. Locations and groups are stored in _location_dict and _group_dict as lists of strings containing light names. """ the_instance = None def __init__(self): self._light_dict = {} self._group_dict = {} self._location_dict = {} self._light_names = SortedList() self._num_successful_discovers = 0 self._num_failed_discovers = 0 @staticmethod def configure(): LightSet.the_instance = LightSet() @staticmethod def get_instance(): return LightSet.the_instance @inject(Lifx) def discover(self, lifx): logging.debug( 'start discover. so far, successes = {}, fails = {}'.format( self._num_successful_discovers, self._num_failed_discovers)) try: for lifx_light in lifx.get_lights(): light = Light(lifx_light) self._light_dict[light.name] = light self._light_names.add(light.name) LightSet._update_memberships(light, light.group, self._group_dict) LightSet._update_memberships(light, light.location, self._location_dict) except lifxlan.errors.WorkflowException as ex: self._num_failed_discovers += 1 logging.warning("In discover():\n{}".format(ex)) return False self._num_successful_discovers += 1 return True def refresh(self): self.discover() self._garbage_collect() @staticmethod def _update_memberships(light, current_set_name, set_dict): LightSet._remove_memberships(light, set_dict) if current_set_name not in set_dict: set_dict[current_set_name] = SortedList(light.name) else: set_dict[current_set_name].add(light.name) @staticmethod def _remove_memberships(light, set_dict): # Remove the light from every set in set_dict that it belongs to. target_set_names = [] for list_name in set_dict.keys(): the_list = set_dict[list_name] the_list.remove(light.name) if len(the_list) == 0: target_set_names.append(list_name) for set_name in target_set_names: del set_dict[set_name] @inject(Settings) def _garbage_collect(self, settings): # Get rid of a light's proxy if it hasn't responded for a while. logging.debug("garbage collect, currently have {} lights".format( len(self._light_dict))) max_age = int(settings.get_value('light_gc_time', 20 * 60)) target_lights = [] for light in self._light_dict.values(): if light.get_age() > max_age: LightSet._remove_memberships(light, self._group_dict) LightSet._remove_memberships(light, self._location_dict) target_lights.append(light.name) for light_name in target_lights: logging.debug("_garbage_collect() deleting {}".format(light_name)) self._light_names.remove(light_name) self._light_dict[light_name] = None del self._light_dict[light_name] @property def light_names(self) -> SortedList: """ list of strings """ return self._light_names @property def group_names(self): """ list of strings """ return SortedList(self._group_dict.keys()) @property def location_names(self): """ list of strings """ return SortedList(self._location_dict.keys()) @property def count(self): return len(self._light_list) @property def successful_discovers(self): return self._num_successful_discovers @property def failed_discovers(self): return self._num_failed_discovers def get_light(self, name): """ returns an instance of i_lib.Light, or None if it's not there """ return self._light_dict.get(name) def get_group(self, name): """ list of light names """ return self._group_dict.get(name) def get_location(self, name): """ list of light names. """ return self._location_dict.get(name) @inject(Lifx) def set_color(self, color, duration, lifx): lifx.set_color_all_lights(rounded_color(color), duration) return True @inject(Lifx) def set_power(self, power_level, duration, lifx): lifx.set_power_all_lights(round(power_level), duration) return True
def location_names(self): """ list of strings """ return SortedList(self._location_dict.keys())
def group_names(self): """ list of strings """ return SortedList(self._group_dict.keys())
def setUp(self): self._list = SortedList() for value in ('b', 'c', 'a', 'c', 'd'): self._list.add(value)
class SortedListTest(unittest.TestCase): def setUp(self): self._list = SortedList() for value in ('b', 'c', 'a', 'c', 'd'): self._list.add(value) def test_forward(self): self.assertEqual(self._list.first(), 'a') self.assertEqual(self._list.next('b'), 'c') self.assertIsNone(self._list.next('d')) self._list.remove('d') self.assertIsNone(self._list.next('d')) self.assertIsNone(self._list.next('c')) self._list.remove('b') self.assertEqual(self._list.next('b'), 'c') def test_reverse(self): self.assertEqual(self._list.last(), 'd') self.assertEqual(self._list.prev('d'), 'c') self.assertIsNone(self._list.prev('a')) self._list.remove('a') self.assertIsNone(self._list.prev('a')) self.assertIsNone(self._list.prev('b')) self._list.remove('d') self.assertEqual(self._list.prev('d'), 'c') def test_empty(self): for value in ('c', 'b', 'a', 'd'): self._list.remove(value) self.assertIsNone(self._list.first()) self.assertIsNone(self._list.last()) self._list.add('e') self.assertEqual(self._list.first(), 'e') self.assertEqual(self._list.last(), 'e')