1313import voluptuous as vol
1414
1515from homeassistant .config_entries import ConfigEntry , ConfigFlow , ConfigFlowResult , OptionsFlow
16- from homeassistant .const import CONF_BRIGHTNESS , CONF_ICON , CONF_LIGHTS , CONF_NAME
16+ from homeassistant .const import CONF_BRIGHTNESS , CONF_ICON , CONF_LIGHTS , CONF_NAME , Platform
1717from homeassistant .core import callback
1818from homeassistant .helpers import selector
1919import homeassistant .helpers .config_validation as cv
7676 ERROR_CHANGE_FREQUENCY_NOT_INT_OR_RANGE ,
7777 ERROR_COLORS_IS_BLANK ,
7878 ERROR_COLORS_MALFORMED ,
79+ ERROR_MUST_SELECT_LIGHTS ,
7980 ERROR_TRANSITION_NOT_INT_OR_RANGE ,
8081 TRANSITION_MAX ,
8182 TRANSITION_MIN ,
@@ -285,7 +286,7 @@ def _get_default(key: str, fallback_default: Any = None) -> Any:
285286 ): selector .IconSelector (selector .IconSelectorConfig ()),
286287 }
287288 )
288- return build_schema .extend (
289+ build_schema = build_schema .extend (
289290 {
290291 vol .Optional (
291292 CONF_PRIORITY , default = _get_default (CONF_PRIORITY , DEFAULT_PRIORITY )
@@ -339,11 +340,18 @@ def _get_default(key: str, fallback_default: Any = None) -> Any:
339340 CONF_RESTORE_POWER ,
340341 default = _get_default (CONF_RESTORE_POWER , DEFAULT_RESTORE_POWER ),
341342 ): selector .BooleanSelector (selector .BooleanSelectorConfig ()),
342- vol .Required (CONF_LIGHTS , default = _get_default (CONF_LIGHTS )): selector .EntitySelector (
343- selector .EntitySelectorConfig (domain = "light" , multiple = True ),
343+ vol .Required (
344+ CONF_LIGHTS , default = _get_default (CONF_LIGHTS , [])
345+ ): selector .EntitySelector (
346+ selector .EntitySelectorConfig (domain = [Platform .LIGHT ], multiple = True ),
344347 ),
348+ }
349+ )
350+
351+ return build_schema .extend (
352+ {
345353 vol .Required (
346- CONF_COLOR_SELECTOR_MODE , default = _get_default (CONF_COLOR_SELECTOR_MODE )
354+ CONF_COLOR_SELECTOR_MODE , default = _get_default (CONF_COLOR_SELECTOR_MODE , "" )
347355 ): selector .SelectSelector (
348356 selector .SelectSelectorConfig (
349357 options = COLOR_SELECTOR_OPTION_LIST ,
@@ -475,7 +483,6 @@ def __init__(self) -> None:
475483 self ._data : dict [str , Any ] = {}
476484 self ._data [CONF_COLOR_RGB_DICT ] = {}
477485 self ._data [CONF_COLORS ] = {}
478- self ._errors : dict [str , Any ] = {}
479486 self ._entry = None
480487
481488 async def async_step_user (self , user_input : dict [str , Any ] | None = None ) -> ConfigFlowResult :
@@ -513,7 +520,7 @@ async def async_step_scene(
513520 configuration steps or creating the config entry.
514521 """
515522
516- self . _errors = {}
523+ errors : dict [ str , Any ] = {}
517524
518525 # Defaults
519526 defaults = {
@@ -539,6 +546,8 @@ async def async_step_scene(
539546 self ._data .get (CONF_CHANGE_AMOUNT ),
540547 type (self ._data .get (CONF_CHANGE_AMOUNT )),
541548 )
549+ if len (self ._data .get (CONF_LIGHTS , [])) == 0 :
550+ errors ["base" ] = ERROR_MUST_SELECT_LIGHTS
542551 change_amount_check , change_amount_value = _is_int_list_or_all (
543552 self ._data .get (CONF_CHANGE_AMOUNT ),
544553 CHANGE_AMOUNT_MIN ,
@@ -547,7 +556,7 @@ async def async_step_scene(
547556 if change_amount_check :
548557 self ._data .update ({CONF_CHANGE_AMOUNT : change_amount_value })
549558 else :
550- self . _errors ["base" ] = ERROR_CHANGE_AMOUNT_NOT_INT_OR_ALL
559+ errors ["base" ] = ERROR_CHANGE_AMOUNT_NOT_INT_OR_ALL
551560 _LOGGER .debug (
552561 "Checking Transition: %s, type: %s" ,
553562 self ._data .get (CONF_TRANSITION ),
@@ -561,7 +570,7 @@ async def async_step_scene(
561570 if transition_check :
562571 self ._data .update ({CONF_TRANSITION : transition_value })
563572 else :
564- self . _errors ["base" ] = ERROR_TRANSITION_NOT_INT_OR_RANGE
573+ errors ["base" ] = ERROR_TRANSITION_NOT_INT_OR_RANGE
565574 _LOGGER .debug (
566575 "Checking Change Frequency: %s, type: %s" ,
567576 self ._data .get (CONF_CHANGE_FREQUENCY ),
@@ -575,7 +584,7 @@ async def async_step_scene(
575584 if change_frequency_check :
576585 self ._data .update ({CONF_CHANGE_FREQUENCY : change_frequency_value })
577586 else :
578- self . _errors ["base" ] = ERROR_CHANGE_FREQUENCY_NOT_INT_OR_RANGE
587+ errors ["base" ] = ERROR_CHANGE_FREQUENCY_NOT_INT_OR_RANGE
579588 _LOGGER .debug (
580589 "Checking Brightness: %s, type: %s" ,
581590 self ._data .get (CONF_BRIGHTNESS ),
@@ -589,7 +598,7 @@ async def async_step_scene(
589598 if brightness_check :
590599 self ._data .update ({CONF_BRIGHTNESS : brightness_value })
591600 else :
592- self . _errors ["base" ] = ERROR_BRIGHTNESS_NOT_INT_OR_RANGE
601+ errors ["base" ] = ERROR_BRIGHTNESS_NOT_INT_OR_RANGE
593602 self ._data .update (
594603 {CONF_PRIORITY : round (self ._data .get (CONF_PRIORITY , DEFAULT_PRIORITY ))}
595604 )
@@ -604,7 +613,7 @@ async def async_step_scene(
604613 for k , v in defaults .items ():
605614 self ._data .setdefault (k , v )
606615 # _LOGGER.debug(f"[async_step_scene] self._data: {self._data}")
607- if not self . _errors :
616+ if not errors :
608617 if yaml_import :
609618 self ._data .update ({CONF_COLOR_SELECTOR_MODE : COLOR_SELECTOR_YAML })
610619 return self .async_create_entry (title = self ._data [CONF_NAME ], data = self ._data )
@@ -618,33 +627,33 @@ async def async_step_scene(
618627 return self .async_show_form (
619628 step_id = "scene" ,
620629 data_schema = await _async_build_schema (user_input , defaults ),
621- errors = self . _errors ,
630+ errors = errors ,
622631 )
623632
624633 async def async_step_color_yaml (
625634 self , user_input : dict [str , Any ] | None = None
626635 ) -> ConfigFlowResult :
627636 """Handle color configuration when the user chooses YAML input."""
628- self . _errors = {}
637+ errors : dict [ str , Any ] = {}
629638
630639 # Defaults
631640 defaults : dict [str , Any ] = {}
632641
633642 if user_input is not None :
634643 self ._data .update (user_input )
635644 if self ._data .get (CONF_COLORS ) is None or self ._data .get (CONF_COLORS ) == {}:
636- self . _errors ["base" ] = ERROR_COLORS_IS_BLANK
645+ errors ["base" ] = ERROR_COLORS_IS_BLANK
637646 if not isinstance (self ._data .get (CONF_COLORS ), list ):
638- self . _errors ["base" ] = ERROR_COLORS_MALFORMED
647+ errors ["base" ] = ERROR_COLORS_MALFORMED
639648 for k , v in defaults .items ():
640649 self ._data .setdefault (k , v )
641650 # _LOGGER.debug(f"[async_step_color_yaml] self._data: {self._data}")
642- if not self . _errors :
651+ if not errors :
643652 return self .async_create_entry (title = self ._data [CONF_NAME ], data = self ._data )
644653 return self .async_show_form (
645654 step_id = "color_yaml" ,
646655 data_schema = await _async_build_color_yaml_schema (user_input , defaults ),
647- errors = self . _errors ,
656+ errors = errors ,
648657 description_placeholders = {
649658 "component_color_config_url" : COMPONENT_COLOR_CONFIG_URL ,
650659 },
@@ -654,7 +663,7 @@ async def async_step_color_rgb_ui(
654663 self , user_input : dict [str , Any ] | None = None
655664 ) -> ConfigFlowResult :
656665 """Handle color configuration when the user uses the RGB UI selectors."""
657- self . _errors = {}
666+ errors : dict [ str , Any ] = {}
658667
659668 # Defaults
660669 defaults = {
@@ -680,7 +689,7 @@ async def async_step_color_rgb_ui(
680689 if brightness_check :
681690 user_input .update ({CONF_BRIGHTNESS : brightness_value })
682691 else :
683- self . _errors ["base" ] = ERROR_BRIGHTNESS_NOT_INT_OR_RANGE
692+ errors ["base" ] = ERROR_BRIGHTNESS_NOT_INT_OR_RANGE
684693 user_input .update (
685694 {CONF_COLOR_WEIGHT : round (user_input .get (CONF_COLOR_WEIGHT , DEFAULT_COLOR_WEIGHT ))}
686695 )
@@ -693,7 +702,7 @@ async def async_step_color_rgb_ui(
693702 )
694703 for k , v in defaults .items ():
695704 user_input .setdefault (k , v )
696- if not self . _errors :
705+ if not errors :
697706 color_uuid = uuid .random_uuid_hex ()
698707 self ._data .get (CONF_COLOR_RGB_DICT , {}).update ({color_uuid : user_input })
699708 # _LOGGER.debug(f"[async_step_color_rgb_ui] self._data: {self._data}")
@@ -712,7 +721,7 @@ async def async_step_color_rgb_ui(
712721 return self .async_show_form (
713722 step_id = "color_rgb_ui" ,
714723 data_schema = await _async_build_color_rgb_ui_schema (user_input , defaults ),
715- errors = self . _errors ,
724+ errors = errors ,
716725 description_placeholders = {
717726 "color_count" : str (len (self ._data .get (CONF_COLOR_RGB_DICT , {})) + 1 ),
718727 },
@@ -741,7 +750,6 @@ def __init__(self, config_entry: ConfigEntry) -> None:
741750 """Initialize."""
742751 self .config = config_entry
743752 self ._data = dict (config_entry .data )
744- self ._errors : dict [str , Any ] = {}
745753 rgb_dict = self ._data .get (CONF_COLOR_RGB_DICT )
746754 if rgb_dict :
747755 self ._rgb_ui_color_keys = list (self ._data .get (CONF_COLOR_RGB_DICT , {}).keys ())
@@ -765,7 +773,7 @@ async def async_step_scene(self, user_input: dict[str, Any] | None = None) -> Co
765773 color configuration steps or update the entry data.
766774 """
767775
768- self . _errors = {}
776+ errors : dict [ str , Any ] = {}
769777
770778 # Defaults
771779 defaults = {
@@ -791,6 +799,8 @@ async def async_step_scene(self, user_input: dict[str, Any] | None = None) -> Co
791799 self ._data .get (CONF_CHANGE_AMOUNT ),
792800 type (self ._data .get (CONF_CHANGE_AMOUNT )),
793801 )
802+ if len (self ._data .get (CONF_LIGHTS , [])) == 0 :
803+ errors ["base" ] = ERROR_MUST_SELECT_LIGHTS
794804 change_amount_check , change_amount_value = _is_int_list_or_all (
795805 self ._data .get (CONF_CHANGE_AMOUNT ),
796806 CHANGE_AMOUNT_MIN ,
@@ -799,7 +809,7 @@ async def async_step_scene(self, user_input: dict[str, Any] | None = None) -> Co
799809 if change_amount_check :
800810 self ._data .update ({CONF_CHANGE_AMOUNT : change_amount_value })
801811 else :
802- self . _errors ["base" ] = ERROR_CHANGE_AMOUNT_NOT_INT_OR_ALL
812+ errors ["base" ] = ERROR_CHANGE_AMOUNT_NOT_INT_OR_ALL
803813 _LOGGER .debug (
804814 "Checking Transition: %s, type: %s" ,
805815 self ._data .get (CONF_TRANSITION ),
@@ -813,7 +823,7 @@ async def async_step_scene(self, user_input: dict[str, Any] | None = None) -> Co
813823 if transition_check :
814824 self ._data .update ({CONF_TRANSITION : transition_value })
815825 else :
816- self . _errors ["base" ] = ERROR_TRANSITION_NOT_INT_OR_RANGE
826+ errors ["base" ] = ERROR_TRANSITION_NOT_INT_OR_RANGE
817827 _LOGGER .debug (
818828 "Checking Change Frequency: %s, type: %s" ,
819829 self ._data .get (CONF_CHANGE_FREQUENCY ),
@@ -827,7 +837,7 @@ async def async_step_scene(self, user_input: dict[str, Any] | None = None) -> Co
827837 if change_frequency_check :
828838 self ._data .update ({CONF_CHANGE_FREQUENCY : change_frequency_value })
829839 else :
830- self . _errors ["base" ] = ERROR_CHANGE_FREQUENCY_NOT_INT_OR_RANGE
840+ errors ["base" ] = ERROR_CHANGE_FREQUENCY_NOT_INT_OR_RANGE
831841 _LOGGER .debug (
832842 "Checking Brightness: %s, type: %s" ,
833843 self ._data .get (CONF_BRIGHTNESS ),
@@ -841,7 +851,7 @@ async def async_step_scene(self, user_input: dict[str, Any] | None = None) -> Co
841851 if brightness_check :
842852 self ._data .update ({CONF_BRIGHTNESS : brightness_value })
843853 else :
844- self . _errors ["base" ] = ERROR_BRIGHTNESS_NOT_INT_OR_RANGE
854+ errors ["base" ] = ERROR_BRIGHTNESS_NOT_INT_OR_RANGE
845855 self ._data .update (
846856 {CONF_PRIORITY : round (self ._data .get (CONF_PRIORITY , DEFAULT_PRIORITY ))}
847857 )
@@ -856,7 +866,7 @@ async def async_step_scene(self, user_input: dict[str, Any] | None = None) -> Co
856866 for k , v in defaults .items ():
857867 self ._data .setdefault (k , v )
858868 # _LOGGER.debug(f"[async_init_user] self._data: {self._data}")
859- if not self . _errors :
869+ if not errors :
860870 if (
861871 self ._data .get (CONF_COLOR_SELECTOR_MODE , COLOR_SELECTOR_RGB_UI )
862872 == COLOR_SELECTOR_RGB_UI
@@ -867,29 +877,29 @@ async def async_step_scene(self, user_input: dict[str, Any] | None = None) -> Co
867877 return self .async_show_form (
868878 step_id = "scene" ,
869879 data_schema = await _async_build_schema (user_input , self ._data , options_flow = True ),
870- errors = self . _errors ,
880+ errors = errors ,
871881 description_placeholders = {"scene_name" : self ._data [CONF_NAME ]},
872882 )
873883
874884 async def async_step_color_yaml (
875885 self , user_input : dict [str , Any ] | None = None
876886 ) -> ConfigFlowResult :
877887 """Handle color configuration in YAML mode within the options flow."""
878- self . _errors = {}
888+ errors : dict [ str , Any ] = {}
879889
880890 # Defaults
881891 defaults : dict [str , Any ] = {}
882892
883893 if user_input is not None :
884894 self ._data .update (user_input )
885895 if self ._data .get (CONF_COLORS ) is None or self ._data .get (CONF_COLORS ) == {}:
886- self . _errors ["base" ] = ERROR_COLORS_IS_BLANK
896+ errors ["base" ] = ERROR_COLORS_IS_BLANK
887897 if not isinstance (self ._data .get (CONF_COLORS ), list ):
888- self . _errors ["base" ] = ERROR_COLORS_MALFORMED
898+ errors ["base" ] = ERROR_COLORS_MALFORMED
889899 for k , v in defaults .items ():
890900 self ._data .setdefault (k , v )
891901 # _LOGGER.debug(f"[async_step_color_yaml] self._data: {self._data}")
892- if not self . _errors :
902+ if not errors :
893903 self ._data .update ({CONF_COLOR_RGB_DICT : {}})
894904 self .hass .config_entries .async_update_entry (
895905 self .config , data = self ._data , options = self .config .options
@@ -900,7 +910,7 @@ async def async_step_color_yaml(
900910 return self .async_show_form (
901911 step_id = "color_yaml" ,
902912 data_schema = await _async_build_color_yaml_schema (user_input , self ._data ),
903- errors = self . _errors ,
913+ errors = errors ,
904914 description_placeholders = {
905915 "component_color_config_url" : COMPONENT_COLOR_CONFIG_URL ,
906916 },
@@ -910,7 +920,7 @@ async def async_step_color_rgb_ui(
910920 self , user_input : dict [str , Any ] | None = None
911921 ) -> ConfigFlowResult :
912922 """Handle color configuration using the RGB UI selectors in options."""
913- self . _errors = {}
923+ errors : dict [ str , Any ] = {}
914924
915925 # Defaults
916926 defaults = {
@@ -941,14 +951,14 @@ async def async_step_color_rgb_ui(
941951 if brightness_check :
942952 color_data .update ({CONF_BRIGHTNESS : brightness_value })
943953 else :
944- self . _errors ["base" ] = ERROR_BRIGHTNESS_NOT_INT_OR_RANGE
954+ errors ["base" ] = ERROR_BRIGHTNESS_NOT_INT_OR_RANGE
945955 color_data .update ({CONF_COLOR_WEIGHT : round (color_data .get (CONF_COLOR_WEIGHT ))})
946956 color_data .update (
947957 {CONF_COLOR_NEARBY_COLORS : round (color_data .get (CONF_COLOR_NEARBY_COLORS ))}
948958 )
949959 for k , v in defaults .items ():
950960 color_data .setdefault (k , v )
951- if not self . _errors :
961+ if not errors :
952962 if self ._rgb_ui_color_index + 1 <= self ._rgb_ui_color_max :
953963 self ._data .get (CONF_COLOR_RGB_DICT , {}).update (
954964 {self ._rgb_ui_color_keys [self ._rgb_ui_color_index ]: color_data }
@@ -987,7 +997,7 @@ async def async_step_color_rgb_ui(
987997 options_flow = True ,
988998 is_last_color = (self ._rgb_ui_color_index + 1 >= self ._rgb_ui_color_max ),
989999 ),
990- errors = self . _errors ,
1000+ errors = errors ,
9911001 description_placeholders = {
9921002 "component_color_config_url" : COMPONENT_COLOR_CONFIG_URL ,
9931003 "color_count" : str (self ._rgb_ui_color_index + 1 ),
0 commit comments