@@ -226,6 +226,7 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
226226 'apps.unused' : 'Unused by any flow' ,
227227 'apps.usedBy' : 'flows' ,
228228 'apps.devices' : 'devices' ,
229+ 'apps.extends' : 'Extends' ,
229230 'apps.installed' : 'Installed' ,
230231 'apps.store' : 'store' ,
231232 'apps.crashed' : 'Crashed' ,
@@ -299,6 +300,7 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
299300 'apps.unused' : 'Ikke brukt av noen flow' ,
300301 'apps.usedBy' : 'flows' ,
301302 'apps.devices' : 'enheter' ,
303+ 'apps.extends' : 'Utvider' ,
302304 'apps.installed' : 'Installert' ,
303305 'apps.store' : 'butikk' ,
304306 'apps.crashed' : 'Krasjet' ,
@@ -372,6 +374,7 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
372374 'apps.unused' : 'Von keinem Flow verwendet' ,
373375 'apps.usedBy' : 'Flows' ,
374376 'apps.devices' : 'Geräte' ,
377+ 'apps.extends' : 'Erweitert' ,
375378 'apps.installed' : 'Installiert' ,
376379 'apps.store' : 'Store' ,
377380 'apps.crashed' : 'Abgestürzt' ,
@@ -445,6 +448,7 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
445448 'apps.unused' : 'Door geen enkele flow gebruikt' ,
446449 'apps.usedBy' : 'flows' ,
447450 'apps.devices' : 'apparaten' ,
451+ 'apps.extends' : 'Breidt uit' ,
448452 'apps.installed' : 'Geïnstalleerd' ,
449453 'apps.store' : 'store' ,
450454 'apps.crashed' : 'Gecrasht' ,
@@ -692,6 +696,15 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
692696 return `https://homey.app/a/${ encodeURIComponent ( appId ) } ` ;
693697 }
694698
699+ isAppUnused ( app ) {
700+ if ( app . usage > 0 || ( app . deviceCount || 0 ) > 0 ) return false ;
701+ if ( app . parentAppId ) {
702+ const parent = ( this . appsList || [ ] ) . find ( p => p . id === app . parentAppId ) ;
703+ if ( parent && ( parent . usage > 0 || ( parent . deviceCount || 0 ) > 0 ) ) return false ;
704+ }
705+ return true ;
706+ }
707+
695708 switchTab ( name ) {
696709 document . querySelectorAll ( '#tab-nav .tab-btn' ) . forEach ( btn => {
697710 const active = btn . getAttribute ( 'data-tab' ) === name ;
@@ -936,7 +949,21 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
936949 mem : a . usage && typeof a . usage . mem === 'number' ? a . usage . mem : null ,
937950 usage : appUsage [ a . id ] || 0 ,
938951 deviceCount : appDeviceCount [ a . id ] || 0 ,
952+ parentAppId : null ,
953+ parentAppName : null ,
939954 } ) ) ;
955+ // Heuristic: if an installed app's id starts with another installed app's id + '.',
956+ // treat it as an extension of that parent (e.g. com.mecloud.extension → com.mecloud).
957+ // Extensions inherit their parent's usage/device signal for the "unused" check,
958+ // since Homey's API doesn't expose runtime app-to-app dependencies.
959+ appsList . forEach ( app => {
960+ const candidates = appsList . filter ( p => p . id !== app . id && app . id . startsWith ( p . id + '.' ) ) ;
961+ if ( candidates . length ) {
962+ const parent = candidates . sort ( ( x , y ) => y . id . length - x . id . length ) [ 0 ] ;
963+ app . parentAppId = parent . id ;
964+ app . parentAppName = parent . name ;
965+ }
966+ } ) ;
940967
941968 this . results = results ;
942969 this . appsList = appsList ;
@@ -995,7 +1022,7 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
9951022 const allApps = this . appsList || [ ] ;
9961023 const nCrashed = allApps . filter ( a => a . crashed ) . length ;
9971024 const nUpdate = allApps . filter ( a => a . hasUpdate && ! a . crashed ) . length ;
998- const nUnused = allApps . filter ( a => ! a . crashed && ! a . hasUpdate && a . usage === 0 && ( a . deviceCount || 0 ) === 0 ) . length ;
1025+ const nUnused = allApps . filter ( a => ! a . crashed && ! a . hasUpdate && this . isAppUnused ( a ) ) . length ;
9991026 const nAppsOk = allApps . length - nCrashed - nUpdate - nUnused ;
10001027 document . getElementById ( 'tab-apps-count' ) . innerHTML =
10011028 `${ allApps . length } (<span class="text-red-600">X:${ nCrashed } </span> · <span class="text-amber-600">U:${ nUpdate } </span> · <span class="text-blue-600">!:${ nUnused } </span> · <span class="text-green-700">OK:${ nAppsOk } </span>)` ;
@@ -1064,7 +1091,7 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
10641091 list . innerHTML = '' ;
10651092 const apps = ( this . appsList || [ ] ) . filter ( a => {
10661093 if ( onlyUpdates && ! a . hasUpdate ) return false ;
1067- if ( onlyUnused && ( a . usage > 0 || ( a . deviceCount || 0 ) > 0 ) ) return false ;
1094+ if ( onlyUnused && ! this . isAppUnused ( a ) ) return false ;
10681095 if ( search && ! ( a . name . toLowerCase ( ) . includes ( search ) || a . id . toLowerCase ( ) . includes ( search ) ) ) return false ;
10691096 return true ;
10701097 } ) ;
@@ -1094,11 +1121,11 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
10941121 } else {
10951122 statusBadge = `<span class="badge badge-ok">${ this . t ( 'apps.upToDate' ) } </span>` ;
10961123 }
1097- const unusedBadge = ( a . usage === 0 && ( a . deviceCount || 0 ) === 0 ) ? `<span class="badge badge-info">${ this . t ( 'apps.unused' ) } </span>` : '' ;
1124+ const unusedBadge = this . isAppUnused ( a ) ? `<span class="badge badge-info">${ this . t ( 'apps.unused' ) } </span>` : '' ;
10981125 const crashedBadge = a . crashed ? `<span class="badge badge-err">${ this . t ( 'apps.crashed' ) } ${ a . crashedCount > 1 ? ' ×' + a . crashedCount : '' } </span>` : '' ;
10991126 const disabledBadge = ! a . enabled ? `<span class="badge badge-info">${ this . t ( 'apps.disabled' ) } </span>` : '' ;
11001127 if ( a . crashed ) sevClass = 'sev-error' ;
1101- else if ( a . usage === 0 && ( a . deviceCount || 0 ) === 0 && ! sevClass ) sevClass = 'sev-info' ;
1128+ else if ( this . isAppUnused ( a ) && ! sevClass ) sevClass = 'sev-info' ;
11021129 const memMb = a . mem != null ? ( a . mem / 1048576 ) . toFixed ( 0 ) : null ;
11031130 const cpuPct = a . cpu != null ? a . cpu . toFixed ( 1 ) : null ;
11041131 const resourceHtml = ( memMb != null || cpuPct != null )
@@ -1116,6 +1143,9 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
11161143 const devicesText = `${ a . deviceCount || 0 } ${ this . t ( 'apps.devices' ) } ` ;
11171144 const devicesHtml = `<span class="text-xs ${ ( a . deviceCount || 0 ) > 0 ? 'text-gray-600' : 'text-gray-400' } ">${ devicesText } </span>` ;
11181145 const usageHtml = `${ devicesHtml } ${ flowsHtml } ` ;
1146+ const extendsHtml = a . parentAppId
1147+ ? `<div class="text-xs text-gray-500 mt-1 italic">${ this . t ( 'apps.extends' ) } ${ this . escape ( a . parentAppName || a . parentAppId ) } </div>`
1148+ : '' ;
11191149
11201150 const div = document . createElement ( 'div' ) ;
11211151 div . className = `issue-row ${ sevClass } bg-white border border-gray-200 rounded-lg p-4` ;
@@ -1130,6 +1160,7 @@ <h3 class="font-bold text-red-800 mb-2" data-i18n="error.title">Something went w
11301160 ${ disabledBadge }
11311161 </div>
11321162 <div class="text-xs text-gray-600 mt-1">${ versionInfo } </div>
1163+ ${ extendsHtml }
11331164 ${ resourceHtml }
11341165 <div class="text-xs text-gray-400 mt-1 font-mono">${ this . escape ( a . id ) } </div>
11351166 </div>
0 commit comments