@@ -182,17 +182,13 @@ export const deviceAutomationEditorMode = (
182182 : "unknown-device" ;
183183} ;
184184
185- // Whether two device automations describe the same kind of automation, meaning
186- // the same domain, type, subtype and event. Which device and which entity they
187- // apply to is ignored, so an automation can be matched against the ones another
188- // device offers.
189185const deviceAutomationsSameType = ( a : DeviceAutomation , b : DeviceAutomation ) =>
190186 deviceAutomationIdentifiers
191187 . filter ( ( property ) => property !== "device_id" && property !== "entity_id" )
192188 . every ( ( property ) => Object . is ( a [ property ] , b [ property ] ) ) ;
193189
194- // Whether two device automations point at the same entity. Both the entity
195- // registry id and the entity id are accepted as reference, on either side .
190+ // An entity can be referenced by its registry id or by its entity id, and the
191+ // two sides do not have to agree .
196192const deviceAutomationsSameEntity = (
197193 entityRegistry : EntityRegistryEntry [ ] ,
198194 a : DeviceAutomation ,
@@ -210,42 +206,45 @@ const deviceAutomationsSameEntity = (
210206 ) ;
211207} ;
212208
213- // Finds, among the automations a device offers, the one matching the given
214- // automation, so a device automation can follow its device after the device was
215- // replaced. A device exposes the same automation type once per entity, so
216- // matching on the type alone picks an arbitrary entity. Splitting a device
217- // leaves the entity registry ids untouched, which makes the entity the reliable
218- // match, the type only serving as a fallback for entity-less automations.
209+ // A device exposes the same automation type once per entity, so the same type
210+ // on another entity is a different automation, not an equivalent one.
219211export const findEquivalentDeviceAutomation = < T extends DeviceAutomation > (
220212 entityRegistry : EntityRegistryEntry [ ] ,
221213 automations : T [ ] ,
222214 automation : DeviceAutomation
223- ) : T | undefined => {
224- const sameType = automations . filter ( ( candidate ) =>
225- deviceAutomationsSameType ( candidate , automation )
215+ ) : T | undefined =>
216+ automations . find (
217+ ( candidate ) =>
218+ deviceAutomationsSameType ( candidate , automation ) &&
219+ deviceAutomationsSameEntity ( entityRegistry , candidate , automation )
226220 ) ;
227- const sameEntity = sameType . find ( ( candidate ) =>
228- deviceAutomationsSameEntity ( entityRegistry , candidate , automation )
229- ) ;
230- return sameEntity || sameType [ 0 ] ;
231- } ;
232221
233- // Everything the automation list does not return: the extra fields of the
234- // capabilities schema (`for`, `above`, ...) and the config of the row holding
235- // the automation (`enabled`, `id`, `alias`, ...).
236- export const deviceAutomationExtraConfig = < T extends DeviceAutomation > (
237- automation : T
238- ) : Partial < T > => {
239- const extraConfig : Partial < T > = { } ;
240- for ( const property in automation ) {
241- if (
242- property !== "metadata" &&
243- ! deviceAutomationIdentifiers . includes ( property )
244- ) {
245- extraConfig [ property ] = automation [ property ] ;
246- }
247- }
248- return extraConfig ;
222+ // Among the split devices that replaced a removed device, the ones that offer
223+ // the given automation. Nothing in the registry says which of them took it over,
224+ // so each candidate has to be asked.
225+ export const fetchReplacementDevices = async < T extends DeviceAutomation > (
226+ hass : HomeAssistant ,
227+ entityRegistry : EntityRegistryEntry [ ] ,
228+ automation : DeviceAutomation ,
229+ compositeSplits : DeviceCompositeSplits ,
230+ fetchDeviceAutomations : ( callWS : CallWS , deviceId : string ) => Promise < T [ ] >
231+ ) : Promise < string [ ] > => {
232+ const candidates =
233+ compositeSplits [ automation . device_id ] ?. split_ids . filter (
234+ ( id ) => id in hass . devices
235+ ) ?? [ ] ;
236+ const automationsPerCandidate = await Promise . all (
237+ candidates . map ( ( id ) =>
238+ fetchDeviceAutomations ( hass . callWS , id ) . catch ( ( ) => [ ] as T [ ] )
239+ )
240+ ) ;
241+ return candidates . filter ( ( _id , index ) =>
242+ findEquivalentDeviceAutomation (
243+ entityRegistry ,
244+ automationsPerCandidate [ index ] ,
245+ automation
246+ )
247+ ) ;
249248} ;
250249
251250const compareEntityIdWithEntityRegId = (
0 commit comments