From 9358ae103a1af0ac1fc54fc6478e24d51ae68e2b Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Mon, 6 Jun 2022 20:18:47 -0700 Subject: [PATCH] Add a way to get diagnostics for issue reports. --- src/card.ts | 58 +++++++++++++++++++++++++++++++-- src/components/message.ts | 6 ++-- src/localize/languages/en.json | 1 + src/scss/message.scss | 2 +- src/types.ts | 1 + src/utils/ha/device-registry.ts | 22 +++++++++++++ 6 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 src/utils/ha/device-registry.ts diff --git a/src/card.ts b/src/card.ts index fb54b880..ac7ae248 100644 --- a/src/card.ts +++ b/src/card.ts @@ -1,5 +1,10 @@ import { getLovelace, HomeAssistant, LovelaceCardEditor } from 'custom-card-helpers'; -import { CSSResultGroup, html, LitElement, PropertyValues, TemplateResult, unsafeCSS } from 'lit'; +import { + CSSResultGroup, + html, + LitElement, + PropertyValues, TemplateResult, unsafeCSS +} from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { createRef, ref, Ref } from 'lit/directives/ref.js'; @@ -77,6 +82,7 @@ import { sideLoadHomeAssistantElements } from './utils/ha'; import { getEventID } from './utils/ha/browse-media.js'; +import { DeviceList, getAllDevices } from './utils/ha/device-registry.js'; import { ExtendedEntityCache, getAllEntities, @@ -146,6 +152,7 @@ export class FrigateCard extends LitElement { // card-mod. @state() protected _config!: FrigateCardConfig; + protected _rawConfig?: RawFrigateCardConfig; @state() protected _overriddenConfig?: FrigateCardConfig; @@ -344,6 +351,9 @@ export class FrigateCard extends LitElement { tap_action: FrigateCardMenu.isHidingMenu(this._getConfig().menu) ? (createFrigateCardCustomAction('menu_toggle') as FrigateCardCustomAction) : (createFrigateCardCustomAction('default') as FrigateCardCustomAction), + hold_action: createFrigateCardCustomAction( + 'diagnostics', + ) as FrigateCardCustomAction, }); if (this._cameras && this._cameras.size > 1) { @@ -860,6 +870,7 @@ export class FrigateCard extends LitElement { getLovelace().setEditMode(true); } + this._rawConfig = inputConfig; this._config = config; this._overriddenConfig = undefined; this._cameras = undefined; @@ -1014,7 +1025,7 @@ export class FrigateCard extends LitElement { /** * Determine if the scan mode is currently triggered. - * @returns + * @returns */ protected _isTriggered(): boolean { return !!this._triggers.size || !!this._untriggerTimerID; @@ -1296,11 +1307,54 @@ export class FrigateCard extends LitElement { frigateCardAction.media_player_action, ); break; + case 'diagnostics': + this._diagnostics(); + break; default: console.warn(`Frigate card received unknown card action: ${action}`); } } + /** + * Generate diagnostics for issue reports. + */ + protected async _diagnostics(): Promise { + if (this._hass) { + let devices: DeviceList = []; + try { + devices = await getAllDevices(this._hass); + } catch (e) { + // Pass. This is optional. + } + + // Get the Frigate devices in order to extract the Frigate integration and + // server version numbers. + const frigateDevices = devices.filter( + (device) => device.manufacturer === 'Frigate', + ); + const frigateVersionMap: Map = new Map(); + frigateDevices.forEach((device) => { + device.config_entries.forEach((configEntry) => { + if (device.model) { + frigateVersionMap.set(configEntry, device.model); + } + }); + }); + + this._setMessageAndUpdate({ + message: localize('error.diagnostics'), + type: 'info', + icon: 'mdi:information', + context: { + ha_version: this._hass.config.version, + card_version: CARD_VERSION, + frigate_version: Object.fromEntries(frigateVersionMap), + ...(this._rawConfig && { config: this._rawConfig }), + }, + }); + } + } + /** * Get the Frigate UI URL from context. * @returns The URL or null if unavailable. diff --git a/src/components/message.ts b/src/components/message.ts index 74156297..aa7edc21 100644 --- a/src/components/message.ts +++ b/src/components/message.ts @@ -111,7 +111,7 @@ export function renderProgressIndicator(message?: string): TemplateResult { * @param icon An optional icon to attach to the message. */ export function dispatchMessageEvent( - element: HTMLElement, + element: EventTarget, message: string, icon?: string, context?: unknown, @@ -130,7 +130,7 @@ export function dispatchMessageEvent( * @param message The message to show. */ export function dispatchErrorMessageEvent( - element: HTMLElement, + element: EventTarget, message: string, context?: unknown, ): void { @@ -147,7 +147,7 @@ export function dispatchErrorMessageEvent( * @param message The message to show. */ export function dispatchFrigateCardErrorEvent( - element: HTMLElement, + element: EventTarget, error: FrigateCardError ): void { dispatchFrigateCardEvent(element, 'message', { diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json index 95396a46..428e3fd2 100644 --- a/src/localize/languages/en.json +++ b/src/localize/languages/en.json @@ -306,6 +306,7 @@ "no_thumbnail": "No thumbnail available" }, "error": { + "diagnostics": "Card diagnostics. Please review for confidential information prior to sharing", "undecodable_response": "Could not decode response from Home Assistant for request", "empty_response": "Received empty response from Home Assistant for request", "invalid_response": "Received invalid response from Home Assistant for request", diff --git a/src/scss/message.scss b/src/scss/message.scss index 696f2507..45e6385f 100644 --- a/src/scss/message.scss +++ b/src/scss/message.scss @@ -18,7 +18,7 @@ div.message { justify-content: center; align-items: center; box-sizing: border-box; - padding: 10%; + padding: 20px; height: 100%; } diff --git a/src/types.ts b/src/types.ts index 3eba2b96..6e56c48d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -204,6 +204,7 @@ const FRIGATE_CARD_GENERAL_ACTIONS = [ 'frigate_ui', 'fullscreen', 'menu_toggle', + 'diagnostics', ] as const; const FRIGATE_CARD_ACTIONS = [ ...FRIGATE_CARD_GENERAL_ACTIONS, diff --git a/src/utils/ha/device-registry.ts b/src/utils/ha/device-registry.ts new file mode 100644 index 00000000..84278eda --- /dev/null +++ b/src/utils/ha/device-registry.ts @@ -0,0 +1,22 @@ +import { HomeAssistant } from 'custom-card-helpers'; +import { z } from 'zod'; +import { homeAssistantWSRequest } from '.'; + +const deviceSchema = z.object({ + model: z.string().nullable(), + config_entries: z.string().array(), + manufacturer: z.string().nullable(), +}) +export const deviceListSchema = deviceSchema.array(); +export type DeviceList = z.infer; + +/** + * Get a list of all entities from the entity registry. May throw. + * @param hass The Home Assistant object. + * @returns An entity list object. + */ +export const getAllDevices = async (hass: HomeAssistant): Promise => { + return await homeAssistantWSRequest(hass, deviceListSchema, { + type: 'config/device_registry/list', + }); +};