diff --git a/src/frigate-hass-card-menu.scss b/src/frigate-hass-card-menu.scss
index a2a46c31..4a1607da 100644
--- a/src/frigate-hass-card-menu.scss
+++ b/src/frigate-hass-card-menu.scss
@@ -43,3 +43,8 @@ ha-icon-button.button {
/* Buttons can always be clicked */
pointer-events: auto;
}
+
+ha-icon-button.emphasized-button {
+ @extend ha-icon-button, .button;
+ color: var(--primary-color, white);
+}
\ No newline at end of file
diff --git a/src/frigate-hass-card.ts b/src/frigate-hass-card.ts
index 09ddd2ec..78de4f83 100644
--- a/src/frigate-hass-card.ts
+++ b/src/frigate-hass-card.ts
@@ -1,4 +1,3 @@
-// TODO Don't show button if no Frigate url.
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
LitElement,
@@ -10,6 +9,7 @@ import {
PropertyValues,
state,
unsafeCSS,
+ query,
} from 'lit-element';
import { NodePart } from 'lit-html';
@@ -30,6 +30,7 @@ import frigate_card_menu_style from './frigate-hass-card-menu.scss';
import {
browseMediaSourceSchema,
frigateCardConfigSchema,
+ MenuButton,
resolvedMediaSchema,
} from './types';
import type {
@@ -111,59 +112,47 @@ export class FrigateCardMenu extends LitElement {
@property({ attribute: false })
protected expand = false;
- @property({ attribute: false })
- protected motionEntity: string | null = null;
-
- @property({ attribute: false })
- public hass: HomeAssistant | null = null;
-
@property({ attribute: false })
protected actionCallback: FrigateCardMenuCallback | null = null;
- protected shouldUpdate(changedProps: PropertyValues): boolean {
- const oldHass = changedProps.get('hass') as HomeAssistant | undefined;
- if (oldHass) {
- return shouldUpdateBasedOnHass(this.hass, oldHass, [this.motionEntity]);
- }
- return true;
- }
-
- // Render the Frigate menu button.
- protected _renderFrigateButton(): TemplateResult {
- return html` {
- if (this.menuMode == 'hidden') {
- this.expand = !this.expand;
- } else {
- this._callAction('default');
- }
- }}
- >`;
- }
+ @property({ attribute: false })
+ public buttons: Map = new Map();
// Call the callback.
protected _callAction(name: string): void {
+ if (name == 'frigate' && this.menuMode == 'hidden') {
+ this.expand = !this.expand;
+ return;
+ }
+
if (this.actionCallback) {
this.actionCallback(name);
}
}
+ // Render a menu button.
+ protected _renderButton(name: string, button: MenuButton): TemplateResult {
+ return html` this._callAction(name)}
+ >`;
+ }
+
+ // Render the Frigate menu button.
+ protected _renderFrigateButton(name: string, button: MenuButton): TemplateResult {
+ const icon =
+ this.menuMode != 'hidden' || this.expand
+ ? 'mdi:alpha-f-box'
+ : 'mdi:alpha-f-box-outline';
+
+ return this._renderButton(name, Object.assign({}, button, { icon: icon }));
+ }
+
// Render the menu.
protected render(): TemplateResult | void | ((part: NodePart) => Promise) {
- let motionIcon: string | null = null;
- if (this.motionEntity && this.hass) {
- motionIcon =
- this.hass.states[this.motionEntity]?.state == 'on'
- ? 'mdi:motion-sensor'
- : 'mdi:walk';
- }
-
let menuClass = 'frigate-card-menu-full';
if (['hidden', 'overlay'].includes(this.menuMode)) {
if (this.menuMode == 'overlay' || this.expand) {
@@ -175,63 +164,15 @@ export class FrigateCardMenu extends LitElement {
return html`
`;
}
@@ -259,16 +200,7 @@ export class FrigateCard extends LitElement {
this._webrtcElement.hass = hass;
}
this._hass = hass;
-
- // Manually set hass in the menu. This is to allow the menu to update,
- // without necessarily re-rendering the entire card (re-rendering interrupts
- // clip playing).
- const menu = this.shadowRoot?.getElementById(
- FrigateCardMenu.FRIGATE_CARD_MENU_ID,
- ) as FrigateCardMenu;
- if (menu) {
- menu.hass = hass;
- }
+ this._updateMenu();
}
@property({ attribute: false })
@@ -297,6 +229,44 @@ export class FrigateCard extends LitElement {
// Whether or not there is an active clip being played.
protected _clipPlaying = false;
+ @query(FrigateCardMenu.FRIGATE_CARD_MENU_ID)
+ _menu!: FrigateCardMenu | null;
+
+ protected _updateMenu(): void {
+ // Manually set hass in the menu. This is to allow the menu to update,
+ // without necessarily re-rendering the entire card (re-rendering interrupts
+ // clip playing).
+ if (!this._menu || !this._hass) {
+ return;
+ }
+
+ this._menu.buttons = this._getMenuButtons();
+ }
+
+ protected _getMenuButtons(): Map {
+ const buttons: Map = new Map();
+
+ buttons.set('frigate', { description: 'Frigate Menu' });
+ buttons.set('live', { icon: 'mdi:cctv', description: 'View Live' });
+ buttons.set('clips', { icon: 'mdi:filmstrip', description: 'View Clips' });
+ buttons.set('snapshots', { icon: 'mdi:camera', description: 'View Snapshots' });
+ if (this.config.frigate_url) {
+ buttons.set('frigate_ui', { icon: 'mdi:web', description: 'View Frigate UI' });
+ }
+
+ if (this._hass && this.config.motion_entity) {
+ const on = this._hass.states[this.config.motion_entity]?.state == 'on';
+ const motionIcon = on ? 'mdi:motion-sensor' : 'mdi:walk';
+
+ buttons.set('motion', {
+ icon: motionIcon,
+ description: 'View Motion Sensor',
+ emphasize: on,
+ });
+ }
+ return buttons;
+ }
+
protected _getParseErrorKeys(error: z.ZodError): string[] {
const errors = error.format();
return Object.keys(errors).filter((v) => !v.startsWith('_'));
@@ -579,7 +549,7 @@ export class FrigateCard extends LitElement {
protected _menuActionHandler(name: string): void {
switch (name) {
- case 'default':
+ case 'frigate':
this._controlVideos({ stop: true, control_clip: true });
this._controlVideos({ stop: true, control_live: true });
this._changeView();
@@ -597,7 +567,7 @@ export class FrigateCard extends LitElement {
this._controlVideos({ stop: true, control_clip: true, control_live: true });
this._changeView(name);
break;
- case 'frigate-ui':
+ case 'frigate_ui':
const frigate_url = this._getFrigateURLFromContext();
if (frigate_url) {
window.open(frigate_url);
@@ -806,7 +776,7 @@ export class FrigateCard extends LitElement {
if (relatedClip) {
this._changeView('clip', relatedClip);
}
- })
+ });
}}
/>`;
}
@@ -848,10 +818,9 @@ export class FrigateCard extends LitElement {
return html`
`;
}
diff --git a/src/types.ts b/src/types.ts
index 980386dc..bf9b1227 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -77,6 +77,12 @@ export interface MediaBeingShown {
resolvedMedia: ResolvedMedia;
}
+export interface MenuButton {
+ icon?: string;
+ description: string;
+ emphasize?: boolean;
+}
+
/**
* Media Browser API types.
*/