@@ -4861,7 +4861,6 @@ class FastSearchCard extends HTMLElement {
48614861 .trim();
48624862 }
48634863
4864-
48654864 discoverEntities() {
48664865 if (!this._hass) {
48674866 console.warn('HASS not available for auto-discovery');
@@ -4890,8 +4889,18 @@ class FastSearchCard extends HTMLElement {
48904889 // Entity-Filter anwenden
48914890 if (this._config.exclude_entities.includes(entityId)) continue;
48924891
4893- // Area ermitteln
4894- const areaName = this.getEntityArea(entityId, state);
4892+ // INTELLIGENTE AREA-ERMITTLUNG basierend auf Domain
4893+ let areaName;
4894+ if (domain === 'script') {
4895+ areaName = await this.getScriptArea(entityId, state);
4896+ } else if (domain === 'scene') {
4897+ areaName = this.getSceneArea(entityId, state);
4898+ } else if (domain === 'automation') {
4899+ areaName = await this.getAutomationArea(entityId, state);
4900+ } else {
4901+ // Standard Area-Ermittlung für Geräte
4902+ areaName = this.getEntityArea(entityId, state);
4903+ }
48954904
48964905 // Area-Filter anwenden
48974906 if (this._config.include_areas.length > 0 && !this._config.include_areas.includes(areaName)) continue;
@@ -4905,7 +4914,9 @@ class FastSearchCard extends HTMLElement {
49054914 entity: entityId,
49064915 title: state.attributes.friendly_name || entityId,
49074916 area: areaName,
4908- auto_discovered: true
4917+ auto_discovered: true,
4918+ domain: domain,
4919+ category: this.categorizeEntity(domain)
49094920 });
49104921
49114922 } catch (entityError) {
@@ -4922,6 +4933,235 @@ class FastSearchCard extends HTMLElement {
49224933 return []; // Fallback to empty array
49234934 }
49244935 }
4936+
4937+ async getScriptArea(entityId, state) {
4938+ try {
4939+ console.log(`🔍 Analyzing script area for: ${entityId}`);
4940+
4941+ // METHODE 1: Prüfe ob Skript explizit einer Area zugeordnet ist (Entity Registry)
4942+ if (this._hass.areas && this._hass.entities && this._hass.entities[entityId]) {
4943+ const entityRegistry = this._hass.entities[entityId];
4944+ if (entityRegistry.area_id && this._hass.areas[entityRegistry.area_id]) {
4945+ const area = this._hass.areas[entityRegistry.area_id];
4946+ console.log(`✅ Script has explicit area: ${area.name}`);
4947+ return area.name;
4948+ }
4949+ }
4950+
4951+ // METHODE 2: Analysiere Skript-Name nach Area-Keywords
4952+ const scriptName = state.attributes.friendly_name || entityId;
4953+ const detectedArea = this.extractAreaFromName(scriptName);
4954+ if (detectedArea !== 'Ohne Raum') {
4955+ console.log(`✅ Script area detected from name: ${detectedArea}`);
4956+ return detectedArea;
4957+ }
4958+
4959+ // METHODE 3: Versuche Skript-Konfiguration zu analysieren (Advanced)
4960+ try {
4961+ const scriptConfig = await this.getScriptConfiguration(entityId);
4962+ if (scriptConfig) {
4963+ const configArea = this.analyzeScriptTargets(scriptConfig);
4964+ if (configArea !== 'Ohne Raum') {
4965+ console.log(`✅ Script area detected from config: ${configArea}`);
4966+ return configArea;
4967+ }
4968+ }
4969+ } catch (configError) {
4970+ console.warn(`Could not analyze script config for ${entityId}:`, configError);
4971+ }
4972+
4973+ // FALLBACK: Wenn keine Area ermittelt werden kann
4974+ console.log(`❌ No area found for script: ${entityId}`);
4975+ return 'Ohne Raum';
4976+
4977+ } catch (error) {
4978+ console.warn(`❌ Error getting script area for ${entityId}:`, error);
4979+ return 'Ohne Raum';
4980+ }
4981+ }
4982+
4983+ // 🎯 HILFSMETHODE: Extrahiere Area aus Namen (verbesserte Version)
4984+ extractAreaFromName(name) {
4985+ if (!name) return 'Ohne Raum';
4986+
4987+ const normalizedName = name.toLowerCase();
4988+
4989+ // Liste der echten Areas aus Home Assistant für Matching
4990+ const realAreas = this._hass.areas ?
4991+ Object.values(this._hass.areas).map(area => area.name.toLowerCase()) : [];
4992+
4993+ // Suche nach echten Area-Namen im Namen (case-insensitive)
4994+ for (const areaName of realAreas) {
4995+ if (normalizedName.includes(areaName)) {
4996+ // Finde die echte Area mit richtigem Case
4997+ const matchedArea = Object.values(this._hass.areas).find(area =>
4998+ area.name.toLowerCase() === areaName
4999+ );
5000+ if (matchedArea) {
5001+ return matchedArea.name;
5002+ }
5003+ }
5004+ }
5005+
5006+ // Zusätzliche Keywords für häufige Raum-Begriffe
5007+ const roomKeywords = {
5008+ 'wohnzimmer': ['wohnzimmer', 'living', 'salon'],
5009+ 'küche': ['küche', 'kitchen', 'kueche'],
5010+ 'schlafzimmer': ['schlafzimmer', 'bedroom', 'schlafen'],
5011+ 'bad': ['bad', 'bathroom', 'badezimmer'],
5012+ 'arbeitszimmer': ['arbeitszimmer', 'office', 'büro', 'buero', 'arbeiten'],
5013+ 'kinderzimmer': ['kinderzimmer', 'children', 'kids'],
5014+ 'garten': ['garten', 'garden', 'outdoor'],
5015+ 'garage': ['garage', 'carport'],
5016+ 'keller': ['keller', 'basement', 'cellar']
5017+ };
5018+
5019+ for (const [room, keywords] of Object.entries(roomKeywords)) {
5020+ if (keywords.some(keyword => normalizedName.includes(keyword))) {
5021+ // Prüfe ob dieser Raum in Home Assistant existiert
5022+ const existingArea = Object.values(this._hass.areas || {}).find(area =>
5023+ area.name.toLowerCase() === room
5024+ );
5025+ if (existingArea) {
5026+ return existingArea.name;
5027+ }
5028+ // Fallback: Nutze Keyword als Raumname
5029+ return room.charAt(0).toUpperCase() + room.slice(1);
5030+ }
5031+ }
5032+
5033+ return 'Ohne Raum';
5034+ }
5035+
5036+ async getScriptConfiguration(entityId) {
5037+ try {
5038+ // Home Assistant bietet keinen direkten API-Endpunkt für Skript-Konfiguration
5039+ // Alternative: Nutze verfügbare Informationen aus dem State
5040+ const state = this._hass.states[entityId];
5041+
5042+ // Prüfe ob es Script-spezifische Attribute gibt
5043+ if (state.attributes) {
5044+ // Manche Skripte haben 'last_triggered' oder andere hilfreiche Attribute
5045+ return {
5046+ attributes: state.attributes,
5047+ // Weitere Analyse könnte hier erfolgen
5048+ };
5049+ }
5050+
5051+ return null;
5052+ } catch (error) {
5053+ console.warn(`Error getting script config for ${entityId}:`, error);
5054+ return null;
5055+ }
5056+ }
5057+
5058+ analyzeScriptTargets(scriptConfig) {
5059+ try {
5060+ // Da wir keinen direkten Zugriff auf Skript-Actions haben,
5061+ // nutzen wir verfügbare Informationen intelligent
5062+
5063+ // Placeholder für erweiterte Analyse
5064+ // In Zukunft könnte hier eine tiefere Integration erfolgen
5065+
5066+ return 'Ohne Raum';
5067+ } catch (error) {
5068+ console.warn('Error analyzing script targets:', error);
5069+ return 'Ohne Raum';
5070+ }
5071+ }
5072+
5073+ getSceneArea(entityId, state) {
5074+ try {
5075+ console.log(`🎬 Analyzing scene area for: ${entityId}`);
5076+
5077+ // METHODE 1: Explizite Area-Zuordnung
5078+ if (this._hass.areas && this._hass.entities && this._hass.entities[entityId]) {
5079+ const entityRegistry = this._hass.entities[entityId];
5080+ if (entityRegistry.area_id && this._hass.areas[entityRegistry.area_id]) {
5081+ const area = this._hass.areas[entityRegistry.area_id];
5082+ console.log(`✅ Scene has explicit area: ${area.name}`);
5083+ return area.name;
5084+ }
5085+ }
5086+
5087+ // METHODE 2: Analysiere betroffene Entities in der Szene
5088+ const entities = state.attributes.entity_id || [];
5089+ const areas = new Set();
5090+
5091+ entities.forEach(targetEntity => {
5092+ if (this._hass.states[targetEntity]) {
5093+ const entityArea = this.getEntityArea(targetEntity, this._hass.states[targetEntity]);
5094+ if (entityArea !== 'Ohne Raum') {
5095+ areas.add(entityArea);
5096+ }
5097+ }
5098+ });
5099+
5100+ // Wenn alle Entities in einem Raum sind
5101+ if (areas.size === 1) {
5102+ const area = [...areas][0];
5103+ console.log(`✅ Scene area detected from entities: ${area}`);
5104+ return area;
5105+ }
5106+
5107+ // METHODE 3: Area aus Namen extrahieren
5108+ const detectedArea = this.extractAreaFromName(state.attributes.friendly_name || entityId);
5109+ if (detectedArea !== 'Ohne Raum') {
5110+ console.log(`✅ Scene area detected from name: ${detectedArea}`);
5111+ return detectedArea;
5112+ }
5113+
5114+ // FALLBACK: Mehrere Räume oder unbekannt
5115+ if (areas.size > 1) {
5116+ console.log(`ℹ️ Scene affects multiple areas: ${[...areas].join(', ')}`);
5117+ return 'Mehrere Räume';
5118+ }
5119+
5120+ console.log(`❌ No area found for scene: ${entityId}`);
5121+ return 'Ohne Raum';
5122+
5123+ } catch (error) {
5124+ console.warn(`❌ Error getting scene area for ${entityId}:`, error);
5125+ return 'Ohne Raum';
5126+ }
5127+ }
5128+
5129+ // 🎯 AUTOMATIONS AREA-DISCOVERY (ähnlich wie Skripte)
5130+ async getAutomationArea(entityId, state) {
5131+ try {
5132+ console.log(`⚙️ Analyzing automation area for: ${entityId}`);
5133+
5134+ // METHODE 1: Explizite Area-Zuordnung
5135+ if (this._hass.areas && this._hass.entities && this._hass.entities[entityId]) {
5136+ const entityRegistry = this._hass.entities[entityId];
5137+ if (entityRegistry.area_id && this._hass.areas[entityRegistry.area_id]) {
5138+ const area = this._hass.areas[entityRegistry.area_id];
5139+ console.log(`✅ Automation has explicit area: ${area.name}`);
5140+ return area.name;
5141+ }
5142+ }
5143+
5144+ // METHODE 2: Area aus Namen extrahieren
5145+ const detectedArea = this.extractAreaFromName(state.attributes.friendly_name || entityId);
5146+ if (detectedArea !== 'Ohne Raum') {
5147+ console.log(`✅ Automation area detected from name: ${detectedArea}`);
5148+ return detectedArea;
5149+ }
5150+
5151+ // METHODE 3: Analyse von Automation-Attributen
5152+ if (state.attributes.last_triggered || state.attributes.current) {
5153+ // Weitere Analyse könnte hier erfolgen
5154+ }
5155+
5156+ console.log(`❌ No area found for automation: ${entityId}`);
5157+ return 'Ohne Raum';
5158+
5159+ } catch (error) {
5160+ console.warn(`❌ Error getting automation area for ${entityId}:`, error);
5161+ return 'Ohne Raum';
5162+ }
5163+ }
5164+
49255165
49265166 getEntityArea(entityId, state) {
49275167 try {
0 commit comments