1212from homeassistant .components .light import ATTR_TRANSITION
1313from homeassistant .config_entries import ConfigEntry
1414from homeassistant .const import CONF_PLATFORM , SERVICE_TURN_ON , STATE_UNAVAILABLE
15- from homeassistant .core import DOMAIN as HOMEASSISTANT_DOMAIN , HomeAssistant
15+ from homeassistant .core import DOMAIN as HOMEASSISTANT_DOMAIN , HomeAssistant , callback
1616from homeassistant .helpers .entity_component import EntityComponent
1717from homeassistant .helpers .restore_state import RestoreEntity
1818from homeassistant .helpers .typing import ConfigType
1919from homeassistant .util import dt as dt_util
20+ from homeassistant .util .async_ import run_callback_threadsafe
2021from homeassistant .util .hass_dict import HassKey
2122
2223DOMAIN : Final = "scene"
23- DATA_COMPONENT : HassKey [EntityComponent [Scene ]] = HassKey (DOMAIN )
24+ DATA_COMPONENT : HassKey [EntityComponent [BaseScene ]] = HassKey (DOMAIN )
2425STATES : Final = "states"
2526
2627
@@ -62,7 +63,7 @@ def _platform_validator(config: dict[str, Any]) -> dict[str, Any]:
6263
6364async def async_setup (hass : HomeAssistant , config : ConfigType ) -> bool :
6465 """Set up the scenes."""
65- component = hass .data [DATA_COMPONENT ] = EntityComponent [Scene ](
66+ component = hass .data [DATA_COMPONENT ] = EntityComponent [BaseScene ](
6667 logging .getLogger (__name__ ), DOMAIN , hass
6768 )
6869
@@ -93,8 +94,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
9394 return await hass .data [DATA_COMPONENT ].async_unload_entry (entry )
9495
9596
96- class Scene (RestoreEntity ):
97- """A scene is a group of entities and the states we want them to be ."""
97+ class BaseScene (RestoreEntity ):
98+ """Base type for scenes ."""
9899
99100 _attr_should_poll = False
100101 __last_activated : str | None = None
@@ -108,14 +109,14 @@ def state(self) -> str | None:
108109 return self .__last_activated
109110
110111 @final
111- async def _async_activate (self , ** kwargs : Any ) -> None :
112- """Activate scene.
112+ def _record_activation (self ) -> None :
113+ run_callback_threadsafe ( self . hass . loop , self . _async_record_activation ). result ()
113114
114- Should not be overridden, handle setting last press timestamp.
115- """
115+ @final
116+ @callback
117+ def _async_record_activation (self ) -> None :
118+ """Update the activation timestamp."""
116119 self .__last_activated = dt_util .utcnow ().isoformat ()
117- self .async_write_ha_state ()
118- await self .async_activate (** kwargs )
119120
120121 async def async_internal_added_to_hass (self ) -> None :
121122 """Call when the scene is added to hass."""
@@ -128,6 +129,10 @@ async def async_internal_added_to_hass(self) -> None:
128129 ):
129130 self .__last_activated = state .state
130131
132+ async def _async_activate (self , ** kwargs : Any ) -> None :
133+ """Activate scene."""
134+ raise NotImplementedError
135+
131136 def activate (self , ** kwargs : Any ) -> None :
132137 """Activate scene. Try to get entities into requested state."""
133138 raise NotImplementedError
@@ -137,3 +142,17 @@ async def async_activate(self, **kwargs: Any) -> None:
137142 task = self .hass .async_add_executor_job (ft .partial (self .activate , ** kwargs ))
138143 if task :
139144 await task
145+
146+
147+ class Scene (BaseScene ):
148+ """A scene is a group of entities and the states we want them to be."""
149+
150+ @final
151+ async def _async_activate (self , ** kwargs : Any ) -> None :
152+ """Activate scene.
153+
154+ Should not be overridden, handle setting last press timestamp.
155+ """
156+ self ._async_record_activation ()
157+ self .async_write_ha_state ()
158+ await self .async_activate (** kwargs )
0 commit comments