44
55from dataclasses import dataclass
66from enum import IntEnum , IntFlag
7+ from functools import lru_cache
8+ from operator import attrgetter
79from typing import Any
810
911from awesomeversion import AwesomeVersion
1012
1113from .exceptions import WLEDError
1214
15+ NAME_GETTER = attrgetter ("name" )
16+
17+
18+ @lru_cache
19+ def get_awesome_version (version : str ) -> AwesomeVersion :
20+ """Return a cached AwesomeVersion object."""
21+ return AwesomeVersion (version )
22+
1323
1424@dataclass
1525class Nightlight :
@@ -140,8 +150,8 @@ def from_dict( # noqa: PLR0913
140150 segment_id : int ,
141151 data : dict [str , Any ],
142152 * ,
143- effects : list [ Effect ],
144- palettes : list [ Palette ],
153+ effects : dict [ int , Effect ],
154+ palettes : dict [ int , Palette ],
145155 state_on : bool ,
146156 state_brightness : int ,
147157 ) -> Segment :
@@ -151,8 +161,8 @@ def from_dict( # noqa: PLR0913
151161 ----
152162 segment_id: The ID of the LED strip segment.
153163 data: The segment data received from the WLED device.
154- effects: A list of Effect objects.
155- palettes: A list of Palette objects.
164+ effects: An indexed dict of Effect objects.
165+ palettes: An indexed dict of Palette objects.
156166 state_on: Boolean the represents the on/off state of this segment.
157167 state_brightness: The brightness level of this segment.
158168
@@ -174,13 +184,9 @@ def from_dict( # noqa: PLR0913
174184 except IndexError :
175185 pass
176186
177- effect = next (
178- (item for item in effects if item .effect_id == data .get ("fx" , 0 )),
179- Effect (effect_id = 0 , name = "Unknown" ),
180- )
181- palette = next (
182- (item for item in palettes if item .palette_id == data .get ("pal" , 0 )),
183- Palette (palette_id = 0 , name = "Unknown" ),
187+ effect = effects .get (data .get ("fx" , 0 )) or Effect (effect_id = 0 , name = "Unknown" )
188+ palette = palettes .get (data .get ("pal" , 0 )) or Palette (
189+ palette_id = 0 , name = "Unknown"
184190 )
185191
186192 return Segment (
@@ -389,15 +395,15 @@ def from_dict(data: dict[str, Any]) -> Info:
389395 websocket = None
390396
391397 if version := data .get ("ver" ):
392- version = AwesomeVersion (version )
398+ version = get_awesome_version (version )
393399 if not version .valid :
394400 version = None
395401
396402 if version_latest_stable := data .get ("version_latest_stable" ):
397- version_latest_stable = AwesomeVersion (version_latest_stable )
403+ version_latest_stable = get_awesome_version (version_latest_stable )
398404
399405 if version_latest_beta := data .get ("version_latest_beta" ):
400- version_latest_beta = AwesomeVersion (version_latest_beta )
406+ version_latest_beta = get_awesome_version (version_latest_beta )
401407
402408 arch = data .get ("arch" , "Unknown" )
403409 if (
@@ -477,20 +483,20 @@ def preset_active(self) -> bool:
477483 @staticmethod
478484 def from_dict (
479485 data : dict [str , Any ],
480- effects : list [ Effect ],
481- palettes : list [ Palette ],
482- presets : list [ Preset ],
483- playlists : list [ Playlist ],
486+ effects : dict [ int , Effect ],
487+ palettes : dict [ int , Palette ],
488+ presets : dict [ int , Preset ],
489+ playlists : dict [ int , Playlist ],
484490 ) -> State :
485491 """Return State object from WLED API response.
486492
487493 Args:
488494 ----
489495 data: The state response received from the WLED device API.
490- effects: A list of effect objects.
491- palettes: A list of palette objects.
492- presets: A list of preset objects.
493- playlists: A list of playlist objects.
496+ effects: A dict index of effect objects.
497+ palettes: A dict index of palette objects.
498+ presets: A dict index of preset objects.
499+ playlists: A dict index of playlist objects.
494500
495501 Returns:
496502 -------
@@ -516,15 +522,8 @@ def from_dict(
516522 playlist = data .get ("pl" , - 1 )
517523 preset = data .get ("ps" , - 1 )
518524 if presets :
519- preset = next (
520- (item for item in presets if item .preset_id == data .get ("ps" )),
521- None ,
522- )
523-
524- playlist = next (
525- (item for item in playlists if item .playlist_id == data .get ("pl" )),
526- None ,
527- )
525+ playlist = playlists .get (playlist )
526+ preset = presets .get (preset )
528527
529528 return State (
530529 brightness = brightness ,
@@ -556,17 +555,17 @@ class Preset:
556555 def from_dict (
557556 preset_id : int ,
558557 data : dict [str , Any ],
559- effects : list [ Effect ],
560- palettes : list [ Palette ],
558+ effects : dict [ int , Effect ],
559+ palettes : dict [ int , Palette ],
561560 ) -> Preset :
562561 """Return Preset object from WLED API response.
563562
564563 Args:
565564 ----
566565 preset_id: The ID of the preset.
567566 data: The data from the WLED device API.
568- effects: A list of effect objects.
569- palettes: A list of palette object.
567+ effects: A indexed dict of effect objects.
568+ palettes: A indexed dict of palette object.
570569
571570 Returns:
572571 -------
@@ -591,10 +590,10 @@ def from_dict(
591590 for segment_id , segment in enumerate (segment_data )
592591 ]
593592
594- main_segment = next (
595- ( item for item in segments if item . segment_id == data .get ("mainseg" , 0 )),
596- None ,
597- )
593+ try :
594+ main_segment = segments [ data .get ("mainseg" , 0 )]
595+ except IndexError :
596+ main_segment = None
598597
599598 return Preset (
600599 main_segment = main_segment ,
@@ -632,7 +631,7 @@ class Playlist:
632631 def from_dict (
633632 playlist_id : int ,
634633 data : dict [str , Any ],
635- presets : list [ Preset ],
634+ presets : dict [ int , Preset ],
636635 ) -> Playlist :
637636 """Return Playlist object from WLED API response.
638637
@@ -657,18 +656,12 @@ def from_dict(
657656 entry_id = entry_id ,
658657 duration = entries_durations [entry_id ],
659658 transition = entries_transitions [entry_id ],
660- preset = next (
661- (item for item in presets if item .preset_id == preset_id ),
662- None ,
663- ),
659+ preset = presets .get (preset_id ),
664660 )
665661 for entry_id , preset_id in enumerate (entries_presets )
666662 ]
667663
668- end = next (
669- (item for item in presets if item .preset_id == playlist .get ("end" )),
670- None ,
671- )
664+ end = presets .get (playlist .get ("end" ))
672665
673666 return Playlist (
674667 playlist_id = playlist_id ,
@@ -703,6 +696,11 @@ def __init__(self, data: dict[str, Any]) -> None:
703696 that a Device object cannot be constructed from it.
704697
705698 """
699+ self ._indexed_effects : dict [int , Effect ] = {}
700+ self ._indexed_palettes : dict [int , Palette ] = {}
701+ self ._indexed_presets : dict [int , Preset ] = {}
702+ self ._indexed_playlists : dict [int , Playlist ] = {}
703+
706704 self .effects = []
707705 self .palettes = []
708706 self .playlists = []
@@ -731,57 +729,60 @@ def update_from_dict(self, data: dict[str, Any]) -> Device:
731729
732730 """
733731 if _effects := data .get ("effects" ):
734- effects = [
735- Effect (effect_id = effect_id , name = effect )
732+ self . _indexed_effects = {
733+ effect_id : Effect (effect_id = effect_id , name = effect )
736734 for effect_id , effect in enumerate (_effects )
737- ]
738- effects .sort (key = lambda x : x .name )
739- self .effects = effects
735+ }
736+ self .effects = sorted (self ._indexed_effects .values (), key = NAME_GETTER )
740737
741738 if _palettes := data .get ("palettes" ):
742- palettes = [
743- Palette (palette_id = palette_id , name = palette )
739+ self . _indexed_palettes = {
740+ palette_id : Palette (palette_id = palette_id , name = palette )
744741 for palette_id , palette in enumerate (_palettes )
745- ]
746- palettes .sort (key = lambda x : x .name )
747- self .palettes = palettes
742+ }
743+ self .palettes = sorted (self ._indexed_palettes .values (), key = NAME_GETTER )
748744
749745 if _presets := data .get ("presets" ):
750746 # The preset data contains both presets and playlists,
751747 # we split those out, so we can handle those correctly.
752-
753- # Nobody cares about 0.
754- _presets .pop ("0" , None )
755-
756- presets = [
757- Preset .from_dict (int (preset_id ), preset , self .effects , self .palettes )
748+ self ._indexed_presets = {
749+ int (preset_id ): Preset .from_dict (
750+ int (preset_id ),
751+ preset ,
752+ self ._indexed_effects ,
753+ self ._indexed_palettes ,
754+ )
758755 for preset_id , preset in _presets .items ()
759756 if "playlist" not in preset
760757 or not ("ps" in preset ["playlist" ] and preset ["playlist" ]["ps" ])
761- ]
762- presets .sort (key = lambda x : x .name )
763- self .presets = presets
758+ }
759+ # Nobody cares about 0.
760+ self ._indexed_presets .pop (0 , None )
761+ self .presets = sorted (self ._indexed_presets .values (), key = NAME_GETTER )
764762
765- playlists = [
766- Playlist .from_dict (int (playlist_id ), playlist , self .presets )
763+ self ._indexed_playlists = {
764+ int (playlist_id ): Playlist .from_dict (
765+ int (playlist_id ), playlist , self ._indexed_presets
766+ )
767767 for playlist_id , playlist in _presets .items ()
768768 if "playlist" in playlist
769769 and "ps" in playlist ["playlist" ]
770770 and playlist ["playlist" ]["ps" ]
771- ]
772- playlists .sort (key = lambda x : x .name )
773- self .playlists = playlists
771+ }
772+ # Nobody cares about 0.
773+ self ._indexed_playlists .pop (0 , None )
774+ self .playlists = sorted (self ._indexed_playlists .values (), key = NAME_GETTER )
774775
775776 if _info := data .get ("info" ):
776777 self .info = Info .from_dict (_info )
777778
778779 if _state := data .get ("state" ):
779780 self .state = State .from_dict (
780781 _state ,
781- self .effects ,
782- self .palettes ,
783- self .presets ,
784- self .playlists ,
782+ self ._indexed_effects ,
783+ self ._indexed_palettes ,
784+ self ._indexed_presets ,
785+ self ._indexed_playlists ,
785786 )
786787
787788 return self
0 commit comments