Add a way to get diagnostics for issue reports.

This commit is contained in:
Dermot Duffy
2022-06-06 20:18:47 -07:00
parent 7b902b604c
commit 9358ae103a
6 changed files with 84 additions and 6 deletions
+56 -2
View File
@@ -1,5 +1,10 @@
import { getLovelace, HomeAssistant, LovelaceCardEditor } from 'custom-card-helpers'; 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 { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
import { createRef, ref, Ref } from 'lit/directives/ref.js'; import { createRef, ref, Ref } from 'lit/directives/ref.js';
@@ -77,6 +82,7 @@ import {
sideLoadHomeAssistantElements sideLoadHomeAssistantElements
} from './utils/ha'; } from './utils/ha';
import { getEventID } from './utils/ha/browse-media.js'; import { getEventID } from './utils/ha/browse-media.js';
import { DeviceList, getAllDevices } from './utils/ha/device-registry.js';
import { import {
ExtendedEntityCache, ExtendedEntityCache,
getAllEntities, getAllEntities,
@@ -146,6 +152,7 @@ export class FrigateCard extends LitElement {
// card-mod. // card-mod.
@state() @state()
protected _config!: FrigateCardConfig; protected _config!: FrigateCardConfig;
protected _rawConfig?: RawFrigateCardConfig;
@state() @state()
protected _overriddenConfig?: FrigateCardConfig; protected _overriddenConfig?: FrigateCardConfig;
@@ -344,6 +351,9 @@ export class FrigateCard extends LitElement {
tap_action: FrigateCardMenu.isHidingMenu(this._getConfig().menu) tap_action: FrigateCardMenu.isHidingMenu(this._getConfig().menu)
? (createFrigateCardCustomAction('menu_toggle') as FrigateCardCustomAction) ? (createFrigateCardCustomAction('menu_toggle') as FrigateCardCustomAction)
: (createFrigateCardCustomAction('default') as FrigateCardCustomAction), : (createFrigateCardCustomAction('default') as FrigateCardCustomAction),
hold_action: createFrigateCardCustomAction(
'diagnostics',
) as FrigateCardCustomAction,
}); });
if (this._cameras && this._cameras.size > 1) { if (this._cameras && this._cameras.size > 1) {
@@ -860,6 +870,7 @@ export class FrigateCard extends LitElement {
getLovelace().setEditMode(true); getLovelace().setEditMode(true);
} }
this._rawConfig = inputConfig;
this._config = config; this._config = config;
this._overriddenConfig = undefined; this._overriddenConfig = undefined;
this._cameras = undefined; this._cameras = undefined;
@@ -1014,7 +1025,7 @@ export class FrigateCard extends LitElement {
/** /**
* Determine if the scan mode is currently triggered. * Determine if the scan mode is currently triggered.
* @returns * @returns
*/ */
protected _isTriggered(): boolean { protected _isTriggered(): boolean {
return !!this._triggers.size || !!this._untriggerTimerID; return !!this._triggers.size || !!this._untriggerTimerID;
@@ -1296,11 +1307,54 @@ export class FrigateCard extends LitElement {
frigateCardAction.media_player_action, frigateCardAction.media_player_action,
); );
break; break;
case 'diagnostics':
this._diagnostics();
break;
default: default:
console.warn(`Frigate card received unknown card action: ${action}`); console.warn(`Frigate card received unknown card action: ${action}`);
} }
} }
/**
* Generate diagnostics for issue reports.
*/
protected async _diagnostics(): Promise<void> {
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<string, string> = 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. * Get the Frigate UI URL from context.
* @returns The URL or null if unavailable. * @returns The URL or null if unavailable.
+3 -3
View File
@@ -111,7 +111,7 @@ export function renderProgressIndicator(message?: string): TemplateResult {
* @param icon An optional icon to attach to the message. * @param icon An optional icon to attach to the message.
*/ */
export function dispatchMessageEvent( export function dispatchMessageEvent(
element: HTMLElement, element: EventTarget,
message: string, message: string,
icon?: string, icon?: string,
context?: unknown, context?: unknown,
@@ -130,7 +130,7 @@ export function dispatchMessageEvent(
* @param message The message to show. * @param message The message to show.
*/ */
export function dispatchErrorMessageEvent( export function dispatchErrorMessageEvent(
element: HTMLElement, element: EventTarget,
message: string, message: string,
context?: unknown, context?: unknown,
): void { ): void {
@@ -147,7 +147,7 @@ export function dispatchErrorMessageEvent(
* @param message The message to show. * @param message The message to show.
*/ */
export function dispatchFrigateCardErrorEvent( export function dispatchFrigateCardErrorEvent(
element: HTMLElement, element: EventTarget,
error: FrigateCardError error: FrigateCardError
): void { ): void {
dispatchFrigateCardEvent<Message>(element, 'message', { dispatchFrigateCardEvent<Message>(element, 'message', {
+1
View File
@@ -306,6 +306,7 @@
"no_thumbnail": "No thumbnail available" "no_thumbnail": "No thumbnail available"
}, },
"error": { "error": {
"diagnostics": "Card diagnostics. Please review for confidential information prior to sharing",
"undecodable_response": "Could not decode response from Home Assistant for request", "undecodable_response": "Could not decode response from Home Assistant for request",
"empty_response": "Received empty 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", "invalid_response": "Received invalid response from Home Assistant for request",
+1 -1
View File
@@ -18,7 +18,7 @@ div.message {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
box-sizing: border-box; box-sizing: border-box;
padding: 10%; padding: 20px;
height: 100%; height: 100%;
} }
+1
View File
@@ -204,6 +204,7 @@ const FRIGATE_CARD_GENERAL_ACTIONS = [
'frigate_ui', 'frigate_ui',
'fullscreen', 'fullscreen',
'menu_toggle', 'menu_toggle',
'diagnostics',
] as const; ] as const;
const FRIGATE_CARD_ACTIONS = [ const FRIGATE_CARD_ACTIONS = [
...FRIGATE_CARD_GENERAL_ACTIONS, ...FRIGATE_CARD_GENERAL_ACTIONS,
+22
View File
@@ -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<typeof deviceListSchema>;
/**
* 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<DeviceList> => {
return await homeAssistantWSRequest<DeviceList>(hass, deviceListSchema, {
type: 'config/device_registry/list',
});
};