From 91a48a46046cbcc4c19bdc3f29e6eeb68abee228 Mon Sep 17 00:00:00 2001 From: Pierre Gironde Date: Wed, 20 May 2026 23:21:13 +0200 Subject: [PATCH] feat: add hover provider for virtual sensors to display entity details --- vscode-appdaemon/src/extension.ts | 46 ++++++++++++++++++- vscode-appdaemon/vscode-appdaemon-0.1.0.vsix | Bin 29052 -> 29933 bytes 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/vscode-appdaemon/src/extension.ts b/vscode-appdaemon/src/extension.ts index 6d670b0..50684fb 100644 --- a/vscode-appdaemon/src/extension.ts +++ b/vscode-appdaemon/src/extension.ts @@ -244,7 +244,7 @@ class ADVirtualSensorDefinitionProvider implements vscode.DefinitionProvider { invalidate() { this.cache = undefined; } - private async getSensors(): Promise { + async getSensors(): Promise { const now = Date.now(); if (this.cache && now < this.cacheExpiry) { return this.cache; } this.cache = await findAllVirtualSensors(); @@ -282,6 +282,46 @@ class ADVirtualSensorDefinitionProvider implements vscode.DefinitionProvider { } } +class ADVirtualSensorHoverProvider implements vscode.HoverProvider { + constructor( + private haClient: HAClient, + private defProvider: ADVirtualSensorDefinitionProvider + ) {} + + async provideHover( + document: vscode.TextDocument, + position: vscode.Position + ): Promise { + // Find a virtual sensor whose definition is on this exact line + const sensors = await this.defProvider.getSensors(); + const match = sensors.find( + s => s.uri.fsPath === document.uri.fsPath && s.line === position.line + ); + if (!match) { return undefined; } + + const entity = this.haClient.getEntity(match.sensorId); + if (!entity) { return undefined; } + + const md = new vscode.MarkdownString(); + md.isTrusted = true; + const friendly = entity.attributes?.friendly_name as string | undefined; + md.appendMarkdown(`### ${friendly || entity.entity_id}\n\n`); + md.appendMarkdown(`| | |\n|---|---|\n`); + md.appendMarkdown(`| **Entity** | \`${entity.entity_id}\` |\n`); + md.appendMarkdown(`| **State** | **\`${entity.state}\`** |\n`); + if (entity.attributes) { + const skip = new Set(['friendly_name', 'icon', 'entity_picture', 'supported_features', 'supported_color_modes']); + for (const [key, value] of Object.entries(entity.attributes).filter(([k]) => !skip.has(k)).slice(0, 12)) { + const display = typeof value === 'object' ? JSON.stringify(value) : String(value); + const truncated = display.length > 60 ? display.substring(0, 57) + '...' : display; + md.appendMarkdown(`| ${key.replace(/\|/g, '\\|')} | \`${truncated.replace(/\|/g, '\\|')}\` |\n`); + } + } + md.appendMarkdown(`\n*Last changed: ${entity.last_changed}*`); + return new vscode.Hover(md); + } +} + // ───────────────────────────────────────────────────────────────────────────── function updateContextualButton(editor: vscode.TextEditor | undefined) { @@ -367,6 +407,10 @@ export async function activate(context: vscode.ExtensionContext) { vscode.languages.registerDefinitionProvider( { scheme: 'file', language: 'yaml' }, virtualSensorDefProvider + ), + vscode.languages.registerHoverProvider( + { scheme: 'file', language: 'yaml' }, + new ADVirtualSensorHoverProvider(haClient, virtualSensorDefProvider) ) ); diff --git a/vscode-appdaemon/vscode-appdaemon-0.1.0.vsix b/vscode-appdaemon/vscode-appdaemon-0.1.0.vsix index 99eb3b60a165ecd44a6c6b7b1a570c573f217ece..daa294bab42cd9570a439193582ce8d35de10831 100644 GIT binary patch delta 9746 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 ZC6YKYgHmh~#Vqj?%^We{q8$FU{~w&ycewxn delta 8873 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