Add native select entity support.

This commit is contained in:
Dermot Duffy
2022-05-08 15:58:56 -07:00
parent c7462e7f4f
commit 28292483fc
6 changed files with 200 additions and 11 deletions
+4
View File
@@ -9,6 +9,7 @@ import {
MenuStateIcon,
PictureElements,
MenuSubmenu,
MenuSubmenuSelect,
} from '../types.js';
import { dispatchErrorMessageEvent, dispatchFrigateCardEvent } from '../common.js';
@@ -331,3 +332,6 @@ export class FrigateCardElementsMenuStateIcon extends FrigateCardElementsBaseMen
@customElement('frigate-card-menu-submenu')
export class FrigateCardElementsMenuSubmenu extends FrigateCardElementsBaseMenuIcon<MenuSubmenu> {}
@customElement('frigate-card-menu-submenu-select')
export class FrigateCardElementsMenuSubmenuSelect extends FrigateCardElementsBaseMenuIcon<MenuSubmenuSelect> {}
+8 -1
View File
@@ -252,13 +252,20 @@ export class FrigateCardMenu extends LitElement {
if (button.enabled === false) {
return;
}
if (button.type == 'custom:frigate-card-menu-submenu') {
if (button.type === 'custom:frigate-card-menu-submenu') {
return html` <frigate-card-submenu
.hass=${this.hass}
.submenu=${button}
@action=${this._actionHandler.bind(this)}
>
</frigate-card-submenu>`;
} else if (button.type === 'custom:frigate-card-menu-submenu-select') {
return html` <frigate-card-submenu-select
.hass=${this.hass}
.submenuSelect=${button}
@action=${this._actionHandler.bind(this)}
>
</frigate-card-submenu-select>`;
}
let stateParameters: StateParameters = { ...button };
+108 -6
View File
@@ -1,17 +1,27 @@
import type { Corner } from '@material/mwc-menu';
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
import {
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
unsafeCSS,
} from 'lit';
import { HomeAssistant } from 'custom-card-helpers';
import { customElement, property } from 'lit/decorators.js';
import {
frigateCardHasAction,
refreshDynamicStateParameters,
shouldUpdateBasedOnHass,
stopEventFromActivatingCardWideActions,
} from '../common.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { styleMap } from 'lit/directives/style-map.js';
import { MenuSubmenu, MenuSubmenuItem } from '../types.js';
import { MenuSubmenu, MenuSubmenuItem, MenuSubmenuSelect } from '../types.js';
import { actionHandler } from '../action-handler-directive.js';
import { domainIcon } from '../icons/domain-icon.js';
import submenuStyle from '../scss/submenu.scss';
@@ -35,7 +45,7 @@ export class FrigateCardSubmenu extends LitElement {
return html`
<mwc-list-item
style="${styleMap(stateParameters.style || {})}"
graphic="icon"
graphic=${ifDefined(stateParameters.icon ? 'icon' : undefined)}
?twoline=${!!item.subtitle}
?selected=${item.selected}
?activated=${item.selected}
@@ -51,9 +61,7 @@ export class FrigateCardSubmenu extends LitElement {
})}
>
<span>${stateParameters.title || ''}</span>
${item.subtitle
? html`<span slot="secondary">${item.subtitle}</span>`
: ''}
${item.subtitle ? html`<span slot="secondary">${item.subtitle}</span>` : ''}
${stateParameters.icon
? html` <ha-icon
data-domain=${ifDefined(stateParameters.data_domain)}
@@ -109,3 +117,97 @@ export class FrigateCardSubmenu extends LitElement {
return unsafeCSS(submenuStyle);
}
}
@customElement('frigate-card-submenu-select')
export class FrigateCardSubmenuSelect extends LitElement {
@property({ attribute: false })
public hass?: HomeAssistant;
@property({ attribute: false })
public submenuSelect?: MenuSubmenuSelect;
@property({ attribute: false })
public corner?: Corner;
protected _generatedSubmenu?: MenuSubmenu;
protected shouldUpdate(changedProps: PropertyValues): boolean {
// No need to update the submenu unless the select entity has changed.
const oldHass = changedProps.get('hass') as HomeAssistant | undefined;
return (
changedProps.size != 1 ||
!this.submenuSelect ||
(!!oldHass &&
shouldUpdateBasedOnHass(this.hass, oldHass, [this.submenuSelect.entity]))
);
}
protected willUpdate(): void {
if (!this.submenuSelect || !this.hass) {
return;
}
const entity = this.submenuSelect.entity;
const stateObj = this.hass.states[entity];
const options = stateObj?.attributes?.options;
if (!stateObj || !options) {
return;
}
const submenu: MenuSubmenu = {
// Default icon. It should be impossible for this to be used, since
// this.submenuSelect will always have an entity, which means
// refreshDynamicStateParameters will always return an icon.
icon: domainIcon('select'),
// Pull out the dynamic properties (like icon, and title) from the state.
...refreshDynamicStateParameters(this.hass, this.submenuSelect),
// Override it with anything explicitly set in the submenuSelect.
...this.submenuSelect,
type: 'custom:frigate-card-menu-submenu',
items: [],
}
// Remove the options parameter which is unused/unsupported in submenu.
delete submenu['options'];
for (const option of options) {
// If there's a device_class there may be a localized translation of the
// select title available via HASS.
const title = stateObj.attributes.device_class
? this.hass.localize(
`component.select.state.${stateObj.attributes.device_class}.${option}`,
)
: option;
submenu.items.push({
selected: stateObj.state === option,
title: title || option,
...((entity.startsWith('select.') || entity.startsWith('input_select.')) && {
tap_action: {
action: 'call-service',
service: entity.startsWith('select.')
? 'select.select_option'
: 'input_select.select_option',
service_data: {
entity_id: entity,
option: option,
},
},
}),
// Apply overrides the user may have specified for a given option.
...(this.submenuSelect.options && this.submenuSelect.options[option]),
});
}
this._generatedSubmenu = submenu;
}
protected render(): TemplateResult {
return html` <frigate-card-submenu
.hass=${this.hass}
.corner=${this.corner}
.submenu=${this._generatedSubmenu}
></frigate-card-submenu>`;
}
}